/src/libvips/libvips/freqfilt/fwfft.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* forward FFT |
2 | | * |
3 | | * Author: Nicos Dessipris |
4 | | * Written on: 12/04/1990 |
5 | | * Modified on : 09/05/1990 to cope with float input |
6 | | * Modified on : 08/03/1991 history removed |
7 | | * Modified on : 03/04/1991 to cope with any input |
8 | | * |
9 | | * 28/6/95 JC |
10 | | * - rewritten to use im_clip2f() rather than own code |
11 | | * - memory leaks fixed |
12 | | * 10/9/98 JC |
13 | | * - frees memory more quickly |
14 | | * 2/4/02 JC |
15 | | * - fftw code added |
16 | | * 13/7/02 JC |
17 | | * - output Type set to IM_TYPE_FOURIER to help nip |
18 | | * 27/2/03 JC |
19 | | * - exploits real_to_complex() path in libfftw for real input (thanks |
20 | | * Matt) for a 2x speed-up |
21 | | * 17/11/03 JC |
22 | | * - fix a segv for wider than high images in the real_to_complex() path |
23 | | * (thanks Andrey) |
24 | | * - fixes to real_to_complex() path to give the correct result for |
25 | | * non-square images, including odd widths and heights |
26 | | * 3/11/04 |
27 | | * - added fftw3 support |
28 | | * 7/2/10 |
29 | | * - cleanups |
30 | | * - gtkdoc |
31 | | * 25/3/10 |
32 | | * - have a "t" image linked to out to keep the image alive for longer |
33 | | * 27/1/12 |
34 | | * - better setting of interpretation |
35 | | * - remove own fft fallback code |
36 | | * - remove fftw2 path |
37 | | * - reduce memuse |
38 | | * 3/1/14 |
39 | | * - redone as a class |
40 | | * 15/12/23 [akash-akya] |
41 | | * - add locks |
42 | | */ |
43 | | |
44 | | /* |
45 | | |
46 | | This file is part of VIPS. |
47 | | |
48 | | VIPS is free software; you can redistribute it and/or modify |
49 | | it under the terms of the GNU Lesser General Public License as published by |
50 | | the Free Software Foundation; either version 2 of the License, or |
51 | | (at your option) any later version. |
52 | | |
53 | | This program is distributed in the hope that it will be useful, |
54 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
55 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
56 | | GNU Lesser General Public License for more details. |
57 | | |
58 | | You should have received a copy of the GNU Lesser General Public License |
59 | | along with this program; if not, write to the Free Software |
60 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
61 | | 02110-1301 USA |
62 | | |
63 | | */ |
64 | | |
65 | | /* |
66 | | |
67 | | These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk |
68 | | |
69 | | */ |
70 | | |
71 | | #ifdef HAVE_CONFIG_H |
72 | | #include <config.h> |
73 | | #endif /*HAVE_CONFIG_H*/ |
74 | | #include <glib/gi18n-lib.h> |
75 | | |
76 | | #include <stdio.h> |
77 | | #include <stdlib.h> |
78 | | |
79 | | #include <vips/vips.h> |
80 | | #include <vips/internal.h> |
81 | | #include "pfreqfilt.h" |
82 | | |
83 | | #ifdef HAVE_FFTW |
84 | | |
85 | | #include <fftw3.h> |
86 | | |
87 | | typedef struct _VipsFwfft { |
88 | | VipsFreqfilt parent_instance; |
89 | | |
90 | | } VipsFwfft; |
91 | | |
92 | | typedef VipsFreqfiltClass VipsFwfftClass; |
93 | | |
94 | | G_DEFINE_TYPE(VipsFwfft, vips_fwfft, VIPS_TYPE_FREQFILT); |
95 | | |
96 | | /* Everything in fftw3 except execute has to be behind a mutex. |
97 | | */ |
98 | | GMutex *vips__fft_lock = NULL; |
99 | | |
100 | | static void * |
101 | | vips__fft_thread_init(void *data) |
102 | 0 | { |
103 | 0 | vips__fft_lock = vips_g_mutex_new(); |
104 | |
|
105 | 0 | return NULL; |
106 | 0 | } |
107 | | |
108 | | void |
109 | | vips__fft_init(void) |
110 | 0 | { |
111 | 0 | static GOnce once = G_ONCE_INIT; |
112 | |
|
113 | 0 | VIPS_ONCE(&once, vips__fft_thread_init, NULL); |
114 | 0 | } |
115 | | |
116 | | /* Real to complex forward transform. |
117 | | */ |
118 | | static int |
119 | | rfwfft1(VipsObject *object, VipsImage *in, VipsImage **out) |
120 | 0 | { |
121 | 0 | VipsFwfft *fwfft = (VipsFwfft *) object; |
122 | 0 | VipsImage **t = (VipsImage **) vips_object_local_array(object, 4); |
123 | 0 | VipsObjectClass *class = VIPS_OBJECT_GET_CLASS(fwfft); |
124 | 0 | const guint64 size = VIPS_IMAGE_N_PELS(in); |
125 | 0 | const int half_width = in->Xsize / 2 + 1; |
126 | |
|
127 | 0 | double *half_complex; |
128 | 0 | double *planner_scratch; |
129 | |
|
130 | 0 | fftw_plan plan; |
131 | 0 | double *buf, *q, *p; |
132 | 0 | int x, y; |
133 | |
|
134 | 0 | if (vips_check_mono(class->nickname, in) || |
135 | 0 | vips_check_uncoded(class->nickname, in)) |
136 | 0 | return -1; |
137 | | |
138 | | /* Convert input to a real double membuffer. |
139 | | */ |
140 | 0 | t[1] = vips_image_new_memory(); |
141 | 0 | if (vips_cast_double(in, &t[0], NULL) || |
142 | 0 | vips_image_write(t[0], t[1])) |
143 | 0 | return -1; |
144 | | |
145 | | /* Make the plan for the transform. Yes, they really do use nx for |
146 | | * height and ny for width. Use a separate scratch buffer for the |
147 | | * planner, we can't overwrite real->data |
148 | | */ |
149 | 0 | if (!(planner_scratch = VIPS_ARRAY(fwfft, |
150 | 0 | VIPS_IMAGE_N_PELS(in), double))) |
151 | 0 | return -1; |
152 | 0 | if (!(half_complex = VIPS_ARRAY(fwfft, |
153 | 0 | in->Ysize * half_width * 2, double))) |
154 | 0 | return -1; |
155 | 0 | g_mutex_lock(vips__fft_lock); |
156 | 0 | if (!(plan = fftw_plan_dft_r2c_2d(in->Ysize, in->Xsize, |
157 | 0 | planner_scratch, (fftw_complex *) half_complex, |
158 | 0 | 0))) { |
159 | 0 | g_mutex_unlock(vips__fft_lock); |
160 | 0 | vips_error(class->nickname, |
161 | 0 | "%s", _("unable to create transform plan")); |
162 | 0 | return -1; |
163 | 0 | } |
164 | 0 | g_mutex_unlock(vips__fft_lock); |
165 | |
|
166 | 0 | fftw_execute_dft_r2c(plan, |
167 | 0 | (double *) t[1]->data, (fftw_complex *) half_complex); |
168 | |
|
169 | 0 | g_mutex_lock(vips__fft_lock); |
170 | 0 | fftw_destroy_plan(plan); |
171 | 0 | g_mutex_unlock(vips__fft_lock); |
172 | | |
173 | | /* Write to out as another memory buffer. |
174 | | */ |
175 | 0 | *out = vips_image_new_memory(); |
176 | 0 | if (vips_image_pipelinev(*out, VIPS_DEMAND_STYLE_ANY, in, NULL)) |
177 | 0 | return -1; |
178 | 0 | (*out)->BandFmt = VIPS_FORMAT_DPCOMPLEX; |
179 | 0 | (*out)->Type = VIPS_INTERPRETATION_FOURIER; |
180 | 0 | if (!(buf = VIPS_ARRAY(fwfft, VIPS_IMAGE_N_PELS(*out), double))) |
181 | 0 | return -1; |
182 | | |
183 | | /* Copy and normalise. The right half is the up/down and |
184 | | * left/right flip of the left, but conjugated. Do the first |
185 | | * row separately, then mirror around the centre row. |
186 | | */ |
187 | 0 | p = half_complex; |
188 | 0 | q = buf; |
189 | |
|
190 | 0 | for (x = 0; x < half_width; x++) { |
191 | 0 | q[0] = p[0] / size; |
192 | 0 | q[1] = p[1] / size; |
193 | 0 | p += 2; |
194 | 0 | q += 2; |
195 | 0 | } |
196 | |
|
197 | 0 | p = half_complex + ((in->Xsize + 1) / 2 - 1) * 2; |
198 | |
|
199 | 0 | for (x = half_width; x < (*out)->Xsize; x++) { |
200 | 0 | q[0] = p[0] / size; |
201 | 0 | q[1] = -1.0 * p[1] / size; |
202 | 0 | p -= 2; |
203 | 0 | q += 2; |
204 | 0 | } |
205 | |
|
206 | 0 | if (vips_image_write_line(*out, 0, (VipsPel *) buf)) |
207 | 0 | return -1; |
208 | | |
209 | 0 | for (y = 1; y < (*out)->Ysize; y++) { |
210 | 0 | p = half_complex + y * half_width * 2; |
211 | 0 | q = buf; |
212 | |
|
213 | 0 | for (x = 0; x < half_width; x++) { |
214 | 0 | q[0] = p[0] / size; |
215 | 0 | q[1] = p[1] / size; |
216 | 0 | p += 2; |
217 | 0 | q += 2; |
218 | 0 | } |
219 | | |
220 | | /* Good grief. |
221 | | */ |
222 | 0 | p = half_complex + 2 * /* clang-format off */ |
223 | 0 | (((*out)->Ysize - y + 1) * half_width - 2 + |
224 | 0 | (in->Xsize & 1)); |
225 | | /* clang-format on */ |
226 | |
|
227 | 0 | for (x = half_width; x < (*out)->Xsize; x++) { |
228 | 0 | q[0] = p[0] / size; |
229 | 0 | q[1] = -1.0 * p[1] / size; |
230 | 0 | p -= 2; |
231 | 0 | q += 2; |
232 | 0 | } |
233 | |
|
234 | 0 | if (vips_image_write_line(*out, y, (VipsPel *) buf)) |
235 | 0 | return -1; |
236 | 0 | } |
237 | | |
238 | 0 | return 0; |
239 | 0 | } |
240 | | |
241 | | /* Complex to complex forward transform. |
242 | | */ |
243 | | static int |
244 | | cfwfft1(VipsObject *object, VipsImage *in, VipsImage **out) |
245 | 0 | { |
246 | 0 | VipsFwfft *fwfft = (VipsFwfft *) object; |
247 | 0 | VipsImage **t = (VipsImage **) vips_object_local_array(object, 4); |
248 | 0 | VipsObjectClass *class = VIPS_OBJECT_GET_CLASS(fwfft); |
249 | |
|
250 | 0 | fftw_plan plan; |
251 | 0 | double *planner_scratch; |
252 | 0 | double *buf, *q, *p; |
253 | 0 | int x, y; |
254 | |
|
255 | 0 | if (vips_check_mono(class->nickname, in) || |
256 | 0 | vips_check_uncoded(class->nickname, in)) |
257 | 0 | return -1; |
258 | | |
259 | | /* Convert input to a complex double membuffer. |
260 | | */ |
261 | 0 | t[1] = vips_image_new_memory(); |
262 | 0 | if (vips_cast_dpcomplex(in, &t[0], NULL) || |
263 | 0 | vips_image_write(t[0], t[1])) |
264 | 0 | return -1; |
265 | | |
266 | | /* We have to have a separate buffer for the planner to work on. |
267 | | */ |
268 | 0 | if (!(planner_scratch = VIPS_ARRAY(fwfft, |
269 | 0 | VIPS_IMAGE_N_PELS(in) * 2, double))) |
270 | 0 | return -1; |
271 | | |
272 | | /* Make the plan for the transform. |
273 | | */ |
274 | 0 | g_mutex_lock(vips__fft_lock); |
275 | 0 | if (!(plan = fftw_plan_dft_2d(in->Ysize, in->Xsize, |
276 | 0 | (fftw_complex *) planner_scratch, |
277 | 0 | (fftw_complex *) planner_scratch, |
278 | 0 | FFTW_FORWARD, |
279 | 0 | 0))) { |
280 | 0 | g_mutex_unlock(vips__fft_lock); |
281 | 0 | vips_error(class->nickname, |
282 | 0 | "%s", _("unable to create transform plan")); |
283 | 0 | return -1; |
284 | 0 | } |
285 | 0 | g_mutex_unlock(vips__fft_lock); |
286 | |
|
287 | 0 | fftw_execute_dft(plan, |
288 | 0 | (fftw_complex *) t[1]->data, (fftw_complex *) t[1]->data); |
289 | |
|
290 | 0 | g_mutex_lock(vips__fft_lock); |
291 | 0 | fftw_destroy_plan(plan); |
292 | 0 | g_mutex_unlock(vips__fft_lock); |
293 | | |
294 | | /* Write to out as another memory buffer. |
295 | | */ |
296 | 0 | *out = vips_image_new_memory(); |
297 | 0 | if (vips_image_pipelinev(*out, VIPS_DEMAND_STYLE_ANY, in, NULL)) |
298 | 0 | return -1; |
299 | 0 | (*out)->BandFmt = VIPS_FORMAT_DPCOMPLEX; |
300 | 0 | (*out)->Type = VIPS_INTERPRETATION_FOURIER; |
301 | 0 | if (!(buf = VIPS_ARRAY(fwfft, VIPS_IMAGE_N_PELS(*out), double))) |
302 | 0 | return -1; |
303 | | |
304 | | /* Copy to out, normalise. |
305 | | */ |
306 | 0 | p = (double *) t[1]->data; |
307 | 0 | for (y = 0; y < (*out)->Ysize; y++) { |
308 | 0 | guint64 size = VIPS_IMAGE_N_PELS(*out); |
309 | |
|
310 | 0 | q = buf; |
311 | |
|
312 | 0 | for (x = 0; x < (*out)->Xsize; x++) { |
313 | 0 | q[0] = p[0] / size; |
314 | 0 | q[1] = p[1] / size; |
315 | 0 | p += 2; |
316 | 0 | q += 2; |
317 | 0 | } |
318 | |
|
319 | 0 | if (vips_image_write_line(*out, y, (VipsPel *) buf)) |
320 | 0 | return -1; |
321 | 0 | } |
322 | | |
323 | 0 | return 0; |
324 | 0 | } |
325 | | |
326 | | static int |
327 | | vips_fwfft_build(VipsObject *object) |
328 | 0 | { |
329 | 0 | VipsFreqfilt *freqfilt = VIPS_FREQFILT(object); |
330 | 0 | VipsFwfft *fwfft = (VipsFwfft *) object; |
331 | 0 | VipsImage **t = (VipsImage **) vips_object_local_array(object, 4); |
332 | |
|
333 | 0 | VipsImage *in; |
334 | |
|
335 | 0 | vips__fft_init(); |
336 | |
|
337 | 0 | if (VIPS_OBJECT_CLASS(vips_fwfft_parent_class)->build(object)) |
338 | 0 | return -1; |
339 | | |
340 | 0 | in = freqfilt->in; |
341 | |
|
342 | 0 | if (vips_image_decode(in, &t[0])) |
343 | 0 | return -1; |
344 | 0 | in = t[0]; |
345 | |
|
346 | 0 | if (vips_band_format_iscomplex(in->BandFmt)) { |
347 | 0 | if (vips__fftproc(VIPS_OBJECT(fwfft), in, &t[1], |
348 | 0 | cfwfft1)) |
349 | 0 | return -1; |
350 | 0 | } |
351 | 0 | else { |
352 | 0 | if (vips__fftproc(VIPS_OBJECT(fwfft), in, &t[1], |
353 | 0 | rfwfft1)) |
354 | 0 | return -1; |
355 | 0 | } |
356 | | |
357 | 0 | if (vips_image_write(t[1], freqfilt->out)) |
358 | 0 | return -1; |
359 | | |
360 | 0 | return 0; |
361 | 0 | } |
362 | | |
363 | | static void |
364 | | vips_fwfft_class_init(VipsFwfftClass *class) |
365 | 17 | { |
366 | 17 | VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS(class); |
367 | | |
368 | 17 | vobject_class->nickname = "fwfft"; |
369 | 17 | vobject_class->description = _("forward FFT"); |
370 | 17 | vobject_class->build = vips_fwfft_build; |
371 | 17 | } |
372 | | |
373 | | static void |
374 | | vips_fwfft_init(VipsFwfft *fwfft) |
375 | 0 | { |
376 | 0 | } |
377 | | |
378 | | #endif /*HAVE_FFTW*/ |
379 | | |
380 | | /** |
381 | | * vips_fwfft: (method) |
382 | | * @in: input image |
383 | | * @out: (out): output image |
384 | | * @...: %NULL-terminated list of optional named arguments |
385 | | * |
386 | | * Transform an image to Fourier space. |
387 | | * |
388 | | * VIPS uses the fftw Fourier Transform library. If this library was not |
389 | | * available when VIPS was configured, these functions will fail. |
390 | | * |
391 | | * See also: vips_invfft(). |
392 | | * |
393 | | * Returns: 0 on success, -1 on error. |
394 | | */ |
395 | | int |
396 | | vips_fwfft(VipsImage *in, VipsImage **out, ...) |
397 | 0 | { |
398 | 0 | va_list ap; |
399 | 0 | int result; |
400 | |
|
401 | 0 | va_start(ap, out); |
402 | 0 | result = vips_call_split("fwfft", ap, in, out); |
403 | 0 | va_end(ap); |
404 | |
|
405 | 0 | return result; |
406 | 0 | } |