/src/libvips/libvips/convolution/convsep.c
Line | Count | Source |
1 | | /* convolve twice, rotating the mask |
2 | | * |
3 | | * 23/10/13 |
4 | | * - from vips_convsep() |
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 <glib/gi18n-lib.h> |
41 | | |
42 | | #include <stdio.h> |
43 | | |
44 | | #include <vips/vips.h> |
45 | | |
46 | | #include "pconvolution.h" |
47 | | |
48 | | typedef struct { |
49 | | VipsConvolution parent_instance; |
50 | | |
51 | | VipsPrecision precision; |
52 | | int layers; |
53 | | int cluster; |
54 | | } VipsConvsep; |
55 | | |
56 | | typedef VipsConvolutionClass VipsConvsepClass; |
57 | | |
58 | 40 | G_DEFINE_TYPE(VipsConvsep, vips_convsep, VIPS_TYPE_CONVOLUTION); |
59 | 40 | |
60 | 40 | static int |
61 | 40 | vips_convsep_build(VipsObject *object) |
62 | 46.5k | { |
63 | 46.5k | VipsObjectClass *class = VIPS_OBJECT_GET_CLASS(object); |
64 | 46.5k | VipsConvolution *convolution = (VipsConvolution *) object; |
65 | 46.5k | VipsConvsep *convsep = (VipsConvsep *) object; |
66 | 46.5k | VipsImage **t = (VipsImage **) vips_object_local_array(object, 4); |
67 | | |
68 | 46.5k | VipsImage *in; |
69 | | |
70 | 46.5k | g_object_set(convsep, "out", vips_image_new(), NULL); |
71 | | |
72 | 46.5k | if (VIPS_OBJECT_CLASS(vips_convsep_parent_class)->build(object)) |
73 | 1 | return -1; |
74 | | |
75 | 46.5k | if (vips_check_separable(class->nickname, convolution->M)) |
76 | 1 | return -1; |
77 | | |
78 | 46.5k | in = convolution->in; |
79 | | |
80 | 46.5k | if (convsep->precision == VIPS_PRECISION_APPROXIMATE) { |
81 | 7 | if (vips_convasep(in, &t[0], convolution->M, |
82 | 7 | "layers", convsep->layers, |
83 | 7 | NULL)) |
84 | 0 | return -1; |
85 | 7 | in = t[0]; |
86 | 7 | } |
87 | 46.5k | else { |
88 | | /* Take a copy, since we must set the offset. |
89 | | */ |
90 | 46.5k | if (vips_rot(convolution->M, &t[0], VIPS_ANGLE_D90, NULL) || |
91 | 46.5k | vips_copy(t[0], &t[3], NULL)) |
92 | 0 | return -1; |
93 | 46.5k | vips_image_set_double(t[3], "offset", 0); |
94 | | |
95 | 46.5k | if (vips_conv(in, &t[1], convolution->M, |
96 | 46.5k | "precision", convsep->precision, |
97 | 46.5k | "layers", convsep->layers, |
98 | 46.5k | "cluster", convsep->cluster, |
99 | 46.5k | NULL) || |
100 | 46.5k | vips_conv(t[1], &t[2], t[3], |
101 | 46.5k | "precision", convsep->precision, |
102 | 46.5k | "layers", convsep->layers, |
103 | 46.5k | "cluster", convsep->cluster, |
104 | 46.5k | NULL)) |
105 | 0 | return -1; |
106 | 46.5k | in = t[2]; |
107 | 46.5k | } |
108 | | |
109 | 46.5k | if (vips_image_write(in, convolution->out)) |
110 | 0 | return -1; |
111 | | |
112 | 46.5k | return 0; |
113 | 46.5k | } |
114 | | |
115 | | static void |
116 | | vips_convsep_class_init(VipsConvsepClass *class) |
117 | 20 | { |
118 | 20 | GObjectClass *gobject_class = G_OBJECT_CLASS(class); |
119 | 20 | VipsObjectClass *object_class = (VipsObjectClass *) class; |
120 | | |
121 | 20 | gobject_class->set_property = vips_object_set_property; |
122 | 20 | gobject_class->get_property = vips_object_get_property; |
123 | | |
124 | 20 | object_class->nickname = "convsep"; |
125 | 20 | object_class->description = _("separable convolution operation"); |
126 | 20 | object_class->build = vips_convsep_build; |
127 | | |
128 | 20 | VIPS_ARG_ENUM(class, "precision", 203, |
129 | 20 | _("Precision"), |
130 | 20 | _("Convolve with this precision"), |
131 | 20 | VIPS_ARGUMENT_OPTIONAL_INPUT, |
132 | 20 | G_STRUCT_OFFSET(VipsConvsep, precision), |
133 | 20 | VIPS_TYPE_PRECISION, VIPS_PRECISION_FLOAT); |
134 | | |
135 | 20 | VIPS_ARG_INT(class, "layers", 204, |
136 | 20 | _("Layers"), |
137 | 20 | _("Use this many layers in approximation"), |
138 | 20 | VIPS_ARGUMENT_OPTIONAL_INPUT, |
139 | 20 | G_STRUCT_OFFSET(VipsConvsep, layers), |
140 | 20 | 1, 1000, 5); |
141 | | |
142 | 20 | VIPS_ARG_INT(class, "cluster", 205, |
143 | 20 | _("Cluster"), |
144 | 20 | _("Cluster lines closer than this in approximation"), |
145 | 20 | VIPS_ARGUMENT_OPTIONAL_INPUT, |
146 | 20 | G_STRUCT_OFFSET(VipsConvsep, cluster), |
147 | 20 | 1, 100, 1); |
148 | 20 | } |
149 | | |
150 | | static void |
151 | | vips_convsep_init(VipsConvsep *convsep) |
152 | 46.5k | { |
153 | 46.5k | convsep->precision = VIPS_PRECISION_FLOAT; |
154 | 46.5k | convsep->layers = 5; |
155 | 46.5k | convsep->cluster = 1; |
156 | 46.5k | } |
157 | | |
158 | | /** |
159 | | * vips_convsep: (method) |
160 | | * @in: input image |
161 | | * @out: (out): output image |
162 | | * @mask: convolution mask |
163 | | * @...: `NULL`-terminated list of optional named arguments |
164 | | * |
165 | | * Perform a separable convolution of @in with @mask. |
166 | | * See [method@Image.conv] for a detailed description. |
167 | | * |
168 | | * The mask must be 1xn or nx1 elements. |
169 | | * |
170 | | * The image is convolved twice: once with @mask and then again with @mask |
171 | | * rotated by 90 degrees. This is much faster for certain types of mask |
172 | | * (gaussian blur, for example) than doing a full 2D convolution. |
173 | | * |
174 | | * ::: tip "Optional arguments" |
175 | | * * @precision: [enum@Precision], calculation accuracy |
176 | | * * @layers: `gint`, number of layers for approximation |
177 | | * * @cluster: `gint`, cluster lines closer than this distance |
178 | | * |
179 | | * ::: seealso |
180 | | * [method@Image.conv], [ctor@Image.gaussmat]. |
181 | | * |
182 | | * Returns: 0 on success, -1 on error |
183 | | */ |
184 | | int |
185 | | vips_convsep(VipsImage *in, VipsImage **out, VipsImage *mask, ...) |
186 | 46.5k | { |
187 | 46.5k | va_list ap; |
188 | 46.5k | int result; |
189 | | |
190 | 46.5k | va_start(ap, mask); |
191 | 46.5k | result = vips_call_split("convsep", ap, in, out, mask); |
192 | 46.5k | va_end(ap); |
193 | | |
194 | 46.5k | return result; |
195 | 46.5k | } |