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