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