make linear and cubic adaptive

This commit is contained in:
John Cupitt 2017-10-15 22:09:44 +01:00
parent cfa96f735a
commit 68ed42e2fa
6 changed files with 90 additions and 28 deletions

View File

@ -28,6 +28,7 @@
- vips_image_write() severs all links between images, when it can ... thanks
Warren and Nakilon
- vector path for convolution is more accurate and can handle larger masks
- linear and cubic kernels for reduce are higer quality
29/8/17 started 8.5.9
- make --fail stop jpeg read on any libjpeg warning, thanks @mceachen

7
TODO
View File

@ -1,9 +1,4 @@
- vips_image_write() to a temp file is rather broken
things like reorder and upstream/downstream will still be keeping links after
write, so unreffing the input will eventually trigger a crash
rewind should break more things ... does it remove upsteam/downstream? does it
- rewind should break more things ... does it remove upsteam/downstream? does it
just need to remove reorder links?
perhaps reorder should use upstream/downstream, then it will be broken anyway

View File

@ -57,14 +57,12 @@
/**
* VipsKernel:
* @VIPS_KERNEL_NEAREST: The nearest pixel to the point.
* @VIPS_KERNEL_LINEAR: Calculate a pixel value using linear interpolation.
* @VIPS_KERNEL_CUBIC: Calculate using a 4-element cubic kernel.
* @VIPS_KERNEL_LANCZOS2: Calculate with a two-lobe Lanczos kernel.
* @VIPS_KERNEL_LANCZOS3: Calculate with a three-lobe Lanczos kernel.
* @VIPS_KERNEL_LINEAR: Convolve with a triangle filter.
* @VIPS_KERNEL_CUBIC: Convolve with a cubic filter.
* @VIPS_KERNEL_LANCZOS2: Convolve with a two-lobe Lanczos kernel.
* @VIPS_KERNEL_LANCZOS3: Convolve with a three-lobe Lanczos kernel.
*
* The resampling kernels vips supports. See vips_reduce(), for example.
*
* The Lanczos kernels vary in size with the downsampling ratio.
*/
typedef struct _VipsReduce {

View File

@ -101,10 +101,10 @@ vips_reduce_get_points( VipsKernel kernel, double shrink )
return( 1 );
case VIPS_KERNEL_LINEAR:
return( 2 );
return( rint( 2 * shrink ) + 1 );
case VIPS_KERNEL_CUBIC:
return( 4 );
return( rint( 4 * shrink ) + 1 );
case VIPS_KERNEL_LANCZOS2:
/* Needs to be in sync with calculate_coefficients_lanczos().
@ -131,12 +131,11 @@ vips_reduce_make_mask( double *c, VipsKernel kernel, double shrink, double x )
break;
case VIPS_KERNEL_LINEAR:
c[0] = 1.0 - x;
c[1] = x;
calculate_coefficients_triangle( c, shrink, x );
break;
case VIPS_KERNEL_CUBIC:
calculate_coefficients_catmull( c, x );
calculate_coefficients_adaptive_catmull( c, shrink, x );
break;
case VIPS_KERNEL_LANCZOS2:

View File

@ -24,6 +24,8 @@
* - add @centre option
* 6/3/17
* - moved the cache to shrinkv
* 15/10/17
* - make LINEAR and CUBIC adaptive
*/
/*
@ -101,13 +103,10 @@ G_DEFINE_TYPE( VipsResize, vips_resize, VIPS_TYPE_RESAMPLE );
* 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.
* The others are 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.
@ -127,12 +126,9 @@ vips_resize_int_shrink( VipsResize *resize, double scale )
case VIPS_KERNEL_LINEAR:
case VIPS_KERNEL_CUBIC:
default:
shrink = VIPS_FLOOR( 1.0 / scale );
break;
case VIPS_KERNEL_LANCZOS2:
case VIPS_KERNEL_LANCZOS3:
default:
shrink = VIPS_MAX( 1, VIPS_FLOOR( 1.0 / (scale * 2) ) );
break;
}

View File

@ -311,6 +311,48 @@ calculate_coefficients_catmull( double c[4], const double x )
c[2] = cthr;
}
/* Calculate a catmull kernel for shrinking.
*/
static void inline
calculate_coefficients_adaptive_catmull( double *c,
const double shrink, const double x )
{
/* Needs to be in sync with vips_reduce_get_points().
*/
const int n_points = rint( 4 * shrink ) + 1;
int i;
double sum;
sum = 0;
for( i = 0; i < n_points; i++ ) {
const double xp = (i - (2 * shrink - 1) - x) / shrink;
const double axp = VIPS_FABS( xp );
const double axp2 = axp * axp;
const double axp3 = axp2 * axp;
const double a = -0.5;
double l;
if( axp <= 1 )
l = (a + 2) * axp3 -
(a + 3) * axp2 + 1;
else if( axp <= 2 )
l = a * axp3 -
5 * a * axp2 +
8 * a * axp -
4 * a;
else
l = 0.0;
c[i] = l;
sum += l;
}
for( i = 0; i < n_points; i++ )
c[i] /= sum;
}
/* 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.
@ -355,6 +397,37 @@ calculate_coefficients_lanczos( double *c,
c[i] /= sum;
}
/* Given an x in [0,1] (we can have x == 1 when building tables),
* calculate c0 .. c(@shrink + 1), the triangle coefficients. This is called
* from the interpolator as well as from the table builder.
*/
static void inline
calculate_coefficients_triangle( double *c, const double shrink, const double x )
{
/* Needs to be in sync with vips_reduce_get_points().
*/
const int n_points = rint( 2 * shrink ) + 1;
int i;
double sum;
sum = 0;
for( i = 0; i < n_points; i++ ) {
double xp = (i - (shrink - 1) - x) / shrink;
double l;
l = 1.0 - VIPS_FABS( xp );
l = VIPS_MAX( 0.0, 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
* type T, gather results in an intermediate of type IT.
*/