281 lines
6.9 KiB
C
281 lines
6.9 KiB
C
/* @(#) 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 <config.h>
|
|
#endif /*HAVE_CONFIG_H*/
|
|
#include <vips/intl.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <assert.h>
|
|
|
|
#include <vips/vips.h>
|
|
|
|
#ifdef WITH_DMALLOC
|
|
#include <dmalloc.h>
|
|
#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 );
|
|
}
|