first attempt

seems to all work, test it a bit more

see https://github.com/jcupitt/libvips/issues/491
This commit is contained in:
John Cupitt 2016-08-15 15:45:26 +01:00
parent 91e31cb830
commit 1f403a4add
14 changed files with 294 additions and 181 deletions

View File

@ -37,6 +37,8 @@
- support --strip for pngsave
- add svgz support [Felix Bünemann]
- rename boostrap.sh -> autogen.sh to help snapcraft
- resize/reduce/shrink now round output size to nearest rather than rounding
down, thanks ioquatix
30/7/16 started 8.3.3
- fix performance regression in 8.3.2, thanks Lovell

View File

@ -92,7 +92,6 @@ Debug build:
Leak check:
$ export G_DEBUG=gc-friendly
$ export G_SLICE=always-malloc
$ valgrind --suppressions=libvips.supp \
--leak-check=yes \
vips ... > vips-vg.log 2>&1

11
TODO
View File

@ -1,3 +1,14 @@
- strange:
$ vips similarity --scale 0.33 k2.jpg x.v
$ vipsheader k2.jpg
k2.jpg: 1450x2048 uchar, 3 bands, srgb, jpegload
$ vipsheader x.v
x.v: 478x676 uchar, 3 bands, srgb, jpegload
1450 * 0.33 = 478.5 ... this was rounded down
2048 * 0.33 = 675.84 ... this was rounded up
- add APPROX convsep test?
- add more webp tests to py suite

View File

@ -48,15 +48,20 @@ typedef enum {
} VipsKernel;
int vips_shrink( VipsImage *in, VipsImage **out,
double xshrink, double yshrink, ... )
double hshrink, double vshrink, ... )
__attribute__((sentinel));
int vips_shrinkh( VipsImage *in, VipsImage **out, int hshrink, ... )
__attribute__((sentinel));
int vips_shrinkv( VipsImage *in, VipsImage **out, int vshrink, ... )
__attribute__((sentinel));
int vips_shrinkh( VipsImage *in, VipsImage **out, int xshrink, ... );
int vips_shrinkv( VipsImage *in, VipsImage **out, int yshrink, ... );
int vips_reduce( VipsImage *in, VipsImage **out,
double xshrink, double yshrink, ... );
int vips_reduceh( VipsImage *in, VipsImage **out, double xshrink, ... );
int vips_reducev( VipsImage *in, VipsImage **out, double yshrink, ... );
double hshrink, double vshrink, ... )
__attribute__((sentinel));
int vips_reduceh( VipsImage *in, VipsImage **out, double hshrink, ... )
__attribute__((sentinel));
int vips_reducev( VipsImage *in, VipsImage **out, double vshrink, ... )
__attribute__((sentinel));
int vips_similarity( VipsImage *in, VipsImage **out, ... )
__attribute__((sentinel));

View File

