in-line reducev

bit quicker
This commit is contained in:
John Cupitt 2016-01-29 13:16:00 +00:00
parent 3bb24f2567
commit dd7b30bd00
7 changed files with 449 additions and 688 deletions

8
TODO
View File

@ -28,6 +28,14 @@
reduceh ... not so clear, need to pick new masks every output pixel, might reduceh ... not so clear, need to pick new masks every output pixel, might
not make much difference not make much difference
with in-line reducev
$ time vipsthumbnail wtc.tif -o x.tif -s 7000
real 0m1.897s
user 0m6.296s
sys 0m0.304s
- get some brightly coloured spots with nohalo / vsqbs on wobble.ws ... very - get some brightly coloured spots with nohalo / vsqbs on wobble.ws ... very

View File

@ -11,12 +11,11 @@ libresample_la_SOURCES = \
shrinkv.c \ shrinkv.c \
reduce.c \ reduce.c \
reduceh.c \ reduceh.c \
reducev.c \ reducev.cpp \
interpolate.c \ interpolate.c \
transform.c \ transform.c \
bicubic.cpp \ bicubic.cpp \
cubich.cpp \ cubich.cpp \
cubicv.cpp \
lbb.cpp \ lbb.cpp \
nohalo.cpp \ nohalo.cpp \
vsqbs.cpp \ vsqbs.cpp \

View File

