libvips/libvips/resample/quadratic.c

392 lines
8.5 KiB
C

/* 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 <config.h>
#endif /*HAVE_CONFIG_H*/
#include <glib/gi18n-lib.h>
#include <stdio.h>
#include <math.h>
#include <vips/vips.h>
#include <vips/debug.h>
#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 );
}