redo im_affine as a class

This commit is contained in:
John Cupitt 2012-12-14 12:33:47 +00:00
parent 43d69e74e7
commit 4c82d45463
13 changed files with 427 additions and 287 deletions

View File

@ -32,6 +32,7 @@
- added scRGB colourspace, linear light float space with sRGB primaries
- all interpolators use corner convention ... we had round-to-nearest in
several of them before, causing a range of annoying problems
- redone im_affine*() as a class
14/11/12 started 7.30.6
- capture tiff warnings earlier

View File

@ -47,6 +47,7 @@
#include <vips/internal.h>
#include <vips/debug.h>
#include <vips/vector.h>
#include <vips/transform.h>
VipsImage *
im_open( const char *filename, const char *mode )
@ -2885,3 +2886,94 @@ im_cross_phase( IMAGE *in1, IMAGE *in2, IMAGE *out )
return( 0 );
}
static int
im__affinei( VipsImage *in, VipsImage *out,
VipsInterpolate *interpolate, VipsTransformation *trn )
{
VipsImage *x;
VipsArea *oarea;
oarea = (VipsArea *) vips_array_int_newv( 4,
trn->oarea.left, trn->oarea.top,
trn->oarea.width, trn->oarea.height );
if( vips_affine( in, &x,
trn->a, trn->b, trn->c, trn->d,
"interpolate", interpolate,
"oarea", oarea,
"odx", trn->dx,
"ody", trn->dy,
NULL ) ) {
vips_area_unref( oarea );
return( -1 );
}
vips_area_unref( oarea );
if( im_copy( x, out ) ) {
g_object_unref( x );
return( -1 );
}
g_object_unref( x );
return( 0 );
}
int
im_affinei( VipsImage *in, VipsImage *out, VipsInterpolate *interpolate,
double a, double b, double c, double d, double dx, double dy,
int ox, int oy, int ow, int oh )
{
VipsTransformation trn;
trn.iarea.left = 0;
trn.iarea.top = 0;
trn.iarea.width = in->Xsize;
trn.iarea.height = in->Ysize;
trn.oarea.left = ox;
trn.oarea.top = oy;
trn.oarea.width = ow;
trn.oarea.height = oh;
trn.a = a;
trn.b = b;
trn.c = c;
trn.d = d;
trn.dx = dx;
trn.dy = dy;
return( im__affinei( in, out, interpolate, &trn ) );
}
int
im_affinei_all( VipsImage *in, VipsImage *out, VipsInterpolate *interpolate,
double a, double b, double c, double d, double dx, double dy )
{
VipsTransformation trn;
trn.iarea.left = 0;
trn.iarea.top = 0;
trn.iarea.width = in->Xsize;
trn.iarea.height = in->Ysize;
trn.a = a;
trn.b = b;
trn.c = c;
trn.d = d;
trn.dx = dx;
trn.dy = dy;
vips__transform_set_area( &trn );
return( im__affinei( in, out, interpolate, &trn ) );
}
/* Still needed by some parts of mosaic.
*/
int
vips__affine( VipsImage *in, VipsImage *out, VipsTransformation *trn )
{
return( im__affinei( in, out,
vips_interpolate_bilinear_static(), trn ) );
}

View File