@ -1,378 +0,0 @@
/* vertical cubic (catmull-rom) interpolator
*
* 26/1/16
* - from bicubic.cpp
*/
/*
This file is part of VIPS.
VIPS is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
/* Cubic (Catmull-Rom) interpolator derived from Nicolas Robidoux's
* original YAFR resampler with permission and thanks.
*/
/*
#define DEBUG
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <stdlib.h>
#include <vips/vips.h>
#include <vips/internal.h>
#include "templates.h"
#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif /*WITH_DMALLOC*/
#define VIPS_TYPE_INTERPOLATE_CUBICV \
(vips_interpolate_cubicv_get_type())
#define VIPS_INTERPOLATE_CUBICV( obj ) \
(G_TYPE_CHECK_INSTANCE_CAST( (obj), \
VIPS_TYPE_INTERPOLATE_CUBICV, VipsInterpolateCubicv ))
#define VIPS_INTERPOLATE_CUBICV_CLASS( klass ) \
(G_TYPE_CHECK_CLASS_CAST( (klass), \
VIPS_TYPE_INTERPOLATE_CUBICV, VipsInterpolateCubicvClass))
#define VIPS_IS_INTERPOLATE_CUBICV( obj ) \
(G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_INTERPOLATE_CUBICV ))
#define VIPS_IS_INTERPOLATE_CUBICV_CLASS( klass ) \
(G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_INTERPOLATE_CUBICV ))
#define VIPS_INTERPOLATE_CUBICV_GET_CLASS( obj ) \
(G_TYPE_INSTANCE_GET_CLASS( (obj), \
VIPS_TYPE_INTERPOLATE_CUBICV, VipsInterpolateCubicvClass ))
typedef VipsInterpolate VipsInterpolateCubicv;
typedef VipsInterpolateClass VipsInterpolateCubicvClass;
/* Precalculated interpolation matrices. int (used for pel
* sizes up to short), and double (for all others). We go to
* scale + 1 so we can round-to-nearest safely.
*/
/* We could keep a large set of 2d 4x4 matricies, but this actually
* works out slower since for many resizes the thing will no longer
* fit in L1.
*/
static int vips_cubicv_matrixi[VIPS_TRANSFORM_SCALE + 1][4];
static double vips_cubicv_matrixf[VIPS_TRANSFORM_SCALE + 1][4];
/* We need C linkage for this.
*/
extern "C" {
G_DEFINE_TYPE( VipsInterpolateCubicv, vips_interpolate_cubicv,
VIPS_TYPE_INTERPOLATE );
}
/* Pointers to write to / read from, number of bands,
* how many bytes to add to move down a line.
*/
/* T is the type of pixels we are reading and writing.
*/
/* Fixed-point version, for 8 and 16-bit types.
*/
template <typename T, int max_value>
static void inline
cubicv_unsigned_int_tab( void *pout, const VipsPel *pin,
const int bands, const int lskip,
const int *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;
for( int z = 0; z < bands; z++ ) {
const T uno = in[0];
const T dos = in[l1];
const T tre = in[l2];
const T qua = in[l3];
int cubicv = cubic_unsigned_int<T>( uno, dos, tre, qua, cy );
cubicv = VIPS_CLIP( 0, cubicv, max_value );
out[z] = cubicv;
in += 1;
}
}
template <typename T, int min_value, int max_value>
static void inline
cubicv_signed_int_tab( void *pout, const VipsPel *pin,
const int bands, const int lskip,
const int *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;
for( int z = 0; z < bands; z++ ) {
const T uno = in[0];
const T dos = in[l1];
const T tre = in[l2];
const T qua = in[l3];
int cubicv = cubic_signed_int<T>( uno, dos, tre, qua, cy );
cubicv = VIPS_CLIP( min_value, cubicv, max_value );
out[z] = cubicv;
in += 1;
}
}
/* Floating-point version, for int/float types.
*/
template <typename T>
static void inline
cubicv_float_tab( void *pout, const VipsPel *pin,
const int bands, 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;
for( int z = 0; z < bands; z++ ) {
const T uno = in[0];
const T dos = in[l1];
const T tre = in[l2];
const T qua = in[l3];
out[z] = cubic_float<T>( uno, dos, tre, qua, cy );
in += 1;
}
}
/* Ultra-high-quality version for double images.
*/
template <typename T>
static void inline
cubicv_notab( void *pout, const VipsPel *pin,
const int bands, const int lskip,
double y )
{
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;
double cy[4];
calculate_coefficients_catmull( y, cy );
for( int z = 0; z < bands; z++ ) {
const T uno = in[0];
const T dos = in[l1];
const T tre = in[l2];
const T qua = in[l3];
out[z] = cubic_float<T>( uno, dos, tre, qua, cy );
in += 1;
}
}
static void
vips_interpolate_cubicv_interpolate( VipsInterpolate *interpolate,
void *out, VipsRegion *in, double x, double y )
{
/* Find the mask index. We round-to-nearest, so we need to generate
* indexes in 0 to VIPS_TRANSFORM_SCALE, 2^n + 1 values. We multiply
* by 2 more than we need to, add one, mask, then shift down again to
* get the extra range.
*/
const int sy = y * VIPS_TRANSFORM_SCALE * 2;
const int siy = sy & (VIPS_TRANSFORM_SCALE * 2 - 1);
const int ty = (siy + 1) >> 1;
/* We know x/y are always positive, so we can just (int) them.
*/
const int ix = (int) x;
const int iy = (int) y;
/* Back and up one to get the top-left of the 4x4.
*/
const VipsPel *p = VIPS_REGION_ADDR( in, ix - 1, iy - 1 );
/* Look up the tables we need.
*/
const int *cyi = vips_cubicv_matrixi[ty];
const double *cyf = vips_cubicv_matrixf[ty];
/* Pel size and line size.
*/
const int bands = in->im->Bands;
const int lskip = VIPS_REGION_LSKIP( in );
g_assert( ix >= in->valid.left );
g_assert( iy - 1 >= in->valid.top );
g_assert( ix < VIPS_RECT_RIGHT( &in->valid ) );
g_assert( iy + 2 < VIPS_RECT_BOTTOM( &in->valid ) );
g_assert( ix == x );
/* Confirm that absolute_y is >= 1, because of window_offset.
*/
g_assert( y >= 1.0 );
#ifdef DEBUG
printf( "vips_interpolate_cubicv_interpolate: %g %g\n", x, y );
printf( "\tleft=%d, top=%d, width=%d, height=%d\n",
ix, iy - 1, 1, 4 );
printf( "\tmasky=%d\n", ty );
#endif /*DEBUG*/
switch( in->im->BandFmt ) {
case VIPS_FORMAT_UCHAR:
cubicv_unsigned_int_tab<unsigned char, UCHAR_MAX>(
out, p, bands, lskip,
cyi );
/*
Handy for benchmarking
cubicv_float_tab<unsigned char>(
out, p, bands, lskip,
cyf );
cubicv_notab<unsigned char>(
out, p, bands, lskip,
y - iy );
*/
break;
case VIPS_FORMAT_CHAR:
cubicv_signed_int_tab<signed char, SCHAR_MIN, SCHAR_MAX>(
out, p, bands, lskip, cyi );
break;
case VIPS_FORMAT_USHORT:
cubicv_unsigned_int_tab<unsigned short, USHRT_MAX>(
out, p, bands, lskip, cyi );
break;
case VIPS_FORMAT_SHORT:
cubicv_signed_int_tab<signed short, SHRT_MIN, SHRT_MAX>(
out, p, bands, lskip, cyi );
break;
case VIPS_FORMAT_UINT:
cubicv_float_tab<unsigned int>( out, p, bands, lskip, cyf );
break;
case VIPS_FORMAT_INT:
cubicv_float_tab<signed int>( out, p, bands, lskip, cyf );
break;
case VIPS_FORMAT_FLOAT:
cubicv_float_tab<float>( out, p, bands, lskip, cyf );
break;
case VIPS_FORMAT_DOUBLE:
cubicv_notab<double>( out, p, bands, lskip, y - iy );
break;
case VIPS_FORMAT_COMPLEX:
cubicv_float_tab<float>( out, p, bands * 2, lskip, cyf );
break;
case VIPS_FORMAT_DPCOMPLEX:
cubicv_notab<double>( out, p, bands * 2, lskip, y - iy );
break;
default:
break;
}
}
static void
vips_interpolate_cubicv_class_init( VipsInterpolateCubicvClass *iclass )
{
VipsObjectClass *object_class = VIPS_OBJECT_CLASS( iclass );
VipsInterpolateClass *interpolate_class =
VIPS_INTERPOLATE_CLASS( iclass );
object_class->nickname = "cubicv";
object_class->description =
_( "vertical cubic interpolation (Catmull-Rom)" );
interpolate_class->interpolate = vips_interpolate_cubicv_interpolate;
interpolate_class->window_size = 4;
/* Build the tables of pre-computed coefficients.
*/
for( int y = 0; y < VIPS_TRANSFORM_SCALE + 1; y++ ) {
calculate_coefficients_catmull(
(float) y / VIPS_TRANSFORM_SCALE,
vips_cubicv_matrixf[y] );
for( int i = 0; i < 4; i++ )
vips_cubicv_matrixi[y][i] =
vips_cubicv_matrixf[y][i] *
VIPS_INTERPOLATE_SCALE;
}
}
static void
vips_interpolate_cubicv_init( VipsInterpolateCubicv *cubicv )
{
#ifdef DEBUG
printf( "vips_interpolate_cubicv_init: " );
vips_object_print( VIPS_OBJECT( cubicv ) );
#endif /*DEBUG*/
}

