better vips_resize() for cubic/linear/nearest
vips_resize() used to do most of a downsize with vips_shrink() and the final 200 - 300% with vips_reduce(). This was correct for lanczos2/3, but not right for linear/cubic, which need more shrink and less reduce to avoid aliasing. This patch makes vips_resize() leave the final 100 - 200% to vips_reduce() for linear/cubic, and leave everything to reduce for nearest.
This commit is contained in:
parent
a5bef08d4a
commit
f3326c8126
@ -24,6 +24,7 @@
|
||||
- added C++ arithmetic assignment overloads, += etc.
|
||||
- VImage::ifthenelse() with double args was missing =0 on options
|
||||
- better accuracy for reducev with smarter multiplication
|
||||
- better quality for vips_resize() with linear/cubic kernels
|
||||
|
||||
18/5/16 started 8.3.2
|
||||
- more robust vips image reading
|
||||
|
20
TODO
20
TODO
@ -1,23 +1,3 @@
|
||||
- try:
|
||||
|
||||
$ vips black x.v 10 10
|
||||
$ vips reducev x.v x2.v 2
|
||||
$ vips avg x2.v
|
||||
1.000000
|
||||
|
||||
argh!
|
||||
|
||||
reduceh seems OK
|
||||
|
||||
$ vips reducev x.v x2.v 2 --vips-novector
|
||||
$ vips avg x2.v
|
||||
0.000000
|
||||
|
||||
also OK ... just the vector path
|
||||
|
||||
- add more reducev tests ... resize constant 255 and constant 0 image, with and
|
||||
without vector
|
||||
|
||||
- add more webp tests to py suite
|
||||
|
||||
- try moving some more of the CLI tests to py
|
||||
|
@ -559,11 +559,10 @@ vips_reduceh_init( VipsReduceh *reduceh )
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* @kernel: #VipsKernel to use to interpolate (default: lanczos3)
|
||||
* * @kernel: #VipsKernel to use to interpolate (default: lanczos3)
|
||||
*
|
||||
* Reduce @in horizontally by a float factor. The pixels in @out are
|
||||
* interpolated with a 1D mask. This operation will not work well for
|
||||
* a reduction of more than a factor of two.
|
||||
* interpolated with a 1D mask.
|
||||
*
|
||||
* This is a very low-level operation: see vips_resize() for a more
|
||||
* convenient way to resize images.
|
||||
|
@ -41,6 +41,8 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG_PIXELS
|
||||
#define DEBUG_COMPILE
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
@ -163,9 +165,9 @@ vips_reducev_compile_section( VipsReducev *reducev, Pass *pass, gboolean first )
|
||||
VipsVector *v;
|
||||
int i;
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifdef DEBUG_COMPILE
|
||||
printf( "starting pass %d\n", pass->first );
|
||||
#endif /*DEBUG*/
|
||||
#endif /*DEBUG_COMPILE*/
|
||||
|
||||
pass->vector = v = vips_vector_new( "reducev", 1 );
|
||||
|
||||
@ -270,10 +272,10 @@ vips_reducev_compile_section( VipsReducev *reducev, Pass *pass, gboolean first )
|
||||
if( !vips_vector_compile( v ) )
|
||||
return( -1 );
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifdef DEBUG_COMPILE
|
||||
printf( "done coeffs %d to %d\n", pass->first, pass->last );
|
||||
vips_vector_print( v );
|
||||
#endif /*DEBUG*/
|
||||
#endif /*DEBUG_COMPILE*/
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@ -623,10 +625,10 @@ vips_reducev_vector_gen( VipsRegion *out_region, void *vseq,
|
||||
VipsExecutor executor[MAX_PASS];
|
||||
VipsRect s;
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifdef DEBUG_PIXELS
|
||||
printf( "vips_reducev_vector_gen: generating %d x %d at %d x %d\n",
|
||||
r->width, r->height, r->left, r->top );
|
||||
#endif /*DEBUG*/
|
||||
#endif /*DEBUG_PIXELS*/
|
||||
|
||||
s.left = r->left;
|
||||
s.top = r->top * reducev->yshrink;
|
||||
@ -635,10 +637,10 @@ vips_reducev_vector_gen( VipsRegion *out_region, void *vseq,
|
||||
if( vips_region_prepare( ir, &s ) )
|
||||
return( -1 );
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifdef DEBUG_PIXELS
|
||||
printf( "vips_reducev_vector_gen: preparing %d x %d at %d x %d\n",
|
||||
s.width, s.height, s.left, s.top );
|
||||
#endif /*DEBUG*/
|
||||
#endif /*DEBUG_PIXELS*/
|
||||
|
||||
for( int i = 0; i < reducev->n_pass; i++ )
|
||||
vips_executor_set_program( &executor[i],
|
||||
@ -656,7 +658,7 @@ vips_reducev_vector_gen( VipsRegion *out_region, void *vseq,
|
||||
const int ty = (siy + 1) >> 1;
|
||||
const int *cyo = reducev->matrixo[ty];
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifdef DEBUG_PIXELS
|
||||
printf( "starting row %d\n", y + r->top );
|
||||
printf( "coefficients:\n" );
|
||||
for( int i = 0; i < reducev->n_point; i++ )
|
||||
@ -665,7 +667,7 @@ vips_reducev_vector_gen( VipsRegion *out_region, void *vseq,
|
||||
for( int i = 0; i < reducev->n_point; i++ )
|
||||
printf( "\t%d - %d\n", i,
|
||||
*VIPS_REGION_ADDR( ir, r->left, r->top + y + i ) );
|
||||
#endif /*DEBUG*/
|
||||
#endif /*DEBUG_PIXELS*/
|
||||
|
||||
/* We run our n passes to generate this scanline.
|
||||
*/
|
||||
@ -687,10 +689,10 @@ vips_reducev_vector_gen( VipsRegion *out_region, void *vseq,
|
||||
VIPS_SWAP( signed short *, seq->t1, seq->t2 );
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifdef DEBUG_PIXELS
|
||||
printf( "pixel result:\n" );
|
||||
printf( "\t%d\n", *q );
|
||||
#endif /*DEBUG*/
|
||||
#endif /*DEBUG_PIXELS*/
|
||||
}
|
||||
|
||||
VIPS_GATE_STOP( "vips_reducev_vector_gen: work" );
|
||||
@ -964,11 +966,10 @@ vips_reducev_init( VipsReducev *reducev )
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* @kernel: #VipsKernel to use to interpolate (default: lanczos3)
|
||||
* * @kernel: #VipsKernel to use to interpolate (default: lanczos3)
|
||||
*
|
||||
* Reduce @in vertically by a float factor. The pixels in @out are
|
||||
* interpolated with a 1D mask. This operation will not work well for
|
||||
* a reduction of more than a factor of two.
|
||||
* interpolated with a 1D mask.
|
||||
*
|
||||
* This is a very low-level operation: see vips_resize() for a more
|
||||
* convenient way to resize images.
|
||||
|
@ -13,6 +13,9 @@
|
||||
* 1/5/16
|
||||
* - allow >1 on one axis, <1 on the other
|
||||
* - expose @kernel setting
|
||||
* 16/6/16
|
||||
* - better quality for linear/cubic kernels ... do more shrink and less
|
||||
* reduce
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -84,6 +87,43 @@ typedef VipsResampleClass VipsResizeClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsResize, vips_resize, VIPS_TYPE_RESAMPLE );
|
||||
|
||||
/* How much of a scale should be by an integer shrink factor?
|
||||
*
|
||||
* This depends on the scale and the kernel we will use for residual resizing.
|
||||
* For upsizing and nearest-neighbour downsize, we want no shrinking.
|
||||
*
|
||||
* Linear and cubic are fixed-size kernels and for a 0 offset are point
|
||||
* samplers. We will get aliasing if we do more than a x2 shrink with them.
|
||||
*
|
||||
* Lanczos is adaptive: the size of the kernel changes with the shrink factor.
|
||||
* We will get the best quality (but be the slowest) if we let reduce do all
|
||||
* the work. Leave it the final 200 - 300% to do as a compromise for
|
||||
* efficiency.
|
||||
*
|
||||
* FIXME: this is rather ugly. Kernel should be a class and this info should be
|
||||
* stored in there.
|
||||
*/
|
||||
static int
|
||||
vips_resize_int_shrink( VipsResize *resize, double scale )
|
||||
{
|
||||
if( scale > 1.0 )
|
||||
return( 1 );
|
||||
|
||||
switch( resize->kernel ) {
|
||||
case VIPS_KERNEL_NEAREST:
|
||||
return( 1 );
|
||||
|
||||
case VIPS_KERNEL_LINEAR:
|
||||
case VIPS_KERNEL_CUBIC:
|
||||
default:
|
||||
return( VIPS_FLOOR( 1.0 / scale ) );
|
||||
|
||||
case VIPS_KERNEL_LANCZOS2:
|
||||
case VIPS_KERNEL_LANCZOS3:
|
||||
return( VIPS_MAX( 1, VIPS_FLOOR( 1.0 / (resize->scale * 2) ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
vips_resize_build( VipsObject *object )
|
||||
{
|
||||
@ -114,25 +154,9 @@ vips_resize_build( VipsObject *object )
|
||||
else
|
||||
target_height = in->Ysize * resize->scale;
|
||||
|
||||
/* If the factor is > 1.0, we need to zoom rather than shrink.
|
||||
* Just set the int part to 1 in this case.
|
||||
*/
|
||||
|
||||
/* We want the int part of the shrink to leave a bit to do with
|
||||
* blur/reduce/sharpen, or we'll see strange changes in aliasing on int
|
||||
* shrink boundaries as we resize.
|
||||
*/
|
||||
|
||||
if( resize->scale > 1.0 )
|
||||
int_hshrink = 1;
|
||||
else
|
||||
int_hshrink = VIPS_FLOOR( 1.0 / (resize->scale * 2) );
|
||||
if( vips_object_argument_isset( object, "vscale" ) ) {
|
||||
if( resize->vscale > 1.0 )
|
||||
int_vshrink = 1;
|
||||
else
|
||||
int_vshrink = VIPS_FLOOR( 1.0 / (resize->vscale * 2) );
|
||||
}
|
||||
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;
|
||||
|
||||
@ -334,21 +358,21 @@ vips_resize_init( VipsResize *resize )
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @vscale: vertical scale factor
|
||||
* * @vscale: %gdouble vertical scale factor
|
||||
* * @kernel: #VipsKernel to reduce with
|
||||
*
|
||||
* Resize an image. When upsizing (@scale > 1), the image is simply block
|
||||
* upsized. When downsizing, the
|
||||
* image is block-shrunk with vips_shrink(),
|
||||
* then the image is shrunk again to the
|
||||
* target size with vips_reduce(). The operation will leave at least the final
|
||||
* x2 to be done with vips_reduce().
|
||||
* target size with vips_reduce(). How much is done by vips_shrink() vs.
|
||||
* vips_reduce() varies with the @kernel setting.
|
||||
*
|
||||
* vips_resize() normally maintains the image apect ratio. If you set
|
||||
* @vscale, that factor is used for the vertical scale and @scale for the
|
||||
* horizontal.
|
||||
*
|
||||
* vips_resize() normally uses #VIPS_KERNEL_LANCZOS3 for thre final shrink, you
|
||||
* vips_resize() normally uses #VIPS_KERNEL_LANCZOS3 for the final shrink, you
|
||||
* can change this with @kernel.
|
||||
*
|
||||
* This operation does not change xres or yres. The image resolution needs to
|
||||
|
@ -435,10 +435,8 @@ vips_shrinkv_init( VipsShrinkv *shrink )
|
||||
* 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.
|
||||
*
|
||||
* You will get aliasing for non-integer shrinks. In this case, shrink with
|
||||
* this function to the nearest integer size above the target shrink, then
|
||||
* downsample to the exact size with vips_affine() and your choice of
|
||||
* interpolator. See vips_resize() for a convenient way to do this.
|
||||
* This is a very low-level operation: see vips_resize() for a more
|
||||
* convenient way to resize images.
|
||||
*
|
||||
* This operation does not change xres or yres. The image resolution needs to
|
||||
* be updated by the application.
|
||||
|
Loading…
Reference in New Issue
Block a user