This commit is contained in:
John Cupitt 2011-11-08 14:46:50 +00:00
parent 9493ea7e29
commit 90849a44c5
3 changed files with 146 additions and 54 deletions

8
TODO
View File

@ -1,5 +1,13 @@
- add an INTERPRETATION for "array"
- im_open_local() is broken? try Adam's prog under 7.27 - im_open_local() is broken? try Adam's prog under 7.27
... it's the operation cache, we just need to call vips_shutdown()
- see: vips_abs_build(), should that set an arithmetic member? ugly - see: vips_abs_build(), should that set an arithmetic member? ugly

View File

@ -79,7 +79,8 @@
* bands of the input image: use im_stats() if you need to find an * bands of the input image: use im_stats() if you need to find an
* maximum for each band. For complex images, find the maximum modulus. * maximum for each band. For complex images, find the maximum modulus.
* *
* See also: #VipsAvg, #VipsMin, im_stats(), im_bandmean(), im_deviate(), im_rank() * See also: #VipsAvg, #VipsMin, im_stats(), im_bandmean(), #VipsDeviate,
* im_rank().
*/ */
typedef struct _VipsMax { typedef struct _VipsMax {

View File

@ -85,8 +85,6 @@
* bands together, row 1 has stats for band 1, and so on. * bands together, row 1 has stats for band 1, and so on.
* *
* See also: #VipsAvg, #VipsMin, and friends. * See also: #VipsAvg, #VipsMin, and friends.
*
* Returns: 0 on success, -1 on error
*/ */
typedef struct _VipsStats { typedef struct _VipsStats {
@ -94,12 +92,33 @@ typedef struct _VipsStats {
VipsImage *out; VipsImage *out;
gboolean set; /* FALSE means no value yet */
} VipsStats; } VipsStats;
typedef VipsStatisticClass VipsStatsClass; typedef VipsStatisticClass VipsStatsClass;
G_DEFINE_TYPE( VipsStats, vips_stats, VIPS_TYPE_STATISTIC ); G_DEFINE_TYPE( VipsStats, vips_stats, VIPS_TYPE_STATISTIC );
/* Names for our columns.
*/
enum {
COL_MIN = 0,
COL_MAX = 1,
COL_SUM = 2,
COL_SUM2 = 3,
COL_AVG = 4,
COL_SD = 5,
COL_XMIN = 6,
COL_YMIN = 7,
COL_XMAX = 8,
COL_YMAX = 9,
COL_LAST = 10
}
/* Address a double in our array image.
*/
#define ARY( im, x, y ) ((double *) VIPS_IMAGE_ADDR( im, x, y ))
static int static int
vips_stats_build( VipsObject *object ) vips_stats_build( VipsObject *object )
{ {
@ -107,76 +126,150 @@ vips_stats_build( VipsObject *object )
VipsStats *stats = (VipsStats *) object; VipsStats *stats = (VipsStats *) object;
gint64 vals; gint64 vals;
double average;
if( statistic->in ) { if( statistic->in ) {
int bands = vips_image_get_bands( statistic->in->Bands ); int bands = vips_image_get_bands( statistic->in->Bands ) : 0;
g_object_set( object, g_object_set( object,
"out", vips_image_new_array( 10, bands + 1 ), "out", vips_image_new_array( COL_LAST, bands + 1 ),
NULL ); NULL );
} }
if( VIPS_OBJECT_CLASS( vips_stats_parent_class )->build( object ) ) if( VIPS_OBJECT_CLASS( vips_stats_parent_class )->build( object ) )
return( -1 ); return( -1 );
/* Calculate average. For complex, we accumulate re*re +
* im*im, so we need to sqrt.
*/
vals = (gint64) vals = (gint64)
vips_image_get_width( statistic->in ) * vips_image_get_width( statistic->in ) *
vips_image_get_height( statistic->in ) * vips_image_get_height( statistic->in ) *
vips_image_get_bands( statistic->in ); vips_image_get_bands( statistic->in );
average = stats->sum / vals;
if( vips_bandfmt_iscomplex( vips_image_get_format( statistic->in ) ) ) for( b = 0; b < vips_image_get_bands( statistic->in ); b++ ) {
average = sqrt( average );
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) );
return( 0 ); return( 0 );
} }
/* Start function: allocate space for a double in which we can accumulate the /* Stop function. Add these little stats to the main set of stats.
* sum for this thread.
*/
static void *
vips_stats_start( VipsStatistic *statistic )
{
return( (void *) g_new0( double, 1 ) );
}
/* Stop function. Add this little sum to the main sum.
*/ */
static int static int
vips_stats_stop( VipsStatistic *statistic, void *seq ) vips_stats_stop( VipsStatistic *statistic, void *seq )
{ {
VipsStats *stats = (VipsStats *) statistic; int bands = vips_image_get_bands( statistic->in->Bands );
double *sum = (double *) seq; VipsStats *global = (VipsStats *) statistic;
VipsStats *local = (VipsStats *) seq;
stats->sum += *sum; int b;
g_free( seq ); if( local->set && !global->set ) {
for( b = 0; b < bands; b++ ) {
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];
}
global->set = TRUE;
}
else if( local->set && global->set ) {
for( b = 0; b < bands; b++ ) {
double *p = ARY( local->out, 0, b + 1 );
double *q = ARY( global->out, 0, b + 1 );
q[COL_MIN] = VIPS_MIN( q[COL_MIN], p[COL_MIN] );
q[COL_MAX] = VIPS_MAX( q[COL_MAX], p[COL_MAX] );
q[COL_SUM] += p[COL_SUM];
q[COL_SUM2] += p[COL_SUM2];
}
}
VIPS_FREEF( g_object_unref, local->out );
VIPS_FREEF( g_free, seq );
return( 0 ); return( 0 );
} }
/* Sum pels in this section. /* Start function: make a dummy local stats for the private use of this thread.
*/ */
#define LOOP( TYPE ) { \ static void *
TYPE *p = (TYPE *) in; \ vips_stats_start( VipsStatistic *statistic )
\ {
for( i = 0; i < sz; i++ ) \ int bands = vips_image_get_bands( statistic->in->Bands );
m += p[i]; \
VipsStats *stats;
stats = g_new( VipsStats, 1 );
if( !(stats->out = vips_image_new_array( COL_LAST, bands + 1 )) ) {
g_free( stats );
return( NULL );
}
stats->set = FALSE;
return( (void *) stats );
} }
#define CLOOP( TYPE ) { \ /* We scan lines bands times to avoid repeating band loops.
TYPE *p = (TYPE *) in; \ * Use temp variables of same type for min/max for faster comparisons.
*/
#define LOOP( TYPE ) { \
for( b = 0; b < bands; b++ ) { \
TYPE *p = ((TYPE *) in) + b; \
double *q = ARY( local->out, 0, b + 1 ); \
TYPE small, big; \
double sum, sum2; \
\ \
for( i = 0; i < sz; i++ ) { \ if( local->set ) { \
double mod; \ small = q[COL_MIN]; \
big = q[COL_MAX]; \
sum = q[COL_SUM]; \
sum2 = q[COL_SUM2]; \
} \
else { \
small = p[0]; \
big = p[0]; \
sum = 0; \
sum2 = 0; \
} \
\ \
mod = p[0] * p[0] + p[1] * p[1]; \ for( i = 0; i < n; i++ ) { \
p += 2; \ TYPE value = *p; \
\ \
m += mod; \ sum += value;\
sum2 += (double) value * (double) value;\
if( value > big ) \
big = value; \
else if( value < small ) \
small = value;\
\
p += bands; \
} \
\
q[COL_MIN] = small; \
q[COL_MAX] = big; \
q[COL_SUM] = sum; \
q[COL_SUM2] = sum2; \
local->set = TRUE; \
} \ } \
} }
@ -186,17 +279,11 @@ static int
vips_stats_scan( VipsStatistic *statistic, void *seq, vips_stats_scan( VipsStatistic *statistic, void *seq,
int x, int y, void *in, int n ) int x, int y, void *in, int n )
{ {
const int sz = n * vips_image_get_bands( statistic->in ); const int bands = vips_image_get_bands( statistic->in );
VipsStats *local = (VipsStats *) seq;
double *sum = (double *) seq; int b, i;
int i;
double m;
m = *sum;
/* Now generate code for all types.
*/
switch( vips_image_get_format( statistic->in ) ) { switch( vips_image_get_format( statistic->in ) ) {
case VIPS_FORMAT_UCHAR: LOOP( unsigned char ); break; case VIPS_FORMAT_UCHAR: LOOP( unsigned char ); break;
case VIPS_FORMAT_CHAR: LOOP( signed char ); break; case VIPS_FORMAT_CHAR: LOOP( signed char ); break;
@ -206,15 +293,11 @@ vips_stats_scan( VipsStatistic *statistic, void *seq,
case VIPS_FORMAT_INT: LOOP( signed int ); break; case VIPS_FORMAT_INT: LOOP( signed int ); break;
case VIPS_FORMAT_FLOAT: LOOP( float ); break; case VIPS_FORMAT_FLOAT: LOOP( float ); break;
case VIPS_FORMAT_DOUBLE: LOOP( double ); break; case VIPS_FORMAT_DOUBLE: LOOP( double ); break;
case VIPS_FORMAT_COMPLEX: CLOOP( float ); break;
case VIPS_FORMAT_DPCOMPLEX: CLOOP( double ); break;
default: default:
g_assert( 0 ); g_assert( 0 );
} }
*sum = m;
return( 0 ); return( 0 );
} }
@ -238,7 +321,7 @@ vips_stats_class_init( VipsStatsClass *class )
VIPS_ARG_IMAGE( class, "out", 100, VIPS_ARG_IMAGE( class, "out", 100,
_( "Output" ), _( "Output" ),
_( "Output image" ), _( "Output array of statistics" ),
VIPS_ARGUMENT_REQUIRED_OUTPUT, VIPS_ARGUMENT_REQUIRED_OUTPUT,
G_STRUCT_OFFSET( VipsStats, out ) ); G_STRUCT_OFFSET( VipsStats, out ) );
} }