Coverage Report

Created: 2024-10-29 06:46

/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
}