blob: c13005dec0c7c6a97be99266054668f0c171c354 [file] [log] [blame]
/* im_avg.c
*
* Copyright: 1990, J. Cupitt
*
* Author: J. Cupitt
* Written on: 02/08/1990
* Modified on:
* 5/5/93 JC
* - now does partial images
* - less likely to overflow
* 1/7/93 JC
* - adapted for partial v2
* - ANSI C
* 21/2/95 JC
* - modernised again
* 11/5/95 JC
* - oops! return( NULL ) in im_avg(), instead of return( -1 )
* 20/6/95 JC
* - now returns double
* 13/1/05
* - use 64 bit arithmetic
* 8/12/06
* - add liboil support
* 18/8/09
* - gtkdoc, minor reformatting
* 7/9/09
* - rewrite for im__wrapiter()
* - add complex case (needed for im_max())
* 8/9/09
* - wrapscan stuff moved here
*/
/*
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 <vips/vips.h>
#include <vips/internal.h>
#ifdef HAVE_LIBOIL
#include <liboil/liboil.h>
#endif /*HAVE_LIBOIL*/
#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif /*WITH_DMALLOC*/
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 )
{
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,
VipsStart start, im__wrapscan_fn scan, VipsStop 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 ) );
}
/* Start function: allocate space for a double in which we can accumulate the
* sum.
*/
static void *
avg_start( IMAGE *out, void *a, void *b )
{
double *sum;
if( !(sum = IM_NEW( NULL, double )) )
return( NULL );
*sum = 0.0;
return( (void *) sum );
}
/* Stop function. Add this little sum to the main sum.
*/
static int
avg_stop( void *seq, void *a, void *b )
{
double *sum = (double *) seq;
double *global_sum = (double *) b;
*global_sum += *sum;
im_free( seq );
return( 0 );
}
/* Sum pels in this section.
*/
#define LOOP( TYPE ) { \
TYPE *p = (TYPE *) in; \
\
for( x = 0; x < sz; x++ ) \
m += p[x]; \
}
#define CLOOP( TYPE ) { \
TYPE *p = (TYPE *) in; \
\
for( x = 0; x < sz; x++ ) { \
double mod, re, im; \
\
re = p[0]; \
im = p[1]; \
p += 2; \
mod = re * re + im * im; \
\
m += mod; \
} \
}
/* Loop over region, accumulating a sum in *tmp.
*/
static int
avg_scan( void *in, int n, void *seq, void *a, void *b )
{
const IMAGE *im = (IMAGE *) a;
const int sz = n * im->Bands;
double *sum = (double *) seq;
int x;
double m;
m = *sum;
/* 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_FLOAT: LOOP( float ); break;
case IM_BANDFMT_DOUBLE:
#ifdef HAVE_LIBOIL
{
double *p = (double *) in;
double t;
oil_sum_f64( &t, p, sizeof( double ), sz );
m += t;
}
#else /*!HAVE_LIBOIL*/
LOOP( double );
#endif /*HAVE_LIBOIL*/
break;
case IM_BANDFMT_COMPLEX: CLOOP( float ); break;
case IM_BANDFMT_DPCOMPLEX: CLOOP( double ); break;
default:
g_assert( 0 );
}
*sum = m;
return( 0 );
}
/**
* im_avg:
* @in: input #IMAGE
* @out: output pixel average
*
* This operation finds the average value in an image. It operates on all
* bands of the input image: use im_stats() if you need to calculate an
* average for each band. For complex images, return the average modulus.
*
* See also: im_stats(), im_bandmean(), im_deviate(), im_rank()
*
* Returns: 0 on success, -1 on error
*/
int
im_avg( IMAGE *in, double *out )
{
double global_sum;
gint64 vals, pels;
/* Check our args.
*/
if( im_pincheck( in ) ||
im_check_noncomplex( "im_avg", in ) ||
im_check_uncoded( "im_avg", in ) )
return( -1 );
/* Loop over input, summing pixels.
*/
global_sum = 0.0;
if( im__wrapscan( in,
avg_start, avg_scan, avg_stop, in, &global_sum ) )
return( -1 );
/* Calculate and return average. For complex, we accumulate re*re +
* im*im, so we need to sqrt.
*/
pels = (gint64) in->Xsize * in->Ysize;
vals = pels * in->Bands;
*out = global_sum / vals;
if( vips_bandfmt_iscomplex( in->BandFmt ) )
*out = sqrt( *out );
return( 0 );
}
/* 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 );
}