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 "meet.h" |
25 |
|
|
|
26 |
|
|
meetPullVol * |
27 |
|
|
meetPullVolNew(void) { |
28 |
|
|
meetPullVol *ret; |
29 |
|
|
|
30 |
|
|
ret = AIR_CALLOC(1, meetPullVol); |
31 |
|
|
if (ret) { |
32 |
|
|
ret->kind = NULL; |
33 |
|
|
ret->fileName = ret->volName = NULL; |
34 |
|
|
ret->sbp = NULL; |
35 |
|
|
ret->leeching = AIR_FALSE; |
36 |
|
|
ret->derivNormSS = AIR_FALSE; |
37 |
|
|
ret->recomputedSS = AIR_FALSE; |
38 |
|
|
ret->derivNormBiasSS = 0.0; |
39 |
|
|
ret->nin = NULL; |
40 |
|
|
ret->ninSS = NULL; |
41 |
|
|
} |
42 |
|
|
return ret; |
43 |
|
|
} |
44 |
|
|
|
45 |
|
|
/* |
46 |
|
|
** DOES use biff |
47 |
|
|
*/ |
48 |
|
|
meetPullVol * |
49 |
|
|
meetPullVolCopy(const meetPullVol *mpv) { |
50 |
|
|
static const char me[]="meetPullVolCopy"; |
51 |
|
|
meetPullVol *ret; |
52 |
|
|
unsigned int si; |
53 |
|
|
airArray *mop; |
54 |
|
|
|
55 |
|
|
mop = airMopNew(); |
56 |
|
|
ret = meetPullVolNew(); |
57 |
|
|
airMopAdd(mop, ret, (airMopper)meetPullVolNix, airMopOnError); |
58 |
|
|
/* HEY: hope this is okay for dynamic kinds */ |
59 |
|
|
ret->kind = mpv->kind; |
60 |
|
|
ret->fileName = airStrdup(mpv->fileName); |
61 |
|
|
ret->volName = airStrdup(mpv->volName); |
62 |
|
|
if (mpv->sbp) { |
63 |
|
|
ret->sbp = gageStackBlurParmNew(); |
64 |
|
|
if (gageStackBlurParmCopy(ret->sbp, mpv->sbp)) { |
65 |
|
|
biffMovef(MEET, GAGE, "%s: problem", me); |
66 |
|
|
airMopError(mop); return NULL; |
67 |
|
|
} |
68 |
|
|
} |
69 |
|
|
ret->leeching = AIR_FALSE; |
70 |
|
|
ret->derivNormSS = mpv->derivNormSS; |
71 |
|
|
ret->recomputedSS = AIR_FALSE; |
72 |
|
|
ret->derivNormBiasSS = mpv->derivNormBiasSS; |
73 |
|
|
if (mpv->sbp) { |
74 |
|
|
ret->nin = NULL; |
75 |
|
|
ret->ninSS = AIR_CALLOC(ret->sbp->num, Nrrd *); |
76 |
|
|
for (si=0; si<mpv->sbp->num; si++) { |
77 |
|
|
ret->ninSS[si] = nrrdNew(); |
78 |
|
|
if (nrrdCopy(ret->ninSS[si], mpv->ninSS[si])) { |
79 |
|
|
biffMovef(MEET, NRRD, "%s: problem with ninSS[%u]", me, si); |
80 |
|
|
airMopError(mop); return NULL; |
81 |
|
|
} |
82 |
|
|
} |
83 |
|
|
} else { |
84 |
|
|
ret->nin = nrrdNew(); |
85 |
|
|
if (nrrdCopy(ret->nin, mpv->nin)) { |
86 |
|
|
biffMovef(MEET, NRRD, "%s: problem with nin", me); |
87 |
|
|
airMopError(mop); return NULL; |
88 |
|
|
} |
89 |
|
|
ret->ninSS = NULL; |
90 |
|
|
} |
91 |
|
|
airMopOkay(mop); |
92 |
|
|
return ret; |
93 |
|
|
} |
94 |
|
|
|
95 |
|
|
/* |
96 |
|
|
******** meetPullVolParse |
97 |
|
|
** |
98 |
|
|
** parses a string to extract all the information necessary to create |
99 |
|
|
** the pullVolume (this function originated in Deft/src/main-pull.c) |
100 |
|
|
*/ |
101 |
|
|
int |
102 |
|
|
meetPullVolParse(meetPullVol *mpv, const char *_str) { |
103 |
|
|
static const char me[]="meetPullVolParse"; |
104 |
|
|
#define VFMT_SHRT "<fileName>:<kind>:<volName>" |
105 |
|
|
/* there are other flags and parms but these are the main ones */ |
106 |
|
|
#define SFMT "<minScl>-<#smp>-<maxScl>[-n][/k=kss][/b=bspec][/s=smpling]" |
107 |
|
|
#define VFMT_LONG "<fileName>:<kind>:" SFMT ":<volName>" |
108 |
|
|
char *str, *ctok, *clast=NULL; |
109 |
|
|
airArray *mop; |
110 |
|
|
int wantSS; |
111 |
|
|
unsigned int ctokn; |
112 |
|
|
|
113 |
|
|
if (!(mpv && _str)) { |
114 |
|
|
biffAddf(MEET, "%s: got NULL pointer", me); |
115 |
|
|
return 1; |
116 |
|
|
} |
117 |
|
|
if (!( str = airStrdup(_str) )) { |
118 |
|
|
biffAddf(MEET, "%s: couldn't strdup input", me); |
119 |
|
|
return 1; |
120 |
|
|
} |
121 |
|
|
|
122 |
|
|
mop = airMopNew(); |
123 |
|
|
airMopAdd(mop, str, airFree, airMopAlways); |
124 |
|
|
ctokn = airStrntok(str, ":"); |
125 |
|
|
/* An annoying side-effect of putting the blurring kernel specification and |
126 |
|
|
boundary specification inside the string representation of the |
127 |
|
|
gageStackBlurParm, is that the colon in dg:1,6" or "pad:10" is now |
128 |
|
|
confused as a delimiter in the (e.g.) "vol.nrrd:scalar:0-8-5.5:V" string |
129 |
|
|
representation of meetPullVol, as in |
130 |
|
|
"vol.nrrd:scalar:0-8-5.5/k=dg:1,6/b=pad:1:V". So we have to be more |
131 |
|
|
permissive in the number of tokens (hacky) */ |
132 |
|
|
if (!( ctokn >= 3 )) { |
133 |
|
|
biffAddf(MEET, "%s: didn't get at least 3 \":\"-separated tokens in " |
134 |
|
|
"\"%s\"; not of form " VFMT_SHRT " or " VFMT_LONG , me, _str); |
135 |
|
|
airMopError(mop); return 1; |
136 |
|
|
} |
137 |
|
|
wantSS = (ctokn > 3); |
138 |
|
|
|
139 |
|
|
ctok = airStrtok(str, ":", &clast); |
140 |
|
|
if (!(mpv->fileName = airStrdup(ctok))) { |
141 |
|
|
biffAddf(MEET, "%s: couldn't strdup fileName", me); |
142 |
|
|
airMopError(mop); return 1; |
143 |
|
|
} |
144 |
|
|
airMopAdd(mop, &(mpv->fileName), (airMopper)airSetNull, airMopOnError); |
145 |
|
|
airMopAdd(mop, mpv->fileName, airFree, airMopOnError); |
146 |
|
|
ctok = airStrtok(NULL, ":", &clast); |
147 |
|
|
if (!(mpv->kind = meetConstGageKindParse(ctok))) { |
148 |
|
|
biffAddf(MEET, "%s: couldn't parse \"%s\" as kind", me, ctok); |
149 |
|
|
airMopError(mop); return 1; |
150 |
|
|
} |
151 |
|
|
if (wantSS) { |
152 |
|
|
int extraFlag[256]; char *extraParm=NULL, *ptok, *plast; |
153 |
|
|
unsigned int efi, cti; |
154 |
|
|
char *sbps; |
155 |
|
|
/* the hack to make the ":" inside a blurring kernel specification or |
156 |
|
|
boundary specification be unlike the ":" that delimits the real |
157 |
|
|
meetPullVol fields */ |
158 |
|
|
sbps = airStrdup(_str); /* to have a buffer big enough */ |
159 |
|
|
airMopAdd(mop, sbps, airFree, airMopAlways); |
160 |
|
|
strcpy(sbps, ""); |
161 |
|
|
for (cti=0; cti<ctokn-3; cti++) { |
162 |
|
|
if (cti) { |
163 |
|
|
strcat(sbps, ":"); |
164 |
|
|
} |
165 |
|
|
ctok = airStrtok(NULL, ":", &clast); |
166 |
|
|
strcat(sbps, ctok); |
167 |
|
|
} |
168 |
|
|
mpv->sbp = gageStackBlurParmNix(mpv->sbp); |
169 |
|
|
mpv->sbp = gageStackBlurParmNew(); |
170 |
|
|
if (gageStackBlurParmParse(mpv->sbp, extraFlag, &extraParm, sbps)) { |
171 |
|
|
biffMovef(MEET, GAGE, "%s: problem parsing sbp from \"%s\"", me, sbps); |
172 |
|
|
airMopError(mop); return 1; |
173 |
|
|
} |
174 |
|
|
mpv->derivNormSS = !!extraFlag['n']; |
175 |
|
|
extraFlag['n'] = AIR_FALSE; |
176 |
|
|
for (efi=0; efi<256; efi++) { |
177 |
|
|
if (extraFlag[AIR_CAST(unsigned char, efi)]) { |
178 |
|
|
biffAddf(MEET, "%s: got unknown extra flag '%c' in \"%s\"", me, |
179 |
|
|
AIR_CAST(char, efi), sbps); |
180 |
|
|
airMopError(mop); return 1; |
181 |
|
|
} |
182 |
|
|
} |
183 |
|
|
if (extraParm) { |
184 |
|
|
unsigned int pmi, pmn; |
185 |
|
|
static const char dnbiase[]="dnbias="; |
186 |
|
|
airMopAdd(mop, extraParm, airFree, airMopAlways); |
187 |
|
|
pmn = airStrntok(extraParm, "/"); |
188 |
|
|
for (pmi=0; pmi<pmn; pmi++) { |
189 |
|
|
ptok = airStrtok(!pmi ? extraParm : NULL, "/", &plast); |
190 |
|
|
if (strstr(ptok, dnbiase) == ptok) { |
191 |
|
|
if (1 != sscanf(ptok + strlen(dnbiase), "%lg", |
192 |
|
|
&(mpv->derivNormBiasSS))) { |
193 |
|
|
biffAddf(MEET, "%s: couldn't parse \"%s\" as double in \"%s\"", |
194 |
|
|
me, ptok + strlen(dnbiase), ptok); |
195 |
|
|
airMopError(mop); return 1; |
196 |
|
|
} |
197 |
|
|
} else { |
198 |
|
|
biffAddf(MEET, "%s: got unknown extra parm %s in \"%s\"", |
199 |
|
|
me, ptok, extraParm); |
200 |
|
|
airMopError(mop); return 1; |
201 |
|
|
} |
202 |
|
|
} |
203 |
|
|
} |
204 |
|
|
} else { |
205 |
|
|
/* no scale-space stuff wanted */ |
206 |
|
|
mpv->sbp = NULL; |
207 |
|
|
mpv->ninSS = NULL; |
208 |
|
|
} |
209 |
|
|
ctok = airStrtok(NULL, ":", &clast); |
210 |
|
|
if (!(mpv->volName = airStrdup(ctok))) { |
211 |
|
|
biffAddf(MEET, "%s: couldn't strdup volName", me); |
212 |
|
|
airMopError(mop); return 1; |
213 |
|
|
} |
214 |
|
|
airMopAdd(mop, &(mpv->volName), (airMopper)airSetNull, airMopOnError); |
215 |
|
|
airMopAdd(mop, mpv->volName, airFree, airMopOnError); |
216 |
|
|
|
217 |
|
|
if (strchr(ctok, '-')) { |
218 |
|
|
biffAddf(MEET, "%s: you probably don't want \"-\" in volume name \"%s\"; " |
219 |
|
|
"forgot last \":\" in scale sampling specification?", me, ctok); |
220 |
|
|
airMopError(mop); return 1; |
221 |
|
|
} |
222 |
|
|
|
223 |
|
|
airMopOkay(mop); |
224 |
|
|
return 0; |
225 |
|
|
} |
226 |
|
|
|
227 |
|
|
int |
228 |
|
|
meetHestPullVolParse(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { |
229 |
|
|
static const char me[]="meetHestPullVolParse"; |
230 |
|
|
meetPullVol *mpv, **mpvP; |
231 |
|
|
airArray *mop; |
232 |
|
|
|
233 |
|
|
if (!(ptr && str)) { |
234 |
|
|
sprintf(err, "%s: got NULL pointer", me); |
235 |
|
|
return 1; |
236 |
|
|
} |
237 |
|
|
mop = airMopNew(); |
238 |
|
|
mpvP = AIR_CAST(meetPullVol **, ptr); |
239 |
|
|
*mpvP = mpv = meetPullVolNew(); |
240 |
|
|
airMopAdd(mop, mpvP, (airMopper)airSetNull, airMopOnError); |
241 |
|
|
airMopAdd(mop, mpv, (airMopper)meetPullVolNix, airMopOnError); |
242 |
|
|
if (meetPullVolParse(mpv, str)) { |
243 |
|
|
char *ler; |
244 |
|
|
airMopAdd(mop, ler = biffGetDone(MEET), airFree, airMopOnError); |
245 |
|
|
airStrcpy(err, AIR_STRLEN_HUGE, ler); |
246 |
|
|
airMopError(mop); |
247 |
|
|
return 1; |
248 |
|
|
} |
249 |
|
|
airMopOkay(mop); |
250 |
|
|
return 0; |
251 |
|
|
} |
252 |
|
|
|
253 |
|
|
/* |
254 |
|
|
******** meetPullVolNix |
255 |
|
|
** |
256 |
|
|
** this frees stuff allocated meetPullVolParse and meetPullVolLoadMulti |
257 |
|
|
*/ |
258 |
|
|
meetPullVol * |
259 |
|
|
meetPullVolNix(meetPullVol *mpv) { |
260 |
|
|
|
261 |
|
|
if (mpv) { |
262 |
|
|
if (!mpv->leeching && mpv->nin) { |
263 |
|
|
nrrdNuke(mpv->nin); |
264 |
|
|
} |
265 |
|
|
if (mpv->sbp) { |
266 |
|
|
unsigned int ssi; |
267 |
|
|
if (mpv->ninSS) { |
268 |
|
|
/* need this check because the mpv may not have benefitted |
269 |
|
|
from meetPullVolLoadMulti, so it might be incomplete */ |
270 |
|
|
for (ssi=0; ssi<mpv->sbp->num; ssi++) { |
271 |
|
|
if (!mpv->leeching) { |
272 |
|
|
nrrdNuke(mpv->ninSS[ssi]); |
273 |
|
|
} |
274 |
|
|
} |
275 |
|
|
airFree(mpv->ninSS); |
276 |
|
|
} |
277 |
|
|
gageStackBlurParmNix(mpv->sbp); |
278 |
|
|
} |
279 |
|
|
airFree(mpv->fileName); |
280 |
|
|
airFree(mpv->volName); |
281 |
|
|
airFree(mpv); |
282 |
|
|
} |
283 |
|
|
return NULL; |
284 |
|
|
} |
285 |
|
|
|
286 |
|
|
hestCB |
287 |
|
|
_meetHestPullVol = { |
288 |
|
|
sizeof(meetPullVol *), |
289 |
|
|
"meetPullVol", |
290 |
|
|
meetHestPullVolParse, |
291 |
|
|
(airMopper)meetPullVolNix, |
292 |
|
|
}; |
293 |
|
|
|
294 |
|
|
hestCB * |
295 |
|
|
meetHestPullVol = &_meetHestPullVol; |
296 |
|
|
|
297 |
|
|
/* |
298 |
|
|
******** meetPullVolLeechable |
299 |
|
|
** |
300 |
|
|
** indicates whether lchr can leech from orig (saved in *can), and if not, |
301 |
|
|
** explanation is saved in explain (if non-NULL) |
302 |
|
|
** |
303 |
|
|
** always uses biff |
304 |
|
|
*/ |
305 |
|
|
int |
306 |
|
|
meetPullVolLeechable(const meetPullVol *lchr, |
307 |
|
|
const meetPullVol *orig, |
308 |
|
|
int *can, char explain[AIR_STRLEN_LARGE]) { |
309 |
|
|
static const char me[]="meetPullVolLeechable"; |
310 |
|
|
char subexplain[AIR_STRLEN_LARGE]; |
311 |
|
|
|
312 |
|
|
if (!( lchr && orig && can )) { |
313 |
|
|
biffAddf(MEET, "%s: got NULL pointer (%p %p %p)", me, lchr, orig, can); |
314 |
|
|
return 1; |
315 |
|
|
} |
316 |
|
|
/* can leech, if not reading from stdin */ |
317 |
|
|
*can = !!strcmp(orig->fileName, "-"); |
318 |
|
|
if (!*can) { |
319 |
|
|
if (explain) { |
320 |
|
|
sprintf(explain, "original loaded from stdin"); |
321 |
|
|
} |
322 |
|
|
return 0; |
323 |
|
|
} |
324 |
|
|
/* can, if coming from same file */ |
325 |
|
|
*can = !strcmp(orig->fileName, lchr->fileName); |
326 |
|
|
if (!*can) { |
327 |
|
|
if (explain) { |
328 |
|
|
sprintf(explain, "not from same file (\"%s\" vs \"%s\")\n", |
329 |
|
|
lchr->fileName, orig->fileName); |
330 |
|
|
} |
331 |
|
|
return 0; |
332 |
|
|
} |
333 |
|
|
/* can, if same kind */ |
334 |
|
|
*can = (orig->kind == lchr->kind); |
335 |
|
|
if (!*can) { |
336 |
|
|
if (explain) { |
337 |
|
|
sprintf(explain, "not same kind (%s vs %s)\n", |
338 |
|
|
lchr->kind->name, orig->kind->name); |
339 |
|
|
} |
340 |
|
|
return 0; |
341 |
|
|
} |
342 |
|
|
/* can, if both using or both not using scale-space */ |
343 |
|
|
*can = (!!lchr->sbp == !!orig->sbp); |
344 |
|
|
if (!*can) { |
345 |
|
|
if (explain) { |
346 |
|
|
sprintf(explain, "not agreeing on use of scale-space (%s vs %s)\n", |
347 |
|
|
lchr->sbp ? "yes" : "no", orig->sbp ? "yes" : "no"); |
348 |
|
|
} |
349 |
|
|
return 0; |
350 |
|
|
} |
351 |
|
|
if (orig->sbp) { |
352 |
|
|
int differ; |
353 |
|
|
if (gageStackBlurParmCompare(lchr->sbp, "potential leecher", |
354 |
|
|
orig->sbp, "original", |
355 |
|
|
&differ, subexplain)) { |
356 |
|
|
biffAddf(MEET, "%s: problem comparing sbps", me); |
357 |
|
|
return 1; |
358 |
|
|
} |
359 |
|
|
if (differ) { |
360 |
|
|
if (explain) { |
361 |
|
|
sprintf(explain, "different uses of scale-space: %s", subexplain); |
362 |
|
|
} |
363 |
|
|
*can = AIR_FALSE; |
364 |
|
|
return 0; |
365 |
|
|
} |
366 |
|
|
} |
367 |
|
|
/* DO allow difference in derivNormSS (the main reason for leeching), |
368 |
|
|
as well as derivNormBiasSS */ |
369 |
|
|
/* no differences so far */ |
370 |
|
|
*can = AIR_TRUE; |
371 |
|
|
return 0; |
372 |
|
|
} |
373 |
|
|
|
374 |
|
|
void |
375 |
|
|
meetPullVolLeech(meetPullVol *vol, |
376 |
|
|
const meetPullVol *volPrev) { |
377 |
|
|
|
378 |
|
|
if (vol && volPrev) { |
379 |
|
|
vol->nin = volPrev->nin; |
380 |
|
|
if (vol->sbp) { |
381 |
|
|
unsigned int ni; |
382 |
|
|
/* have to allocate ninSS here; in volPrev it was probably allocated |
383 |
|
|
by gageStackBlurManage */ |
384 |
|
|
vol->ninSS = AIR_CALLOC(vol->sbp->num, Nrrd *); |
385 |
|
|
for (ni=0; ni<vol->sbp->num; ni++) { |
386 |
|
|
vol->ninSS[ni] = volPrev->ninSS[ni]; |
387 |
|
|
} |
388 |
|
|
} |
389 |
|
|
vol->leeching = AIR_TRUE; |
390 |
|
|
} |
391 |
|
|
return; |
392 |
|
|
} |
393 |
|
|
|
394 |
|
|
/* |
395 |
|
|
** This is kind of a sad function. The big re-write of gageStackBlurParm in |
396 |
|
|
** late August 2013 was motivated by the frustration of how there was no |
397 |
|
|
** centralized and consistent way of representing (by text or by command-line |
398 |
|
|
** options) all the things that determine scale-space "stack" creation. |
399 |
|
|
** Having re-organized gageStackBlurParm, the meetPullVol was re-organized to |
400 |
|
|
** include one inside, which is clearly better than the previous reduplication |
401 |
|
|
** of the stack blur parms inside the meetPullVol. Parsing the meetPullVol |
402 |
|
|
** from the command-line (as in done in puller) highlights the annoying fact |
403 |
|
|
** that hest wants to be the origin of information: you can't have hest |
404 |
|
|
** supplement existing information with whatever it learns from the |
405 |
|
|
** command-line, especially when hest is parsing 1 or more of something, and |
406 |
|
|
** especially when the existing information would be coming from other |
407 |
|
|
** command-line arguments. |
408 |
|
|
** |
409 |
|
|
** So, this sad function says, "ok all you meetPullVol parsed from the |
410 |
|
|
** command-line: if you don't already have a boundary or a kernel set, here's |
411 |
|
|
** one to use". What makes it sad is how the whole point of the |
412 |
|
|
** gageStackBlurParm re-org was that knowledge about the internals of the |
413 |
|
|
** gageStackBlurParm was now going to be entirely localized in gage. But here |
414 |
|
|
** we are listing off two of its fields as parameters to this function, which |
415 |
|
|
** means its API might change the next time the gageStackBlurParm is updated. |
416 |
|
|
** |
417 |
|
|
** To help keep track of what info was actually used, *kssSetP and *bspSetP |
418 |
|
|
** (if non-NULL) are set to the number of kernel and boundary specs that are |
419 |
|
|
** "finished" in this way. |
420 |
|
|
*/ |
421 |
|
|
int |
422 |
|
|
meetPullVolStackBlurParmFinishMulti(meetPullVol **mpv, unsigned int mpvNum, |
423 |
|
|
unsigned int *kssSetP, |
424 |
|
|
unsigned int *bspSetP, |
425 |
|
|
const NrrdKernelSpec *kssblur, |
426 |
|
|
const NrrdBoundarySpec *bspec) { |
427 |
|
|
static const char me[]="meetPullVolStackBlurParmFinishMulti"; |
428 |
|
|
unsigned int ii, kssSet, bspSet; |
429 |
|
|
|
430 |
|
|
if (!mpv || !mpvNum) { |
431 |
|
|
biffAddf(MEET, "%s: got NULL mpv (%p) or 0 mpvNum (%u)", |
432 |
|
|
me, AIR_VOIDP(mpv), mpvNum); |
433 |
|
|
return 1; |
434 |
|
|
} |
435 |
|
|
kssSet = bspSet = 0; |
436 |
|
|
for (ii=0; ii<mpvNum; ii++) { |
437 |
|
|
if (kssblur && mpv[ii]->sbp && !(mpv[ii]->sbp->kspec)) { |
438 |
|
|
if (gageStackBlurParmKernelSet(mpv[ii]->sbp, kssblur)) { |
439 |
|
|
biffMovef(MEET, GAGE, "%s: trouble w/ kernel on mpv[%u]", me, ii); |
440 |
|
|
return 1; |
441 |
|
|
} |
442 |
|
|
kssSet++; |
443 |
|
|
} |
444 |
|
|
if (bspec && mpv[ii]->sbp && !(mpv[ii]->sbp->bspec)) { |
445 |
|
|
if (gageStackBlurParmBoundarySpecSet(mpv[ii]->sbp, bspec)) { |
446 |
|
|
biffMovef(MEET, GAGE, "%s: trouble w/ boundary on mpv[%u]", me, ii); |
447 |
|
|
return 1; |
448 |
|
|
} |
449 |
|
|
bspSet++; |
450 |
|
|
} |
451 |
|
|
} |
452 |
|
|
if (kssSetP) { |
453 |
|
|
*kssSetP = kssSet; |
454 |
|
|
} |
455 |
|
|
if (bspSetP) { |
456 |
|
|
*bspSetP = bspSet; |
457 |
|
|
} |
458 |
|
|
return 0; |
459 |
|
|
} |
460 |
|
|
|
461 |
|
|
/* |
462 |
|
|
******** meetPullVolLoadMulti |
463 |
|
|
** |
464 |
|
|
** at this point the only per-pullVolume information required for |
465 |
|
|
** loading/creating the volumes, which isn't already in the |
466 |
|
|
** meetPullVol, is the cachePath, so that is passed explicitly. |
467 |
|
|
*/ |
468 |
|
|
int |
469 |
|
|
meetPullVolLoadMulti(meetPullVol **mpv, unsigned int mpvNum, |
470 |
|
|
char *cachePath, int verbose) { |
471 |
|
|
static const char me[]="meetPullVolLoadMulti"; |
472 |
|
|
unsigned int mpvIdx; |
473 |
|
|
airArray *mop; |
474 |
|
|
meetPullVol *vol; |
475 |
|
|
|
476 |
|
|
if (!( mpv && cachePath)) { |
477 |
|
|
biffAddf(MEET, "%s: got NULL pointer", me); |
478 |
|
|
return 1; |
479 |
|
|
} |
480 |
|
|
mop = airMopNew(); |
481 |
|
|
for (mpvIdx=0; mpvIdx<mpvNum; mpvIdx++) { |
482 |
|
|
unsigned int pvi; |
483 |
|
|
int leechable; |
484 |
|
|
char explain[AIR_STRLEN_LARGE]; |
485 |
|
|
vol = mpv[mpvIdx]; |
486 |
|
|
for (pvi=0; pvi<mpvIdx; pvi++) { |
487 |
|
|
if (meetPullVolLeechable(vol, mpv[pvi], &leechable, explain)) { |
488 |
|
|
biffAddf(MEET, "%s: problem testing leechable(v[%u]->v[%u])", |
489 |
|
|
me, mpvIdx, pvi); |
490 |
|
|
return 1; |
491 |
|
|
} |
492 |
|
|
if (leechable) { |
493 |
|
|
meetPullVolLeech(vol, mpv[pvi]); |
494 |
|
|
break; /* prevent a chain of leeching */ |
495 |
|
|
} else { |
496 |
|
|
if (verbose) { |
497 |
|
|
fprintf(stderr, "%s: mpv[%u] cannot leech mpv[%u]: %s\n", me, |
498 |
|
|
mpvIdx, pvi, explain); |
499 |
|
|
} |
500 |
|
|
} |
501 |
|
|
} |
502 |
|
|
if (pvi < mpvIdx) { |
503 |
|
|
/* we leeched this one, move on */ |
504 |
|
|
if (verbose) { |
505 |
|
|
fprintf(stderr, "%s: vspec[%u] (%s) leeching off vspec[%u] (%s)\n", |
506 |
|
|
me, mpvIdx, vol->volName, pvi, mpv[pvi]->volName); |
507 |
|
|
} |
508 |
|
|
continue; |
509 |
|
|
} |
510 |
|
|
/* else we're not leeching */ |
511 |
|
|
vol->leeching = AIR_FALSE; |
512 |
|
|
vol->nin = nrrdNew(); |
513 |
|
|
airMopAdd(mop, &(vol->nin), (airMopper)airSetNull, airMopOnError); |
514 |
|
|
airMopAdd(mop, vol->nin, (airMopper)nrrdNuke, airMopOnError); |
515 |
|
|
if (nrrdLoad(vol->nin, vol->fileName, NULL)) { |
516 |
|
|
biffMovef(MEET, NRRD, "%s: trouble loading mpv[%u]->nin (\"%s\")", |
517 |
|
|
me, mpvIdx, vol->volName); |
518 |
|
|
airMopError(mop); return 1; |
519 |
|
|
} |
520 |
|
|
if (vol->sbp) { |
521 |
|
|
char formatSS[AIR_STRLEN_LARGE]; |
522 |
|
|
sprintf(formatSS, "%s/%s-%%03u-%03u.nrrd", |
523 |
|
|
cachePath, vol->volName, vol->sbp->num); |
524 |
|
|
if (verbose) { |
525 |
|
|
fprintf(stderr, "%s: managing %s ... \n", me, formatSS); |
526 |
|
|
} |
527 |
|
|
if (gageStackBlurManage(&(vol->ninSS), &(vol->recomputedSS), vol->sbp, |
528 |
|
|
formatSS, AIR_TRUE, NULL, |
529 |
|
|
vol->nin, vol->kind)) { |
530 |
|
|
biffMovef(MEET, GAGE, "%s: trouble getting volume stack (\"%s\")", |
531 |
|
|
me, formatSS); |
532 |
|
|
airMopError(mop); return 1; |
533 |
|
|
} |
534 |
|
|
if (verbose) { |
535 |
|
|
fprintf(stderr, "%s: ... done\n", me); |
536 |
|
|
} |
537 |
|
|
} |
538 |
|
|
} |
539 |
|
|
airMopOkay(mop); |
540 |
|
|
return 0; |
541 |
|
|
} |
542 |
|
|
|
543 |
|
|
/* |
544 |
|
|
******** meetPullVolAddMulti |
545 |
|
|
** |
546 |
|
|
** the spatial (k00, k11, k22) and scale (kSSrecon) reconstruction |
547 |
|
|
** kernels are not (yet) part of the meetPullVol, so have to be passed |
548 |
|
|
** in here |
549 |
|
|
*/ |
550 |
|
|
int |
551 |
|
|
meetPullVolAddMulti(pullContext *pctx, |
552 |
|
|
meetPullVol **mpv, unsigned int mpvNum, |
553 |
|
|
const NrrdKernelSpec *k00, |
554 |
|
|
const NrrdKernelSpec *k11, |
555 |
|
|
const NrrdKernelSpec *k22, |
556 |
|
|
const NrrdKernelSpec *kSSrecon) { |
557 |
|
|
static const char me[]="meetPullVolAddMulti"; |
558 |
|
|
unsigned int mpvIdx; |
559 |
|
|
|
560 |
|
|
if (!( pctx && mpv )) { |
561 |
|
|
biffAddf(MEET, "%s: got NULL pointer", me); |
562 |
|
|
return 1; |
563 |
|
|
} |
564 |
|
|
for (mpvIdx=0; mpvIdx<mpvNum; mpvIdx++) { |
565 |
|
|
meetPullVol *vol; |
566 |
|
|
int E; |
567 |
|
|
vol = mpv[mpvIdx]; |
568 |
|
|
if (!vol->sbp) { |
569 |
|
|
E = pullVolumeSingleAdd(pctx, vol->kind, vol->volName, |
570 |
|
|
vol->nin, k00, k11, k22); |
571 |
|
|
} else { |
572 |
|
|
E = pullVolumeStackAdd(pctx, vol->kind, vol->volName, vol->nin, |
573 |
|
|
AIR_CAST(const Nrrd *const *, |
574 |
|
|
vol->ninSS), |
575 |
|
|
vol->sbp->sigma, vol->sbp->num, |
576 |
|
|
vol->derivNormSS, vol->derivNormBiasSS, |
577 |
|
|
k00, k11, k22, kSSrecon); |
578 |
|
|
} |
579 |
|
|
if (E) { |
580 |
|
|
biffMovef(MEET, PULL, "%s: trouble adding volume %u/%u (\"%s\")", |
581 |
|
|
me, mpvIdx, mpvNum, vol->volName); |
582 |
|
|
return 1; |
583 |
|
|
} |
584 |
|
|
} |
585 |
|
|
|
586 |
|
|
return 0; |
587 |
|
|
} |
588 |
|
|
|
589 |
|
|
meetPullInfo * |
590 |
|
|
meetPullInfoNew(void) { |
591 |
|
|
meetPullInfo *ret; |
592 |
|
|
|
593 |
|
|
ret = AIR_CALLOC(1, meetPullInfo); |
594 |
|
|
if (ret) { |
595 |
|
|
ret->info = 0; |
596 |
|
|
ret->source = pullSourceUnknown; |
597 |
|
|
ret->prop = pullPropUnknown; |
598 |
|
|
ret->constraint = AIR_FALSE; |
599 |
|
|
ret->volName = ret->itemStr = NULL; |
600 |
|
|
ret->zero = ret->scale = AIR_NAN; |
601 |
|
|
} |
602 |
|
|
return ret; |
603 |
|
|
} |
604 |
|
|
|
605 |
|
|
meetPullInfo * |
606 |
|
|
meetPullInfoNix(meetPullInfo *minf) { |
607 |
|
|
|
608 |
|
|
if (minf) { |
609 |
|
|
airFree(minf->volName); |
610 |
|
|
airFree(minf->itemStr); |
611 |
|
|
free(minf); |
612 |
|
|
} |
613 |
|
|
return NULL; |
614 |
|
|
} |
615 |
|
|
|
616 |
|
|
static int |
617 |
|
|
zeroScaleSet(meetPullInfo *minf, int haveZS, char **lastP) { |
618 |
|
|
static const char me[]="_zeroScaleSet"; |
619 |
|
|
char *tok; |
620 |
|
|
|
621 |
|
|
if (haveZS) { |
622 |
|
|
tok = airStrtok(NULL, ":", lastP); |
623 |
|
|
if (1 != sscanf(tok, "%lf", &(minf->zero))) { |
624 |
|
|
biffAddf(MEET, "%s: couldn't parse %s as zero (double)", me, tok); |
625 |
|
|
return 1; |
626 |
|
|
} |
627 |
|
|
tok = airStrtok(NULL, ":", lastP); |
628 |
|
|
if (1 != sscanf(tok, "%lf", &(minf->scale))) { |
629 |
|
|
biffAddf(MEET, "%s: couldn't parse %s as scale (double)", me, tok); |
630 |
|
|
return 1; |
631 |
|
|
} |
632 |
|
|
} else { |
633 |
|
|
minf->zero = minf->scale = AIR_NAN; |
634 |
|
|
} |
635 |
|
|
return 0; |
636 |
|
|
} |
637 |
|
|
|
638 |
|
|
int |
639 |
|
|
meetPullInfoParse(meetPullInfo *minf, const char *_str) { |
640 |
|
|
static const char me[]="meetPullInfoParse"; |
641 |
|
|
#define IFMT_GAGE "<info>[-c]:<volname>:<item>[:<zero>:<scale>]" |
642 |
|
|
#define IFMT_PROP "<info>:prop=<prop>[:<zero>:<scale>]" |
643 |
|
|
#define PROP_PREFIX "prop=" /* has to end with = */ |
644 |
|
|
char *str, *tok, *last=NULL, *iflags; |
645 |
|
|
airArray *mop; |
646 |
|
|
int haveZS, source; |
647 |
|
|
|
648 |
|
|
if (!(minf && _str)) { |
649 |
|
|
biffAddf(MEET, "%s: got NULL pointer", me); |
650 |
|
|
return 1; |
651 |
|
|
} |
652 |
|
|
if ( (3 == airStrntok(_str, ":") || 5 == airStrntok(_str, ":")) |
653 |
|
|
&& 1 == airStrntok(_str, "=") ) { |
654 |
|
|
source = pullSourceGage; |
655 |
|
|
haveZS = (5 == airStrntok(_str, ":")); |
656 |
|
|
} else if ( (2 == airStrntok(_str, ":") || 4 == airStrntok(_str, ":")) |
657 |
|
|
&& 2 == airStrntok(_str, "=") ) { |
658 |
|
|
source = pullSourceProp; |
659 |
|
|
haveZS = (4 == airStrntok(_str, ":")); |
660 |
|
|
} else { |
661 |
|
|
biffAddf(MEET, "%s: \"%s\" not of form " IFMT_GAGE " or " IFMT_PROP, |
662 |
|
|
me, _str); |
663 |
|
|
return 1; |
664 |
|
|
} |
665 |
|
|
|
666 |
|
|
mop = airMopNew(); |
667 |
|
|
if (!( str = airStrdup(_str) )) { |
668 |
|
|
biffAddf(MEET, "%s: couldn't strdup input", me); |
669 |
|
|
return 1; |
670 |
|
|
} |
671 |
|
|
airMopAdd(mop, str, airFree, airMopAlways); |
672 |
|
|
|
673 |
|
|
minf->source = source; |
674 |
|
|
if (pullSourceGage == source) { |
675 |
|
|
tok = airStrtok(str, ":", &last); |
676 |
|
|
iflags = strchr(tok, '-'); |
677 |
|
|
if (iflags) { |
678 |
|
|
*iflags = '\0'; |
679 |
|
|
iflags++; |
680 |
|
|
} |
681 |
|
|
if (!(minf->info = airEnumVal(pullInfo, tok))) { |
682 |
|
|
biffAddf(MEET, "%s: couldn't parse \"%s\" as %s", |
683 |
|
|
me, tok, pullInfo->name); |
684 |
|
|
airMopError(mop); return 1; |
685 |
|
|
} |
686 |
|
|
if (iflags) { |
687 |
|
|
if (strchr(iflags, 'c')) { |
688 |
|
|
minf->constraint = AIR_TRUE; |
689 |
|
|
} |
690 |
|
|
} |
691 |
|
|
tok = airStrtok(NULL, ":", &last); |
692 |
|
|
airFree(minf->volName); |
693 |
|
|
minf->volName = airStrdup(tok); |
694 |
|
|
airMopAdd(mop, minf->volName, airFree, airMopOnError); |
695 |
|
|
tok = airStrtok(NULL, ":", &last); |
696 |
|
|
airFree(minf->itemStr); |
697 |
|
|
minf->itemStr = airStrdup(tok); |
698 |
|
|
airMopAdd(mop, minf->itemStr, airFree, airMopOnError); |
699 |
|
|
if (zeroScaleSet(minf, haveZS, &last)) { |
700 |
|
|
biffAddf(MEET, "%s: couldn't parse zero or scale", me); |
701 |
|
|
airMopError(mop); return 1; |
702 |
|
|
} |
703 |
|
|
} else if (pullSourceProp == source) { |
704 |
|
|
/* "<info>:prop=<prop>[:<zero>:<scale>]" */ |
705 |
|
|
tok = airStrtok(str, ":", &last); |
706 |
|
|
if (!(minf->info = airEnumVal(pullInfo, tok))) { |
707 |
|
|
biffAddf(MEET, "%s: couldn't parse \"%s\" as %s", |
708 |
|
|
me, tok, pullInfo->name); |
709 |
|
|
airMopError(mop); return 1; |
710 |
|
|
} |
711 |
|
|
tok = airStrtok(NULL, ":", &last); |
712 |
|
|
if (strncmp(PROP_PREFIX, tok, strlen(PROP_PREFIX))) { |
713 |
|
|
biffAddf(MEET, "%s: property info didn't start with %s", |
714 |
|
|
me, PROP_PREFIX); |
715 |
|
|
} |
716 |
|
|
tok += strlen(PROP_PREFIX); |
717 |
|
|
if (!(minf->prop = airEnumVal(pullProp, tok))) { |
718 |
|
|
biffAddf(MEET, "%s: couldn't parse \"%s\" as %s", |
719 |
|
|
me, tok, pullProp->name); |
720 |
|
|
airMopError(mop); return 1; |
721 |
|
|
} |
722 |
|
|
if (zeroScaleSet(minf, haveZS, &last)) { |
723 |
|
|
biffAddf(MEET, "%s: couldn't parse zero or scale", me); |
724 |
|
|
airMopError(mop); return 1; |
725 |
|
|
} |
726 |
|
|
} else { |
727 |
|
|
biffAddf(MEET, "%s: sorry, source %s not handled", |
728 |
|
|
me, airEnumStr(pullSource, source)); |
729 |
|
|
airMopError(mop); return 1; |
730 |
|
|
} |
731 |
|
|
|
732 |
|
|
airMopOkay(mop); |
733 |
|
|
return 0; |
734 |
|
|
} |
735 |
|
|
|
736 |
|
|
int |
737 |
|
|
meetHestPullInfoParse(void *ptr, char *str, char err[AIR_STRLEN_HUGE]) { |
738 |
|
|
static const char me[]="meetHestPullInfoParse"; |
739 |
|
|
airArray *mop; |
740 |
|
|
meetPullInfo **minfP, *minf; |
741 |
|
|
|
742 |
|
|
if (!(ptr && str)) { |
743 |
|
|
sprintf(err, "%s: got NULL pointer", me); |
744 |
|
|
return 1; |
745 |
|
|
} |
746 |
|
|
mop = airMopNew(); |
747 |
|
|
minfP = AIR_CAST(meetPullInfo **, ptr); |
748 |
|
|
*minfP = minf = meetPullInfoNew(); |
749 |
|
|
airMopAdd(mop, minfP, (airMopper)airSetNull, airMopOnError); |
750 |
|
|
airMopAdd(mop, minf, (airMopper)meetPullInfoNix, airMopOnError); |
751 |
|
|
if (meetPullInfoParse(minf, str)) { |
752 |
|
|
char *ler; |
753 |
|
|
airMopAdd(mop, ler = biffGetDone(MEET), airFree, airMopOnError); |
754 |
|
|
airStrcpy(err, AIR_STRLEN_HUGE, ler); |
755 |
|
|
airMopError(mop); |
756 |
|
|
return 1; |
757 |
|
|
} |
758 |
|
|
airMopOkay(mop); |
759 |
|
|
return 0; |
760 |
|
|
} |
761 |
|
|
|
762 |
|
|
hestCB |
763 |
|
|
_meetHestPullInfo = { |
764 |
|
|
sizeof(meetPullInfo *), |
765 |
|
|
"meetPullInfo", |
766 |
|
|
meetHestPullInfoParse, |
767 |
|
|
(airMopper)meetPullInfoNix |
768 |
|
|
}; |
769 |
|
|
|
770 |
|
|
hestCB * |
771 |
|
|
meetHestPullInfo = &_meetHestPullInfo; |
772 |
|
|
|
773 |
|
|
int |
774 |
|
|
meetPullInfoAddMulti(pullContext *pctx, |
775 |
|
|
meetPullInfo **minf, unsigned int minfNum) { |
776 |
|
|
static const char me[]="meetPullInfoAddMulti"; |
777 |
|
|
const pullVolume *vol; |
778 |
|
|
unsigned int ii; |
779 |
|
|
airArray *mop; |
780 |
|
|
|
781 |
|
|
if (!( pctx && minf )) { |
782 |
|
|
biffAddf(MEET, "%s: got NULL pointer", me); |
783 |
|
|
return 1; |
784 |
|
|
} |
785 |
|
|
|
786 |
|
|
mop = airMopNew(); |
787 |
|
|
for (ii=0; ii<minfNum; ii++) { |
788 |
|
|
pullInfoSpec *ispec; |
789 |
|
|
ispec = pullInfoSpecNew(); |
790 |
|
|
airMopAdd(mop, ispec, (airMopper)pullInfoSpecNix, airMopOnError); |
791 |
|
|
ispec->volName = airStrdup(minf[ii]->volName); |
792 |
|
|
ispec->source = minf[ii]->source; |
793 |
|
|
ispec->info = minf[ii]->info; |
794 |
|
|
ispec->prop = minf[ii]->prop; |
795 |
|
|
ispec->zero = minf[ii]->zero; |
796 |
|
|
ispec->scale = minf[ii]->scale; |
797 |
|
|
ispec->constraint = minf[ii]->constraint; |
798 |
|
|
/* the item is the one thing that takes some work to recover; |
799 |
|
|
we need to find the volume and find the item from its kind->enm */ |
800 |
|
|
if (pullSourceGage == ispec->source) { |
801 |
|
|
if (!( vol = pullVolumeLookup(pctx, minf[ii]->volName) )) { |
802 |
|
|
biffMovef(MEET, PULL, "%s: can't find volName \"%s\" for minf[%u]", |
803 |
|
|
me, minf[ii]->volName, ii); |
804 |
|
|
airMopError(mop); return 1; |
805 |
|
|
} |
806 |
|
|
if (!( ispec->item = airEnumVal(vol->kind->enm, minf[ii]->itemStr))) { |
807 |
|
|
biffAddf(MEET, "%s: can't parse \"%s\" as item of %s kind (minf[%u])\n", |
808 |
|
|
me, minf[ii]->itemStr, vol->kind->name, ii); |
809 |
|
|
airMopError(mop); return 1; |
810 |
|
|
} |
811 |
|
|
} |
812 |
|
|
if (pullInfoSpecAdd(pctx, ispec)) { |
813 |
|
|
biffMovef(MEET, PULL, "%s: trouble adding ispec from minf[%u]", me, ii); |
814 |
|
|
airMopError(mop); return 1; |
815 |
|
|
} |
816 |
|
|
/* else added the ispec okay. If we have an error with a different |
817 |
|
|
ispec later in this loop, who's job is it to free up the ispecs |
818 |
|
|
that have been added successfully? In teem/src/bin/puller, that |
819 |
|
|
is done by pullContextNix. So we now extricate ourself from the |
820 |
|
|
business of freeing this ispec in case of error; one of the few |
821 |
|
|
times that airMopSub is really needed */ |
822 |
|
|
airMopSub(mop, ispec, (airMopper)pullInfoSpecNix); |
823 |
|
|
} |
824 |
|
|
|
825 |
|
|
airMopOkay(mop); |
826 |
|
|
return 0; |
827 |
|
|
} |