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? - 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. /* The max size of the vector we use.
*/ */
#define MAX_POINTS (6) #define MAX_POINTS (50)
int vips_reduce_get_points( VipsKernel kernel ); int vips_reduce_get_points( VipsKernel kernel, double shrink );
void vips_reduce_make_mask( VipsKernel kernel, double x, double *c ); void vips_reduce_make_mask( VipsKernel kernel, double shrink,
double x, double *c );
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -81,8 +81,8 @@ typedef struct _VipsReduceh {
* sizes up to short), and double (for all others). We go to * sizes up to short), and double (for all others). We go to
* scale + 1 so we can round-to-nearest safely. * scale + 1 so we can round-to-nearest safely.
*/ */
int matrixi[VIPS_TRANSFORM_SCALE + 1][MAX_POINTS]; int *matrixi[VIPS_TRANSFORM_SCALE + 1];
double matrixf[VIPS_TRANSFORM_SCALE + 1][MAX_POINTS]; double *matrixf[VIPS_TRANSFORM_SCALE + 1];
} VipsReduceh; } VipsReduceh;
@ -94,10 +94,10 @@ extern "C" {
G_DEFINE_TYPE( VipsReduceh, vips_reduceh, VIPS_TYPE_RESAMPLE ); 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 int
vips_reduce_get_points( VipsKernel kernel ) vips_reduce_get_points( VipsKernel kernel, double shrink )
{ {
switch( kernel ) { switch( kernel ) {
case VIPS_KERNEL_NEAREST: case VIPS_KERNEL_NEAREST:
@ -110,10 +110,12 @@ vips_reduce_get_points( VipsKernel kernel )
return( 4 ); return( 4 );
case VIPS_KERNEL_LANCZOS2: 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: case VIPS_KERNEL_LANCZOS3:
return( 6 ); return( 2 * 3 * ceil( shrink ) + 2 );
default: default:
g_assert_not_reached(); g_assert_not_reached();
@ -121,10 +123,10 @@ vips_reduce_get_points( VipsKernel kernel )
} }
} }
/* Calculate a mask. /* Calculate a mask element.
*/ */
void 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 ) { switch( kernel ) {
case VIPS_KERNEL_NEAREST: case VIPS_KERNEL_NEAREST:
@ -141,11 +143,11 @@ vips_reduce_make_mask( VipsKernel kernel, double x, double *c )
break; break;
case VIPS_KERNEL_LANCZOS2: case VIPS_KERNEL_LANCZOS2:
calculate_coefficients_lanczos( 2, x, c ); calculate_coefficients_lanczos( 2, shrink, x, c );
break; break;
case VIPS_KERNEL_LANCZOS3: case VIPS_KERNEL_LANCZOS3:
calculate_coefficients_lanczos( 3, x, c ); calculate_coefficients_lanczos( 3, shrink, x, c );
break; break;
default: 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 * 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 * 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]; 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++ ) { for( int z = 0; z < bands; z++ ) {
out[z] = reduce_sum<T, double>( in, bands, cx, n ); 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. /* 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++ ) { 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, (float) x / VIPS_TRANSFORM_SCALE,
reduceh->matrixf[x] ); reduceh->matrixf[x] );

View File

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

View File

@ -96,8 +96,6 @@ vips_resize_build( VipsObject *object )
int int_vshrink; int int_vshrink;
double hresidual; double hresidual;
double vresidual; double vresidual;
double sigma;
gboolean anti_alias;
if( VIPS_OBJECT_CLASS( vips_resize_parent_class )->build( object ) ) if( VIPS_OBJECT_CLASS( vips_resize_parent_class )->build( object ) )
return( -1 ); return( -1 );
@ -124,12 +122,12 @@ vips_resize_build( VipsObject *object )
if( resize->scale > 1.0 ) if( resize->scale > 1.0 )
int_hshrink = 1; int_hshrink = 1;
else 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( vips_object_argument_isset( object, "vscale" ) ) {
if( resize->vscale > 1.0 ) if( resize->vscale > 1.0 )
int_vshrink = 1; int_vshrink = 1;
else else
int_vshrink = VIPS_FLOOR( 1.0 / (resize->vscale * 1.4) ); int_vshrink = VIPS_FLOOR( 1.0 / (resize->vscale * 2) );
} }
else else
int_vshrink = int_hshrink; int_vshrink = int_hshrink;
@ -202,25 +200,8 @@ vips_resize_build( VipsObject *object )
in = t[6]; in = t[6];
} }
/* If the final affine will be doing a large downsample, we can get /* Any downsizing.
* 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.
*/ */
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 || if( hresidual < 1.0 ||
vresidual < 1.0 ) { vresidual < 1.0 ) {
vips_info( class->nickname, "residual reduce by %g x %g", vips_info( class->nickname, "residual reduce by %g x %g",
@ -231,6 +212,9 @@ vips_resize_build( VipsObject *object )
return( -1 ); return( -1 );
in = t[2]; in = t[2];
} }
/* Any upsizing.
*/
if( hresidual > 1.0 || if( hresidual > 1.0 ||
vresidual > 1.0 ) { vresidual > 1.0 ) {
vips_info( class->nickname, "residual scale %g x %g", vips_info( class->nickname, "residual scale %g x %g",
@ -242,17 +226,15 @@ vips_resize_build( VipsObject *object )
in = t[3]; in = t[3];
} }
/* If we are upsampling, don't sharpen. Also don't sharpen if we /* If we are upsizing, don't sharpen.
* skipped the anti-alias filter.
*/ */
if( int_hshrink >= 1 && if( int_hshrink >= 1 ) {
anti_alias ) {
vips_info( class->nickname, "final sharpen" ); vips_info( class->nickname, "final sharpen" );
t[4] = vips_image_new_matrixv( 3, 3, t[4] = vips_image_new_matrixv( 3, 3,
-1.0, -1.0, -1.0, -1.0, -1.0, -1.0,
-1.0, 24.0, -1.0, -1.0, 32.0, -1.0,
-1.0, -1.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 ) ) if( vips_conv( in, &t[5], t[4], NULL ) )
return( -1 ); return( -1 );

View File

@ -311,17 +311,28 @@ calculate_coefficients_catmull( const double x, double c[4] )
c[2] = cthr; c[2] = cthr;
} }
/* Given an offset in [0,1] (we can have x == 1 when building tables), /* Given an x in [0,1] (we can have x == 1 when building tables),
* calculate c0 .. c(a * 2 - 1), the lanczos coefficients. This is called * calculate c0 .. c(@a * @shrink + 1), the lanczos coefficients. This is called
* from the interpolator as well as from the table builder. * 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 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++ ) { int i;
double xp = (i - a) + (1 - x); double sum;
sum = 0;
for( i = 0; i < n_points; i++ ) {
double xp = (i - (n_points - 2) / 2 - x) / shrink;
double l; double l;
@ -337,7 +348,11 @@ calculate_coefficients_lanczos( int a, const double x, double *c )
(VIPS_PI * VIPS_PI * xp * xp); (VIPS_PI * VIPS_PI * xp * xp);
c[i] = l; 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 /* 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 * doing no further processing, which would make a 400-px version look
* very different from a 450-px version. * very different from a 450-px version.
*/ */
if( shrink >= 9 ) if( shrink >= 10 )
return( 8 ); return( 8 );
else if( shrink >= 5 ) else if( shrink >= 6 )
return( 4 ); return( 4 );
else if( shrink >= 3 ) else if( shrink >= 4 )
return( 2 ); return( 2 );
else else
return( 1 ); return( 1 );