diff --git a/ChangeLog b/ChangeLog index 2c27bbd3..e30ee507 100644 --- a/ChangeLog +++ b/ChangeLog @@ -37,7 +37,7 @@ - vips_colourspace() allows B_W, GREY16, RGB16 as source / target - added vips_thread_shutdown(), thanks Lovell - vips_linear() has a uchar output mode -- redone im_cntlines() as a class +- redone im_cntlines(), im_rank() as classes 9/1/14 started 7.36.6 - fix some clang compiler warnings diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index e7e627cd..452323a6 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -4497,3 +4497,20 @@ im_cntlines( VipsImage *im, double *nolines, int flag ) VIPS_DIRECTION_HORIZONTAL : VIPS_DIRECTION_VERTICAL, NULL ) ); } + +int +im_rank( IMAGE *in, IMAGE *out, int width, int height, int index ) +{ + VipsImage *x; + + if( vips_rank( in, &x, width, height, index, NULL ) ) + return( -1 ); + + if( im_copy( x, out ) ) { + g_object_unref( x ); + return( -1 ); + } + g_object_unref( x ); + + return( 0 ); +} diff --git a/libvips/include/vips/morphology.h b/libvips/include/vips/morphology.h index 6aea942c..8ff62451 100644 --- a/libvips/include/vips/morphology.h +++ b/libvips/include/vips/morphology.h @@ -42,12 +42,15 @@ int vips_countlines( VipsImage *in, double *nolines, VipsDirection direction, ... ) __attribute__((sentinel)); +int vips_rank( VipsImage *in, VipsImage **out, + int width, int height, int index, ... ) + __attribute__((sentinel)); +int vips_median( VipsImage *in, VipsImage **out, int size, ... ) + __attribute__((sentinel)); -int im_rank( VipsImage *in, VipsImage *out, int width, int height, int index ); - int im_zerox( VipsImage *in, VipsImage *out, int sign ); int im_label_regions( VipsImage *test, VipsImage *mask, int *segments ); diff --git a/libvips/include/vips/vips7compat.h b/libvips/include/vips/vips7compat.h index f62f4ddd..29d4f27f 100644 --- a/libvips/include/vips/vips7compat.h +++ b/libvips/include/vips/vips7compat.h @@ -976,6 +976,7 @@ int im_fractsurf( VipsImage *out, int size, double frd ); int im_phasecor_fft( VipsImage *in1, VipsImage *in2, VipsImage *out ); int im_cntlines( VipsImage *im, double *nolines, int flag ); +int im_rank( VipsImage *in, VipsImage *out, int width, int height, int index ); #ifdef __cplusplus } diff --git a/libvips/morphology/Makefile.am b/libvips/morphology/Makefile.am index 8afaab28..eb252ca3 100644 --- a/libvips/morphology/Makefile.am +++ b/libvips/morphology/Makefile.am @@ -3,8 +3,8 @@ noinst_LTLIBRARIES = libmorphology.la libmorphology_la_SOURCES = \ morphology.c \ countlines.c \ + rank.c \ hitmiss.c\ - im_rank.c \ im_zerox.c \ morph_dispatch.c \ im_label_regions.c diff --git a/libvips/morphology/im_rank.c b/libvips/morphology/im_rank.c deleted file mode 100644 index 1e4c2484..00000000 --- a/libvips/morphology/im_rank.c +++ /dev/null @@ -1,430 +0,0 @@ -/* Rank filter. - * - * Author: JC - * Written on: 19/8/96 - * Modified on: - * JC 20/8/96 - * - now uses insert-sort rather than bubble-sort - * - now works for any non-complex type - * JC 22/6/01 - * - oops, sanity check on n wrong - * JC 28/8/03 - * - cleanups - * - better selection algorithm ... same speed for 3x3, about 3x faster - * for 5x5, faster still for larger windows - * - index from zero for consistency with other parts of vips - * 7/4/04 - * - now uses im_embed() with edge stretching on the input, not - * the output - * - sets Xoffset / Yoffset - * 7/10/04 - * - oops, im_embed() size was wrong - * 10/11/10 - * - cleanups - * - gtk-doc - */ - -/* - - 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., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 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 - -/* Global state: save our parameters here. - */ -typedef struct { - IMAGE *in, *out; /* Images we run */ - int xsize, ysize; /* Window size */ - int index; /* Element select */ - int n; /* xsize * ysize */ -} RankInfo; - -/* Sequence value: just the array we sort in. - */ -typedef struct { - REGION *ir; - VipsPel *sort; -} SeqInfo; - -/* Free a sequence value. - */ -static int -rank_stop( void *vseq, void *a, void *b ) -{ - SeqInfo *seq = (SeqInfo *) vseq; - - IM_FREEF( im_region_free, seq->ir ); - - return( 0 ); -} - -/* Rank start function. - */ -static void * -rank_start( IMAGE *out, void *a, void *b ) -{ - IMAGE *in = (IMAGE *) a; - RankInfo *rnk = (RankInfo *) b; - SeqInfo *seq; - - if( !(seq = IM_NEW( out, SeqInfo )) ) - return( NULL ); - - /* Init! - */ - seq->ir = NULL; - seq->sort = NULL; - - /* Attach region and arrays. - */ - seq->ir = im_region_create( in ); - seq->sort = IM_ARRAY( out, - IM_IMAGE_SIZEOF_ELEMENT( in ) * rnk->n, VipsPel ); - if( !seq->ir || !seq->sort ) { - rank_stop( seq, in, rnk ); - return( NULL ); - } - - return( (void *) seq ); -} - -/* Inner loop for select-sorting TYPE. - */ -#define LOOP_SELECT( TYPE ) { \ - TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ - TYPE *p = (TYPE *) IM_REGION_ADDR( ir, le, y ); \ - TYPE *sort = (TYPE *) seq->sort; \ - TYPE a; \ - \ - for( x = 0; x < sz; x++ ) { \ - TYPE *d = p + x; \ - \ - /* Copy window into sort[]. - */ \ - for( k = 0, j = 0; j < rnk->ysize; j++ ) { \ - for( i = 0; i < eaw; i += bands, k++ ) \ - sort[k] = d[i]; \ - d += ls; \ - } \ - \ - /* Rearrange sort[] to make the index-th element the index-th - * smallest, adapted from Numerical Recipes in C. - */ \ - lower = 0; /* Range we know the result lies in */ \ - upper = rnk->n - 1; \ - for(;;) { \ - if( upper - lower < 2 ) { \ - /* 1 or 2 elements left. - */ \ - if( upper - lower == 1 && \ - sort[lower] > sort[upper] ) \ - IM_SWAP( TYPE, \ - sort[lower], sort[upper] ); \ - break; \ - } \ - else { \ - /* Pick mid-point of remaining elements. - */ \ - mid = (lower + upper) >> 1; \ - \ - /* Sort lower/mid/upper elements, hold - * midpoint in sort[lower + 1] for - * partitioning. - */ \ - IM_SWAP( TYPE, sort[lower + 1], sort[mid] ); \ - if( sort[lower] > sort[upper] ) \ - IM_SWAP( TYPE, \ - sort[lower], sort[upper] ); \ - if( sort[lower + 1] > sort[upper] ) \ - IM_SWAP( TYPE, \ - sort[lower + 1], sort[upper] );\ - if( sort[lower] > sort[lower + 1] ) \ - IM_SWAP( TYPE, \ - sort[lower], sort[lower + 1] );\ - \ - i = lower + 1; \ - j = upper; \ - a = sort[lower + 1]; \ - \ - for(;;) { \ - /* Search for out of order elements. - */ \ - do \ - i++; \ - while( sort[i] < a ); \ - do \ - j--; \ - while( sort[j] > a ); \ - if( j < i ) \ - break; \ - IM_SWAP( TYPE, sort[i], sort[j] ); \ - } \ - \ - /* Replace mid element. - */ \ - sort[lower + 1] = sort[j]; \ - sort[j] = a; \ - \ - /* Move to partition with the kth element. - */ \ - if( j >= rnk->index ) \ - upper = j - 1; \ - if( j <= rnk->index ) \ - lower = i; \ - } \ - } \ - \ - q[x] = sort[rnk->index]; \ - } \ -} - -/* Loop for find max of window. - */ -#define LOOP_MAX( TYPE ) { \ - TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ - TYPE *p = (TYPE *) IM_REGION_ADDR( ir, le, y ); \ - \ - for( x = 0; x < sz; x++ ) { \ - TYPE *d = &p[x]; \ - TYPE max; \ - \ - max = *d; \ - for( j = 0; j < rnk->ysize; j++ ) { \ - TYPE *e = d; \ - \ - for( i = 0; i < rnk->xsize; i++ ) { \ - if( *e > max ) \ - max = *e; \ - \ - e += bands; \ - } \ - \ - d += ls; \ - } \ - \ - q[x] = max; \ - } \ -} - -/* Loop for find min of window. - */ -#define LOOP_MIN( TYPE ) { \ - TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ - TYPE *p = (TYPE *) IM_REGION_ADDR( ir, le, y ); \ - \ - for( x = 0; x < sz; x++ ) { \ - TYPE *d = &p[x]; \ - TYPE min; \ - \ - min = *d; \ - for( j = 0; j < rnk->ysize; j++ ) { \ - TYPE *e = d; \ - \ - for( i = 0; i < rnk->xsize; i++ ) { \ - if( *e < min ) \ - min = *e; \ - \ - e += bands; \ - } \ - \ - d += ls; \ - } \ - \ - q[x] = min; \ - } \ -} - -#define SWITCH( OPERATION ) \ - switch( rnk->out->BandFmt ) { \ - case IM_BANDFMT_UCHAR: OPERATION( unsigned char ); break; \ - case IM_BANDFMT_CHAR: OPERATION( signed char ); break; \ - case IM_BANDFMT_USHORT: OPERATION( unsigned short ); break; \ - case IM_BANDFMT_SHORT: OPERATION( signed short ); break; \ - case IM_BANDFMT_UINT: OPERATION( unsigned int ); break; \ - case IM_BANDFMT_INT: OPERATION( signed int ); break; \ - case IM_BANDFMT_FLOAT: OPERATION( float ); break; \ - case IM_BANDFMT_DOUBLE: OPERATION( double ); break; \ - \ - default: \ - assert( 0 ); \ - } - -/* Rank of a REGION. - */ -static int -rank_gen( REGION *or, void *vseq, void *a, void *b ) -{ - SeqInfo *seq = (SeqInfo *) vseq; - IMAGE *in = (IMAGE *) a; - RankInfo *rnk = (RankInfo *) b; - REGION *ir = seq->ir; - - Rect *r = &or->valid; - Rect s; - int le = r->left; - int to = r->top; - int bo = IM_RECT_BOTTOM(r); - int sz = IM_REGION_N_ELEMENTS( or ); - - int ls; - int bands = in->Bands; - int eaw = rnk->xsize * bands; /* elements across window */ - - int x, y; - int i, j, k; - int upper, lower, mid; - - /* Prepare the section of the input image we need. A little larger - * than the section of the output image we are producing. - */ - s = *r; - s.width += rnk->xsize - 1; - s.height += rnk->ysize - 1; - if( im_prepare( ir, &s ) ) - return( -1 ); - ls = IM_REGION_LSKIP( ir ) / IM_IMAGE_SIZEOF_ELEMENT( in ); - - for( y = to; y < bo; y++ ) { - if( rnk->index == 0 ) - SWITCH( LOOP_MIN ) - else if( rnk->index == rnk->n - 1 ) - SWITCH( LOOP_MAX ) - else - SWITCH( LOOP_SELECT ) } - - return( 0 ); -} - -/* Rank filter. - */ -int -im_rank_raw( IMAGE *in, IMAGE *out, int xsize, int ysize, int index ) -{ - RankInfo *rnk; - - if( im_piocheck( in, out ) || - im_check_uncoded( "im_rank", in ) || - im_check_noncomplex( "im_rank", in ) ) - return( -1 ); - if( xsize > 1000 || ysize > 1000 || xsize <= 0 || ysize <= 0 || - index < 0 || index > xsize * ysize - 1 ) { - im_error( "im_rank", "%s", _( "bad parameters" ) ); - return( -1 ); - } - - /* Save parameters. - */ - if( !(rnk = IM_NEW( out, RankInfo )) ) - return( -1 ); - rnk->in = in; - rnk->out = out; - rnk->xsize = xsize; - rnk->ysize = ysize; - rnk->index = index; - rnk->n = xsize * ysize; - - /* Prepare output. Consider a 7x7 window and a 7x7 image --- the output - * would be 1x1. - */ - if( im_cp_desc( out, in ) ) - return( -1 ); - out->Xsize -= xsize - 1; - out->Ysize -= ysize - 1; - if( out->Xsize <= 0 || out->Ysize <= 0 ) { - im_error( "im_rank", "%s", _( "image too small for window" ) ); - return( -1 ); - } - - if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) ) - return( -1 ); - - /* Generate! - */ - if( im_generate( out, rank_start, rank_gen, rank_stop, in, rnk ) ) - return( -1 ); - - out->Xoffset = -xsize / 2; - out->Yoffset = -ysize / 2; - - return( 0 ); -} - - -/** - * im_rank: - * @in: input image - * @out: output image - * @width: window width - * @height: window height - * @index: select pixel - * - * im_rank() does rank filtering on an image. A window of size @width by - * @height is passed over the image. At each position, the pixels inside the - * window are sorted into ascending order and the pixel at position @index is - * output. @index numbers from 0. - * - * It works for any non-complex image type, with any number of bands. - * The input is expanded by copying edge pixels before performing the - * operation so that the output image has the same size as the input. - * Edge pixels in the output image are therefore only approximate. - * - * For a median filter with mask size m (3 for 3x3, 5 for 5x5, etc.) use - * - * im_rank( in, out, m, m, m * m / 2 ); - * - * The special cases n == 0 and n == m * m - 1 are useful dilate and - * expand operators. - * - * See also: im_conv(), im_fastcor(). - * - * Returns: 0 on success, -1 on error - */ -int -im_rank( IMAGE *in, IMAGE *out, int width, int height, int index ) -{ - IMAGE *t1; - - if( !(t1 = im_open_local( out, "im_rank", "p" )) || - im_embed( in, t1, 1, - width / 2, height / 2, - in->Xsize + width - 1, in->Ysize + height - 1 ) || - im_rank_raw( t1, out, width, height, index ) ) - return( -1 ); - - out->Xoffset = 0; - out->Yoffset = 0; - - return( 0 ); -} diff --git a/libvips/morphology/morphology.c b/libvips/morphology/morphology.c index 355b6848..095e4ec3 100644 --- a/libvips/morphology/morphology.c +++ b/libvips/morphology/morphology.c @@ -132,6 +132,8 @@ void vips_morphology_operation_init( void ) { extern int vips_countlines_get_type( void ); + extern int vips_rank_get_type( void ); vips_countlines_get_type(); + vips_rank_get_type(); } diff --git a/libvips/morphology/rank.c b/libvips/morphology/rank.c new file mode 100644 index 00000000..fdc47695 --- /dev/null +++ b/libvips/morphology/rank.c @@ -0,0 +1,512 @@ +/* Rank filter. + * + * Author: JC + * Written on: 19/8/96 + * Modified on: + * JC 20/8/96 + * - now uses insert-sort rather than bubble-sort + * - now works for any non-complex type + * JC 22/6/01 + * - oops, sanity check on n wrong + * JC 28/8/03 + * - cleanups + * - better selection algorithm ... same speed for 3x3, about 3x faster + * for 5x5, faster still for larger windows + * - index from zero for consistency with other parts of vips + * 7/4/04 + * - now uses im_embed() with edge stretching on the input, not + * the output + * - sets Xoffset / Yoffset + * 7/10/04 + * - oops, im_embed() size was wrong + * 10/11/10 + * - cleanups + * - gtk-doc + * 17/1/14 + * - redone as a class + */ + +/* + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 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 + +#include + +#include "pmorphology.h" + +typedef struct _VipsRank { + VipsMorphology parent_instance; + + VipsImage *out; + + int width; + int height; + int index; + + int n; + +} VipsRank; + +typedef VipsMorphologyClass VipsRankClass; + +G_DEFINE_TYPE( VipsRank, vips_rank, VIPS_TYPE_MORPHOLOGY ); + +/* Sequence value: just the array we sort in. + */ +typedef struct { + VipsRegion *ir; + VipsPel *sort; +} VipsRankSequence; + +static int +vips_rank_stop( void *vseq, void *a, void *b ) +{ + VipsRankSequence *seq = (VipsRankSequence *) vseq; + + VIPS_FREEF( g_object_unref, seq->ir ); + + return( 0 ); +} + +static void * +vips_rank_start( IMAGE *out, void *a, void *b ) +{ + VipsImage *in = (VipsImage *) a; + VipsRank *rank = (VipsRank *) b; + VipsRankSequence *seq; + + if( !(seq = VIPS_NEW( out, VipsRankSequence )) ) + return( NULL ); + seq->ir = NULL; + seq->sort = NULL; + + seq->ir = vips_region_new( in ); + if( !(seq->sort = VIPS_ARRAY( out, + VIPS_IMAGE_SIZEOF_ELEMENT( in ) * rank->n, VipsPel )) ) { + vips_rank_stop( seq, in, rank ); + return( NULL ); + } + + return( (void *) seq ); +} + +/* Inner loop for select-sorting TYPE. + */ +#define LOOP_SELECT( TYPE ) { \ + TYPE *q = (TYPE *) VIPS_REGION_ADDR( or, r->left, r->top + y ); \ + TYPE *p = (TYPE *) VIPS_REGION_ADDR( ir, r->left, r->top + y ); \ + TYPE *sort = (TYPE *) seq->sort; \ + TYPE a; \ + \ + for( x = 0; x < sz; x++ ) { \ + TYPE *d = p + x; \ + \ + /* Copy window into sort[]. + */ \ + for( k = 0, j = 0; j < rank->height; j++ ) { \ + for( i = 0; i < eaw; i += bands, k++ ) \ + sort[k] = d[i]; \ + d += ls; \ + } \ + \ + /* Rearrange sort[] to make the index-th element the index-th + * smallest, adapted from Numerical Recipes in C. + */ \ + lower = 0; /* Range we know the result lies in */ \ + upper = rank->n - 1; \ + for(;;) { \ + if( upper - lower < 2 ) { \ + /* 1 or 2 elements left. + */ \ + if( upper - lower == 1 && \ + sort[lower] > sort[upper] ) \ + VIPS_SWAP( TYPE, \ + sort[lower], sort[upper] ); \ + break; \ + } \ + else { \ + /* Pick mid-point of remaining elements. + */ \ + mid = (lower + upper) >> 1; \ + \ + /* Sort lower/mid/upper elements, hold + * midpoint in sort[lower + 1] for + * partitioning. + */ \ + VIPS_SWAP( TYPE, sort[lower + 1], sort[mid] ); \ + if( sort[lower] > sort[upper] ) \ + VIPS_SWAP( TYPE, \ + sort[lower], sort[upper] ); \ + if( sort[lower + 1] > sort[upper] ) \ + VIPS_SWAP( TYPE, \ + sort[lower + 1], sort[upper] );\ + if( sort[lower] > sort[lower + 1] ) \ + VIPS_SWAP( TYPE, \ + sort[lower], sort[lower + 1] );\ + \ + i = lower + 1; \ + j = upper; \ + a = sort[lower + 1]; \ + \ + for(;;) { \ + /* Search for out of order elements. + */ \ + do \ + i++; \ + while( sort[i] < a ); \ + do \ + j--; \ + while( sort[j] > a ); \ + if( j < i ) \ + break; \ + VIPS_SWAP( TYPE, sort[i], sort[j] ); \ + } \ + \ + /* Replace mid element. + */ \ + sort[lower + 1] = sort[j]; \ + sort[j] = a; \ + \ + /* Move to partition with the kth element. + */ \ + if( j >= rank->index ) \ + upper = j - 1; \ + if( j <= rank->index ) \ + lower = i; \ + } \ + } \ + \ + q[x] = sort[rank->index]; \ + } \ +} + +/* Loop for find max of window. + */ +#define LOOP_MAX( TYPE ) { \ + TYPE *q = (TYPE *) VIPS_REGION_ADDR( or, r->left, r->top + y ); \ + TYPE *p = (TYPE *) VIPS_REGION_ADDR( ir, r->left, r->top + y ); \ + \ + for( x = 0; x < sz; x++ ) { \ + TYPE *d = &p[x]; \ + TYPE max; \ + \ + max = *d; \ + for( j = 0; j < rank->height; j++ ) { \ + TYPE *e = d; \ + \ + for( i = 0; i < rank->width; i++ ) { \ + if( *e > max ) \ + max = *e; \ + \ + e += bands; \ + } \ + \ + d += ls; \ + } \ + \ + q[x] = max; \ + } \ +} + +/* Loop for find min of window. + */ +#define LOOP_MIN( TYPE ) { \ + TYPE *q = (TYPE *) VIPS_REGION_ADDR( or, r->left, r->top + y ); \ + TYPE *p = (TYPE *) VIPS_REGION_ADDR( ir, r->left, r->top + y ); \ + \ + for( x = 0; x < sz; x++ ) { \ + TYPE *d = &p[x]; \ + TYPE min; \ + \ + min = *d; \ + for( j = 0; j < rank->height; j++ ) { \ + TYPE *e = d; \ + \ + for( i = 0; i < rank->width; i++ ) { \ + if( *e < min ) \ + min = *e; \ + \ + e += bands; \ + } \ + \ + d += ls; \ + } \ + \ + q[x] = min; \ + } \ +} + +#define SWITCH( OPERATION ) \ + switch( rank->out->BandFmt ) { \ + case VIPS_FORMAT_UCHAR: OPERATION( unsigned char ); break; \ + case VIPS_FORMAT_CHAR: OPERATION( signed char ); break; \ + case VIPS_FORMAT_USHORT: OPERATION( unsigned short ); break; \ + case VIPS_FORMAT_SHORT: OPERATION( signed short ); break; \ + case VIPS_FORMAT_UINT: OPERATION( unsigned int ); break; \ + case VIPS_FORMAT_INT: OPERATION( signed int ); break; \ + case VIPS_FORMAT_FLOAT: OPERATION( float ); break; \ + case VIPS_FORMAT_DOUBLE: OPERATION( double ); break; \ + \ + default: \ + g_assert( 0 ); \ + } + +static int +vips_rank_generate( VipsRegion *or, + void *vseq, void *a, void *b, gboolean *stop ) +{ + VipsRect *r = &or->valid; + VipsRankSequence *seq = (VipsRankSequence *) vseq; + VipsRegion *ir = seq->ir; + VipsImage *in = (VipsImage *) a; + VipsRank *rank = (VipsRank *) b; + int bands = in->Bands; + int eaw = rank->width * bands; /* elements across window */ + int sz = VIPS_REGION_N_ELEMENTS( or ); + + VipsRect s; + int ls; + + int x, y; + int i, j, k; + int upper, lower, mid; + + /* Prepare the section of the input image we need. A little larger + * than the section of the output image we are producing. + */ + s = *r; + s.width += rank->width - 1; + s.height += rank->height - 1; + if( vips_region_prepare( ir, &s ) ) + return( -1 ); + ls = VIPS_REGION_LSKIP( ir ) / VIPS_IMAGE_SIZEOF_ELEMENT( in ); + + for( y = 0; y < r->height; y++ ) { + if( rank->index == 0 ) + SWITCH( LOOP_MIN ) + else if( rank->index == rank->n - 1 ) + SWITCH( LOOP_MAX ) + else + SWITCH( LOOP_SELECT ) } + + return( 0 ); +} + +static int +vips_rank_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsMorphology *morphology = VIPS_MORPHOLOGY( object ); + VipsRank *rank = (VipsRank *) object; + VipsImage **t = (VipsImage **) vips_object_local_array( object, 3 ); + + VipsImage *in; + + if( VIPS_OBJECT_CLASS( vips_rank_parent_class )->build( object ) ) + return( -1 ); + + in = morphology->in; + + if( vips_check_uncoded( class->nickname, in ) || + vips_check_noncomplex( class->nickname, in ) ) + return( -1 ); + if( rank->width > in->Xsize || + rank->height > in->Ysize ) { + vips_error( class->nickname, "%s", _( "window too large" ) ); + return( -1 ); + } + rank->n = rank->width * rank->height; + if( rank->index < 0 || rank->index > rank->n - 1 ) { + vips_error( class->nickname, "%s", _( "index out of range" ) ); + return( -1 ); + } + + /* Expand the input. + */ + if( vips_embed( in, &t[0], + rank->width / 2, rank->height / 2, + in->Xsize + rank->width - 1, in->Ysize + rank->height - 1, + "extend", VIPS_EXTEND_COPY, + NULL ) ) + return( -1 ); + in = t[0]; + + g_object_set( object, "out", vips_image_new(), NULL ); + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + if( vips_image_pipelinev( rank->out, + VIPS_DEMAND_STYLE_FATSTRIP, in, NULL ) ) + return( -1 ); + rank->out->Xsize -= rank->width - 1; + rank->out->Ysize -= rank->height - 1; + + if( vips_image_generate( rank->out, + vips_rank_start, + vips_rank_generate, + vips_rank_stop, + in, rank ) ) + return( -1 ); + + rank->out->Xoffset = 0; + rank->out->Yoffset = 0; + + return( 0 ); +} + +static void +vips_rank_class_init( VipsRankClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "rank"; + object_class->description = _( "rank filter" ); + object_class->build = vips_rank_build; + + VIPS_ARG_IMAGE( class, "out", 2, + _( "Output" ), + _( "Output image" ), + VIPS_ARGUMENT_REQUIRED_OUTPUT, + G_STRUCT_OFFSET( VipsRank, out ) ); + + VIPS_ARG_INT( class, "width", 4, + _( "Width" ), + _( "Window width in pixels" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsRank, width ), + 1, 100000, 11 ); + + VIPS_ARG_INT( class, "height", 5, + _( "Height" ), + _( "Window height in pixels" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsRank, height ), + 1, 100000, 11 ); + + VIPS_ARG_INT( class, "index", 6, + _( "index" ), + _( "Select pixel at index" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsRank, index ), + 1, 100000000, 50 ); + +} + +static void +vips_rank_init( VipsRank *rank ) +{ + rank->width = 11; + rank->height = 11; + rank->index = 50; +} + +/** + * vips_rank: + * @in: input image + * @out: output image + * @width: width of region + * @height: height of region + * @index: select pixel + * @...: %NULL-terminated list of optional named arguments + * + * vips_rank() does rank filtering on an image. A window of size @width by + * @height is passed over the image. At each position, the pixels inside the + * window are sorted into ascending order and the pixel at position @index is + * output. @index numbers from 0. + * + * It works for any non-complex image type, with any number of bands. + * The input is expanded by copying edge pixels before performing the + * operation so that the output image has the same size as the input. + * Edge pixels in the output image are therefore only approximate. + * + * For a median filter with mask size m (3 for 3x3, 5 for 5x5, etc.) use + * + * vips_rank( in, out, m, m, m * m / 2 ); + * + * The special cases n == 0 and n == m * m - 1 are useful dilate and + * expand operators. + * + * See also: vips_conv(), vips_median(), vips_spcor(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_rank( VipsImage *in, VipsImage **out, + int width, int height, int index, ... ) +{ + va_list ap; + int result; + + va_start( ap, index ); + result = vips_call_split( "rank", ap, in, out, width, height, index ); + va_end( ap ); + + return( result ); +} + +/** + * vips_median: + * @in: input image + * @out: output image + * @size: size of region + * @...: %NULL-terminated list of optional named arguments + * + * A convenience function equivalent to: + * + * vips_rank( in, out, size, size, (size * size) / 2 ); + * + * See also: vips_rank(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_median( VipsImage *in, VipsImage **out, int size, ... ) +{ + va_list ap; + int result; + + va_start( ap, size ); + result = vips_call_split( "rank", ap, in, out, + size, size, (size * size) / 2 ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/resample/shrink.c b/libvips/resample/shrink.c index 5bc996c5..d15731e7 100644 --- a/libvips/resample/shrink.c +++ b/libvips/resample/shrink.c @@ -137,8 +137,6 @@ vips_shrink_start( VipsImage *out, void *a, void *b ) if( !(seq = IM_NEW( out, VipsShrinkSequence )) ) return( NULL ); - /* Init! - */ seq->ir = vips_region_new( in ); if( !(seq->sum = (VipsPel *) VIPS_ARRAY( out, in->Bands, double )) ) { vips_shrink_stop( seq, in, shrink );