diff --git a/ChangeLog b/ChangeLog index 0140cae7..d39a3a93 100644 --- a/ChangeLog +++ b/ChangeLog @@ -59,6 +59,9 @@ - im_generate() checks that im_demand_hint() has been called for this image - im_jpeg2vips.c, set scale_num on shrink (thanks Guido) - heh argh reading history always stopped after the first line (thanks Haida) +- added im_histindexed +- new im_iterate() calls start functions from workers so resources they make + are owned by the worker thread 25/3/09 started 7.18.0 - revised version numbers diff --git a/libvips/histograms_lut/Makefile.am b/libvips/histograms_lut/Makefile.am index c781026e..050cd31b 100644 --- a/libvips/histograms_lut/Makefile.am +++ b/libvips/histograms_lut/Makefile.am @@ -9,6 +9,7 @@ libhistograms_lut_la_SOURCES = \ im_histgr.c \ im_histnD.c \ im_histplot.c \ + im_histindexed.c \ im_histspec.c \ im_hsp.c \ im_identity.c \ diff --git a/libvips/histograms_lut/hist_dispatch.c b/libvips/histograms_lut/hist_dispatch.c index f49044b6..e85df786 100644 --- a/libvips/histograms_lut/hist_dispatch.c +++ b/libvips/histograms_lut/hist_dispatch.c @@ -107,6 +107,31 @@ static im_function heq_desc = { heq_args /* Arg list */ }; +static im_arg_desc histindexed_args[] = { + IM_INPUT_IMAGE( "index" ), + IM_INPUT_IMAGE( "value" ), + IM_OUTPUT_IMAGE( "out" ) +}; + +/* Call im_histindexed via arg vector. + */ +static int +histindexed_vec( im_object *argv ) +{ + return( im_hist_indexed( argv[0], argv[1], argv[2] ) ); +} + +/* Description of im_histindexed. + */ +static im_function histindexed_desc = { + "im_hist_indexed", /* Name */ + "make a histogram with an index image", /* Description */ + IM_FN_PIO, /* Flags */ + histindexed_vec, /* Dispatch function */ + IM_NUMBER( histindexed_args ), /* Size of arg list */ + histindexed_args /* Arg list */ +}; + /* Call im_hist via arg vector. */ static int @@ -779,6 +804,7 @@ static im_function *hist_list[] = { &hist_desc, &histcum_desc, &histeq_desc, + &histindexed_desc, &histgr_desc, &histnD_desc, &histnorm_desc, diff --git a/libvips/histograms_lut/im_histindexed.c b/libvips/histograms_lut/im_histindexed.c new file mode 100644 index 00000000..768c7302 --- /dev/null +++ b/libvips/histograms_lut/im_histindexed.c @@ -0,0 +1,351 @@ +/* indexed histogram: use an index image to pick the bins + * + * 13/10/09 + * - from im_histgr.c + */ + +/* + + 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 + +#ifdef WITH_DMALLOC +#include +#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 ); +} + +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_same_size( "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( im_iterate( index, + hist_start, scanfn, hist_stop, mhist, NULL ) || + hist_write( out, mhist ) ) { + hist_free( mhist ); + return( -1 ); + } + + hist_free( mhist ); + + return( 0 ); +} diff --git a/libvips/include/vips/check.h b/libvips/include/vips/check.h index 10d481a0..3c8559ef 100644 --- a/libvips/include/vips/check.h +++ b/libvips/include/vips/check.h @@ -52,6 +52,7 @@ int im_check_complex( const char *domain, IMAGE *im ); int im_check_format( const char *domain, IMAGE *im, VipsBandFmt fmt ); int im_check_mono( const char *domain, IMAGE *im ); int im_check_int( const char *domain, IMAGE *im ); +int im_check_u8or16( const char *domain, IMAGE *im ); int im_check_same_size( const char *domain, IMAGE *im1, IMAGE *im2 ); int im_check_same_bands( const char *domain, IMAGE *im1, IMAGE *im2 ); int im_check_same_format( const char *domain, IMAGE *im1, IMAGE *im2 ); diff --git a/libvips/include/vips/proto.h b/libvips/include/vips/proto.h index fbe1c5c0..acaa24eb 100644 --- a/libvips/include/vips/proto.h +++ b/libvips/include/vips/proto.h @@ -210,6 +210,7 @@ int im_maplut( IMAGE *, IMAGE *, IMAGE * ); int im_gammacorrect( IMAGE *, IMAGE *, double ); int im_heq( IMAGE *in, IMAGE *out, int bandno ); int im_hist( IMAGE *in, IMAGE *out, int bandno ); +int im_hist_indexed( IMAGE *index, IMAGE *value, IMAGE *out ); int im_histeq( IMAGE *in, IMAGE *out ); int im_histnorm( IMAGE *in, IMAGE *out ); int im_histcum( IMAGE *in, IMAGE *out ); diff --git a/libvips/iofuncs/check.c b/libvips/iofuncs/check.c index 57b86ca5..507640cd 100644 --- a/libvips/iofuncs/check.c +++ b/libvips/iofuncs/check.c @@ -737,6 +737,31 @@ im_check_int( const char *domain, IMAGE *im ) return( 0 ); } +/** + * im_check_u8or16: + * @domain: the originating domain for the error message + * @im: image to check + * + * Check that the image is 8 or 16-bit unsigned integer. + * Otherwise set an error message + * and return non-zero. + * + * Returns: 0 if OK, -1 otherwise. + * + * See also: im_error(). + */ +int +im_check_u8or16( const char *domain, IMAGE *im ) +{ + if( im->BandFmt != IM_BANDFMT_UCHAR && + im->BandFmt != IM_BANDFMT_USHORT ) { + im_error( domain, "%s", + _( "image must be 8- or 16-bit unsigned integer" ) ); + return( -1 ); + } + + return( 0 ); +} /** * im_check_same_size: @@ -966,7 +991,6 @@ im_isscalar( IMAGE *im ) } } - /** * im_iscomplex: * @im: image to test diff --git a/libvips/iofuncs/im_iterate.c b/libvips/iofuncs/im_iterate.c index a5ef0df8..9762c953 100644 --- a/libvips/iofuncs/im_iterate.c +++ b/libvips/iofuncs/im_iterate.c @@ -73,10 +73,144 @@ #include #endif /*WITH_DMALLOC*/ +/* Track this stuff during an im_iterate(). + */ +typedef struct _Iterate { + IMAGE *im; + + /* We need a temp "p" image between the source image and us to + * make sure we can't damage the original. + */ + IMAGE *t; + + /* Store our sequence values in tg->thr[i]->a. The seq values in the + * regions are used by the im_copy() to t. + */ + im_threadgroup_t *tg; + + im_start_fn start; + im_generate_fn generate; + im_stop_fn stop; + void *b; + void *c; +} Iterate; + +/* Call all stop functions. + */ +static int +iterate_call_all_stop( Iterate *iter, im_threadgroup_t *tg ) +{ + int i; + + for( i = 0; i < tg->nthr; i++ ) { + if( tg->thr[i]->a && iter->stop ) { + if( iter->stop( tg->thr[i]->a, iter->b, iter->c ) ) + /* Drastic! + */ + im_error( "im_iterate", + _( "stop function failed " + "for image \"%s\"" ), + iter->im->filename ); + tg->thr[i]->a = NULL; + } + } + + return( 0 ); +} + +static void +iterate_free( Iterate *iter ) +{ + /* Check all the stop functions have been called. + */ + if( iter->tg ) { + int i; + + for( i = 0; i < iter->tg->nthr; i++ ) + g_assert( !iter->tg->thr[i]->a ); + } + + IM_FREEF( im_threadgroup_free, iter->tg ); + IM_FREEF( im_close, iter->t ); +} + +/* Call the start function for this thread, if necessary. + */ +static int +iterate_call_start( Iterate *iter, im_thread_t *thr ) +{ + if( !thr->a && iter->start ) { + g_mutex_lock( iter->t->sslock ); + thr->a = iter->start( iter->t, iter->b, iter->c ); + g_mutex_unlock( iter->t->sslock ); + + if( !thr->a ) { + im_error( "im_iterate", + _( "start function failed for image \"%s\"" ), + iter->im->filename ); + return( -1 ); + } + } + + return( 0 ); +} + +/* Our generate function. We need to call the user's start function from the + * worker thread so that any regions it makes are owned by the thread. + */ +static int +iterate_gen( REGION *reg, void *seq, void *a, void *b ) +{ + Iterate *iter = (Iterate *) a; + im_thread_t *thr = (im_thread_t *) b; + + /* Make sure the start function has run and we have the sequence value + * set. + */ + iterate_call_start( iter, thr ); + seq = thr->a; + + return( iter->generate( reg, seq, iter->b, iter->c ) ); +} + +static int +iterate_init( Iterate *iter, + IMAGE *im, + im_start_fn start, im_generate_fn generate, im_stop_fn stop, + void *b, void *c ) +{ + iter->im = im; + iter->t = NULL; + iter->tg = NULL; + iter->start = start; + iter->generate = generate; + iter->stop = stop; + iter->b = b; + iter->c = c; + + if( !(iter->t = im_open( "iterate", "p" )) || + im_copy( iter->im, iter->t ) || + !(iter->tg = im_threadgroup_create( iter->t )) ) { + iterate_free( iter ); + return( -1 ); + } + + iter->tg->work = iterate_gen; + iter->tg->inplace = 0; + +#ifdef DEBUG_IO + if( iter->tg->nthr > 1 ) + im_diagnostics( "im_iterate: using %d threads", + iter->tg->nthr ); +#endif /*DEBUG_IO*/ + + return( 0 ); +} + /* Loop over an image, preparing in parts with threads. */ static int -eval_to_image( im_threadgroup_t *tg, IMAGE *im ) +iterate_loop( Iterate *iter, im_threadgroup_t *tg, IMAGE *t ) { int x, y; Rect image; @@ -87,13 +221,13 @@ eval_to_image( im_threadgroup_t *tg, IMAGE *im ) image.left = 0; image.top = 0; - image.width = im->Xsize; - image.height = im->Ysize; + image.width = t->Xsize; + image.height = t->Ysize; /* Loop over or, attaching to all sub-parts in turn. */ - for( y = 0; y < im->Ysize; y += tg->ph ) - for( x = 0; x < im->Xsize; x += tg->pw ) { + for( y = 0; y < t->Ysize; y += tg->ph ) + for( x = 0; x < t->Xsize; x += tg->pw ) { im_thread_t *thr; Rect pos; Rect clipped; @@ -114,6 +248,11 @@ eval_to_image( im_threadgroup_t *tg, IMAGE *im ) thr->pos = clipped; + /* Other stuff we want passed to iterate_gen(). + */ + thr->b = iter; + thr->c = thr; + /* Start worker going. */ im_threadgroup_trigger( thr ); @@ -121,7 +260,7 @@ eval_to_image( im_threadgroup_t *tg, IMAGE *im ) /* Trigger any eval callbacks on our source image, * check for errors. */ - if( im__handle_eval( im, tg->pw, tg->ph ) || + if( im__handle_eval( t, tg->pw, tg->ph ) || im_threadgroup_iserror( tg ) ) { /* Don't kill threads yet ... we may want to * get some error stuff out of them. @@ -143,56 +282,6 @@ eval_to_image( im_threadgroup_t *tg, IMAGE *im ) return( 0 ); } -static int -iterate( im_threadgroup_t *tg, IMAGE *im, - im_start_fn start, im_generate_fn generate, im_stop_fn stop, - void *b, void *c ) -{ - int i; - int res; - -#ifdef DEBUG_IO - if( tg && tg->nthr > 1 ) - im_diagnostics( "im_iterate: using %d threads", tg->nthr ); -#endif /*DEBUG_IO*/ - - /* Call all the start functions, and pop in the sequence values. - */ - for( i = 0; i < tg->nthr; i++ ) { - if( start && !(tg->thr[i]->a = start( im, b, c )) ) { - im_error( "im_iterate", - _( "start function failed for image \"%s\"" ), - im->filename ); - return( -1 ); - } - tg->thr[i]->b = b; - tg->thr[i]->c = c; - } - - /* Loop and generate multi-thread. - */ - res = eval_to_image( tg, im ); - - /* Call all stop functions. - */ - for( i = 0; i < tg->nthr; i++ ) { - if( tg->thr[i]->a && stop ) { - /* Trigger the stop function. - */ - if( stop( tg->thr[i]->a, b, c ) ) - /* Drastic! - */ - im_error( "im_iterate", - _( "stop function failed " - "for image \"%s\"" ), - im->filename ); - tg->thr[i]->a = NULL; - } - } - - return( res ); -} - /** * im_iterate: * @im: scan over this image @@ -215,44 +304,30 @@ im_iterate( IMAGE *im, im_start_fn start, im_generate_fn generate, im_stop_fn stop, void *b, void *c ) { - IMAGE *t; - im_threadgroup_t *tg; + Iterate iter; int result; g_assert( !im_image_sanity( im ) ); - if( !(t = im_open( "iterate", "p" )) ) + if( iterate_init( &iter, im, start, generate, stop, b, c ) ) return( -1 ); - if( im_copy( im, t ) ) { - im_close( t ); - return( -1 ); - } - - if( !(tg = im_threadgroup_create( t )) ) { - im_close( t ); - return( -1 ); - } - tg->work = generate; - tg->inplace = 0; - -#ifdef DEBUG_IO - if( tg && tg->nthr > 1 ) - im_diagnostics( "im_iterate: using %d threads", tg->nthr ); -#endif /*DEBUG_IO*/ /* Signal start of eval. */ - if( im__start_eval( t ) ) + if( im__start_eval( iter.t ) ) { + iterate_free( &iter ); return( -1 ); + } - result = iterate( tg, t, start, generate, stop, b, c ); + /* Loop and generate multi-thread. + */ + result = iterate_loop( &iter, iter.tg, iter.t ); /* Signal end of eval. */ - result |= im__end_eval( t ); - - im_threadgroup_free( tg ); - im_close( t ); + result |= im__end_eval( iter.t ); + result |= iterate_call_all_stop( &iter, iter.tg ); + iterate_free( &iter ); return( result ); }