diff --git a/ChangeLog b/ChangeLog index 9b69524c..8ff3e18a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -49,7 +49,7 @@ and else parts - better im_check() functions - added im_flood_other() as start of simple segmentation operator -- added im_segment() +- added im_label_regions() - im_printlines(), im_debugim() deprecated (use im_vips2csv() instead) - meta, header, callback, error, region, check, generate, memory gtkdocs - removed printlines tool, vips2csv is much better @@ -67,7 +67,7 @@ - threadgroup no longer has any default action, you must attach a work function - added im_copy_file() -- added im_insertplaceset() +- added im_insertset() - im_insertplace() allows small to be outside big - added im__colour_difference(), colour ops now work on any image format - added im_col_display_get_table(), so display tables are now shared by name diff --git a/TODO b/TODO index 42efe4a0..6f243468 100644 --- a/TODO +++ b/TODO @@ -1,11 +1,9 @@ +- vips.c should drop "set" suffix from operators + - poor SMP scaling in benchmark avg is different? used to 120.134, now 120.151 -- segment should be in morph - -- insertplaceset should be with insert in conversion - - _raw() variants should be deprecated? - look through more sections for stuff in the wrong place diff --git a/libvips/colour/colour_dispatch.c b/libvips/colour/colour_dispatch.c index 735eb254..7e8b242a 100644 --- a/libvips/colour/colour_dispatch.c +++ b/libvips/colour/colour_dispatch.c @@ -86,7 +86,7 @@ * of a and b. * * You cannot perform calculations on LabQ images (they are - * tagged with IM_CODING_LABQ), though a few operations such as + * tagged with %IM_CODING_LABQ), though a few operations such as * im_extract_area() will work directly with them. * * @@ -118,7 +118,15 @@ * * XYZ * - * CIE XYZ colour space represented as a three-band #IM_BANDFMT_FLOAT + * CIE XYZ colour space represented as a three-band %IM_BANDFMT_FLOAT + * image. + * + * + * + * + * Yxy + * + * CIE Yxy colour space represented as a three-band %IM_BANDFMT_FLOAT * image. * * @@ -146,7 +154,7 @@ * * * - * LCh + * UCS * * A colour space based on the CMC(1:1) colour difference measurement. * This is a highly uniform colour space, much better than CIELAB for diff --git a/libvips/conversion/conver_dispatch.c b/libvips/conversion/conver_dispatch.c index ad5a91ee..0fcfd3fe 100644 --- a/libvips/conversion/conver_dispatch.c +++ b/libvips/conversion/conver_dispatch.c @@ -921,6 +921,47 @@ static im_function insert_desc = { insert_args /* Arg list */ }; +/* Args for im_insertset. + */ +static im_arg_desc insertset_args[] = { + IM_INPUT_IMAGE( "main" ), + IM_INPUT_IMAGE( "sub" ), + IM_OUTPUT_IMAGE( "out" ), + IM_INPUT_INTVEC( "x" ), + IM_INPUT_INTVEC( "y" ) +}; + +/* Call im_insertplaceset via arg vector. + */ +static int +insertset_vec( im_object *argv ) +{ + im_intvec_object *xv = (im_intvec_object *) argv[3]; + im_intvec_object *yv = (im_intvec_object *) argv[4]; + + if( xv->n != yv->n ) { + im_error( "im_insertset", "%s", + _( "vectors not same length" ) ); + return( -1 ); + } + + if( im_insertset( argv[0], argv[1], argv[2], xv->n, xv->vec, yv->vec ) ) + return( -1 ); + + return( 0 ); +} + +/* Description of im_insertset. + */ +static im_function insertset_desc = { + "im_insertset", /* Name */ + "insert sub into main at every position in x, y", + 0, /* Flags */ + insertset_vec, /* Dispatch function */ + IM_NUMBER( insertset_args ), /* Size of arg list */ + insertset_args /* Arg list */ +}; + /* Call im_insert_noexpand via arg vector. */ static int @@ -1376,6 +1417,7 @@ static im_function *conv_list[] = { &gbandjoin_desc, &grid_desc, &insert_desc, + &insertset_desc, &insert_noexpand_desc, &embed_desc, &lrjoin_desc, diff --git a/libvips/conversion/im_insert.c b/libvips/conversion/im_insert.c index 0d993f94..0e80a6a2 100644 --- a/libvips/conversion/im_insert.c +++ b/libvips/conversion/im_insert.c @@ -385,3 +385,28 @@ im_insert_noexpand( IMAGE *main, IMAGE *sub, IMAGE *out, int x, int y ) return( 0 ); } + +/* Insert sub repeatedly. This can be a lot quicker for large n, but will use + * (potentially) a lot of memory. + */ +int +im_insertset( IMAGE *main, IMAGE *sub, IMAGE *out, int n, int *x, int *y ) +{ + IMAGE *t; + int i; + + /* Copy to a memory image, zap that, then copy to out. + */ + if( !(t = im_open_local( out, "im_insertset", "t" )) || + im_copy( main, t ) ) + return( -1 ); + + for( i = 0; i < n; i++ ) + if( im_insertplace( t, sub, x[i], y[i] ) ) + return( -1 ); + + if( im_copy( t, out ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libvips/deprecated/deprecated_dispatch.c b/libvips/deprecated/deprecated_dispatch.c index a7519979..f308a5d1 100644 --- a/libvips/deprecated/deprecated_dispatch.c +++ b/libvips/deprecated/deprecated_dispatch.c @@ -361,6 +361,37 @@ static im_function icc_export_desc = { icc_export_args /* Arg list */ }; +/* Args for im_segment(). + */ +static im_arg_desc segment_args[] = { + IM_INPUT_IMAGE( "test" ), + IM_OUTPUT_IMAGE( "mask" ), + IM_OUTPUT_INT( "segments" ) +}; + +/* Call im_segment() via arg vector. + */ +static int +segment_vec( im_object *argv ) +{ + IMAGE *test = argv[0]; + IMAGE *mask = argv[1]; + int *serial = (int *) argv[2]; + + return( im_segment( test, mask, serial ) ); +} + +/* Description of im_segment(). + */ +static im_function segment_desc = { + "im_segment", /* Name */ + "number continuous regions in an image", + 0, /* Flags */ + segment_vec, /* Dispatch function */ + IM_NUMBER( segment_args ),/* Size of arg list */ + segment_args /* Arg list */ +}; + static int print_vec( im_object *argv ) { @@ -778,6 +809,7 @@ static im_function *deprecated_list[] = { &print_desc, &slice_desc, &bernd_desc, + &segment_desc, &line_desc, &thresh_desc, &similarity_area_desc, diff --git a/libvips/deprecated/rename.c b/libvips/deprecated/rename.c index e593ca1f..a2d8530d 100644 --- a/libvips/deprecated/rename.c +++ b/libvips/deprecated/rename.c @@ -226,3 +226,9 @@ im_icc_export( IMAGE *in, IMAGE *out, return( im_icc_export_depth( in, out, 8, output_profile_filename, intent ) ); } + +int +im_segment( IMAGE *test, IMAGE *mask, int *segments ) +{ + return( im_label_regions( test, mask, segments ) ); +} diff --git a/libvips/include/vips/conversion.h b/libvips/include/vips/conversion.h index 79273eed..f48a6664 100644 --- a/libvips/include/vips/conversion.h +++ b/libvips/include/vips/conversion.h @@ -94,6 +94,7 @@ int im_bandjoin( IMAGE *in1, IMAGE *in2, IMAGE *out ); int im_gbandjoin( IMAGE **in, IMAGE *out, int n ); int im_insert( IMAGE *main, IMAGE *sub, IMAGE *out, int x, int y ); int im_insert_noexpand( IMAGE *main, IMAGE *sub, IMAGE *out, int x, int y ); +int im_insertset( IMAGE *main, IMAGE *sub, IMAGE *out, int n, int *x, int *y ); int im_lrjoin( IMAGE *in1, IMAGE *in2, IMAGE *out ); int im_tbjoin( IMAGE *in1, IMAGE *in2, IMAGE *out ); int im_replicate( IMAGE *in, IMAGE *out, int across, int down ); diff --git a/libvips/include/vips/deprecated.h b/libvips/include/vips/deprecated.h index 1244d5d0..c0e18991 100644 --- a/libvips/include/vips/deprecated.h +++ b/libvips/include/vips/deprecated.h @@ -350,6 +350,7 @@ int im_bernd( const char *tiffname, int x, int y, int w, int h ); int im_resize_linear( IMAGE *, IMAGE *, int, int ); int im_line( IMAGE *, int, int, int, int, int ); +int im_segment( IMAGE *test, IMAGE *mask, int *segments ); #ifdef __cplusplus } diff --git a/libvips/include/vips/inplace.h b/libvips/include/vips/inplace.h index c5187195..4a01a307 100644 --- a/libvips/include/vips/inplace.h +++ b/libvips/include/vips/inplace.h @@ -54,7 +54,7 @@ int im_flood_blob_copy( IMAGE *in, IMAGE *out, int x, int y, PEL *ink ); int im_flood_other( IMAGE *mask, IMAGE *test, int x, int y, int serial ); int im_flood_other_copy( IMAGE *mask, IMAGE *test, IMAGE *out, int x, int y, int serial ); -int im_segment( IMAGE *test, IMAGE *mask, int *segments ); + int im_lineset( IMAGE *in, IMAGE *out, IMAGE *mask, IMAGE *ink, int n, int *x1v, int *y1v, int *x2v, int *y2v ); diff --git a/libvips/include/vips/morphology.h b/libvips/include/vips/morphology.h index 1a6ac7cb..780916dc 100644 --- a/libvips/include/vips/morphology.h +++ b/libvips/include/vips/morphology.h @@ -50,6 +50,7 @@ int im_maxvalue( IMAGE **in, IMAGE *out, int n ); int im_cntlines( IMAGE *im, double *nolines, int flag ); int im_zerox( IMAGE *in, IMAGE *out, int flag ); int im_profile( IMAGE *in, IMAGE *out, int dir ); +int im_label_regions( IMAGE *test, IMAGE *mask, int *segments ); #ifdef __cplusplus } diff --git a/libvips/inplace/im_flood_other.c b/libvips/inplace/im_flood_other.c index 30f3bce9..fb7947d7 100644 --- a/libvips/inplace/im_flood_other.c +++ b/libvips/inplace/im_flood_other.c @@ -328,50 +328,3 @@ im_flood_other_copy( IMAGE *mask, IMAGE *test, IMAGE *out, return( 0 ); } - -/* Now: segment with im_flood_other(). - */ -int -im_segment( IMAGE *test, IMAGE *mask, int *segments ) -{ - IMAGE *t[2]; - int serial; - int *m; - int x, y; - - /* Create the zero mask image. - */ - if( im_open_local_array( mask, t, 2, "im_segment", "p" ) || - im_black( t[0], test->Xsize, test->Ysize, 1 ) || - im_clip2fmt( t[0], t[1], IM_BANDFMT_INT ) ) - return( -1 ); - - /* Search the mask image, flooding as we find zero pixels. - */ - if( im_incheck( t[1] ) ) - return( -1 ); - serial = 0; - m = (int *) t[1]->data; - for( y = 0; y < test->Ysize; y++ ) { - for( x = 0; x < test->Xsize; x++ ) { - if( !m[x] ) { - if( im_flood_other( t[1], test, x, y, serial ) ) - return( -1 ); - - serial += 1; - } - } - - m += test->Xsize; - } - - /* Copy result to mask. - */ - if( im_copy( t[1], mask ) ) - return( -1 ); - if( segments ) - *segments = serial; - - return( 0 ); -} - diff --git a/libvips/inplace/inplace_dispatch.c b/libvips/inplace/inplace_dispatch.c index 49b8cd1b..2a5fa686 100644 --- a/libvips/inplace/inplace_dispatch.c +++ b/libvips/inplace/inplace_dispatch.c @@ -162,55 +162,6 @@ static im_function lineset_desc = { lineset_args /* Arg list */ }; -/* Args for im_insertplaceset. - */ -static im_arg_desc insertplaceset_args[] = { - IM_INPUT_IMAGE( "main" ), - IM_INPUT_IMAGE( "sub" ), - IM_OUTPUT_IMAGE( "out" ), - IM_INPUT_INTVEC( "x" ), - IM_INPUT_INTVEC( "y" ) -}; - -/* Call im_insertplaceset via arg vector. - */ -static int -insertplaceset_vec( im_object *argv ) -{ - im_intvec_object *xv = (im_intvec_object *) argv[3]; - im_intvec_object *yv = (im_intvec_object *) argv[4]; - int i; - - if( xv->n != yv->n ) { - im_error( "im_insertplaceset", "%s", - _( "vectors not same length" ) ); - return( -1 ); - } - - /* Copy the image then repeatedly im_insertplace(). This will make - * "out" into a "t", usually. - */ - if( im_copy( argv[0], argv[2] ) ) - return( -1 ); - - for( i = 0; i < xv->n; i++ ) - if( im_insertplace( argv[2], argv[1], xv->vec[i], yv->vec[i] ) ) - return( -1 ); - - return( 0 ); -} - -/* Description of im_insertplaceset. - */ -static im_function insertplaceset_desc = { - "im_insertplaceset", /* Name */ - "insert sub into main at every position in x, y", - 0, /* Flags */ - insertplaceset_vec, /* Dispatch function */ - IM_NUMBER( insertplaceset_args ), /* Size of arg list */ - insertplaceset_args /* Arg list */ -}; - /* Calculate a pixel for an image from a vec of double. Valid while im is * valid. */ @@ -320,37 +271,6 @@ static im_function flood_other_copy_desc = { flood_other_copy_args /* Arg list */ }; -/* Args for im_segment(). - */ -static im_arg_desc segment_args[] = { - IM_INPUT_IMAGE( "test" ), - IM_OUTPUT_IMAGE( "mask" ), - IM_OUTPUT_INT( "segments" ) -}; - -/* Call im_segment() via arg vector. - */ -static int -segment_vec( im_object *argv ) -{ - IMAGE *test = argv[0]; - IMAGE *mask = argv[1]; - int *serial = (int *) argv[2]; - - return( im_segment( test, mask, serial ) ); -} - -/* Description of im_segment(). - */ -static im_function segment_desc = { - "im_segment", /* Name */ - "number continuous regions in an image", - 0, /* Flags */ - segment_vec, /* Dispatch function */ - IM_NUMBER( segment_args ),/* Size of arg list */ - segment_args /* Arg list */ -}; - /* To do: * these all need some kind of pel type * @@ -369,9 +289,7 @@ static im_function *inplace_list[] = { &circle_desc, &flood_blob_copy_desc, &flood_other_copy_desc, - &segment_desc, &insertplace_desc, - &insertplaceset_desc, &lineset_desc }; diff --git a/libvips/morphology/Makefile.am b/libvips/morphology/Makefile.am index c57a9dd1..9de3bb12 100644 --- a/libvips/morphology/Makefile.am +++ b/libvips/morphology/Makefile.am @@ -8,6 +8,7 @@ libmorphology_la_SOURCES = \ im_rank_image.c \ im_zerox.c \ morph_dispatch.c \ + im_label_regions.c \ im_profile.c INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/morphology/im_label_regions.c b/libvips/morphology/im_label_regions.c new file mode 100644 index 00000000..2113c11d --- /dev/null +++ b/libvips/morphology/im_label_regions.c @@ -0,0 +1,111 @@ +/* im_label_regions.c + * + * 5/11/09 + * - renamed from im_segment() + */ + +/* + + 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 + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +/** + * im_label_regions(): + * @test: image to test + * @mask: write labelled regions here + * @segments: return number of regions here + * + * The @test image is repeatedly scanned and regions of 4-connected pixels + * with the same pixel value found. Every time a region is discovered, those + * pixels are marked in @mask with a unique serial number. Once all pixels + * have been labelled, the operation returns, setting @segments to the number + * of discrete regions which were detected. + * + * @mask is always a 1-band %IM_BANDFMT_UINT image of the same dimensions as + * @test. + * + * This operation is useful for, for example, blob counting. You can use the + * morphological operators to detect and isolate a series of objects, then use + * im_label_regions() to number them all. + * + * Use im_histindexed() to (for example) find blob coordinates. + * + * See also: im_histindexed() + * + * Returns: 0 on success, -1 on error. + */ +int +im_label_regions( IMAGE *test, IMAGE *mask, int *segments ) +{ + IMAGE *t[2]; + int serial; + int *m; + int x, y; + + /* Create the zero mask image. + */ + if( im_open_local_array( mask, t, 2, "im_label_regions", "p" ) || + im_black( t[0], test->Xsize, test->Ysize, 1 ) || + im_clip2fmt( t[0], t[1], IM_BANDFMT_INT ) ) + return( -1 ); + + /* Search the mask image, flooding as we find zero pixels. + */ + serial = 0; + m = (int *) t[1]->data; + for( y = 0; y < test->Ysize; y++ ) { + for( x = 0; x < test->Xsize; x++ ) { + if( !m[x] ) { + if( im_flood_other( t[1], test, x, y, serial ) ) + return( -1 ); + + serial += 1; + } + } + + m += test->Xsize; + } + + /* Copy result to mask. + */ + if( im_copy( t[1], mask ) ) + return( -1 ); + if( segments ) + *segments = serial; + + return( 0 ); +} diff --git a/libvips/morphology/morph_dispatch.c b/libvips/morphology/morph_dispatch.c index fda7907a..14e88567 100644 --- a/libvips/morphology/morph_dispatch.c +++ b/libvips/morphology/morph_dispatch.c @@ -331,10 +331,41 @@ static im_function rank_raw_desc = { "rank filter nth element of xsize/ysize window, no border", IM_FN_PIO, /* Flags */ rank_raw_vec, /* Dispatch function */ - IM_NUMBER( rank_args ), /* Size of arg list */ + IM_NUMBER( rank_args ), /* Size of arg list */ rank_args /* Arg list */ }; +/* Args for im_label_regions(). + */ +static im_arg_desc label_regions_args[] = { + IM_INPUT_IMAGE( "test" ), + IM_OUTPUT_IMAGE( "mask" ), + IM_OUTPUT_INT( "segments" ) +}; + +/* Call im_label_regions() via arg vector. + */ +static int +label_regions_vec( im_object *argv ) +{ + IMAGE *test = argv[0]; + IMAGE *mask = argv[1]; + int *serial = (int *) argv[2]; + + return( im_label_regions( test, mask, serial ) ); +} + +/* Description of im_label_regions(). + */ +static im_function label_regions_desc = { + "im_label_regions", /* Name */ + "number continuous regions in an image", + 0, /* Flags */ + label_regions_vec, /* Dispatch function */ + IM_NUMBER( label_regions_args ),/* Size of arg list */ + label_regions_args /* Arg list */ +}; + /* Package up all these functions. */ static im_function *morph_list[] = { @@ -343,6 +374,7 @@ static im_function *morph_list[] = { &rank_desc, &rank_image_desc, &maxvalue_desc, + &label_regions_desc, &zerox_desc, &rank_raw_desc, &dilate_raw_desc,