vips_max() returns arrays for position and value

int array type too for x/y position arrays
This commit is contained in:
John Cupitt 2012-12-05 13:11:29 +00:00
parent 4b9ca4cfce
commit 1e332d2f29
5 changed files with 284 additions and 34 deletions

View File

@ -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

View File

@ -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().
*

View File

@ -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 );
}

View File

@ -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 );

View File

@ -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