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