try making the reduce mask larger with residual
This commit is contained in:
parent
45c3fea6ac
commit
ff88087a28
@ -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
|
||||
}
|
||||
|
@ -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] );
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 );
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
|
Loading…
Reference in New Issue
Block a user