diff --git a/libvips/convolution/Makefile.am b/libvips/convolution/Makefile.am index 6c40e01d..f37d26f3 100644 --- a/libvips/convolution/Makefile.am +++ b/libvips/convolution/Makefile.am @@ -6,7 +6,6 @@ libconvolution_la_SOURCES = \ im_compass.c \ im_conv.c \ im_conv_f.c \ - im_convsep_f.c \ im_contrast_surface.c \ im_fastcor.c \ im_gradcor.c \ diff --git a/libvips/convolution/im_conv_f.c b/libvips/convolution/im_conv_f.c index cf467584..e3b29af8 100644 --- a/libvips/convolution/im_conv_f.c +++ b/libvips/convolution/im_conv_f.c @@ -38,6 +38,9 @@ * - more cleanups * 1/10/10 * - support complex (just double the bands) + * 29/10/10 + * - get rid of im_convsep_f(), just call this twice, no longer worth + * keeping two versions */ /* @@ -390,3 +393,76 @@ im_conv_f( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) return( 0 ); } + +int +im_convsep_f_raw( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) +{ + IMAGE *t; + DOUBLEMASK *rmask; + + if( mask->xsize != 1 && mask->ysize != 1 ) { + im_error( "im_convsep_f", + "%s", _( "expect 1xN or Nx1 input mask" ) ); + return( -1 ); + } + + if( !(t = im_open_local( out, "im_convsep_f", "p" )) || + !(rmask = (INTMASK *) im_local( out, + (im_construct_fn) im_dup_dmask, + (im_callback_fn) im_free_dmask, mask, mask->filename, NULL )) ) + return( -1 ); + + rmask->xsize = mask->ysize; + rmask->ysize = mask->xsize; + + if( im_conv_f_raw( in, t, mask ) || + im_conv_f_raw( t, out, rmask ) ) + return( -1 ); + + return( 0 ); +} + +/** + * im_convsep_f: + * @in: input image + * @out: output image + * @mask: convolution mask + * + * Perform a separable convolution of @in with @mask using floating-point + * arithmetic. + * + * The mask must be 1xn or nx1 elements. + * The output image + * is always %IM_BANDFMT_FLOAT unless @in is %IM_BANDFMT_DOUBLE, in which case + * @out is also %IM_BANDFMT_DOUBLE. + * + * The image is convolved twice: once with @mask and then again with @mask + * rotated by 90 degrees. This is much faster for certain types of mask + * (gaussian blur, for example) than doing a full 2D convolution. + * + * Each output pixel is + * calculated as sigma[i]{pixel[i] * mask[i]} / scale + offset, where scale + * and offset are part of @mask. + * + * See also: im_convsep(), im_conv(), im_create_dmaskv(). + * + * Returns: 0 on success, -1 on error + */ +int +im_convsep_f( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) +{ + IMAGE *t1 = im_open_local( out, "im_convsep intermediate", "p" ); + int size = mask->xsize * mask->ysize; + + if( !t1 || + im_embed( in, t1, 1, size / 2, size / 2, + in->Xsize + size - 1, + in->Ysize + size - 1 ) || + im_convsep_f_raw( t1, out, mask ) ) + return( -1 ); + + out->Xoffset = 0; + out->Yoffset = 0; + + return( 0 ); +} diff --git a/libvips/convolution/im_convsep_f.c b/libvips/convolution/im_convsep_f.c deleted file mode 100644 index 946827dc..00000000 --- a/libvips/convolution/im_convsep_f.c +++ /dev/null @@ -1,368 +0,0 @@ -/* im_convsep_f - * - * Copyright: 1990, N. Dessipris. - * - * Author: Nicos Dessipris - * Written on: 29/04/1991 - * Modified on: 29/4/93 K.Martinez for sys5 - * 9/3/01 JC - * - rewritten using im_conv() - * 7/4/04 - * - now uses im_embed() with edge stretching on the input, not - * the output - * - sets Xoffset / Yoffset - * 3/2/10 - * - gtkdoc - * - more cleanups - * 1/10/10 - * - support complex (just double the bands) - */ - -/* - - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - */ - -/* - - These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk - - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#include -#include - -#include - -#ifdef WITH_DMALLOC -#include -#endif /*WITH_DMALLOC*/ - -/* Our parameters ... we take a copy of the mask argument. - */ -typedef struct { - IMAGE *in; - IMAGE *out; - DOUBLEMASK *mask; /* Copy of mask arg */ - - int size; /* N for our 1xN or Nx1 mask */ - int scale; /* Our scale ... we have to ^2 mask->scale */ -} Conv; - -/* End of evaluation. - */ -static int -conv_destroy( Conv *conv ) -{ - IM_FREEF( im_free_dmask, conv->mask ); - - return( 0 ); -} - -static Conv * -conv_new( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) -{ - Conv *conv = IM_NEW( out, Conv ); - - if( !conv ) - return( NULL ); - - conv->in = in; - conv->out = out; - conv->mask = NULL; - conv->size = mask->xsize * mask->ysize; - conv->scale = mask->scale * mask->scale; - - if( im_add_close_callback( out, - (im_callback_fn) conv_destroy, conv, NULL ) || - !(conv->mask = im_dup_dmask( mask, "conv_mask" )) ) - return( NULL ); - - return( conv ); -} - -/* Our sequence value. - */ -typedef struct { - Conv *conv; - REGION *ir; /* Input region */ - - PEL *sum; /* Line buffer */ -} ConvSequence; - -/* Free a sequence value. - */ -static int -conv_stop( void *vseq, void *a, void *b ) -{ - ConvSequence *seq = (ConvSequence *) vseq; - - IM_FREEF( im_region_free, seq->ir ); - - return( 0 ); -} - -/* Convolution start function. - */ -static void * -conv_start( IMAGE *out, void *a, void *b ) -{ - IMAGE *in = (IMAGE *) a; - Conv *conv = (Conv *) b; - ConvSequence *seq; - - if( !(seq = IM_NEW( out, ConvSequence )) ) - return( NULL ); - - /* Init! - */ - seq->conv = conv; - seq->ir = NULL; - seq->sum = NULL; - - /* Attach region and arrays. - */ - seq->ir = im_region_create( in ); - if( vips_bandfmt_isint( conv->out->BandFmt ) ) - seq->sum = (PEL *) - IM_ARRAY( out, IM_IMAGE_N_ELEMENTS( in ), int ); - else - seq->sum = (PEL *) - IM_ARRAY( out, IM_IMAGE_N_ELEMENTS( in ), double ); - if( !seq->ir || !seq->sum ) { - conv_stop( seq, in, conv ); - return( NULL ); - } - - return( (void *) seq ); -} - -/* What we do for every point in the mask, for each pixel. - */ -#define VERTICAL_CONV { z -= 1; li -= lskip; sum += coeff[z] * vfrom[li]; } -#define HORIZONTAL_CONV { z -= 1; li -= bands; sum += coeff[z] * hfrom[li]; } - -#define CONV_FLOAT( ITYPE, OTYPE ) { \ - ITYPE *vfrom; \ - double *vto; \ - double *hfrom; \ - OTYPE *hto; \ - \ - /* Convolve to sum array. We convolve the full width of \ - * this input line. \ - */ \ - vfrom = (ITYPE *) IM_REGION_ADDR( ir, le, y ); \ - vto = (double *) seq->sum; \ - for( x = 0; x < isz; x++ ) { \ - double sum; \ - \ - z = conv->size; \ - li = lskip * z; \ - sum = 0; \ - \ - IM_UNROLL( z, VERTICAL_CONV ); \ - \ - vto[x] = sum; \ - vfrom += 1; \ - } \ - \ - /* Convolve sums to output. \ - */ \ - hfrom = (double *) seq->sum; \ - hto = (OTYPE *) IM_REGION_ADDR( or, le, y ); \ - for( x = 0; x < osz; x++ ) { \ - double sum; \ - \ - z = conv->size; \ - li = bands * z; \ - sum = 0; \ - \ - IM_UNROLL( z, HORIZONTAL_CONV ); \ - \ - sum = (sum / conv->scale) + mask->offset; \ - \ - hto[x] = sum; \ - hfrom += 1; \ - } \ -} - -/* Convolve! - */ -static int -conv_gen( REGION *or, void *vseq, void *a, void *b ) -{ - ConvSequence *seq = (ConvSequence *) vseq; - IMAGE *in = (IMAGE *) a; - Conv *conv = (Conv *) b; - REGION *ir = seq->ir; - DOUBLEMASK *mask = conv->mask; - double *coeff = conv->mask->coeff; - int bands = in->Bands; - - Rect *r = &or->valid; - int le = r->left; - int to = r->top; - int bo = IM_RECT_BOTTOM(r); - int osz = IM_REGION_N_ELEMENTS( or ) * - (vips_bandfmt_iscomplex( in->BandFmt ) ? 2 : 1); - - Rect s; - int lskip; - int isz; - int x, y, z, li; - - /* Prepare the section of the input image we need. A little larger - * than the section of the output image we are producing. - */ - s = *r; - s.width += conv->size - 1; - s.height += conv->size - 1; - if( im_prepare( ir, &s ) ) - return( -1 ); - lskip = IM_REGION_LSKIP( ir ) / IM_IMAGE_SIZEOF_ELEMENT( in ); - isz = IM_REGION_N_ELEMENTS( ir ); - - for( y = to; y < bo; y++ ) { - switch( in->BandFmt ) { - case IM_BANDFMT_UCHAR: - CONV_FLOAT( unsigned char, float ); break; - case IM_BANDFMT_CHAR: - CONV_FLOAT( signed char, float ); break; - case IM_BANDFMT_USHORT: - CONV_FLOAT( unsigned short, float ); break; - case IM_BANDFMT_SHORT: - CONV_FLOAT( signed short, float ); break; - case IM_BANDFMT_UINT: - CONV_FLOAT( unsigned int, float ); break; - case IM_BANDFMT_INT: - CONV_FLOAT( signed int, float ); break; - case IM_BANDFMT_FLOAT: - case IM_BANDFMT_COMPLEX: - CONV_FLOAT( float, float ); break; - case IM_BANDFMT_DOUBLE: - case IM_BANDFMT_DPCOMPLEX: - CONV_FLOAT( double, double ); break; - - default: - g_assert( 0 ); - } - } - - return( 0 ); -} - -int -im_convsep_f_raw( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) -{ - Conv *conv; - - /* Check parameters. - */ - if( im_piocheck( in, out ) || - im_check_uncoded( "im_convsep_f", in ) || - im_check_dmask( "im_convsep_f", mask ) ) - return( -1 ); - if( mask->xsize != 1 && mask->ysize != 1 ) { - im_error( "im_convsep_f", - "%s", _( "expect 1xN or Nx1 input mask" ) ); - return( -1 ); - } - if( mask->scale == 0 ) { - im_error( "im_convsep_f", "%s", "mask scale must be non-zero" ); - return( -1 ); - } - if( !(conv = conv_new( in, out, mask )) ) - return( -1 ); - - /* Prepare output. Consider a 7x7 mask and a 7x7 image --- the output - * would be 1x1. - */ - if( im_cp_desc( out, in ) ) - return( -1 ); - if( vips_bandfmt_isint( in->BandFmt ) ) - out->BandFmt = IM_BANDFMT_FLOAT; - out->Xsize -= conv->size - 1; - out->Ysize -= conv->size - 1; - if( out->Xsize <= 0 || out->Ysize <= 0 ) { - im_error( "im_convsep_f", - "%s", _( "image too small for mask" ) ); - return( -1 ); - } - - /* SMALLTILE seems fastest. - */ - if( im_demand_hint( out, IM_SMALLTILE, in, NULL ) || - im_generate( out, conv_start, conv_gen, conv_stop, in, conv ) ) - return( -1 ); - - out->Xoffset = -conv->size / 2; - out->Yoffset = -conv->size / 2; - - return( 0 ); -} - -/** - * im_convsep_f: - * @in: input image - * @out: output image - * @mask: convolution mask - * - * Perform a separable convolution of @in with @mask using floating-point - * arithmetic. - * - * The mask must be 1xn or nx1 elements. - * The output image - * is always %IM_BANDFMT_FLOAT unless @in is %IM_BANDFMT_DOUBLE, in which case - * @out is also %IM_BANDFMT_DOUBLE. - * - * The image is convolved twice: once with @mask and then again with @mask - * rotated by 90 degrees. This is much faster for certain types of mask - * (gaussian blur, for example) than doing a full 2D convolution. - * - * Each output pixel is - * calculated as sigma[i]{pixel[i] * mask[i]} / scale + offset, where scale - * and offset are part of @mask. - * - * See also: im_convsep(), im_conv(), im_create_dmaskv(). - * - * Returns: 0 on success, -1 on error - */ -int -im_convsep_f( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) -{ - IMAGE *t1 = im_open_local( out, "im_convsep_f intermediate", "p" ); - int size = mask->xsize * mask->ysize; - - if( !t1 || - im_embed( in, t1, 1, size / 2, size / 2, - in->Xsize + size - 1, - in->Ysize + size - 1 ) || - im_convsep_f_raw( t1, out, mask ) ) - return( -1 ); - - out->Xoffset = 0; - out->Yoffset = 0; - - return( 0 ); -}