@ -699,7 +699,11 @@ vips_operation_set_valist_required( VipsOperation *operation, va_list ap )
g_assert( argument_instance );
if( (argument_class->flags & VIPS_ARGUMENT_REQUIRED) ) {
/* We skip deprecated required args. There will be a new,
* renamed arg in the same place.
*/
if( (argument_class->flags & VIPS_ARGUMENT_REQUIRED) &&
!(argument_class->flags & VIPS_ARGUMENT_DEPRECATED) ) {
VIPS_ARGUMENT_COLLECT_SET( pspec, argument_class, ap );
#ifdef VIPS_DEBUG

View File

@ -2,6 +2,8 @@
*
* 27/1/16
* - from shrink.c
* 15/8/16
* - rename xshrink -> hshrink for greater consistency
*/
/*
@ -66,8 +68,8 @@
typedef struct _VipsReduce {
VipsResample parent_instance;
double xshrink; /* Shrink factors */
double yshrink;
double hshrink; /* Shrink factors */
double vshrink;
/* The thing we use to make the kernel.
*/
@ -90,10 +92,10 @@ vips_reduce_build( VipsObject *object )
if( VIPS_OBJECT_CLASS( vips_reduce_parent_class )->build( object ) )
return( -1 );
if( vips_reducev( resample->in, &t[0], reduce->yshrink,
if( vips_reducev( resample->in, &t[0], reduce->vshrink,
"kernel", reduce->kernel,
NULL ) ||
vips_reduceh( t[0], &t[1], reduce->xshrink,
vips_reduceh( t[0], &t[1], reduce->hshrink,
"kernel", reduce->kernel,
NULL ) ||
vips_image_write( t[1], resample->out ) )
@ -120,18 +122,18 @@ vips_reduce_class_init( VipsReduceClass *class )
operation_class->flags = VIPS_OPERATION_SEQUENTIAL;
VIPS_ARG_DOUBLE( class, "xshrink", 8,
_( "Xshrink" ),
VIPS_ARG_DOUBLE( class, "hshrink", 8,
_( "Hshrink" ),
_( "Horizontal shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsReduce, xshrink ),
G_STRUCT_OFFSET( VipsReduce, hshrink ),
1.0, 1000000.0, 1.0 );
VIPS_ARG_DOUBLE( class, "yshrink", 9,
_( "Yshrink" ),
VIPS_ARG_DOUBLE( class, "vshrink", 9,
_( "Vshrink" ),
_( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsReduce, yshrink ),
G_STRUCT_OFFSET( VipsReduce, vshrink ),
1.0, 1000000.0, 1.0 );
VIPS_ARG_ENUM( class, "kernel", 3,
@ -141,6 +143,22 @@ vips_reduce_class_init( VipsReduceClass *class )
G_STRUCT_OFFSET( VipsReduce, kernel ),
VIPS_TYPE_KERNEL, VIPS_KERNEL_LANCZOS3 );
/* The old names .. now use h and v everywhere.
*/
VIPS_ARG_DOUBLE( class, "xshrink", 8,
_( "Xshrink" ),
_( "Horizontal shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsReduce, hshrink ),
1.0, 1000000.0, 1.0 );
VIPS_ARG_DOUBLE( class, "yshrink", 9,
_( "Yshrink" ),
_( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsReduce, vshrink ),
1.0, 1000000.0, 1.0 );
}
static void
@ -153,8 +171,8 @@ vips_reduce_init( VipsReduce *reduce )
* vips_reduce:
* @in: input image
* @out: output image
* @xshrink: horizontal shrink
* @yshrink: vertical shrink
* @hshrink: horizontal shrink
* @vshrink: vertical shrink
* @...: %NULL-terminated list of optional named arguments
*
* Optional arguments:
@ -176,13 +194,13 @@ vips_reduce_init( VipsReduce *reduce )
*/
int
vips_reduce( VipsImage *in, VipsImage **out,
double xshrink, double yshrink, ... )
double hshrink, double vshrink, ... )
{
va_list ap;
int result;
va_start( ap, yshrink );
result = vips_call_split( "reduce", ap, in, out, xshrink, yshrink );
va_start( ap, vshrink );
result = vips_call_split( "reduce", ap, in, out, hshrink, vshrink );
va_end( ap );
return( result );

View File

@ -4,6 +4,8 @@
* - from shrinkh.c
* 10/3/16
* - add other kernels
* 15/8/16
* - rename xshrink as hshrink for consistency
*/
/*
@ -67,7 +69,7 @@
typedef struct _VipsReduceh {
VipsResample parent_instance;
double xshrink; /* Reduce factor */
double hshrink; /* Reduce factor */
/* The thing we use to make the kernel.
*/
@ -276,7 +278,7 @@ reduceh_notab( VipsReduceh *reduceh,
double cx[MAX_POINT];
vips_reduce_make_mask( cx, reduceh->kernel, reduceh->xshrink, x );
vips_reduce_make_mask( cx, reduceh->kernel, reduceh->hshrink, x );
for( int z = 0; z < bands; z++ ) {
out[z] = reduce_sum<T, double>( in, bands, cx, n );
@ -311,9 +313,9 @@ vips_reduceh_gen( VipsRegion *out_region, void *seq,
r->width, r->height, r->left, r->top );
#endif /*DEBUG*/
s.left = r->left * reduceh->xshrink;
s.left = r->left * reduceh->hshrink;
s.top = r->top;
s.width = r->width * reduceh->xshrink + reduceh->n_point;
s.width = r->width * reduceh->hshrink + reduceh->n_point;
s.height = r->height;
if( vips_region_prepare( ir, &s ) )
return( -1 );
@ -328,7 +330,7 @@ vips_reduceh_gen( VipsRegion *out_region, void *seq,
q = VIPS_REGION_ADDR( out_region, r->left, r->top + y );
X = r->left * reduceh->xshrink;
X = r->left * reduceh->hshrink;
/* We want p0 to be the start (ie. x == 0) of the input
* scanline we are reading from. We can then calculate the p we
@ -411,7 +413,7 @@ vips_reduceh_gen( VipsRegion *out_region, void *seq,
break;
}
X += reduceh->xshrink;
X += reduceh->hshrink;
q += ps;
}
}
@ -439,19 +441,19 @@ vips_reduceh_build( VipsObject *object )
in = resample->in;
if( reduceh->xshrink < 1 ) {
if( reduceh->hshrink < 1 ) {
vips_error( object_class->nickname,
"%s", _( "reduce factors should be >= 1" ) );
return( -1 );
}
if( reduceh->xshrink == 1 )
if( reduceh->hshrink == 1 )
return( vips_image_write( in, resample->out ) );
/* Build the tables of pre-computed coefficients.
*/
reduceh->n_point =
vips_reduce_get_points( reduceh->kernel, reduceh->xshrink );
vips_reduce_get_points( reduceh->kernel, reduceh->hshrink );
vips_info( object_class->nickname, "%d point mask", reduceh->n_point );
if( reduceh->n_point > MAX_POINT ) {
vips_error( object_class->nickname,
@ -468,7 +470,7 @@ vips_reduceh_build( VipsObject *object )
return( -1 );
vips_reduce_make_mask( reduceh->matrixf[x],
reduceh->kernel, reduceh->xshrink,
reduceh->kernel, reduceh->hshrink,
(float) x / VIPS_TRANSFORM_SCALE );
for( int i = 0; i < reduceh->n_point; i++ )
@ -503,7 +505,7 @@ vips_reduceh_build( VipsObject *object )
* fractional part), we just see the integer part here.
*/
resample->out->Xsize = VIPS_RINT(
(in->Xsize - reduceh->n_point + 1) / reduceh->xshrink );
(in->Xsize - reduceh->n_point + 1) / reduceh->hshrink );
if( resample->out->Xsize <= 0 ) {
vips_error( object_class->nickname,
"%s", _( "image has shrunk to nothing" ) );
@ -543,11 +545,11 @@ vips_reduceh_class_init( VipsReducehClass *reduceh_class )
operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED;
VIPS_ARG_DOUBLE( reduceh_class, "xshrink", 3,
_( "Xshrink" ),
VIPS_ARG_DOUBLE( reduceh_class, "hshrink", 3,
_( "Hshrink" ),
_( "Horizontal shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsReduceh, xshrink ),
G_STRUCT_OFFSET( VipsReduceh, hshrink ),
1, 1000000, 1 );
VIPS_ARG_ENUM( reduceh_class, "kernel", 3,
@ -557,6 +559,15 @@ vips_reduceh_class_init( VipsReducehClass *reduceh_class )
G_STRUCT_OFFSET( VipsReduceh, kernel ),
VIPS_TYPE_KERNEL, VIPS_KERNEL_LANCZOS3 );
/* Old name.
*/
VIPS_ARG_DOUBLE( reduceh_class, "xshrink", 3,
_( "Xshrink" ),
_( "Horizontal shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsReduceh, hshrink ),
1, 1000000, 1 );
}
static void
@ -569,7 +580,7 @@ vips_reduceh_init( VipsReduceh *reduceh )
* vips_reduceh:
* @in: input image
* @out: output image
* @xshrink: horizontal reduce
* @hshrink: horizontal reduce
* @...: %NULL-terminated list of optional named arguments
*
* Optional arguments:
@ -590,13 +601,13 @@ vips_reduceh_init( VipsReduceh *reduceh )
* Returns: 0 on success, -1 on error
*/
int
vips_reduceh( VipsImage *in, VipsImage **out, double xshrink, ... )
vips_reduceh( VipsImage *in, VipsImage **out, double hshrink, ... )
{
va_list ap;
int result;
va_start( ap, xshrink );
result = vips_call_split( "reduceh", ap, in, out, xshrink );
va_start( ap, hshrink );
result = vips_call_split( "reduceh", ap, in, out, hshrink );
va_end( ap );
return( result );

View File

@ -11,6 +11,8 @@
* equal to the target scale
* 15/6/16
* - better accuracy with smarter multiplication
* 15/8/16
* - rename yshrink as vshrink for consistency
*/
/*
@ -92,7 +94,7 @@ typedef struct {
typedef struct _VipsReducev {
VipsResample parent_instance;
double yshrink; /* Shrink factor */
double vshrink; /* Shrink factor */
/* The thing we use to make the kernel.
*/
@ -491,7 +493,7 @@ reducev_notab( VipsReducev *reducev,
double cy[MAX_POINT];
vips_reduce_make_mask( cy, reducev->kernel, reducev->yshrink, y );
vips_reduce_make_mask( cy, reducev->kernel, reducev->vshrink, y );
for( int z = 0; z < ne; z++ )
out[z] = reduce_sum<T, double>( in + z, l1, cy, n );
@ -521,9 +523,9 @@ vips_reducev_gen( VipsRegion *out_region, void *vseq,
#endif /*DEBUG*/
s.left = r->left;
s.top = r->top * reducev->yshrink;
s.top = r->top * reducev->vshrink;
s.width = r->width;
s.height = r->height * reducev->yshrink + reducev->n_point;
s.height = r->height * reducev->vshrink + reducev->n_point;
if( vips_region_prepare( ir, &s ) )
return( -1 );
@ -532,7 +534,7 @@ vips_reducev_gen( VipsRegion *out_region, void *vseq,
for( int y = 0; y < r->height; y ++ ) {
VipsPel *q =
VIPS_REGION_ADDR( out_region, r->left, r->top + y );
const double Y = (r->top + y) * reducev->yshrink;
const double Y = (r->top + y) * reducev->vshrink;
VipsPel *p = VIPS_REGION_ADDR( ir, r->left, (int) Y );
const int sy = Y * VIPS_TRANSFORM_SCALE * 2;
const int siy = sy & (VIPS_TRANSFORM_SCALE * 2 - 1);
@ -631,9 +633,9 @@ vips_reducev_vector_gen( VipsRegion *out_region, void *vseq,
#endif /*DEBUG_PIXELS*/
s.left = r->left;
s.top = r->top * reducev->yshrink;
s.top = r->top * reducev->vshrink;
s.width = r->width;
s.height = r->height * reducev->yshrink + reducev->n_point;
s.height = r->height * reducev->vshrink + reducev->n_point;
if( vips_region_prepare( ir, &s ) )
return( -1 );
@ -651,7 +653,7 @@ vips_reducev_vector_gen( VipsRegion *out_region, void *vseq,
for( int y = 0; y < r->height; y ++ ) {
VipsPel *q =
VIPS_REGION_ADDR( out_region, r->left, r->top + y );
const double Y = (r->top + y) * reducev->yshrink;
const double Y = (r->top + y) * reducev->vshrink;
const int py = (int) Y;
const int sy = Y * VIPS_TRANSFORM_SCALE * 2;
const int siy = sy & (VIPS_TRANSFORM_SCALE * 2 - 1);
@ -719,7 +721,7 @@ vips_reducev_raw( VipsReducev *reducev, VipsImage *in )
return( -1 );
vips_reduce_make_mask( reducev->matrixf[y],
reducev->kernel, reducev->yshrink,
reducev->kernel, reducev->vshrink,
(float) y / VIPS_TRANSFORM_SCALE );
#ifdef DEBUG
@ -780,7 +782,7 @@ vips_reducev_raw( VipsReducev *reducev, VipsImage *in )
* fractional part), we just see the integer part here.
*/
resample->out->Ysize = VIPS_RINT(
(in->Ysize - reducev->n_point + 1) / reducev->yshrink );
(in->Ysize - reducev->n_point + 1) / reducev->vshrink );
if( resample->out->Ysize <= 0 ) {
vips_error( object_class->nickname,
"%s", _( "image has shrunk to nothing" ) );
@ -816,17 +818,17 @@ vips_reducev_build( VipsObject *object )
in = resample->in;
if( reducev->yshrink < 1 ) {
if( reducev->vshrink < 1 ) {
vips_error( object_class->nickname,
"%s", _( "reduce factor should be >= 1" ) );
return( -1 );
}
if( reducev->yshrink == 1 )
if( reducev->vshrink == 1 )
return( vips_image_write( in, resample->out ) );
reducev->n_point =
vips_reduce_get_points( reducev->kernel, reducev->yshrink );
vips_reduce_get_points( reducev->kernel, reducev->vshrink );
if( reducev->n_point > MAX_POINT ) {
vips_error( object_class->nickname,
"%s", _( "reduce factor too large" ) );
@ -876,20 +878,29 @@ vips_reducev_class_init( VipsReducevClass *reducev_class )
operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED;
VIPS_ARG_DOUBLE( reducev_class, "yshrink", 3,
_( "Xshrink" ),
VIPS_ARG_DOUBLE( reducev_class, "vshrink", 3,
_( "Vshrink" ),
_( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsReducev, yshrink ),
G_STRUCT_OFFSET( VipsReducev, vshrink ),
1, 1000000, 1 );
VIPS_ARG_ENUM( reducev_class, "kernel", 3,
VIPS_ARG_ENUM( reducev_class, "kernel", 4,
_( "Kernel" ),
_( "Resampling kernel" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsReducev, kernel ),
VIPS_TYPE_KERNEL, VIPS_KERNEL_LANCZOS3 );
/* Old name.
*/
VIPS_ARG_DOUBLE( reducev_class, "yshrink", 3,
_( "Yshrink" ),
_( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsReducev, vshrink ),
1, 1000000, 1 );
}
static void
@ -902,7 +913,7 @@ vips_reducev_init( VipsReducev *reducev )
* vips_reducev:
* @in: input image
* @out: output image
* @yshrink: horizontal reduce
* @vshrink: horizontal reduce
* @...: %NULL-terminated list of optional named arguments
*
* Optional arguments:
@ -923,13 +934,13 @@ vips_reducev_init( VipsReducev *reducev )
* Returns: 0 on success, -1 on error
*/
int
vips_reducev( VipsImage *in, VipsImage **out, double yshrink, ... )
vips_reducev( VipsImage *in, VipsImage **out, double vshrink, ... )
{
va_list ap;
int result;
va_start( ap, yshrink );
result = vips_call_split( "reducev", ap, in, out, yshrink );
va_start( ap, vshrink );
result = vips_call_split( "reducev", ap, in, out, vshrink );
va_end( ap );
return( result );

View File

@ -18,6 +18,8 @@
* reduce
* 22/6/16
* - faster and better upsizing
* 15/8/16
* - more accurate resizing
*/
/*
@ -157,37 +159,36 @@ vips_resize_build( VipsObject *object )
VipsImage **t = (VipsImage **) vips_object_local_array( object, 7 );
VipsImage *in;
int target_width;
int target_height;
double hscale;
double vscale;
int int_hshrink;
int int_vshrink;
double hresidual;
double vresidual;
if( VIPS_OBJECT_CLASS( vips_resize_parent_class )->build( object ) )
return( -1 );
in = resample->in;
/* The image size we are aiming for.
/* Updated below when we do the int part of our shrink.
*/
target_width = in->Xsize * resize->scale;
hscale = resize->scale;
if( vips_object_argument_isset( object, "vscale" ) )
target_height = in->Ysize * resize->vscale;
vscale = resize->vscale;
else
target_height = in->Ysize * resize->scale;
vscale = resize->scale;
int_hshrink = vips_resize_int_shrink( resize, resize->scale );
if( vips_object_argument_isset( object, "vscale" ) )
int_vshrink = vips_resize_int_shrink( resize, resize->vscale );
else
int_vshrink = int_hshrink;
/* The int part of our scale.
*/
int_hshrink = vips_resize_int_shrink( resize, hscale );
int_vshrink = vips_resize_int_shrink( resize, vscale );
if( int_vshrink > 1 ) {
vips_info( class->nickname, "shrinkv by %d", int_vshrink );
if( vips_shrinkv( in, &t[0], int_vshrink, NULL ) )
return( -1 );
in = t[0];
vscale *= int_vshrink;
}
if( int_hshrink > 1 ) {
@ -195,16 +196,9 @@ vips_resize_build( VipsObject *object )
if( vips_shrinkh( in, &t[1], int_hshrink, NULL ) )
return( -1 );
in = t[1];
}
/* Do we need a further size adjustment? It's the difference
* between our target size and the size we have after vips_shrink().
*
* This can break the aspect ratio slightly :/ but hopefully no one
* will notice.
*/
hresidual = (double) target_width / in->Xsize;
vresidual = (double) target_height / in->Ysize;
hscale *= int_hshrink;
}
/* We will get overcomputation on vips_shrink() from the vips_reduce()
* coming later, so read into a cache where tiles are scanlines, and
@ -239,7 +233,7 @@ vips_resize_build( VipsObject *object )
vips_get_tile_size( in,
&tile_width, &tile_height, &n_lines );
need_lines = 1.2 * n_lines / vresidual;
need_lines = 1.2 * n_lines / vscale;
if( vips_tilecache( in, &t[6],
"tile_width", in->Xsize,
"tile_height", 10,
@ -253,20 +247,20 @@ vips_resize_build( VipsObject *object )
/* Any residual downsizing.
*/
if( vresidual < 1.0 ) {
if( vscale < 1.0 ) {
vips_info( class->nickname, "residual reducev by %g",
vresidual );
if( vips_reducev( in, &t[2], 1.0 / vresidual,
vscale );
if( vips_reducev( in, &t[2], 1.0 / vscale,
"kernel", resize->kernel,
NULL ) )
return( -1 );
in = t[2];
}
if( hresidual < 1.0 ) {
if( hscale < 1.0 ) {
vips_info( class->nickname, "residual reduceh by %g",
hresidual );
if( vips_reduceh( in, &t[3], 1.0 / hresidual,
hscale );
if( vips_reduceh( in, &t[3], 1.0 / hscale,
"kernel", resize->kernel,
NULL ) )
return( -1 );
@ -275,8 +269,8 @@ vips_resize_build( VipsObject *object )
/* Any upsizing.
*/
if( hresidual > 1.0 ||
vresidual > 1.0 ) {
if( hscale > 1.0 ||
vscale > 1.0 ) {
const char *nickname = vips_resize_interpolate( resize->kernel );
VipsInterpolate *interpolate;
@ -284,21 +278,21 @@ vips_resize_build( VipsObject *object )
return( -1 );
vips_object_local( object, interpolate );
if( hresidual > 1.0 &&
vresidual > 1.0 ) {
if( hscale > 1.0 &&
vscale > 1.0 ) {
vips_info( class->nickname,
"residual scale %g x %g", hresidual, vresidual );
"residual scale %g x %g", hscale, vscale );
if( vips_affine( in, &t[4],
hresidual, 0.0, 0.0, vresidual,
hscale, 0.0, 0.0, vscale,
"interpolate", interpolate,
NULL ) )
return( -1 );
in = t[4];
}
else if( hresidual > 1.0 ) {
else if( hscale > 1.0 ) {
vips_info( class->nickname,
"residual scaleh %g", hresidual );
if( vips_affine( in, &t[4], hresidual, 0.0, 0.0, 1.0,
"residual scale %g", hscale );
if( vips_affine( in, &t[4], hscale, 0.0, 0.0, 1.0,
"interpolate", interpolate,
NULL ) )
return( -1 );
@ -306,8 +300,8 @@ vips_resize_build( VipsObject *object )
}
else {
vips_info( class->nickname,
"residual scalev %g", vresidual );
if( vips_affine( in, &t[4], 1.0, 0.0, 0.0, vresidual,
"residual scale %g", vscale );
if( vips_affine( in, &t[4], 1.0, 0.0, 0.0, vscale,
"interpolate", interpolate,
NULL ) )
return( -1 );

View File

@ -4,6 +4,9 @@
* - from shrink.c (now renamed as shrink2.c)
* - split to h and v shrinks for a large memory saving
* - now handles complex
* 15/8/16
* - more accurate resize
* - rename xshrink -> hshrink for greater consistency
*/
/*
@ -55,8 +58,8 @@
typedef struct _VipsShrink {
VipsResample parent_instance;
double xshrink; /* Shrink factors */
double yshrink;
double hshrink; /* Shrink factors */
double vshrink;
} VipsShrink;
@ -72,40 +75,34 @@ vips_shrink_build( VipsObject *object )
VipsImage **t = (VipsImage **)
vips_object_local_array( object, 3 );
int xshrink_int;
int yshrink_int;
int hshrink_int;
int vshrink_int;
if( VIPS_OBJECT_CLASS( vips_shrink_parent_class )->build( object ) )
return( -1 );
xshrink_int = (int) shrink->xshrink;
yshrink_int = (int) shrink->yshrink;
hshrink_int = (int) shrink->hshrink;
vshrink_int = (int) shrink->vshrink;
if( xshrink_int != shrink->xshrink ||
yshrink_int != shrink->yshrink ) {
if( hshrink_int != shrink->hshrink ||
vshrink_int != shrink->vshrink ) {
/* Shrink by int factors, affine to final size.
*/
int target_width = resample->in->Xsize / shrink->xshrink;
int target_height = resample->in->Ysize / shrink->yshrink;
double xresidual = hshrink_int / shrink->hshrink;
double yresidual = vshrink_int / shrink->vshrink;
double xresidual;
double yresidual;
if( vips_shrinkv( resample->in, &t[0], yshrink_int, NULL ) ||
vips_shrinkh( t[0], &t[1], xshrink_int, NULL ) )
if( vips_shrinkv( resample->in, &t[0], vshrink_int, NULL ) ||
vips_shrinkh( t[0], &t[1], hshrink_int, NULL ) )
return( -1 );
xresidual = (double) target_width / t[1]->Xsize;
yresidual = (double) target_height / t[1]->Ysize;
if( vips_affine( t[1], &t[2],
xresidual, 0.0, 0.0, yresidual, NULL ) ||
vips_image_write( t[2], resample->out ) )
return( -1 );
}
else {
if( vips_shrinkv( resample->in, &t[0], shrink->yshrink, NULL ) ||
vips_shrinkh( t[0], &t[1], shrink->xshrink, NULL ) ||
if( vips_shrinkv( resample->in, &t[0], shrink->vshrink, NULL ) ||
vips_shrinkh( t[0], &t[1], shrink->hshrink, NULL ) ||
vips_image_write( t[1], resample->out ) )
return( -1 );
}
@ -131,18 +128,34 @@ vips_shrink_class_init( VipsShrinkClass *class )
operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED;
VIPS_ARG_DOUBLE( class, "vshrink", 9,
_( "Vshrink" ),
_( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsShrink, vshrink ),
1.0, 1000000.0, 1.0 );
VIPS_ARG_DOUBLE( class, "hshrink", 8,
_( "Hshrink" ),
_( "Horizontal shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsShrink, hshrink ),
1.0, 1000000.0, 1.0 );
/* The old names .. now use h and v everywhere.
*/
VIPS_ARG_DOUBLE( class, "xshrink", 8,
_( "Xshrink" ),
_( "Horizontal shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsShrink, xshrink ),
VIPS_ARGUMENT_REQUIRED_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsShrink, hshrink ),
1.0, 1000000.0, 1.0 );
VIPS_ARG_DOUBLE( class, "yshrink", 9,
_( "Yshrink" ),
_( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsShrink, yshrink ),
VIPS_ARGUMENT_REQUIRED_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsShrink, vshrink ),
1.0, 1000000.0, 1.0 );
}
@ -156,8 +169,8 @@ vips_shrink_init( VipsShrink *shrink )
* vips_shrink:
* @in: input image
* @out: output image
* @xshrink: horizontal shrink
* @yshrink: vertical shrink
* @hshrink: horizontal shrink
* @vshrink: vertical shrink
* @...: %NULL-terminated list of optional named arguments
*
* Shrink @in by a pair of factors with a simple box filter. For non-integer
@ -177,13 +190,13 @@ vips_shrink_init( VipsShrink *shrink )
*/
int
vips_shrink( VipsImage *in, VipsImage **out,
double xshrink, double yshrink, ... )
double hshrink, double vshrink, ... )
{
va_list ap;
int result;
va_start( ap, yshrink );
result = vips_call_split( "shrink", ap, in, out, xshrink, yshrink );
va_start( ap, vshrink );
result = vips_call_split( "shrink", ap, in, out, hshrink, vshrink );
va_end( ap );
return( result );

View File

@ -4,6 +4,8 @@
* - from shrink.c
* 22/1/16
* - reorganise loops, 30% faster, vectorisable
* 15/8/16
* - rename xshrink -> hshrink for greater consistency
*/
/*
@ -55,7 +57,7 @@
typedef struct _VipsShrinkh {
VipsResample parent_instance;
int xshrink; /* Shrink factor */
int hshrink; /* Shrink factor */
} VipsShrinkh;
@ -79,9 +81,9 @@ G_DEFINE_TYPE( VipsShrinkh, vips_shrinkh, VIPS_TYPE_RESAMPLE );
\
sum = 0; \
x1 = b; \
VIPS_UNROLL( shrink->xshrink, INNER( BANDS ) ); \
q[b] = (sum + shrink->xshrink / 2) / \
shrink->xshrink; \
VIPS_UNROLL( shrink->hshrink, INNER( BANDS ) ); \
q[b] = (sum + shrink->hshrink / 2) / \
shrink->hshrink; \
} \
p += ne; \
q += BANDS; \
@ -100,8 +102,8 @@ G_DEFINE_TYPE( VipsShrinkh, vips_shrinkh, VIPS_TYPE_RESAMPLE );
\
sum = 0.0; \
x1 = b; \
VIPS_UNROLL( shrink->xshrink, INNER( bands ) ); \
q[b] = sum / shrink->xshrink; \
VIPS_UNROLL( shrink->hshrink, INNER( bands ) ); \
q[b] = sum / shrink->hshrink; \
} \
p += ne; \
q += bands; \
@ -118,9 +120,9 @@ vips_shrinkh_gen2( VipsShrinkh *shrink, VipsRegion *or, VipsRegion *ir,
const int bands = resample->in->Bands *
(vips_band_format_iscomplex( resample->in->BandFmt ) ?
2 : 1);
const int ne = shrink->xshrink * bands;
const int ne = shrink->hshrink * bands;
VipsPel *out = VIPS_REGION_ADDR( or, left, top );
VipsPel *in = VIPS_REGION_ADDR( ir, left * shrink->xshrink, top );
VipsPel *in = VIPS_REGION_ADDR( ir, left * shrink->hshrink, top );
int x;
int x1, b;
@ -200,9 +202,9 @@ vips_shrinkh_gen( VipsRegion *or, void *seq,
for( y = 0; y < r->height; y ++ ) {
VipsRect s;
s.left = r->left * shrink->xshrink;
s.left = r->left * shrink->hshrink;
s.top = r->top + y;
s.width = r->width * shrink->xshrink;
s.width = r->width * shrink->hshrink;
s.height = 1;
#ifdef DEBUG
printf( "shrinkh_gen: requesting line %d\n", s.top );
@ -230,7 +232,7 @@ vips_shrinkh_build( VipsObject *object )
VipsResample *resample = VIPS_RESAMPLE( object );
VipsShrinkh *shrink = (VipsShrinkh *) object;
VipsImage **t = (VipsImage **)
vips_object_local_array( object, 1 );
vips_object_local_array( object, 2 );
VipsImage *in;
@ -239,13 +241,13 @@ vips_shrinkh_build( VipsObject *object )
in = resample->in;
if( shrink->xshrink < 1 ) {
if( shrink->hshrink < 1 ) {
vips_error( class->nickname,
"%s", _( "shrink factors should be >= 1" ) );
return( -1 );
}
if( shrink->xshrink == 1 )
if( shrink->hshrink == 1 )
return( vips_image_write( in, resample->out ) );
/* Unpack for processing.
@ -254,6 +256,17 @@ vips_shrinkh_build( VipsObject *object )
return( -1 );
in = t[0];
/* We need new pixels at the right so that we don't have small chunks
* to average down the right edge.
*/
if( vips_embed( in, &t[1],
0, 0,
in->Xsize + shrink->hshrink, in->Ysize,
"extend", VIPS_EXTEND_COPY,
NULL ) )
return( -1 );
in = t[1];
/* THINSTRIP will work, anything else will break seq mode. If you
* combine shrink with conv you'll need to use a line cache to maintain
* sequentiality.
@ -262,13 +275,14 @@ vips_shrinkh_build( VipsObject *object )
VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) )
return( -1 );
/* Size output. Note: we round the output width down!
/* Size output. We need to rint() from the size of the original image.
*
* Don't change xres/yres, leave that to the application layer. For
* example, vipsthumbnail knows the true shrink factor (including the
* fractional part), we just see the integer part here.
*/
resample->out->Xsize = in->Xsize / shrink->xshrink;
resample->out->Xsize = VIPS_RINT(
resample->in->Xsize / shrink->hshrink );
if( resample->out->Xsize <= 0 ) {
vips_error( class->nickname,
"%s", _( "image has shrunk to nothing" ) );
@ -307,11 +321,20 @@ vips_shrinkh_class_init( VipsShrinkhClass *class )
operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED;
VIPS_ARG_INT( class, "hshrink", 8,
_( "Hshrink" ),
_( "Horizontal shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsShrinkh, hshrink ),
1, 1000000, 1 );
/* The old name .. now use h and v everywhere.
*/
VIPS_ARG_INT( class, "xshrink", 8,
_( "Xshrink" ),
_( "Horizontal shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsShrinkh, xshrink ),
VIPS_ARGUMENT_REQUIRED_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsShrinkh, hshrink ),
1, 1000000, 1 );
}
@ -325,11 +348,11 @@ vips_shrinkh_init( VipsShrinkh *shrink )
* vips_shrinkh:
* @in: input image
* @out: output image
* @xshrink: horizontal shrink
* @hshrink: horizontal shrink
* @...: %NULL-terminated list of optional named arguments
*
* Shrink @in horizontally by an integer factor. Each pixel in the output is
* the average of the corresponding line of @xshrink pixels in the input.
* the average of the corresponding line of @hshrink pixels in the input.
*
* This is a very low-level operation: see vips_resize() for a more
* convenient way to resize images.
@ -342,13 +365,13 @@ vips_shrinkh_init( VipsShrinkh *shrink )
* Returns: 0 on success, -1 on error
*/
int
vips_shrinkh( VipsImage *in, VipsImage **out, int xshrink, ... )
vips_shrinkh( VipsImage *in, VipsImage **out, int hshrink, ... )
{
va_list ap;
int result;
va_start( ap, xshrink );
result = vips_call_split( "shrinkh", ap, in, out, xshrink );
va_start( ap, hshrink );
result = vips_call_split( "shrinkh", ap, in, out, hshrink );
va_end( ap );
return( result );

View File

@ -41,6 +41,8 @@
* 6/6/13
* - don't chunk horizontally, fixes seq problems with large shrink
* factors
* 15/8/16
* - rename yshrink -> vshrink for greater consistency
*/
/*
@ -93,7 +95,7 @@
typedef struct _VipsShrinkv {
VipsResample parent_instance;
int yshrink;
int vshrink;
size_t sizeof_line_buffer;
} VipsShrinkv;
@ -200,7 +202,7 @@ vips_shrinkv_add_line( VipsShrinkv *shrink, VipsShrinkvSequence *seq,
TYPE * restrict q = (TYPE *) out; \
\
for( x = 0; x < sz; x++ ) \
q[x] = (sum[x] + shrink->yshrink / 2) / shrink->yshrink; \
q[x] = (sum[x] + shrink->vshrink / 2) / shrink->vshrink; \
}
/* Float average.
@ -210,7 +212,7 @@ vips_shrinkv_add_line( VipsShrinkv *shrink, VipsShrinkvSequence *seq,
TYPE * restrict q = (TYPE *) out; \
\
for( x = 0; x < sz; x++ ) \
q[x] = sum[x] / shrink->yshrink; \
q[x] = sum[x] / shrink->vshrink; \
}
/* Average the line of sums to out.
@ -287,11 +289,11 @@ vips_shrinkv_gen( VipsRegion *or, void *vseq,
for( y = 0; y < r->height; y++ ) {
memset( seq->sum, 0, shrink->sizeof_line_buffer );
for( y1 = 0; y1 < shrink->yshrink; y1++ ) {
for( y1 = 0; y1 < shrink->vshrink; y1++ ) {
VipsRect s;
s.left = r->left;
s.top = y1 + (y + r->top) * shrink->yshrink;
s.top = y1 + (y + r->top) * shrink->vshrink;
s.width = r->width;
s.height = 1;
#ifdef DEBUG
@ -328,7 +330,7 @@ vips_shrinkv_build( VipsObject *object )
VipsResample *resample = VIPS_RESAMPLE( object );
VipsShrinkv *shrink = (VipsShrinkv *) object;
VipsImage **t = (VipsImage **)
vips_object_local_array( object, 1 );
vips_object_local_array( object, 2 );
VipsImage *in;
@ -337,13 +339,13 @@ vips_shrinkv_build( VipsObject *object )
in = resample->in;
if( shrink->yshrink < 1 ) {
if( shrink->vshrink < 1 ) {
vips_error( class->nickname,
"%s", _( "shrink factors should be >= 1" ) );
return( -1 );
}
if( shrink->yshrink == 1 )
if( shrink->vshrink == 1 )
return( vips_image_write( in, resample->out ) );
/* Unpack for processing.
@ -352,6 +354,17 @@ vips_shrinkv_build( VipsObject *object )
return( -1 );
in = t[0];
/* We need new pixels along the bottom so that we don't have small chunks
* to average along the bottom edge.
*/
if( vips_embed( in, &t[1],
0, 0,
in->Xsize, in->Ysize + shrink->vshrink,
"extend", VIPS_EXTEND_COPY,
NULL ) )
return( -1 );
in = t[1];
/* We have to keep a line buffer as we sum columns.
*/
shrink->sizeof_line_buffer =
@ -366,13 +379,14 @@ vips_shrinkv_build( VipsObject *object )
VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) )
return( -1 );
/* Size output. Note: we round the output width down!
/* Size output. We need to rint() from the size of the original image.
*
* Don't change xres/yres, leave that to the application layer. For
* example, vipsthumbnail knows the true shrink factor (including the
* fractional part), we just see the integer part here.
*/
resample->out->Ysize = in->Ysize / shrink->yshrink;
resample->out->Ysize = VIPS_RINT(
resample->in->Ysize / shrink->vshrink );
if( resample->out->Ysize <= 0 ) {
vips_error( class->nickname,
"%s", _( "image has shrunk to nothing" ) );
@ -411,11 +425,20 @@ vips_shrinkv_class_init( VipsShrinkvClass *class )
operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED;
VIPS_ARG_INT( class, "vshrink", 9,
_( "Vshrink" ),
_( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsShrinkv, vshrink ),
1, 1000000, 1 );
/* The old name .. now use h and v everywhere.
*/
VIPS_ARG_INT( class, "yshrink", 9,
_( "Yshrink" ),
_( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsShrinkv, yshrink ),
VIPS_ARGUMENT_REQUIRED_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsShrinkv, vshrink ),
1, 1000000, 1 );
}
@ -429,11 +452,11 @@ vips_shrinkv_init( VipsShrinkv *shrink )
* vips_shrinkv:
* @in: input image
* @out: output image
* @yshrink: vertical shrink
* @vshrink: vertical shrink
* @...: %NULL-terminated list of optional named arguments
*
* Shrink @in vertically by an integer factor. Each pixel in the output is
* the average of the corresponding column of @yshrink pixels in the input.
* the average of the corresponding column of @vshrink pixels in the input.
*
* This is a very low-level operation: see vips_resize() for a more
* convenient way to resize images.
@ -446,13 +469,13 @@ vips_shrinkv_init( VipsShrinkv *shrink )
* Returns: 0 on success, -1 on error
*/
int
vips_shrinkv( VipsImage *in, VipsImage **out, int yshrink, ... )
vips_shrinkv( VipsImage *in, VipsImage **out, int vshrink, ... )
{
va_list ap;
int result;
va_start( ap, yshrink );
result = vips_call_split( "shrinkv", ap, in, out, yshrink );
va_start( ap, vshrink );
result = vips_call_split( "shrinkv", ap, in, out, vshrink );
va_end( ap );
return( result );

View File

@ -167,19 +167,19 @@ class TestResample(unittest.TestCase):
def test_resize(self):
im = Vips.Image.new_from_file("images/IMG_4618.jpg")
im2 = im.resize(0.25)
self.assertEqual(im2.width, im.width // 4)
self.assertEqual(im2.height, im.height // 4)
self.assertEqual(im2.width, round(im.width / 4.0))
self.assertEqual(im2.height, round(im.height / 4.0))
def test_shrink(self):
im = Vips.Image.new_from_file("images/IMG_4618.jpg")
im2 = im.shrink(4, 4)
self.assertEqual(im2.width, im.width // 4)
self.assertEqual(im2.height, im.height // 4)
self.assertEqual(im2.width, round(im.width / 4.0))
self.assertEqual(im2.height, round(im.height / 4.0))
self.assertTrue(abs(im.avg() - im2.avg()) < 1)
im2 = im.shrink(2.5, 2.5)
self.assertEqual(im2.width, im.width // 2.5)
self.assertEqual(im2.height, im.height // 2.5)
self.assertEqual(im2.width, round(im.width / 2.5))
self.assertEqual(im2.height, round(im.height / 2.5))
self.assertLess(abs(im.avg() - im2.avg()), 1)
def test_similarity(self):

View File

@ -83,6 +83,8 @@
* - no need to guess max-alpha now premultiply does this for us
* 1/8/16
* - use scRGB as the working space in linear mode
* 15/8/16
* - can now remove 0.1 rounding adjustment
*/
#ifdef HAVE_CONFIG_H
@ -192,12 +194,9 @@ calculate_shrink( VipsImage *im )
*
* In crop mode, we aim to fill the bounding box, so we must use the
* smaller axis.
*
* Add a small amount so when vips_resize() later rounds down, we
* don't round below target.
*/
double horizontal = (double) width / (thumbnail_width + 0.1);
double vertical = (double) height / (thumbnail_height + 0.1);
double horizontal = (double) width / thumbnail_width;
double vertical = (double) height / thumbnail_height;
if( crop_image ) {
if( horizontal < vertical )