From 4e06d0a2b47b2ade90d3ac781a88468e18dfc2da Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 20 Oct 2013 12:17:29 +0100 Subject: [PATCH] redo im_gauss*mask*() as classes --- ChangeLog | 4 + libvips/conversion/rot45.c | 30 ++-- libvips/create/Makefile.am | 1 + libvips/create/create.c | 2 + libvips/create/gauss.c | 244 +++++++++++++++++++++++++ libvips/deprecated/Makefile.am | 1 - libvips/deprecated/im_gaussmasks.c | 280 ----------------------------- libvips/deprecated/vips7compat.c | 76 ++++++++ libvips/include/vips/create.h | 2 + libvips/include/vips/mask.h | 3 + 10 files changed, 344 insertions(+), 299 deletions(-) create mode 100644 libvips/create/gauss.c delete mode 100644 libvips/deprecated/im_gaussmasks.c diff --git a/ChangeLog b/ChangeLog index 23dfb8c6..3e2a4739 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +19/10/13 started 7.37.0 +- added vips_rot45() +- redone im_gauss_*mask*() as classes + 18/10/13 started 7.36.3 - fix compiler warnings in ubuntu 13.10 diff --git a/libvips/conversion/rot45.c b/libvips/conversion/rot45.c index bc47750a..4d2ef978 100644 --- a/libvips/conversion/rot45.c +++ b/libvips/conversion/rot45.c @@ -79,30 +79,24 @@ typedef VipsConversionClass VipsRot45Class; G_DEFINE_TYPE( VipsRot45, vips_rot45, VIPS_TYPE_CONVERSION ); -#define ASSIGN( Xout, Yout, Xin, Yin ) { \ - VipsPel *q = VIPS_IMAGE_ADDR( out, Xout, Yout ); \ - VipsPel *p = VIPS_IMAGE_ADDR( in, Xin, Yin ); \ +#define COPY( Q, P ) { \ + VipsPel *q = (Q); \ + VipsPel *p = (P); \ int b;\ \ for( b = 0; b < ps; b++ )\ q[b] = p[b];\ } -#define POINT_TO_TEMP( q, Xin, Yin ) { \ - VipsPel *p = VIPS_IMAGE_ADDR( in, Xin, Yin ); \ - int b;\ - \ - for( b = 0; b < ps; b++ )\ - q[b] = p[b];\ -} +#define ASSIGN( Xout, Yout, Xin, Yin ) \ + COPY( VIPS_IMAGE_ADDR( out, Xout, Yout ), \ + VIPS_IMAGE_ADDR( in, Xin, Yin ) ) -#define TEMP_TO_POINT( Xout, Yout, p ) { \ - VipsPel *q = VIPS_IMAGE_ADDR( out, Xout, Yout ); \ - int b;\ - \ - for( b = 0; b < ps; b++ )\ - q[b] = p[b];\ -} +#define POINT_TO_TEMP( q, Xin, Yin ) \ + COPY( q, VIPS_IMAGE_ADDR( in, Xin, Yin ) ) + +#define TEMP_TO_POINT( Xout, Yout, p ) \ + COPY( VIPS_IMAGE_ADDR( out, Xout, Yout ), p ) /* This can work inplace, ie. in == out is allowed. */ @@ -118,7 +112,7 @@ vips_rot45_rot45( VipsImage *out, VipsImage *in ) g_assert( in->Xsize == in->Ysize ); g_assert( out->Xsize == out->Ysize ); - g_assert( in->Xsize == out->Ssize ); + g_assert( in->Xsize == out->Xsize ); g_assert( in->Xsize % 2 == 0 ); /* Split the square into 8 triangles. Loop over the top-left one, diff --git a/libvips/create/Makefile.am b/libvips/create/Makefile.am index 60812e38..44752d97 100644 --- a/libvips/create/Makefile.am +++ b/libvips/create/Makefile.am @@ -3,6 +3,7 @@ noinst_LTLIBRARIES = libcreate.la libcreate_la_SOURCES = \ create.c \ pcreate.h \ + gauss.c \ buildlut.c \ invertlut.c \ tonelut.c \ diff --git a/libvips/create/create.c b/libvips/create/create.c index 61b6e234..4f75dea1 100644 --- a/libvips/create/create.c +++ b/libvips/create/create.c @@ -111,6 +111,7 @@ void vips_create_operation_init( void ) { extern GType vips_black_get_type( void ); + extern GType vips_gauss_get_type( void ); extern GType vips_gaussnoise_get_type( void ); #ifdef HAVE_PANGOFT2 extern GType vips_text_get_type( void ); @@ -126,6 +127,7 @@ vips_create_operation_init( void ) extern GType vips_identity_get_type( void ); vips_black_get_type(); + vips_gauss_get_type(); vips_gaussnoise_get_type(); #ifdef HAVE_PANGOFT2 vips_text_get_type(); diff --git a/libvips/create/gauss.c b/libvips/create/gauss.c new file mode 100644 index 00000000..47a64cca --- /dev/null +++ b/libvips/create/gauss.c @@ -0,0 +1,244 @@ +/* generate gaussian images + * + * Written on: 30/11/1989 by Nicos + * Updated on: 6/12/1991 + * 7/8/96 JC + * - ansified, mem leaks plugged + * 20/11/98 JC + * - mask too large check added + * 18/3/09 + * - bumped max mask size *40 + * - added _sep variant + * 30/3/09 + * - set scale in _sep variant, why not + * 21/10/10 + * - gtkdoc + * 20/10/10 + * - redone as a class + */ + +/* + + 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 VIPS_DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#include "pcreate.h" + +typedef struct _VipsGauss { + VipsCreate parent_instance; + + double sigma; + double min_ampl; + + gboolean separable; + gboolean integer; + +} VipsGauss; + +typedef struct _VipsGaussClass { + VipsCreateClass parent_class; + +} VipsGaussClass; + +G_DEFINE_TYPE( VipsGauss, vips_gauss, VIPS_TYPE_CREATE ); + +static int +vips_gauss_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsCreate *create = VIPS_CREATE( object ); + VipsGauss *gauss = (VipsGauss *) object; + + int x, y; + int max_x; + int width, height; + double sig2; /* sig2 = 2. * sigma * sigma */ + double sum; + + if( VIPS_OBJECT_CLASS( vips_gauss_parent_class )->build( object ) ) + return( -1 ); + + /* Find the size of the mask. Limit the mask size to 10k x 10k for + * sanity. + */ + sig2 = 2. * gauss->sigma * gauss->sigma; + max_x = 8 * gauss->sigma > 5000 ? 5000 : 8 * gauss->sigma ; + for( x = 0; x < max_x; x++ ) { + double v = exp( - ((double)(x * x)) / sig2 ); + + if( v < gauss->min_ampl ) + break; + } + if( x == max_x ) { + vips_error( class->nickname, "%s", _( "mask too large" ) ); + return( -1 ); + } + width = x * 2 + 1; + height = gauss->separable ? 1 : width; + + vips_image_init_fields( create->out, + width, height, 1, + VIPS_FORMAT_DOUBLE, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, + 1.0, 1.0 ); + vips_demand_hint( create->out, + VIPS_DEMAND_STYLE_ANY, NULL ); + if( vips_image_write_prepare( create->out ) ) + return( -1 ); + + sum = 0.0; + for( y = 0; y < height; y++ ) { + for( x = 0; x < width; x++ ) { + int xo = x - width / 2; + int yo = y - height / 2; + double distance = xo * xo + yo * yo; + double v = exp( -distance / sig2 ); + + if( gauss->integer ) + v = VIPS_RINT( 20 * v ); + + *VIPS_MATRIX( create->out, x, y ) = v; + sum += v; + } + } + + vips_image_set_double( create->out, "scale", sum ); + vips_image_set_double( create->out, "offset", 0.0 ); + + return( 0 ); +} + +static void +vips_gauss_class_init( VipsGaussClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "gauss"; + vobject_class->description = _( "make a gaussian image" ); + vobject_class->build = vips_gauss_build; + + VIPS_ARG_DOUBLE( class, "sigma", 2, + _( "Radius" ), + _( "Radius of Gaussian" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsGauss, sigma ), + 0.000001, 10000.0, 1.0 ); + + VIPS_ARG_DOUBLE( class, "min_ampl", 3, + _( "Width" ), + _( "Minimum amplitude of Gaussian" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsGauss, min_ampl ), + 0.000001, 10000.0, 0.1 ); + + VIPS_ARG_BOOL( class, "separable", 4, + _( "Separable" ), + _( "Generate separable Gaussian" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsGauss, separable ), + FALSE ); + + VIPS_ARG_BOOL( class, "integer", 5, + _( "Integer" ), + _( "Generate integer Gaussian" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsGauss, integer ), + FALSE ); + +} + +static void +vips_gauss_init( VipsGauss *gauss ) +{ + gauss->sigma = 1; + gauss->min_ampl = 0.1; +} + +/** + * vips_gauss: + * @sigma: standard deviation of mask + * @min_ampl: minimum amplitude + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * @separable: generate a separable gaussian + * @integer: generate an integer gaussian + * + * Creates a circularly symmetric Gaussian image of radius + * @sigma. The size of the mask is determined by the variable @min_ampl; + * if for instance the value .1 is entered this means that the produced mask + * is clipped at values less than 10 percent of the maximum amplitude. + * + * The program uses the following equation: + * + * H(r) = exp( -(r * r) / (2 * @sigma * @sigma) ) + * + * The generated image has odd size and its maximum value is normalised to + * 1.0, unless @integer is set. + * + * If @separable is set, only the centre horizontal is generated. This is + * useful for separable convolutions. + * + * If @integer is set, an integer gaussian is generated. This is useful for + * integer convolutions. + * + * "scale" is set to the sum of all the mask elements. + * + * See also: im_log_dmask(), vips_conv(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_gauss( VipsImage **out, double sigma, double min_ampl, ... ) +{ + va_list ap; + int result; + + va_start( ap, min_ampl ); + result = vips_call_split( "gauss", ap, out, sigma, min_ampl ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/deprecated/Makefile.am b/libvips/deprecated/Makefile.am index c61a6f82..cbd17ab9 100644 --- a/libvips/deprecated/Makefile.am +++ b/libvips/deprecated/Makefile.am @@ -45,7 +45,6 @@ libdeprecated_la_SOURCES = \ im_mask2vips.c \ im_vips2mask.c \ rotmask.c \ - im_gaussmasks.c \ im_logmasks.c \ rw_mask.c \ im_matcat.c \ diff --git a/libvips/deprecated/im_gaussmasks.c b/libvips/deprecated/im_gaussmasks.c deleted file mode 100644 index 0d1dc13d..00000000 --- a/libvips/deprecated/im_gaussmasks.c +++ /dev/null @@ -1,280 +0,0 @@ -/* generate gaussian masks - * - * Written on: 30/11/1989 by Nicos - * Updated on: 6/12/1991 - * 7/8/96 JC - * - ansified, mem leaks plugged - * 20/11/98 JC - * - mask too large check added - * 18/3/09 - * - bumped max mask size *40 - * - added _sep variant - * 30/3/09 - * - set scale in _sep variant, why not - * 21/10/10 - * - gtkdoc - */ - -/* - - 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 - - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#include - -#include - -#define IM_MAXMASK 5000 - -/** - * im_gauss_dmask: - * @filename: the returned mask has this set as the filename - * @sigma: standard deviation of mask - * @min_ampl: minimum amplitude - * - * im_gauss_dmask() creates a circularly symmetric Gaussian mask of radius - * @sigma. The size of the mask is determined by the variable @min_ampl; - * if for instance the value .1 is entered this means that the produced mask - * is clipped at values less than 10 percent of the maximum amplitude. - * - * The program uses the following equation: - * - * H(r) = exp( -(r * r) / (2 * sigma * sigma) ) - * - * The generated mask has odd size and its maximum value is normalised to - * 1.0. - * - * See also: im_gauss_imask(), im_gauss_imask_sep(), im_log_dmask(), im_conv(). - * - * Returns: the calculated mask on success, or NULL on error. - */ -DOUBLEMASK * -im_gauss_dmask( const char *filename, double sigma, double min_ampl ) -{ - int x, y, k; - double distance; - double temp; - double *pt1, *pt2, *pt3, *pt4; - int max_x; - int xm, ym; - int xm2, ym2; /* xm2 = xm/2 */ - int offset; - double *cf, *cfs, *mc; - DOUBLEMASK *m; - double sig2, sum; /* sig2 = 2. * sigma * sigma */ - - /* Find the size of the mask depending on the entered data - */ - sig2 = 2. * sigma * sigma; - max_x = 8 * sigma > IM_MAXMASK ? IM_MAXMASK : 8 * sigma ; - for( x = 0; x < max_x; x++ ) { - temp = exp( - ((double)(x * x))/sig2 ); - if( temp < min_ampl ) - break; - } - if( x == max_x ) { - im_error( "im_gauss_dmask", "%s", _( "mask too large" ) ); - return( NULL ); - } - - xm2 = x; - ym2 = x; - xm = xm2 * 2 + 1; - ym = ym2 * 2 + 1; - - if( !(cfs = IM_ARRAY( NULL, (xm2+1)*(ym2+1), double )) ) - return( NULL ); - - for( k = 0, y = 0; y <= ym2; y++ ) { - for( x = 0; x <= xm2; x++, k++ ) { - distance = x*x + y*y; - cfs[k] = exp( -distance / sig2 ); - } - } - -#ifdef PIM_RINT - for( k = 0, y = 0; y <= ymask_2; y++ ) { - for( x = 0; x <= xmask_2; x++, k++ ) - fprintf(stderr, "%3.2f ", cfs[k] ); - fprintf(stderr, "\n"); - } -#endif - - if( !(m = im_create_dmask( filename, xm, ym )) ) { - im_free( cfs ); - return( NULL ); - } - - /* copy the 1/4 cfs into the m - */ - cf = cfs; - offset = xm2 * (xm + 1); - mc = m->coeff + offset; - for( y = 0; y <= ym2; y++ ) { - for( x = 0; x <= xm2; x++ ) { - pt1 = mc + (y * xm) + x; - pt2 = mc - (y * xm) + x; - pt3 = mc + (y * xm) - x; - pt4 = mc - (y * xm) - x; - - *pt1 = cf[x]; - *pt2 = cf[x]; - *pt3 = cf[x]; - *pt4 = cf[x]; - } - - cf += (xm2 + 1); - } - im_free( cfs ); - - sum = 0.0; - for( k = 0, y = 0; y < m->ysize; y++ ) - for( x = 0; x < m->xsize; x++, k++ ) - sum += m->coeff[k]; - m->scale = sum; - m->offset = 0.0; - -#ifdef PIM_RINT - im_print_dmask( m ); -#endif - return( m ); -} - -/** - * im_gauss_dmask_sep: - * @filename: the returned mask has this set as the filename - * @sigma: standard deviation of mask - * @min_ampl: minimum amplitude - * - * im_gauss_dmask_sep() works exactly as im_gauss_dmask(), but returns only - * the central line of the mask. This is useful with im_convsepf(). - * - * See also: im_gauss_dmask(), im_convsepf(). - * - * Returns: the calculated mask on success, or NULL on error. - */ -DOUBLEMASK * -im_gauss_dmask_sep( const char *filename, double sigma, double min_ampl ) -{ - DOUBLEMASK *im; - DOUBLEMASK *im2; - int i; - double sum; - - if( !(im = im_gauss_dmask( filename, sigma, min_ampl )) ) - return( NULL ); - if( !(im2 = im_create_dmask( filename, im->xsize, 1 )) ) { - im_free_dmask( im ); - return( NULL ); - } - - sum = 0; - for( i = 0; i < im->xsize; i++ ) { - im2->coeff[i] = im->coeff[i + im->xsize * (im->ysize / 2)]; - sum += im2->coeff[i]; - } - im2->scale = sum; - - im_free_dmask( im ); - - return( im2 ); -} - -/** - * im_gauss_imask: - * @filename: the returned mask has this set as the filename - * @sigma: standard deviation of mask - * @min_ampl: minimum amplitude - * - * im_gauss_imask() works exactly as im_gauss_dmask(), but the returned mask - * is scaled so that it's maximum value it set to 100. - * - * See also: im_gauss_dmask(), im_gauss_imask_sep(), im_conv(), im_convsep(). - * - * Returns: the calculated mask on success, or NULL on error. - */ -INTMASK * -im_gauss_imask( const char *filename, double sigma, double min_ampl ) -{ - DOUBLEMASK *dm; - INTMASK *im; - - if( !(dm = im_gauss_dmask( filename, sigma, min_ampl )) ) - return( NULL ); - - if( !(im = im_scale_dmask( dm, dm->filename )) ) { - im_free_dmask( dm ); - return( NULL ); - } - im_free_dmask( dm ); - - return( im ) ; -} - -/** - * im_gauss_imask_sep: - * @filename: the returned mask has this set as the filename - * @sigma: standard deviation of mask - * @min_ampl: minimum amplitude - * - * im_gauss_imask_sep() works exactly as im_gauss_imask(), but returns only - * the central line of the mask. This is useful with im_convsep(). - * - * See also: im_gauss_dmask(), im_convsep(). - * - * Returns: the calculated mask on success, or NULL on error. - */ -INTMASK * -im_gauss_imask_sep( const char *filename, double sigma, double min_ampl ) -{ - INTMASK *im; - INTMASK *im2; - int i; - int sum; - - if( !(im = im_gauss_imask( filename, sigma, min_ampl )) ) - return( NULL ); - if( !(im2 = im_create_imask( filename, im->xsize, 1 )) ) { - im_free_imask( im ); - return( NULL ); - } - - sum = 0; - for( i = 0; i < im->xsize; i++ ) { - im2->coeff[i] = im->coeff[i + im->xsize * (im->ysize / 2)]; - sum += im2->coeff[i]; - } - im2->scale = sum; - - im_free_imask( im ); - - return( im2 ); -} diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index 046ae5ef..783554c5 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -1894,6 +1894,82 @@ im_stats( VipsImage *in ) return( msk ); } +DOUBLEMASK * +im_gauss_dmask( const char *filename, double sigma, double min_ampl ) +{ + VipsImage *t; + DOUBLEMASK *msk; + + if( vips_gauss( &t, sigma, min_ampl, + NULL ) ) + return( NULL ); + if( !(msk = im_vips2mask( t, filename )) ) { + g_object_unref( t ); + return( NULL ); + } + g_object_unref( t ); + + return( msk ); +} + +DOUBLEMASK * +im_gauss_dmask_sep( const char *filename, double sigma, double min_ampl ) +{ + VipsImage *t; + DOUBLEMASK *msk; + + if( vips_gauss( &t, sigma, min_ampl, + "seperable", TRUE, + NULL ) ) + return( NULL ); + if( !(msk = im_vips2mask( t, filename )) ) { + g_object_unref( t ); + return( NULL ); + } + g_object_unref( t ); + + return( msk ); +} + +INTMASK * +im_gauss_imask( const char *filename, double sigma, double min_ampl ) +{ + VipsImage *t; + INTMASK *msk; + + if( vips_gauss( &t, sigma, min_ampl, + "integer", TRUE, + NULL ) ) + return( NULL ); + if( !(msk = im_vips2imask( t, filename )) ) { + g_object_unref( t ); + return( NULL ); + } + g_object_unref( t ); + + return( msk ); +} + +INTMASK * +im_gauss_imask_sep( const char *filename, double sigma, double min_ampl ) +{ + VipsImage *t; + INTMASK *msk; + + if( vips_gauss( &t, sigma, min_ampl, + "integer", TRUE, + "seperable", TRUE, + NULL ) ) + return( NULL ); + if( !(msk = im_vips2imask( t, filename )) ) { + g_object_unref( t ); + return( NULL ); + } + g_object_unref( t ); + + return( msk ); +} + int im_recomb( IMAGE *in, IMAGE *out, DOUBLEMASK *recomb ) { diff --git a/libvips/include/vips/create.h b/libvips/include/vips/create.h index abaa7167..4c68abe8 100644 --- a/libvips/include/vips/create.h +++ b/libvips/include/vips/create.h @@ -47,6 +47,8 @@ int vips_xyz( VipsImage **out, int width, int height, ... ) __attribute__((sentinel)); int vips_grey( VipsImage **out, int width, int height, ... ) __attribute__((sentinel)); +int vips_gauss( VipsImage **out, double sigma, double min_ampl, ... ) + __attribute__((sentinel)); int vips_text( VipsImage **out, const char *text, ... ) __attribute__((sentinel)); diff --git a/libvips/include/vips/mask.h b/libvips/include/vips/mask.h index 8ce14e19..624dc7c0 100644 --- a/libvips/include/vips/mask.h +++ b/libvips/include/vips/mask.h @@ -4,6 +4,9 @@ * - from proto.h */ +/* All deprecated. + */ + /* This file is part of VIPS.