View File

@ -601,7 +601,6 @@ vips__interpolate_init( void )
{ {
extern GType vips_interpolate_bicubic_get_type( void ); extern GType vips_interpolate_bicubic_get_type( void );
extern GType vips_interpolate_cubich_get_type( void ); extern GType vips_interpolate_cubich_get_type( void );
extern GType vips_interpolate_cubicv_get_type( void );
extern GType vips_interpolate_lbb_get_type( void ); extern GType vips_interpolate_lbb_get_type( void );
extern GType vips_interpolate_nohalo_get_type( void ); extern GType vips_interpolate_nohalo_get_type( void );
extern GType vips_interpolate_vsqbs_get_type( void ); extern GType vips_interpolate_vsqbs_get_type( void );
@ -611,7 +610,6 @@ vips__interpolate_init( void )
vips_interpolate_bicubic_get_type(); vips_interpolate_bicubic_get_type();
vips_interpolate_cubich_get_type(); vips_interpolate_cubich_get_type();
vips_interpolate_cubicv_get_type();
vips_interpolate_lbb_get_type(); vips_interpolate_lbb_get_type();
vips_interpolate_nohalo_get_type(); vips_interpolate_nohalo_get_type();
vips_interpolate_vsqbs_get_type(); vips_interpolate_vsqbs_get_type();

View File

@ -56,7 +56,6 @@ typedef struct _VipsReduce {
double xshrink; /* Shrink factors */ double xshrink; /* Shrink factors */
double yshrink; double yshrink;
VipsInterpolate *interpolateh; VipsInterpolate *interpolateh;
VipsInterpolate *interpolatev;
} VipsReduce; } VipsReduce;
@ -82,14 +81,13 @@ vips_reduce_build( VipsObject *object )
"tile_height", 4, "tile_height", 4,
NULL ) || NULL ) ||
vips_reducev( t[1], &t[2], reduce->yshrink, vips_reducev( t[1], &t[2], reduce->yshrink,
"interpolate", reduce->interpolatev, NULL ) || NULL ) ||
vips_image_write( t[2], resample->out ) ) vips_image_write( t[2], resample->out ) )
return( -1 ); return( -1 );
*/ */
/* /*
if( vips_reducev( resample->in, &t[0], reduce->yshrink, if( vips_reducev( resample->in, &t[0], reduce->yshrink,
"interpolate", reduce->interpolatev,
NULL ) || NULL ) ||
vips_linecache( t[0], &t[1], vips_linecache( t[0], &t[1],
"tile_height", 4, "tile_height", 4,
@ -101,8 +99,7 @@ vips_reduce_build( VipsObject *object )
return( -1 ); return( -1 );
*/ */
if( vips_reducev( resample->in, &t[0], reduce->yshrink, if( vips_reducev( resample->in, &t[0], reduce->yshrink, NULL ) ||
"interpolate", reduce->interpolatev, NULL ) ||
vips_reduceh( t[0], &t[1], reduce->xshrink, vips_reduceh( t[0], &t[1], reduce->xshrink,
"interpolate", reduce->interpolateh, NULL ) || "interpolate", reduce->interpolateh, NULL ) ||
vips_image_write( t[1], resample->out ) ) vips_image_write( t[1], resample->out ) )
@ -111,8 +108,7 @@ vips_reduce_build( VipsObject *object )
/* /*
if( vips_reduceh( resample->in, &t[0], reduce->xshrink, if( vips_reduceh( resample->in, &t[0], reduce->xshrink,
"interpolate", reduce->interpolateh, NULL ) || "interpolate", reduce->interpolateh, NULL ) ||
vips_reducev( t[0], &t[1], reduce->yshrink, vips_reducev( t[0], &t[1], reduce->yshrink, NULL ) ||
"interpolate", reduce->interpolatev, NULL ) ||
vips_image_write( t[1], resample->out ) ) vips_image_write( t[1], resample->out ) )
return( -1 ); return( -1 );
*/ */
@ -158,12 +154,6 @@ vips_reduce_class_init( VipsReduceClass *class )
VIPS_ARGUMENT_OPTIONAL_INPUT, VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsReduce, interpolateh ) ); G_STRUCT_OFFSET( VipsReduce, interpolateh ) );
VIPS_ARG_INTERPOLATE( class, "interpolatev", 10,
_( "Interpolatev" ),
_( "Interpolate vertical pixels with this" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsReduce, interpolatev ) );
} }
static void static void
@ -182,7 +172,6 @@ vips_reduce_init( VipsReduce *reduce )
* Optional arguments: * Optional arguments:
* *
* @interpolateh: interpolate horizontally with this, default cubich * @interpolateh: interpolate horizontally with this, default cubich
* @interpolatev: interpolate vertically with this, default cubicv
* *
* Reduce @in by a pair of factors with a pair of 1D interpolators. This iwll * Reduce @in by a pair of factors with a pair of 1D interpolators. This iwll
* not work well for shrink factors greater than two. * not work well for shrink factors greater than two.

