add max and min to region_shrink

Add max and min to region_shrink. Useful with tiffsave and dzsave when
the image is (for example) line art. Thanks rgluskin.

See: https://github.com/libvips/libvips/issues/1490
This commit is contained in:
John Cupitt 2020-01-24 17:55:11 +00:00
parent df6ff62cde
commit c14d7c254b
5 changed files with 106 additions and 89 deletions

View File

@ -1,6 +1,7 @@
24/1/20 started 8.10.0
- more conformat IIIF output from dzsave [regisrob]
- add @id to dzsave to set IIIF id property [regisrob]
- add max and min to region shrink [rgluskin]
20/6/19 started 8.9.1
- don't use the new source loaders for new_from_file or new_from_buffer, it

View File

@ -60,6 +60,8 @@ extern "C" {
* @VIPS_REGION_SHRINK_MEAN: use the average
* @VIPS_REGION_SHRINK_MEDIAN: use the median
* @VIPS_REGION_SHRINK_MODE: use the mode
* @VIPS_REGION_SHRINK_MAX: use the maximum
* @VIPS_REGION_SHRINK_MIN: use the minimum
*
* How to calculate the output pixels when shrinking a 2x2 region.
*/
@ -67,6 +69,8 @@ typedef enum {
VIPS_REGION_SHRINK_MEAN,
VIPS_REGION_SHRINK_MEDIAN,
VIPS_REGION_SHRINK_MODE,
VIPS_REGION_SHRINK_MAX,
VIPS_REGION_SHRINK_MIN,
VIPS_REGION_SHRINK_LAST
} VipsRegionShrink;

View File

@ -903,6 +903,8 @@ vips_region_shrink_get_type( void )
{VIPS_REGION_SHRINK_MEAN, "VIPS_REGION_SHRINK_MEAN", "mean"},
{VIPS_REGION_SHRINK_MEDIAN, "VIPS_REGION_SHRINK_MEDIAN", "median"},
{VIPS_REGION_SHRINK_MODE, "VIPS_REGION_SHRINK_MODE", "mode"},
{VIPS_REGION_SHRINK_MAX, "VIPS_REGION_SHRINK_MAX", "max"},
{VIPS_REGION_SHRINK_MIN, "VIPS_REGION_SHRINK_MIN", "min"},
{VIPS_REGION_SHRINK_LAST, "VIPS_REGION_SHRINK_LAST", "last"},
{0, NULL, NULL}
};

View File

@ -611,7 +611,7 @@ vips_operation_get_flags( VipsOperation *operation )
void
vips_operation_class_print_usage( VipsOperationClass *operation_class )
{
char str[2048];
char str[4096];
VipsBuf buf = VIPS_BUF_STATIC( str );
operation_class->usage( operation_class, &buf );

View File

@ -1254,50 +1254,6 @@ vips_region_shrink_uncoded_mean( 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();
}
}
}
/* This method is implemented so as to perform well and to always select an
* output pixel from one of the input pixels. As such we make only the
* following guarantees:
@ -1334,50 +1290,94 @@ vips_region_shrink_uncoded_median( VipsRegion *from,
} \
}
/* Generate area @target in @to using pixels in @from. Non-complex.
*/
static void
vips_region_shrink_uncoded_mode( 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_MODE( unsigned char ); break;
case VIPS_FORMAT_CHAR:
SHRINK_TYPE_MODE( signed char ); break;
case VIPS_FORMAT_USHORT:
SHRINK_TYPE_MODE( unsigned short ); break;
case VIPS_FORMAT_SHORT:
SHRINK_TYPE_MODE( signed short ); break;
case VIPS_FORMAT_UINT:
SHRINK_TYPE_MODE( unsigned int ); break;
case VIPS_FORMAT_INT:
SHRINK_TYPE_MODE( signed int ); break;
case VIPS_FORMAT_FLOAT:
SHRINK_TYPE_MODE( float ); break;
case VIPS_FORMAT_DOUBLE:
SHRINK_TYPE_MODE( double ); break;
default:
g_assert_not_reached();
}
}
#define SHRINK_TYPE_MAX( 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_MAX( \
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; \
} \
}
#define SHRINK_TYPE_MIN( 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_MIN( tp[z], tp[z + nb] ), \
VIPS_MIN( tp1[z], tp1[z + nb] ) \
); \
} \
\
/* Move on two pels in input. \
*/ \
p += ps << 1; \
q += ps; \
} \
}
#define VIPS_REGION_SHRINK( OP ) \
static void \
vips_region_shrink_uncoded_ ## OP( 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_ ## OP( unsigned char ); break; \
case VIPS_FORMAT_CHAR: \
SHRINK_TYPE_ ## OP( signed char ); break; \
case VIPS_FORMAT_USHORT: \
SHRINK_TYPE_ ## OP( unsigned short ); break; \
case VIPS_FORMAT_SHORT: \
SHRINK_TYPE_ ## OP( signed short ); break; \
case VIPS_FORMAT_UINT: \
SHRINK_TYPE_ ## OP( unsigned int ); break; \
case VIPS_FORMAT_INT: \
SHRINK_TYPE_ ## OP( signed int ); break; \
case VIPS_FORMAT_FLOAT: \
SHRINK_TYPE_ ## OP( float ); break; \
case VIPS_FORMAT_DOUBLE: \
SHRINK_TYPE_ ## OP( double ); break; \
\
default: \
g_assert_not_reached(); \
} \
} \
}
VIPS_REGION_SHRINK( MAX );
VIPS_REGION_SHRINK( MIN );
VIPS_REGION_SHRINK( MODE );
VIPS_REGION_SHRINK( MEDIAN );
/* Generate area @target in @to using pixels in @from. Non-complex.
*/
static void
@ -1388,11 +1388,21 @@ vips_region_shrink_uncoded( VipsRegion *from,
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 );
vips_region_shrink_uncoded_MEDIAN( from, to, target );
break;
case VIPS_REGION_SHRINK_MODE:
vips_region_shrink_uncoded_mode( from, to, target );
vips_region_shrink_uncoded_MODE( from, to, target );
break;
case VIPS_REGION_SHRINK_MAX:
vips_region_shrink_uncoded_MAX( from, to, target );
break;
case VIPS_REGION_SHRINK_MIN:
vips_region_shrink_uncoded_MIN( from, to, target );
break;
default: