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 "unrrdu.h" |
25 |
|
|
#include "privateUnrrdu.h" |
26 |
|
|
|
27 |
|
|
#define INFO "Fast Fourier Transform of selected axes" |
28 |
|
|
static const char *_unrrdu_fftInfoL_yes = |
29 |
|
|
(INFO |
30 |
|
|
". Initial attempt at wrapping the FFTW3 library; options are " |
31 |
|
|
"likely to change in Teem 2.0.\n " |
32 |
|
|
"* Uses nrrdFFT"); |
33 |
|
|
|
34 |
|
|
static const char *_unrrdu_fftInfoL_no = |
35 |
|
|
(INFO |
36 |
|
|
". This Teem has NOT been compiled with FFTW3 <http://www.fftw.org/>. " |
37 |
|
|
"If it had been, " |
38 |
|
|
"this would be a command-line interface to that functionality. " |
39 |
|
|
"There is currently no non-FFTW implementation of the FFT available.\n " |
40 |
|
|
"* Uses nrrdFFT"); |
41 |
|
|
|
42 |
|
|
/* We create an airEnum to parse the "forward" and "backwards" values |
43 |
|
|
needed to specify which kind of transform to run */ |
44 |
|
|
|
45 |
|
|
static const char * |
46 |
|
|
_directionStr[] = { |
47 |
|
|
"(unknown direction)", |
48 |
|
|
"forward", |
49 |
|
|
"backward" |
50 |
|
|
}; |
51 |
|
|
|
52 |
|
|
static const char * |
53 |
|
|
_directionDesc[] = { |
54 |
|
|
"unknown direction", |
55 |
|
|
"forward transform", |
56 |
|
|
"backward (inverse) transform" |
57 |
|
|
}; |
58 |
|
|
|
59 |
|
|
/* from fftw3.h |
60 |
|
|
#define FFTW_FORWARD (-1) |
61 |
|
|
#define FFTW_BACKWARD (+1) |
62 |
|
|
*/ |
63 |
|
|
|
64 |
|
|
#define FORW (-1) |
65 |
|
|
#define BACK (+1) |
66 |
|
|
|
67 |
|
|
static const int |
68 |
|
|
_directionVal[] = { |
69 |
|
|
0, |
70 |
|
|
FORW, |
71 |
|
|
BACK |
72 |
|
|
}; |
73 |
|
|
|
74 |
|
|
static const char * |
75 |
|
|
_directionStrEqv[] = { |
76 |
|
|
"f", "forw", "forward", |
77 |
|
|
"b", "back", "backward", "i", "inv", "inverse", |
78 |
|
|
"" |
79 |
|
|
}; |
80 |
|
|
|
81 |
|
|
static const int |
82 |
|
|
_directionValEqv[] = { |
83 |
|
|
FORW, FORW, FORW, |
84 |
|
|
BACK, BACK, BACK, BACK, BACK, BACK |
85 |
|
|
}; |
86 |
|
|
|
87 |
|
|
static const airEnum |
88 |
|
|
_direction_enm = { |
89 |
|
|
"direction", |
90 |
|
|
2, |
91 |
|
|
_directionStr, |
92 |
|
|
_directionVal, |
93 |
|
|
_directionDesc, |
94 |
|
|
_directionStrEqv, |
95 |
|
|
_directionValEqv, |
96 |
|
|
AIR_FALSE |
97 |
|
|
}; |
98 |
|
|
|
99 |
|
|
static const airEnum *const |
100 |
|
|
direction_enm = &_direction_enm; |
101 |
|
|
|
102 |
|
|
int |
103 |
|
|
unrrdu_fftMain(int argc, const char **argv, const char *me, |
104 |
|
|
hestParm *hparm) { |
105 |
|
2 |
hestOpt *opt = NULL; |
106 |
|
1 |
char *out, *err; |
107 |
|
1 |
Nrrd *nin, *_nin, *nout; |
108 |
|
|
int pret; |
109 |
|
|
airArray *mop; |
110 |
|
|
|
111 |
|
1 |
int sign, rigor, rescale, realInput; |
112 |
|
1 |
char *wispath; |
113 |
|
|
FILE *fwise; |
114 |
|
1 |
unsigned int *axes, axesLen; |
115 |
|
|
|
116 |
|
1 |
hestOptAdd(&opt, NULL, "dir", airTypeEnum, 1, 1, &sign, NULL, |
117 |
|
|
"forward (\"forw\", \"f\") or backward/inverse " |
118 |
|
|
"(\"back\", \"b\") transform ", NULL, direction_enm); |
119 |
|
1 |
hestOptAdd(&opt, "a,axes", "ax0", airTypeUInt, 1, -1, &axes, NULL, |
120 |
|
|
"the one or more axes that should be transformed", &axesLen); |
121 |
|
1 |
hestOptAdd(&opt, "pr,planrigor", "pr", airTypeEnum, 1, 1, &rigor, "est", |
122 |
|
|
"rigor with which fftw plan is constructed. Options include:\n " |
123 |
|
|
"\b\bo \"e\", \"est\", \"estimate\": only an estimate\n " |
124 |
|
|
"\b\bo \"m\", \"meas\", \"measure\": standard amount of " |
125 |
|
|
"measurements of system properties\n " |
126 |
|
|
"\b\bo \"p\", \"pat\", \"patient\": slower, more measurements\n " |
127 |
|
|
"\b\bo \"x\", \"ex\", \"exhaustive\": slowest, most measurements", |
128 |
|
1 |
NULL, nrrdFFTWPlanRigor); |
129 |
|
1 |
hestOptAdd(&opt, "r,rescale", "bool", airTypeBool, 1, 1, &rescale, "true", |
130 |
|
|
"scale fftw output (by sqrt(1/N)) so that forward and backward " |
131 |
|
|
"transforms will get back to original values"); |
132 |
|
1 |
hestOptAdd(&opt, "w,wisdom", "filename", airTypeString, 1, 1, &wispath, "", |
133 |
|
|
"A filename here is used to read in fftw wisdom (if the file " |
134 |
|
|
"exists already), and is used to save out updated wisdom " |
135 |
|
|
"after the transform. By default (not using this option), " |
136 |
|
|
"no wisdom is read or saved. Note: no wisdom is gained " |
137 |
|
|
"(that is, learned by FFTW) with planning rigor \"estimate\"."); |
138 |
|
1 |
OPT_ADD_NIN(_nin, "input nrrd"); |
139 |
|
1 |
hestOptAdd(&opt, "ri,realinput", NULL, airTypeInt, 0, 0, &realInput, NULL, |
140 |
|
|
"input is real-valued, so insert new length-2 axis 0 " |
141 |
|
|
"and set complex component to 0.0. Axes to transform " |
142 |
|
|
"(indicated by \"-a\") will be incremented accordingly."); |
143 |
|
1 |
OPT_ADD_NOUT(out, "output nrrd"); |
144 |
|
|
|
145 |
|
1 |
mop = airMopNew(); |
146 |
|
1 |
airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); |
147 |
|
|
|
148 |
✗✓✗✓
|
2 |
if (nrrdFFTWEnabled) { |
149 |
✗✗ |
1 |
USAGE(_unrrdu_fftInfoL_yes); |
150 |
|
|
} else { |
151 |
✓✗ |
2 |
USAGE(_unrrdu_fftInfoL_no); |
152 |
|
|
} |
153 |
|
|
PARSE(); |
154 |
|
|
airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); |
155 |
|
|
|
156 |
|
|
nout = nrrdNew(); |
157 |
|
|
airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); |
158 |
|
|
|
159 |
|
|
if (realInput) { |
160 |
|
|
ptrdiff_t minPad[NRRD_DIM_MAX], maxPad[NRRD_DIM_MAX]; |
161 |
|
|
unsigned int axi; |
162 |
|
|
Nrrd *ntmp; |
163 |
|
|
ntmp = nrrdNew(); |
164 |
|
|
airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); |
165 |
|
|
if (nrrdAxesInsert(ntmp, _nin, 0)) { |
166 |
|
|
airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); |
167 |
|
|
fprintf(stderr, "%s: error creating complex axis:\n%s", me, err); |
168 |
|
|
airMopError(mop); |
169 |
|
|
return 1; |
170 |
|
|
} |
171 |
|
|
nin = nrrdNew(); |
172 |
|
|
airMopAdd(mop, nin, (airMopper)nrrdNuke, airMopAlways); |
173 |
|
|
minPad[0] = 0; |
174 |
|
|
maxPad[0] = 1; |
175 |
|
|
for (axi=1; axi<ntmp->dim; axi++) { |
176 |
|
|
minPad[axi] = 0; |
177 |
|
|
maxPad[axi] = AIR_CAST(ptrdiff_t, ntmp->axis[axi].size-1); |
178 |
|
|
} |
179 |
|
|
if (nrrdPad_nva(nin, ntmp, minPad, maxPad, nrrdBoundaryPad, 0.0)) { |
180 |
|
|
airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); |
181 |
|
|
fprintf(stderr, "%s: error padding out complex axis:\n%s", me, err); |
182 |
|
|
airMopError(mop); |
183 |
|
|
return 1; |
184 |
|
|
} |
185 |
|
|
/* increment specified axes to transform */ |
186 |
|
|
for (axi=0; axi<axesLen; axi++) { |
187 |
|
|
axes[axi]++; |
188 |
|
|
} |
189 |
|
|
/* ntmp is really done with, we can free up the space now; this |
190 |
|
|
is one of the rare times we want airMopSub */ |
191 |
|
|
airMopSub(mop, ntmp, (airMopper)nrrdNuke); |
192 |
|
|
nrrdNuke(ntmp); |
193 |
|
|
} else { |
194 |
|
|
/* input is apparently already complex */ |
195 |
|
|
nin = _nin; |
196 |
|
|
} |
197 |
|
|
|
198 |
|
|
if (airStrlen(wispath) && nrrdFFTWEnabled) { |
199 |
|
|
fwise = fopen(wispath, "r"); |
200 |
|
|
if (fwise) { |
201 |
|
|
if (nrrdFFTWWisdomRead(fwise)) { |
202 |
|
|
airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); |
203 |
|
|
fprintf(stderr, "%s: error with fft wisdom:\n%s", me, err); |
204 |
|
|
airMopError(mop); |
205 |
|
|
return 1; |
206 |
|
|
} |
207 |
|
|
fclose(fwise); |
208 |
|
|
} else { |
209 |
|
|
fprintf(stderr, "%s: (\"%s\" couldn't be opened, will try to save " |
210 |
|
|
"wisdom afterwards)", me, wispath); |
211 |
|
|
} |
212 |
|
|
} |
213 |
|
|
|
214 |
|
|
if (nrrdFFT(nout, nin, axes, axesLen, sign, rescale, rigor)) { |
215 |
|
|
airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); |
216 |
|
|
fprintf(stderr, "%s: error with fft:\n%s", me, err); |
217 |
|
|
airMopError(mop); |
218 |
|
|
return 1; |
219 |
|
|
} |
220 |
|
|
|
221 |
|
|
if (airStrlen(wispath) && nrrdFFTWEnabled) { |
222 |
|
|
if (!(fwise = fopen(wispath, "w"))) { |
223 |
|
|
fprintf(stderr, "%s: couldn't open %s for writing: %s\n", |
224 |
|
|
me, wispath, strerror(errno)); |
225 |
|
|
airMopError(mop); |
226 |
|
|
return 1; |
227 |
|
|
} |
228 |
|
|
if (nrrdFFTWWisdomWrite(fwise)) { |
229 |
|
|
airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); |
230 |
|
|
fprintf(stderr, "%s: error with fft wisdom:\n%s", me, err); |
231 |
|
|
airMopError(mop); |
232 |
|
|
return 1; |
233 |
|
|
} |
234 |
|
|
fclose(fwise); |
235 |
|
|
} |
236 |
|
|
|
237 |
|
|
SAVE(out, nout, NULL); |
238 |
|
|
|
239 |
|
|
airMopOkay(mop); |
240 |
|
|
return 0; |
241 |
|
1 |
} |
242 |
|
|
|
243 |
|
|
UNRRDU_CMD(fft, INFO); |