| /* indexed histogram: use an index image to pick the bins |
| * |
| * 13/10/09 |
| * - from im_histgr.c |
| * 24/3/10 |
| * - gtkdoc |
| */ |
| |
| /* |
| |
| 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 <string.h> |
| |
| #include <vips/vips.h> |
| |
| #ifdef WITH_DMALLOC |
| #include <dmalloc.h> |
| #endif /*WITH_DMALLOC*/ |
| |
| /* Accumulate a histogram in one of these. |
| */ |
| typedef struct { |
| IMAGE *index; /* Get bin number from here */ |
| IMAGE *value; /* Add values from here */ |
| IMAGE *out; |
| |
| REGION *vreg; /* Get value pixels with this */ |
| |
| int bands; /* Number of bands in output */ |
| int size; /* Length of bins */ |
| int mx; /* Maximum value we have seen */ |
| double *bins; /* All the bins! */ |
| } Histogram; |
| |
| /* Free a Histogram. |
| */ |
| static void |
| hist_free( Histogram *hist ) |
| { |
| IM_FREE( hist->bins ); |
| IM_FREEF( im_region_free, hist->vreg ); |
| IM_FREE( hist ); |
| } |
| |
| /* Build a Histogram. |
| */ |
| static Histogram * |
| hist_build( IMAGE *index, IMAGE *value, IMAGE *out, int bands, int size ) |
| { |
| Histogram *hist; |
| |
| if( !(hist = IM_NEW( NULL, Histogram )) ) |
| return( NULL ); |
| |
| hist->index = index; |
| hist->value = value; |
| hist->out = out; |
| hist->vreg = NULL; |
| hist->bands = bands; |
| hist->size = size; |
| hist->mx = 0; |
| hist->bins = NULL; |
| |
| if( !(hist->bins = IM_ARRAY( NULL, bands * size, double )) || |
| !(hist->vreg = im_region_create( value )) ) { |
| hist_free( hist ); |
| return( NULL ); |
| } |
| |
| memset( hist->bins, 0, bands * size * sizeof( double ) ); |
| |
| return( hist ); |
| } |
| |
| /* Build a sub-hist, based on the main hist. |
| */ |
| static void * |
| hist_start( IMAGE *out, void *a, void *b ) |
| { |
| Histogram *mhist = (Histogram *) a; |
| |
| return( (void *) |
| hist_build( mhist->index, mhist->value, mhist->out, |
| mhist->bands, mhist->size ) ); |
| } |
| |
| /* Join a sub-hist onto the main hist, then free it. |
| */ |
| static int |
| hist_stop( void *seq, void *a, void *b ) |
| { |
| Histogram *shist = (Histogram *) seq; |
| Histogram *mhist = (Histogram *) a; |
| int i; |
| |
| g_assert( shist->bands == mhist->bands && shist->size == mhist->size ); |
| |
| /* Add on sub-data. |
| */ |
| mhist->mx = IM_MAX( mhist->mx, shist->mx ); |
| for( i = 0; i < mhist->bands * mhist->size; i++ ) |
| mhist->bins[i] += shist->bins[i]; |
| |
| hist_free( shist ); |
| |
| return( 0 ); |
| } |
| |
| /* Accumulate a buffer of pels, uchar index. |
| */ |
| #define ACCUMULATE_UCHAR( TYPE ) { \ |
| int x, z; \ |
| TYPE *tv = (TYPE *) v; \ |
| \ |
| for( x = 0; x < width; x++ ) { \ |
| double *bin = hist->bins + i[x] * bands; \ |
| \ |
| for( z = 0; z < bands; z++ ) \ |
| bin[z] += tv[z]; \ |
| \ |
| tv += bands; \ |
| } \ |
| } |
| |
| /* A uchar index image. |
| */ |
| static int |
| hist_scan_uchar( REGION *reg, void *seq, void *a, void *b ) |
| { |
| Histogram *hist = (Histogram *) seq; |
| Rect *r = ®->valid; |
| IMAGE *value = hist->value; |
| int bands = value->Bands; |
| int width = r->width; |
| |
| int y; |
| |
| /* Need the correspondiing area of the value image. |
| */ |
| if( im_prepare( hist->vreg, r ) ) |
| return( -1 ); |
| |
| /* Accumulate! |
| */ |
| for( y = 0; y < r->height; y++ ) { |
| PEL *i = (PEL *) IM_REGION_ADDR( reg, r->left, r->top + y ); |
| PEL *v = (PEL *) IM_REGION_ADDR( hist->vreg, |
| r->left, r->top + y ); |
| |
| switch( value->BandFmt ) { |
| case IM_BANDFMT_UCHAR: |
| ACCUMULATE_UCHAR( unsigned char ); break; |
| case IM_BANDFMT_CHAR: |
| ACCUMULATE_UCHAR( signed char ); break; |
| case IM_BANDFMT_USHORT: |
| ACCUMULATE_UCHAR( unsigned short ); break; |
| case IM_BANDFMT_SHORT: |
| ACCUMULATE_UCHAR( signed short ); break; |
| case IM_BANDFMT_UINT: |
| ACCUMULATE_UCHAR( unsigned int ); break; |
| case IM_BANDFMT_INT: |
| ACCUMULATE_UCHAR( signed int ); break; |
| case IM_BANDFMT_FLOAT: |
| ACCUMULATE_UCHAR( float ); break; |
| case IM_BANDFMT_DOUBLE: |
| ACCUMULATE_UCHAR( double ); break; |
| |
| default: |
| g_assert( 0 ); |
| } |
| } |
| |
| /* Max is always 255. |
| */ |
| hist->mx = 255; |
| |
| return( 0 ); |
| } |
| |
| /* Accumulate a buffer of pels, ushort index. |
| */ |
| #define ACCUMULATE_USHORT( TYPE ) { \ |
| int x, z; \ |
| TYPE *tv = (TYPE *) v; \ |
| \ |
| for( x = 0; x < width; x++ ) { \ |
| int ix = i[x]; \ |
| double *bin = hist->bins + ix * bands; \ |
| \ |
| if( ix > mx ) \ |
| mx = ix; \ |
| \ |
| for( z = 0; z < bands; z++ ) \ |
| bin[z] += tv[z]; \ |
| \ |
| tv += bands; \ |
| } \ |
| } |
| |
| /* A ushort index image. |
| */ |
| static int |
| hist_scan_ushort( REGION *reg, void *seq, void *a, void *b ) |
| { |
| Histogram *hist = (Histogram *) seq; |
| Rect *r = ®->valid; |
| IMAGE *value = hist->value; |
| int bands = value->Bands; |
| int width = r->width; |
| |
| int y, mx; |
| |
| /* Need the correspondiing area of the value image. |
| */ |
| if( im_prepare( hist->vreg, r ) ) |
| return( -1 ); |
| |
| /* Accumulate! |
| */ |
| mx = hist->mx; |
| for( y = 0; y < r->height; y++ ) { |
| unsigned short *i = (unsigned short *) IM_REGION_ADDR( reg, |
| r->left, r->top + y ); |
| PEL *v = (PEL *) IM_REGION_ADDR( hist->vreg, |
| r->left, r->top + y ); |
| |
| switch( value->BandFmt ) { |
| case IM_BANDFMT_UCHAR: |
| ACCUMULATE_USHORT( unsigned char ); break; |
| case IM_BANDFMT_CHAR: |
| ACCUMULATE_USHORT( signed char ); break; |
| case IM_BANDFMT_USHORT: |
| ACCUMULATE_USHORT( unsigned short ); break; |
| case IM_BANDFMT_SHORT: |
| ACCUMULATE_USHORT( signed short ); break; |
| case IM_BANDFMT_UINT: |
| ACCUMULATE_USHORT( unsigned int ); break; |
| case IM_BANDFMT_INT: |
| ACCUMULATE_USHORT( signed int ); break; |
| case IM_BANDFMT_FLOAT: |
| ACCUMULATE_USHORT( float ); break; |
| case IM_BANDFMT_DOUBLE: |
| ACCUMULATE_USHORT( double ); break; |
| |
| default: |
| g_assert( 0 ); |
| } |
| } |
| |
| /* Note the maximum. |
| */ |
| hist->mx = mx; |
| |
| return( 0 ); |
| } |
| |
| static int |
| hist_write( IMAGE *out, Histogram *hist ) |
| { |
| if( im_cp_descv( out, hist->index, hist->value, NULL ) ) |
| return( -1 ); |
| im_initdesc( out, |
| hist->mx + 1, 1, hist->value->Bands, |
| IM_BBITS_DOUBLE, IM_BANDFMT_DOUBLE, |
| IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); |
| if( im_setupout( out ) ) |
| return( -1 ); |
| |
| if( im_writeline( 0, out, (PEL *) hist->bins ) ) |
| return( -1 ); |
| |
| return( 0 ); |
| } |
| |
| /** |
| * im_hist_indexed: |
| * @index: input image |
| * @value: input image |
| * @out: output image |
| * |
| * Make a histogram of @value, but use image @index to pick the bins. In other |
| * words, element zero in @out contains the sum of all the pixels in @value |
| * whose corresponding pixel in @index is zero. |
| * |
| * @index must have just one band and be u8 or u16. @value must be |
| * non-complex. @out always has the same size and format as @value. |
| * |
| * This operation is useful in conjunction with im_label_regions(). You can |
| * use it to find the centre of gravity of blobs in an image, for example. |
| * |
| * See also: im_histgr(), im_label_regions(). |
| * |
| * Returns: 0 on success, -1 on error |
| */ |
| int |
| im_hist_indexed( IMAGE *index, IMAGE *value, IMAGE *out ) |
| { |
| int size; /* Length of hist */ |
| Histogram *mhist; |
| im_generate_fn scanfn; |
| |
| /* Check images. PIO from in, WIO to out. |
| */ |
| if( im_pincheck( index ) || |
| im_pincheck( value ) || |
| im_outcheck( out ) || |
| im_check_uncoded( "im_hist_indexed", index ) || |
| im_check_uncoded( "im_hist_indexed", value ) || |
| im_check_noncomplex( "im_hist_indexed", value ) || |
| im_check_size_same( "im_hist_indexed", index, value ) || |
| im_check_u8or16( "im_hist_indexed", index ) || |
| im_check_mono( "im_hist_indexed", index ) ) |
| return( -1 ); |
| |
| /* Find the range of pixel values we must handle. |
| */ |
| if( index->BandFmt == IM_BANDFMT_UCHAR ) { |
| size = 256; |
| scanfn = hist_scan_uchar; |
| } |
| else { |
| size = 65536; |
| scanfn = hist_scan_ushort; |
| } |
| |
| /* Build main hist we accumulate data in. |
| */ |
| if( !(mhist = hist_build( index, value, out, value->Bands, size )) ) |
| return( -1 ); |
| |
| /* Accumulate data. |
| */ |
| if( vips_sink( index, |
| hist_start, scanfn, hist_stop, mhist, NULL ) || |
| hist_write( out, mhist ) ) { |
| hist_free( mhist ); |
| return( -1 ); |
| } |
| |
| hist_free( mhist ); |
| |
| return( 0 ); |
| } |