1 /*
2 Teem: Tools to process and visualize scientific data and images .
3 Copyright (C) 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 /*
28 ** does not use biff
29 */
30 gageStackBlurParm *
gageStackBlurParmNew()31 gageStackBlurParmNew() {
32 gageStackBlurParm *parm;
33
34 parm = AIR_CALLOC(1, gageStackBlurParm);
35 if (parm) {
36 parm->scale = NULL;
37 parm->sigmaMax = gageDefStackBlurSigmaMax;
38 parm->padValue = AIR_NAN;
39 parm->kspec = NULL;
40 parm->dataCheck = AIR_TRUE;
41 parm->boundary = nrrdBoundaryUnknown;
42 parm->renormalize = AIR_FALSE;
43 parm->verbose = 0;
44 }
45 return parm;
46 }
47
48 gageStackBlurParm *
gageStackBlurParmNix(gageStackBlurParm * sbp)49 gageStackBlurParmNix(gageStackBlurParm *sbp) {
50
51 if (sbp) {
52 airFree(sbp->scale);
53 nrrdKernelSpecNix(sbp->kspec);
54 free(sbp);
55 }
56 return NULL;
57 }
58
59 int
gageStackBlurParmScaleSet(gageStackBlurParm * sbp,unsigned int num,double scaleMin,double scaleMax,int uniform,int optim)60 gageStackBlurParmScaleSet(gageStackBlurParm *sbp, unsigned int num,
61 double scaleMin, double scaleMax,
62 int uniform, int optim) {
63 static const char me[]="gageStackBlurParmScaleSet";
64 unsigned int ii;
65
66 if (!( sbp )) {
67 biffAddf(GAGE, "%s: got NULL pointer", me);
68 return 1;
69 }
70 airFree(sbp->scale);
71 sbp->scale = NULL;
72 if (!( scaleMin < scaleMax )) {
73 biffAddf(GAGE, "%s: scaleMin %g not < scaleMax %g", me,
74 scaleMin, scaleMax);
75 return 1;
76 }
77 sbp->scale = AIR_CALLOC(num, double);
78 if (!sbp->scale) {
79 biffAddf(GAGE, "%s: couldn't alloc scale for %u", me, num);
80 return 1;
81 }
82 sbp->num = num;
83
84 if (uniform) {
85 for (ii=0; ii<num; ii++) {
86 sbp->scale[ii] = AIR_AFFINE(0, ii, num-1, scaleMin, scaleMax);
87 }
88 } else if (!optim) {
89 double tau0, tau1, tau;
90 tau0 = gageTauOfSig(scaleMin);
91 tau1 = gageTauOfSig(scaleMax);
92 for (ii=0; ii<num; ii++) {
93 tau = AIR_AFFINE(0, ii, num-1, tau0, tau1);
94 sbp->scale[ii] = gageSigOfTau(tau);
95 }
96 } else {
97 unsigned int sigmax;
98 sigmax = AIR_CAST(unsigned int, scaleMax);
99 if (!( 0 == scaleMin && sigmax == scaleMax )) {
100 biffAddf(GAGE, "%s: range [%g,%g] not [0,N] w/ integral N", me,
101 scaleMin, scaleMax);
102 return 1;
103 }
104 if (gageOptimSigSet(sbp->scale, num, sigmax)) {
105 biffAddf(GAGE, "%s: trouble w/ optimal sigmas", me);
106 return 1;
107 }
108 }
109
110 return 0;
111 }
112
113 int
gageStackBlurParmKernelSet(gageStackBlurParm * sbp,const NrrdKernelSpec * kspec,int renormalize)114 gageStackBlurParmKernelSet(gageStackBlurParm *sbp,
115 const NrrdKernelSpec *kspec,
116 int renormalize) {
117 static const char me[]="gageStackBlurParmKernelSet";
118
119 if (!( sbp && kspec )) {
120 biffAddf(GAGE, "%s: got NULL pointer", me);
121 return 1;
122 }
123 nrrdKernelSpecNix(sbp->kspec);
124 sbp->kspec = nrrdKernelSpecCopy(kspec);
125 sbp->renormalize = renormalize;
126 return 0;
127 }
128
129 int
gageStackBlurParmBoundarySet(gageStackBlurParm * sbp,int boundary,double padValue)130 gageStackBlurParmBoundarySet(gageStackBlurParm *sbp,
131 int boundary, double padValue) {
132 static const char me[]="gageStackBlurParmBoundarySet";
133
134 if (!sbp) {
135 biffAddf(GAGE, "%s: got NULL pointer", me);
136 return 1;
137 }
138 if (airEnumValCheck(nrrdBoundary, boundary)) {
139 biffAddf(GAGE, "%s: %d not a known %s", me,
140 boundary, nrrdBoundary->name);
141 return 1;
142 }
143 if (nrrdBoundaryPad == boundary && !AIR_EXISTS(padValue)) {
144 biffAddf(GAGE, "%s: want boundary %s but padValue %g doesn't exist", me,
145 airEnumStr(nrrdBoundary, boundary), padValue);
146 return 1;
147 }
148 sbp->boundary = boundary;
149 sbp->padValue = padValue;
150 return 0;
151 }
152
153 int
gageStackBlurParmVerboseSet(gageStackBlurParm * sbp,int verbose)154 gageStackBlurParmVerboseSet(gageStackBlurParm *sbp, int verbose) {
155 static const char me[]="gageStackBlurParmVerboseSet";
156
157 if (!sbp) {
158 biffAddf(GAGE, "%s: got NULL pointer", me);
159 return 1;
160 }
161 sbp->verbose = verbose;
162 return 0;
163 }
164
165 int
gageStackBlurParmCheck(gageStackBlurParm * sbp)166 gageStackBlurParmCheck(gageStackBlurParm *sbp) {
167 static const char me[]="gageStackBlurParmCheck";
168 unsigned int ii;
169
170 if (!sbp) {
171 biffAddf(GAGE, "%s: got NULL pointer", me);
172 return 1;
173 }
174 if (!( sbp->scale && sbp->kspec )) {
175 biffAddf(GAGE, "%s: scale and kernel aren't set", me);
176 return 1;
177 }
178 if (!( sbp->num >= 2)) {
179 biffAddf(GAGE, "%s: need num >= 2, not %u", me, sbp->num);
180 return 1;
181 }
182 for (ii=0; ii<sbp->num; ii++) {
183 if (!AIR_EXISTS(sbp->scale[ii])) {
184 biffAddf(GAGE, "%s: scale[%u] = %g doesn't exist", me, ii,
185 sbp->scale[ii]);
186 return 1;
187 }
188 if (ii) {
189 if (!( sbp->scale[ii-1] < sbp->scale[ii] )) {
190 biffAddf(GAGE, "%s: scale[%u] = %g not < scale[%u] = %g", me,
191 ii, sbp->scale[ii-1], ii+1, sbp->scale[ii]);
192 return 1;
193 }
194 }
195 }
196 if (airEnumValCheck(nrrdBoundary, sbp->boundary)) {
197 biffAddf(GAGE, "%s: %d not a known %s", me,
198 sbp->boundary, nrrdBoundary->name);
199 return 1;
200 }
201 return 0;
202 }
203
204 static int
_checkNrrd(Nrrd * const nblur[],const Nrrd * const ncheck[],unsigned int blNum,int checking,const Nrrd * nin,const gageKind * kind)205 _checkNrrd(Nrrd *const nblur[], const Nrrd *const ncheck[],
206 unsigned int blNum, int checking,
207 const Nrrd *nin, const gageKind *kind) {
208 static const char me[]="_checkNrrd";
209 unsigned int blIdx;
210
211 for (blIdx=0; blIdx<blNum; blIdx++) {
212 if (checking) {
213 if (nrrdCheck(ncheck[blIdx])) {
214 biffMovef(GAGE, NRRD, "%s: bad ncheck[%u]", me, blIdx);
215 return 1;
216 }
217 } else {
218 if (!nblur[blIdx]) {
219 biffAddf(GAGE, "%s: NULL blur[%u]", me, blIdx);
220 return 1;
221 }
222 }
223 }
224 if (3 + kind->baseDim != nin->dim) {
225 biffAddf(GAGE, "%s: need nin->dim %u (not %u) with baseDim %u", me,
226 3 + kind->baseDim, nin->dim, kind->baseDim);
227 return 1;
228 }
229 return 0;
230 }
231
232 #define KVP_NUM 5
233
234 static const char /* 0 1 2 3 4 */
235 _blurKey[KVP_NUM][AIR_STRLEN_LARGE] = {"gageStackBlur", "scale", "kernel", "boundary", "renormalize"};
236
237 typedef struct {
238 char val[KVP_NUM][AIR_STRLEN_LARGE];
239 } blurVal_t;
240
241 static blurVal_t *
_blurValAlloc(airArray * mop,gageStackBlurParm * sbp)242 _blurValAlloc(airArray *mop, gageStackBlurParm *sbp) {
243 static const char me[]="_blurValAlloc";
244 blurVal_t *blurVal;
245 unsigned int blIdx;
246
247 blurVal = AIR_CAST(blurVal_t *, calloc(sbp->num, sizeof(blurVal_t)));
248 if (!blurVal) {
249 biffAddf(GAGE, "%s: couldn't alloc blurVal for %u", me, sbp->num);
250 return NULL;
251 }
252 for (blIdx=0; blIdx<sbp->num; blIdx++) {
253 sbp->kspec->parm[0] = sbp->scale[blIdx];
254 sprintf(blurVal[blIdx].val[0], "true");
255 sprintf(blurVal[blIdx].val[1], "%g", sbp->scale[blIdx]);
256 nrrdKernelSpecSprint(blurVal[blIdx].val[2], sbp->kspec);
257 sprintf(blurVal[blIdx].val[3], "%s", sbp->renormalize ? "true" : "false");
258 sprintf(blurVal[blIdx].val[4], "%s",
259 airEnumStr(nrrdBoundary, sbp->boundary));
260 }
261 airMopAdd(mop, blurVal, airFree, airMopAlways);
262 return blurVal;
263 }
264
265 /*
266 ** little helper function to do pre-blurring of a given nrrd
267 ** of the sort that might be useful for scale-space gage use
268 **
269 ** nblur has to already be allocated for "blNum" Nrrd*s, AND, they all
270 ** have to point to valid (possibly empty) Nrrds, so they can hold the
271 ** results of blurring
272 */
273 int
gageStackBlur(Nrrd * const nblur[],gageStackBlurParm * sbp,const Nrrd * nin,const gageKind * kind)274 gageStackBlur(Nrrd *const nblur[], gageStackBlurParm *sbp,
275 const Nrrd *nin, const gageKind *kind) {
276 static const char me[]="gageStackBlur";
277 unsigned int blIdx, axi;
278 NrrdResampleContext *rsmc;
279 blurVal_t *blurVal;
280 airArray *mop;
281 int E;
282 Nrrd *ntmp;
283
284 if (!(nblur && sbp && nin && kind)) {
285 biffAddf(GAGE, "%s: got NULL pointer", me);
286 return 1;
287 }
288 mop = airMopNew();
289 if (gageStackBlurParmCheck(sbp)
290 || _checkNrrd(nblur, NULL, sbp->num, AIR_FALSE, nin, kind)
291 || (!( blurVal = _blurValAlloc(mop, sbp) )) ) {
292 biffAddf(GAGE, "%s: problem", me);
293 airMopError(mop); return 1;
294 }
295 rsmc = nrrdResampleContextNew();
296 airMopAdd(mop, rsmc, (airMopper)nrrdResampleContextNix, airMopAlways);
297 /* may or may not be needed for iterative diffusion */
298 ntmp = nrrdNew();
299 airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways);
300
301 E = 0;
302 if (!E) E |= nrrdResampleDefaultCenterSet(rsmc, nrrdDefaultCenter);
303 if (!E) E |= nrrdResampleInputSet(rsmc, nin);
304 if (kind->baseDim) {
305 unsigned int bai;
306 for (bai=0; bai<kind->baseDim; bai++) {
307 if (!E) E |= nrrdResampleKernelSet(rsmc, bai, NULL, NULL);
308 }
309 }
310 for (axi=0; axi<3; axi++) {
311 if (!E) E |= nrrdResampleSamplesSet(rsmc, kind->baseDim + axi,
312 nin->axis[kind->baseDim + axi].size);
313 if (!E) E |= nrrdResampleRangeFullSet(rsmc, kind->baseDim + axi);
314 }
315 if (!E) E |= nrrdResampleBoundarySet(rsmc, sbp->boundary);
316 if (!E) E |= nrrdResampleTypeOutSet(rsmc, nrrdTypeDefault);
317 if (!E) E |= nrrdResampleRenormalizeSet(rsmc, sbp->renormalize);
318 if (E) {
319 biffAddf(GAGE, "%s: trouble setting up resampling", me);
320 airMopError(mop); return 1;
321 }
322
323 for (blIdx=0; blIdx<sbp->num; blIdx++) {
324 unsigned int kvpIdx;
325 if (sbp->verbose) {
326 fprintf(stderr, "%s: blurring %u / %u (scale %g) ... ", me, blIdx,
327 sbp->num, sbp->scale[blIdx]);
328 fflush(stderr);
329 }
330 if (nrrdKernelDiscreteGaussian == sbp->kspec->kernel
331 && sbp->scale[blIdx] > sbp->sigmaMax) {
332 double timeLeft, /* amount of diffusion time left to do */
333 timeStep, /* length of diffusion time per blur */
334 timeDo; /* amount of diffusion time just applied */
335 unsigned int passIdx = 0;
336 timeLeft = sbp->scale[blIdx]*sbp->scale[blIdx];
337 timeStep = (sbp->sigmaMax)*(sbp->sigmaMax);
338 if (sbp->verbose) {
339 fprintf(stderr, "\n");
340 fprintf(stderr, "%s: scale %g > sigmaMax %g\n", me,
341 sbp->scale[blIdx], sbp->sigmaMax);
342 fprintf(stderr, "%s: diffusing for time %g in steps of %g\n", me,
343 timeLeft, timeStep);
344 fflush(stderr);
345 }
346 do {
347 if (!passIdx) {
348 if (!E) E |= nrrdResampleInputSet(rsmc, nin);
349 } else {
350 if (!E) E |= nrrdResampleInputSet(rsmc, ntmp);
351 }
352 timeDo = (timeLeft > timeStep
353 ? timeStep
354 : timeLeft);
355 sbp->kspec->parm[0] = sqrt(timeDo);
356 for (axi=0; axi<3; axi++) {
357 if (!E) E |= nrrdResampleKernelSet(rsmc, kind->baseDim + axi,
358 sbp->kspec->kernel,
359 sbp->kspec->parm);
360 }
361 if (sbp->verbose) {
362 fprintf(stderr, " pass %u (timeLeft=%g => time=%g, sigma=%g) ...\n",
363 passIdx, timeLeft, timeDo, sbp->kspec->parm[0]);
364 }
365 if (!E) E |= nrrdResampleExecute(rsmc, ntmp);
366 timeLeft -= timeDo;
367 passIdx++;
368 } while (!E && timeLeft > 0.0);
369 if (!E) E |= nrrdCopy(nblur[blIdx], ntmp);
370 } else {
371 /* do blurring in one shot as usual */
372 sbp->kspec->parm[0] = sbp->scale[blIdx];
373 for (axi=0; axi<3; axi++) {
374 if (!E) E |= nrrdResampleKernelSet(rsmc, kind->baseDim + axi,
375 sbp->kspec->kernel,
376 sbp->kspec->parm);
377 }
378 if (!E) E |= nrrdResampleExecute(rsmc, nblur[blIdx]);
379 }
380 if (E) {
381 if (sbp->verbose) {
382 fprintf(stderr, "problem!\n");
383 }
384 biffAddf(GAGE, "%s: trouble w/ %u of %u (scale %g)",
385 me, blIdx, sbp->num, sbp->scale[blIdx]);
386 airMopError(mop); return 1;
387 }
388 if (sbp->verbose) {
389 fprintf(stderr, "done.\n");
390 }
391 /* add the KVPs to document how these were blurred */
392 for (kvpIdx=0; kvpIdx<KVP_NUM; kvpIdx++) {
393 if (!E) E |= nrrdKeyValueAdd(nblur[blIdx], _blurKey[kvpIdx],
394 blurVal[blIdx].val[kvpIdx]);
395 }
396 }
397
398 airMopOkay(mop);
399 return 0;
400 }
401
402 /*
403 ******** gageStackBlurCheck
404 **
405 ** (docs)
406 **
407 ** on why sbp->dataCheck should be non-zero: really need to check that
408 ** the data values themselves are correct; its too dangerous to have
409 ** this unchecked, because it means that experimental changes in
410 ** volumes could mysteriously have no effect, because the cached
411 ** pre-blurred volumes from the old data are being re-used
412 */
413 int
gageStackBlurCheck(const Nrrd * const nblur[],gageStackBlurParm * sbp,const Nrrd * nin,const gageKind * kind)414 gageStackBlurCheck(const Nrrd *const nblur[],
415 gageStackBlurParm *sbp,
416 const Nrrd *nin, const gageKind *kind) {
417 static const char me[]="gageStackBlurCheck";
418 gageShape *shapeOld, *shapeNew;
419 blurVal_t *blurVal;
420 airArray *mop;
421 unsigned int blIdx, kvpIdx;
422
423 if (!(nblur && sbp && nin && kind)) {
424 biffAddf(GAGE, "%s: got NULL pointer", me);
425 return 1;
426 }
427 mop = airMopNew();
428 if (gageStackBlurParmCheck(sbp)
429 || _checkNrrd(NULL, nblur, sbp->num, AIR_TRUE, nin, kind)
430 || (!( blurVal = _blurValAlloc(mop, sbp) )) ) {
431 biffAddf(GAGE, "%s: problem", me);
432 airMopError(mop); return 1;
433 }
434
435 shapeNew = gageShapeNew();
436 airMopAdd(mop, shapeNew, (airMopper)gageShapeNix, airMopAlways);
437 if (gageShapeSet(shapeNew, nin, kind->baseDim)) {
438 biffAddf(GAGE, "%s: trouble setting up reference shape", me);
439 airMopError(mop); return 1;
440 }
441 shapeOld = gageShapeNew();
442 airMopAdd(mop, shapeOld, (airMopper)gageShapeNix, airMopAlways);
443
444 for (blIdx=0; blIdx<sbp->num; blIdx++) {
445 if (nin->type != nblur[blIdx]->type) {
446 biffAddf(GAGE, "%s: nblur[%u]->type %s != nin type %s\n", me,
447 blIdx, airEnumStr(nrrdType, nblur[blIdx]->type),
448 airEnumStr(nrrdType, nin->type));
449 airMopError(mop); return 1;
450 }
451 /* check to see if nblur[blIdx] is as expected */
452 if (gageShapeSet(shapeOld, nblur[blIdx], kind->baseDim)
453 || !gageShapeEqual(shapeOld, "nblur", shapeNew, "nin")) {
454 biffAddf(GAGE, "%s: trouble, or nblur[%u] shape != nin shape",
455 me, blIdx);
456 airMopError(mop); return 1;
457 }
458 /* see if the KVPs are already there */
459 for (kvpIdx=0; kvpIdx<KVP_NUM; kvpIdx++) {
460 char *tmpval;
461 tmpval = nrrdKeyValueGet(nblur[blIdx], _blurKey[kvpIdx]);
462 if (!tmpval) {
463 biffAddf(GAGE, "%s: didn't see key \"%s\" in nblur[%u]", me,
464 _blurKey[kvpIdx], blIdx);
465 airMopError(mop); return 1;
466 }
467 airMopAdd(mop, tmpval, airFree, airMopAlways);
468 if (strcmp(tmpval, blurVal[blIdx].val[kvpIdx])) {
469 biffAddf(GAGE, "%s: found key[%s] \"%s\" != wanted \"%s\"", me,
470 _blurKey[kvpIdx], tmpval, blurVal[blIdx].val[kvpIdx]);
471 airMopError(mop); return 1;
472 }
473 }
474 }
475 if (sbp->dataCheck) {
476 double (*lup)(const void *, size_t), vin, vbl;
477 size_t II, NN;
478 if (!(0.0 == sbp->scale[0])) {
479 biffAddf(GAGE, "%s: sorry, dataCheck w/ scale[0] %g "
480 "!= 0.0 not implemented", me, sbp->scale[0]);
481 airMopError(mop); return 1;
482 /* so the non-zero return here will be acted upon as though there
483 was a difference between the desired and the current stack,
484 so things will be recomputed, which is conservative but costly */
485 }
486 lup = nrrdDLookup[nin->type];
487 NN = nrrdElementNumber(nin);
488 for (II=0; II<NN; II++) {
489 vin = lup(nin->data, II);
490 vbl = lup(nblur[0]->data, II);
491 if (vin != vbl) {
492 biffAddf(GAGE, "%s: value[%u] in nin %g != in nblur[0] %g\n", me,
493 AIR_CAST(unsigned int, II), vin, vbl);
494 airMopError(mop); return 1;
495 }
496 }
497 }
498
499 airMopOkay(mop);
500 return 0;
501 }
502
503 int
gageStackBlurGet(Nrrd * const nblur[],int * recomputedP,gageStackBlurParm * sbp,const char * format,const Nrrd * nin,const gageKind * kind)504 gageStackBlurGet(Nrrd *const nblur[], int *recomputedP,
505 gageStackBlurParm *sbp,
506 const char *format,
507 const Nrrd *nin, const gageKind *kind) {
508 static const char me[]="gageStackBlurGet";
509 airArray *mop;
510 int recompute;
511 unsigned int ii;
512
513 if (!( nblur && sbp && nin && kind )) {
514 biffAddf(GAGE, "%s: got NULL pointer", me);
515 return 1;
516 }
517 for (ii=0; ii<sbp->num; ii++) {
518 if (!nblur[ii]) {
519 biffAddf(GAGE, "%s: nblur[%u] NULL", me, ii);
520 return 1;
521 }
522 }
523 if (gageStackBlurParmCheck(sbp)) {
524 biffAddf(GAGE, "%s: trouble with blur parms", me);
525 return 1;
526 }
527 mop = airMopNew();
528
529 /* set recompute flag */
530 if (!airStrlen(format)) {
531 /* no info about files to load, obviously have to recompute */
532 if (sbp->verbose) {
533 fprintf(stderr, "%s: no file info, must recompute blurrings\n", me);
534 }
535 recompute = AIR_TRUE;
536 } else {
537 char *fname, *suberr;
538 int firstExists;
539 FILE *file;
540 /* do have info about files to load, but may fail in many ways */
541 fname = AIR_CALLOC(strlen(format) + AIR_STRLEN_SMALL, char);
542 if (!fname) {
543 biffAddf(GAGE, "%s: couldn't allocate fname", me);
544 airMopError(mop); return 1;
545 }
546 airMopAdd(mop, fname, airFree, airMopAlways);
547 /* see if we can get the first file (number 0) */
548 sprintf(fname, format, 0);
549 firstExists = !!(file = fopen(fname, "r"));
550 airFclose(file);
551 if (!firstExists) {
552 if (sbp->verbose) {
553 fprintf(stderr, "%s: no file \"%s\"; will recompute blurrings\n",
554 me, fname);
555 }
556 recompute = AIR_TRUE;
557 } else if (nrrdLoadMulti(nblur, sbp->num, format, 0, NULL)) {
558 airMopAdd(mop, suberr = biffGetDone(NRRD), airFree, airMopAlways);
559 if (sbp->verbose) {
560 fprintf(stderr, "%s: will recompute blurrings that couldn't be "
561 "read:\n%s\n", me, suberr);
562 }
563 recompute = AIR_TRUE;
564 } else if (gageStackBlurCheck(AIR_CAST(const Nrrd*const*, nblur),
565 sbp, nin, kind)) {
566 airMopAdd(mop, suberr = biffGetDone(GAGE), airFree, airMopAlways);
567 if (sbp->verbose) {
568 fprintf(stderr, "%s: will recompute blurrings (from \"%s\") "
569 "that don't match:\n%s\n", me, format, suberr);
570 }
571 recompute = AIR_TRUE;
572 } else {
573 /* else precomputed blurrings could all be read, and did match */
574 if (sbp->verbose) {
575 fprintf(stderr, "%s: will reuse %u %s pre-blurrings.\n", me,
576 sbp->num, format);
577 }
578 recompute = AIR_FALSE;
579 }
580 }
581 if (recompute) {
582 if (gageStackBlur(nblur, sbp, nin, kind)) {
583 biffAddf(GAGE, "%s: trouble computing blurrings", me);
584 airMopError(mop); return 1;
585 }
586 }
587 if (recomputedP) {
588 *recomputedP = recompute;
589 }
590
591 airMopOkay(mop);
592 return 0;
593 }
594
595 /*
596 ******** gageStackBlurManage
597 **
598 ** does the work of gageStackBlurGet and then some:
599 ** allocates the array of Nrrds, allocates an array of doubles for scale,
600 ** and saves output if recomputed
601 */
602 int
gageStackBlurManage(Nrrd *** nblurP,int * recomputedP,gageStackBlurParm * sbp,const char * format,int saveIfComputed,NrrdEncoding * enc,const Nrrd * nin,const gageKind * kind)603 gageStackBlurManage(Nrrd ***nblurP, int *recomputedP,
604 gageStackBlurParm *sbp,
605 const char *format,
606 int saveIfComputed, NrrdEncoding *enc,
607 const Nrrd *nin, const gageKind *kind) {
608 static const char me[]="gageStackBlurManage";
609 Nrrd **nblur;
610 unsigned int ii;
611 airArray *mop;
612 int recomputed;
613
614 if (!( nblurP && sbp && nin && kind )) {
615 biffAddf(GAGE, "%s: got NULL pointer", me);
616 return 1;
617 }
618 nblur = *nblurP = AIR_CALLOC(sbp->num, Nrrd *);
619 if (!nblur) {
620 biffAddf(GAGE, "%s: couldn't alloc %u Nrrd*s", me, sbp->num);
621 return 1;
622 }
623
624 mop = airMopNew();
625 airMopAdd(mop, nblurP, (airMopper)airSetNull, airMopOnError);
626 airMopAdd(mop, *nblurP, airFree, airMopOnError);
627 for (ii=0; ii<sbp->num; ii++) {
628 nblur[ii] = nrrdNew();
629 airMopAdd(mop, nblur[ii], (airMopper)nrrdNuke, airMopOnError);
630 }
631 if (gageStackBlurGet(nblur, &recomputed, sbp, format, nin, kind)) {
632 biffAddf(GAGE, "%s: trouble getting nblur", me);
633 airMopError(mop); return 1;
634 }
635 if (recomputedP) {
636 *recomputedP = recomputed;
637 }
638 if (recomputed && format && saveIfComputed) {
639 NrrdIoState *nio;
640 int E;
641 E = 0;
642 if (enc) {
643 if (!enc->available()) {
644 biffAddf(GAGE, "%s: requested %s encoding which is not "
645 "available in this build", me, enc->name);
646 airMopError(mop); return 1;
647 }
648 nio = nrrdIoStateNew();
649 airMopAdd(mop, nio, (airMopper)nrrdIoStateNix, airMopAlways);
650 if (!E) E |= nrrdIoStateEncodingSet(nio, nrrdEncodingGzip);
651 } else {
652 nio = NULL;
653 }
654 if (!E) E |= nrrdSaveMulti(format, AIR_CAST(const Nrrd *const *,
655 nblur),
656 sbp->num, 0, nio);
657 if (E) {
658 biffMovef(GAGE, NRRD, "%s: trouble saving blurrings", me);
659 airMopError(mop); return 1;
660 }
661 }
662
663 airMopOkay(mop);
664 return 0;
665 }
666