Coverage Report

Created: 2025-01-28 06:32

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