GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/nrrd/fftNrrd.c Lines: 0 4 0.0 %
Date: 2017-05-26 Branches: 0 0 0.0 %

Line Branch Exec Source
1
/*
2
  Teem: Tools to process and visualize scientific data and images             .
3
  Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
4
  Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
5
  Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
6
7
  This library is free software; you can redistribute it and/or
8
  modify it under the terms of the GNU Lesser General Public License
9
  (LGPL) as published by the Free Software Foundation; either
10
  version 2.1 of the License, or (at your option) any later version.
11
  The terms of redistributing and/or modifying this software also
12
  include exceptions to the LGPL that facilitate static linking.
13
14
  This library is distributed in the hope that it will be useful,
15
  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
  Lesser General Public License for more details.
18
19
  You should have received a copy of the GNU Lesser General Public License
20
  along with this library; if not, write to Free Software Foundation, Inc.,
21
  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22
*/
23
24
#include "nrrd.h"
25
#include "privateNrrd.h"
26
27
#if TEEM_FFTW3 /* =========================================================== */
28
29
#include <fftw3.h>
30
31
const int nrrdFFTWEnabled = AIR_TRUE;
32
33
int
34
nrrdFFTWWisdomRead(FILE *file) {
35
  static const char me[]="nrrdFFTWWisdomRead";
36
37
  if (!(file)) {
38
    biffAddf(NRRD, "%s: given file NULL", me);
39
    return 1;
40
  }
41
  if (!fftw_import_wisdom_from_file(file)) {
42
    biffAddf(NRRD, "%s: trouble importing wisdom", me);
43
    return 1;
44
  }
45
  return 0;
46
}
47
48
static void *
49
_nrrdFftwFreeWrapper(void *ptr) {
50
  fftw_free(ptr);
51
  return NULL;
52
}
53
54
static void *
55
_nrrdFftwDestroyPlanWrapper(void *ptr) {
56
  fftw_plan plan;
57
  plan = AIR_CAST(fftw_plan, ptr);
58
  fftw_destroy_plan(plan);
59
  return NULL;
60
}
61
62
static void
63
_nrrdDimsReverse(fftw_iodim *dims, unsigned int len) {
64
  fftw_iodim buff[NRRD_DIM_MAX];
65
  unsigned int ii;
66
  for (ii=0; ii<len; ii++) {
67
    buff[len-1-ii].n = dims[ii].n;
68
    buff[len-1-ii].is = dims[ii].is;
69
    buff[len-1-ii].os = dims[ii].os;
70
  }
71
  for (ii=0; ii<len; ii++) {
72
    dims[ii].n = buff[ii].n;
73
    dims[ii].is = buff[ii].is;
74
    dims[ii].os = buff[ii].os;
75
  }
76
}
77
78
/*
79
******** nrrdFFT
80
**
81
** First pass at a wrapper around FFTW.  This was implemented out of need for a
82
** specific project; and better decisions and different interfaces will become
83
** apparent with time and experience; these can be in Teem 2.0.
84
**
85
** currently *requires* that input be complex-valued, in that axis 0 has to
86
** have size 2.  nrrdKindComplex would be sensible for input axis 0 but we don't
87
** require it, though it is set on the output.
88
*/
89
int
90
nrrdFFT(Nrrd *nout, const Nrrd *_nin,
91
        unsigned int *axes, unsigned int axesNum,
92
        int sign, int rescale, int rigor) {
93
  static const char me[]="nrrdFFT";
94
  size_t inSize[NRRD_DIM_MAX], II, NN, nprod;
95
  double *inData, *outData;
96
  airArray *mop;
97
  Nrrd *nin;
98
  unsigned int axi, axisDo[NRRD_DIM_MAX];
99
  fftw_plan plan;
100
  void *dataBef;
101
  unsigned int txfRank, howRank, flags;
102
  size_t stride;
103
  fftw_iodim txfDims[NRRD_DIM_MAX], howDims[NRRD_DIM_MAX];
104
105
  if (!(nout && _nin && axes)) {
106
    biffAddf(NRRD, "%s: got NULL pointer", me);
107
    return 1;
108
  }
109
  if (!( _nin->dim > 1 && 2 == _nin->axis[0].size )) {
110
    biffAddf(NRRD, "%s: nin doesn't look like a complex-valued array", me);
111
    return 1;
112
  }
113
  if (!( axesNum >= 1 )) {
114
    biffAddf(NRRD, "%s: axesNum 0, no axes to transform?", me);
115
    return 1;
116
  }
117
  for (axi=0; axi<_nin->dim; axi++) {
118
    axisDo[axi] = 0;
119
  }
120
  for (axi=0; axi<axesNum; axi++) {
121
    if (0 == axes[axi]) {
122
      biffAddf(NRRD, "%s: can't transform axis 0 (axes[%u]) for "
123
               "real/complex values", me, axi);
124
      return 1;
125
    }
126
    if (!( axes[axi] < _nin->dim )) {
127
      biffAddf(NRRD, "%s: axis %u (axes[%u]) out of range [1,%u]", me,
128
               axes[axi], axi, _nin->dim-1);
129
      return 1;
130
    }
131
    axisDo[axes[axi]]++;
132
    if (2 == axisDo[axes[axi]]) {
133
      biffAddf(NRRD, "%s: axis %u (axes[%u]) already transformed",
134
               me, axes[axi], axi);
135
      return 1;
136
    }
137
  }
138
139
  NN = nrrdElementNumber(_nin);
140
  /* We always make a new buffer to hold the double-type copy of input for two
141
     reasons: if input is not double we have to convert it, and we want input
142
     to be const, and we can't have const with the plan creation over-writing
143
     the input (except with FFTW_ESTIMATE).  Given that, we might as well use
144
     the memory-alignment-savvy fftw_malloc; the freeing is handled by both
145
     _nrrdFftwFreeWrapper and nrrdNix. */
146
  /* (NN = 2 * number of complex values) */
147
  inData = AIR_CAST(double *, fftw_malloc(NN*sizeof(double)));
148
  if (!inData) {
149
    biffAddf(NRRD, "%s: couldn't allocate input data copy", me);
150
    return 1;
151
  }
152
  mop = airMopNew();
153
  airMopAdd(mop, inData, _nrrdFftwFreeWrapper, airMopAlways);
154
  nin = nrrdNew();
155
  airMopAdd(mop, nin, (airMopper)nrrdNix /* NOT Nuke */, airMopAlways);
156
  nrrdAxisInfoGet_nva(_nin, nrrdAxisInfoSize, inSize);
157
  /* we don't copy data yet; it may be over-written during plan creation */
158
  if (nrrdWrap_nva(nin, inData, nrrdTypeDouble, _nin->dim, inSize)) {
159
    biffAddf(NRRD, "%s: couldn't wrap or copy input", me);
160
    airMopError(mop);
161
    return 1;
162
  }
163
  /* But on the output, we just use regular malloc, because we don't (yet) have
164
     a way of telling nrrd to use fftw_malloc/fftw_free instead of the generic
165
     malloc/free, and we don't want two whole copies of the output (one that is
166
     memory-aligned, internal to this function, and one that isn't, in nout) */
167
  if (nrrdMaybeAlloc_nva(nout, nrrdTypeDouble, _nin->dim, inSize)) {
168
    biffAddf(NRRD, "%s: couldn't allocate output", me);
169
    airMopError(mop);
170
    return 1;
171
  }
172
  outData = AIR_CAST(double *, nout->data);
173
174
  /* As far as GLK can tell, the guru interface is needed, and the "advanced"
175
     fftw_plan_many_dft won't work, because its simplistic accounting of stride
176
     can't handle having non-contiguous non-transformed axes (e.g. transforming
177
     only axes 2 and not 1, 3 in a 3-D complex-valued array) */
178
  txfRank = howRank = 0;
179
  stride = 1;
180
  nprod = 1;
181
  for (axi=1; axi<nin->dim; axi++) {
182
    if (axisDo[axi]) {
183
      txfDims[txfRank].n = AIR_CAST(int, inSize[axi]);
184
      txfDims[txfRank].is = txfDims[txfRank].os = AIR_CAST(int, stride);
185
      nprod *= inSize[axi];
186
      txfRank++;
187
    } else {
188
      howDims[howRank].n = AIR_CAST(int, inSize[axi]);
189
      howDims[howRank].is = howDims[howRank].os = AIR_CAST(int, stride);
190
      howRank++;
191
    }
192
    stride *= inSize[axi];
193
  }
194
  _nrrdDimsReverse(txfDims, txfRank);
195
  _nrrdDimsReverse(howDims, howRank);
196
197
  /*
198
  fprintf(stderr, "!%s: ------------- txfRank %u, howRank %u\n",
199
          me, txfRank, howRank);
200
  for (axi=0; axi<txfRank; axi++) {
201
    fprintf(stderr, "!%s: txfDims[%u]: n %d; s %d\n", me, axi,
202
            txfDims[axi].n, txfDims[axi].is);
203
  }
204
  for (axi=0; axi<howRank; axi++) {
205
    fprintf(stderr, "!%s: howDims[%u]: n %d; s %d\n", me, axi,
206
            howDims[axi].n, howDims[axi].is);
207
  }
208
  */
209
210
  switch (rigor) {
211
  case nrrdFFTWPlanRigorEstimate:
212
    flags = FFTW_ESTIMATE;
213
    break;
214
  case nrrdFFTWPlanRigorMeasure:
215
    flags = FFTW_MEASURE;
216
    break;
217
  case nrrdFFTWPlanRigorPatient:
218
    flags = FFTW_PATIENT;
219
    break;
220
  case nrrdFFTWPlanRigorExhaustive:
221
    flags = FFTW_EXHAUSTIVE;
222
    break;
223
  default:
224
    biffAddf(NRRD, "%s: unsupported rigor %d", me, rigor);
225
    airMopError(mop); return 1;
226
  }
227
  /* HEY: figure out why fftw expects txfRank and howRank to be
228
     signed and not unsigned */
229
  plan = fftw_plan_guru_dft(AIR_CAST(int, txfRank), txfDims,
230
                            AIR_CAST(int, howRank), howDims,
231
                            AIR_CAST(fftw_complex *, inData),
232
                            AIR_CAST(fftw_complex *, outData),
233
                            sign, flags);
234
  if (!plan) {
235
    biffAddf(NRRD, "%s: unable to create plan", me);
236
    airMopError(mop); return 1;
237
  }
238
  airMopAdd(mop, plan, _nrrdFftwDestroyPlanWrapper, airMopAlways);
239
240
  /* only after planning is done (which can over-write contents of inData)
241
     do we copy the real input values over */
242
  dataBef = nin->data;
243
  if (nrrdConvert(nin, _nin, nrrdTypeDouble)) {
244
    biffAddf(NRRD, "%s: couldn't initialize input", me);
245
    airMopError(mop); return 1;
246
  }
247
  /* a reasonable assert: data buffer was allocated correctly */
248
  if (dataBef != nin->data) {
249
    biffAddf(NRRD, "%s: pre-allocation error; nrrdConvert reallocated data",
250
             me);
251
    airMopError(mop); return 1;
252
  }
253
254
  /* run the transform; HEY, no indication of success? */
255
  fftw_execute(plan);
256
257
  /* if wanted, remove the sqrt(nprod) scaling that fftw adds at each pass */
258
  if (rescale) {
259
    double scale;
260
    scale = sqrt(1.0/AIR_CAST(double, nprod));
261
    for (II=0; II<NN; II++) {
262
      outData[II] *= scale;
263
    }
264
  }
265
266
  if (nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_NONE)
267
      || nrrdBasicInfoCopy(nout, nin,
268
                           NRRD_BASIC_INFO_DATA_BIT
269
                           | NRRD_BASIC_INFO_TYPE_BIT
270
                           | NRRD_BASIC_INFO_BLOCKSIZE_BIT
271
                           | NRRD_BASIC_INFO_DIMENSION_BIT
272
                           | NRRD_BASIC_INFO_CONTENT_BIT
273
                           | NRRD_BASIC_INFO_COMMENTS_BIT
274
                           | (nrrdStateKeyValuePairsPropagate
275
                              ? 0
276
                              : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) {
277
    biffAddf(NRRD, "%s:", me);
278
    airMopError(mop); return 1;
279
  }
280
  nout->axis[0].kind = nrrdKindComplex;
281
282
  airMopOkay(mop);
283
  return 0;
284
}
285
286
int
287
nrrdFFTWWisdomWrite(FILE *file) {
288
  static const char me[]="nrrdFFTWWisdomWrite";
289
290
  if (!(file)) {
291
    biffAddf(NRRD, "%s: given file NULL", me);
292
    return 1;
293
  }
294
  /* HEY: weird that there's no return value on this function? */
295
  fftw_export_wisdom_to_file(file);
296
  return 0;
297
}
298
299
300
#else /* TEEM_FFTW3 ========================================================= */
301
302
/* we do NOT have the FFTW library to link against; have to
303
   supply the same symbols anyway */
304
305
const int nrrdFFTWEnabled = AIR_FALSE;
306
307
int
308
nrrdFFTWWisdomRead(FILE *file) {
309
  AIR_UNUSED(file);
310
  return 0;
311
}
312
313
int
314
nrrdFFT(Nrrd *nout, const Nrrd *nin,
315
        unsigned int *axes, unsigned int axesNum,
316
        int sign, int rescale, int rigor) {
317
  static const char me[]="nrrdFFT";
318
319
  AIR_UNUSED(nout);
320
  AIR_UNUSED(nin);
321
  AIR_UNUSED(axes);
322
  AIR_UNUSED(axesNum);
323
  AIR_UNUSED(sign);
324
  AIR_UNUSED(rescale);
325
  AIR_UNUSED(rigor);
326
  biffAddf(NRRD, "%s: sorry, non-fftw3 version not yet implemented\n", me);
327
  return 1;
328
}
329
330
int
331
nrrdFFTWWisdomWrite(FILE *file) {
332
  AIR_UNUSED(file);
333
  return 0;
334
}
335
336
#endif /* TEEM_FFTW3 ======================================================== */
337