try making the reduce mask larger with residual

This commit is contained in:
John Cupitt 2016-03-15 14:59:59 +00:00
parent 45c3fea6ac
commit ff88087a28
7 changed files with 97 additions and 64 deletions

1
TODO
View File

@ -1,3 +1,4 @@
- try orc version of reducev? and shrinkv? maybe shrinkh?

View File

@ -67,10 +67,11 @@ GType vips_resample_get_type( void );
/* The max size of the vector we use.
*/
#define MAX_POINTS (6)
#define MAX_POINTS (50)
int vips_reduce_get_points( VipsKernel kernel );
void vips_reduce_make_mask( VipsKernel kernel, double x, double *c );
int vips_reduce_get_points( VipsKernel kernel, double shrink );
void vips_reduce_make_mask( VipsKernel kernel, double shrink,
double x, double *c );
#ifdef __cplusplus
}

View File

@ -81,8 +81,8 @@ typedef struct _VipsReduceh {
* sizes up to short), and double (for all others). We go to
* scale + 1 so we can round-to-nearest safely.
*/
int matrixi[VIPS_TRANSFORM_SCALE + 1][MAX_POINTS];
double matrixf[VIPS_TRANSFORM_SCALE + 1][MAX_POINTS];
int *matrixi[VIPS_TRANSFORM_SCALE + 1];
double *matrixf[VIPS_TRANSFORM_SCALE + 1];
} VipsReduceh;
@ -94,10 +94,10 @@ extern "C" {
G_DEFINE_TYPE( VipsReduceh, vips_reduceh, VIPS_TYPE_RESAMPLE );
}
/* Get n points.
/* Get n points. @shrink is the shrink factor, so 2 for a 50% reduction.
*/
int
vips_reduce_get_points( VipsKernel kernel )
vips_reduce_get_points( VipsKernel kernel, double shrink )
{
switch( kernel ) {
case VIPS_KERNEL_NEAREST:
@ -110,10 +110,12 @@ vips_reduce_get_points( VipsKernel kernel )
return( 4 );
case VIPS_KERNEL_LANCZOS2:
return( 4 );
/* Needs to be in sync with calculate_coefficients_lanczos().
*/
return( 2 * 2 * ceil( shrink ) + 2 );
case VIPS_KERNEL_LANCZOS3:
return( 6 );
return( 2 * 3 * ceil( shrink ) + 2 );
default:
g_assert_not_reached();
@ -121,10 +123,10 @@ vips_reduce_get_points( VipsKernel kernel )
}
}
/* Calculate a mask.
/* Calculate a mask element.
*/
void
vips_reduce_make_mask( VipsKernel kernel, double x, double *c )
vips_reduce_make_mask( VipsKernel kernel, double shrink, double x, double *c )
{
switch( kernel ) {
case VIPS_KERNEL_NEAREST:
@ -141,11 +143,11 @@ vips_reduce_make_mask( VipsKernel kernel, double x, double *c )
break;
case VIPS_KERNEL_LANCZOS2:
calculate_coefficients_lanczos( 2, x, c );
calculate_coefficients_lanczos( 2, shrink, x, c );
break;
case VIPS_KERNEL_LANCZOS3:
calculate_coefficients_lanczos( 3, x, c );
calculate_coefficients_lanczos( 3, shrink, x, c );
break;
default:
@ -177,7 +179,7 @@ reduceh_unsigned_int_tab( VipsReduceh *reduceh,
}
}
/* A 4-point interpolation on uint8 is the most common case ... unroll that.
/* A 6-point interpolation on uint8 is the most common case ... unroll that.
*
* The inner loop here won't vectorise, but our inner loop doesn't run for
* long enough for vectorisation to be useful :-( gcc says it needs about an
@ -316,7 +318,7 @@ reduceh_notab( VipsReduceh *reduceh,
double cx[MAX_POINTS];
vips_reduce_make_mask( reduceh->kernel, x, cx );
vips_reduce_make_mask( reduceh->kernel, reduceh->xshrink, x, cx );
for( int z = 0; z < bands; z++ ) {
out[z] = reduce_sum<T, double>( in, bands, cx, n );
@ -476,9 +478,23 @@ vips_reduceh_build( VipsObject *object )
/* Build the tables of pre-computed coefficients.
*/
reduceh->n_points = vips_reduce_get_points( reduceh->kernel );
reduceh->n_points =
vips_reduce_get_points( reduceh->kernel, reduceh->xshrink );
if( reduceh->n_points > MAX_POINTS ) {
vips_error( object_class->nickname,
"%s", _( "reduce factor too large" ) );
return( -1 );
}
for( int x = 0; x < VIPS_TRANSFORM_SCALE + 1; x++ ) {
vips_reduce_make_mask( reduceh->kernel,
reduceh->matrixf[x] =
VIPS_ARRAY( object, reduceh->n_points, double );
reduceh->matrixi[x] =
VIPS_ARRAY( object, reduceh->n_points, int );
if( !reduceh->matrixf[x] ||
!reduceh->matrixi[x] )
return( -1 );
vips_reduce_make_mask( reduceh->kernel, reduceh->xshrink,
(float) x / VIPS_TRANSFORM_SCALE,
reduceh->matrixf[x] );

View File

@ -53,10 +53,6 @@
#include "presample.h"
#include "templates.h"
/* The max size of the vector we use.
*/
#define MAX_POINTS (6)
typedef struct _VipsReducev {
VipsResample parent_instance;
@ -74,8 +70,8 @@ typedef struct _VipsReducev {
* sizes up to short), and double (for all others). We go to
* scale + 1 so we can round-to-nearest safely.
*/
int matrixi[VIPS_TRANSFORM_SCALE + 1][MAX_POINTS];
double matrixf[VIPS_TRANSFORM_SCALE + 1][MAX_POINTS];
int *matrixi[VIPS_TRANSFORM_SCALE + 1];
double *matrixf[VIPS_TRANSFORM_SCALE + 1];
} VipsReducev;
@ -243,7 +239,7 @@ reducev_notab( VipsReducev *reducev,
double cy[MAX_POINTS];
vips_reduce_make_mask( reducev->kernel, y, cy );
vips_reduce_make_mask( reducev->kernel, reducev->yshrink, y, cy );
for( int z = 0; z < ne; z++ )
out[z] = reduce_sum<T, double>( in + z, l1, cy, n );
@ -379,7 +375,7 @@ vips_reducev_build( VipsObject *object )
if( reducev->yshrink < 1 ) {
vips_error( object_class->nickname,
"%s", _( "reduce factors should be >= 1" ) );
"%s", _( "reduce factor should be >= 1" ) );
return( -1 );
}
if( reducev->yshrink > 3 )
@ -391,12 +387,34 @@ vips_reducev_build( VipsObject *object )
/* Build the tables of pre-computed coefficients.
*/
reducev->n_points = vips_reduce_get_points( reducev->kernel );
reducev->n_points =
vips_reduce_get_points( reducev->kernel, reducev->yshrink );
if( reducev->n_points > MAX_POINTS ) {
vips_error( object_class->nickname,
"%s", _( "reduce factor too large" ) );
return( -1 );
}
for( int y = 0; y < VIPS_TRANSFORM_SCALE + 1; y++ ) {
vips_reduce_make_mask( reducev->kernel,
reducev->matrixf[y] =
VIPS_ARRAY( object, reducev->n_points, double );
reducev->matrixi[y] =
VIPS_ARRAY( object, reducev->n_points, int );
if( !reducev->matrixf[y] ||
!reducev->matrixi[y] )
return( -1 );
vips_reduce_make_mask( reducev->kernel, reducev->yshrink,
(float) y / VIPS_TRANSFORM_SCALE,
reducev->matrixf[y] );
#ifdef DEBUG
printf( "vips_reducev_build: masks:\n" );
printf( "%4g ", (double) y / VIPS_TRANSFORM_SCALE );
for( int i = 0; i < reducev->n_points; i++ )
printf( " %4g", reducev->matrixf[y][i] );
printf( "\n" );
#endif /*DEBUG*/
for( int i = 0; i < reducev->n_points; i++ )
reducev->matrixi[y][i] = reducev->matrixf[y][i] *
VIPS_INTERPOLATE_SCALE;

View File

@ -96,8 +96,6 @@ vips_resize_build( VipsObject *object )
int int_vshrink;
double hresidual;
double vresidual;
double sigma;
gboolean anti_alias;
if( VIPS_OBJECT_CLASS( vips_resize_parent_class )->build( object ) )
return( -1 );
@ -124,12 +122,12 @@ vips_resize_build( VipsObject *object )
if( resize->scale > 1.0 )
int_hshrink = 1;
else
int_hshrink = VIPS_FLOOR( 1.0 / (resize->scale * 1.4) );
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 * 1.4) );
int_vshrink = VIPS_FLOOR( 1.0 / (resize->vscale * 2) );
}
else
int_vshrink = int_hshrink;
@ -202,25 +200,8 @@ vips_resize_build( VipsObject *object )
in = t[6];
}
/* If the final affine will be doing a large downsample, we can get
* nasty aliasing on hard edges. Blur before affine to smooth this out.
*
* Don't blur for very small shrinks, or very small sigma.
*
* Don't try to be clever for non-rectangular shrinks. We just
* consider the horizontal factor.
/* Any downsizing.
*/
sigma = (1.0 / hresidual) / 2.7;
anti_alias = hresidual < 0.9 && sigma > 0.45;
if( anti_alias ) {
vips_info( class->nickname, "anti-alias sigma %g", sigma );
if( vips_gaussblur( in, &t[1], sigma,
"min_ampl", 0.1,
NULL ) )
return( -1 );
in = t[1];
}
if( hresidual < 1.0 ||
vresidual < 1.0 ) {
vips_info( class->nickname, "residual reduce by %g x %g",
@ -231,6 +212,9 @@ vips_resize_build( VipsObject *object )
return( -1 );
in = t[2];
}
/* Any upsizing.
*/
if( hresidual > 1.0 ||
vresidual > 1.0 ) {
vips_info( class->nickname, "residual scale %g x %g",
@ -242,17 +226,15 @@ vips_resize_build( VipsObject *object )
in = t[3];
}
/* If we are upsampling, don't sharpen. Also don't sharpen if we
* skipped the anti-alias filter.
/* If we are upsizing, don't sharpen.
*/
if( int_hshrink >= 1 &&
anti_alias ) {
if( int_hshrink >= 1 ) {
vips_info( class->nickname, "final sharpen" );
t[4] = vips_image_new_matrixv( 3, 3,
-1.0, -1.0, -1.0,
-1.0, 24.0, -1.0,
-1.0, 32.0, -1.0,
-1.0, -1.0, -1.0 );
vips_image_set_double( t[4], "scale", 16 );
vips_image_set_double( t[4], "scale", 24 );
if( vips_conv( in, &t[5], t[4], NULL ) )
return( -1 );

View File

@ -311,17 +311,28 @@ calculate_coefficients_catmull( const double x, double c[4] )
c[2] = cthr;
}
/* Given an offset in [0,1] (we can have x == 1 when building tables),
* calculate c0 .. c(a * 2 - 1), the lanczos coefficients. This is called
/* Given an x in [0,1] (we can have x == 1 when building tables),
* calculate c0 .. c(@a * @shrink + 1), the lanczos coefficients. This is called
* from the interpolator as well as from the table builder.
*
* @a is the number of lobes, so usually 2 or 3. @shrink is the reduction
* factor, so 1 for interpolation, 2 for a x2 reduction, etc. We need more
* points for large decimations to avoid aliasing.
*/
static void inline
calculate_coefficients_lanczos( int a, const double x, double *c )
calculate_coefficients_lanczos( const int a, const double shrink,
const double x, double *c )
{
int i;
/* Needs to be in sync with vips_reduce_get_points().
*/
const int n_points = 2 * a * ceil( shrink ) + 2;
for( i = 0; i < a * 2; i++ ) {
double xp = (i - a) + (1 - x);
int i;
double sum;
sum = 0;
for( i = 0; i < n_points; i++ ) {
double xp = (i - (n_points - 2) / 2 - x) / shrink;
double l;
@ -337,7 +348,11 @@ calculate_coefficients_lanczos( int a, const double x, double *c )
(VIPS_PI * VIPS_PI * xp * xp);
c[i] = l;
sum += l;
}
for( i = 0; i < n_points; i++ )
c[i] /= sum;
}
/* Our inner loop for resampling with a convolution. Operate on elements of

View File

@ -233,11 +233,11 @@ thumbnail_find_jpegshrink( VipsImage *im )
* doing no further processing, which would make a 400-px version look
* very different from a 450-px version.
*/
if( shrink >= 9 )
if( shrink >= 10 )
return( 8 );
else if( shrink >= 5 )
else if( shrink >= 6 )
return( 4 );
else if( shrink >= 3 )
else if( shrink >= 4 )
return( 2 );
else
return( 1 );