1 /* generate gaussian images
2  *
3  * Written on: 30/11/1989 by Nicos
4  * Updated on: 6/12/1991
5  * 7/8/96 JC
6  *	- ansified, mem leaks plugged
7  * 20/11/98 JC
8  *	- mask too large check added
9  * 18/3/09
10  * 	- bumped max mask size *40
11  * 	- added _sep variant
12  * 30/3/09
13  * 	- set scale in _sep variant, why not
14  * 21/10/10
15  * 	- gtkdoc
16  * 20/10/13
17  * 	- redone as a class
18  * 16/12/14
19  * 	- default to int output to match vips_conv()
20  * 	- use @precision, not @integer
21  * 10/3/16
22  * 	- allow 1x1 masks
23  * 	- better size calc
24  */
25 
26 /*
27 
28     This file is part of VIPS.
29 
30     VIPS is free software; you can redistribute it and/or modify
31     it under the terms of the GNU Lesser General Public License as published by
32     the Free Software Foundation; either version 2 of the License, or
33     (at your option) any later version.
34 
35     This program is distributed in the hope that it will be useful,
36     but WITHOUT ANY WARRANTY; without even the implied warranty of
37     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
38     GNU Lesser General Public License for more details.
39 
40     You should have received a copy of the GNU Lesser General Public License
41     along with this program; if not, write to the Free Software
42     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
43     02110-1301  USA
44 
45  */
46 
47 /*
48 
49     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
50 
51  */
52 
53 /*
54 #define VIPS_DEBUG
55  */
56 
57 #ifdef HAVE_CONFIG_H
58 #include <config.h>
59 #endif /*HAVE_CONFIG_H*/
60 #include <vips/intl.h>
61 
62 #include <stdio.h>
63 #include <string.h>
64 #include <stdlib.h>
65 #include <math.h>
66 
67 #include <vips/vips.h>
68 
69 #include "pcreate.h"
70 
71 typedef struct _VipsGaussmat {
72 	VipsCreate parent_instance;
73 
74 	double sigma;
75 	double min_ampl;
76 
77 	gboolean separable;
78 	gboolean integer;		/* Deprecated */
79 	VipsPrecision precision;
80 
81 } VipsGaussmat;
82 
83 typedef struct _VipsGaussmatClass {
84 	VipsCreateClass parent_class;
85 
86 } VipsGaussmatClass;
87 
88 G_DEFINE_TYPE( VipsGaussmat, vips_gaussmat, VIPS_TYPE_CREATE );
89 
90 /* Don't allow mask radius to go over this.
91  */
92 #define MASK_SANITY (5000)
93 
94 static int
vips_gaussmat_build(VipsObject * object)95 vips_gaussmat_build( VipsObject *object )
96 {
97 	VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
98 	VipsCreate *create = VIPS_CREATE( object );
99 	VipsGaussmat *gaussmat = (VipsGaussmat *) object;
100 	double sig2 =  2. * gaussmat->sigma * gaussmat->sigma;
101 	int max_x = VIPS_CLIP( 0, 8 * gaussmat->sigma, MASK_SANITY );
102 
103 	int x, y;
104 	int width, height;
105 	double sum;
106 
107 	if( VIPS_OBJECT_CLASS( vips_gaussmat_parent_class )->build( object ) )
108 		return( -1 );
109 
110 	/* The old, deprecated @integer property has been deliberately set to
111 	 * FALSE and they've not used the new @precision property ... switch
112 	 * to float to help them out.
113 	 */
114 	if( vips_object_argument_isset( object, "integer" ) &&
115 		!vips_object_argument_isset( object, "precision" ) &&
116 		!gaussmat->integer )
117 		gaussmat->precision = VIPS_PRECISION_FLOAT;
118 
119 	/* Find the size of the mask. Limit the mask size to 10k x 10k for
120 	 * sanity. We allow x == 0, meaning a 1x1 mask.
121 	 */
122 	for( x = 0; x < max_x; x++ ) {
123 		double v = exp( - ((double)(x * x)) / sig2 );
124 
125 		if( v < gaussmat->min_ampl )
126 			break;
127 	}
128 	if( x >= MASK_SANITY ) {
129 		vips_error( class->nickname, "%s", _( "mask too large" ) );
130 		return( -1 );
131 	}
132 	width = 2 * VIPS_MAX( x - 1, 0 ) + 1;
133 	height = gaussmat->separable ? 1 : width;
134 
135 	vips_image_init_fields( create->out,
136 		width, height, 1,
137 		VIPS_FORMAT_DOUBLE, VIPS_CODING_NONE,
138 		VIPS_INTERPRETATION_MULTIBAND,
139 		1.0, 1.0 );
140 	if( vips_image_pipelinev( create->out, VIPS_DEMAND_STYLE_ANY, NULL ) ||
141 		vips_image_write_prepare( create->out ) )
142 		return( -1 );
143 
144 	sum = 0.0;
145 	for( y = 0; y < height; y++ ) {
146 		for( x = 0; x < width; x++ ) {
147 			int xo = x - width / 2;
148 			int yo = y - height / 2;
149 			double distance = xo * xo + yo * yo;
150 			double v = exp( -distance / sig2 );
151 
152 			if( gaussmat->precision != VIPS_PRECISION_FLOAT )
153 				v = VIPS_RINT( 20 * v );
154 
155 			*VIPS_MATRIX( create->out, x, y ) = v;
156 			sum += v;
157 		}
158 	}
159 
160 	/* Make sure we can't make sum == 0: it'd certainly cause /0 later.
161 	 */
162 	if( sum == 0 )
163 		sum = 1;
164 
165 	vips_image_set_double( create->out, "scale", sum );
166 	vips_image_set_double( create->out, "offset", 0.0 );
167 
168 	return( 0 );
169 }
170 
171 static void
vips_gaussmat_class_init(VipsGaussmatClass * class)172 vips_gaussmat_class_init( VipsGaussmatClass *class )
173 {
174 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
175 	VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class );
176 
177 	gobject_class->set_property = vips_object_set_property;
178 	gobject_class->get_property = vips_object_get_property;
179 
180 	vobject_class->nickname = "gaussmat";
181 	vobject_class->description = _( "make a gaussian image" );
182 	vobject_class->build = vips_gaussmat_build;
183 
184 	VIPS_ARG_DOUBLE( class, "sigma", 2,
185 		_( "Sigma" ),
186 		_( "Sigma of Gaussian" ),
187 		VIPS_ARGUMENT_REQUIRED_INPUT,
188 		G_STRUCT_OFFSET( VipsGaussmat, sigma ),
189 		0.000001, 10000.0, 1.0 );
190 
191 	VIPS_ARG_DOUBLE( class, "min_ampl", 3,
192 		_( "Minimum amplitude" ),
193 		_( "Minimum amplitude of Gaussian" ),
194 		VIPS_ARGUMENT_REQUIRED_INPUT,
195 		G_STRUCT_OFFSET( VipsGaussmat, min_ampl ),
196 		0.000001, 10000.0, 0.1 );
197 
198 	VIPS_ARG_BOOL( class, "separable", 4,
199 		_( "Separable" ),
200 		_( "Generate separable Gaussian" ),
201 		VIPS_ARGUMENT_OPTIONAL_INPUT,
202 		G_STRUCT_OFFSET( VipsGaussmat, separable ),
203 		FALSE );
204 
205 	VIPS_ARG_BOOL( class, "integer", 5,
206 		_( "Integer" ),
207 		_( "Generate integer Gaussian" ),
208 		VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED,
209 		G_STRUCT_OFFSET( VipsGaussmat, integer ),
210 		FALSE );
211 
212 	VIPS_ARG_ENUM( class, "precision", 6,
213 		_( "Precision" ),
214 		_( "Generate with this precision" ),
215 		VIPS_ARGUMENT_OPTIONAL_INPUT,
216 		G_STRUCT_OFFSET( VipsGaussmat, precision ),
217 		VIPS_TYPE_PRECISION, VIPS_PRECISION_INTEGER );
218 
219 }
220 
221 static void
vips_gaussmat_init(VipsGaussmat * gaussmat)222 vips_gaussmat_init( VipsGaussmat *gaussmat )
223 {
224 	gaussmat->sigma = 1;
225 	gaussmat->min_ampl = 0.1;
226 	gaussmat->precision = VIPS_PRECISION_INTEGER;
227 }
228 
229 /**
230  * vips_gaussmat:
231  * @out: (out): output image
232  * @sigma: standard deviation of mask
233  * @min_ampl: minimum amplitude
234  * @...: %NULL-terminated list of optional named arguments
235  *
236  * Optional arguments:
237  *
238  * * @separable: generate a separable gaussian
239  * * @precision: #VipsPrecision for @out
240  *
241  * Creates a circularly symmetric Gaussian image of radius
242  * @sigma.  The size of the mask is determined by the variable @min_ampl;
243  * if for instance the value .1 is entered this means that the produced mask
244  * is clipped at values less than 10 percent of the maximum amplitude.
245  *
246  * The program uses the following equation:
247  *
248  *   H(r) = exp( -(r * r) / (2 * @sigma * @sigma) )
249  *
250  * The generated image has odd size and its maximum value is normalised to
251  * 1.0, unless @precision is #VIPS_PRECISION_INTEGER.
252  *
253  * If @separable is set, only the centre horizontal is generated. This is
254  * useful for separable convolutions.
255  *
256  * If @precision is #VIPS_PRECISION_INTEGER, an integer gaussian is generated.
257  * This is useful for integer convolutions.
258  *
259  * "scale" is set to the sum of all the mask elements.
260  *
261  * See also: vips_logmat(), vips_conv().
262  *
263  * Returns: 0 on success, -1 on error
264  */
265 int
vips_gaussmat(VipsImage ** out,double sigma,double min_ampl,...)266 vips_gaussmat( VipsImage **out, double sigma, double min_ampl, ... )
267 {
268 	va_list ap;
269 	int result;
270 
271 	va_start( ap, min_ampl );
272 	result = vips_call_split( "gaussmat", ap, out, sigma, min_ampl );
273 	va_end( ap );
274 
275 	return( result );
276 }
277