View File

@ -1,292 +0,0 @@
/* horizontal reduce by a float factor
*
* 30/10/15
* - from reducev.c
*/
/*
This file is part of VIPS.
VIPS is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
/*
#define DEBUG
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <vips/vips.h>
#include <vips/debug.h>
#include <vips/internal.h>
#include "presample.h"
typedef struct _VipsReducev {
VipsResample parent_instance;
double yshrink; /* Shrink factor */
VipsInterpolate *interpolate;
} VipsReducev;
typedef VipsResampleClass VipsReducevClass;
G_DEFINE_TYPE( VipsReducev, vips_reducev, VIPS_TYPE_RESAMPLE );
static int
vips_reducev_gen( VipsRegion *or, void *seq,
void *a, void *b, gboolean *stop )
{
VipsImage *in = (VipsImage *) a;
VipsReducev *reducev = (VipsReducev *) b;
int window_size =
vips_interpolate_get_window_size( reducev->interpolate );
int window_offset =
vips_interpolate_get_window_offset( reducev->interpolate );
const VipsInterpolateMethod interpolate =
vips_interpolate_get_method( reducev->interpolate );
int ps = VIPS_IMAGE_SIZEOF_PEL( in );
VipsRegion *ir = (VipsRegion *) seq;
VipsRect *r = &or->valid;
VipsRect s;
int y;
#ifdef DEBUG
printf( "vips_reducev_gen: generating %d x %d at %d x %d\n",
r->width, r->height, r->left, r->top );
#endif /*DEBUG*/
s.left = r->left;
s.top = r->top * reducev->yshrink - window_offset;
s.width = r->width;
s.height = r->height * reducev->yshrink + window_size - 1;
if( vips_region_prepare( ir, &s ) )
return( -1 );
VIPS_GATE_START( "vips_reducev_gen: work" );
for( y = 0; y < r->height; y ++ ) {
VipsPel *q = VIPS_REGION_ADDR( or, r->left, r->top + y );
double X = r->left;
double Y = window_offset + (r->top + y) * reducev->yshrink;
int x;
for( x = 0; x < r->width; x++ ) {
interpolate( reducev->interpolate, q, ir, X, Y );
X += 1;
q += ps;
}
}
VIPS_GATE_STOP( "vips_reducev_gen: work" );
return( 0 );
}
static int
vips_reducev_build( VipsObject *object )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
VipsResample *resample = VIPS_RESAMPLE( object );
VipsReducev *reducev = (VipsReducev *) object;
VipsImage **t = (VipsImage **)
vips_object_local_array( object, 1 );
VipsImage *in;
int window_size;
int window_offset;
if( VIPS_OBJECT_CLASS( vips_reducev_parent_class )->build( object ) )
return( -1 );
in = resample->in;
/* We can't use vips_object_argument_isset(), since it may have been
* set to NULL, see vips_similarity().
*/
if( !reducev->interpolate ) {
VipsInterpolate *interpolate;
interpolate = vips_interpolate_new( "cubich" );
g_object_set( object,
"interpolate", interpolate,
NULL );
g_object_unref( interpolate );
/* coverity gets confused by this, it thinks
* reducev->interpolate may still be null. Assign ourselves,
* even though we don't need to.
*/
reducev->interpolate = interpolate;
}
window_size = vips_interpolate_get_window_size( reducev->interpolate );
window_offset =
vips_interpolate_get_window_offset( reducev->interpolate );
if( reducev->yshrink < 1 ) {
vips_error( class->nickname,
"%s", _( "reduce factors should be >= 1" ) );
return( -1 );
}
if( reducev->yshrink > 2 )
vips_warn( class->nickname,
"%s", _( "reduce factor greater than 2" ) );
if( reducev->yshrink == 1 )
return( vips_image_write( in, resample->out ) );
/* Unpack for processing.
*/
if( vips_image_decode( in, &t[0] ) )
return( -1 );
in = t[0];
/* Add new pixels around the input so we can interpolate at the edges.
*/
if( vips_embed( in, &t[1],
0, window_offset,
in->Xsize, in->Ysize + window_size - 1,
"extend", VIPS_EXTEND_COPY,
NULL ) )
return( -1 );
in = t[1];
if( vips_image_pipelinev( resample->out,
VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ) )
return( -1 );
/* Size output. Note: we round the output width down!
*
* Don't change xres/yres, leave that to the application layer. For
* example, vipsthumbnail knows the true reduce factor (including the
* fractional part), we just see the integer part here.
*/
resample->out->Ysize = (in->Ysize - window_size + 1) / reducev->yshrink;
if( resample->out->Ysize <= 0 ) {
vips_error( class->nickname,
"%s", _( "image has shrunk to nothing" ) );
return( -1 );
}
#ifdef DEBUG
printf( "vips_reducev_build: reducing %d x %d image to %d x %d\n",
in->Xsize, in->Ysize,
resample->out->Xsize, resample->out->Ysize );
#endif /*DEBUG*/
if( vips_image_generate( resample->out,
vips_start_one, vips_reducev_gen, vips_stop_one,
in, reducev ) )
return( -1 );
return( 0 );
}
static void
vips_reducev_class_init( VipsReducevClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class );
VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
VIPS_DEBUG_MSG( "vips_reducev_class_init\n" );
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
vobject_class->nickname = "reducev";
vobject_class->description = _( "shrink an image vertically" );
vobject_class->build = vips_reducev_build;
operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED;
VIPS_ARG_DOUBLE( class, "yshrink", 3,
_( "Xshrink" ),
_( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsReducev, yshrink ),
1, 1000000, 1 );
VIPS_ARG_INTERPOLATE( class, "interpolate", 4,
_( "Interpolate" ),
_( "Interpolate pixels with this" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsReducev, interpolate ) );
}
static void
vips_reducev_init( VipsReducev *reducev )
{
}
/**
* vips_reducev:
* @in: input image
* @out: output image
* @yshrink: horizontal reduce
* @...: %NULL-terminated list of optional named arguments
*
* Optional arguments:
*
* @interpolate: interpolate pixels with this, default cubicv
*
* Reduce @in vertically by a float factor. The pixels in @out are
* interpolated with a 1D cubic mask. This operation will not work well for
* a reduction of more than a factor of two.
*
* This is a very low-level operation: see vips_resize() for a more
* convenient way to resize images.
*
* This operation does not change xres or yres. The image resolution needs to
* be updated by the application.
*
* See also: vips_shrink(), vips_resize(), vips_affine().
*
* Returns: 0 on success, -1 on error
*/
int
vips_reducev( VipsImage *in, VipsImage **out, double yshrink, ... )
{
va_list ap;
int result;
va_start( ap, yshrink );
result = vips_call_split( "reducev", ap, in, out, yshrink );
va_end( ap );
return( result );
}

