/* @(#) im_stats: find general image statistics for all bands separately (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 */ /* 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 */ #ifdef HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ #include #include #include #include #include #include #ifdef WITH_DMALLOC #include #endif /*WITH_DMALLOC*/ /* Make and initialise a DOUBLEMASK suitable for grabbing statistics. */ static void * make_mask( IMAGE *im, void *a, void *b ) { DOUBLEMASK *out; /* Make temp output. */ if( !(out = im_create_dmask( "stats", 6, im->Bands + 1 )) ) return( NULL ); /* Set offset to magic value: 42 indicates we have not yet initialised * max and min for this mask. */ out->offset = 42; return( out ); } /* Merge a temp DOUBLEMASK into the real DOUBLEMASK. Row 0 is unused, row 1 * has the stats for band 1. These are: (minimum, maximum, sum, sum^2). If the * offset of out if 42, then it has not been inited yet and we just copy. */ static int merge_mask( void *seq, void *a, void *b ) { DOUBLEMASK *tmp = (DOUBLEMASK *) seq; DOUBLEMASK *out = (DOUBLEMASK *) a; double *rowi, *rowo; int z; /* Merge, or just copy? */ if( out->offset != 42 ) /* Add info from tmp. */ for( z = 1; z < tmp->ysize; z++ ) { rowi = tmp->coeff + z * 6; rowo = out->coeff + z * 6; rowo[0] = IM_MIN( rowi[0], rowo[0] ); rowo[1] = IM_MAX( rowi[1], rowo[1] ); rowo[2] += rowi[2]; rowo[3] += rowi[3]; } else { /* Copy info from tmp. */ for( z = 1; z < tmp->ysize; z++ ) { rowi = tmp->coeff + z * 6; rowo = out->coeff + z * 6; rowo[0] = rowi[0]; rowo[1] = rowi[1]; rowo[2] = rowi[2]; rowo[3] = rowi[3]; } out->offset = 0; } /* Can now free tmp. */ im_free_dmask( tmp ); return( 0 ); } /* Loop over region, adding information to the appropriate fields of tmp. * We set max, min, sum, sum of squares. Our caller fills in the rest. */ static int scan_fn( REGION *reg, void *seq, void *a, void *b ) { DOUBLEMASK *tmp = (DOUBLEMASK *) seq; Rect *r = ®->valid; IMAGE *im = reg->im; int bands = im->Bands; int le = r->left; int ri = IM_RECT_RIGHT(r); int to = r->top; int bo = IM_RECT_BOTTOM(r); int x, y, z; /* What type? First define the loop we want to perform for all types. * We scan lines bands times to avoid repeating band loops. * Use temp variables of same type for min/max for faster comparisons. * Use double to sum bands. */ #define non_complex_loop(TYPE) \ { TYPE *p, *q; \ TYPE value, small, big; \ double *row; \ \ /* Have min and max been initialised? \ */ \ if( tmp->offset == 42 ) { \ /* Init min and max for each band. \ */ \ p = (TYPE *) IM_REGION_ADDR( reg, le, to ); \ for( z = 1; z < bands + 1; z++ ) { \ row = tmp->coeff + z * 6; \ row[0] = p[z - 1]; \ row[1] = p[z - 1]; \ } \ tmp->offset = 0; \ } \ \ for( y = to; y < bo; y++ ) { \ p = (TYPE *) IM_REGION_ADDR( reg, le, y ); \ \ for( z = 0; z < bands; z++ ) { \ q = p + z; \ row = tmp->coeff + (z + 1)*6; \ small = row[0]; \ big = row[1]; \ \ for( x = le; x < ri; x++ ) { \ value = *q; \ q += bands; \ row[2] += value;\ row[3] += (double)value*(double)value;\ if( value > big ) \ big = value; \ else if( value < small ) \ small = value;\ }\ \ row[0] = small; \ row[1] = big; \ }\ }\ } /* Now generate code for all types. */ switch( im->BandFmt ) { case IM_BANDFMT_UCHAR: non_complex_loop(unsigned char); break; case IM_BANDFMT_CHAR: non_complex_loop(signed char); break; case IM_BANDFMT_USHORT: non_complex_loop(unsigned short); break; case IM_BANDFMT_SHORT: non_complex_loop(signed short); break; case IM_BANDFMT_UINT: non_complex_loop(unsigned int); break; case IM_BANDFMT_INT: non_complex_loop(signed int); break; case IM_BANDFMT_DOUBLE: non_complex_loop(double); break; case IM_BANDFMT_FLOAT: non_complex_loop(float); break; default: assert( 0 ); } return( 0 ); } /* Find the statistics of an image. Take any non-complex format. Write the * stats to a DOUBLEMASK of size 6 by (in->Bands+1). We hold a row for each * band, plus one row for all bands. Row n has 6 elements, which are, in * order, (minimum, maximum, sum, sum^2, mean, deviation) for band n. Row 0 has * the figures for all bands together. */ DOUBLEMASK * im_stats( IMAGE *in ) { DOUBLEMASK *out; double *row, *base; gint64 pels, vals, z; /* Check our args. */ if( im_pincheck( in ) ) return( NULL ); if( im_iscomplex( in ) ) { im_error( "im_stats", _( "bad input type" ) ); return( NULL ); } if( in->Coding != IM_CODING_NONE ) { im_error( "im_stats", _( "not uncoded" ) ); return( NULL ); } /* Make output. */ pels = (gint64) in->Xsize * in->Ysize; vals = pels * in->Bands; if( !(out = make_mask( in, NULL, NULL )) ) return( NULL ); /* Loop over input, calculating min, max, sum, sum^2 for each band * separately. */ if( im_iterate( in, make_mask, scan_fn, merge_mask, out, NULL ) ) { im_free_dmask( out ); return( NULL ); } /* Calculate mean, deviation, plus overall stats. */ base = out->coeff; base[0] = base[6]; /* Init global max/min */ base[1] = base[7]; for( z = 0; z < in->Bands; z++ ) { row = base + (z + 1) * 6; base[0] = IM_MIN( base[0], row[0] ); base[1] = IM_MAX( base[1], row[1] ); base[2] += row[2]; base[3] += row[3]; row[4] = row[2] / pels; row[5] = sqrt( fabs( row[3] - (row[2] * row[2] / pels) ) / (pels - 1) ); } base[4] = base[2] / vals; base[5] = sqrt( fabs( base[3] - (base[2] * base[2] / vals) ) / (vals - 1) ); return( out ); }