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 "gage.h" |
25 |
|
|
#include "privateGage.h" |
26 |
|
|
|
27 |
|
|
const char * |
28 |
|
|
_gageSigmaSamplingStr[] = { |
29 |
|
|
"(unknown_sampling)", |
30 |
|
|
"unisig", /* "uniform-sigma", */ |
31 |
|
|
"unitau", /* "uniform-tau", */ |
32 |
|
|
"optil2" /* "optimal-3d-l2l2" */ |
33 |
|
|
}; |
34 |
|
|
|
35 |
|
|
const char * |
36 |
|
|
_gageSigmaSamplingDesc[] = { |
37 |
|
|
"unknown sampling", |
38 |
|
|
"uniform samples along sigma", |
39 |
|
|
"uniform samples along Lindeberg's tau", |
40 |
|
|
"optimal sampling (3D L2 image error and L2 error across scales)" |
41 |
|
|
}; |
42 |
|
|
|
43 |
|
|
const char * |
44 |
|
|
_gageSigmaSamplingStrEqv[] = { |
45 |
|
|
"uniform-sigma", "unisigma", "unisig", |
46 |
|
|
"uniform-tau", "unitau", |
47 |
|
|
"optimal-3d-l2l2", "optimal-l2l2", "optil2", |
48 |
|
|
"" |
49 |
|
|
}; |
50 |
|
|
|
51 |
|
|
const int |
52 |
|
|
_gageSigmaSamplingValEqv[] = { |
53 |
|
|
gageSigmaSamplingUniformSigma, gageSigmaSamplingUniformSigma, |
54 |
|
|
/* */ gageSigmaSamplingUniformSigma, |
55 |
|
|
gageSigmaSamplingUniformTau, gageSigmaSamplingUniformTau, |
56 |
|
|
gageSigmaSamplingOptimal3DL2L2, gageSigmaSamplingOptimal3DL2L2, |
57 |
|
|
/* */ gageSigmaSamplingOptimal3DL2L2 |
58 |
|
|
}; |
59 |
|
|
|
60 |
|
|
const airEnum |
61 |
|
|
_gageSigmaSampling_enum = { |
62 |
|
|
"sigma sampling strategy", |
63 |
|
|
GAGE_SIGMA_SAMPLING_MAX, |
64 |
|
|
_gageSigmaSamplingStr, NULL, |
65 |
|
|
_gageSigmaSamplingDesc, |
66 |
|
|
_gageSigmaSamplingStrEqv, _gageSigmaSamplingValEqv, |
67 |
|
|
AIR_FALSE |
68 |
|
|
}; |
69 |
|
|
const airEnum *const |
70 |
|
|
gageSigmaSampling = &_gageSigmaSampling_enum; |
71 |
|
|
|
72 |
|
|
|
73 |
|
|
void |
74 |
|
|
gageStackBlurParmInit(gageStackBlurParm *parm) { |
75 |
|
|
|
76 |
✓✗ |
26 |
if (parm) { |
77 |
|
13 |
parm->num = 0; |
78 |
|
13 |
parm->sigmaRange[0] = AIR_NAN; |
79 |
|
13 |
parm->sigmaRange[1] = AIR_NAN; |
80 |
|
13 |
parm->sigmaSampling = gageSigmaSamplingUnknown; |
81 |
|
13 |
parm->sigma = airFree(parm->sigma); |
82 |
|
13 |
parm->kspec = nrrdKernelSpecNix(parm->kspec); |
83 |
|
|
/* this will be effectively moot when nrrdKernelDiscreteGaussian is used |
84 |
|
|
with a bit cut-off, and will only help with smaller cut-offs and with |
85 |
|
|
any other kernel, and will be moot for FFT-based blurring */ |
86 |
|
13 |
parm->renormalize = AIR_TRUE; |
87 |
|
13 |
parm->bspec = nrrdBoundarySpecNix(parm->bspec); |
88 |
|
13 |
parm->oneDim = AIR_FALSE; |
89 |
|
|
/* the cautious application of the FFT--based blurring justifies enables |
90 |
|
|
it by default */ |
91 |
|
13 |
parm->needSpatialBlur = AIR_FALSE; |
92 |
|
13 |
parm->verbose = 1; /* HEY: this may be revisited */ |
93 |
|
13 |
parm->dgGoodSigmaMax = nrrdKernelDiscreteGaussianGoodSigmaMax; |
94 |
|
13 |
} |
95 |
|
13 |
return; |
96 |
|
|
} |
97 |
|
|
|
98 |
|
|
/* |
99 |
|
|
** does not use biff |
100 |
|
|
*/ |
101 |
|
|
gageStackBlurParm * |
102 |
|
|
gageStackBlurParmNew() { |
103 |
|
|
gageStackBlurParm *parm; |
104 |
|
|
|
105 |
|
6 |
parm = AIR_CALLOC(1, gageStackBlurParm); |
106 |
|
3 |
gageStackBlurParmInit(parm); |
107 |
|
3 |
return parm; |
108 |
|
|
} |
109 |
|
|
|
110 |
|
|
gageStackBlurParm * |
111 |
|
|
gageStackBlurParmNix(gageStackBlurParm *sbp) { |
112 |
|
|
|
113 |
✓✗ |
6 |
if (sbp) { |
114 |
|
3 |
airFree(sbp->sigma); |
115 |
|
3 |
nrrdKernelSpecNix(sbp->kspec); |
116 |
|
3 |
nrrdBoundarySpecNix(sbp->bspec); |
117 |
|
3 |
free(sbp); |
118 |
|
3 |
} |
119 |
|
3 |
return NULL; |
120 |
|
|
} |
121 |
|
|
|
122 |
|
|
/* |
123 |
|
|
** *differ is set to 0 or 1; not useful for sorting |
124 |
|
|
*/ |
125 |
|
|
int |
126 |
|
|
gageStackBlurParmCompare(const gageStackBlurParm *aa, const char *_nameA, |
127 |
|
|
const gageStackBlurParm *bb, const char *_nameB, |
128 |
|
|
int *differ, char explain[AIR_STRLEN_LARGE]) { |
129 |
|
|
static const char me[]="gageStackBlurParmCompare", |
130 |
|
|
baseA[]="A", baseB[]="B"; |
131 |
|
|
const char *nameA, *nameB; |
132 |
|
|
unsigned int si, warnLen = AIR_STRLEN_LARGE/4; |
133 |
|
10 |
char stmp[2][AIR_STRLEN_LARGE], subexplain[AIR_STRLEN_LARGE]; |
134 |
|
|
|
135 |
✗✓ |
5 |
if (!(aa && bb && differ)) { |
136 |
|
|
biffAddf(GAGE, "%s: got NULL pointer (%p %p %p)", me, |
137 |
|
|
AIR_VOIDP(aa), AIR_VOIDP(bb), AIR_VOIDP(differ)); |
138 |
|
|
return 1; |
139 |
|
|
} |
140 |
|
5 |
nameA = _nameA ? _nameA : baseA; |
141 |
|
5 |
nameB = _nameB ? _nameB : baseB; |
142 |
✗✓ |
5 |
if (strlen(nameA) + strlen(nameB) > warnLen) { |
143 |
|
|
biffAddf(GAGE, "%s: names (len %s, %s) might lead to overflow", me, |
144 |
|
|
airSprintSize_t(stmp[0], strlen(nameA)), |
145 |
|
|
airSprintSize_t(stmp[1], strlen(nameB))); |
146 |
|
|
return 1; |
147 |
|
|
} |
148 |
|
|
/* |
149 |
|
|
** HEY: really ambivalent about not doing this check: |
150 |
|
|
** its unusual in Teem to not take an opportunity to do this kind |
151 |
|
|
** of sanity check when its available, but we don't really know the |
152 |
|
|
** circumstances of when this will be called, and if that includes |
153 |
|
|
** some interaction with hest, there may not yet have been the chance |
154 |
|
|
** to complete the sbp. |
155 |
|
|
if (gageStackBlurParmCheck(aa)) { |
156 |
|
|
biffAddf(GAGE, "%s: problem with sbp %s", me, nameA); |
157 |
|
|
return 1; |
158 |
|
|
} |
159 |
|
|
if (gageStackBlurParmCheck(bb)) { |
160 |
|
|
biffAddf(GAGE, "%s: problem with sbp %s", me, nameB); |
161 |
|
|
return 1; |
162 |
|
|
} |
163 |
|
|
*/ |
164 |
|
|
#define CHECK(VAR, FMT) \ |
165 |
|
|
if (aa->VAR != bb->VAR) { \ |
166 |
|
|
if (explain) { \ |
167 |
|
|
sprintf(explain, "%s->" #VAR "=" #FMT " != %s->" #VAR "=" #FMT, \ |
168 |
|
|
nameA, aa->VAR, nameB, bb->VAR); \ |
169 |
|
|
} \ |
170 |
|
|
*differ = 1; \ |
171 |
|
|
return 0; \ |
172 |
|
|
} |
173 |
✗✓✗✗
|
5 |
CHECK(num, %u); |
174 |
✗✓✗✗
|
5 |
CHECK(sigmaRange[0], %.17g); |
175 |
✗✓✗✗
|
5 |
CHECK(sigmaRange[1], %.17g); |
176 |
✗✓✗✗
|
5 |
CHECK(renormalize, %d); |
177 |
✗✓✗✗
|
5 |
CHECK(oneDim, %d); |
178 |
✗✓✗✗
|
5 |
CHECK(needSpatialBlur, %d); |
179 |
|
|
/* This is sketchy: the apparent point of the function is to see if two |
180 |
|
|
sbp's are different. But a big role of the function is to enable |
181 |
|
|
leeching in meet. And for leeching, a difference in verbose is moot */ |
182 |
|
|
/* CHECK(verbose, %d); */ |
183 |
✗✓✗✗
|
5 |
CHECK(dgGoodSigmaMax, %.17g); |
184 |
|
|
#undef CHECK |
185 |
✗✓ |
5 |
if (aa->sigmaSampling != bb->sigmaSampling) { |
186 |
|
|
if (explain) { |
187 |
|
|
sprintf(explain, "%s->sigmaSampling=%s != %s->sigmaSampling=%s", |
188 |
|
|
nameA, airEnumStr(gageSigmaSampling, aa->sigmaSampling), |
189 |
|
|
nameB, airEnumStr(gageSigmaSampling, bb->sigmaSampling)); |
190 |
|
|
} |
191 |
|
|
*differ = 1; return 0; |
192 |
|
|
} |
193 |
✓✓ |
50 |
for (si=0; si<aa->num; si++) { |
194 |
✗✓ |
20 |
if (aa->sigma[si] != bb->sigma[si]) { |
195 |
|
|
if (explain) { |
196 |
|
|
sprintf(explain, "%s->sigma[%u]=%.17g != %s->sigma[%u]=%.17g", |
197 |
|
|
nameA, si, aa->sigma[si], nameB, si, bb->sigma[si]); |
198 |
|
|
} |
199 |
|
|
*differ = 1; return 0; |
200 |
|
|
} |
201 |
|
|
} |
202 |
✗✓✗✓
|
10 |
if (nrrdKernelSpecCompare(aa->kspec, bb->kspec, |
203 |
|
5 |
differ, subexplain)) { |
204 |
|
|
biffMovef(GAGE, NRRD, "%s: trouble comparing kernel specs", me); |
205 |
|
|
return 1; |
206 |
|
|
} |
207 |
✗✓ |
5 |
if (*differ) { |
208 |
|
|
if (explain) { |
209 |
|
|
sprintf(explain, "kernel specs different: %s", subexplain); |
210 |
|
|
} |
211 |
|
|
*differ = 1; return 0; |
212 |
|
|
} |
213 |
✗✓ |
5 |
if (nrrdBoundarySpecCompare(aa->bspec, bb->bspec, |
214 |
|
|
differ, subexplain)) { |
215 |
|
|
biffMovef(GAGE, NRRD, "%s: trouble comparing boundary specs", me); |
216 |
|
|
return 1; |
217 |
|
|
} |
218 |
✗✓ |
5 |
if (*differ) { |
219 |
|
|
if (explain) { |
220 |
|
|
sprintf(explain, "boundary specs different: %s", subexplain); |
221 |
|
|
} |
222 |
|
|
*differ = 1; return 0; |
223 |
|
|
} |
224 |
|
|
/* no differences so far */ |
225 |
|
5 |
*differ = 0; |
226 |
|
5 |
return 0; |
227 |
|
5 |
} |
228 |
|
|
|
229 |
|
|
int |
230 |
|
|
gageStackBlurParmCopy(gageStackBlurParm *dst, |
231 |
|
|
const gageStackBlurParm *src) { |
232 |
|
|
static const char me[]="gageStackBlurParmCopy"; |
233 |
|
|
int differ; |
234 |
|
|
char explain[AIR_STRLEN_LARGE]; |
235 |
|
|
|
236 |
|
|
if (!(dst && src)) { |
237 |
|
|
biffAddf(GAGE, "%s: got NULL pointer", me); |
238 |
|
|
return 1; |
239 |
|
|
} |
240 |
|
|
if (gageStackBlurParmCheck(src)) { |
241 |
|
|
biffAddf(GAGE, "%s: given src parm has problems", me); |
242 |
|
|
return 1; |
243 |
|
|
} |
244 |
|
|
if (gageStackBlurParmSigmaSet(dst, src->num, |
245 |
|
|
src->sigmaRange[0], src->sigmaRange[1], |
246 |
|
|
src->sigmaSampling) |
247 |
|
|
|| gageStackBlurParmKernelSet(dst, src->kspec) |
248 |
|
|
|| gageStackBlurParmRenormalizeSet(dst, src->renormalize) |
249 |
|
|
|| gageStackBlurParmDgGoodSigmaMaxSet(dst, src->dgGoodSigmaMax) |
250 |
|
|
|| gageStackBlurParmBoundarySpecSet(dst, src->bspec) |
251 |
|
|
|| gageStackBlurParmNeedSpatialBlurSet(dst, src->needSpatialBlur) |
252 |
|
|
|| gageStackBlurParmVerboseSet(dst, src->verbose) |
253 |
|
|
|| gageStackBlurParmOneDimSet(dst, src->oneDim)) { |
254 |
|
|
biffAddf(GAGE, "%s: problem setting dst parm", me); |
255 |
|
|
return 1; |
256 |
|
|
} |
257 |
|
|
if (gageStackBlurParmCompare(dst, "copy", src, "original", |
258 |
|
|
&differ, explain)) { |
259 |
|
|
biffAddf(GAGE, "%s: trouble assessing correctness of copy", me); |
260 |
|
|
return 1; |
261 |
|
|
} |
262 |
|
|
if (differ) { |
263 |
|
|
biffAddf(GAGE, "%s: problem: copy not equal: %s", me, explain); |
264 |
|
|
return 1; |
265 |
|
|
} |
266 |
|
|
return 0; |
267 |
|
|
} |
268 |
|
|
|
269 |
|
|
int |
270 |
|
|
gageStackBlurParmSigmaSet(gageStackBlurParm *sbp, unsigned int num, |
271 |
|
|
double sigmaMin, double sigmaMax, |
272 |
|
|
int sigmaSampling) { |
273 |
|
|
static const char me[]="gageStackBlurParmSigmaSet"; |
274 |
|
|
unsigned int ii; |
275 |
|
|
|
276 |
✗✓ |
22 |
if (!( sbp )) { |
277 |
|
|
biffAddf(GAGE, "%s: got NULL pointer", me); |
278 |
|
|
return 1; |
279 |
|
|
} |
280 |
|
11 |
airFree(sbp->sigma); |
281 |
|
11 |
sbp->sigma = NULL; |
282 |
✗✓ |
11 |
if (!( 0 <= sigmaMin )) { |
283 |
|
|
biffAddf(GAGE, "%s: need sigmaMin >= 0 (not %g)", me, sigmaMin); |
284 |
|
|
return 1; |
285 |
|
|
} |
286 |
✗✓ |
11 |
if (!( sigmaMin < sigmaMax )) { |
287 |
|
|
biffAddf(GAGE, "%s: need sigmaMax %g > sigmaMin %g", |
288 |
|
|
me, sigmaMax, sigmaMin); |
289 |
|
|
return 1; |
290 |
|
|
} |
291 |
✗✓ |
11 |
if (airEnumValCheck(gageSigmaSampling, sigmaSampling)) { |
292 |
|
|
biffAddf(GAGE, "%s: %d is not a valid %s", me, |
293 |
|
|
sigmaSampling, gageSigmaSampling->name); |
294 |
|
|
return 1; |
295 |
|
|
} |
296 |
✗✓ |
11 |
if (!( num >= 2 )) { |
297 |
|
|
biffAddf(GAGE, "%s: need # scale samples >= 2 (not %u)", me, num); |
298 |
|
|
return 1; |
299 |
|
|
} |
300 |
|
11 |
sbp->sigma = AIR_CALLOC(num, double); |
301 |
✗✓ |
11 |
if (!sbp->sigma) { |
302 |
|
|
biffAddf(GAGE, "%s: couldn't alloc scale for %u", me, num); |
303 |
|
|
return 1; |
304 |
|
|
} |
305 |
|
11 |
sbp->num = num; |
306 |
|
11 |
sbp->sigmaRange[0] = sigmaMin; |
307 |
|
11 |
sbp->sigmaRange[1] = sigmaMax; |
308 |
|
11 |
sbp->sigmaSampling = sigmaSampling; |
309 |
|
|
|
310 |
✓✓✓✗
|
11 |
switch (sigmaSampling) { |
311 |
|
|
double tau0, tau1, tau; |
312 |
|
|
unsigned int sigmax; |
313 |
|
|
case gageSigmaSamplingUniformSigma: |
314 |
✓✓ |
60 |
for (ii=0; ii<num; ii++) { |
315 |
|
24 |
sbp->sigma[ii] = AIR_AFFINE(0, ii, num-1, sigmaMin, sigmaMax); |
316 |
|
|
} |
317 |
|
|
break; |
318 |
|
|
case gageSigmaSamplingUniformTau: |
319 |
|
3 |
tau0 = gageTauOfSig(sigmaMin); |
320 |
|
3 |
tau1 = gageTauOfSig(sigmaMax); |
321 |
✓✓ |
32 |
for (ii=0; ii<num; ii++) { |
322 |
|
13 |
tau = AIR_AFFINE(0, ii, num-1, tau0, tau1); |
323 |
|
13 |
sbp->sigma[ii] = gageSigOfTau(tau); |
324 |
|
|
} |
325 |
|
|
break; |
326 |
|
|
case gageSigmaSamplingOptimal3DL2L2: |
327 |
|
2 |
sigmax = AIR_CAST(unsigned int, sigmaMax); |
328 |
✗✓ |
2 |
if (0 != sigmaMin) { |
329 |
|
|
biffAddf(GAGE, "%s: sigmaMin %g != 0", me, sigmaMin); |
330 |
|
|
return 1; |
331 |
|
|
} |
332 |
✗✓ |
2 |
if (sigmax != sigmaMax) { |
333 |
|
|
biffAddf(GAGE, "%s: sigmaMax %g not an integer", me, sigmaMax); |
334 |
|
|
return 1; |
335 |
|
|
} |
336 |
✗✓ |
2 |
if (gageOptimSigSet(sbp->sigma, num, sigmax)) { |
337 |
|
|
biffAddf(GAGE, "%s: trouble setting optimal sigmas", me); |
338 |
|
|
return 1; |
339 |
|
|
} |
340 |
|
|
break; |
341 |
|
|
default: |
342 |
|
|
biffAddf(GAGE, "%s: sorry, sigmaSampling %s (%d) not implemented", me, |
343 |
|
|
airEnumStr(gageSigmaSampling, sigmaSampling), sigmaSampling); |
344 |
|
|
return 1; |
345 |
|
|
} |
346 |
✗✓ |
11 |
if (sbp->verbose > 1) { |
347 |
|
|
fprintf(stderr, "%s: %u samples in [%g,%g] via %s:\n", me, |
348 |
|
|
num, sigmaMin, sigmaMax, |
349 |
|
|
airEnumStr(gageSigmaSampling, sigmaSampling)); |
350 |
|
|
for (ii=0; ii<num; ii++) { |
351 |
|
|
if (ii) { |
352 |
|
|
fprintf(stderr, "%s: " |
353 |
|
|
"| deltas: %g\t %g\n", me, |
354 |
|
|
sbp->sigma[ii] - sbp->sigma[ii-1], |
355 |
|
|
gageTauOfSig(sbp->sigma[ii]) |
356 |
|
|
- gageTauOfSig(sbp->sigma[ii-1])); |
357 |
|
|
} |
358 |
|
|
fprintf(stderr, "%s: sigma[%02u]=%g%s\t tau=%g\n", me, ii, |
359 |
|
|
sbp->sigma[ii], !sbp->sigma[ii] ? " " : "", |
360 |
|
|
gageTauOfSig(sbp->sigma[ii])); |
361 |
|
|
} |
362 |
|
|
} |
363 |
|
|
|
364 |
|
11 |
return 0; |
365 |
|
11 |
} |
366 |
|
|
|
367 |
|
|
int |
368 |
|
|
gageStackBlurParmScaleSet(gageStackBlurParm *sbp, |
369 |
|
|
unsigned int num, |
370 |
|
|
double smin, double smax, |
371 |
|
|
int uniform, int optimal) { |
372 |
|
|
static const char me[]="gageStackBlurParmScaleSet"; |
373 |
|
|
int sampling; |
374 |
|
|
|
375 |
|
|
fprintf(stderr, "\n%s: !!! This function is deprecated; use " |
376 |
|
|
"gageStackBlurParmSigmaSet instead !!!\n\n", me); |
377 |
|
|
if (uniform && optimal) { |
378 |
|
|
biffAddf(GAGE, "%s: can't have both uniform and optimal sigma sampling", |
379 |
|
|
me); |
380 |
|
|
return 1; |
381 |
|
|
} |
382 |
|
|
sampling = (uniform |
383 |
|
|
? gageSigmaSamplingUniformSigma |
384 |
|
|
: (optimal |
385 |
|
|
? gageSigmaSamplingOptimal3DL2L2 |
386 |
|
|
: gageSigmaSamplingUniformTau)); |
387 |
|
|
if (gageStackBlurParmSigmaSet(sbp, num, smin, smax, sampling)) { |
388 |
|
|
biffAddf(GAGE, "%s: trouble", me); |
389 |
|
|
return 1; |
390 |
|
|
} |
391 |
|
|
return 0; |
392 |
|
|
} |
393 |
|
|
|
394 |
|
|
int |
395 |
|
|
gageStackBlurParmKernelSet(gageStackBlurParm *sbp, |
396 |
|
|
const NrrdKernelSpec *kspec) { |
397 |
|
|
static const char me[]="gageStackBlurParmKernelSet"; |
398 |
|
|
|
399 |
✗✓ |
8 |
if (!( sbp && kspec )) { |
400 |
|
|
biffAddf(GAGE, "%s: got NULL pointer", me); |
401 |
|
|
return 1; |
402 |
|
|
} |
403 |
|
4 |
nrrdKernelSpecNix(sbp->kspec); |
404 |
|
4 |
sbp->kspec = nrrdKernelSpecCopy(kspec); |
405 |
|
4 |
return 0; |
406 |
|
4 |
} |
407 |
|
|
|
408 |
|
|
int |
409 |
|
|
gageStackBlurParmRenormalizeSet(gageStackBlurParm *sbp, |
410 |
|
|
int renormalize) { |
411 |
|
|
static const char me[]="gageStackBlurParmRenormalizeSet"; |
412 |
|
|
|
413 |
✗✓ |
8 |
if (!sbp) { |
414 |
|
|
biffAddf(GAGE, "%s: got NULL pointer", me); |
415 |
|
|
return 1; |
416 |
|
|
} |
417 |
|
4 |
sbp->renormalize = renormalize; |
418 |
|
4 |
return 0; |
419 |
|
4 |
} |
420 |
|
|
|
421 |
|
|
int |
422 |
|
|
gageStackBlurParmBoundarySet(gageStackBlurParm *sbp, |
423 |
|
|
int boundary, double padValue) { |
424 |
|
|
static const char me[]="gageStackBlurParmBoundarySet"; |
425 |
|
|
|
426 |
|
|
if (!sbp) { |
427 |
|
|
biffAddf(GAGE, "%s: got NULL pointer", me); |
428 |
|
|
return 1; |
429 |
|
|
} |
430 |
|
|
nrrdBoundarySpecNix(sbp->bspec); |
431 |
|
|
sbp->bspec = nrrdBoundarySpecNew(); |
432 |
|
|
sbp->bspec->boundary = boundary; |
433 |
|
|
sbp->bspec->padValue = padValue; |
434 |
|
|
if (nrrdBoundarySpecCheck(sbp->bspec)) { |
435 |
|
|
biffMovef(GAGE, NRRD, "%s: problem", me); |
436 |
|
|
return 1; |
437 |
|
|
} |
438 |
|
|
return 0; |
439 |
|
|
} |
440 |
|
|
|
441 |
|
|
int |
442 |
|
|
gageStackBlurParmBoundarySpecSet(gageStackBlurParm *sbp, |
443 |
|
|
const NrrdBoundarySpec *bspec) { |
444 |
|
|
static const char me[]="gageStackBlurParmBoundarySet"; |
445 |
|
|
|
446 |
✗✓ |
4 |
if (!sbp) { |
447 |
|
|
biffAddf(GAGE, "%s: got NULL pointer", me); |
448 |
|
|
return 1; |
449 |
|
|
} |
450 |
|
2 |
nrrdBoundarySpecNix(sbp->bspec); |
451 |
|
2 |
sbp->bspec = nrrdBoundarySpecCopy(bspec); |
452 |
✗✓ |
2 |
if (nrrdBoundarySpecCheck(sbp->bspec)) { |
453 |
|
|
biffMovef(GAGE, NRRD, "%s: problem", me); |
454 |
|
|
return 1; |
455 |
|
|
} |
456 |
|
2 |
return 0; |
457 |
|
2 |
} |
458 |
|
|
|
459 |
|
|
int |
460 |
|
|
gageStackBlurParmOneDimSet(gageStackBlurParm *sbp, int oneDim) { |
461 |
|
|
static const char me[]="gageStackBlurParmOneDimSet"; |
462 |
|
|
|
463 |
✗✓ |
8 |
if (!sbp) { |
464 |
|
|
biffAddf(GAGE, "%s: got NULL pointer", me); |
465 |
|
|
return 1; |
466 |
|
|
} |
467 |
|
4 |
sbp->oneDim = oneDim; |
468 |
|
4 |
return 0; |
469 |
|
4 |
} |
470 |
|
|
|
471 |
|
|
int |
472 |
|
|
gageStackBlurParmNeedSpatialBlurSet(gageStackBlurParm *sbp, |
473 |
|
|
int needSpatialBlur) { |
474 |
|
|
static const char me[]="gageStackBlurParmNeedSpatialBlurSet"; |
475 |
|
|
|
476 |
✗✓ |
8 |
if (!sbp) { |
477 |
|
|
biffAddf(GAGE, "%s: got NULL pointer", me); |
478 |
|
|
return 1; |
479 |
|
|
} |
480 |
|
4 |
sbp->needSpatialBlur = needSpatialBlur; |
481 |
|
4 |
return 0; |
482 |
|
4 |
} |
483 |
|
|
|
484 |
|
|
int |
485 |
|
|
gageStackBlurParmVerboseSet(gageStackBlurParm *sbp, int verbose) { |
486 |
|
|
static const char me[]="gageStackBlurParmVerboseSet"; |
487 |
|
|
|
488 |
✗✓ |
4 |
if (!sbp) { |
489 |
|
|
biffAddf(GAGE, "%s: got NULL pointer", me); |
490 |
|
|
return 1; |
491 |
|
|
} |
492 |
|
2 |
sbp->verbose = verbose; |
493 |
|
2 |
return 0; |
494 |
|
2 |
} |
495 |
|
|
|
496 |
|
|
int |
497 |
|
|
gageStackBlurParmDgGoodSigmaMaxSet(gageStackBlurParm *sbp, |
498 |
|
|
double dgGoodSigmaMax) { |
499 |
|
|
static const char me[]="gageStackBlurParmDgGoodSigmaMaxSet"; |
500 |
|
|
|
501 |
✗✓ |
4 |
if (!sbp) { |
502 |
|
|
biffAddf(GAGE, "%s: got NULL pointer", me); |
503 |
|
|
return 1; |
504 |
|
|
} |
505 |
✗✓ |
2 |
if (!dgGoodSigmaMax > 0) { |
506 |
|
|
biffAddf(GAGE, "%s: given dgGoodSigmaMax %g not > 0", me, dgGoodSigmaMax); |
507 |
|
|
return 1; |
508 |
|
|
} |
509 |
|
2 |
sbp->dgGoodSigmaMax = dgGoodSigmaMax; |
510 |
|
2 |
return 0; |
511 |
|
2 |
} |
512 |
|
|
|
513 |
|
|
int |
514 |
|
|
gageStackBlurParmCheck(const gageStackBlurParm *sbp) { |
515 |
|
|
static const char me[]="gageStackBlurParmCheck"; |
516 |
|
|
unsigned int ii; |
517 |
|
|
|
518 |
|
|
if (!sbp) { |
519 |
|
|
biffAddf(GAGE, "%s: got NULL pointer", me); |
520 |
|
|
return 1; |
521 |
|
|
} |
522 |
|
|
if (!( sbp->num >= 2)) { |
523 |
|
|
biffAddf(GAGE, "%s: need num >= 2, not %u", me, sbp->num); |
524 |
|
|
return 1; |
525 |
|
|
} |
526 |
|
|
if (!sbp->sigma) { |
527 |
|
|
biffAddf(GAGE, "%s: sigma vector not allocated", me); |
528 |
|
|
return 1; |
529 |
|
|
} |
530 |
|
|
if (!sbp->kspec) { |
531 |
|
|
biffAddf(GAGE, "%s: blurring kernel not set", me); |
532 |
|
|
return 1; |
533 |
|
|
} |
534 |
|
|
if (!sbp->bspec) { |
535 |
|
|
biffAddf(GAGE, "%s: boundary specification not set", me); |
536 |
|
|
return 1; |
537 |
|
|
} |
538 |
|
|
for (ii=0; ii<sbp->num; ii++) { |
539 |
|
|
if (!AIR_EXISTS(sbp->sigma[ii])) { |
540 |
|
|
biffAddf(GAGE, "%s: sigma[%u] = %g doesn't exist", me, ii, |
541 |
|
|
sbp->sigma[ii]); |
542 |
|
|
return 1; |
543 |
|
|
} |
544 |
|
|
if (ii) { |
545 |
|
|
if (!( sbp->sigma[ii-1] < sbp->sigma[ii] )) { |
546 |
|
|
biffAddf(GAGE, "%s: sigma[%u] = %g not < sigma[%u] = %g", me, |
547 |
|
|
ii, sbp->sigma[ii-1], ii+1, sbp->sigma[ii]); |
548 |
|
|
return 1; |
549 |
|
|
} |
550 |
|
|
} |
551 |
|
|
} |
552 |
|
|
/* HEY: no sanity check on kernel because there is no |
553 |
|
|
nrrdKernelSpecCheck(), but there should be! */ |
554 |
|
|
if (nrrdBoundarySpecCheck(sbp->bspec)) { |
555 |
|
|
biffMovef(GAGE, NRRD, "%s: problem with boundary", me); |
556 |
|
|
return 1; |
557 |
|
|
} |
558 |
|
|
return 0; |
559 |
|
|
} |
560 |
|
|
|
561 |
|
|
int |
562 |
|
|
gageStackBlurParmParse(gageStackBlurParm *sbp, |
563 |
|
|
int extraFlags[256], |
564 |
|
|
char **extraParmsP, |
565 |
|
|
const char *_str) { |
566 |
|
|
static const char me[]="gageStackBlurParmParse"; |
567 |
|
46 |
char *str, *mnmfS, *stok, *slast=NULL, *parmS, *eps; |
568 |
|
23 |
int flagSeen[256]; |
569 |
|
23 |
double sigmaMin, sigmaMax, dggsm; |
570 |
|
23 |
unsigned int sigmaNum, parmNum; |
571 |
|
23 |
int haveFlags, verbose, verboseGot=AIR_FALSE, dggsmGot=AIR_FALSE, |
572 |
|
|
sampling = AIR_FALSE, samplingGot=AIR_FALSE, E; |
573 |
|
|
airArray *mop, *epsArr; |
574 |
|
|
NrrdKernelSpec *kspec=NULL; |
575 |
|
|
NrrdBoundarySpec *bspec=NULL; |
576 |
|
|
|
577 |
✗✓ |
23 |
if (!( sbp && _str )) { |
578 |
|
|
biffAddf(GAGE, "%s: got NULL pointer", me); |
579 |
|
|
return 1; |
580 |
|
|
} |
581 |
✗✓ |
23 |
if (!( str = airStrdup(_str) )) { |
582 |
|
|
biffAddf(GAGE, "%s: couldn't copy input", me); |
583 |
|
|
return 1; |
584 |
|
|
} |
585 |
|
23 |
mop = airMopNew(); |
586 |
|
23 |
airMopAdd(mop, str, airFree, airMopAlways); |
587 |
✓✓ |
23 |
if (extraParmsP) { |
588 |
|
|
/* start with empty string */ |
589 |
|
22 |
epsArr = airArrayNew(AIR_CAST(void **, &eps), NULL, sizeof(char), 42); |
590 |
|
22 |
airMopAdd(mop, epsArr, (airMopper)airArrayNuke, airMopAlways); |
591 |
|
22 |
airArrayLenIncr(epsArr, 1); |
592 |
|
22 |
*eps = '\0'; |
593 |
|
22 |
} else { |
594 |
|
|
epsArr = NULL; |
595 |
|
|
} |
596 |
|
|
|
597 |
|
|
/* working with assumption that '/' does not appear |
598 |
|
|
in mnmfS <minScl>-<#smp>-<maxScl>[-<flags>] */ |
599 |
✓✓ |
23 |
if ( (parmS = strchr(str, '/')) ) { |
600 |
|
|
/* there are in fact parms */ |
601 |
|
10 |
*parmS = '\0'; |
602 |
|
10 |
parmS++; |
603 |
|
10 |
} else { |
604 |
|
|
parmS = NULL; |
605 |
|
|
} |
606 |
|
|
mnmfS = str; |
607 |
✓✓✓✓
|
40 |
if (!( 3 == airStrntok(mnmfS, "-") || 4 == airStrntok(mnmfS, "-") )) { |
608 |
|
1 |
biffAddf(GAGE, "%s: didn't get 3 or 4 \"-\"-separated tokens in \"%s\"", |
609 |
|
|
me, mnmfS); |
610 |
|
1 |
airMopError(mop); return 1; |
611 |
|
|
} |
612 |
|
22 |
haveFlags = (4 == airStrntok(mnmfS, "-")); |
613 |
|
22 |
stok = airStrtok(mnmfS, "-", &slast); |
614 |
✗✓ |
22 |
if (1 != sscanf(stok, "%lg", &sigmaMin)) { |
615 |
|
|
biffAddf(GAGE, "%s: couldn't parse \"%s\" as max sigma", me, stok); |
616 |
|
|
airMopError(mop); return 1; |
617 |
|
|
} |
618 |
|
22 |
stok = airStrtok(NULL, "-", &slast); |
619 |
✓✓ |
22 |
if (1 != sscanf(stok, "%u", &sigmaNum)) { |
620 |
|
2 |
biffAddf(GAGE, "%s: couldn't parse \"%s\" as # scale samples", me, stok); |
621 |
|
2 |
airMopError(mop); return 1; |
622 |
|
|
} |
623 |
|
20 |
stok = airStrtok(NULL, "-", &slast); |
624 |
✓✓ |
20 |
if (1 != sscanf(stok, "%lg", &sigmaMax)) { |
625 |
|
1 |
biffAddf(GAGE, "%s: couldn't parse \"%s\" as max scale", me, stok); |
626 |
|
1 |
airMopError(mop); return 1; |
627 |
|
|
} |
628 |
|
19 |
memset(flagSeen, 0, sizeof(flagSeen)); |
629 |
✓✓ |
19 |
if (extraFlags) { |
630 |
|
|
/* not sizeof(extraFlags) == sizeof(int*) */ |
631 |
|
18 |
memset(extraFlags, 0, sizeof(flagSeen)); |
632 |
|
18 |
} |
633 |
✓✓ |
19 |
if (haveFlags) { |
634 |
|
|
char *flags, *ff; |
635 |
|
|
/* look for various things in flags */ |
636 |
|
15 |
flags = airToLower(airStrdup(airStrtok(NULL, "-", &slast))); |
637 |
|
15 |
airMopAdd(mop, flags, airFree, airMopAlways); |
638 |
|
|
ff = flags; |
639 |
✓✓✓✓
|
127 |
while (*ff && '+' != *ff) { |
640 |
|
|
/* '1': oneDim |
641 |
|
|
'r': turn OFF spatial kernel renormalize |
642 |
|
|
'u': uniform (in sigma) sampling |
643 |
|
|
'o': optimized (3d l2l2) sampling |
644 |
|
|
'p': need spatial blur |
645 |
|
|
*/ |
646 |
✓✓ |
32 |
if (strchr("1ruop", *ff)) { |
647 |
|
28 |
flagSeen[AIR_CAST(unsigned char, *ff)] = AIR_TRUE; |
648 |
|
28 |
} else { |
649 |
✓✗ |
4 |
if (extraFlags) { |
650 |
|
4 |
extraFlags[AIR_CAST(unsigned char, *ff)] = AIR_TRUE; |
651 |
|
|
} else { |
652 |
|
|
biffAddf(GAGE, "%s: got extra flag '%c' but NULL extraFlag", |
653 |
|
|
me, *ff); |
654 |
|
|
airMopError(mop); return 1; |
655 |
|
|
} |
656 |
|
|
} |
657 |
|
32 |
ff++; |
658 |
|
|
} |
659 |
✓✓✓✓
|
28 |
if (flagSeen['u'] && flagSeen['o']) { |
660 |
|
1 |
biffAddf(GAGE, "%s: can't have both optimal ('o') and uniform ('u') " |
661 |
|
|
"flags set in \"%s\"", me, flags); |
662 |
|
1 |
airMopError(mop); return 1; |
663 |
|
|
} |
664 |
✓✗✓✓
|
28 |
if (ff && '+' == *ff) { |
665 |
|
1 |
biffAddf(GAGE, "%s: sorry, can no longer indicate a derivative " |
666 |
|
|
"normalization bias via '+' in \"%s\" in flags \"%s\"; " |
667 |
|
|
"use \"dnbias=\" parm instead", me, ff, flags); |
668 |
|
1 |
airMopError(mop); return 1; |
669 |
|
|
} |
670 |
|
13 |
} |
671 |
✓✓ |
17 |
if (parmS) { |
672 |
|
|
unsigned int parmIdx; |
673 |
|
10 |
char *pval, xeq[AIR_STRLEN_SMALL]; |
674 |
|
10 |
parmNum = airStrntok(parmS, "/"); |
675 |
✓✓ |
64 |
for (parmIdx=0; parmIdx<parmNum; parmIdx++) { |
676 |
✓✓ |
27 |
if (!parmIdx) { |
677 |
|
10 |
stok = airStrtok(parmS, "/", &slast); |
678 |
|
10 |
} else { |
679 |
|
17 |
stok = airStrtok(NULL, "/", &slast); |
680 |
|
|
} |
681 |
✓✗✓✓
|
54 |
if (strcpy(xeq, "k=") && stok == strstr(stok, xeq)) { |
682 |
|
10 |
pval = stok + strlen(xeq); |
683 |
|
10 |
kspec = nrrdKernelSpecNew(); |
684 |
|
10 |
airMopAdd(mop, kspec, (airMopper)nrrdKernelSpecNix, airMopAlways); |
685 |
✓✓ |
10 |
if (nrrdKernelSpecParse(kspec, pval)) { |
686 |
|
1 |
biffMovef(GAGE, NRRD, "%s: couldn't parse \"%s\" as blurring kernel", |
687 |
|
|
me, pval); |
688 |
|
1 |
airMopError(mop); return 1; |
689 |
|
|
} |
690 |
✓✗✓✓
|
34 |
} else if (strcpy(xeq, "b=") && strstr(stok, xeq) == stok) { |
691 |
|
7 |
pval = stok + strlen(xeq); |
692 |
|
7 |
bspec = nrrdBoundarySpecNew(); |
693 |
|
7 |
airMopAdd(mop, bspec, (airMopper)nrrdBoundarySpecNix, airMopAlways); |
694 |
✓✓ |
7 |
if (nrrdBoundarySpecParse(bspec, pval)) { |
695 |
|
2 |
biffMovef(GAGE, NRRD, "%s: couldn't parse \"%s\" as boundary", |
696 |
|
|
me, pval); |
697 |
|
2 |
airMopError(mop); return 1; |
698 |
|
|
} |
699 |
✓✗✓✓
|
20 |
} else if (strcpy(xeq, "v=") && strstr(stok, xeq) == stok) { |
700 |
|
5 |
pval = stok + strlen(xeq); |
701 |
✓✓ |
5 |
if (1 != sscanf(pval, "%d", &verbose)) { |
702 |
|
1 |
biffAddf(GAGE, "%s: couldn't parse \"%s\" as verbose int", me, pval); |
703 |
|
1 |
airMopError(mop); return 1; |
704 |
|
|
} |
705 |
|
|
verboseGot = AIR_TRUE; |
706 |
✓✗✓✓
|
14 |
} else if (strcpy(xeq, "s=") && strstr(stok, xeq) == stok) { |
707 |
|
2 |
pval = stok + strlen(xeq); |
708 |
|
2 |
sampling = airEnumVal(gageSigmaSampling, pval); |
709 |
✗✓ |
2 |
if (gageSigmaSamplingUnknown == sampling) { |
710 |
|
|
biffAddf(GAGE, "%s: couldn't parse \"%s\" as %s", me, pval, |
711 |
|
|
gageSigmaSampling->name); |
712 |
|
|
airMopError(mop); return 1; |
713 |
|
|
} |
714 |
|
|
samplingGot = AIR_TRUE; |
715 |
✓✗✓✗
|
8 |
} else if (strcpy(xeq, "dggsm=") && strstr(stok, xeq) == stok) { |
716 |
|
3 |
pval = stok + strlen(xeq); |
717 |
✓✓ |
3 |
if (1 != sscanf(pval, "%lg", &dggsm)) { |
718 |
|
1 |
biffAddf(GAGE, "%s: couldn't parse \"%s\" as dgGoodSigmaMax double", |
719 |
|
|
me, pval); |
720 |
|
1 |
airMopError(mop); return 1; |
721 |
|
|
} |
722 |
|
|
dggsmGot = AIR_TRUE; |
723 |
|
2 |
} else { |
724 |
|
|
/* doesn't match any of the parms we know how to parse */ |
725 |
|
|
if (extraParmsP) { |
726 |
|
|
airArrayLenIncr(epsArr, AIR_CAST(int, 2 + strlen(stok))); |
727 |
|
|
if (strlen(eps)) { |
728 |
|
|
strcat(eps, "/"); |
729 |
|
|
} |
730 |
|
|
strcat(eps, stok); |
731 |
|
|
} else { |
732 |
|
|
biffAddf(GAGE, "%s: got extra parm \"%s\" but NULL extraParmsP", |
733 |
|
|
me, stok); |
734 |
|
|
airMopError(mop); return 1; |
735 |
|
|
} |
736 |
|
|
} |
737 |
|
|
} |
738 |
✓✓ |
15 |
} |
739 |
|
|
/* have parsed everything, now error checking and making sense */ |
740 |
✓✓✗✓
|
19 |
if (flagSeen['u'] && flagSeen['o']) { |
741 |
|
|
biffAddf(GAGE, "%s: can't use flags 'u' and 'o' at same time", me); |
742 |
|
|
airMopError(mop); return 1; |
743 |
|
|
} |
744 |
✓✓✗✓ ✓✓ |
24 |
if ((flagSeen['u'] || flagSeen['o']) && samplingGot) { |
745 |
|
1 |
biffAddf(GAGE, "%s: can't use both 'u','o' flags and parms to " |
746 |
|
|
"specify sigma sampling", me); |
747 |
|
1 |
airMopError(mop); return 1; |
748 |
|
|
} |
749 |
✓✗ |
11 |
if (!samplingGot) { |
750 |
|
|
/* have to set sampling from flags */ |
751 |
✓✓ |
11 |
if (flagSeen['u']) { |
752 |
|
|
sampling = gageSigmaSamplingUniformSigma; |
753 |
✓✓ |
11 |
} else if (flagSeen['o']) { |
754 |
|
|
sampling = gageSigmaSamplingOptimal3DL2L2; |
755 |
|
2 |
} else { |
756 |
|
|
sampling = gageSigmaSamplingUniformTau; |
757 |
|
|
} |
758 |
|
|
} |
759 |
|
|
/* setting sbp fields */ |
760 |
|
|
E = 0; |
761 |
✓✗ |
33 |
if (!E) E |= gageStackBlurParmSigmaSet(sbp, sigmaNum, |
762 |
|
11 |
sigmaMin, sigmaMax, sampling); |
763 |
✓✓✓✓
|
22 |
if (kspec) { |
764 |
|
15 |
if (!E) E |= gageStackBlurParmKernelSet(sbp, kspec); |
765 |
|
|
} |
766 |
✓✓✓✓
|
22 |
if (flagSeen['r']) { |
767 |
|
15 |
if (!E) E |= gageStackBlurParmRenormalizeSet(sbp, AIR_FALSE); |
768 |
|
|
} |
769 |
✓✓✓✓
|
22 |
if (dggsmGot) { |
770 |
|
13 |
if (!E) E |= gageStackBlurParmDgGoodSigmaMaxSet(sbp, dggsm); |
771 |
|
|
} |
772 |
✓✓✓✓
|
22 |
if (bspec) { |
773 |
|
13 |
if (!E) E |= gageStackBlurParmBoundarySpecSet(sbp, bspec); |
774 |
|
|
} |
775 |
✓✓✓✓
|
22 |
if (flagSeen['p']) { |
776 |
|
15 |
if (!E) E |= gageStackBlurParmNeedSpatialBlurSet(sbp, AIR_TRUE); |
777 |
|
|
} |
778 |
✓✓✓✓
|
22 |
if (verboseGot) { |
779 |
|
13 |
if (!E) E |= gageStackBlurParmVerboseSet(sbp, verbose); |
780 |
|
|
} |
781 |
✓✓✓✓
|
22 |
if (flagSeen['1']) { |
782 |
|
15 |
if (!E) E |= gageStackBlurParmOneDimSet(sbp, AIR_TRUE); |
783 |
|
|
} |
784 |
|
|
/* NOT doing the final check, because if this is being called from |
785 |
|
|
hest, the caller won't have had time to set the default info in |
786 |
|
|
the sbp (like the default kernel), so it will probably look |
787 |
|
|
incomplete. |
788 |
|
|
if (!E) E |= gageStackBlurParmCheck(sbp); */ |
789 |
✗✓ |
11 |
if (E) { |
790 |
|
|
biffAddf(GAGE, "%s: problem with blur parm specification", me); |
791 |
|
|
airMopError(mop); return 1; |
792 |
|
|
} |
793 |
✓✓ |
11 |
if (extraParmsP) { |
794 |
✗✓ |
10 |
if (airStrlen(eps)) { |
795 |
|
|
*extraParmsP = airStrdup(eps); |
796 |
|
|
} else { |
797 |
|
10 |
*extraParmsP = NULL; |
798 |
|
|
} |
799 |
|
|
} |
800 |
|
11 |
airMopOkay(mop); |
801 |
|
11 |
return 0; |
802 |
|
23 |
} |
803 |
|
|
|
804 |
|
|
int |
805 |
|
|
gageStackBlurParmSprint(char str[AIR_STRLEN_LARGE], |
806 |
|
|
const gageStackBlurParm *sbp, |
807 |
|
|
int extraFlag[256], |
808 |
|
|
char *extraParm) { |
809 |
|
|
static const char me[]="gageStackBlurParmSprint"; |
810 |
|
12 |
char *out, stmp[AIR_STRLEN_LARGE]; |
811 |
|
|
int needFlags, hef; |
812 |
|
|
unsigned int fi; |
813 |
|
|
|
814 |
✗✓ |
6 |
if (!(str && sbp)) { |
815 |
|
|
biffAddf(GAGE, "%s: got NULL pointer", me); |
816 |
|
|
return 1; |
817 |
|
|
} |
818 |
|
|
|
819 |
|
|
out = str; |
820 |
|
6 |
sprintf(out, "%.17g-%u-%.17g", |
821 |
|
|
sbp->sigmaRange[0], sbp->num, sbp->sigmaRange[1]); |
822 |
|
6 |
out += strlen(out); |
823 |
|
|
hef = AIR_FALSE; |
824 |
✓✓ |
6 |
if (extraFlag) { |
825 |
✓✓ |
2570 |
for (fi=0; fi<256; fi++) { |
826 |
|
1280 |
hef |= extraFlag[fi]; |
827 |
|
|
} |
828 |
|
|
} |
829 |
|
6 |
needFlags = (sbp->oneDim |
830 |
✓✓ |
10 |
|| sbp->renormalize |
831 |
✗✓ |
4 |
|| sbp->needSpatialBlur |
832 |
✗✗ |
6 |
|| hef); |
833 |
✓✗ |
6 |
if (needFlags) { |
834 |
|
6 |
strcat(out, "-"); |
835 |
✓✓ |
8 |
if (sbp->oneDim) { strcat(out, "1"); } |
836 |
✓✓ |
10 |
if (sbp->renormalize) { strcat(out, "r"); } |
837 |
✓✓ |
8 |
if (sbp->needSpatialBlur) { strcat(out, "p"); } |
838 |
✓✓ |
6 |
if (hef) { |
839 |
✓✓ |
1028 |
for (fi=0; fi<256; fi++) { |
840 |
✓✓ |
512 |
if (extraFlag[fi]) { |
841 |
|
2 |
sprintf(stmp, "%c", AIR_CAST(char, fi)); |
842 |
|
2 |
strcat(out, stmp); |
843 |
|
2 |
} |
844 |
|
|
} |
845 |
|
|
} |
846 |
|
|
} |
847 |
|
|
|
848 |
✓✓ |
6 |
if (sbp->kspec) { |
849 |
|
2 |
strcat(out, "/"); |
850 |
✗✓ |
2 |
if (nrrdKernelSpecSprint(stmp, sbp->kspec)) { |
851 |
|
|
biffMovef(GAGE, NRRD, "%s: problem with kernel", me); |
852 |
|
|
return 1; |
853 |
|
|
} |
854 |
|
2 |
strcat(out, "k="); strcat(out, stmp); |
855 |
|
2 |
} |
856 |
|
|
|
857 |
✓✓ |
6 |
if (sbp->bspec) { |
858 |
|
1 |
strcat(out, "/"); |
859 |
✗✓ |
1 |
if (nrrdBoundarySpecSprint(stmp, sbp->bspec)) { |
860 |
|
|
biffMovef(GAGE, NRRD, "%s: problem with boundary", me); |
861 |
|
|
return 1; |
862 |
|
|
} |
863 |
|
1 |
strcat(out, "b="); strcat(out, stmp); |
864 |
|
1 |
} |
865 |
|
|
|
866 |
✓✗ |
6 |
if (!airEnumValCheck(gageSigmaSampling, sbp->sigmaSampling)) { |
867 |
|
6 |
strcat(out, "/s="); |
868 |
|
6 |
strcat(out, airEnumStr(gageSigmaSampling, sbp->sigmaSampling)); |
869 |
|
6 |
} |
870 |
|
|
|
871 |
✓✗ |
6 |
if (sbp->verbose) { |
872 |
|
6 |
sprintf(stmp, "/v=%d", sbp->verbose); |
873 |
|
6 |
strcat(out, stmp); |
874 |
|
6 |
} |
875 |
|
|
|
876 |
✓✓ |
8 |
if (sbp->kspec |
877 |
✓✓ |
8 |
&& nrrdKernelDiscreteGaussian == sbp->kspec->kernel |
878 |
✓✗ |
4 |
&& nrrdKernelDiscreteGaussianGoodSigmaMax != sbp->dgGoodSigmaMax) { |
879 |
|
1 |
sprintf(stmp, "/dggsm=%.17g", sbp->dgGoodSigmaMax); |
880 |
|
1 |
strcat(out, stmp); |
881 |
|
1 |
} |
882 |
|
|
|
883 |
✗✓ |
6 |
if (extraParm) { |
884 |
|
|
strcat(out, "/"); |
885 |
|
|
strcat(out, extraParm); |
886 |
|
|
} |
887 |
|
|
|
888 |
|
6 |
return 0; |
889 |
|
6 |
} |
890 |
|
|
|
891 |
|
|
int |
892 |
|
|
_gageHestStackBlurParmParse(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { |
893 |
|
|
gageStackBlurParm **sbp; |
894 |
|
2 |
char me[]="_gageHestStackBlurParmParse", *nerr; |
895 |
|
|
|
896 |
✗✓ |
1 |
if (!(ptr && str)) { |
897 |
|
|
sprintf(err, "%s: got NULL pointer", me); |
898 |
|
|
return 1; |
899 |
|
|
} |
900 |
|
1 |
sbp = (gageStackBlurParm **)ptr; |
901 |
✗✓ |
1 |
if (!strlen(str)) { |
902 |
|
|
/* got an empty string; we trying to emulate an "optional" |
903 |
|
|
command-line option, in a program that likely still has |
904 |
|
|
the older -ssn, -ssr, -kssb, and which may or may not |
905 |
|
|
need any scale-space functionality */ |
906 |
|
|
*sbp = NULL; |
907 |
|
|
} else { |
908 |
|
1 |
*sbp = gageStackBlurParmNew(); |
909 |
|
|
/* NOTE: no way to retrieve extraFlags or extraParms from hest */ |
910 |
✗✓ |
1 |
if (gageStackBlurParmParse(*sbp, NULL, NULL, str)) { |
911 |
|
|
nerr = biffGetDone(GAGE); |
912 |
|
|
airStrcpy(err, AIR_STRLEN_HUGE, nerr); |
913 |
|
|
gageStackBlurParmNix(*sbp); |
914 |
|
|
free(nerr); |
915 |
|
|
return 1; |
916 |
|
|
} |
917 |
|
|
} |
918 |
|
1 |
return 0; |
919 |
|
1 |
} |
920 |
|
|
|
921 |
|
|
hestCB |
922 |
|
|
_gageHestStackBlurParm = { |
923 |
|
|
sizeof(gageStackBlurParm*), |
924 |
|
|
"stack blur specification", |
925 |
|
|
_gageHestStackBlurParmParse, |
926 |
|
|
(airMopper)gageStackBlurParmNix |
927 |
|
|
}; |
928 |
|
|
|
929 |
|
|
hestCB * |
930 |
|
|
gageHestStackBlurParm = &_gageHestStackBlurParm; |
931 |
|
|
|
932 |
|
|
static int |
933 |
|
|
_checkNrrd(Nrrd *const nblur[], const Nrrd *const ncheck[], |
934 |
|
|
unsigned int blNum, int checking, |
935 |
|
|
const Nrrd *nin, const gageKind *kind) { |
936 |
|
|
static const char me[]="_checkNrrd"; |
937 |
|
|
unsigned int blIdx; |
938 |
|
|
|
939 |
|
|
for (blIdx=0; blIdx<blNum; blIdx++) { |
940 |
|
|
if (checking) { |
941 |
|
|
if (nrrdCheck(ncheck[blIdx])) { |
942 |
|
|
biffMovef(GAGE, NRRD, "%s: bad ncheck[%u]", me, blIdx); |
943 |
|
|
return 1; |
944 |
|
|
} |
945 |
|
|
} else { |
946 |
|
|
if (!nblur[blIdx]) { |
947 |
|
|
biffAddf(GAGE, "%s: NULL blur[%u]", me, blIdx); |
948 |
|
|
return 1; |
949 |
|
|
} |
950 |
|
|
} |
951 |
|
|
} |
952 |
|
|
if (3 + kind->baseDim != nin->dim) { |
953 |
|
|
biffAddf(GAGE, "%s: need nin->dim %u (not %u) with baseDim %u", me, |
954 |
|
|
3 + kind->baseDim, nin->dim, kind->baseDim); |
955 |
|
|
return 1; |
956 |
|
|
} |
957 |
|
|
return 0; |
958 |
|
|
} |
959 |
|
|
|
960 |
|
|
#define KVP_NUM 9 |
961 |
|
|
|
962 |
|
|
static const char |
963 |
|
|
_blurKey[KVP_NUM][AIR_STRLEN_LARGE] = {/* 0 */ "gageStackBlur", |
964 |
|
|
/* 1 */ "cksum", |
965 |
|
|
/* 2 */ "scale", |
966 |
|
|
/* 3 */ "kernel", |
967 |
|
|
/* 4 */ "renormalize", |
968 |
|
|
/* 5 */ "boundary", |
969 |
|
|
/* 6 */ "onedim", |
970 |
|
|
/* 7 */ "spatialblurred", |
971 |
|
|
#define KVP_SBLUR_IDX 7 |
972 |
|
|
/* 8 */ "dgGoodSigmaMax" |
973 |
|
|
#define KVP_DGGSM_IDX 8 |
974 |
|
|
/* (9 == KVP_NUM, above) */}; |
975 |
|
|
|
976 |
|
|
typedef struct { |
977 |
|
|
char val[KVP_NUM][AIR_STRLEN_LARGE]; |
978 |
|
|
} blurVal_t; |
979 |
|
|
|
980 |
|
|
static blurVal_t * |
981 |
|
|
_blurValAlloc(airArray *mop, gageStackBlurParm *sbp, NrrdKernelSpec *kssb, |
982 |
|
|
const Nrrd *nin, int spatialBlurred) { |
983 |
|
|
static const char me[]="_blurValAlloc"; |
984 |
|
|
blurVal_t *blurVal; |
985 |
|
|
unsigned int blIdx, cksum; |
986 |
|
|
|
987 |
|
|
blurVal = AIR_CAST(blurVal_t *, calloc(sbp->num, sizeof(blurVal_t))); |
988 |
|
|
if (!blurVal) { |
989 |
|
|
biffAddf(GAGE, "%s: couldn't alloc blurVal for %u", me, sbp->num); |
990 |
|
|
return NULL; |
991 |
|
|
} |
992 |
|
|
cksum = nrrdCRC32(nin, airEndianLittle); |
993 |
|
|
|
994 |
|
|
for (blIdx=0; blIdx<sbp->num; blIdx++) { |
995 |
|
|
kssb->parm[0] = sbp->sigma[blIdx]; |
996 |
|
|
sprintf(blurVal[blIdx].val[0], "true"); |
997 |
|
|
sprintf(blurVal[blIdx].val[1], "%u", cksum); |
998 |
|
|
sprintf(blurVal[blIdx].val[2], "%.17g", sbp->sigma[blIdx]); |
999 |
|
|
nrrdKernelSpecSprint(blurVal[blIdx].val[3], kssb); |
1000 |
|
|
sprintf(blurVal[blIdx].val[4], "%s", sbp->renormalize ? "true" : "false"); |
1001 |
|
|
nrrdBoundarySpecSprint(blurVal[blIdx].val[5], sbp->bspec); |
1002 |
|
|
sprintf(blurVal[blIdx].val[6], "%s", |
1003 |
|
|
sbp->oneDim ? "true" : "false"); |
1004 |
|
|
sprintf(blurVal[blIdx].val[7], "%s", |
1005 |
|
|
spatialBlurred ? "true" : "false"); |
1006 |
|
|
sprintf(blurVal[blIdx].val[8], "%.17g", sbp->dgGoodSigmaMax); |
1007 |
|
|
} |
1008 |
|
|
airMopAdd(mop, blurVal, airFree, airMopAlways); |
1009 |
|
|
return blurVal; |
1010 |
|
|
} |
1011 |
|
|
|
1012 |
|
|
/* |
1013 |
|
|
** some spot checks suggest that where the PSF of lindeberg-gaussian blurring |
1014 |
|
|
** should be significantly non-zero, this is more accurate than the current |
1015 |
|
|
** "discrete gauss" kernel, but for small values near zero, the spatial |
1016 |
|
|
** blurring is more accurate. This is due to how with limited numerical |
1017 |
|
|
** precision, the FFT can produce very low amplitude noise. |
1018 |
|
|
*/ |
1019 |
|
|
static int |
1020 |
|
|
_stackBlurDiscreteGaussFFT(Nrrd *const nblur[], gageStackBlurParm *sbp, |
1021 |
|
|
const Nrrd *nin, const gageKind *kind) { |
1022 |
|
|
static const char me[]="_stackBlurDiscreteGaussFFT"; |
1023 |
|
|
size_t sizeAll[NRRD_DIM_MAX], *size, ii, xi, yi, zi, nn; |
1024 |
|
|
Nrrd *ninC, /* complex version of input, same as input type */ |
1025 |
|
|
*ninFT, /* FT of input, type double */ |
1026 |
|
|
*noutFT, /* FT of output, values set manually from ninFT, as double */ |
1027 |
|
|
*noutCd, /* complex version of output, still as double */ |
1028 |
|
|
*noutC; /* complex version of output, as input type; |
1029 |
|
|
will convert/clamp this to get nblur[i] */ |
1030 |
|
|
double (*lup)(const void *, size_t), (*ins)(void *, size_t, double), |
1031 |
|
|
*ww[3], tblur, theta, *inFT, *outFT; |
1032 |
|
|
unsigned int blIdx, axi, ftaxes[3] = {1,2,3}; |
1033 |
|
|
airArray *mop; |
1034 |
|
|
int axmap[NRRD_DIM_MAX]; |
1035 |
|
|
|
1036 |
|
|
mop = airMopNew(); |
1037 |
|
|
ninC = nrrdNew(); |
1038 |
|
|
airMopAdd(mop, ninC, (airMopper)nrrdNuke, airMopAlways); |
1039 |
|
|
ninFT = nrrdNew(); |
1040 |
|
|
airMopAdd(mop, ninFT, (airMopper)nrrdNuke, airMopAlways); |
1041 |
|
|
noutFT = nrrdNew(); |
1042 |
|
|
airMopAdd(mop, noutFT, (airMopper)nrrdNuke, airMopAlways); |
1043 |
|
|
noutCd = nrrdNew(); |
1044 |
|
|
airMopAdd(mop, noutCd, (airMopper)nrrdNuke, airMopAlways); |
1045 |
|
|
noutC = nrrdNew(); |
1046 |
|
|
airMopAdd(mop, noutC, (airMopper)nrrdNuke, airMopAlways); |
1047 |
|
|
|
1048 |
|
|
if (gageKindScl != kind) { |
1049 |
|
|
biffAddf(GAGE, "%s: sorry, non-scalar kind not yet implemented", me); |
1050 |
|
|
/* but it really wouldn't be that hard ... */ |
1051 |
|
|
airMopError(mop); return 1; |
1052 |
|
|
} |
1053 |
|
|
if (3 != nin->dim) { |
1054 |
|
|
biffAddf(GAGE, "%s: sanity check fail: nin->dim %u != 3", me, nin->dim); |
1055 |
|
|
airMopError(mop); return 1; |
1056 |
|
|
} |
1057 |
|
|
lup = nrrdDLookup[nin->type]; |
1058 |
|
|
ins = nrrdDInsert[nin->type]; |
1059 |
|
|
/* unurrdu/fft.c handles real input by doing an axis insert and then |
1060 |
|
|
padding, but that is overkill; this is a more direct way, which perhaps |
1061 |
|
|
should be migrated to unurrdu/fft.c */ |
1062 |
|
|
sizeAll[0] = 2; |
1063 |
|
|
size = sizeAll + 1; |
1064 |
|
|
nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size); |
1065 |
|
|
if (nrrdMaybeAlloc_nva(ninC, nin->type, nin->dim+1, sizeAll)) { |
1066 |
|
|
biffMovef(GAGE, NRRD, "%s: couldn't allocate complex-valued input", me); |
1067 |
|
|
airMopError(mop); return 1; |
1068 |
|
|
} |
1069 |
|
|
for (axi=0; axi<3; axi++) { |
1070 |
|
|
if (!( ww[axi] = AIR_CALLOC(size[axi], double) )) { |
1071 |
|
|
biffAddf(GAGE, "%s: couldn't allocate axis %u buffer", me, axi); |
1072 |
|
|
airMopError(mop); return 1; |
1073 |
|
|
} |
1074 |
|
|
airMopAdd(mop, ww[axi], airFree, airMopAlways); |
1075 |
|
|
} |
1076 |
|
|
nn = size[0]*size[1]*size[2]; |
1077 |
|
|
for (ii=0; ii<nn; ii++) { |
1078 |
|
|
ins(ninC->data, 0 + 2*ii, lup(nin->data, ii)); |
1079 |
|
|
ins(ninC->data, 1 + 2*ii, 0.0); |
1080 |
|
|
} |
1081 |
|
|
for (axi=0; axi<4; axi++) { |
1082 |
|
|
if (!axi) { |
1083 |
|
|
axmap[axi] = -1; |
1084 |
|
|
} else { |
1085 |
|
|
axmap[axi] = axi-1; |
1086 |
|
|
} |
1087 |
|
|
} |
1088 |
|
|
if (nrrdAxisInfoCopy(ninC, nin, axmap, NRRD_AXIS_INFO_NONE) |
1089 |
|
|
|| nrrdBasicInfoCopy(ninC, nin, |
1090 |
|
|
(NRRD_BASIC_INFO_DATA_BIT | |
1091 |
|
|
NRRD_BASIC_INFO_DIMENSION_BIT | |
1092 |
|
|
NRRD_BASIC_INFO_CONTENT_BIT | |
1093 |
|
|
NRRD_BASIC_INFO_COMMENTS_BIT | |
1094 |
|
|
NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { |
1095 |
|
|
biffMovef(GAGE, NRRD, "%s: couldn't set complex-valued axinfo", me); |
1096 |
|
|
airMopError(mop); return 1; |
1097 |
|
|
} |
1098 |
|
|
ninC->axis[0].kind = nrrdKindComplex; /* should use API */ |
1099 |
|
|
/* |
1100 |
|
|
nrrdSave("ninC.nrrd", ninC, NULL); |
1101 |
|
|
*/ |
1102 |
|
|
/* the copy to noutFT is just to allocate it; the values |
1103 |
|
|
there will be re-written over and over in the loop below */ |
1104 |
|
|
if (nrrdFFT(ninFT, ninC, ftaxes, 3, |
1105 |
|
|
+1 /* forward */, |
1106 |
|
|
AIR_TRUE /* rescale */, |
1107 |
|
|
nrrdFFTWPlanRigorEstimate /* should generalize! */) |
1108 |
|
|
|| nrrdCopy(noutFT, ninFT)) { |
1109 |
|
|
biffMovef(GAGE, NRRD, "%s: trouble with initial transforms", me); |
1110 |
|
|
airMopError(mop); return 1; |
1111 |
|
|
} |
1112 |
|
|
/* |
1113 |
|
|
nrrdSave("ninFT.nrrd", ninFT, NULL); |
1114 |
|
|
*/ |
1115 |
|
|
inFT = AIR_CAST(double *, ninFT->data); |
1116 |
|
|
outFT = AIR_CAST(double *, noutFT->data); |
1117 |
|
|
for (blIdx=0; blIdx<sbp->num; blIdx++) { |
1118 |
|
|
if (sbp->verbose) { |
1119 |
|
|
fprintf(stderr, "%s: . . . %u/%u (scale %g, tau %g) . . . ", me, |
1120 |
|
|
blIdx, sbp->num, sbp->sigma[blIdx], |
1121 |
|
|
gageTauOfSig(sbp->sigma[blIdx])); |
1122 |
|
|
fflush(stderr); |
1123 |
|
|
} |
1124 |
|
|
tblur = sbp->sigma[blIdx]*sbp->sigma[blIdx]; |
1125 |
|
|
for (axi=0; axi<3; axi++) { |
1126 |
|
|
for (ii=0; ii<size[axi]; ii++) { |
1127 |
|
|
theta = AIR_AFFINE(0, ii, size[axi], 0.0, 2*AIR_PI); |
1128 |
|
|
/* from eq (22) of T. Lindeberg "Scale-Space for Discrete |
1129 |
|
|
Signals", IEEE PAMI 12(234-254); 1990 */ |
1130 |
|
|
ww[axi][ii] = exp(tblur*(cos(theta)-1.0)); |
1131 |
|
|
/* |
1132 |
|
|
fprintf(stderr, "!%s: ww[%u][%u] = %g\n", me, axi, |
1133 |
|
|
AIR_CAST(unsigned int, ii), ww[axi][ii]); |
1134 |
|
|
*/ |
1135 |
|
|
} |
1136 |
|
|
} |
1137 |
|
|
ii=0; |
1138 |
|
|
/* |
1139 |
|
|
for (axi=0; axi<3; axi++) { |
1140 |
|
|
fprintf(stderr, "!%s: size[%u] = %u\n", me, axi, |
1141 |
|
|
AIR_CAST(unsigned int, size[axi])); |
1142 |
|
|
} |
1143 |
|
|
*/ |
1144 |
|
|
for (zi=0; zi<size[2]; zi++) { |
1145 |
|
|
for (yi=0; yi<size[1]; yi++) { |
1146 |
|
|
for (xi=0; xi<size[0]; xi++) { |
1147 |
|
|
double wght; |
1148 |
|
|
wght = sbp->oneDim ? 1.0 : ww[1][yi]*ww[2][zi]; |
1149 |
|
|
wght *= ww[0][xi]; |
1150 |
|
|
outFT[0 + 2*ii] = wght*inFT[0 + 2*ii]; |
1151 |
|
|
outFT[1 + 2*ii] = wght*inFT[1 + 2*ii]; |
1152 |
|
|
/* |
1153 |
|
|
fprintf(stderr, "!%s: out[%u] = (%g,%g) = %g * (%g,%g)\n", me, |
1154 |
|
|
AIR_CAST(unsigned int, ii), |
1155 |
|
|
outFT[0 + 2*ii], outFT[1 + 2*ii], |
1156 |
|
|
wght, |
1157 |
|
|
inFT[0 + 2*ii], inFT[1 + 2*ii]); |
1158 |
|
|
*/ |
1159 |
|
|
ii++; |
1160 |
|
|
} |
1161 |
|
|
} |
1162 |
|
|
} |
1163 |
|
|
if (nrrdFFT(noutCd, noutFT, ftaxes, 3, |
1164 |
|
|
-1 /* backward */, |
1165 |
|
|
AIR_TRUE /* rescale */, |
1166 |
|
|
nrrdFFTWPlanRigorEstimate /* should generalize! */) |
1167 |
|
|
|| (nrrdTypeDouble == nin->type |
1168 |
|
|
? nrrdCopy(noutC, noutCd) |
1169 |
|
|
: nrrdCastClampRound(noutC, noutCd, nin->type, |
1170 |
|
|
AIR_TRUE /* clamp */, |
1171 |
|
|
+1 /* roundDir, when needed */)) |
1172 |
|
|
|| nrrdSlice(nblur[blIdx], noutC, 0, 0) |
1173 |
|
|
|| nrrdContentSet_va(nblur[blIdx], "blur", nin, "")) { |
1174 |
|
|
biffMovef(GAGE, NRRD, "%s: trouble with back transform %u", me, blIdx); |
1175 |
|
|
airMopError(mop); return 1; |
1176 |
|
|
} |
1177 |
|
|
if (sbp->verbose) { |
1178 |
|
|
fprintf(stderr, "done\n"); |
1179 |
|
|
} |
1180 |
|
|
/* |
1181 |
|
|
if (0) { |
1182 |
|
|
char fname[AIR_STRLEN_SMALL]; |
1183 |
|
|
sprintf(fname, "noutFT-%03u.nrrd", blIdx); |
1184 |
|
|
nrrdSave(fname, noutFT, NULL); |
1185 |
|
|
sprintf(fname, "noutCd-%03u.nrrd", blIdx); |
1186 |
|
|
nrrdSave(fname, noutCd, NULL); |
1187 |
|
|
sprintf(fname, "noutC-%03u.nrrd", blIdx); |
1188 |
|
|
nrrdSave(fname, noutC, NULL); |
1189 |
|
|
sprintf(fname, "nblur-%03u.nrrd", blIdx); |
1190 |
|
|
nrrdSave(fname, nblur[blIdx], NULL); |
1191 |
|
|
} |
1192 |
|
|
*/ |
1193 |
|
|
} |
1194 |
|
|
|
1195 |
|
|
airMopOkay(mop); |
1196 |
|
|
return 0; |
1197 |
|
|
} |
1198 |
|
|
|
1199 |
|
|
static int |
1200 |
|
|
_stackBlurSpatial(Nrrd *const nblur[], gageStackBlurParm *sbp, |
1201 |
|
|
NrrdKernelSpec *kssb, |
1202 |
|
|
const Nrrd *nin, const gageKind *kind) { |
1203 |
|
|
static const char me[]="_stackBlurSpatial"; |
1204 |
|
|
NrrdResampleContext *rsmc; |
1205 |
|
|
Nrrd *niter; |
1206 |
|
|
unsigned int axi, blIdx; |
1207 |
|
|
int E, iterative, rsmpType; |
1208 |
|
|
double timeStepMax, /* max length of diffusion time allowed per blur, |
1209 |
|
|
as determined by sbp->dgGoodSigmaMax */ |
1210 |
|
|
timeDone, /* amount of diffusion time just applied */ |
1211 |
|
|
timeLeft; /* amount of diffusion time left to do */ |
1212 |
|
|
airArray *mop; |
1213 |
|
|
|
1214 |
|
|
mop = airMopNew(); |
1215 |
|
|
rsmc = nrrdResampleContextNew(); |
1216 |
|
|
airMopAdd(mop, rsmc, (airMopper)nrrdResampleContextNix, airMopAlways); |
1217 |
|
|
if (nrrdKernelDiscreteGaussian == kssb->kernel) { |
1218 |
|
|
iterative = AIR_TRUE; |
1219 |
|
|
/* we don't want to lose precision when iterating */ |
1220 |
|
|
rsmpType = nrrdResample_nt; |
1221 |
|
|
/* may be used with iterative diffusion */ |
1222 |
|
|
niter = nrrdNew(); |
1223 |
|
|
airMopAdd(mop, niter, (airMopper)nrrdNuke, airMopAlways); |
1224 |
|
|
} else { |
1225 |
|
|
iterative = AIR_FALSE; |
1226 |
|
|
rsmpType = nrrdTypeDefault; |
1227 |
|
|
niter = NULL; |
1228 |
|
|
} |
1229 |
|
|
|
1230 |
|
|
E = 0; |
1231 |
|
|
if (!E) E |= nrrdResampleDefaultCenterSet(rsmc, nrrdDefaultCenter); |
1232 |
|
|
/* the input for the first scale is indeed nin, regardless |
1233 |
|
|
of iterative */ |
1234 |
|
|
if (!E) E |= nrrdResampleInputSet(rsmc, nin); |
1235 |
|
|
if (kind->baseDim) { |
1236 |
|
|
unsigned int bai; |
1237 |
|
|
for (bai=0; bai<kind->baseDim; bai++) { |
1238 |
|
|
if (!E) E |= nrrdResampleKernelSet(rsmc, bai, NULL, NULL); |
1239 |
|
|
} |
1240 |
|
|
} |
1241 |
|
|
for (axi=0; axi<3; axi++) { |
1242 |
|
|
if (!E) E |= nrrdResampleSamplesSet(rsmc, kind->baseDim + axi, |
1243 |
|
|
nin->axis[kind->baseDim + axi].size); |
1244 |
|
|
if (!E) E |= nrrdResampleRangeFullSet(rsmc, kind->baseDim + axi); |
1245 |
|
|
} |
1246 |
|
|
if (!E) E |= nrrdResampleBoundarySpecSet(rsmc, sbp->bspec); |
1247 |
|
|
if (!E) E |= nrrdResampleTypeOutSet(rsmc, rsmpType); |
1248 |
|
|
if (!E) E |= nrrdResampleClampSet(rsmc, AIR_TRUE); /* probably moot */ |
1249 |
|
|
if (!E) E |= nrrdResampleRenormalizeSet(rsmc, sbp->renormalize); |
1250 |
|
|
if (E) { |
1251 |
|
|
biffAddf(GAGE, "%s: trouble setting up resampling", me); |
1252 |
|
|
airMopError(mop); return 1; |
1253 |
|
|
} |
1254 |
|
|
|
1255 |
|
|
timeDone = 0; |
1256 |
|
|
timeStepMax = (sbp->dgGoodSigmaMax)*(sbp->dgGoodSigmaMax); |
1257 |
|
|
for (blIdx=0; blIdx<sbp->num; blIdx++) { |
1258 |
|
|
if (sbp->verbose) { |
1259 |
|
|
fprintf(stderr, "%s: . . . blurring %u / %u (scale %g) . . . ", |
1260 |
|
|
me, blIdx, sbp->num, sbp->sigma[blIdx]); |
1261 |
|
|
fflush(stderr); |
1262 |
|
|
} |
1263 |
|
|
if (iterative) { |
1264 |
|
|
double timeNow = sbp->sigma[blIdx]*sbp->sigma[blIdx]; |
1265 |
|
|
unsigned int passIdx = 0; |
1266 |
|
|
timeLeft = timeNow - timeDone; |
1267 |
|
|
if (sbp->verbose) { |
1268 |
|
|
fprintf(stderr, "\n"); |
1269 |
|
|
fprintf(stderr, "%s: scale %g == time %g (tau %g);\n" |
1270 |
|
|
" timeLeft %g = %g - %g\n", |
1271 |
|
|
me, sbp->sigma[blIdx], timeNow, gageTauOfTee(timeNow), |
1272 |
|
|
timeLeft, timeNow, timeDone); |
1273 |
|
|
if (timeLeft > timeStepMax) { |
1274 |
|
|
fprintf(stderr, "%s: diffusing for time %g in steps of %g\n", me, |
1275 |
|
|
timeLeft, timeStepMax); |
1276 |
|
|
} |
1277 |
|
|
fflush(stderr); |
1278 |
|
|
} |
1279 |
|
|
do { |
1280 |
|
|
double timeDo; |
1281 |
|
|
if (blIdx || passIdx) { |
1282 |
|
|
/* either we're past the first scale (blIdx >= 1), or |
1283 |
|
|
(unlikely) we're on the first scale but after the first |
1284 |
|
|
pass of a multi-pass blurring, so we have to feed the |
1285 |
|
|
previous result back in as input. |
1286 |
|
|
AND: the way that niter is being used is very sneaky, |
1287 |
|
|
and probably too clever: the resampling happens in |
1288 |
|
|
multiple passes, among buffers internal to nrrdResample; |
1289 |
|
|
so its okay have the output and input nrrds be the same: |
1290 |
|
|
they're never used at the same time. */ |
1291 |
|
|
if (!E) E |= nrrdResampleInputSet(rsmc, niter); |
1292 |
|
|
} |
1293 |
|
|
timeDo = (timeLeft > timeStepMax |
1294 |
|
|
? timeStepMax |
1295 |
|
|
: timeLeft); |
1296 |
|
|
/* it is the repeated re-setting of this parm[0] which motivated |
1297 |
|
|
copying to our own kernel spec, so that the given one in the |
1298 |
|
|
gageStackBlurParm can stay untouched */ |
1299 |
|
|
kssb->parm[0] = sqrt(timeDo); |
1300 |
|
|
for (axi=0; axi<3; axi++) { |
1301 |
|
|
if (!sbp->oneDim || !axi) { |
1302 |
|
|
/* we set the blurring kernel on this axis if |
1303 |
|
|
we are NOT doing oneDim, or, we are, |
1304 |
|
|
but this is axi == 0 */ |
1305 |
|
|
if (!E) E |= nrrdResampleKernelSet(rsmc, kind->baseDim + axi, |
1306 |
|
|
kssb->kernel, |
1307 |
|
|
kssb->parm); |
1308 |
|
|
} else { |
1309 |
|
|
/* what to do with oneDom on axi 1, 2 */ |
1310 |
|
|
/* you might think that we should just do no resampling at all |
1311 |
|
|
on this axis, but that would undermine the in==out==niter |
1312 |
|
|
trick described above; and produce the mysterious behavior |
1313 |
|
|
that the second scale-space volume is all 0.0 */ |
1314 |
|
|
double boxparm[NRRD_KERNEL_PARMS_NUM] = {1.0}; |
1315 |
|
|
if (!E) E |= nrrdResampleKernelSet(rsmc, kind->baseDim + axi, |
1316 |
|
|
nrrdKernelBox, boxparm); |
1317 |
|
|
} |
1318 |
|
|
} |
1319 |
|
|
if (sbp->verbose) { |
1320 |
|
|
fprintf(stderr, " pass %u (timeLeft=%g => " |
1321 |
|
|
"time=%g, sigma=%g) ...\n", |
1322 |
|
|
passIdx, timeLeft, timeDo, kssb->parm[0]); |
1323 |
|
|
} |
1324 |
|
|
if (!E) E |= nrrdResampleExecute(rsmc, niter); |
1325 |
|
|
/* for debugging problem with getting zero output |
1326 |
|
|
if (!E) { |
1327 |
|
|
NrrdRange *nrange; |
1328 |
|
|
nrange = nrrdRangeNewSet(niter, AIR_FALSE); |
1329 |
|
|
fprintf(stderr, "%s: min/max = %g/%g\n", me, |
1330 |
|
|
nrange->min, nrange->max); |
1331 |
|
|
if (!nrange->min || !nrange->max) { |
1332 |
|
|
fprintf(stderr, "%s: what? zero zero\n", me); |
1333 |
|
|
biffAddf(GAGE, "%s: no good", me); |
1334 |
|
|
airMopError(mop); return 1; |
1335 |
|
|
} |
1336 |
|
|
} |
1337 |
|
|
*/ |
1338 |
|
|
timeLeft -= timeDo; |
1339 |
|
|
passIdx++; |
1340 |
|
|
} while (!E && timeLeft > 0.0); |
1341 |
|
|
/* at this point we have to replicate the behavior of the |
1342 |
|
|
last stage of resampling (e.g. _nrrdResampleOutputUpdate |
1343 |
|
|
in nrrd/resampleContext.c), since we've gently hijacked |
1344 |
|
|
the resampling to access the nrrdResample_t blurring |
1345 |
|
|
result (for further blurring) */ |
1346 |
|
|
if (!E) E |= nrrdCastClampRound(nblur[blIdx], niter, nin->type, |
1347 |
|
|
AIR_TRUE, |
1348 |
|
|
nrrdTypeIsIntegral[nin->type]); |
1349 |
|
|
if (!E) E |= nrrdContentSet_va(nblur[blIdx], "blur", nin, ""); |
1350 |
|
|
timeDone = timeNow; |
1351 |
|
|
} else { /* do blurring in one shot */ |
1352 |
|
|
kssb->parm[0] = sbp->sigma[blIdx]; |
1353 |
|
|
for (axi=0; axi<(sbp->oneDim ? 1u : 3u); axi++) { |
1354 |
|
|
if (!E) E |= nrrdResampleKernelSet(rsmc, kind->baseDim + axi, |
1355 |
|
|
kssb->kernel, |
1356 |
|
|
kssb->parm); |
1357 |
|
|
} |
1358 |
|
|
if (!E) E |= nrrdResampleExecute(rsmc, nblur[blIdx]); |
1359 |
|
|
} |
1360 |
|
|
if (E) { |
1361 |
|
|
if (sbp->verbose) { |
1362 |
|
|
fprintf(stderr, "problem!\n"); |
1363 |
|
|
} |
1364 |
|
|
biffMovef(GAGE, NRRD, "%s: trouble w/ %u of %u (scale %g)", |
1365 |
|
|
me, blIdx, sbp->num, sbp->sigma[blIdx]); |
1366 |
|
|
airMopError(mop); return 1; |
1367 |
|
|
} |
1368 |
|
|
if (sbp->verbose) { |
1369 |
|
|
fprintf(stderr, " done.\n"); |
1370 |
|
|
} |
1371 |
|
|
} /* for blIdx */ |
1372 |
|
|
|
1373 |
|
|
airMopOkay(mop); |
1374 |
|
|
return 0; |
1375 |
|
|
} |
1376 |
|
|
|
1377 |
|
|
/* |
1378 |
|
|
** little helper function to do pre-blurring of a given nrrd |
1379 |
|
|
** of the sort that might be useful for scale-space gage use |
1380 |
|
|
** |
1381 |
|
|
** nblur has to already be allocated for "blNum" Nrrd*s, AND, they all |
1382 |
|
|
** have to point to valid (possibly empty) Nrrds, so they can hold the |
1383 |
|
|
** results of blurring |
1384 |
|
|
*/ |
1385 |
|
|
int |
1386 |
|
|
gageStackBlur(Nrrd *const nblur[], gageStackBlurParm *sbp, |
1387 |
|
|
const Nrrd *nin, const gageKind *kind) { |
1388 |
|
|
static const char me[]="gageStackBlur"; |
1389 |
|
|
unsigned int blIdx, kvpIdx; |
1390 |
|
|
NrrdKernelSpec *kssb; |
1391 |
|
|
blurVal_t *blurVal; |
1392 |
|
|
airArray *mop; |
1393 |
|
|
int E, fftable, spatialBlurred; |
1394 |
|
|
|
1395 |
|
|
if (!(nblur && sbp && nin && kind)) { |
1396 |
|
|
biffAddf(GAGE, "%s: got NULL pointer", me); |
1397 |
|
|
return 1; |
1398 |
|
|
} |
1399 |
|
|
if (gageStackBlurParmCheck(sbp)) { |
1400 |
|
|
biffAddf(GAGE, "%s: problem with parms", me); |
1401 |
|
|
return 1; |
1402 |
|
|
} |
1403 |
|
|
if (_checkNrrd(nblur, NULL, sbp->num, AIR_FALSE, nin, kind)) { |
1404 |
|
|
biffAddf(GAGE, "%s: problem with input ", me); |
1405 |
|
|
return 1; |
1406 |
|
|
} |
1407 |
|
|
mop = airMopNew(); |
1408 |
|
|
kssb = nrrdKernelSpecCopy(sbp->kspec); |
1409 |
|
|
airMopAdd(mop, kssb, (airMopper)nrrdKernelSpecNix, airMopAlways); |
1410 |
|
|
/* see if we can use FFT-based implementation */ |
1411 |
|
|
fftable = (!sbp->needSpatialBlur |
1412 |
|
|
&& nrrdBoundaryWrap == sbp->bspec->boundary |
1413 |
|
|
&& nrrdKernelDiscreteGaussian == sbp->kspec->kernel); |
1414 |
|
|
if (fftable && nrrdFFTWEnabled) { |
1415 |
|
|
/* go directly to FFT-based blurring */ |
1416 |
|
|
if (_stackBlurDiscreteGaussFFT(nblur, sbp, nin, kind)) { |
1417 |
|
|
biffAddf(GAGE, "%s: trouble with frequency-space blurring", me); |
1418 |
|
|
airMopError(mop); return 1; |
1419 |
|
|
} |
1420 |
|
|
spatialBlurred = AIR_FALSE; |
1421 |
|
|
} else { /* else either not fft-able, or not it was, but not available; |
1422 |
|
|
in either case we have to do spatial blurring */ |
1423 |
|
|
if (fftable && !nrrdFFTWEnabled) { |
1424 |
|
|
if (sbp->verbose) { |
1425 |
|
|
fprintf(stderr, "%s: NOTE: FFT-based blurring applicable but not " |
1426 |
|
|
"available in this Teem build (not built with FFTW)\n", me); |
1427 |
|
|
} |
1428 |
|
|
} else { |
1429 |
|
|
if (sbp->verbose) { |
1430 |
|
|
char kstr[AIR_STRLEN_LARGE], bstr[AIR_STRLEN_LARGE]; |
1431 |
|
|
nrrdKernelSpecSprint(kstr, kssb); |
1432 |
|
|
nrrdBoundarySpecSprint(bstr, sbp->bspec); |
1433 |
|
|
fprintf(stderr, "%s: (FFT-based blurring not applicable: " |
1434 |
|
|
"need spatial blur=%s, boundary=%s, kernel=%s)\n", me, |
1435 |
|
|
sbp->needSpatialBlur ? "yes" : "no", bstr, kstr); |
1436 |
|
|
} |
1437 |
|
|
} |
1438 |
|
|
if (_stackBlurSpatial(nblur, sbp, kssb, nin, kind)) { |
1439 |
|
|
biffAddf(GAGE, "%s: trouble with spatial-domain blurring", me); |
1440 |
|
|
airMopError(mop); return 1; |
1441 |
|
|
} |
1442 |
|
|
spatialBlurred = AIR_TRUE; |
1443 |
|
|
} |
1444 |
|
|
/* add the KVPs to document how these were blurred */ |
1445 |
|
|
if (!( blurVal = _blurValAlloc(mop, sbp, kssb, nin, spatialBlurred) )) { |
1446 |
|
|
biffAddf(GAGE, "%s: problem getting KVP buffer", me); |
1447 |
|
|
airMopError(mop); return 1; |
1448 |
|
|
} |
1449 |
|
|
E = 0; |
1450 |
|
|
for (blIdx=0; blIdx<sbp->num; blIdx++) { |
1451 |
|
|
for (kvpIdx=0; kvpIdx<KVP_NUM; kvpIdx++) { |
1452 |
|
|
if (KVP_DGGSM_IDX != kvpIdx) { |
1453 |
|
|
if (!E) E |= nrrdKeyValueAdd(nblur[blIdx], _blurKey[kvpIdx], |
1454 |
|
|
blurVal[blIdx].val[kvpIdx]); |
1455 |
|
|
} else { |
1456 |
|
|
/* only need to save dgGoodSigmaMax if it was spatially blurred |
1457 |
|
|
with the discrete gaussian kernel */ |
1458 |
|
|
if (spatialBlurred |
1459 |
|
|
&& nrrdKernelDiscreteGaussian == kssb->kernel) { |
1460 |
|
|
if (!E) E |= nrrdKeyValueAdd(nblur[blIdx], _blurKey[kvpIdx], |
1461 |
|
|
blurVal[blIdx].val[kvpIdx]); |
1462 |
|
|
} |
1463 |
|
|
} |
1464 |
|
|
} |
1465 |
|
|
} |
1466 |
|
|
if (E) { |
1467 |
|
|
biffMovef(GAGE, NRRD, "%s: trouble adding KVPs", me); |
1468 |
|
|
airMopError(mop); return 1; |
1469 |
|
|
} |
1470 |
|
|
|
1471 |
|
|
airMopOkay(mop); |
1472 |
|
|
return 0; |
1473 |
|
|
} |
1474 |
|
|
|
1475 |
|
|
/* |
1476 |
|
|
******** gageStackBlurCheck |
1477 |
|
|
** |
1478 |
|
|
** (docs) |
1479 |
|
|
** |
1480 |
|
|
*/ |
1481 |
|
|
int |
1482 |
|
|
gageStackBlurCheck(const Nrrd *const nblur[], |
1483 |
|
|
gageStackBlurParm *sbp, |
1484 |
|
|
const Nrrd *nin, const gageKind *kind) { |
1485 |
|
|
static const char me[]="gageStackBlurCheck"; |
1486 |
|
|
gageShape *shapeOld, *shapeNew; |
1487 |
|
|
blurVal_t *blurVal; |
1488 |
|
|
airArray *mop; |
1489 |
|
|
unsigned int blIdx, kvpIdx; |
1490 |
|
|
NrrdKernelSpec *kssb; |
1491 |
|
|
|
1492 |
|
|
if (!(nblur && sbp && nin && kind)) { |
1493 |
|
|
biffAddf(GAGE, "%s: got NULL pointer", me); |
1494 |
|
|
return 1; |
1495 |
|
|
} |
1496 |
|
|
mop = airMopNew(); |
1497 |
|
|
kssb = nrrdKernelSpecCopy(sbp->kspec); |
1498 |
|
|
airMopAdd(mop, kssb, (airMopper)nrrdKernelSpecNix, airMopAlways); |
1499 |
|
|
if (gageStackBlurParmCheck(sbp) |
1500 |
|
|
|| _checkNrrd(NULL, nblur, sbp->num, AIR_TRUE, nin, kind) |
1501 |
|
|
|| (!( blurVal = _blurValAlloc(mop, sbp, kssb, nin, |
1502 |
|
|
(sbp->needSpatialBlur |
1503 |
|
|
? AIR_TRUE |
1504 |
|
|
: AIR_FALSE)) )) ) { |
1505 |
|
|
biffAddf(GAGE, "%s: problem", me); |
1506 |
|
|
airMopError(mop); return 1; |
1507 |
|
|
} |
1508 |
|
|
|
1509 |
|
|
shapeNew = gageShapeNew(); |
1510 |
|
|
airMopAdd(mop, shapeNew, (airMopper)gageShapeNix, airMopAlways); |
1511 |
|
|
if (gageShapeSet(shapeNew, nin, kind->baseDim)) { |
1512 |
|
|
biffAddf(GAGE, "%s: trouble setting up reference shape", me); |
1513 |
|
|
airMopError(mop); return 1; |
1514 |
|
|
} |
1515 |
|
|
shapeOld = gageShapeNew(); |
1516 |
|
|
airMopAdd(mop, shapeOld, (airMopper)gageShapeNix, airMopAlways); |
1517 |
|
|
|
1518 |
|
|
for (blIdx=0; blIdx<sbp->num; blIdx++) { |
1519 |
|
|
if (nin->type != nblur[blIdx]->type) { |
1520 |
|
|
biffAddf(GAGE, "%s: nblur[%u]->type %s != nin type %s\n", me, |
1521 |
|
|
blIdx, airEnumStr(nrrdType, nblur[blIdx]->type), |
1522 |
|
|
airEnumStr(nrrdType, nin->type)); |
1523 |
|
|
airMopError(mop); return 1; |
1524 |
|
|
} |
1525 |
|
|
/* check to see if nblur[blIdx] is as expected */ |
1526 |
|
|
if (gageShapeSet(shapeOld, nblur[blIdx], kind->baseDim) |
1527 |
|
|
|| !gageShapeEqual(shapeOld, "nblur", shapeNew, "nin")) { |
1528 |
|
|
biffAddf(GAGE, "%s: trouble, or nblur[%u] shape != nin shape", |
1529 |
|
|
me, blIdx); |
1530 |
|
|
airMopError(mop); return 1; |
1531 |
|
|
} |
1532 |
|
|
/* see if the KVPs are there with the required values */ |
1533 |
|
|
for (kvpIdx=0; kvpIdx<KVP_NUM; kvpIdx++) { |
1534 |
|
|
char *tmpval; |
1535 |
|
|
tmpval = nrrdKeyValueGet(nblur[blIdx], _blurKey[kvpIdx]); |
1536 |
|
|
airMopAdd(mop, tmpval, airFree, airMopAlways); |
1537 |
|
|
if (KVP_DGGSM_IDX != kvpIdx) { |
1538 |
|
|
if (!tmpval) { |
1539 |
|
|
biffAddf(GAGE, "%s: didn't see key \"%s\" in nblur[%u]", me, |
1540 |
|
|
_blurKey[kvpIdx], blIdx); |
1541 |
|
|
airMopError(mop); return 1; |
1542 |
|
|
} |
1543 |
|
|
if (KVP_SBLUR_IDX == kvpIdx) { |
1544 |
|
|
/* this KVP is handled differently */ |
1545 |
|
|
if (!sbp->needSpatialBlur) { |
1546 |
|
|
/* we don't care if it was frequency-domain or spatial-domain |
1547 |
|
|
blurring, so there's no right answer */ |
1548 |
|
|
continue; |
1549 |
|
|
} |
1550 |
|
|
/* else we do need spatial domain blurring; so do the check below */ |
1551 |
|
|
} |
1552 |
|
|
if (strcmp(tmpval, blurVal[blIdx].val[kvpIdx])) { |
1553 |
|
|
biffAddf(GAGE, "%s: found key[%s] \"%s\" != wanted \"%s\"", me, |
1554 |
|
|
_blurKey[kvpIdx], tmpval, blurVal[blIdx].val[kvpIdx]); |
1555 |
|
|
airMopError(mop); return 1; |
1556 |
|
|
} |
1557 |
|
|
} else { |
1558 |
|
|
/* KVP_DGGSM_IDX == kvpIdx; handled differently since KVP isn't saved |
1559 |
|
|
when not needed */ |
1560 |
|
|
if (!sbp->needSpatialBlur) { |
1561 |
|
|
/* if you don't care about needing spatial blurring, you |
1562 |
|
|
lose the right to care about a difference in dggsm */ |
1563 |
|
|
continue; |
1564 |
|
|
} |
1565 |
|
|
if (tmpval) { |
1566 |
|
|
/* therefore it was spatially blurred, so there's a saved DGGSM, |
1567 |
|
|
and we do need spatial blurring, so we compare them */ |
1568 |
|
|
if (strcmp(tmpval, blurVal[blIdx].val[kvpIdx])) { |
1569 |
|
|
biffAddf(GAGE, "%s: found key[%s] \"%s\" != wanted \"%s\"", me, |
1570 |
|
|
_blurKey[kvpIdx], tmpval, blurVal[blIdx].val[kvpIdx]); |
1571 |
|
|
/* HEY: a change in the discrete Gaussian cut-off will result |
1572 |
|
|
in a recomputation of the blurrings, even with FFT-based |
1573 |
|
|
blurring, though cut-off is completely moot then */ |
1574 |
|
|
airMopError(mop); return 1; |
1575 |
|
|
} |
1576 |
|
|
} |
1577 |
|
|
continue; |
1578 |
|
|
} |
1579 |
|
|
} |
1580 |
|
|
} |
1581 |
|
|
|
1582 |
|
|
airMopOkay(mop); |
1583 |
|
|
return 0; |
1584 |
|
|
} |
1585 |
|
|
|
1586 |
|
|
int |
1587 |
|
|
gageStackBlurGet(Nrrd *const nblur[], int *recomputedP, |
1588 |
|
|
gageStackBlurParm *sbp, |
1589 |
|
|
const char *format, |
1590 |
|
|
const Nrrd *nin, const gageKind *kind) { |
1591 |
|
|
static const char me[]="gageStackBlurGet"; |
1592 |
|
|
airArray *mop; |
1593 |
|
|
int recompute; |
1594 |
|
|
unsigned int ii; |
1595 |
|
|
|
1596 |
|
|
|
1597 |
|
|
if (!( nblur && sbp && nin && kind )) { |
1598 |
|
|
biffAddf(GAGE, "%s: got NULL pointer", me); |
1599 |
|
|
return 1; |
1600 |
|
|
} |
1601 |
|
|
for (ii=0; ii<sbp->num; ii++) { |
1602 |
|
|
if (!nblur[ii]) { |
1603 |
|
|
biffAddf(GAGE, "%s: nblur[%u] NULL", me, ii); |
1604 |
|
|
return 1; |
1605 |
|
|
} |
1606 |
|
|
} |
1607 |
|
|
if (gageStackBlurParmCheck(sbp)) { |
1608 |
|
|
biffAddf(GAGE, "%s: trouble with blur parms", me); |
1609 |
|
|
return 1; |
1610 |
|
|
} |
1611 |
|
|
mop = airMopNew(); |
1612 |
|
|
|
1613 |
|
|
/* set recompute flag */ |
1614 |
|
|
if (!airStrlen(format)) { |
1615 |
|
|
/* no info about files to load, obviously have to recompute */ |
1616 |
|
|
if (sbp->verbose) { |
1617 |
|
|
fprintf(stderr, "%s: no file info, must recompute blurrings\n", me); |
1618 |
|
|
} |
1619 |
|
|
recompute = AIR_TRUE; |
1620 |
|
|
} else { |
1621 |
|
|
char *fname, *suberr; |
1622 |
|
|
int firstExists; |
1623 |
|
|
FILE *file; |
1624 |
|
|
/* do have info about files to load, but may fail in many ways */ |
1625 |
|
|
fname = AIR_CALLOC(strlen(format) + AIR_STRLEN_SMALL, char); |
1626 |
|
|
if (!fname) { |
1627 |
|
|
biffAddf(GAGE, "%s: couldn't allocate fname", me); |
1628 |
|
|
airMopError(mop); return 1; |
1629 |
|
|
} |
1630 |
|
|
airMopAdd(mop, fname, airFree, airMopAlways); |
1631 |
|
|
/* see if we can get the first file (number 0) */ |
1632 |
|
|
sprintf(fname, format, 0); |
1633 |
|
|
firstExists = !!(file = fopen(fname, "r")); |
1634 |
|
|
airFclose(file); |
1635 |
|
|
if (!firstExists) { |
1636 |
|
|
if (sbp->verbose) { |
1637 |
|
|
fprintf(stderr, "%s: no file \"%s\"; will recompute blurrings\n", |
1638 |
|
|
me, fname); |
1639 |
|
|
} |
1640 |
|
|
recompute = AIR_TRUE; |
1641 |
|
|
} else if (nrrdLoadMulti(nblur, sbp->num, format, 0, NULL)) { |
1642 |
|
|
airMopAdd(mop, suberr = biffGetDone(NRRD), airFree, airMopAlways); |
1643 |
|
|
if (sbp->verbose) { |
1644 |
|
|
fprintf(stderr, "%s: will recompute blurrings that couldn't be " |
1645 |
|
|
"read:\n%s\n", me, suberr); |
1646 |
|
|
} |
1647 |
|
|
recompute = AIR_TRUE; |
1648 |
|
|
} else if (gageStackBlurCheck(AIR_CAST(const Nrrd*const*, nblur), |
1649 |
|
|
sbp, nin, kind)) { |
1650 |
|
|
airMopAdd(mop, suberr = biffGetDone(GAGE), airFree, airMopAlways); |
1651 |
|
|
if (sbp->verbose) { |
1652 |
|
|
fprintf(stderr, "%s: will recompute blurrings (from \"%s\") " |
1653 |
|
|
"that don't match:\n%s\n", me, format, suberr); |
1654 |
|
|
} |
1655 |
|
|
recompute = AIR_TRUE; |
1656 |
|
|
} else { |
1657 |
|
|
/* else precomputed blurrings could all be read, and did match */ |
1658 |
|
|
if (sbp->verbose) { |
1659 |
|
|
fprintf(stderr, "%s: will reuse %u %s pre-blurrings.\n", me, |
1660 |
|
|
sbp->num, format); |
1661 |
|
|
} |
1662 |
|
|
recompute = AIR_FALSE; |
1663 |
|
|
} |
1664 |
|
|
} |
1665 |
|
|
if (recompute) { |
1666 |
|
|
if (gageStackBlur(nblur, sbp, nin, kind)) { |
1667 |
|
|
biffAddf(GAGE, "%s: trouble computing blurrings", me); |
1668 |
|
|
airMopError(mop); return 1; |
1669 |
|
|
} |
1670 |
|
|
} |
1671 |
|
|
if (recomputedP) { |
1672 |
|
|
*recomputedP = recompute; |
1673 |
|
|
} |
1674 |
|
|
|
1675 |
|
|
airMopOkay(mop); |
1676 |
|
|
return 0; |
1677 |
|
|
} |
1678 |
|
|
|
1679 |
|
|
/* |
1680 |
|
|
******** gageStackBlurManage |
1681 |
|
|
** |
1682 |
|
|
** does the work of gageStackBlurGet and then some: |
1683 |
|
|
** allocates the array of Nrrds, allocates an array of doubles for scale, |
1684 |
|
|
** and saves output if recomputed |
1685 |
|
|
*/ |
1686 |
|
|
int |
1687 |
|
|
gageStackBlurManage(Nrrd ***nblurP, int *recomputedP, |
1688 |
|
|
gageStackBlurParm *sbp, |
1689 |
|
|
const char *format, |
1690 |
|
|
int saveIfComputed, NrrdEncoding *enc, |
1691 |
|
|
const Nrrd *nin, const gageKind *kind) { |
1692 |
|
|
static const char me[]="gageStackBlurManage"; |
1693 |
|
|
Nrrd **nblur; |
1694 |
|
|
unsigned int ii; |
1695 |
|
|
airArray *mop; |
1696 |
|
|
int recomputed; |
1697 |
|
|
|
1698 |
|
|
if (!( nblurP && sbp && nin && kind )) { |
1699 |
|
|
biffAddf(GAGE, "%s: got NULL pointer", me); |
1700 |
|
|
return 1; |
1701 |
|
|
} |
1702 |
|
|
nblur = *nblurP = AIR_CALLOC(sbp->num, Nrrd *); |
1703 |
|
|
if (!nblur) { |
1704 |
|
|
biffAddf(GAGE, "%s: couldn't alloc %u Nrrd*s", me, sbp->num); |
1705 |
|
|
return 1; |
1706 |
|
|
} |
1707 |
|
|
|
1708 |
|
|
mop = airMopNew(); |
1709 |
|
|
airMopAdd(mop, nblurP, (airMopper)airSetNull, airMopOnError); |
1710 |
|
|
airMopAdd(mop, *nblurP, airFree, airMopOnError); |
1711 |
|
|
for (ii=0; ii<sbp->num; ii++) { |
1712 |
|
|
nblur[ii] = nrrdNew(); |
1713 |
|
|
airMopAdd(mop, nblur[ii], (airMopper)nrrdNuke, airMopOnError); |
1714 |
|
|
} |
1715 |
|
|
if (gageStackBlurGet(nblur, &recomputed, sbp, format, nin, kind)) { |
1716 |
|
|
biffAddf(GAGE, "%s: trouble getting nblur", me); |
1717 |
|
|
airMopError(mop); return 1; |
1718 |
|
|
} |
1719 |
|
|
if (recomputedP) { |
1720 |
|
|
*recomputedP = recomputed; |
1721 |
|
|
} |
1722 |
|
|
if (recomputed && format && saveIfComputed) { |
1723 |
|
|
NrrdIoState *nio; |
1724 |
|
|
int E; |
1725 |
|
|
E = 0; |
1726 |
|
|
if (enc) { |
1727 |
|
|
if (!enc->available()) { |
1728 |
|
|
biffAddf(GAGE, "%s: requested %s encoding which is not " |
1729 |
|
|
"available in this build", me, enc->name); |
1730 |
|
|
airMopError(mop); return 1; |
1731 |
|
|
} |
1732 |
|
|
nio = nrrdIoStateNew(); |
1733 |
|
|
airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways); |
1734 |
|
|
if (!E) E |= nrrdIoStateEncodingSet(nio, nrrdEncodingGzip); |
1735 |
|
|
} else { |
1736 |
|
|
nio = NULL; |
1737 |
|
|
} |
1738 |
|
|
if (!E) E |= nrrdSaveMulti(format, AIR_CAST(const Nrrd *const *, |
1739 |
|
|
nblur), |
1740 |
|
|
sbp->num, 0, nio); |
1741 |
|
|
if (E) { |
1742 |
|
|
biffMovef(GAGE, NRRD, "%s: trouble saving blurrings", me); |
1743 |
|
|
airMopError(mop); return 1; |
1744 |
|
|
} |
1745 |
|
|
} |
1746 |
|
|
|
1747 |
|
|
airMopOkay(mop); |
1748 |
|
|
return 0; |
1749 |
|
|
} |