finish reduce

and fix up bicubic a bit, it works better on int32 images now
This commit is contained in:
John Cupitt 2016-02-07 17:41:39 +00:00
parent 39e6963033
commit 8cffd136e9
13 changed files with 439 additions and 92 deletions

View File

@ -1,6 +1,7 @@
29/1/16 started 8.3
- add vips_reduce*() ... a fast path for bicubic downsize
- vips_resize() and vips_similarity use it when they can
- bicubic is better on 32-bit int images
12/1/16 started 8.2.2
- changes to ease compiling C++ binding with MSVC [Lovell Fuller]

24
TODO
View File

@ -1,15 +1,3 @@
- strange, try
$ vips affine babe.jpg x.v "0.909 0 0 0.909"
$ vipsheader x.v
x.v: 931x698 uchar, 3 bands, srgb, jpegload
but 1024 * 0.909 == 930.9, shouldn't we be rounding down? I guess we are
rounding up
need to fix this if we want to be able to used reduce as a shortcut for
similarity
- new vips_reduce:
affine
@ -20,17 +8,15 @@
reduce
$ time vipsthumbnail wtc.tif -o x.tif -s 7000
real 0m1.818s
user 0m5.956s
sys 0m0.296s
add tests for vips_reduce(), update c++ binding, check docs and "see also"
lines
real 0m1.677s
user 0m5.996s
sys 0m0.300s
- get some brightly coloured spots with nohalo / vsqbs on wobble.ws ... very
odd, the interpolation shouldn't change between bands
probably over/underflow in interpolation ... clipping problems?
- still not happy about float->int mask conversion in im_vips2mask.c
- need to follow up on

View File

@ -1,5 +1,5 @@
// headers for vips operations
// Sat Jan 9 15:06:40 GMT 2016
// Sun Feb 7 16:27:14 GMT 2016
// this file is generated automatically, do not edit!
static void system( char * cmd_format , VOption *options = 0 );
@ -146,7 +146,9 @@ VImage mapim( VImage index , VOption *options = 0 );
VImage shrink( double xshrink , double yshrink , VOption *options = 0 );
VImage shrinkh( int xshrink , VOption *options = 0 );
VImage shrinkv( int yshrink , VOption *options = 0 );
VImage shrink2( double xshrink , double yshrink , VOption *options = 0 );
VImage reduceh( double xshrink , VOption *options = 0 );
VImage reducev( double yshrink , VOption *options = 0 );
VImage reduce( double xshrink , double yshrink , VOption *options = 0 );
VImage quadratic( VImage coeff , VOption *options = 0 );
VImage affine( std::vector<double> matrix , VOption *options = 0 );
VImage similarity( VOption *options = 0 );

View File

