From 1e332d2f296e10acc9568ab8c522ec27006ab3d2 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 5 Dec 2012 13:11:29 +0000 Subject: [PATCH] vips_max() returns arrays for position and value int array type too for x/y position arrays --- ChangeLog | 1 + libvips/arithmetic/max.c | 127 +++++++++++++++++------ libvips/deprecated/vips7compat.c | 4 + libvips/include/vips/type.h | 15 ++- libvips/iofuncs/type.c | 171 +++++++++++++++++++++++++++++++ 5 files changed, 284 insertions(+), 34 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3254fce7..eb826a07 100644 --- a/ChangeLog +++ b/ChangeLog @@ -24,6 +24,7 @@ - preserve jpeg app13 (photoshop ipct) - nearest neighbour goes back to round down ... round to nearest caused a range of annoying problems, such as strange half-pixels along edges +- vips_max() tracks the top n maxima 14/11/12 started 7.30.6 - capture tiff warnings earlier diff --git a/libvips/arithmetic/max.c b/libvips/arithmetic/max.c index a1204de2..88f0eb25 100644 --- a/libvips/arithmetic/max.c +++ b/libvips/arithmetic/max.c @@ -22,7 +22,7 @@ * - avoid NaN in float/double/complex images * - allow +/- INFINITY as a result * 4/12/12 - * - track top n values + * - track and return top n values */ /* @@ -109,6 +109,13 @@ typedef struct _VipsMax { int x; int y; + /* And the postions and values we found as VipsArrays for returning to + * our caller. + */ + VipsArrayDouble *max_array; + VipsArrayInt *x_array; + VipsArrayInt *y_array; + /* Global state here. */ VipsValues values; @@ -159,7 +166,7 @@ vips_values_add( VipsValues *values, double v, int x, int y ) else { /* Not full, move stuff to the right into empty space. */ - for( j = values->n + 1; j > i; j-- ) { + for( j = values->n; j > i; j-- ) { values->value[j] = values->value[j - 1]; values->x_pos[j] = values->x_pos[j - 1]; values->y_pos[j] = values->y_pos[j - 1]; @@ -198,6 +205,14 @@ vips_max_build( VipsObject *object ) * will trigger an error later. */ if( values->n > 0 ) { + VipsArrayDouble *out_array; + VipsArrayInt *x_array; + VipsArrayInt *y_array; + + out_array = vips_array_double_new( values->value, values->n ); + x_array = vips_array_int_new( values->x_pos, values->n ); + y_array = vips_array_int_new( values->y_pos, values->n ); + /* We have to set the props via g_object_set() to stop vips * complaining they are unset. */ @@ -205,9 +220,25 @@ vips_max_build( VipsObject *object ) "out", values->value[values->n - 1], "x", values->x_pos[values->n - 1], "y", values->y_pos[values->n - 1], + "out_array", out_array, + "x_array", x_array, + "y_array", y_array, NULL ); + + vips_area_unref( (VipsArea *) out_array ); + vips_area_unref( (VipsArea *) x_array ); + vips_area_unref( (VipsArea *) y_array ); } +#ifdef DEBUG + printf( "vips_max_build: %d values found\n", values->n ); + for( i = 0; i < values->n; i++ ) + printf( "%d) %g\t%d\t%d\n", + i, + values->value[i], + values->x_pos[i], values->y_pos[i] ); +#endif /*DEBUG*/ + return( 0 ); } @@ -243,19 +274,25 @@ vips_max_stop( VipsStatistic *statistic, void *seq ) return( 0 ); } -/* Real max with an upper bound. Stop if our array fills with maxval. +/* Real max with an upper bound. + * + * Add values to the buffer if they are greater than the buffer minimum. If + * the buffer isn't full, there is no minimum. + * + * Avoid a double test by splitting the loop into two phases: before and after + * the buffer fills. + * + * Stop if our array fills with maxval. */ #define LOOPU( TYPE, UPPER ) { \ TYPE *p = (TYPE *) in; \ TYPE m; \ \ - if( values->n ) \ - m = values->value[0]; \ - else { \ - m = p[0]; \ - } \ + for( i = 0; i < sz && values->n < values->size; i++ ) \ + vips_values_add( values, p[i], x + i / bands, y ); \ + m = values->value[0]; \ \ - for( i = 0; i < sz; i++ ) { \ + for( ; i < sz; i++ ) { \ if( p[i] > m ) { \ vips_values_add( values, p[i], x + i / bands, y ); \ m = values->value[0]; \ @@ -277,45 +314,44 @@ vips_max_stop( VipsStatistic *statistic, void *seq ) #define LOOPF( TYPE ) { \ TYPE *p = (TYPE *) in; \ TYPE m; \ - gboolean set; \ \ - set = values->n > 0; \ - if( set ) \ - m = values->value[0]; \ + for( i = 0; i < sz && values->n < values->size; i++ ) \ + if( !isnan( p[i] ) ) \ + vips_values_add( values, p[i], x + i / bands, y ); \ + m = values->value[0]; \ \ - for( i = 0; i < sz; i++ ) { \ - if( (set && p[i] > m) || \ - (!set && !isnan( p[i] )) ) { \ + for( ; i < sz; i++ ) \ + if( p[i] > m ) { \ vips_values_add( values, p[i], x + i / bands, y ); \ m = values->value[0]; \ - set = TRUE; \ } \ - } \ } -/* As LOOPF, but complex. Track max(mod) to avoid sqrt(). +/* As LOOPF, but complex. Track max(mod ** 2) to avoid sqrt(). */ #define LOOPC( TYPE ) { \ TYPE *p = (TYPE *) in; \ TYPE m; \ - gboolean set; \ \ - set = values->n > 0; \ - if( set ) \ - m = values->value[0]; \ - \ - for( i = 0; i < sz; i++ ) { \ - TYPE mod; \ + for( i = 0; i < sz && values->n < values->size; i++ ) { \ + TYPE mod2 = p[0] * p[0] + p[1] * p[1]; \ + \ + if( !isnan( mod2 ) ) \ + vips_values_add( values, p[i], x + i / bands, y ); \ \ - mod = p[0] * p[0] + p[1] * p[1]; \ p += 2; \ + } \ + m = values->value[0]; \ + \ + for( ; i < sz; i++ ) { \ + TYPE mod2 = p[0] * p[0] + p[1] * p[1]; \ \ - if( (set && mod > m) || \ - (!set && !isnan( mod )) ) { \ - vips_values_add( values, mod, x + i / bands, y ); \ + if( mod2 > m ) { \ + vips_values_add( values, mod2, x + i / bands, y ); \ m = values->value[0]; \ - set = TRUE; \ } \ + \ + p += 2; \ } \ } @@ -408,12 +444,32 @@ vips_max_class_init( VipsMaxClass *class ) G_STRUCT_OFFSET( VipsMax, size ), 0, 1000000, 10 ); + VIPS_ARG_BOXED( class, "out_array", 6, + _( "Output array" ), + _( "Array of output values" ), + VIPS_ARGUMENT_OPTIONAL_OUTPUT, + G_STRUCT_OFFSET( VipsMax, max_array ), + VIPS_TYPE_ARRAY_DOUBLE ); + + VIPS_ARG_BOXED( class, "x_array", 7, + _( "x array" ), + _( "Array of horizontal positions" ), + VIPS_ARGUMENT_OPTIONAL_OUTPUT, + G_STRUCT_OFFSET( VipsMax, x_array ), + VIPS_TYPE_ARRAY_INT ); + + VIPS_ARG_BOXED( class, "y_array", 8, + _( "y array" ), + _( "Array of vertical positions" ), + VIPS_ARGUMENT_OPTIONAL_OUTPUT, + G_STRUCT_OFFSET( VipsMax, y_array ), + VIPS_TYPE_ARRAY_INT ); } static void vips_max_init( VipsMax *max ) { - max->size = 10; + max->size = 1; } /** @@ -427,6 +483,9 @@ vips_max_init( VipsMax *max ) * @x: horizontal position of maximum * @y: vertical position of maximum * @size: number of maxima to find + * @out_array: return array of maximum values + * @x_array: corresponding horizontal positions + * @y_array: corresponding vertical positions * * This operation finds the maximum value in an image. * @@ -439,7 +498,9 @@ vips_max_init( VipsMax *max ) * * For complex images, this operation finds the maximum modulus. * - * You can read out the position of the maximum with @x and @y. + * You can read out the position of the maximum with @x and @y. You can read + * out arrays of the values and positions of the top @size maxima with + * @out_array, @x_array and @y_array. * * See also: vips_min(), vips_stats(). * diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index 643fa530..dfeac7d6 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -2810,9 +2810,13 @@ im_quadratic( IMAGE *in, IMAGE *out, IMAGE *coeff ) int im_maxpos_vec( VipsImage *im, int *xpos, int *ypos, double *maxima, int n ) { + printf( "im_maxpos_vec: fixme\n" ); + return( -1 ); } int im_minpos_vec( VipsImage *im, int *xpos, int *ypos, double *minima, int n ) { + printf( "im_minpos_vec: fixme\n" ); + return( -1 ); } diff --git a/libvips/include/vips/type.h b/libvips/include/vips/type.h index 823f273e..053ff11e 100644 --- a/libvips/include/vips/type.h +++ b/libvips/include/vips/type.h @@ -128,7 +128,6 @@ GType vips_ref_string_get_type( void ); #define VIPS_TYPE_BLOB (vips_blob_get_type()) GType vips_blob_get_type( void ); - /** * VIPS_TYPE_ARRAY_DOUBLE: * @@ -140,6 +139,17 @@ VipsArrayDouble *vips_array_double_new( const double *array, int n ); VipsArrayDouble *vips_array_double_newv( int n, ... ); GType vips_array_double_get_type( void ); +/** + * VIPS_TYPE_ARRAY_INT: + * + * The #GType for a #VipsArrayInt. + */ +#define VIPS_TYPE_ARRAY_INT (vips_array_int_get_type()) +typedef VipsArea VipsArrayInt; +VipsArrayInt *vips_array_int_new( const int *array, int n ); +VipsArrayInt *vips_array_int_newv( int n, ... ); +GType vips_array_int_get_type( void ); + /** * VIPS_TYPE_ARRAY_IMAGE: * @@ -171,6 +181,9 @@ void *vips_value_get_array( const GValue *value, double *vips_value_get_array_double( const GValue *value, int *n ); int vips_value_set_array_double( GValue *value, const double *array, int n ); +int *vips_value_get_array_int( const GValue *value, int *n ); +int vips_value_set_array_int( GValue *value, const int *array, int n ); + GObject **vips_value_get_array_object( const GValue *value, int *n ); int vips_value_set_array_object( GValue *value, int n ); diff --git a/libvips/iofuncs/type.c b/libvips/iofuncs/type.c index c3474fb8..dfe68915 100644 --- a/libvips/iofuncs/type.c +++ b/libvips/iofuncs/type.c @@ -546,6 +546,78 @@ vips_blob_get_type( void ) return( type ); } +static void +transform_array_int_g_string( const GValue *src_value, GValue *dest_value ) +{ + int n; + int *array = vips_value_get_array_int( src_value, &n ); + + char txt[1024]; + VipsBuf buf = VIPS_BUF_STATIC( txt ); + int i; + + for( i = 0; i < n; i++ ) + /* Use space as a separator since ',' may be a decimal point + * in this locale. + */ + vips_buf_appendf( &buf, "%d ", array[i] ); + + g_value_set_string( dest_value, vips_buf_all( &buf ) ); +} + +/* It'd be great to be able to write a generic string->array function, but + * it doesn't seem possible. + */ +static void +transform_g_string_array_int( const GValue *src_value, GValue *dest_value ) +{ + char *str; + int n; + char *p, *q; + int i; + int *array; + + /* Walk the string to get the number of elements. + * We need a copy of the string, since we insert \0 during + * scan. + * + * We can't allow ',' as a separator, since some locales use it as a + * decimal point. + */ + str = g_value_dup_string( src_value ); + n = 0; + for( p = str; (q = vips_break_token( p, "\t; " )); p = q ) + n += 1; + g_free( str ); + + vips_value_set_array( dest_value, n, G_TYPE_INT, sizeof( int ) ); + array = (int *) vips_value_get_array( dest_value, NULL, NULL, NULL ); + + str = g_value_dup_string( src_value ); + i = 0; + for( p = str; (q = vips_break_token( p, "\t; " )); p = q ) + array[i++] = atoi( p ); + g_free( str ); +} + +GType +vips_array_int_get_type( void ) +{ + static GType type = 0; + + if( !type ) { + type = g_boxed_type_register_static( "VipsArrayInt", + (GBoxedCopyFunc) vips_area_copy, + (GBoxedFreeFunc) vips_area_unref ); + g_value_register_transform_func( type, G_TYPE_STRING, + transform_array_int_g_string ); + g_value_register_transform_func( G_TYPE_STRING, type, + transform_g_string_array_int ); + } + + return( type ); +} + static void transform_array_double_g_string( const GValue *src_value, GValue *dest_value ) { @@ -915,6 +987,105 @@ vips_value_get_array( const GValue *value, return( area->data ); } +/** + * vips_array_int_new: + * @array: (array length=n): array of int + * @n: number of ints + * + * Allocate a new array of ints and copy @array into it. Free with + * vips_area_unref(). + * + * See also: #VipsArea. + * + * Returns: (transfer full): A new #VipsArrayInt. + */ +VipsArrayInt * +vips_array_int_new( const int *array, int n ) +{ + VipsArea *area; + int *array_copy; + + area = vips_area_new_array( G_TYPE_INT, sizeof( int ), n ); + array_copy = vips_area_get_data( area, NULL, NULL, NULL, NULL ); + memcpy( array_copy, array, n * sizeof( int ) ); + + return( area ); +} + +/** + * vips_array_int_newv: + * @n: number of ints + * @...: list of int arguments + * + * Allocate a new array of @n ints and copy @... into it. Free with + * vips_area_unref(). + * + * See also: vips_array_int_new() + * + * Returns: (transfer full): A new #VipsArrayInt. + */ +VipsArrayInt * +vips_array_int_newv( int n, ... ) +{ + va_list ap; + VipsArea *area; + int *array; + int i; + + area = vips_area_new_array( G_TYPE_INT, sizeof( int ), n ); + array = vips_area_get_data( area, NULL, NULL, NULL, NULL ); + + va_start( ap, n ); + for( i = 0; i < n; i++ ) + array[i] = va_arg( ap, int ); + va_end( ap ); + + return( area ); +} + +/** + * vips_value_get_array_int: + * @value: %GValue to get from + * @n: (allow-none): return the number of elements here, optionally + * + * Return the start of the array of ints held by @value. + * optionally return the number of elements in @n. + * + * See also: vips_array_int_set(). + * + * Returns: (transfer none): The array address. + */ +int * +vips_value_get_array_int( const GValue *value, int *n ) +{ + return( vips_value_get_array( value, n, NULL, NULL ) ); +} + +/** + * vips_value_set_array_int: + * @value: (out): %GValue to get from + * @array: (array length=n): array of ints + * @n: the number of elements + * + * Set @value to hold a copy of @array. Pass in the array length in @n. + * + * See also: vips_array_int_get(). + * + * Returns: 0 on success, -1 otherwise. + */ +int +vips_value_set_array_int( GValue *value, const int *array, int n ) +{ + int *array_copy; + + g_value_init( value, VIPS_TYPE_ARRAY_INT ); + vips_value_set_array( value, n, G_TYPE_INT, sizeof( int ) ); + array_copy = vips_value_get_array_int( value, NULL ); + memcpy( array_copy, array, n * sizeof( int ) ); + + return( 0 ); +} + /** * vips_array_double_new: * @array: (array length=n): array of double