From eada4a7731e1a67175a674cc976e99a60d305261 Mon Sep 17 00:00:00 2001 From: Simon Harris Date: Tue, 27 Mar 2018 21:15:48 +1100 Subject: [PATCH] Adds MEAN and MEDIAN methods for region shrink --- libvips/foreign/dzsave.c | 4 +- libvips/foreign/vips2tiff.c | 4 +- libvips/include/vips/region.h | 15 ++-- libvips/iofuncs/region.c | 130 +++++++++++++++++++++++++++------- 4 files changed, 121 insertions(+), 32 deletions(-) diff --git a/libvips/foreign/dzsave.c b/libvips/foreign/dzsave.c index 44d3ed82..f9e98fcd 100644 --- a/libvips/foreign/dzsave.c +++ b/libvips/foreign/dzsave.c @@ -1401,10 +1401,10 @@ strip_shrink( Layer *layer ) /* None? All done. */ - if( vips_rect_isempty( &target ) ) + if( vips_rect_isempty( &target ) ) break; - (void) vips_region_shrink( from, to, &target ); + (void) vips_region_shrink( from, to, &target, VIPS_REGION_SHRINK_MEAN ); below->write_y += target.height; diff --git a/libvips/foreign/vips2tiff.c b/libvips/foreign/vips2tiff.c index 1350befe..20fb9e99 100644 --- a/libvips/foreign/vips2tiff.c +++ b/libvips/foreign/vips2tiff.c @@ -1389,10 +1389,10 @@ layer_strip_shrink( Layer *layer ) /* None? All done. */ - if( vips_rect_isempty( &target ) ) + if( vips_rect_isempty( &target ) ) break; - (void) vips_region_shrink( from, to, &target ); + (void) vips_region_shrink( from, to, &target, VIPS_REGION_SHRINK_MEAN ); below->write_y += target.height; diff --git a/libvips/include/vips/region.h b/libvips/include/vips/region.h index 3d5972c4..a1543abe 100644 --- a/libvips/include/vips/region.h +++ b/libvips/include/vips/region.h @@ -55,6 +55,12 @@ extern "C" { (G_TYPE_INSTANCE_GET_CLASS( (obj), \ VIPS_TYPE_REGION, VipsRegionClass )) +typedef enum { + VIPS_REGION_SHRINK_MEAN, + VIPS_REGION_SHRINK_MEDIAN, + VIPS_REGION_SHRINK_LAST +} VipsRegionShrink; + /* Sub-area of image. */ typedef struct _VipsRegion { @@ -115,13 +121,14 @@ void vips_region_paint( VipsRegion *reg, const VipsRect *r, int value ); void vips_region_paint_pel( VipsRegion *reg, const VipsRect *r, const VipsPel *ink ); void vips_region_black( VipsRegion *reg ); -void vips_region_copy( VipsRegion *reg, VipsRegion *dest, +void vips_region_copy( VipsRegion *reg, VipsRegion *dest, const VipsRect *r, int x, int y ); -int vips_region_shrink( VipsRegion *from, - VipsRegion *to, const VipsRect *target ); +int vips_region_shrink( VipsRegion *from, + VipsRegion *to, const VipsRect *target, + VipsRegionShrink method ); int vips_region_prepare( VipsRegion *reg, const VipsRect *r ); -int vips_region_prepare_to( VipsRegion *reg, +int vips_region_prepare_to( VipsRegion *reg, VipsRegion *dest, const VipsRect *r, int x, int y ); void vips_region_invalidate( VipsRegion *reg ); diff --git a/libvips/iofuncs/region.c b/libvips/iofuncs/region.c index 48337d44..8b348e25 100644 --- a/libvips/iofuncs/region.c +++ b/libvips/iofuncs/region.c @@ -1166,7 +1166,7 @@ vips_region_shrink_labpack( VipsRegion *from, } } -#define SHRINK_TYPE_INT( TYPE ) \ +#define SHRINK_TYPE_MEAN_INT( TYPE ) \ for( x = 0; x < target->width; x++ ) { \ TYPE *tp = (TYPE *) p; \ TYPE *tp1 = (TYPE *) (p + ls); \ @@ -1185,7 +1185,7 @@ vips_region_shrink_labpack( VipsRegion *from, q += ps; \ } -#define SHRINK_TYPE_FLOAT( TYPE ) \ +#define SHRINK_TYPE_MEAN_FLOAT( TYPE ) \ for( x = 0; x < target->width; x++ ) { \ TYPE *tp = (TYPE *) p; \ TYPE *tp1 = (TYPE *) (p + ls); \ @@ -1204,10 +1204,30 @@ vips_region_shrink_labpack( VipsRegion *from, q += ps; \ } +// This *DOES* gurantee the result is taken from one of the input values +// It therefore does *NOT* interpolate between the two middle values +// It is *NOT* stable with respect to the set of values +// It *IS* however stable with respect to the initial arrangement of values +#define SHRINK_TYPE_MEDIAN( TYPE ) \ + for( x = 0; x < target->width; x++ ) { \ + TYPE *tp = (TYPE *) p; \ + TYPE *tp1 = (TYPE *) (p + ls); \ + TYPE *tq = (TYPE *) q; \ + \ + for( z = 0; z < nb; z++ ) { \ + tq[z] = VIPS_MIN( VIPS_MAX( tp[z], tp[z + nb] ), VIPS_MAX( tp1[z], tp1[z + nb] ) ); \ + } \ + \ + /* Move on two pels in input. \ + */ \ + p += ps << 1; \ + q += ps; \ + } + /* Generate area @target in @to using pixels in @from. Non-complex. */ static void -vips_region_shrink_uncoded( VipsRegion *from, +vips_region_shrink_uncoded_mean( VipsRegion *from, VipsRegion *to, const VipsRect *target ) { int ls = VIPS_REGION_LSKIP( from ); @@ -1225,22 +1245,22 @@ vips_region_shrink_uncoded( VipsRegion *from, /* Process this line of pels. */ switch( from->im->BandFmt ) { - case VIPS_FORMAT_UCHAR: - SHRINK_TYPE_INT( unsigned char ); break; - case VIPS_FORMAT_CHAR: - SHRINK_TYPE_INT( signed char ); break; - case VIPS_FORMAT_USHORT: - SHRINK_TYPE_INT( unsigned short ); break; - case VIPS_FORMAT_SHORT: - SHRINK_TYPE_INT( signed short ); break; - case VIPS_FORMAT_UINT: - SHRINK_TYPE_INT( unsigned int ); break; - case VIPS_FORMAT_INT: - SHRINK_TYPE_INT( signed int ); break; - case VIPS_FORMAT_FLOAT: - SHRINK_TYPE_FLOAT( float ); break; - case VIPS_FORMAT_DOUBLE: - SHRINK_TYPE_FLOAT( double ); break; + case VIPS_FORMAT_UCHAR: + SHRINK_TYPE_MEAN_INT( unsigned char ); break; + case VIPS_FORMAT_CHAR: + SHRINK_TYPE_MEAN_INT( signed char ); break; + case VIPS_FORMAT_USHORT: + SHRINK_TYPE_MEAN_INT( unsigned short ); break; + case VIPS_FORMAT_SHORT: + SHRINK_TYPE_MEAN_INT( signed short ); break; + case VIPS_FORMAT_UINT: + SHRINK_TYPE_MEAN_INT( unsigned int ); break; + case VIPS_FORMAT_INT: + SHRINK_TYPE_MEAN_INT( signed int ); break; + case VIPS_FORMAT_FLOAT: + SHRINK_TYPE_MEAN_FLOAT( float ); break; + case VIPS_FORMAT_DOUBLE: + SHRINK_TYPE_MEAN_FLOAT( double ); break; default: g_assert_not_reached(); @@ -1248,6 +1268,67 @@ vips_region_shrink_uncoded( VipsRegion *from, } } +/* Generate area @target in @to using pixels in @from. Non-complex. + */ +static void +vips_region_shrink_uncoded_median( VipsRegion *from, + VipsRegion *to, const VipsRect *target ) +{ + int ls = VIPS_REGION_LSKIP( from ); + int ps = VIPS_IMAGE_SIZEOF_PEL( from->im ); + int nb = from->im->Bands; + + int x, y, z; + + for( y = 0; y < target->height; y++ ) { + VipsPel *p = VIPS_REGION_ADDR( from, + target->left * 2, (target->top + y) * 2 ); + VipsPel *q = VIPS_REGION_ADDR( to, + target->left, target->top + y ); + + /* Process this line of pels. + */ + switch( from->im->BandFmt ) { + case VIPS_FORMAT_UCHAR: + SHRINK_TYPE_MEDIAN( unsigned char ); break; + case VIPS_FORMAT_CHAR: + SHRINK_TYPE_MEDIAN( signed char ); break; + case VIPS_FORMAT_USHORT: + SHRINK_TYPE_MEDIAN( unsigned short ); break; + case VIPS_FORMAT_SHORT: + SHRINK_TYPE_MEDIAN( signed short ); break; + case VIPS_FORMAT_UINT: + SHRINK_TYPE_MEDIAN( unsigned int ); break; + case VIPS_FORMAT_INT: + SHRINK_TYPE_MEDIAN( signed int ); break; + case VIPS_FORMAT_FLOAT: + SHRINK_TYPE_MEDIAN( float ); break; + case VIPS_FORMAT_DOUBLE: + SHRINK_TYPE_MEDIAN( double ); break; + + default: + g_assert_not_reached(); + } + } +} + +/* Generate area @target in @to using pixels in @from. Non-complex. + */ +static void +vips_region_shrink_uncoded( VipsRegion *from, + VipsRegion *to, const VipsRect *target, VipsRegionShrink method ) +{ + switch( method ) { + case VIPS_REGION_SHRINK_MEAN: + vips_region_shrink_uncoded_mean( from, to, target ); break; + case VIPS_REGION_SHRINK_MEDIAN: + vips_region_shrink_uncoded_median( from, to, target ); break; + + default: + g_assert_not_reached(); + } +} + /* No point having an int path, this will always be horribly slow. */ #define SHRINK_ALPHA_TYPE( TYPE ) { \ @@ -1333,9 +1414,10 @@ vips_region_shrink_alpha( VipsRegion *from, /** * vips_region_shrink: - * @from: source region - * @to: (inout): destination region + * @from: source region + * @to: (inout): destination region * @target: #VipsRect of pixels you need to copy + * @method: #VipsRegionShrink method to use when generating target pixels * * Write the pixels @target in @to from the x2 larger area in @from. * Non-complex uncoded images and LABQ only. Images with alpha (see @@ -1344,7 +1426,7 @@ vips_region_shrink_alpha( VipsRegion *from, * See also: vips_region_copy(). */ int -vips_region_shrink( VipsRegion *from, VipsRegion *to, const VipsRect *target ) +vips_region_shrink( VipsRegion *from, VipsRegion *to, const VipsRect *target, VipsRegionShrink method ) { VipsImage *image = from->im; @@ -1355,10 +1437,10 @@ vips_region_shrink( VipsRegion *from, VipsRegion *to, const VipsRect *target ) if( vips_check_noncomplex( "vips_region_shrink", image ) ) return( -1 ); - if( vips_image_hasalpha( image ) ) + if( vips_image_hasalpha( image ) ) vips_region_shrink_alpha( from, to, target ); else - vips_region_shrink_uncoded( from, to, target ); + vips_region_shrink_uncoded( from, to, target, method ); } else vips_region_shrink_labpack( from, to, target );