im_stats() redone as a class

This commit is contained in:
John Cupitt 2011-11-08 18:18:42 +00:00
parent 90849a44c5
commit ff2e7b26de
12 changed files with 83 additions and 386 deletions

View File

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

15
TODO
View File

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

View File

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

View File

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

View File

@ -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 <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <vips/vips.h>
#include <vips/internal.h>
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 = &reg->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 );
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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