/* Resample an image with a quadratic transform. * * Original code from Reimar Lenz, * Adapted by Lars Raffelt for many bands, * VIPSified by JC ... other numeric types, partial output * * 7/11/12 * - rewritten again for vips8 */ /* 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 #define DEBUG_GEOMETRY */ #ifdef HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ #include #include #include #include #include #include "presample.h" /* The transform we compute: x',y' = coordinates of srcim x,y = coordinates of dstim a .. l = coefficients x = x' + a : order 0 image shift only + b x' + c y' : order 1 + affine transf. + d x' y' : order 2 + bilinear transf. + e x' x' + f y' y' : order 3 + quadratic transf. y = y' + g + h y' + i x' + j y' x' + k y' y' + l x' x' input matrix: a g -- b h c i -- d j -- e k f l matrix height may be 1, 3, 4, 6 */ typedef struct _VipsQuadratic { VipsResample parent_instance; VipsImage *coeff; VipsInterpolate *interpolate; /* The coeff array argment, made into an in-memory double. */ VipsImage *mat; /* Transform order. */ int order; } VipsQuadratic; typedef VipsResampleClass VipsQuadraticClass; G_DEFINE_TYPE( VipsQuadratic, vips_quadratic, VIPS_TYPE_RESAMPLE ); static void vips_quadratic_dispose( GObject *gobject ) { VipsQuadratic *quadratic = (VipsQuadratic *) gobject; VIPS_UNREF( quadratic->mat ); G_OBJECT_CLASS( vips_quadratic_parent_class )->dispose( gobject ); } static int vips_quadratic_gen( VipsRegion *or, void *vseq, void *a, void *b, gboolean *stop ) { VipsRegion *ir = (VipsRegion *) vseq; VipsQuadratic *quadratic = (VipsQuadratic *) b; VipsResample *resample = VIPS_RESAMPLE( quadratic ); VipsInterpolateMethod interpolate_fn = vips_interpolate_get_method( quadratic->interpolate ); /* @in is the enlarged image (borders on, after vips_embed()). Use * @resample->in for the original, not-expanded image. */ const VipsImage *in = (VipsImage *) a; const int ps = VIPS_IMAGE_SIZEOF_PEL( in ); double *vec = VIPS_MATRIX( quadratic->mat, 0, 0 ); int clip_width = resample->in->Xsize; int clip_height = resample->in->Ysize; int xlow = or->valid.left; int ylow = or->valid.top; int xhigh = VIPS_RECT_RIGHT( &or->valid ); int yhigh = VIPS_RECT_BOTTOM( &or->valid ); VipsPel *q; int xo, yo; /* output coordinates, dstimage */ int z; double fxi, fyi; /* input coordinates */ double dx, dy; /* xo derivative of input coord. */ double ddx, ddy; /* 2nd xo derivative of input coord. */ VipsRect image; image.left = 0; image.top = 0; image.width = in->Xsize; image.height = in->Ysize; if( vips_region_image( ir, &image ) ) return( -1 ); for( yo = ylow; yo < yhigh; yo++ ) { fxi = 0.0; fyi = 0.0; dx = 0.0; dy = 0.0; ddx = 0.0; ddy = 0.0; switch( quadratic->order ) { case 3: fxi += vec[10] * yo * yo + vec[8] * xlow * xlow; fyi += vec[11] * yo * yo + vec[9] * xlow * xlow; dx += vec[8]; ddx += vec[8] * 2.0; dy += vec[9]; ddy += vec[9] * 2.0; case 2: fxi += vec[6] * xlow * yo; fyi += vec[7] * xlow * yo; dx += vec[6] * yo; dy += vec[7] * yo; case 1: fxi += vec[4] * yo + vec[2] * xlow; fyi += vec[5] * yo + vec[3] * xlow; dx += vec[2]; dy += vec[3]; case 0: fxi += vec[0]; fyi += vec[1]; break; default: g_assert_not_reached(); } printf( "dx = %g, dy = %g\n", dx, dy ); q = VIPS_REGION_ADDR( or, xlow, yo ); for( xo = xlow; xo < xhigh; xo++ ) { int xi, yi; xi = fxi; yi = fyi; /* Clipping! */ if( xi < 0 || yi < 0 || xi >= clip_width || yi >= clip_height ) { for( z = 0; z < ps; z++ ) q[z] = 0; } else interpolate_fn( quadratic->interpolate, q, ir, fxi, fyi ); q += ps; fxi += dx; fyi += dy; if( quadratic->order > 2 ) { dx += ddx; dy += ddy; } } } return( 0 ); } static int vips_quadratic_build( VipsObject *object ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); VipsResample *resample = VIPS_RESAMPLE( object ); VipsQuadratic *quadratic = (VipsQuadratic *) object; int window_size; int window_offset; VipsImage *in; VipsImage *t; if( VIPS_OBJECT_CLASS( vips_quadratic_parent_class )->build( object ) ) return( -1 ); /* We have the whole of the input in memory, so we can generate any * output. */ if( vips_image_pipelinev( resample->out, VIPS_DEMAND_STYLE_ANY, resample->in, NULL ) ) return( -1 ); in = resample->in; if( vips_check_uncoded( class->nickname, in ) || vips_check_noncomplex( class->nickname, in ) || vips_check_matrix( class->nickname, quadratic->coeff, &quadratic->mat ) ) return( -1 ); if( quadratic->mat->Xsize != 2 ) { vips_error( class->nickname, "%s", _( "coefficient matrix must have width 2" ) ); return( -1 ); } switch( quadratic->mat->Ysize ) { case 1: quadratic->order = 0; break; case 3: quadratic->order = 1; break; case 4: quadratic->order = 2; break; case 6: quadratic->order = 3; break; default: vips_error( class->nickname, "%s", _( "coefficient matrix must have height " "1, 3, 4 or 6" ) ); return( -1 ); } if( !quadratic->interpolate ) quadratic->interpolate = vips_interpolate_new( "bilinear" ); window_size = vips_interpolate_get_window_size( quadratic->interpolate ); window_offset = vips_interpolate_get_window_offset( quadratic->interpolate ); /* Enlarge the input image. */ if( vips_embed( in, &t, window_offset, window_offset, in->Xsize + window_size, in->Ysize + window_size, "extend", VIPS_EXTEND_COPY, NULL ) ) return( -1 ); vips_object_local( object, t ); in = t; /* We need random access to our input. */ if( !(t = vips_image_copy_memory( in )) ) return( -1 ); vips_object_local( object, t ); in = t; if( vips_image_generate( resample->out, vips_start_one, vips_quadratic_gen, vips_stop_one, in, quadratic ) ) return( -1 ); return( 0 ); } static void vips_quadratic_class_init( VipsQuadraticClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); VIPS_DEBUG_MSG( "vips_quadratic_class_init\n" ); gobject_class->dispose = vips_quadratic_dispose; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; vobject_class->nickname = "quadratic"; vobject_class->description = _( "resample an image with a quadratic transform" ); vobject_class->build = vips_quadratic_build; VIPS_ARG_IMAGE( class, "coeff", 8, _( "Coeff" ), _( "Coefficient matrix" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsQuadratic, coeff ) ); VIPS_ARG_INTERPOLATE( class, "interpolate", 9, _( "Interpolate" ), _( "Interpolate values with this" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsQuadratic, interpolate ) ); } static void vips_quadratic_init( VipsQuadratic *quadratic ) { } /** * vips_quadratic: (method) * @in: input image * @out: (out): output image * @coeff: horizontal quadratic * @...: %NULL-terminated list of optional named arguments * * Optional arguments: * * * @interpolate: use this interpolator (default bilinear) * * This operation is unfinished and unusable, sorry. * * See also: vips_affine(). * * Returns: 0 on success, -1 on error */ int vips_quadratic( VipsImage *in, VipsImage **out, VipsImage *coeff, ... ) { va_list ap; int result; va_start( ap, coeff ); result = vips_call_split( "quadratic", ap, in, out, coeff ); va_end( ap ); return( result ); }