@ -1,5 +1,5 @@
// bodies for vips operations
// Sat Jan 9 15:05:58 GMT 2016
// Sun Feb 7 16:26:59 GMT 2016
// this file is generated automatically, do not edit!
void VImage::system( char * cmd_format , VOption *options )
@ -1783,11 +1783,37 @@ VImage VImage::shrinkv( int yshrink , VOption *options )
return( out );
}
VImage VImage::shrink2( double xshrink , double yshrink , VOption *options )
VImage VImage::reduceh( double xshrink , VOption *options )
{
VImage out;
call( "shrink2" ,
call( "reduceh" ,
(options ? options : VImage::option()) ->
set( "in", *this ) ->
set( "out", &out ) ->
set( "xshrink", xshrink ) );
return( out );
}
VImage VImage::reducev( double yshrink , VOption *options )
{
VImage out;
call( "reducev" ,
(options ? options : VImage::option()) ->
set( "in", *this ) ->
set( "out", &out ) ->
set( "yshrink", yshrink ) );
return( out );
}
VImage VImage::reduce( double xshrink , double yshrink , VOption *options )
{
VImage out;
call( "reduce" ,
(options ? options : VImage::option()) ->
set( "in", *this ) ->
set( "out", &out ) ->

View File

@ -9,7 +9,7 @@
<bookinfo>
<title>VIPS Reference Manual</title>
<releaseinfo>
For VIPS 8.2.2.
For VIPS 8.3.0.
The latest version of this documentation can be found on the
<ulink role="online-location"
url="http://www.vips.ecs.soton.ac.uk/index.php?title=Documentation">VIPS website</ulink>.

View File

@ -2,6 +2,8 @@
*
* 12/8/10
* - revise window_size / window_offset stuff again
* 7/2/16
* - double intermediate for 32-bit int types
*/
/*
@ -231,7 +233,7 @@ bicubic_signed_int_tab( void *pout, const VipsPel *pin,
}
}
/* Floating-point version, for int/float types.
/* Floating-point version.
*/
template <typename T>
static void inline
@ -294,6 +296,135 @@ bicubic_float_tab( void *pout, const VipsPel *pin,
}
}
/* uint32 version needs a double intermediate.
*/
template <typename T, int max_value>
static void inline
bicubic_unsigned_int32_tab( void *pout, const VipsPel *pin,
const int bands, const int lskip,
const double *cx, const double *cy )
{
T* restrict out = (T *) pout;
const T* restrict in = (T *) pin;
const int b1 = bands;
const int b2 = b1 + b1;
const int b3 = b1 + b2;
const int l1 = lskip / sizeof( T );
const int l2 = l1 + l1;
const int l3 = l1 + l2;
const int l1_plus_b1 = l1 + b1;
const int l1_plus_b2 = l1 + b2;
const int l1_plus_b3 = l1 + b3;
const int l2_plus_b1 = l2 + b1;
const int l2_plus_b2 = l2 + b2;
const int l2_plus_b3 = l2 + b3;
const int l3_plus_b1 = l3 + b1;
const int l3_plus_b2 = l3 + b2;
const int l3_plus_b3 = l3 + b3;
for( int z = 0; z < bands; z++ ) {
const T uno_one = in[0];
const T uno_two = in[b1];
const T uno_thr = in[b2];
const T uno_fou = in[b3];
const T dos_one = in[l1];
const T dos_two = in[l1_plus_b1];
const T dos_thr = in[l1_plus_b2];
const T dos_fou = in[l1_plus_b3];
const T tre_one = in[l2];
const T tre_two = in[l2_plus_b1];
const T tre_thr = in[l2_plus_b2];
const T tre_fou = in[l2_plus_b3];
const T qua_one = in[l3];
const T qua_two = in[l3_plus_b1];
const T qua_thr = in[l3_plus_b2];
const T qua_fou = in[l3_plus_b3];
double bicubic = bicubic_float<double>(
uno_one, uno_two, uno_thr, uno_fou,
dos_one, dos_two, dos_thr, dos_fou,
tre_one, tre_two, tre_thr, tre_fou,
qua_one, qua_two, qua_thr, qua_fou,
cx, cy );
bicubic = VIPS_CLIP( 0, bicubic, max_value );
out[z] = bicubic;
in += 1;
}
}
template <typename T, int min_value, int max_value>
static void inline
bicubic_signed_int32_tab( void *pout, const VipsPel *pin,
const int bands, const int lskip,
const double *cx, const double *cy )
{
T* restrict out = (T *) pout;
const T* restrict in = (T *) pin;
const int b1 = bands;
const int b2 = b1 + b1;
const int b3 = b1 + b2;
const int l1 = lskip / sizeof( T );
const int l2 = l1 + l1;
const int l3 = l1 + l2;
const int l1_plus_b1 = l1 + b1;
const int l1_plus_b2 = l1 + b2;
const int l1_plus_b3 = l1 + b3;
const int l2_plus_b1 = l2 + b1;
const int l2_plus_b2 = l2 + b2;
const int l2_plus_b3 = l2 + b3;
const int l3_plus_b1 = l3 + b1;
const int l3_plus_b2 = l3 + b2;
const int l3_plus_b3 = l3 + b3;
for( int z = 0; z < bands; z++ ) {
const T uno_one = in[0];
const T uno_two = in[b1];
const T uno_thr = in[b2];
const T uno_fou = in[b3];
const T dos_one = in[l1];
const T dos_two = in[l1_plus_b1];
const T dos_thr = in[l1_plus_b2];
const T dos_fou = in[l1_plus_b3];
const T tre_one = in[l2];
const T tre_two = in[l2_plus_b1];
const T tre_thr = in[l2_plus_b2];
const T tre_fou = in[l2_plus_b3];
const T qua_one = in[l3];
const T qua_two = in[l3_plus_b1];
const T qua_thr = in[l3_plus_b2];
const T qua_fou = in[l3_plus_b3];
double bicubic = bicubic_float<double>(
uno_one, uno_two, uno_thr, uno_fou,
dos_one, dos_two, dos_thr, dos_fou,
tre_one, tre_two, tre_thr, tre_fou,
qua_one, qua_two, qua_thr, qua_fou,
cx, cy );
bicubic = VIPS_CLIP( min_value, bicubic, max_value );
out[z] = bicubic;
in += 1;
}
}
/* Ultra-high-quality version for double images.
*/
template <typename T>
@ -461,12 +592,14 @@ vips_interpolate_bicubic_interpolate( VipsInterpolate *interpolate,
break;
case VIPS_FORMAT_UINT:
bicubic_float_tab<unsigned int>( out, p, bands, lskip,
bicubic_unsigned_int32_tab<unsigned int, INT_MAX>(
out, p, bands, lskip,
cxf, cyf );
break;
case VIPS_FORMAT_INT:
bicubic_float_tab<signed int>( out, p, bands, lskip,
bicubic_signed_int32_tab<signed int, INT_MIN, INT_MAX>(
out, p, bands, lskip,
cxf, cyf );
break;

View File

@ -160,9 +160,76 @@ reduceh_float_tab( VipsPel *pout, const VipsPel *pin,
for( int z = 0; z < bands; z++ ) {
out[z] =
c0 * in[0] +
c1 * in[1] +
c2 * in[2] +
c3 * in[3];
c1 * in[b1] +
c2 * in[b2] +
c3 * in[b3];
in += 1;
}
}
/* 32-bit output needs a double intermediate.
*/
template <typename T, int max_value>
static void inline
reduceh_unsigned_int32_tab( VipsPel *pout, const VipsPel *pin,
const int bands, const double *cx )
{
T* restrict out = (T *) pout;
const T* restrict in = (T *) pin;
const int b1 = bands;
const int b2 = b1 + b1;
const int b3 = b1 + b2;
const double c0 = cx[0];
const double c1 = cx[1];
const double c2 = cx[2];
const double c3 = cx[3];
for( int z = 0; z < bands; z++ ) {
double cubich =
c0 * in[0] +
c1 * in[b1] +
c2 * in[b2] +
c3 * in[b3];
cubich = VIPS_CLIP( 0, cubich, max_value );
out[z] = cubich;
in += 1;
}
}
template <typename T, int min_value, int max_value>
static void inline
reduceh_signed_int32_tab( VipsPel *pout, const VipsPel *pin,
const int bands, const double *cx )
{
T* restrict out = (T *) pout;
const T* restrict in = (T *) pin;
const int b1 = bands;
const int b2 = b1 + b1;
const int b3 = b1 + b2;
const double c0 = cx[0];
const double c1 = cx[1];
const double c2 = cx[2];
const double c3 = cx[3];
for( int z = 0; z < bands; z++ ) {
double cubich =
c0 * in[0] +
c1 * in[b1] +
c2 * in[b2] +
c3 * in[b3];
cubich = VIPS_CLIP( min_value, cubich, max_value );
out[z] = cubich;
in += 1;
}
@ -222,7 +289,7 @@ vips_reduceh_gen( VipsRegion *out_region, void *seq,
s.left = r->left * reduceh->xshrink;
s.top = r->top;
s.width = r->width * reduceh->xshrink + 3;
s.width = r->width * reduceh->xshrink + 4;
s.height = r->height;
if( vips_region_prepare( ir, &s ) )
return( -1 );
@ -271,12 +338,14 @@ vips_reduceh_gen( VipsRegion *out_region, void *seq,
break;
case VIPS_FORMAT_UINT:
reduceh_float_tab<unsigned int>(
reduceh_unsigned_int32_tab
<unsigned int, INT_MAX>(
q, p, bands, cxf );
break;
case VIPS_FORMAT_INT:
reduceh_float_tab<signed int>(
reduceh_signed_int32_tab
<signed int, INT_MIN, INT_MAX>(
q, p, bands, cxf );
break;
@ -326,9 +395,9 @@ vips_reduceh_build( VipsObject *object )
"%s", _( "reduce factors should be >= 1" ) );
return( -1 );
}
if( reduceh->xshrink > 2 )
if( reduceh->xshrink > 3 )
vips_warn( object_class->nickname,
"%s", _( "reduce factor greater than 2" ) );
"%s", _( "reduce factor greater than 3" ) );
if( reduceh->xshrink == 1 )
return( vips_image_write( in, resample->out ) );

View File

@ -140,8 +140,9 @@ reducev_signed_int_tab( VipsPel *pout, const VipsPel *pin,
}
}
/* Floating-point version, for int/float types.
/* Floating-point version.
*/
template <typename T>
static void inline
reducev_float_tab( VipsPel *pout, const VipsPel *pin,
@ -171,6 +172,75 @@ reducev_float_tab( VipsPel *pout, const VipsPel *pin,
}
}
/* 32-bit int version needs a double intermediate.
*/
template <typename T, int max_value>
static void inline
reducev_unsigned_int32_tab( VipsPel *pout, const VipsPel *pin,
const int ne, const int lskip,
const double *cy )
{
T* restrict out = (T *) pout;
const T* restrict in = (T *) pin;
const int l1 = lskip / sizeof( T );
const int l2 = l1 + l1;
const int l3 = l1 + l2;
const double c0 = cy[0];
const double c1 = cy[1];
const double c2 = cy[2];
const double c3 = cy[3];
for( int z = 0; z < ne; z++ ) {
double cubicv =
c0 * in[0] +
c1 * in[l1] +
c2 * in[l2] +
c3 * in[l3];
cubicv = VIPS_CLIP( 0, cubicv, max_value );
out[z] = cubicv;
in += 1;
}
}
template <typename T, int min_value, int max_value>
static void inline
reducev_signed_int32_tab( VipsPel *pout, const VipsPel *pin,
const int ne, const int lskip,
const double *cy )
{
T* restrict out = (T *) pout;
const T* restrict in = (T *) pin;
const int l1 = lskip / sizeof( T );
const int l2 = l1 + l1;
const int l3 = l1 + l2;
const double c0 = cy[0];
const double c1 = cy[1];
const double c2 = cy[2];
const double c3 = cy[3];
for( int z = 0; z < ne; z++ ) {
double cubicv =
c0 * in[0] +
c1 * in[l1] +
c2 * in[l2] +
c3 * in[l3];
cubicv = VIPS_CLIP( min_value, cubicv, max_value );
out[z] = cubicv;
in += 1;
}
}
/* Ultra-high-quality version for double images.
*/
template <typename T>
@ -231,7 +301,7 @@ vips_reducev_gen( VipsRegion *out_region, void *seq,
s.left = r->left;
s.top = r->top * reducev->yshrink;
s.width = r->width;
s.height = r->height * reducev->yshrink + 3;
s.height = r->height * reducev->yshrink + 4;
if( vips_region_prepare( ir, &s ) )
return( -1 );
@ -274,11 +344,15 @@ vips_reducev_gen( VipsRegion *out_region, void *seq,
break;
case VIPS_FORMAT_UINT:
reducev_float_tab<unsigned int>( q, p, ne, lskip, cyf );
reducev_unsigned_int32_tab
<unsigned int, INT_MAX>(
q, p, ne, lskip, cyf );
break;
case VIPS_FORMAT_INT:
reducev_float_tab<signed int>( q, p, ne, lskip, cyf );
reducev_signed_int32_tab
<signed int, INT_MIN, INT_MAX>(
q, p, ne, lskip, cyf );
break;
case VIPS_FORMAT_FLOAT:
@ -322,9 +396,9 @@ vips_reducev_build( VipsObject *object )
"%s", _( "reduce factors should be >= 1" ) );
return( -1 );
}
if( reducev->yshrink > 2 )
if( reducev->yshrink > 3 )
vips_warn( object_class->nickname,
"%s", _( "reduce factor greater than 2" ) );
"%s", _( "reduce factor greater than 3" ) );
if( reducev->yshrink == 1 )
return( vips_image_write( in, resample->out ) );

View File

@ -52,6 +52,28 @@
#include "presample.h"
/**
* SECTION: resample
* @short_description: resample images in various ways
* @stability: Stable
* @include: vips/vips.h
*
* There are three types of operation in this section.
*
* First, vips_affine() applies an affine transform to an image. This is any
* sort of 2D transform which preserves straight lines; so any combination of
* stretch, sheer, rotate and translate. You supply an interpolator for it to
* use to generate pixels, see vips_interpolate_new(). It will not produce
* good results for very large shrinks.
*
* Next, vips_resize() specialises in the common task of image reduce and
* enlarge. It strings together combinations of vips_shrink(), vips_reduce(),
* vips_affine() and others to implement a general, high-quality image
* resizer.
*
* Finally, vips_mapim() can apply arbitrary 2D image transforms to an image.
*/
G_DEFINE_ABSTRACT_TYPE( VipsResample, vips_resample, VIPS_TYPE_OPERATION );
static int

View File

@ -8,6 +8,8 @@
* rest of vips
* 13/8/14
* - oops, missing scale from b, thanks Topochicho
* 7/2/16
* - use vips_reduce(), if we can
*/
/*

View File

@ -154,18 +154,6 @@ unsigned_fixed_round( int v )
return( (v + round_by) >> VIPS_INTERPOLATE_SHIFT );
}
template <typename T> static int inline
cubic_unsigned_int(
const T one, const T two, const T thr, const T fou,
const int* restrict cx )
{
return( unsigned_fixed_round(
cx[0] * one +
cx[1] * two +
cx[2] * thr +
cx[3] * fou ) );
}
/* Fixed-point integer bicubic, used for 8 and 16-bit types.
*/
template <typename T> static int inline
@ -176,16 +164,37 @@ bicubic_unsigned_int(
const T qua_one, const T qua_two, const T qua_thr, const T qua_fou,
const int* restrict cx, const int* restrict cy )
{
const int r0 = cubic_unsigned_int<T>(
uno_one, uno_two, uno_thr, uno_fou, cx );
const int r1 = cubic_unsigned_int<T>(
dos_one, dos_two, dos_thr, dos_fou, cx );
const int r2 = cubic_unsigned_int<T>(
tre_one, tre_two, tre_thr, tre_fou, cx );
const int r3 = cubic_unsigned_int<T>(
qua_one, qua_two, qua_thr, qua_fou, cx );
const int c0 = cx[0];
const int c1 = cx[1];
const int c2 = cx[2];
const int c3 = cx[3];
return( cubic_unsigned_int<T>( r0, r1, r2, r3, cy ) );
const int r0 = unsigned_fixed_round(
c0 * uno_one +
c1 * uno_two +
c2 * uno_thr +
c3 * uno_fou );
const int r1 = unsigned_fixed_round(
c0 * dos_one +
c1 * dos_two +
c2 * dos_thr +
c3 * dos_fou );
const int r2 = unsigned_fixed_round(
c0 * tre_one +
c1 * tre_two +
c2 * tre_thr +
c3 * tre_fou );
const int r3 = unsigned_fixed_round(
c0 * qua_one +
c1 * qua_two +
c2 * qua_thr +
c3 * qua_fou );
return( unsigned_fixed_round(
cy[0] * r0 +
cy[1] * r1 +
cy[2] * r2 +
cy[3] * r3 ) );
}
static int inline
@ -197,18 +206,6 @@ signed_fixed_round( int v )
return( (v + round_by) >> VIPS_INTERPOLATE_SHIFT );
}
template <typename T> static int inline
cubic_signed_int(
const T one, const T two, const T thr, const T fou,
const int* restrict cx )
{
return( signed_fixed_round(
cx[0] * one +
cx[1] * two +
cx[2] * thr +
cx[3] * fou ) );
}
/* Fixed-point integer bicubic, used for 8 and 16-bit types.
*/
template <typename T> static int inline
@ -219,16 +216,37 @@ bicubic_signed_int(
const T qua_one, const T qua_two, const T qua_thr, const T qua_fou,
const int* restrict cx, const int* restrict cy )
{
const int r0 = cubic_signed_int<T>(
uno_one, uno_two, uno_thr, uno_fou, cx );
const int r1 = cubic_signed_int<T>(
dos_one, dos_two, dos_thr, dos_fou, cx );
const int r2 = cubic_signed_int<T>(
tre_one, tre_two, tre_thr, tre_fou, cx );
const int r3 = cubic_signed_int<T>(
qua_one, qua_two, qua_thr, qua_fou, cx );
const int c0 = cx[0];
const int c1 = cx[1];
const int c2 = cx[2];
const int c3 = cx[3];
return( cubic_signed_int<T>( r0, r1, r2, r3, cy ) );
const int r0 = signed_fixed_round(
c0 * uno_one +
c1 * uno_two +
c2 * uno_thr +
c3 * uno_fou );
const int r1 = signed_fixed_round(
c0 * dos_one +
c1 * dos_two +
c2 * dos_thr +
c3 * dos_fou );
const int r2 = signed_fixed_round(
c0 * tre_one +
c1 * tre_two +
c2 * tre_thr +
c3 * tre_fou );
const int r3 = signed_fixed_round(
c0 * qua_one +
c1 * qua_two +
c2 * qua_thr +
c3 * qua_fou );
return( signed_fixed_round(
cy[0] * r0 +
cy[1] * r1 +
cy[2] * r2 +
cy[3] * r3 ) );
}
template <typename T> static T inline

View File

@ -11,7 +11,6 @@ from gi.repository import Vips
Vips.leak_set(True)
unsigned_formats = [Vips.BandFormat.UCHAR,
Vips.BandFormat.USHORT,
Vips.BandFormat.UINT]

View File

@ -10,6 +10,20 @@ from gi.repository import Vips
Vips.leak_set(True)
unsigned_formats = [Vips.BandFormat.UCHAR,
Vips.BandFormat.USHORT,
Vips.BandFormat.UINT]
signed_formats = [Vips.BandFormat.CHAR,
Vips.BandFormat.SHORT,
Vips.BandFormat.INT]
float_formats = [Vips.BandFormat.FLOAT,
Vips.BandFormat.DOUBLE]
complex_formats = [Vips.BandFormat.COMPLEX,
Vips.BandFormat.DPCOMPLEX]
int_formats = unsigned_formats + signed_formats
noncomplex_formats = int_formats + float_formats
all_formats = int_formats + float_formats + complex_formats
# Run a function expecting a complex image on a two-band image
def run_cmplx(fn, image):
if image.format == Vips.BandFormat.FLOAT:
@ -105,13 +119,14 @@ class TestResample(unittest.TestCase):
bicubic = Vips.Interpolate.new("bicubic")
for fac in [1, 1.1, 1.5, 1.999]:
print("fac =", fac)
r = im.reduce(fac, fac)
a = im.affine([1.0 / fac, 0, 0, 1.0 / fac], interpolate = bicubic)
r.write_to_file("r.v")
a.write_to_file("a.v")
d = (r - a).abs().max()
self.assertLess(d, 0.1)
for fmt in all_formats:
x = im.cast(fmt)
r = x.reduce(fac, fac)
a = x.affine([1.0 / fac, 0, 0, 1.0 / fac],
interpolate = bicubic,
oarea = [0, 0, x.width / fac, x.height / fac])
d = (r - a).abs().max()
self.assertLess(d, 5)
def test_resize(self):
im = Vips.Image.new_from_file("images/IMG_4618.jpg")