1 /* convolution
2  *
3  * 12/8/13
4  * 	- from vips_hist_cum()
5  * 8/5/17
6  * 	- default to float ... int will often lose precision and should not be
7  * 	  the default
8  */
9 
10 /*
11 
12     This file is part of VIPS.
13 
14     VIPS is free software; you can redistribute it and/or modify
15     it under the terms of the GNU Lesser General Public License as published by
16     the Free Software Foundation; either version 2 of the License, or
17     (at your option) any later version.
18 
19     This program is distributed in the hope that it will be useful,
20     but WITHOUT ANY WARRANTY; without even the implied warranty of
21     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22     GNU Lesser General Public License for more details.
23 
24     You should have received a copy of the GNU Lesser General Public License
25     along with this program; if not, write to the Free Software
26     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27     02110-1301  USA
28 
29  */
30 
31 /*
32 
33     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
34 
35  */
36 
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif /*HAVE_CONFIG_H*/
40 #include <vips/intl.h>
41 
42 #include <stdio.h>
43 
44 #include <vips/vips.h>
45 #include <vips/internal.h>
46 
47 #include "pconvolution.h"
48 
49 typedef struct {
50 	VipsConvolution parent_instance;
51 
52 	VipsPrecision precision;
53 	int layers;
54 	int cluster;
55 } VipsConv;
56 
57 typedef VipsConvolutionClass VipsConvClass;
58 
59 G_DEFINE_TYPE( VipsConv, vips_conv, VIPS_TYPE_CONVOLUTION );
60 
61 static int
vips_conv_build(VipsObject * object)62 vips_conv_build( VipsObject *object )
63 {
64 	VipsConvolution *convolution = (VipsConvolution *) object;
65 	VipsConv *conv = (VipsConv *) object;
66 	VipsImage **t = (VipsImage **) vips_object_local_array( object, 4 );
67 
68 	VipsImage *in;
69 
70 	if( VIPS_OBJECT_CLASS( vips_conv_parent_class )->build( object ) )
71 		return( -1 );
72 
73 	g_object_set( conv, "out", vips_image_new(), NULL );
74 
75 	in = convolution->in;
76 
77 	/*
78 	printf( "vips_conv_build: convolving with:\n" );
79 	vips_matrixprint( convolution->M, NULL );
80  	 */
81 
82 	/* Unpack for processing.
83 	 */
84 	if( vips_image_decode( in, &t[0] ) )
85 		return( -1 );
86 	in = t[0];
87 
88 	switch( conv->precision ) {
89 	case VIPS_PRECISION_FLOAT:
90 		if( vips_convf( in, &t[1], convolution->M, NULL ) ||
91 			vips_image_write( t[1], convolution->out ) )
92 			return( -1 );
93 		break;
94 
95 	case VIPS_PRECISION_INTEGER:
96 		if( vips_convi( in, &t[1], convolution->M, NULL ) ||
97 			vips_image_write( t[1], convolution->out ) )
98 			return( -1 );
99 		break;
100 
101 	case VIPS_PRECISION_APPROXIMATE:
102 		if( vips_conva( in, &t[1], convolution->M,
103 			"layers", conv->layers,
104 			"cluster", conv->cluster,
105 			NULL ) ||
106 			vips_image_write( t[1], convolution->out ) )
107 			return( -1 );
108 		break;
109 
110 	default:
111 		g_assert_not_reached();
112 	}
113 
114 	vips_reorder_margin_hint( convolution->out,
115 		convolution->M->Xsize * convolution->M->Ysize );
116 
117 	return( 0 );
118 }
119 
120 static void
vips_conv_class_init(VipsConvClass * class)121 vips_conv_class_init( VipsConvClass *class )
122 {
123 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
124 	VipsObjectClass *object_class = (VipsObjectClass *) class;
125 
126 	gobject_class->set_property = vips_object_set_property;
127 	gobject_class->get_property = vips_object_get_property;
128 
129 	object_class->nickname = "conv";
130 	object_class->description = _( "convolution operation" );
131 	object_class->build = vips_conv_build;
132 
133 	VIPS_ARG_ENUM( class, "precision", 103,
134 		_( "Precision" ),
135 		_( "Convolve with this precision" ),
136 		VIPS_ARGUMENT_OPTIONAL_INPUT,
137 		G_STRUCT_OFFSET( VipsConv, precision ),
138 		VIPS_TYPE_PRECISION, VIPS_PRECISION_FLOAT );
139 
140 	VIPS_ARG_INT( class, "layers", 104,
141 		_( "Layers" ),
142 		_( "Use this many layers in approximation" ),
143 		VIPS_ARGUMENT_OPTIONAL_INPUT,
144 		G_STRUCT_OFFSET( VipsConv, layers ),
145 		1, 1000, 5 );
146 
147 	VIPS_ARG_INT( class, "cluster", 105,
148 		_( "Cluster" ),
149 		_( "Cluster lines closer than this in approximation" ),
150 		VIPS_ARGUMENT_OPTIONAL_INPUT,
151 		G_STRUCT_OFFSET( VipsConv, cluster ),
152 		1, 100, 1 );
153 
154 }
155 
156 static void
vips_conv_init(VipsConv * conv)157 vips_conv_init( VipsConv *conv )
158 {
159 	conv->precision = VIPS_PRECISION_FLOAT;
160 	conv->layers = 5;
161 	conv->cluster = 1;
162 }
163 
164 /**
165  * vips_conv: (method)
166  * @in: input image
167  * @out: (out): output image
168  * @mask: convolve with this mask
169  * @...: %NULL-terminated list of optional named arguments
170  *
171  * Optional arguments:
172  *
173  * * @precision: #VipsPrecision, calculation accuracy
174  * * @layers: %gint, number of layers for approximation
175  * * @cluster: %gint, cluster lines closer than this distance
176  *
177  * Convolution.
178  *
179  * Perform a convolution of @in with @mask.
180  * Each output pixel is calculated as:
181  *
182  * |[
183  * sigma[i]{pixel[i] * mask[i]} / scale + offset
184  * ]|
185  *
186  * where scale and offset are part of @mask.
187  *
188  * By default, @precision is
189  * #VIPS_PRECISION_FLOAT. The output image
190  * is always #VIPS_FORMAT_FLOAT unless @in is #VIPS_FORMAT_DOUBLE, in which case
191  * @out is also #VIPS_FORMAT_DOUBLE.
192  *
193  * If @precision is #VIPS_PRECISION_INTEGER, then
194  * elements of @mask are converted to
195  * integers before convolution, using rint(),
196  * and the output image
197  * always has the same #VipsBandFormat as the input image.
198  *
199  * For #VIPS_FORMAT_UCHAR images and #VIPS_PRECISION_INTEGER @precision,
200  * vips_conv() uses a fast vector path based on
201  * fixed-point arithmetic. This can produce slightly different results.
202  * Disable the vector path with `--vips-novector` or `VIPS_NOVECTOR` or
203  * vips_vector_set_enabled().
204  *
205  * If @precision is #VIPS_PRECISION_APPROXIMATE then, like
206  * #VIPS_PRECISION_INTEGER, @mask is converted to int before convolution, and
207  * the output image
208  * always has the same #VipsBandFormat as the input image.
209  *
210  * Larger values for @layers give more accurate
211  * results, but are slower. As @layers approaches the mask radius, the
212  * accuracy will become close to exact convolution and the speed will drop to
213  * match. For many large masks, such as Gaussian, @n_layers need be only 10% of
214  * this value and accuracy will still be good.
215  *
216  * Smaller values of @cluster will give more accurate results, but be slower
217  * and use more memory. 10% of the mask radius is a good rule of thumb.
218  *
219  * See also: vips_convsep().
220  *
221  * Returns: 0 on success, -1 on error
222  */
223 int
vips_conv(VipsImage * in,VipsImage ** out,VipsImage * mask,...)224 vips_conv( VipsImage *in, VipsImage **out, VipsImage *mask, ... )
225 {
226 	va_list ap;
227 	int result;
228 
229 	va_start( ap, mask );
230 	result = vips_call_split( "conv", ap, in, out, mask );
231 	va_end( ap );
232 
233 	return( result );
234 }
235