@ -30,25 +30,13 @@
*/
#ifndef IM_RESAMPLE_H
#define IM_RESAMPLE_H
#ifndef VIPS_RESAMPLE_H
#define VIPS_RESAMPLE_H
#ifdef __cplusplus
extern "C" {
#endif /*__cplusplus*/
int im_affinei( VipsImage *in, VipsImage *out,
VipsInterpolate *interpolate,
double a, double b, double c, double d, double dx, double dy,
int ox, int oy, int ow, int oh );
int im_affinei_all( VipsImage *in, VipsImage *out, VipsInterpolate *interpolate,
double a, double b, double c, double d, double dx, double dy ) ;
int vips_quadratic( VipsImage *in, VipsImage **out, VipsImage *coeff, ... );
int im_rightshift_size( VipsImage *in, VipsImage *out,
int xshift, int yshift, int band_fmt );
int im_match_linear( VipsImage *ref, VipsImage *sec, VipsImage *out,
int xr1, int yr1, int xs1, int ys1,
int xr2, int yr2, int xs2, int ys2 );
@ -59,13 +47,17 @@ int im_match_linear_search( VipsImage *ref, VipsImage *sec, VipsImage *out,
int vips_quadratic( VipsImage *in, VipsImage **out, VipsImage *coeff, ... )
__attribute__((sentinel));
int vips_shrink( VipsImage *in, VipsImage **out,
double xshrink, double yshrink, ... )
__attribute__((sentinel));
int vips_affine( VipsImage *in, VipsImage **out,
double a, double b, double c, double d, ... )
__attribute__((sentinel));
#ifdef __cplusplus
}
#endif /*__cplusplus*/
#endif /*IM_RESAMPLE_H*/
#endif /*VIPS_RESAMPLE_H*/

View File

@ -27,6 +27,13 @@
*/
#ifndef VIPS_TRANSFORM_H
#define VIPS_TRANSFORM_H
#ifdef __cplusplus
extern "C" {
#endif /*__cplusplus*/
/* Params for an affine transformation.
*/
typedef struct {
@ -46,24 +53,31 @@ typedef struct {
double dx, dy;
double ia, ib, ic, id; /* Inverse of matrix abcd */
} Transformation;
} VipsTransformation;
void im__transform_init( Transformation *trn );
int im__transform_calc_inverse( Transformation *trn );
int im__transform_isidentity( const Transformation *trn );
int im__transform_add( const Transformation *in1, const Transformation *in2,
Transformation *out );
void im__transform_print( const Transformation *trn );
void vips__transform_init( VipsTransformation *trn );
int vips__transform_calc_inverse( VipsTransformation *trn );
int vips__transform_isidentity( const VipsTransformation *trn );
int vips__transform_add( const VipsTransformation *in1,
const VipsTransformation *in2,
VipsTransformation *out );
void vips__transform_print( const VipsTransformation *trn );
void im__transform_forward_point( const Transformation *trn,
void vips__transform_forward_point( const VipsTransformation *trn,
const double x, const double y, double *ox, double *oy );
void im__transform_invert_point( const Transformation *trn,
void vips__transform_invert_point( const VipsTransformation *trn,
const double x, const double y, double *ox, double *oy );
void im__transform_forward_rect( const Transformation *trn,
void vips__transform_forward_rect( const VipsTransformation *trn,
const VipsRect *in, VipsRect *out );
void im__transform_invert_rect( const Transformation *trn,
void vips__transform_invert_rect( const VipsTransformation *trn,
const VipsRect *in, VipsRect *out );
void im__transform_set_area( Transformation * );
void vips__transform_set_area( VipsTransformation * );
int im__affine( VipsImage *in, VipsImage *out, Transformation *trn );
int vips__affine( VipsImage *in, VipsImage *out, VipsTransformation *trn );
#ifdef __cplusplus
}
#endif /*__cplusplus*/
#endif /*VIPS_TRANSFORM_H*/

View File

@ -730,6 +730,14 @@ int im_recomb( VipsImage *in, VipsImage *out, DOUBLEMASK *recomb );
int im_argb2rgba( VipsImage *in, VipsImage *out );
int im_shrink( VipsImage *in, VipsImage *out, double xshrink, double yshrink );
int im_affinei( VipsImage *in, VipsImage *out,
VipsInterpolate *interpolate,
double a, double b, double c, double d, double dx, double dy,
int ox, int oy, int ow, int oh );
int im_affinei_all( VipsImage *in, VipsImage *out, VipsInterpolate *interpolate,
double a, double b, double c, double d, double dx, double dy ) ;
int im_rightshift_size( VipsImage *in, VipsImage *out,
int xshift, int yshift, int band_fmt );
int im_Lab2XYZ_temp( IMAGE *in, IMAGE *out, double X0, double Y0, double Z0 );
int im_Lab2XYZ( IMAGE *in, IMAGE *out );

View File

@ -214,7 +214,7 @@ build_node( SymbolTable *st, char *name )
node->dirty = 0;
node->mwidth = -2;
node->st = st;
im__transform_init( &node->cumtrn );
vips__transform_init( &node->cumtrn );
node->trnim = NULL;
node->arg1 = NULL;
node->arg2 = NULL;
@ -407,7 +407,7 @@ calc_geometry( JoinNode *node )
node->cumtrn.iarea.top = 0;
node->cumtrn.iarea.width = um.width;
node->cumtrn.iarea.height = um.height;
im__transform_set_area( &node->cumtrn );
vips__transform_set_area( &node->cumtrn );
break;
case JOIN_CP:
@ -424,7 +424,7 @@ calc_geometry( JoinNode *node )
node->cumtrn.iarea.top = 0;
node->cumtrn.iarea.width = node->im->Xsize;
node->cumtrn.iarea.height = node->im->Ysize;
im__transform_set_area( &node->cumtrn );
vips__transform_set_area( &node->cumtrn );
}
break;
@ -440,7 +440,7 @@ calc_geometry( JoinNode *node )
* have circularity.
*/
static int
propogate_transform( JoinNode *node, Transformation *trn )
propogate_transform( JoinNode *node, VipsTransformation *trn )
{
if( !node )
return( 0 );
@ -460,7 +460,7 @@ propogate_transform( JoinNode *node, Transformation *trn )
/* Transform us, and recalculate our position and size.
*/
im__transform_add( &node->cumtrn, trn, &node->cumtrn );
vips__transform_add( &node->cumtrn, trn, &node->cumtrn );
calc_geometry( node );
return( 0 );
@ -475,7 +475,7 @@ make_join( SymbolTable *st, JoinType type,
JoinNode *arg1, JoinNode *arg2, JoinNode *out,
double a, double b, double dx, double dy, int mwidth )
{
Transformation trn;
VipsTransformation trn;
/* Check output is ok.
*/
@ -1528,13 +1528,13 @@ generate_trn_leaves( JoinNode *node, SymbolTable *st )
/* Special case: if this is an untransformed leaf (there will
* always be at least one), then skip the affine.
*/
if( im__transform_isidentity( &node->cumtrn ) )
if( vips__transform_isidentity( &node->cumtrn ) )
node->trnim = node->im;
else
if( !(node->trnim =
im_open_local( node->st->im,
"trnleaf:1", "p" )) ||
im__affine( node->im, node->trnim,
vips__affine( node->im, node->trnim,
&node->cumtrn ) )
return( node );
}

View File

@ -83,13 +83,13 @@ struct _JoinNode {
* cumtrn.area is position and size of us, thistrn.area is pos and
* size of arg2.
*/
Transformation cumtrn;
VipsTransformation cumtrn;
/* X-tras for LR/TB. thistrn is what we do to arg2.
*/
JoinNode *arg1; /* Left or up thing to join */
JoinNode *arg2; /* Right or down thing to join */
Transformation thistrn; /* Transformation for arg2 */
VipsTransformation thistrn; /* Transformation for arg2 */
/* Special for leaves: all the join_nodes we overlap with, the
* IMAGE for that file, and the index.

View File

@ -69,7 +69,7 @@
/* Like im_similarity(), but return the transform we generated.
*/
static int
apply_similarity( Transformation *trn, IMAGE *in, IMAGE *out,
apply_similarity( VipsTransformation *trn, IMAGE *in, IMAGE *out,
double a, double b, double dx, double dy )
{
trn->iarea.left = 0;
@ -82,11 +82,11 @@ apply_similarity( Transformation *trn, IMAGE *in, IMAGE *out,
trn->d = a;
trn->dx = dx;
trn->dy = dy;
im__transform_set_area( trn );
if( im__transform_calc_inverse( trn ) )
vips__transform_set_area( trn );
if( vips__transform_calc_inverse( trn ) )
return( -1 );
if( im__affine( in, out, trn ) )
if( vips__affine( in, out, trn ) )
return( -1 );
return( 0 );
@ -103,7 +103,7 @@ int
im__lrmerge1( IMAGE *ref, IMAGE *sec, IMAGE *out,
double a, double b, double dx, double dy, int mwidth )
{
Transformation trn;
VipsTransformation trn;
IMAGE *t1 = im_open_local( out, "im_lrmosaic1:1", "p" );
VipsBuf buf;
char text[1024];
@ -145,7 +145,7 @@ int
im__tbmerge1( IMAGE *ref, IMAGE *sec, IMAGE *out,
double a, double b, double dx, double dy, int mwidth )
{
Transformation trn;
VipsTransformation trn;
IMAGE *t1 = im_open_local( out, "im_lrmosaic1:2", "p" );
VipsBuf buf;
char text[1024];
@ -316,7 +316,7 @@ rotjoin_search( IMAGE *ref, IMAGE *sec, IMAGE *out, joinfn jfn,
int balancetype,
int mwidth )
{
Transformation trn;
VipsTransformation trn;
double cor1, cor2;
double a, b, dx, dy;
double xs3, ys3;
@ -357,11 +357,11 @@ rotjoin_search( IMAGE *ref, IMAGE *sec, IMAGE *out, joinfn jfn,
/* Map points on sec to rotated image.
*/
im__transform_forward_point( &trn, xs1, ys1, &xs3, &ys3 );
im__transform_forward_point( &trn, xs2, ys2, &xs4, &ys4 );
vips__transform_forward_point( &trn, xs1, ys1, &xs3, &ys3 );
vips__transform_forward_point( &trn, xs2, ys2, &xs4, &ys4 );
/* Refine tie-points on rotated image. Remember the clip
* im__transform_set_area() has set, and move the sec tie-points
* vips__transform_set_area() has set, and move the sec tie-points
* accordingly.
*/
if( im_correl( t[0], t[2], xr1, yr1,
@ -391,8 +391,8 @@ rotjoin_search( IMAGE *ref, IMAGE *sec, IMAGE *out, joinfn jfn,
/* ... and now back to input space again.
*/
im__transform_invert_point( &trn, xs5, ys5, &xs7, &ys7 );
im__transform_invert_point( &trn, xs6, ys6, &xs8, &ys8 );
vips__transform_invert_point( &trn, xs5, ys5, &xs7, &ys7 );
vips__transform_invert_point( &trn, xs6, ys6, &xs8, &ys8 );
/* Recalc the transform using the refined points.
*/
@ -548,7 +548,7 @@ old_lrmosaic1( IMAGE *ref, IMAGE *sec, IMAGE *out,
int balancetype,
int mwidth )
{
Transformation trn1, trn2;
VipsTransformation trn1, trn2;
int dx0, dy0;
double a, b, dx, dy;
double a1, b1, dx1, dy1;

View File

@ -5,11 +5,11 @@
if ENABLE_CXX
libresample_la_SOURCES = \
affine.c \
quadratic.c \
resample.c \
resample.h \
shrink.c \
im_affine.c \
interpolate.c \
transform.c \
resample_dispatch.c \

View File

@ -76,6 +76,8 @@
* interpolate.c
* 2/2/11
* - gtk-doc
* 14/12/12
* - redone as a class
*/
/*
@ -121,9 +123,31 @@
#include <limits.h>
#include <vips/vips.h>
#include <vips/debug.h>
#include <vips/internal.h>
#include <vips/transform.h>
#include "resample.h"
typedef struct _VipsAffine {
VipsResample parent_instance;
VipsArea *matrix;
VipsInterpolate *interpolate;
VipsArea *oarea;
double odx;
double ody;
double idx;
double idy;
VipsTransformation trn;
} VipsAffine;
typedef VipsResampleClass VipsAffineClass;
G_DEFINE_TYPE( VipsAffine, vips_affine, VIPS_TYPE_RESAMPLE );
/*
* FAST_PSEUDO_FLOOR is a floor and floorf replacement which has been
* found to be faster on several linux boxes than the library
@ -147,23 +171,6 @@
*/
#define FAST_PSEUDO_FLOOR(x) ( (int)(x) - ( (x) < 0. ) )
/* Per-call state.
*/
typedef struct _Affine {
VipsImage *in;
VipsImage *out;
VipsInterpolate *interpolate;
Transformation trn;
} Affine;
static int
affine_free( Affine *affine )
{
VIPS_FREEF( g_object_unref, affine->interpolate );
return( 0 );
}
/* We have five (!!) coordinate systems. Working forward through them, these
* are:
*
@ -171,7 +178,7 @@ affine_free( Affine *affine )
*
* 2. This is embedded in a larger image to provide borders for the
* interpolator. iarea->left/top give the offset. These are the coordinates we
* pass to VIPS_REGION_ADDR()/im_prepare() for the input image.
* pass to VIPS_REGION_ADDR()/vips_region_prepare() for the input image.
*
* The borders are sized by the interpolator's window_size property and offset
* by the interpolator's window_offset property. For example,
@ -189,17 +196,17 @@ affine_free( Affine *affine )
* 4. Output transform space. This is the where the transform maps to. Pixels
* can be negative, since a rotated image can go up and left of the origin.
*
* 5. Output image space. This is the wh of the xywh passed to im_affine()
* 5. Output image space. This is the wh of the xywh passed to vips_affine()
* below. These are the coordinates we pass to VIPS_REGION_ADDR() for the
* output image, and that affinei_gen() is asked for.
*/
static int
affinei_gen( VipsRegion *or, void *seq, void *a, void *b )
vips_affine_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
{
VipsRegion *ir = (VipsRegion *) seq;
const VipsAffine *affine = (VipsAffine *) b;
const VipsImage *in = (VipsImage *) a;
const Affine *affine = (Affine *) b;
const int window_size =
vips_interpolate_get_window_size( affine->interpolate );
const int window_offset =
@ -209,19 +216,19 @@ affinei_gen( VipsRegion *or, void *seq, void *a, void *b )
/* Area we generate in the output image.
*/
const Rect *r = &or->valid;
const VipsRect *r = &or->valid;
const int le = r->left;
const int ri = VIPS_RECT_RIGHT( r );
const int to = r->top;
const int bo = VIPS_RECT_BOTTOM( r );
const Rect *iarea = &affine->trn.iarea;
const Rect *oarea = &affine->trn.oarea;
const VipsRect *iarea = &affine->trn.iarea;
const VipsRect *oarea = &affine->trn.oarea;
int ps = VIPS_IMAGE_SIZEOF_PEL( in );
int x, y, z;
Rect image, want, need, clipped;
VipsRect image, want, need, clipped;
#ifdef DEBUG
printf( "affine: generating left=%d, top=%d, width=%d, height=%d\n",
@ -239,7 +246,7 @@ affinei_gen( VipsRegion *or, void *seq, void *a, void *b )
/* Find the area of the input image we need.
*/
im__transform_invert_rect( &affine->trn, &want, &need );
vips__transform_invert_rect( &affine->trn, &want, &need );
/* That does round-to-nearest, because it has to stop rounding errors
* growing images unexpectedly. We need round-down, so we must
@ -248,7 +255,7 @@ affinei_gen( VipsRegion *or, void *seq, void *a, void *b )
*
* Add an extra line along the right and bottom as well, for rounding.
*/
im_rect_marginadjust( &need, 1 );
vips_rect_marginadjust( &need, 1 );
/* Now go to space (2) above.
*/
@ -268,19 +275,19 @@ affinei_gen( VipsRegion *or, void *seq, void *a, void *b )
image.top = 0;
image.width = in->Xsize;
image.height = in->Ysize;
im_rect_intersectrect( &need, &image, &clipped );
vips_rect_intersectrect( &need, &image, &clipped );
/* Outside input image? All black.
*/
if( im_rect_isempty( &clipped ) ) {
im_region_black( or );
if( vips_rect_isempty( &clipped ) ) {
vips_region_black( or );
return( 0 );
}
/* We do need some pixels from the input image to make our output -
* ask for them.
*/
if( im_prepare( ir, &clipped ) )
if( vips_region_prepare( ir, &clipped ) )
return( -1 );
#ifdef DEBUG
@ -355,50 +362,74 @@ affinei_gen( VipsRegion *or, void *seq, void *a, void *b )
return( 0 );
}
static int
affinei( VipsImage *in, VipsImage *out,
VipsInterpolate *interpolate, Transformation *trn )
static int
vips_affine_build( VipsObject *object )
{
Affine *affine;
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
VipsResample *resample = VIPS_RESAMPLE( object );
VipsAffine *affine = (VipsAffine *) object;
VipsImage **t = (VipsImage **)
vips_object_local_array( object, 4 );
VipsImage *in;
gboolean repack;
int window_size;
int window_offset;
double edge;
/* Make output image.
if( VIPS_OBJECT_CLASS( vips_affine_parent_class )->build( object ) )
return( -1 );
if( vips_check_coding_noneorlabq( class->nickname, in ) )
return( -1 );
if( vips_check_vector_length( class->nickname,
affine->matrix->n, 4 ) )
return( -1 );
if( vips_object_argument_isset( object, "oarea" ) &&
vips_check_vector_length( class->nickname,
affine->oarea->n, 4 ) )
return( -1 );
in = resample->in;
/* Set up transform.
*/
if( im_piocheck( in, out ) ||
im_cp_desc( out, in ) )
return( -1 );
/* Need a copy of the params for the lifetime of out.
*/
if( !(affine = VIPS_NEW( out, Affine )) )
return( -1 );
affine->interpolate = NULL;
if( im_add_close_callback( out,
(im_callback_fn) affine_free, affine, NULL ) )
return( -1 );
affine->in = in;
affine->out = out;
affine->interpolate = interpolate;
g_object_ref( interpolate );
affine->trn = *trn;
window_size = vips_interpolate_get_window_size( affine->interpolate );
window_offset =
vips_interpolate_get_window_offset( affine->interpolate );
if( im__transform_calc_inverse( &affine->trn ) )
return( -1 );
affine->trn.iarea.left = window_offset;
affine->trn.iarea.top = window_offset;
affine->trn.iarea.width = in->Xsize;
affine->trn.iarea.height = in->Ysize;
affine->trn.a = ((double *) affine->matrix->data)[0];
affine->trn.b = ((double *) affine->matrix->data)[1];
affine->trn.c = ((double *) affine->matrix->data)[2];
affine->trn.d = ((double *) affine->matrix->data)[3];
affine->trn.dx = 0;
affine->trn.dy = 0;
out->Xsize = affine->trn.oarea.width;
out->Ysize = affine->trn.oarea.height;
/* Normally SMALLTILE ... except if this is a size up/down affine.
*/
if( affine->trn.b == 0.0 && affine->trn.c == 0.0 ) {
if( im_demand_hint( out, VIPS_DEMAND_STYLE_FATSTRIP, in, NULL ) )
return( -1 );
}
else {
if( im_demand_hint( out, VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ) )
return( -1 );
vips__transform_set_area( &affine->trn );
if( vips_object_argument_isset( object, "oarea" ) ) {
affine->trn.oarea.left = ((int *) affine->oarea->data)[0];
affine->trn.oarea.top = ((int *) affine->oarea->data)[1];
affine->trn.oarea.width = ((int *) affine->oarea->data)[2];
affine->trn.oarea.height = ((int *) affine->oarea->data)[3];
}
if( vips_object_argument_isset( object, "odx" ) )
affine->trn.dx = affine->odx;
if( vips_object_argument_isset( object, "ody" ) )
affine->trn.dx = affine->ody;
if( vips__transform_calc_inverse( &affine->trn ) )
return( -1 );
resample->out->Xsize = affine->trn.oarea.width;
resample->out->Ysize = affine->trn.oarea.height;
/* Check for coordinate overflow ... we want to be able to hold the
* output space inside INT_MAX / TRANSFORM_SCALE.
*/
@ -406,186 +437,189 @@ affinei( VipsImage *in, VipsImage *out,
if( affine->trn.oarea.left < -edge || affine->trn.oarea.top < -edge ||
VIPS_RECT_RIGHT( &affine->trn.oarea ) > edge ||
VIPS_RECT_BOTTOM( &affine->trn.oarea ) > edge ) {
im_error( "im_affinei",
vips_error( class->nickname,
"%s", _( "output coordinates out of range" ) );
return( -1 );
}
/* Generate!
/* Unpack labq for processing ... we repack after, see below.
*/
if( im_generate( out,
im_start_one, affinei_gen, im_stop_one, in, affine ) )
return( -1 );
return( 0 );
}
/* As above, but do VIPS_CODING_LABQ too. And embed the input.
*/
static int
im__affinei( VipsImage *in, VipsImage *out,
VipsInterpolate *interpolate, Transformation *trn )
{
VipsImage *t3 = im_open_local( out, "im_affine:3", "p" );
const int window_size =
vips_interpolate_get_window_size( interpolate );
const int window_offset =
vips_interpolate_get_window_offset( interpolate );
Transformation trn2;
repack = FALSE;
if( in->Coding == VIPS_CODING_LABQ ) {
if( vips_LabQ2LabS( in, &t[0], NULL ) )
return( -1 );
repack = TRUE;
in = t[0];
}
/* Add new pixels around the input so we can interpolate at the edges.
*/
if( !t3 ||
im_embed( in, t3, 1,
window_offset, window_offset,
in->Xsize + window_size, in->Ysize + window_size ) )
if( vips_embed( in, &t[1], 1,
window_offset, window_offset,
in->Xsize + window_size, in->Ysize + window_size,
"extend", VIPS_EXTEND_COPY,
NULL ) )
return( -1 );
in = t[1];
/* Set iarea so we know what part of the input we can take.
/* Normally SMALLTILE ... except if this is a size up/down affine.
*/
trn2 = *trn;
trn2.iarea.left += window_offset;
trn2.iarea.top += window_offset;
if( affine->trn.b == 0.0 &&
affine->trn.c == 0.0 )
vips_demand_hint( resample->out,
VIPS_DEMAND_STYLE_FATSTRIP, resample->in, NULL );
else
vips_demand_hint( resample->out,
VIPS_DEMAND_STYLE_SMALLTILE, resample->in, NULL );
#ifdef DEBUG_GEOMETRY
printf( "im__affinei: %s\n", in->filename );
im__transform_print( &trn2 );
#endif /*DEBUG_GEOMETRY*/
if( in->Coding == VIPS_CODING_LABQ ) {
VipsImage *t[2];
if( im_open_local_array( out, t, 2, "im_affine:2", "p" ) ||
im_LabQ2LabS( t3, t[0] ) ||
affinei( t[0], t[1], interpolate, &trn2 ) ||
im_LabS2LabQ( t[1], out ) )
return( -1 );
}
else if( in->Coding == VIPS_CODING_NONE ) {
if( affinei( t3, out, interpolate, &trn2 ) )
return( -1 );
}
else {
im_error( "im_affinei", "%s", _( "unknown coding type" ) );
/* Generate!
*/
if( vips_image_generate( resample->out,
vips_start_one, vips_affine_gen, vips_stop_one,
resample->in, affine ) )
return( -1 );
}
/* Finally: can now set Xoffset/Yoffset.
*/
out->Xoffset = trn->dx - trn->oarea.left;
out->Yoffset = trn->dy - trn->oarea.top;
resample->out->Xoffset = affine->trn.dx - affine->trn.oarea.left;
resample->out->Yoffset = affine->trn.dy - affine->trn.oarea.top;
if( repack ) {
if( vips_LabS2LabQ( resample->out, &t[2], NULL ) )
return( -1 );
resample->out = t[2];
}
return( 0 );
}
static void
vips_affine_class_init( VipsAffineClass *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_affine_class_init\n" );
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
vobject_class->nickname = "affine";
vobject_class->description = _( "affine transform of an image" );
vobject_class->build = vips_affine_build;
VIPS_ARG_BOXED( class, "matrix", 110,
_( "Matrix" ),
_( "Transformation matrix" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsAffine, matrix ),
VIPS_TYPE_ARRAY_DOUBLE );
VIPS_ARG_INTERPOLATE( class, "interpolate", 2,
_( "Interpolate" ),
_( "Interpolate pixels with this" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsAffine, interpolate ) );
VIPS_ARG_BOXED( class, "oarea", 111,
_( "Output rect" ),
_( "Area of output to generate" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsAffine, oarea ),
VIPS_TYPE_ARRAY_INT );
VIPS_ARG_DOUBLE( class, "odx", 112,
_( "Output offset" ),
_( "Horizontal output displacement" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsAffine, odx ),
0, 10000000, 0 );
VIPS_ARG_DOUBLE( class, "ody", 113,
_( "Output offset" ),
_( "Vertical output displacement" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsAffine, ody ),
0, 10000000, 0 );
VIPS_ARG_DOUBLE( class, "idx", 112,
_( "Input offset" ),
_( "Horizontal input displacement" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsAffine, idx ),
0, 10000000, 0 );
VIPS_ARG_DOUBLE( class, "idy", 113,
_( "Input offset" ),
_( "Vertical input displacement" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsAffine, idy ),
0, 10000000, 0 );
}
static void
vips_affine_init( VipsAffine *affine )
{
}
/**
* im_affinei:
* vips_affine:
* @in: input image
* @out: output image
* @interpolate: interpolation method
* @a: transformation matrix
* @b: transformation matrix
* @c: transformation matrix
* @d: transformation matrix
* @dx: output offset
* @dy: output offset
* @ox: output region
* @oy: output region
* @ow: output region
* @oh: output region
* @a: transformation matrix coefficient
* @b: transformation matrix coefficient
* @c: transformation matrix coefficient
* @d: transformation matrix coefficient
*
* Optional arguments:
*
* @interpolate: interpolate pixels with this
* @oarea: output rectangle
* @idx: input horizontal offset
* @idy: input vertical offset
* @odx: output horizontal offset
* @ody: output vertical offset
*
* This operator performs an affine transform on an image using @interpolate.
*
* The transform is:
*
* X = @a * x + @b * y + @dx
* Y = @c * x + @d * y + @dy
* X = @a * (x + @idx) + @b * (y + @idy) + @odx
* Y = @c * (x + @idx) + @d * (y + @idy) + @doy
*
* x and y are the coordinates in input image.
* X and Y are the coordinates in output image.
* (0,0) is the upper left corner.
*
* The section of the output space defined by @ox, @oy, @ow, @oh is written to
* @out. See im_affinei_all() for a function which outputs all the transformed
* pixels.
* The section of the output space defined by @oarea is written to
* @out. @oarea is a four-element int array of left, top, width, height.
* By default @oarea is just large enough to cover the whole of the
* transformed input image.
*
* See also: im_affinei_all(), #VipsInterpolate.
* @interpolate defaults to bilinear.
*
* @idx, @idy, @odx, @ody default to zero.
*
* See also: vips_shrink(), #VipsInterpolate.
*
* Returns: 0 on success, -1 on error
*/
int
im_affinei( VipsImage *in, VipsImage *out, VipsInterpolate *interpolate,
double a, double b, double c, double d, double dx, double dy,
int ox, int oy, int ow, int oh )
int
vips_affine( VipsImage *in, VipsImage **out,
double a, double b, double c, double d, ... )
{
Transformation trn;
va_list ap;
VipsArea *matrix;
int result;
trn.iarea.left = 0;
trn.iarea.top = 0;
trn.iarea.width = in->Xsize;
trn.iarea.height = in->Ysize;
matrix = (VipsArea *) vips_array_double_newv( 4, a, b, c, d );
trn.oarea.left = ox;
trn.oarea.top = oy;
trn.oarea.width = ow;
trn.oarea.height = oh;
va_start( ap, d );
result = vips_call_split( "shrink", ap, in, out, matrix );
va_end( ap );
trn.a = a;
trn.b = b;
trn.c = c;
trn.d = d;
trn.dx = dx;
trn.dy = dy;
vips_area_unref( matrix );
return( im__affinei( in, out, interpolate, &trn ) );
}
/**
* im_affinei_all:
* @in: input image
* @out: output image
* @interpolate: interpolation method
* @a: transformation matrix
* @b: transformation matrix
* @c: transformation matrix
* @d: transformation matrix
* @dx: output offset
* @dy: output offset
*
* As im_affinei(), but the entire image is output.
*
* See also: im_affinei(), #VipsInterpolate.
*
* Returns: 0 on success, -1 on error
*/
int
im_affinei_all( VipsImage *in, VipsImage *out, VipsInterpolate *interpolate,
double a, double b, double c, double d, double dx, double dy )
{
Transformation trn;
trn.iarea.left = 0;
trn.iarea.top = 0;
trn.iarea.width = in->Xsize;
trn.iarea.height = in->Ysize;
trn.a = a;
trn.b = b;
trn.c = c;
trn.d = d;
trn.dx = dx;
trn.dy = dy;
im__transform_set_area( &trn );
return( im__affinei( in, out, interpolate, &trn ) );
}
/* Still needed by some parts of mosaic.
*/
int
im__affine( VipsImage *in, VipsImage *out, Transformation *trn )
{
return( im__affinei( in, out,
vips_interpolate_bilinear_static(), trn ) );
return( result );
}

View File

@ -115,8 +115,10 @@ vips_resample_operation_init( void )
{
extern GType vips_shrink_get_type( void );
extern GType vips_quadratic_get_type( void );
extern GType vips_affine_get_type( void );
vips_shrink_get_type();
vips_quadratic_get_type();
vips_affine_get_type();
}

View File

@ -307,7 +307,7 @@ vips_shrink_build( VipsObject *object )
shrink->mh = ceil( shrink->yshrink );
shrink->np = shrink->mw * shrink->mh;
if( im_check_noncomplex( class->nickname, resample->in ) )
if( vips_check_noncomplex( class->nickname, resample->in ) )
return( -1 );
if( shrink->xshrink < 1.0 ||
@ -327,9 +327,6 @@ vips_shrink_build( VipsObject *object )
shrink->yshrink == 1.0 )
return( vips_image_write( resample->in, resample->out ) );
if( vips_image_copy_fields( resample->out, resample->in ) )
return( -1 );
/* THINSTRIP will work, FATSTRIP will break seq mode. If you combine
* shrink with conv you'll need to use a line cache to maintain
* sequentiality.
@ -410,10 +407,10 @@ vips_shrink_init( VipsShrink *shrink )
*
* You will get aliasing for non-integer shrinks. In this case, shrink with
* this function to the nearest integer size above the target shrink, then
* downsample to the exact size with im_affinei() and your choice of
* downsample to the exact size with vips_affine() and your choice of
* interpolator.
*
* See also: im_affinei().
* See also: vips_affine().
*
* Returns: 0 on success, -1 on error
*/

View File

@ -49,7 +49,7 @@
/* Calculate the inverse transformation.
*/
int
im__transform_calc_inverse( Transformation *trn )
vips__transform_calc_inverse( VipsTransformation *trn )
{
DOUBLEMASK *msk, *msk2;
@ -70,10 +70,10 @@ im__transform_calc_inverse( Transformation *trn )
return( 0 );
}
/* Init a Transform.
/* Init a VipsTransform.
*/
void
im__transform_init( Transformation *trn )
vips__transform_init( VipsTransformation *trn )
{
trn->oarea.left = 0;
trn->oarea.top = 0;
@ -90,13 +90,13 @@ im__transform_init( Transformation *trn )
trn->dx = 0.0;
trn->dy = 0.0;
(void) im__transform_calc_inverse( trn );
(void) vips__transform_calc_inverse( trn );
}
/* Test for transform is identity function.
*/
int
im__transform_isidentity( const Transformation *trn )
vips__transform_isidentity( const VipsTransformation *trn )
{
if( trn->a == 1.0 && trn->b == 0.0 && trn->c == 0.0 &&
trn->d == 1.0 && trn->dx == 0.0 && trn->dy == 0.0 )
@ -108,8 +108,8 @@ im__transform_isidentity( const Transformation *trn )
/* Combine two transformations. out can be one of the ins.
*/
int
im__transform_add( const Transformation *in1, const Transformation *in2,
Transformation *out )
vips__transform_add( const VipsTransformation *in1,
const VipsTransformation *in2, VipsTransformation *out )
{
out->a = in1->a * in2->a + in1->c * in2->b;
out->b = in1->b * in2->a + in1->d * in2->b;
@ -119,16 +119,16 @@ im__transform_add( const Transformation *in1, const Transformation *in2,
out->dx = in1->dx * in2->a + in1->dy * in2->b + in2->dx;
out->dy = in1->dx * in2->c + in1->dy * in2->d + in2->dy;
if( im__transform_calc_inverse( out ) )
if( vips__transform_calc_inverse( out ) )
return( -1 );
return( 0 );
}
void
im__transform_print( const Transformation *trn )
vips__transform_print( const VipsTransformation *trn )
{
printf( "im__transform_print:\n" );
printf( "vips__transform_print:\n" );
printf( " iarea: left=%d, top=%d, width=%d, height=%d\n",
trn->iarea.left,
trn->iarea.top,
@ -148,9 +148,9 @@ im__transform_print( const Transformation *trn )
/* Map a pixel coordinate through the transform.
*/
void
im__transform_forward_point( const Transformation *trn,
vips__transform_forward_point( const VipsTransformation *trn,
const double x, const double y, /* In input space */
double *ox, double *oy ) /* In output space */
double *ox, double *oy ) /* In output space */
{
*ox = trn->a * x + trn->b * y + trn->dx;
*oy = trn->c * x + trn->d * y + trn->dy;
@ -159,9 +159,9 @@ im__transform_forward_point( const Transformation *trn,
/* Map a pixel coordinate through the inverse transform.
*/
void
im__transform_invert_point( const Transformation *trn,
vips__transform_invert_point( const VipsTransformation *trn,
const double x, const double y, /* In output space */
double *ox, double *oy ) /* In input space */
double *ox, double *oy ) /* In input space */
{
double mx = x - trn->dx;
double my = y - trn->dy;
@ -170,13 +170,13 @@ im__transform_invert_point( const Transformation *trn,
*oy = trn->ic * mx + trn->id * my;
}
typedef void (*transform_fn)( const Transformation *,
typedef void (*transform_fn)( const VipsTransformation *,
const double, const double, double*, double* );
/* Transform a rect using a point transformer.
*/
static void
transform_rect( const Transformation *trn, transform_fn transform,
transform_rect( const VipsTransformation *trn, transform_fn transform,
const Rect *in, /* In input space */
Rect *out ) /* In output space */
{
@ -211,28 +211,28 @@ transform_rect( const Transformation *trn, transform_fn transform,
* pixels in the output image.
*/
void
im__transform_forward_rect( const Transformation *trn,
vips__transform_forward_rect( const VipsTransformation *trn,
const Rect *in, /* In input space */
Rect *out ) /* In output space */
{
transform_rect( trn, im__transform_forward_point, in, out );
transform_rect( trn, vips__transform_forward_point, in, out );
}
/* Given an area in the output image, calculate the bounding box for the
* corresponding pixels in the input image.
*/
void
im__transform_invert_rect( const Transformation *trn,
vips__transform_invert_rect( const VipsTransformation *trn,
const Rect *in, /* In output space */
Rect *out ) /* In input space */
{
transform_rect( trn, im__transform_invert_point, in, out );
transform_rect( trn, vips__transform_invert_point, in, out );
}
/* Set output area of trn so that it just holds all of our input pels.
*/
void
im__transform_set_area( Transformation *trn )
vips__transform_set_area( VipsTransformation *trn )
{
im__transform_forward_rect( trn, &trn->iarea, &trn->oarea );
vips__transform_forward_rect( trn, &trn->iarea, &trn->oarea );
}