/src/libvips/libvips/freqfilt/invfft.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Inverse FFT |
2 | | * |
3 | | * Author: Nicos Dessipris |
4 | | * Written on: 12/04/1990 |
5 | | * Modified on : |
6 | | * 28/6/95 JC |
7 | | * - rewritten, based on new im_invfft() code |
8 | | * 10/9/98 JC |
9 | | * - frees memory more quickly |
10 | | * 2/4/02 JC |
11 | | * - fftw code added |
12 | | * 13/7/02 JC |
13 | | * - Type reset |
14 | | * 27/2/03 JC |
15 | | * - tiny speed-up ... save 1 copy on write |
16 | | * 22/1/04 JC |
17 | | * - oops, fix for segv on wider than high fftw transforms |
18 | | * 3/11/04 |
19 | | * - added fftw3 support |
20 | | * 7/2/10 |
21 | | * - gtkdoc |
22 | | * 27/1/12 |
23 | | * - better setting of interpretation |
24 | | * - remove own fft fallback code |
25 | | * - remove fftw2 path |
26 | | * - reduce memuse |
27 | | * 3/1/14 |
28 | | * - redone as a class |
29 | | * 15/12/23 [akash-akya] |
30 | | * - add locks |
31 | | */ |
32 | | |
33 | | /* |
34 | | |
35 | | This file is part of VIPS. |
36 | | |
37 | | VIPS is free software; you can redistribute it and/or modify |
38 | | it under the terms of the GNU Lesser General Public License as published by |
39 | | the Free Software Foundation; either version 2 of the License, or |
40 | | (at your option) any later version. |
41 | | |
42 | | This program is distributed in the hope that it will be useful, |
43 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
44 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
45 | | GNU Lesser General Public License for more details. |
46 | | |
47 | | You should have received a copy of the GNU Lesser General Public License |
48 | | along with this program; if not, write to the Free Software |
49 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
50 | | 02110-1301 USA |
51 | | |
52 | | */ |
53 | | |
54 | | /* |
55 | | |
56 | | These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk |
57 | | |
58 | | */ |
59 | | |
60 | | #ifdef HAVE_CONFIG_H |
61 | | #include <config.h> |
62 | | #endif /*HAVE_CONFIG_H*/ |
63 | | #include <glib/gi18n-lib.h> |
64 | | |
65 | | #include <stdio.h> |
66 | | #include <stdlib.h> |
67 | | |
68 | | #include <vips/vips.h> |
69 | | #include <vips/internal.h> |
70 | | #include "pfreqfilt.h" |
71 | | |
72 | | #ifdef HAVE_FFTW |
73 | | |
74 | | #include <fftw3.h> |
75 | | |
76 | | typedef struct _VipsInvfft { |
77 | | VipsFreqfilt parent_instance; |
78 | | |
79 | | gboolean real; |
80 | | |
81 | | } VipsInvfft; |
82 | | |
83 | | typedef VipsFreqfiltClass VipsInvfftClass; |
84 | | |
85 | | G_DEFINE_TYPE(VipsInvfft, vips_invfft, VIPS_TYPE_FREQFILT); |
86 | | |
87 | | /* Complex to complex inverse transform. |
88 | | */ |
89 | | static int |
90 | | cinvfft1(VipsObject *object, VipsImage *in, VipsImage **out) |
91 | 0 | { |
92 | 0 | VipsImage **t = (VipsImage **) vips_object_local_array(object, 4); |
93 | 0 | VipsInvfft *invfft = (VipsInvfft *) object; |
94 | 0 | VipsObjectClass *class = VIPS_OBJECT_GET_CLASS(invfft); |
95 | |
|
96 | 0 | fftw_plan plan; |
97 | 0 | double *planner_scratch; |
98 | |
|
99 | 0 | if (vips_check_mono(class->nickname, in) || |
100 | 0 | vips_check_uncoded(class->nickname, in)) |
101 | 0 | return -1; |
102 | | |
103 | | /* Convert input to a complex double membuffer. |
104 | | */ |
105 | 0 | *out = vips_image_new_memory(); |
106 | 0 | if (vips_cast_dpcomplex(in, &t[0], NULL) || |
107 | 0 | vips_image_write(t[0], *out)) |
108 | 0 | return -1; |
109 | | |
110 | | /* Make the plan for the transform. Yes, they really do use nx for |
111 | | * height and ny for width. |
112 | | */ |
113 | 0 | if (!(planner_scratch = VIPS_ARRAY(invfft, |
114 | 0 | VIPS_IMAGE_N_PELS(in) * 2, double))) |
115 | 0 | return -1; |
116 | 0 | g_mutex_lock(&vips__fft_lock); |
117 | 0 | if (!(plan = fftw_plan_dft_2d(in->Ysize, in->Xsize, |
118 | 0 | (fftw_complex *) planner_scratch, |
119 | 0 | (fftw_complex *) planner_scratch, |
120 | 0 | FFTW_BACKWARD, |
121 | 0 | 0))) { |
122 | 0 | g_mutex_unlock(&vips__fft_lock); |
123 | 0 | vips_error(class->nickname, |
124 | 0 | "%s", _("unable to create transform plan")); |
125 | 0 | return -1; |
126 | 0 | } |
127 | 0 | g_mutex_unlock(&vips__fft_lock); |
128 | |
|
129 | 0 | fftw_execute_dft(plan, |
130 | 0 | (fftw_complex *) (*out)->data, (fftw_complex *) (*out)->data); |
131 | |
|
132 | 0 | g_mutex_lock(&vips__fft_lock); |
133 | 0 | fftw_destroy_plan(plan); |
134 | 0 | g_mutex_unlock(&vips__fft_lock); |
135 | |
|
136 | 0 | (*out)->Type = VIPS_INTERPRETATION_B_W; |
137 | |
|
138 | 0 | return 0; |
139 | 0 | } |
140 | | |
141 | | /* Complex to real inverse transform. |
142 | | */ |
143 | | static int |
144 | | rinvfft1(VipsObject *object, VipsImage *in, VipsImage **out) |
145 | 0 | { |
146 | 0 | VipsImage **t = (VipsImage **) vips_object_local_array(object, 4); |
147 | 0 | VipsInvfft *invfft = (VipsInvfft *) object; |
148 | 0 | VipsObjectClass *class = VIPS_OBJECT_GET_CLASS(invfft); |
149 | 0 | const int half_width = in->Xsize / 2 + 1; |
150 | |
|
151 | 0 | double *half_complex; |
152 | 0 | double *planner_scratch; |
153 | 0 | fftw_plan plan; |
154 | 0 | int x, y; |
155 | 0 | double *q, *p; |
156 | | |
157 | | /* Convert input to a complex double membuffer. |
158 | | */ |
159 | 0 | t[1] = vips_image_new_memory(); |
160 | 0 | if (vips_cast_dpcomplex(in, &t[0], NULL) || |
161 | 0 | vips_image_write(t[0], t[1])) |
162 | 0 | return -1; |
163 | | |
164 | | /* Build half-complex image. |
165 | | */ |
166 | 0 | if (!(half_complex = VIPS_ARRAY(invfft, |
167 | 0 | t[1]->Ysize * half_width * 2, double))) |
168 | 0 | return -1; |
169 | 0 | q = half_complex; |
170 | 0 | for (y = 0; y < t[1]->Ysize; y++) { |
171 | 0 | p = ((double *) t[1]->data) + (guint64) y * t[1]->Xsize * 2; |
172 | |
|
173 | 0 | for (x = 0; x < half_width; x++) { |
174 | 0 | q[0] = p[0]; |
175 | 0 | q[1] = p[1]; |
176 | 0 | p += 2; |
177 | 0 | q += 2; |
178 | 0 | } |
179 | 0 | } |
180 | | |
181 | | /* Make mem buffer real image for output. |
182 | | */ |
183 | 0 | *out = vips_image_new_memory(); |
184 | 0 | if (vips_image_pipelinev(*out, VIPS_DEMAND_STYLE_ANY, t[1], NULL)) |
185 | 0 | return -1; |
186 | 0 | (*out)->BandFmt = VIPS_FORMAT_DOUBLE; |
187 | 0 | (*out)->Type = VIPS_INTERPRETATION_B_W; |
188 | 0 | if (vips_image_write_prepare(*out)) |
189 | 0 | return -1; |
190 | | |
191 | | /* Make the plan for the transform. Yes, they really do use nx for |
192 | | * height and ny for width. |
193 | | */ |
194 | 0 | if (!(planner_scratch = VIPS_ARRAY(invfft, |
195 | 0 | t[1]->Ysize * half_width * 2, double))) |
196 | 0 | return -1; |
197 | 0 | g_mutex_lock(&vips__fft_lock); |
198 | 0 | if (!(plan = fftw_plan_dft_c2r_2d(t[1]->Ysize, t[1]->Xsize, |
199 | 0 | (fftw_complex *) planner_scratch, (double *) (*out)->data, |
200 | 0 | 0))) { |
201 | 0 | g_mutex_unlock(&vips__fft_lock); |
202 | 0 | vips_error(class->nickname, |
203 | 0 | "%s", _("unable to create transform plan")); |
204 | 0 | return -1; |
205 | 0 | } |
206 | 0 | g_mutex_unlock(&vips__fft_lock); |
207 | |
|
208 | 0 | fftw_execute_dft_c2r(plan, |
209 | 0 | (fftw_complex *) half_complex, (double *) (*out)->data); |
210 | |
|
211 | 0 | g_mutex_lock(&vips__fft_lock); |
212 | 0 | fftw_destroy_plan(plan); |
213 | 0 | g_mutex_unlock(&vips__fft_lock); |
214 | |
|
215 | 0 | return 0; |
216 | 0 | } |
217 | | |
218 | | static int |
219 | | vips_invfft_build(VipsObject *object) |
220 | 0 | { |
221 | 0 | VipsFreqfilt *freqfilt = VIPS_FREQFILT(object); |
222 | 0 | VipsInvfft *invfft = (VipsInvfft *) object; |
223 | 0 | VipsImage **t = (VipsImage **) vips_object_local_array(object, 4); |
224 | |
|
225 | 0 | VipsImage *in; |
226 | |
|
227 | 0 | if (VIPS_OBJECT_CLASS(vips_invfft_parent_class)->build(object)) |
228 | 0 | return -1; |
229 | | |
230 | 0 | in = freqfilt->in; |
231 | |
|
232 | 0 | if (vips_image_decode(in, &t[0])) |
233 | 0 | return -1; |
234 | 0 | in = t[0]; |
235 | |
|
236 | 0 | if (invfft->real) { |
237 | 0 | if (vips__fftproc(VIPS_OBJECT(invfft), |
238 | 0 | in, &t[1], rinvfft1)) |
239 | 0 | return -1; |
240 | 0 | } |
241 | 0 | else { |
242 | 0 | if (vips__fftproc(VIPS_OBJECT(invfft), |
243 | 0 | in, &t[1], cinvfft1)) |
244 | 0 | return -1; |
245 | 0 | } |
246 | | |
247 | 0 | if (vips_image_write(t[1], freqfilt->out)) |
248 | 0 | return -1; |
249 | | |
250 | 0 | return 0; |
251 | 0 | } |
252 | | |
253 | | static void |
254 | | vips_invfft_class_init(VipsInvfftClass *class) |
255 | 0 | { |
256 | 0 | GObjectClass *gobject_class = G_OBJECT_CLASS(class); |
257 | 0 | VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS(class); |
258 | |
|
259 | 0 | gobject_class->set_property = vips_object_set_property; |
260 | 0 | gobject_class->get_property = vips_object_get_property; |
261 | |
|
262 | 0 | vobject_class->nickname = "invfft"; |
263 | 0 | vobject_class->description = _("inverse FFT"); |
264 | 0 | vobject_class->build = vips_invfft_build; |
265 | |
|
266 | 0 | VIPS_ARG_BOOL(class, "real", 4, |
267 | 0 | _("Real"), |
268 | 0 | _("Output only the real part of the transform"), |
269 | 0 | VIPS_ARGUMENT_OPTIONAL_INPUT, |
270 | 0 | G_STRUCT_OFFSET(VipsInvfft, real), |
271 | 0 | FALSE); |
272 | 0 | } |
273 | | |
274 | | static void |
275 | | vips_invfft_init(VipsInvfft *invfft) |
276 | 0 | { |
277 | 0 | } |
278 | | |
279 | | #endif /*HAVE_FFTW*/ |
280 | | |
281 | | /** |
282 | | * vips_invfft: (method) |
283 | | * @in: input image |
284 | | * @out: (out): output image |
285 | | * @...: %NULL-terminated list of optional named arguments |
286 | | * |
287 | | * Optional arguments: |
288 | | * |
289 | | * * @real: only output the real part |
290 | | * |
291 | | * Transform an image from Fourier space to real space. The result is complex. |
292 | | * If you are OK with a real result, set @real, it's quicker. |
293 | | * |
294 | | * VIPS uses the fftw Fourier Transform library. If this library was not |
295 | | * available when VIPS was configured, these functions will fail. |
296 | | * |
297 | | * See also: vips_fwfft(). |
298 | | * |
299 | | * Returns: 0 on success, -1 on error. |
300 | | */ |
301 | | int |
302 | | vips_invfft(VipsImage *in, VipsImage **out, ...) |
303 | 0 | { |
304 | 0 | va_list ap; |
305 | 0 | int result; |
306 | |
|
307 | 0 | va_start(ap, out); |
308 | 0 | result = vips_call_split("invfft", ap, in, out); |
309 | 0 | va_end(ap); |
310 | |
|
311 | 0 | return result; |
312 | 0 | } |