diff --git a/ChangeLog b/ChangeLog index 354506cc..2ff5014b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,7 +8,7 @@ im_sintra(), im_costra(), im_tantra(), im_asintra(), im_acostra(), im_atantra(), im_exptra(), im_exp10tra(), im_logtra(), im_log10tra(), im_abs(), im_sign(), im_max(), im_maxpos(), im_deviate(), im_divide(), - im_multiply() + im_multiply(), im_stats() redone as classes - added argument priorites to help control arg ordering - generate has a 'stop' param to signal successful early termination diff --git a/TODO b/TODO index 59ee6ccf..02306fa4 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,20 @@ - add an INTERPRETATION for "array" + can move the whole of mask, plus headers, plus conversion/im_vips2mask and + mask2vips to deprecated + +- try: + + $ vips stats babe.jpg + + reports leaks, we need to unref more stuff + +- try: + + $ vips add babe.jpg + + GLib-GObject-CRITICAL **: g_o .... + diff --git a/libvips/arithmetic/Makefile.am b/libvips/arithmetic/Makefile.am index ab2de947..cfe3afa2 100644 --- a/libvips/arithmetic/Makefile.am +++ b/libvips/arithmetic/Makefile.am @@ -16,7 +16,7 @@ libarithmetic_la_SOURCES = \ im_point_bilinear.c \ im_remainder.c \ sign.c \ - im_stats.c \ + stats.c \ statistic.c \ statistic.h \ avg.c \ diff --git a/libvips/arithmetic/arithmetic.c b/libvips/arithmetic/arithmetic.c index e4d73c22..56d1945b 100644 --- a/libvips/arithmetic/arithmetic.c +++ b/libvips/arithmetic/arithmetic.c @@ -514,6 +514,7 @@ vips_arithmetic_operation_init( void ) extern GType vips_math_get_type( void ); extern GType vips_abs_get_type( void ); extern GType vips_sign_get_type( void ); + extern GType vips_stats_get_type( void ); vips_add_get_type(); vips_subtract_get_type(); @@ -528,5 +529,6 @@ vips_arithmetic_operation_init( void ) vips_math_get_type(); vips_abs_get_type(); vips_sign_get_type(); + vips_stats_get_type(); } diff --git a/libvips/arithmetic/im_stats.c b/libvips/arithmetic/im_stats.c deleted file mode 100644 index 706c5e25..00000000 --- a/libvips/arithmetic/im_stats.c +++ /dev/null @@ -1,351 +0,0 @@ -/* im_stats.c - * -(C) Kirk Martinez 1993 -23/4/93 J.Cupitt - - adapted to partial images - - special struct abandoned; now returns DOUBLEMASK. -1/7/93 JC - - adapted for partial v2 - - ANSIfied -27/7/93 JC - - init of mask changed to use actual values, not IM_MAXDOUBLE and - (-IM_MAXDOUBLE). These caused problems when assigned to floats. - funny business with offset==42, yuk! -31/8/93 JC - - forgot to init global max/min properly! sorry. -21/6/95 JC - - still did not init max and min correctly --- now fixed for good - - * 13/1/05 - * - use 64 bit arithmetic - * 1/9/09 - * - argh nope min/max was broken again for >1CPU in short pipelines on - * some architectures - * 7/9/09 - * - rework based on new im__wrapscan() / im_max() ideas for a proper fix - * - gtkdoc comment - */ - -/* - - This file is part of VIPS. - - VIPS is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - */ - -/* - - These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk - - */ - -/* -#define DEBUG - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#include -#include - -#include -#include - -typedef struct _Wrapscan { - IMAGE *in; - im_start_fn start; - im__wrapscan_fn scan; - im_stop_fn stop; - void *a; - void *b; -} Wrapscan; - -static void * -wrapscan_start( VipsImage *in, void *a, void *b ) -{ - Wrapscan *wrapscan = (Wrapscan *) a; - - return( wrapscan->start( in, wrapscan->a, wrapscan->b ) ); -} - -static int -wrapscan_stop( void *seq, void *a, void *b ) -{ - Wrapscan *wrapscan = (Wrapscan *) a; - - return( wrapscan->stop( seq, wrapscan->a, wrapscan->b ) ); -} - -static int -wrapscan_scan( REGION *reg, void *seq, void *a, void *b, gboolean *stop ) -{ - Wrapscan *wrapscan = (Wrapscan *) a; - Rect *r = ®->valid; - int lsk = IM_REGION_LSKIP( reg ); - - int y; - PEL *p; - - p = (PEL *) IM_REGION_ADDR( reg, r->left, r->top ); - for( y = 0; y < r->height; y++ ) { - if( wrapscan->scan( p, r->width, seq, - wrapscan->a, wrapscan->b ) ) - return( -1 ); - p += lsk; - } - - return( 0 ); -} - -/* Like vips_sink(), but the scan function works a line at a time, like - * im_wrap*(). Shared with im_min(), im_deviate() etc. - */ -int -im__wrapscan( VipsImage *in, - VipsStartFn start, im__wrapscan_fn scan, VipsStopFn stop, - void *a, void *b ) -{ - Wrapscan wrapscan; - - wrapscan.in = in; - wrapscan.start = start; - wrapscan.scan = scan; - wrapscan.stop = stop; - wrapscan.a = a; - wrapscan.b = b; - - return( vips_sink( in, - wrapscan_start, wrapscan_scan, wrapscan_stop, - &wrapscan, NULL ) ); -} - -/* Get the value of pixel (0, 0). Use this to init the min/max value for - * im_max()/im_stats()/etc. - */ -int -im__value( IMAGE *im, double *value ) -{ - IMAGE *t; - - if( !(t = im_open_local( im, "im__value", "p" )) || - im_extract_areabands( im, t, 0, 0, 1, 1, 0, 1 ) || - im_avg( t, value ) ) - return( -1 ); - - return( 0 ); -} - - -/* Track min/max/sum/sum-of-squares for each thread during a scan. - */ -static void * -stats_start( IMAGE *im, void *a, void *b ) -{ - double *global_stats = (double *) b; - - double *stats; - int i; - - if( !(stats = IM_ARRAY( NULL, 4 * im->Bands, double )) ) - return( NULL ); - - for( i = 0; i < 4 * im->Bands; i++ ) - stats[i] = global_stats[i]; - - return( stats ); -} - -/* Merge a thread's output back into the global stats. - */ -static int -stats_stop( void *seq, void *a, void *b ) -{ - const IMAGE *im = (IMAGE *) a; - double *global_stats = (double *) b; - double *stats = (double *) seq; - - int i; - - for( i = 0; i < 4 * im->Bands; i += 4 ) { - global_stats[0] = IM_MIN( global_stats[0], stats[0] ); - global_stats[1] = IM_MAX( global_stats[1], stats[1] ); - global_stats[2] += stats[2]; - global_stats[3] += stats[3]; - - global_stats += 4; - stats += 4; - } - - im_free( seq ); - - return( 0 ); -} - -/* We scan lines bands times to avoid repeating band loops. - * Use temp variables of same type for min/max for faster comparisons. - */ -#define LOOP( TYPE ) { \ - for( z = 0; z < im->Bands; z++ ) { \ - TYPE *q = (TYPE *) in + z; \ - double *row = stats + z * 4; \ - TYPE small, big; \ - double sum, sum2; \ - \ - small = row[0]; \ - big = row[1]; \ - sum = row[2]; \ - sum2 = row[3]; \ - \ - for( x = 0; x < n; x++ ) { \ - TYPE value = *q; \ - \ - sum += value;\ - sum2 += (double) value * (double) value;\ - if( value > big ) \ - big = value; \ - else if( value < small ) \ - small = value;\ - \ - q += im->Bands; \ - } \ - \ - row[0] = small; \ - row[1] = big; \ - row[2] = sum; \ - row[3] = sum2; \ - } \ -} - -/* Loop over region, adding to seq. - */ -static int -stats_scan( void *in, int n, void *seq, void *a, void *b ) -{ - const IMAGE *im = (IMAGE *) a; - - double *stats = (double *) seq; - - int x, z; - - /* Now generate code for all types. - */ - switch( im->BandFmt ) { - case IM_BANDFMT_UCHAR: LOOP( unsigned char ); break; - case IM_BANDFMT_CHAR: LOOP( signed char ); break; - case IM_BANDFMT_USHORT: LOOP( unsigned short ); break; - case IM_BANDFMT_SHORT: LOOP( signed short ); break; - case IM_BANDFMT_UINT: LOOP( unsigned int ); break; - case IM_BANDFMT_INT: LOOP( signed int ); break; - case IM_BANDFMT_DOUBLE: LOOP( double ); break; - case IM_BANDFMT_FLOAT: LOOP( float ); break; - - default: - g_assert( 0 ); - } - - return( 0 ); -} - -/** - * im_stats: - * @in: image to scan - * - * Find many image statistics in a single pass through the data. Returns a - * #DOUBLEMASK of 6 columns by n + 1 (where n is number of bands in image @in) - * rows. Columns are statistics, and are, in order: minimum, maximum, sum, - * sum of squares, mean, standard deviation. Row 0 has statistics for all - * bands together, row 1 has stats for band 1, and so on. - * - * See also: im_maxpos(), im_min(), im_deviate(). - * - * Returns: 0 on success, -1 on error - */ -DOUBLEMASK * -im_stats( IMAGE *im ) -{ - DOUBLEMASK *out; - double *row; - gint64 pels, vals; - double *global_stats; - int i, j; - double value; - - if( im_pincheck( im ) || - im_check_noncomplex( "im_stats", im ) || - im_check_uncoded( "im_stats", im ) ) - return( NULL ); - - if( !(global_stats = IM_ARRAY( im, 4 * im->Bands, double )) ) - return( NULL ); - if( im__value( im, &value ) ) - return( NULL ); - for( i = 0; i < 4 * im->Bands; i += 4 ) { - global_stats[i + 0] = value; - global_stats[i + 1] = value; - global_stats[i + 2] = 0.0; - global_stats[i + 3] = 0.0; - } - - /* Loop over input, calculating min, max, sum, sum^2 for each band - * separately. - */ - if( im__wrapscan( im, stats_start, stats_scan, stats_stop, - im, global_stats ) ) - return( NULL ); - - /* Calculate mean, deviation, plus overall stats. - */ - if( !(out = im_create_dmask( "stats", 6, im->Bands + 1 )) ) - return( NULL ); - - /* Init global max/min/sum/sum2. - */ - out->coeff[0] = value; - out->coeff[1] = value; - out->coeff[2] = 0.0; - out->coeff[3] = 0.0; - - pels = (gint64) im->Xsize * im->Ysize; - vals = pels * im->Bands; - - for( i = 0; i < im->Bands; i++ ) { - row = out->coeff + (i + 1) * 6; - for( j = 0; j < 4; j++ ) - row[j] = global_stats[i * 4 + j]; - - out->coeff[0] = IM_MIN( out->coeff[0], row[0] ); - out->coeff[1] = IM_MAX( out->coeff[1], row[1] ); - out->coeff[2] += row[2]; - out->coeff[3] += row[3]; - row[4] = row[2] / pels; - row[5] = sqrt( fabs( row[3] - (row[2] * row[2] / pels) ) / - (pels - 1) ); - } - out->coeff[4] = out->coeff[2] / vals; - out->coeff[5] = sqrt( fabs( out->coeff[3] - - (out->coeff[2] * out->coeff[2] / vals) ) / (vals - 1) ); - -#ifdef DEBUG - printf( "im_stats:\n" ); - im_print_dmask( out ); -#endif /*DEBUG*/ - - return( out ); -} diff --git a/libvips/arithmetic/stats.c b/libvips/arithmetic/stats.c index 35906299..714fa697 100644 --- a/libvips/arithmetic/stats.c +++ b/libvips/arithmetic/stats.c @@ -113,7 +113,7 @@ enum { COL_XMAX = 8, COL_YMAX = 9, COL_LAST = 10 -} +}; /* Address a double in our array image. */ @@ -125,10 +125,15 @@ vips_stats_build( VipsObject *object ) VipsStatistic *statistic = VIPS_STATISTIC( object ); VipsStats *stats = (VipsStats *) object; - gint64 vals; + gint64 vals, pels; + double *row0; + int b; if( statistic->in ) { - int bands = vips_image_get_bands( statistic->in->Bands ) : 0; + int bands = vips_image_get_bands( statistic->in ); + + if( vips_check_noncomplex( "VipsStats", statistic->in ) ) + return( -1 ); g_object_set( object, "out", vips_image_new_array( COL_LAST, bands + 1 ), @@ -138,32 +143,34 @@ vips_stats_build( VipsObject *object ) if( VIPS_OBJECT_CLASS( vips_stats_parent_class )->build( object ) ) return( -1 ); - vals = (gint64) + pels = (gint64) vips_image_get_width( statistic->in ) * - vips_image_get_height( statistic->in ) * - vips_image_get_bands( statistic->in ); + vips_image_get_height( statistic->in ); + vals = pels * vips_image_get_bands( statistic->in ); + + row0 = ARY( stats->out, 0, 0 ); + + row0[COL_MIN] = *ARY( stats->out, 0, COL_MIN ); + row0[COL_MAX] = *ARY( stats->out, 0, COL_MAX ); + row0[COL_SUM] = 0; + row0[COL_SUM2] = 0; for( b = 0; b < vips_image_get_bands( statistic->in ); b++ ) { + double *row = ARY( stats->out, 0, b + 1 ); + row0[COL_MIN] = VIPS_MIN( row0[COL_MIN], row[COL_MIN] ); + row0[COL_MAX] = VIPS_MAX( row0[COL_MAX], row[COL_MAX] ); + row0[COL_SUM] += row[COL_SUM]; + row0[COL_SUM2] += row[COL_SUM2]; + row[COL_AVG] = row[COL_SUM] / pels; + row[COL_SD] = sqrt( fabs( row[COL_SUM2] - + (row[COL_SUM] * row[COL_SUM] / pels) ) / (pels - 1) ); + } - - - row = out->coeff + (i + 1) * 6; - for( j = 0; j < 4; j++ ) - row[j] = global_stats[i * 4 + j]; - - out->coeff[0] = IM_MIN( out->coeff[0], row[0] ); - out->coeff[1] = IM_MAX( out->coeff[1], row[1] ); - out->coeff[2] += row[2]; - out->coeff[3] += row[3]; - row[4] = row[2] / pels; - row[5] = sqrt( fabs( row[3] - (row[2] * row[2] / pels) ) / - (pels - 1) ); - } - out->coeff[4] = out->coeff[2] / vals; - out->coeff[5] = sqrt( fabs( out->coeff[3] - - (out->coeff[2] * out->coeff[2] / vals) ) / (vals - 1) ); + row0[COL_AVG] = row0[COL_SUM] / vals; + row0[COL_SD] = sqrt( fabs( row0[COL_SUM2] - + (row0[COL_SUM] * row0[COL_SUM] / vals) ) / (vals - 1) ); return( 0 ); } @@ -173,7 +180,7 @@ vips_stats_build( VipsObject *object ) static int vips_stats_stop( VipsStatistic *statistic, void *seq ) { - int bands = vips_image_get_bands( statistic->in->Bands ); + int bands = vips_image_get_bands( statistic->in ); VipsStats *global = (VipsStats *) statistic; VipsStats *local = (VipsStats *) seq; @@ -184,10 +191,10 @@ vips_stats_stop( VipsStatistic *statistic, void *seq ) double *p = ARY( local->out, 0, b + 1 ); double *q = ARY( global->out, 0, b + 1 ); - p[COL_MIN] = q[COL_MIN]; - p[COL_MAX] = q[COL_MAX]; - p[COL_SUM] = q[COL_SUM]; - p[COL_SUM2] = q[COL_SUM2]; + q[COL_MIN] = p[COL_MIN]; + q[COL_MAX] = p[COL_MAX]; + q[COL_SUM] = p[COL_SUM]; + q[COL_SUM2] = p[COL_SUM2]; } global->set = TRUE; @@ -215,7 +222,7 @@ vips_stats_stop( VipsStatistic *statistic, void *seq ) static void * vips_stats_start( VipsStatistic *statistic ) { - int bands = vips_image_get_bands( statistic->in->Bands ); + int bands = vips_image_get_bands( statistic->in ); VipsStats *stats; @@ -332,7 +339,7 @@ vips_stats_init( VipsStats *stats ) } int -vips_stats( VipsImage *in, double *out, ... ) +vips_stats( VipsImage *in, VipsImage **out, ... ) { va_list ap; int result; diff --git a/libvips/deprecated/dispatch_types.c b/libvips/deprecated/dispatch_types.c index b487c3e9..1178d9d3 100644 --- a/libvips/deprecated/dispatch_types.c +++ b/libvips/deprecated/dispatch_types.c @@ -725,7 +725,7 @@ im__dmsprint( im_object obj ) band minimum maximum sum sum^2 mean deviation\ \n" ); for( j = 0; j < mask->ysize; j++ ) { - row = mask->coeff + j * 6; + row = mask->coeff + j * mask->xsize; if( j == 0 ) printf( "all" ); else diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index 9d471a31..8d9ac6ff 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -1604,3 +1604,21 @@ im_exp10tra( IMAGE *in, IMAGE *out ) { return( vips__math( in, out, VIPS_MATH_OPERATION_EXP10 ) ); } + +DOUBLEMASK * +im_stats( VipsImage *in ) +{ + VipsImage *t; + DOUBLEMASK *msk; + + if( vips_stats( in, &t, + NULL ) ) + return( NULL ); + if( !(msk = im_vips2mask( t, "im_stats" )) ) { + g_object_unref( t ); + return( NULL ); + } + g_object_unref( t ); + + return( msk ); +} diff --git a/libvips/include/vips/arithmetic.h b/libvips/include/vips/arithmetic.h index 12d7ea30..a91c27b9 100644 --- a/libvips/include/vips/arithmetic.h +++ b/libvips/include/vips/arithmetic.h @@ -96,6 +96,8 @@ int vips_abs( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); int vips_sign( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); +int vips_stats( VipsImage *in, VipsImage **out, ... ) + __attribute__((sentinel)); @@ -104,7 +106,6 @@ DOUBLEMASK *im_measure_area( VipsImage *im, int left, int top, int width, int height, int h, int v, int *sel, int nsel, const char *name ); -DOUBLEMASK *im_stats( VipsImage *in ); int im_maxpos_avg( VipsImage *im, double *xpos, double *ypos, double *out ); int im_maxpos_vec( VipsImage *im, int *xpos, int *ypos, double *maxima, int n ); int im_minpos_vec( VipsImage *im, int *xpos, int *ypos, double *minima, int n ); diff --git a/libvips/include/vips/vips7compat.h b/libvips/include/vips/vips7compat.h index 05f80702..d0a6ead2 100644 --- a/libvips/include/vips/vips7compat.h +++ b/libvips/include/vips/vips7compat.h @@ -535,6 +535,7 @@ int im_lintra( double a, VipsImage *in, double b, VipsImage *out ); int im_lintra_vec( int n, double *a, VipsImage *in, double *b, VipsImage *out ); int im_abs( VipsImage *in, VipsImage *out ); int im_sign( VipsImage *in, VipsImage *out ); +DOUBLEMASK *im_stats( VipsImage *in ); int im_sintra( VipsImage *in, VipsImage *out ); int im_costra( VipsImage *in, VipsImage *out ); diff --git a/libvips/iofuncs/error.c b/libvips/iofuncs/error.c index 89bc2111..c506f504 100644 --- a/libvips/iofuncs/error.c +++ b/libvips/iofuncs/error.c @@ -388,7 +388,8 @@ vips_warn( const char *domain, const char *fmt, ... ) * @Varargs: arguments to the format string * * Sends a formatted error message to stderr, then sends the contents of the - * error buffer, if any, then terminates the program with an error code. + * error buffer, if any, then shuts down vips and terminates the program with + * an error code. * * @fmt may be %NULL, in which case only the error buffer is printed before * exiting. @@ -412,6 +413,8 @@ vips_error_exit( const char *fmt, ... ) fprintf( stderr, "%s", vips_error_buffer() ); + vips_shutdown(); + exit( 1 ); } diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index ba39dd41..b4043410 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -1850,6 +1850,7 @@ vips_object_print_all_cb( VipsObject *object, int *n ) *n, G_OBJECT_TYPE_NAME( object ), object ); class->print_class( class, &buf ); + vips_buf_appends( &buf, " " ); class->print( object, &buf ); fprintf( stderr, "%s\n", vips_buf_all( &buf ) );