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
25 #include "pull.h"
26 #include "privatePull.h"
27
28 pullVolume *
pullVolumeNew()29 pullVolumeNew() {
30 pullVolume *vol;
31
32 vol = AIR_CAST(pullVolume *, calloc(1, sizeof(pullVolume)));
33 if (vol) {
34 vol->verbose = 0;
35 vol->name = NULL;
36 vol->kind = NULL;
37 vol->ninSingle = NULL;
38 vol->ninScale = NULL;
39 vol->scaleNum = 0;
40 vol->scalePos = NULL;
41 vol->scaleDerivNorm = AIR_FALSE;
42 vol->scaleDerivNormBias = 0.0;
43 vol->ksp00 = nrrdKernelSpecNew();
44 vol->ksp11 = nrrdKernelSpecNew();
45 vol->ksp22 = nrrdKernelSpecNew();
46 vol->kspSS = nrrdKernelSpecNew();
47 GAGE_QUERY_RESET(vol->pullValQuery);
48 vol->gctx = NULL;
49 vol->gpvl = NULL;
50 vol->gpvlSS = NULL;
51 /* this is turned OFF in volumes that have infos that aren't seedthresh,
52 see pullInfoSpecAdd() */
53 vol->seedOnly = AIR_TRUE;
54 vol->forSeedPreThresh = AIR_FALSE;
55 }
56 return vol;
57 }
58
59 pullVolume *
pullVolumeNix(pullVolume * vol)60 pullVolumeNix(pullVolume *vol) {
61
62 if (vol) {
63 airFree(vol->name);
64 airFree(vol->scalePos);
65 vol->ksp00 = nrrdKernelSpecNix(vol->ksp00);
66 vol->ksp11 = nrrdKernelSpecNix(vol->ksp11);
67 vol->ksp22 = nrrdKernelSpecNix(vol->ksp22);
68 vol->kspSS = nrrdKernelSpecNix(vol->kspSS);
69 if (vol->gctx) {
70 vol->gctx = gageContextNix(vol->gctx);
71 }
72 airFree(vol->gpvlSS);
73 airFree(vol);
74 }
75 return NULL;
76 }
77
78 /*
79 ** used to set all the fields of pullVolume at once, including the
80 ** gageContext inside the pullVolume
81 **
82 ** used both for top-level volumes in the pullContext (pctx->vol[i])
83 ** in which case pctx is non-NULL,
84 ** and for the per-task volumes (task->vol[i]),
85 ** in which case pctx is NULL
86 */
87 int
_pullVolumeSet(pullContext * pctx,pullVolume * vol,const gageKind * kind,int verbose,const char * name,const Nrrd * ninSingle,const Nrrd * const * ninScale,double * scalePos,unsigned int ninNum,int scaleDerivNorm,double scaleDerivNormBias,const NrrdKernelSpec * ksp00,const NrrdKernelSpec * ksp11,const NrrdKernelSpec * ksp22,const NrrdKernelSpec * kspSS)88 _pullVolumeSet(pullContext *pctx, pullVolume *vol,
89 const gageKind *kind,
90 int verbose, const char *name,
91 const Nrrd *ninSingle,
92 const Nrrd *const *ninScale,
93 double *scalePos,
94 unsigned int ninNum,
95 int scaleDerivNorm,
96 double scaleDerivNormBias,
97 const NrrdKernelSpec *ksp00,
98 const NrrdKernelSpec *ksp11,
99 const NrrdKernelSpec *ksp22,
100 const NrrdKernelSpec *kspSS) {
101 static const char me[]="_pullVolumeSet";
102 int E;
103 unsigned int vi;
104
105 if (!( vol && kind && airStrlen(name) && ksp00 && ksp11 && ksp22 )) {
106 biffAddf(PULL, "%s: got NULL pointer", me);
107 return 1;
108 }
109 if (!ninSingle) {
110 biffAddf(PULL, "%s: needed non-NULL ninSingle", me);
111 return 1;
112 }
113 if (pctx) {
114 for (vi=0; vi<pctx->volNum; vi++) {
115 if (pctx->vol[vi] == vol) {
116 biffAddf(PULL, "%s: already got vol %p as vol[%u]", me,
117 AIR_VOIDP(vol), vi);
118 return 1;
119 }
120 }
121 }
122 if (ninNum) {
123 if (!( ninNum >= 2 )) {
124 biffAddf(PULL, "%s: need at least 2 volumes (not %u)", me, ninNum);
125 return 1;
126 }
127 if (!scalePos) {
128 biffAddf(PULL, "%s: need non-NULL scalePos with ninNum %u", me, ninNum);
129 return 1;
130 }
131 if (!ninScale) {
132 biffAddf(PULL, "%s: need non-NULL ninScale with ninNum %u", me, ninNum);
133 return 1;
134 }
135 }
136
137 vol->verbose = verbose;
138 vol->kind = kind;
139 vol->gctx = gageContextNew();
140 gageParmSet(vol->gctx, gageParmVerbose,
141 vol->verbose > 0 ? vol->verbose - 1 : 0);
142 gageParmSet(vol->gctx, gageParmRenormalize, AIR_FALSE);
143 /* because we're likely only using accurate kernels */
144 gageParmSet(vol->gctx, gageParmStackNormalizeRecon, AIR_FALSE);
145 vol->scaleDerivNorm = scaleDerivNorm;
146 gageParmSet(vol->gctx, gageParmStackNormalizeDeriv, scaleDerivNorm);
147 vol->scaleDerivNormBias = scaleDerivNormBias;
148 gageParmSet(vol->gctx, gageParmStackNormalizeDerivBias,
149 scaleDerivNormBias);
150 gageParmSet(vol->gctx, gageParmCheckIntegrals, AIR_TRUE);
151 E = 0;
152 if (!E) E |= gageKernelSet(vol->gctx, gageKernel00,
153 ksp00->kernel, ksp00->parm);
154 if (!E) E |= gageKernelSet(vol->gctx, gageKernel11,
155 ksp11->kernel, ksp11->parm);
156 if (!E) E |= gageKernelSet(vol->gctx, gageKernel22,
157 ksp22->kernel, ksp22->parm);
158 if (ninScale) {
159 if (!kspSS) {
160 biffAddf(PULL, "%s: got NULL kspSS", me);
161 return 1;
162 }
163 gageParmSet(vol->gctx, gageParmStackUse, AIR_TRUE);
164 if (!E) E |= !(vol->gpvl = gagePerVolumeNew(vol->gctx, ninSingle, kind));
165 vol->gpvlSS = AIR_CAST(gagePerVolume **,
166 calloc(ninNum, sizeof(gagePerVolume *)));
167 if (!E) E |= gageStackPerVolumeNew(vol->gctx, vol->gpvlSS,
168 ninScale, ninNum, kind);
169 if (!E) E |= gageStackPerVolumeAttach(vol->gctx, vol->gpvl, vol->gpvlSS,
170 scalePos, ninNum);
171 if (!E) E |= gageKernelSet(vol->gctx, gageKernelStack,
172 kspSS->kernel, kspSS->parm);
173 } else {
174 vol->gpvlSS = NULL;
175 if (!E) E |= !(vol->gpvl = gagePerVolumeNew(vol->gctx, ninSingle, kind));
176 if (!E) E |= gagePerVolumeAttach(vol->gctx, vol->gpvl);
177 }
178 if (E) {
179 biffMovef(PULL, GAGE, "%s: trouble (%s %s)", me,
180 ninSingle ? "ninSingle" : "",
181 ninScale ? "ninScale" : "");
182 return 1;
183 }
184 gageQueryReset(vol->gctx, vol->gpvl);
185 /* the query is the single thing remaining unset in the gageContext */
186
187 vol->name = airStrdup(name);
188 if (!vol->name) {
189 biffAddf(PULL, "%s: couldn't strdup name (len %u)", me,
190 AIR_CAST(unsigned int, airStrlen(name)));
191 return 1;
192 }
193 if (vol->verbose) {
194 printf("%s: ---- vol=%p, name = %p = |%s|\n", me, AIR_VOIDP(vol),
195 AIR_VOIDP(vol->name), vol->name);
196 if (0 != vol->scaleDerivNormBias) {
197 printf("%s: ---- scale deriv norm bias = %g\n", me,
198 vol->scaleDerivNormBias);
199 }
200 }
201 nrrdKernelSpecSet(vol->ksp00, ksp00->kernel, ksp00->parm);
202 nrrdKernelSpecSet(vol->ksp11, ksp11->kernel, ksp11->parm);
203 nrrdKernelSpecSet(vol->ksp22, ksp22->kernel, ksp22->parm);
204 if (ninScale) {
205 vol->ninSingle = ninSingle;
206 vol->ninScale = ninScale;
207 vol->scaleNum = ninNum;
208 vol->scalePos = AIR_CAST(double *, calloc(ninNum, sizeof(double)));
209 if (!vol->scalePos) {
210 biffAddf(PULL, "%s: couldn't calloc scalePos", me);
211 return 1;
212 }
213 for (vi=0; vi<ninNum; vi++) {
214 vol->scalePos[vi] = scalePos[vi];
215 }
216 nrrdKernelSpecSet(vol->kspSS, kspSS->kernel, kspSS->parm);
217 } else {
218 vol->ninSingle = ninSingle;
219 vol->ninScale = NULL;
220 vol->scaleNum = 0;
221 /* leave kspSS as is (unset) */
222 }
223
224 return 0;
225 }
226
227 /*
228 ** the effect is to give pctx ownership of the vol
229 */
230 int
pullVolumeSingleAdd(pullContext * pctx,const gageKind * kind,char * name,const Nrrd * nin,const NrrdKernelSpec * ksp00,const NrrdKernelSpec * ksp11,const NrrdKernelSpec * ksp22)231 pullVolumeSingleAdd(pullContext *pctx,
232 const gageKind *kind,
233 char *name, const Nrrd *nin,
234 const NrrdKernelSpec *ksp00,
235 const NrrdKernelSpec *ksp11,
236 const NrrdKernelSpec *ksp22) {
237 static const char me[]="pullVolumeSingleSet";
238 pullVolume *vol;
239
240 vol = pullVolumeNew();
241 if (_pullVolumeSet(pctx, vol, kind,
242 pctx->verbose, name,
243 nin,
244 NULL, NULL, 0, AIR_FALSE, 0.0,
245 ksp00, ksp11, ksp22, NULL)) {
246 biffAddf(PULL, "%s: trouble", me);
247 return 1;
248 }
249
250 /* add this volume to context */
251 if (pctx->verbose) {
252 printf("%s: adding pctx->vol[%u] = %p\n", me, pctx->volNum,
253 AIR_VOIDP(vol));
254 }
255 pctx->vol[pctx->volNum] = vol;
256 pctx->volNum++;
257 return 0;
258 }
259
260 /*
261 ** the effect is to give pctx ownership of the vol
262 */
263 int
pullVolumeStackAdd(pullContext * pctx,const gageKind * kind,char * name,const Nrrd * nin,const Nrrd * const * ninSS,double * scalePos,unsigned int ninNum,int scaleDerivNorm,double scaleDerivNormBias,const NrrdKernelSpec * ksp00,const NrrdKernelSpec * ksp11,const NrrdKernelSpec * ksp22,const NrrdKernelSpec * kspSS)264 pullVolumeStackAdd(pullContext *pctx,
265 const gageKind *kind,
266 char *name,
267 const Nrrd *nin,
268 const Nrrd *const *ninSS,
269 double *scalePos,
270 unsigned int ninNum,
271 int scaleDerivNorm,
272 double scaleDerivNormBias,
273 const NrrdKernelSpec *ksp00,
274 const NrrdKernelSpec *ksp11,
275 const NrrdKernelSpec *ksp22,
276 const NrrdKernelSpec *kspSS) {
277 static const char me[]="pullVolumeStackAdd";
278 pullVolume *vol;
279
280 vol = pullVolumeNew();
281 if (_pullVolumeSet(pctx, vol, kind, pctx->verbose, name,
282 nin,
283 ninSS, scalePos, ninNum,
284 scaleDerivNorm, scaleDerivNormBias,
285 ksp00, ksp11, ksp22, kspSS)) {
286 biffAddf(PULL, "%s: trouble", me);
287 return 1;
288 }
289
290 /* add this volume to context */
291 pctx->vol[pctx->volNum++] = vol;
292 return 0;
293 }
294
295 /*
296 ** this is only used to create pullVolumes for the pullTasks
297 **
298 ** DOES use biff
299 */
300 pullVolume *
_pullVolumeCopy(const pullVolume * volOrig)301 _pullVolumeCopy(const pullVolume *volOrig) {
302 static const char me[]="pullVolumeCopy";
303 pullVolume *volNew;
304
305 volNew = pullVolumeNew();
306 if (_pullVolumeSet(NULL, volNew, volOrig->kind,
307 volOrig->verbose, volOrig->name,
308 volOrig->ninSingle,
309 volOrig->ninScale,
310 volOrig->scalePos,
311 volOrig->scaleNum,
312 volOrig->scaleDerivNorm,
313 volOrig->scaleDerivNormBias,
314 volOrig->ksp00, volOrig->ksp11,
315 volOrig->ksp22, volOrig->kspSS)) {
316 biffAddf(PULL, "%s: trouble creating new volume", me);
317 return NULL;
318 }
319 volNew->seedOnly = volOrig->seedOnly;
320 volNew->forSeedPreThresh = volOrig->forSeedPreThresh;
321 /* _pullVolumeSet just created a new (per-task) gageContext, and
322 it will not learn the items from the info specs, so we have to
323 add query here */
324 if (gageQuerySet(volNew->gctx, volNew->gpvl, volOrig->gpvl->query)
325 || gageUpdate(volNew->gctx)) {
326 biffMovef(PULL, GAGE, "%s: trouble with new volume gctx", me);
327 return NULL;
328 }
329 return volNew;
330 }
331
332 int
_pullInsideBBox(pullContext * pctx,double pos[4])333 _pullInsideBBox(pullContext *pctx, double pos[4]) {
334
335 return (AIR_IN_CL(pctx->bboxMin[0], pos[0], pctx->bboxMax[0]) &&
336 AIR_IN_CL(pctx->bboxMin[1], pos[1], pctx->bboxMax[1]) &&
337 AIR_IN_CL(pctx->bboxMin[2], pos[2], pctx->bboxMax[2]) &&
338 AIR_IN_CL(pctx->bboxMin[3], pos[3], pctx->bboxMax[3]));
339 }
340
341 /*
342 ** sets:
343 ** pctx->haveScale
344 ** pctx->voxelSizeSpace, voxelSizeScale
345 ** pctx->bboxMin ([0] through [3], always)
346 ** pctx->bboxMax (same)
347 */
348 int
_pullVolumeSetup(pullContext * pctx)349 _pullVolumeSetup(pullContext *pctx) {
350 static const char me[]="_pullVolumeSetup";
351 unsigned int ii, numScale;
352
353 /* first see if there are any gage problems */
354 for (ii=0; ii<pctx->volNum; ii++) {
355 if (pctx->verbose) {
356 printf("%s: gageUpdate(vol[%u])\n", me, ii);
357 }
358 if (pctx->vol[ii]->gctx) {
359 if (gageUpdate(pctx->vol[ii]->gctx)) {
360 biffMovef(PULL, GAGE, "%s: trouble setting up gage on vol "
361 "%u/%u (\"%s\")", me, ii, pctx->volNum,
362 pctx->vol[ii]->name);
363 return 1;
364 }
365 } else {
366 biffAddf(PULL, "%s: vol[%u] has NULL gctx", me, ii);
367 }
368 }
369
370 pctx->voxelSizeSpace = 0.0;
371 for (ii=0; ii<pctx->volNum; ii++) {
372 double min[3], max[3];
373 gageContext *gctx;
374 gctx = pctx->vol[ii]->gctx;
375 gageShapeBoundingBox(min, max, gctx->shape);
376 if (!ii) {
377 ELL_3V_COPY(pctx->bboxMin, min);
378 ELL_3V_COPY(pctx->bboxMax, max);
379 } else {
380 ELL_3V_MIN(pctx->bboxMin, pctx->bboxMin, min);
381 ELL_3V_MIN(pctx->bboxMax, pctx->bboxMax, max);
382 }
383 pctx->voxelSizeSpace += ELL_3V_LEN(gctx->shape->spacing)/sqrt(3.0);
384 if (ii && !pctx->initParm.unequalShapesAllow) {
385 if (!gageShapeEqual(pctx->vol[0]->gctx->shape, pctx->vol[0]->name,
386 pctx->vol[ii]->gctx->shape, pctx->vol[ii]->name)) {
387 biffMovef(PULL, GAGE,
388 "%s: need equal shapes, but vol 0 and %u different",
389 me, ii);
390 return 1;
391 }
392 }
393 }
394 pctx->voxelSizeSpace /= pctx->volNum;
395 /* have now computed bbox{Min,Max}[0,1,2]; now do bbox{Min,Max}[3] */
396 pctx->bboxMin[3] = pctx->bboxMax[3] = 0.0;
397 pctx->haveScale = AIR_FALSE;
398 pctx->voxelSizeScale = 0.0;
399 numScale = 0;
400 for (ii=0; ii<pctx->volNum; ii++) {
401 if (pctx->vol[ii]->ninScale) {
402 double sclMin, sclMax, sclStep;
403 unsigned int si;
404 numScale ++;
405 sclMin = pctx->vol[ii]->scalePos[0];
406 if (pctx->flag.scaleIsTau) {
407 sclMin = gageTauOfSig(sclMin);
408 }
409 sclMax = pctx->vol[ii]->scalePos[pctx->vol[ii]->scaleNum-1];
410 if (pctx->flag.scaleIsTau) {
411 sclMax = gageTauOfSig(sclMax);
412 }
413 sclStep = 0;
414 for (si=0; si<pctx->vol[ii]->scaleNum-1; si++) {
415 double scl0, scl1;
416 scl1 = pctx->vol[ii]->scalePos[si+1];
417 scl0 = pctx->vol[ii]->scalePos[si];
418 if (pctx->flag.scaleIsTau) {
419 scl1 = gageTauOfSig(scl1);
420 scl0 = gageTauOfSig(scl0);
421 }
422 sclStep += (scl1 - scl0);
423 }
424 sclStep /= pctx->vol[ii]->scaleNum-1;
425 pctx->voxelSizeScale += sclStep;
426 if (!pctx->haveScale) {
427 pctx->bboxMin[3] = sclMin;
428 pctx->bboxMax[3] = sclMax;
429 pctx->haveScale = AIR_TRUE;
430 } else {
431 /* we already know haveScale; expand existing range */
432 pctx->bboxMin[3] = AIR_MIN(sclMin, pctx->bboxMin[3]);
433 pctx->bboxMax[3] = AIR_MAX(sclMax, pctx->bboxMax[3]);
434 }
435 }
436 }
437 if (numScale) {
438 pctx->voxelSizeScale /= numScale;
439 }
440 if (pctx->verbose) {
441 printf("%s: bboxMin (%g,%g,%g,%g) max (%g,%g,%g,%g)\n", me,
442 pctx->bboxMin[0], pctx->bboxMin[1],
443 pctx->bboxMin[2], pctx->bboxMin[3],
444 pctx->bboxMax[0], pctx->bboxMax[1],
445 pctx->bboxMax[2], pctx->bboxMax[3]);
446 printf("%s: voxelSizeSpace %g Scale %g\n", me,
447 pctx->voxelSizeSpace, pctx->voxelSizeScale);
448 }
449
450 /* _energyInterParticle() depends on this error checking */
451 if (pctx->haveScale) {
452 if (pullInterTypeJustR == pctx->interType) {
453 biffAddf(PULL, "%s: need scale-aware intertype (not %s) with "
454 "a scale-space volume",
455 me, airEnumStr(pullInterType, pullInterTypeJustR));
456 return 1;
457 }
458 } else {
459 /* don't have scale */
460 if (pullInterTypeJustR != pctx->interType) {
461 biffAddf(PULL, "%s: can't use scale-aware intertype (%s) without "
462 "a scale-space volume",
463 me, airEnumStr(pullInterType, pctx->interType));
464 return 1;
465 }
466 }
467 if (pctx->flag.energyFromStrength
468 && !(pctx->ispec[pullInfoStrength] && pctx->haveScale)) {
469 biffAddf(PULL, "%s: sorry, can use energyFromStrength only with both "
470 "a scale-space volume, and a strength info", me);
471 return 1;
472 }
473
474 return 0;
475 }
476
477 /*
478 ** basis of pullVolumeLookup
479 **
480 ** uses biff, returns UINT_MAX in case of error
481 */
482 unsigned int
_pullVolumeIndex(const pullContext * pctx,const char * volName)483 _pullVolumeIndex(const pullContext *pctx,
484 const char *volName) {
485 static const char me[]="_pullVolumeIndex";
486 unsigned int vi;
487
488 if (!( pctx && volName )) {
489 biffAddf(PULL, "%s: got NULL pointer", me);
490 return UINT_MAX;
491 }
492 if (0 == pctx->volNum) {
493 biffAddf(PULL, "%s: given context has no volumes", me);
494 return UINT_MAX;
495 }
496 for (vi=0; vi<pctx->volNum; vi++) {
497 if (!strcmp(pctx->vol[vi]->name, volName)) {
498 break;
499 }
500 }
501 if (vi == pctx->volNum) {
502 biffAddf(PULL, "%s: no volume has name \"%s\"", me, volName);
503 return UINT_MAX;
504 }
505 return vi;
506 }
507
508 const pullVolume *
pullVolumeLookup(const pullContext * pctx,const char * volName)509 pullVolumeLookup(const pullContext *pctx,
510 const char *volName) {
511 static const char me[]="pullVolumeLookup";
512 unsigned int vi;
513
514 vi = _pullVolumeIndex(pctx, volName);
515 if (UINT_MAX == vi) {
516 biffAddf(PULL, "%s: trouble looking up \"%s\"", me, volName);
517 return NULL;
518 }
519 return pctx->vol[vi];
520 }
521
522 int
pullConstraintScaleRange(pullContext * pctx,double ssrange[2])523 pullConstraintScaleRange(pullContext *pctx, double ssrange[2]) {
524 static const char me[]="pullConstraintScaleRange";
525 pullVolume *cvol;
526
527 if (!(pctx && ssrange)) {
528 biffAddf(PULL, "%s: got NULL pointer", me);
529 return 1;
530 }
531 if (!(pctx->constraint)) {
532 biffAddf(PULL, "%s: given context doesn't have constraint set", me);
533 return 1;
534 }
535 if (!(pctx->ispec[pctx->constraint])) {
536 biffAddf(PULL, "%s: info %s not set for constriant", me,
537 airEnumStr(pullInfo, pctx->constraint));
538 return 1;
539 }
540 cvol = pctx->vol[pctx->ispec[pctx->constraint]->volIdx];
541 if (!cvol->ninScale) {
542 biffAddf(PULL, "%s: volume \"%s\" has constraint but no scale-space",
543 me, cvol->name);
544 return 1;
545 }
546 ssrange[0] = cvol->scalePos[0];
547 ssrange[1] = cvol->scalePos[cvol->scaleNum-1];
548 if (pctx->flag.scaleIsTau) {
549 ssrange[0] = gageTauOfSig(ssrange[0]);
550 ssrange[1] = gageTauOfSig(ssrange[1]);
551 }
552
553 return 0;
554 }
555