View File

@ -0,0 +1,437 @@
/* horizontal reduce by a float factor
*
* 30/10/15
* - from reducev.c
*/
/*
This file is part of VIPS.
VIPS is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
/*
#define DEBUG
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <vips/vips.h>
#include <vips/debug.h>
#include <vips/internal.h>
#include "presample.h"
#include "templates.h"
typedef struct _VipsReducev {
VipsResample parent_instance;
double yshrink; /* Shrink factor */
} VipsReducev;
typedef VipsResampleClass VipsReducevClass;
static int vips_reducev_matrixi[VIPS_TRANSFORM_SCALE + 1][4];
static double vips_reducev_matrixf[VIPS_TRANSFORM_SCALE + 1][4];
/* We need C linkage for this.
*/
extern "C" {
G_DEFINE_TYPE( VipsReducev, vips_reducev, VIPS_TYPE_RESAMPLE );
}
template <typename T, int max_value>
static void inline
reducev_unsigned_int_tab( VipsPel *pout, const VipsPel *pin,
const int ne, const int lskip,
const int *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;
for( int z = 0; z < ne; z++ ) {
const T uno = in[0];
const T dos = in[l1];
const T tre = in[l2];
const T qua = in[l3];
int cubicv = cubic_unsigned_int<T>( uno, dos, tre, qua, cy );
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_int_tab( VipsPel *pout, const VipsPel *pin,
const int ne, const int lskip,
const int *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;
for( int z = 0; z < ne; z++ ) {
const T uno = in[0];
const T dos = in[l1];
const T tre = in[l2];
const T qua = in[l3];
int cubicv = cubic_signed_int<T>( uno, dos, tre, qua, cy );
cubicv = VIPS_CLIP( min_value, cubicv, max_value );
out[z] = cubicv;
in += 1;
}
}
/* Floating-point version, for int/float types.
*/
template <typename T>
static void inline
reducev_float_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;
for( int z = 0; z < ne; z++ ) {
const T uno = in[0];
const T dos = in[l1];
const T tre = in[l2];
const T qua = in[l3];
out[z] = cubic_float<T>( uno, dos, tre, qua, cy );
in += 1;
}
}
/* Ultra-high-quality version for double images.
*/
template <typename T>
static void inline
reducev_notab( VipsPel *pout, const VipsPel *pin,
const int ne, const int lskip,
double y )
{
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;
double cy[4];
calculate_coefficients_catmull( y, cy );
for( int z = 0; z < ne; z++ ) {
const T uno = in[0];
const T dos = in[l1];
const T tre = in[l2];
const T qua = in[l3];
out[z] = cubic_float<T>( uno, dos, tre, qua, cy );
in += 1;
}
}
static int
vips_reducev_gen( VipsRegion *out_region, void *seq,
void *a, void *b, gboolean *stop )
{
VipsImage *in = (VipsImage *) a;
VipsReducev *reducev = (VipsReducev *) b;
VipsRegion *ir = (VipsRegion *) seq;
VipsRect *r = &out_region->valid;
/* Double bands for complex.
*/
const int bands = in->Bands *
(vips_band_format_iscomplex( in->BandFmt ) ? 2 : 1);
int ne = r->width * bands;
VipsRect s;
int y;
#ifdef DEBUG
printf( "vips_reducev_gen: generating %d x %d at %d x %d\n",
r->width, r->height, r->left, r->top );
#endif /*DEBUG*/
s.left = r->left;
s.top = r->top * reducev->yshrink;
s.width = r->width;
s.height = r->height * reducev->yshrink + 3;
if( vips_region_prepare( ir, &s ) )
return( -1 );
VIPS_GATE_START( "vips_reducev_gen: work" );
for( y = 0; y < r->height; y ++ ) {
VipsPel *q = VIPS_REGION_ADDR( out_region, r->left, r->top + y );
const double Y = (r->top + y) * reducev->yshrink;
VipsPel *p = VIPS_REGION_ADDR( ir, r->left, (int) Y );
const int sy = Y * VIPS_TRANSFORM_SCALE * 2;
const int siy = sy & (VIPS_TRANSFORM_SCALE * 2 - 1);
const int ty = (siy + 1) >> 1;
const int *cyi = vips_reducev_matrixi[ty];
const double *cyf = vips_reducev_matrixf[ty];
const int lskip = VIPS_REGION_LSKIP( ir );
switch( in->BandFmt ) {
case VIPS_FORMAT_UCHAR:
reducev_unsigned_int_tab
<unsigned char, UCHAR_MAX>(
q, p, ne, lskip, cyi );
break;
case VIPS_FORMAT_CHAR:
reducev_signed_int_tab
<signed char, SCHAR_MIN, SCHAR_MAX>(
q, p, ne, lskip, cyi );
break;
case VIPS_FORMAT_USHORT:
reducev_unsigned_int_tab
<unsigned short, USHRT_MAX>(
q, p, ne, lskip, cyi );
break;
case VIPS_FORMAT_SHORT:
reducev_signed_int_tab
<signed short, SHRT_MIN, SHRT_MAX>(
q, p, ne, lskip, cyi );
break;
case VIPS_FORMAT_UINT:
reducev_float_tab<unsigned int>( q, p, ne, lskip, cyf );
break;
case VIPS_FORMAT_INT:
reducev_float_tab<signed int>( q, p, ne, lskip, cyf );
break;
case VIPS_FORMAT_FLOAT:
case VIPS_FORMAT_COMPLEX:
reducev_float_tab<float>( q, p, ne, lskip, cyf );
break;
case VIPS_FORMAT_DPCOMPLEX:
case VIPS_FORMAT_DOUBLE:
reducev_notab<double>( q, p, ne, lskip, Y - (int) Y );
break;
default:
g_assert_not_reached();
break;
}
}
VIPS_GATE_STOP( "vips_reducev_gen: work" );
return( 0 );
}
static int
vips_reducev_build( VipsObject *object )
{
VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( object );
VipsResample *resample = VIPS_RESAMPLE( object );
VipsReducev *reducev = (VipsReducev *) object;
VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 );
VipsImage *in;
if( VIPS_OBJECT_CLASS( vips_reducev_parent_class )->build( object ) )
return( -1 );
in = resample->in;
if( reducev->yshrink < 1 ) {
vips_error( object_class->nickname,
"%s", _( "reduce factors should be >= 1" ) );
return( -1 );
}
if( reducev->yshrink > 2 )
vips_warn( object_class->nickname,
"%s", _( "reduce factor greater than 2" ) );
if( reducev->yshrink == 1 )
return( vips_image_write( in, resample->out ) );
/* Unpack for processing.
*/
if( vips_image_decode( in, &t[0] ) )
return( -1 );
in = t[0];
/* Add new pixels around the input so we can interpolate at the edges.
*/
if( vips_embed( in, &t[1],
0, 1,
in->Xsize, in->Ysize + 3,
"extend", VIPS_EXTEND_COPY,
NULL ) )
return( -1 );
in = t[1];
if( vips_image_pipelinev( resample->out,
VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ) )
return( -1 );
/* Size output. Note: we round the output width down!
*
* Don't change xres/yres, leave that to the application layer. For
* example, vipsthumbnail knows the true reduce factor (including the
* fractional part), we just see the integer part here.
*/
resample->out->Ysize = (in->Ysize - 3) / reducev->yshrink;
if( resample->out->Ysize <= 0 ) {
vips_error( object_class->nickname,
"%s", _( "image has shrunk to nothing" ) );
return( -1 );
}
#ifdef DEBUG
printf( "vips_reducev_build: reducing %d x %d image to %d x %d\n",
in->Xsize, in->Ysize,
resample->out->Xsize, resample->out->Ysize );
#endif /*DEBUG*/
if( vips_image_generate( resample->out,
vips_start_one, vips_reducev_gen, vips_stop_one,
in, reducev ) )
return( -1 );
return( 0 );
}
static void
vips_reducev_class_init( VipsReducevClass *reducev_class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( reducev_class );
VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( reducev_class );
VipsOperationClass *operation_class =
VIPS_OPERATION_CLASS( reducev_class );
VIPS_DEBUG_MSG( "vips_reducev_class_init\n" );
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
vobject_class->nickname = "reducev";
vobject_class->description = _( "shrink an image vertically" );
vobject_class->build = vips_reducev_build;
operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED;
VIPS_ARG_DOUBLE( reducev_class, "yshrink", 3,
_( "Xshrink" ),
_( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsReducev, yshrink ),
1, 1000000, 1 );
/* Build the tables of pre-computed coefficients.
*/
for( int y = 0; y < VIPS_TRANSFORM_SCALE + 1; y++ ) {
calculate_coefficients_catmull(
(float) y / VIPS_TRANSFORM_SCALE,
vips_reducev_matrixf[y] );
for( int i = 0; i < 4; i++ )
vips_reducev_matrixi[y][i] =
vips_reducev_matrixf[y][i] *
VIPS_INTERPOLATE_SCALE;
}
}
static void
vips_reducev_init( VipsReducev *reducev )
{
}
/**
* vips_reducev:
* @in: input image
* @out: output image
* @yshrink: horizontal reduce
* @...: %NULL-terminated list of optional named arguments
*
* Reduce @in vertically by a float factor. The pixels in @out are
* interpolated with a 1D cubic mask. This operation will not work well for
* a reduction of more than a factor of two.
*
* This is a very low-level operation: see vips_resize() for a more
* convenient way to resize images.
*
* This operation does not change xres or yres. The image resolution needs to
* be updated by the application.
*
* See also: vips_shrink(), vips_resize(), vips_affine().
*
* Returns: 0 on success, -1 on error
*/
int
vips_reducev( VipsImage *in, VipsImage **out, double yshrink, ... )
{
va_list ap;
int result;
va_start( ap, yshrink );
result = vips_call_split( "reducev", ap, in, out, yshrink );
va_end( ap );
return( result );
}