GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: Testing/meet/probeSS.c Lines: 396 517 76.6 %
Date: 2017-05-26 Branches: 187 285 65.6 %

Line Branch Exec Source
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 "teem/meet.h"
25
26
/*
27
** Tests:
28
** ... lots of gage stuff ...
29
**
30
** The main point of all this is to make sure of two (or maybe 2.5) separate
31
** things about gage, the testing of which requires so much in common that one
32
** program might as well do them all. The tasks are:
33
**
34
** 1) ensure that values and their derivatives (where the gageKind supports
35
** it) are correctly handled in the multi-value gageKinds (gageKindVec,
36
** tenGageKind, tenDwiGageKind), relative to the gageKindScl ground-truth: the
37
** per-component values and derivatives have to match those reconstructed from
38
** sets of scalar volumes of the components.
39
** ==> Also, that gageContextCopy works even on dynamic kinds (like DWIs)
40
**
41
** 1.5) that there is expected consistency between the scalar, vector, tensor,
42
** and DWI properties of a related set of volumes.  So the test starts with a
43
** diffusion tensor field and generates DWIs, scalars (norm squared of the
44
** tensor), and vectors (gradient of norm squared) from this.
45
**
46
** 2) that scale-space reconstruction works: that sets of pre-blurred volumes
47
** can be generated and saved via the utilities in meet, that the smart
48
** hermite-spline based scale-space interpolation is working (for all kinds),
49
** and that gageContextCopy works on scale-space contexts
50
*/
51
52
#define BKEY "probeSS"
53
#define KIND_NUM 4
54
#define KI_SCL 0
55
#define KI_VEC 1
56
#define KI_TEN 2
57
#define KI_DWI 3
58
59
/*
60
static const char *kindStr[KIND_NUM] = {
61
  "scalar",
62
  "vector",
63
  "tensor",
64
  "  DWI "
65
};
66
*/
67
68
#define HALTON_BASE 100
69
70
static unsigned int volSize[3] = {45, 46, 47};
71
72
#define NRRD_NEW(nn, mm)                                        \
73
  (nn) = nrrdNew();                                             \
74
       airMopAdd((mm), (nn), (airMopper)nrrdNuke, airMopAlways)
75
76
/* the weird function names of the various local functions here (created in a
77
   rather adhoc and organic way) should be read in usual Teem order: from
78
   right to left */
79
80
static int
81
engageGenTensor(gageContext *gctx, Nrrd *ncten,
82
                /* out/in */
83
                double noiseStdv, unsigned int seed,
84
                unsigned int sx, unsigned int sy, unsigned int sz) {
85
  static const char me[]="engageGenTensor";
86
  hestParm *hparm;
87
  airArray *smop;
88
16
  char tmpStr[4][AIR_STRLEN_SMALL];
89
  Nrrd *nclean;
90
  NrrdIter *narg0, *narg1;
91
8
  const char *helixArgv[] =
92
  /*   0     1     2    3     4     5     6     7     8     9 */
93
    {"-s", NULL, NULL, NULL, "-v", "0", "-r", "40", "-o", NULL,
94
     "-ev", "0.00086", "0.00043", "0.00021", "-bg", "0.003", "-b", "3",
95
     NULL};
96
  int helixArgc;
97
  gagePerVolume *pvl;
98
99
8
  smop = airMopNew();
100
  /* NOTE: this is currently the only place where a unrrduCmd
101
     is called from within C code; it was educational to get working.
102
     Learned:
103
     * hest does NOT tolerate having empty or NULL elements of
104
     its argv[]!  More error checking for this in hest is needed.
105
     * the "const char **argv" type is not very convenient to
106
     set up in a dynamic way; the per-element setting done below
107
     is certainly awkward
108
  */
109
8
  hparm = hestParmNew();
110
8
  airMopAdd(smop, hparm, (airMopper)hestParmFree, airMopAlways);
111
8
  sprintf(tmpStr[0], "%u", sx); helixArgv[1] = tmpStr[0];
112
8
  sprintf(tmpStr[1], "%u", sy); helixArgv[2] = tmpStr[1];
113
8
  sprintf(tmpStr[2], "%u", sz); helixArgv[3] = tmpStr[2];
114
8
  sprintf(tmpStr[3], "tmp-ten.nrrd");
115
8
  helixArgv[9] = tmpStr[3];
116
  helixArgc = AIR_CAST(int, sizeof(helixArgv)/sizeof(char *)) - 1;
117
8
  if (tend_helixCmd.main(helixArgc, helixArgv, me, hparm)) {
118
    /* error already went to stderr, not to any biff container */
119
    biffAddf(BKEY, "%s: problem running tend %s", me, tend_helixCmd.name);
120
    airMopError(smop); return 1;
121
  }
122
8
  NRRD_NEW(nclean, smop);
123
8
  if (nrrdLoad(nclean, tmpStr[3], NULL)) {
124
    biffAddf(BKEY, "%s: trouble loading from new vol %s", me, tmpStr[3]);
125
    airMopError(smop); return 1;
126
  }
127
128
  /* add some noise to tensor value; no, this isn't really physical;
129
     since we're adding noise to the tensor and then simulating DWIs,
130
     rather than adding noise to DWIs and then estimating tensor,
131
     but for the purposes of gage testing its fine */
132
8
  narg0 = nrrdIterNew();
133
8
  narg1 = nrrdIterNew();
134
8
  airMopAdd(smop, narg0, (airMopper)nrrdIterNix, airMopAlways);
135
8
  airMopAdd(smop, narg1, (airMopper)nrrdIterNix, airMopAlways);
136
8
  nrrdIterSetNrrd(narg0, nclean);
137
8
  nrrdIterSetValue(narg1, noiseStdv);
138
8
  airSrandMT(seed);
139
8
  if (nrrdArithIterBinaryOp(ncten, nrrdBinaryOpNormalRandScaleAdd,
140
                            narg0, narg1)) {
141
    biffMovef(BKEY, NRRD, "%s: trouble noisying output", me);
142
    airMopError(smop); return 1;
143
  }
144
145
  /* wrap in gage context */
146
16
  if ( !(pvl = gagePerVolumeNew(gctx, ncten, tenGageKind))
147
16
       || gagePerVolumeAttach(gctx, pvl) ) {
148
    biffMovef(BKEY, GAGE, "%s: trouble engaging tensor", me);
149
    return 1;
150
  }
151
152
8
  airMopOkay(smop);
153
8
  return 0;
154
8
}
155
156
/*
157
** takes in ncten, measures "S" to nscl, copies that to nsclCopy,
158
** wraps those in gctx and gctxComp
159
*/
160
static int
161
engageGenScalar(gageContext *gctx, Nrrd *nscl,
162
                gageContext *gctxComp, Nrrd *nsclCopy,
163
                /* out/in */
164
                const Nrrd *ncten) {
165
  static const char me[]="engageGenScalar";
166
  gagePerVolume *pvl;
167
168
16
  if (tenAnisoVolume(nscl, ncten, tenAniso_S, 0)) {
169
    biffMovef(BKEY, TEN, "%s: trouble creating scalar volume", me);
170
    return 1;
171
  }
172
8
  if (nrrdCopy(nsclCopy, nscl)) {
173
    biffMovef(BKEY, NRRD, "%s: trouble copying scalar volume", me);
174
    return 1;
175
  }
176
177
  /* wrap both in gage context */
178
16
  if ( !(pvl = gagePerVolumeNew(gctx, nscl, gageKindScl))
179
16
       || gagePerVolumeAttach(gctx, pvl)
180
16
       || !(pvl = gagePerVolumeNew(gctxComp, nsclCopy, gageKindScl))
181
16
       || gagePerVolumeAttach(gctxComp, pvl)) {
182
    biffMovef(BKEY, GAGE, "%s: trouble engaging scalars", me);
183
    return 1;
184
  }
185
186
8
  return 0;
187
8
}
188
189
/*
190
** Makes a vector volume by measuring the gradient
191
** Being the gradient of the given scalar volume is just to make
192
** something vaguely interesting, and to test consistency with
193
** the the same gradient as measured in the tensor field.
194
*/
195
static int
196
engageGenVector(gageContext *gctx, Nrrd *nvec,
197
                /* out/in */
198
                const Nrrd *nscl) {
199
  static const char me[]="engageGenVector";
200
16
  ptrdiff_t padMin[4] = {0, 0, 0, 0}, padMax[4];
201
  Nrrd *ntmp;
202
  airArray *smop;
203
  float *vec, *scl;
204
  size_t sx, sy, sz, xi, yi, zi, Px, Mx, Py, My, Pz, Mz;
205
  double dnmX, dnmY, dnmZ;
206
  gagePerVolume *pvl;
207
208
8
  smop = airMopNew();
209
8
  if (nrrdTypeFloat != nscl->type) {
210
    biffAddf(BKEY, "%s: expected %s not %s type", me,
211
             airEnumStr(nrrdType, nrrdTypeFloat),
212
             airEnumStr(nrrdType, nscl->type));
213
    airMopError(smop); return 1;
214
  }
215
8
  NRRD_NEW(ntmp, smop);
216
8
  sx = nscl->axis[0].size;
217
8
  sy = nscl->axis[1].size;
218
8
  sz = nscl->axis[2].size;
219
8
  ELL_4V_SET(padMax, 2,
220
             AIR_CAST(ptrdiff_t, sx-1),
221
             AIR_CAST(ptrdiff_t, sy-1),
222
             AIR_CAST(ptrdiff_t, sz-1));
223
  /* we do axinsert and pad in order to keep all the per-axis info */
224
16
  if (nrrdAxesInsert(ntmp, nscl, 0)
225
16
      || nrrdPad_nva(nvec, ntmp, padMin, padMax,
226
                     nrrdBoundaryPad, 0.0)) {
227
    biffMovef(BKEY, NRRD, "%s: trouble", me);
228
    airMopError(smop); return 1;
229
  }
230
8
  dnmX = 0.5/nrrdSpaceVecNorm(nscl->spaceDim, nscl->axis[0].spaceDirection);
231
8
  dnmY = 0.5/nrrdSpaceVecNorm(nscl->spaceDim, nscl->axis[1].spaceDirection);
232
8
  dnmZ = 0.5/nrrdSpaceVecNorm(nscl->spaceDim, nscl->axis[2].spaceDirection);
233
8
  vec = AIR_CAST(float *, nvec->data);
234
8
  scl = AIR_CAST(float *, nscl->data);
235
#define INDEX(xj, yj, zj) (xj + sx*(yj + sy*zj))
236
768
  for (zi=0; zi<sz; zi++) {
237
376
    Mz = (zi == 0 ? 0 : zi-1);
238
376
    Pz = AIR_MIN(zi+1, sz-1);
239
35344
    for (yi=0; yi<sy; yi++) {
240
17296
      My = (yi == 0 ? 0 : yi-1);
241
17296
      Py = AIR_MIN(yi+1, sy-1);
242
1591232
      for (xi=0; xi<sx; xi++) {
243
778320
        Mx = (xi == 0 ? 0 : xi-1);
244
778320
        Px = AIR_MIN(xi+1, sx-1);
245
778320
        vec[0 + 3*INDEX(xi, yi, zi)] =
246
778320
          AIR_CAST(float, (scl[INDEX(Px, yi, zi)] - scl[INDEX(Mx, yi, zi)])*dnmX);
247
778320
        vec[1 + 3*INDEX(xi, yi, zi)] =
248
778320
          AIR_CAST(float, (scl[INDEX(xi, Py, zi)] - scl[INDEX(xi, My, zi)])*dnmY);
249
778320
        vec[2 + 3*INDEX(xi, yi, zi)] =
250
778320
          AIR_CAST(float, (scl[INDEX(xi, yi, Pz)] - scl[INDEX(xi, yi, Mz)])*dnmZ);
251
      }
252
    }
253
  }
254
#undef INDEX
255
256
  /* wrap in gage context */
257
16
  if ( !(pvl = gagePerVolumeNew(gctx, nvec, gageKindVec))
258
16
       || gagePerVolumeAttach(gctx, pvl) ) {
259
    biffMovef(BKEY, GAGE, "%s: trouble engaging vectors", me);
260
    return 1;
261
  }
262
8
  airMopError(smop);
263
8
  return 0;
264
8
}
265
266
/*
267
** make a DWI volume by simulating DWIs from given tensor
268
** this includes generating a new gradient set
269
*/
270
static int
271
genDwi(Nrrd *ndwi, Nrrd *ngrad,
272
       /* out/in */
273
       unsigned int gradNum, double bval, const Nrrd *ncten) {
274
  static const char me[]="genDwi";
275
  tenGradientParm *gparm;
276
  tenExperSpec *espec;
277
  Nrrd *nten, *nb0;
278
  NrrdIter *narg0, *narg1;
279
16
  size_t cropMin[4] = {1, 0, 0, 0}, cropMax[4];
280
  airArray *smop;
281
282
8
  smop = airMopNew();
283
8
  gparm = tenGradientParmNew();
284
8
  airMopAdd(smop, gparm, (airMopper)tenGradientParmNix, airMopAlways);
285
8
  espec = tenExperSpecNew();
286
8
  airMopAdd(smop, espec, (airMopper)tenExperSpecNix, airMopAlways);
287
8
  gparm->verbose = 0;
288
8
  gparm->minMean = 0.002;
289
8
  gparm->seed = 4242;
290
8
  gparm->insertZeroVec = AIR_TRUE;
291
16
  if (tenGradientGenerate(ngrad, gradNum, gparm)
292
16
      || tenExperSpecGradSingleBValSet(espec, AIR_FALSE /* insertB0 */,
293
                                       bval,
294
8
                                       AIR_CAST(double *, ngrad->data),
295
8
                                       AIR_CAST(unsigned int,
296
                                                ngrad->axis[1].size))) {
297
    biffMovef(BKEY, TEN, "%s: trouble generating grads or espec", me);
298
    airMopError(smop); return 1;
299
  }
300
8
  NRRD_NEW(nten, smop);
301
8
  NRRD_NEW(nb0, smop);
302
8
  ELL_4V_SET(cropMax,
303
             ncten->axis[0].size-1,
304
             ncten->axis[1].size-1,
305
             ncten->axis[2].size-1,
306
             ncten->axis[3].size-1);
307
16
  if (nrrdSlice(nb0, ncten, 0, 0)
308
16
      || nrrdCrop(nten, ncten, cropMin, cropMax)) {
309
    biffMovef(BKEY, NRRD, "%s: trouble slicing or cropping ten vol", me);
310
    airMopError(smop); return 1;
311
  }
312
8
  narg0 = nrrdIterNew();
313
8
  narg1 = nrrdIterNew();
314
8
  airMopAdd(smop, narg0, (airMopper)nrrdIterNix, airMopAlways);
315
8
  airMopAdd(smop, narg1, (airMopper)nrrdIterNix, airMopAlways);
316
8
  nrrdIterSetValue(narg1, 50000.0);
317
8
  nrrdIterSetNrrd(narg0, nb0);
318
8
  if (nrrdArithIterBinaryOp(nb0, nrrdBinaryOpMultiply,
319
                            narg0, narg1)) {
320
    biffMovef(BKEY, NRRD, "%s: trouble generating b0 vol", me);
321
    airMopError(smop); return 1;
322
  }
323
8
  if (tenModelSimulate(ndwi, nrrdTypeUShort, espec,
324
8
                       tenModel1Tensor2,
325
                       nb0, nten, AIR_TRUE /* keyValueSet */)) {
326
    biffMovef(BKEY, TEN, "%s: trouble simulating DWI vol", me);
327
    airMopError(smop); return 1;
328
  }
329
330
8
  airMopOkay(smop);
331
8
  return 0;
332
8
}
333
334
int
335
engageMopDiceVector(gageContext *gctx, Nrrd *nvecComp[3],
336
                    /* out/in */
337
                    airArray *mop, const Nrrd* nvec) {
338
  static const char me[]="engageMopDiceVector";
339
  gagePerVolume *pvl;
340
  unsigned int ci;
341
16
  char stmp[AIR_STRLEN_SMALL];
342
343

16
  if (!( 4 == nvec->dim && 3 == nvec->axis[0].size )) {
344
    biffAddf(BKEY, "%s: expected 4-D 3-by-X nrrd (not %u-D %s-by-X)",
345
             me, nvec->dim, airSprintSize_t(stmp, nvec->axis[0].size));
346
    return 1;
347
  }
348
64
  for (ci=0; ci<3; ci++) {
349
24
    NRRD_NEW(nvecComp[ci], mop);
350
24
    if (nrrdSlice(nvecComp[ci], nvec, 0, ci)) {
351
      biffMovef(BKEY, NRRD, "%s: trouble getting component %u", me, ci);
352
      return 1;
353
    }
354
48
    if ( !(pvl = gagePerVolumeNew(gctx, nvecComp[ci], gageKindScl))
355
48
         || gagePerVolumeAttach(gctx, pvl) ) {
356
      biffMovef(BKEY, GAGE, "%s: trouble engaging component %u", me, ci);
357
      return 1;
358
    }
359
  }
360
361
8
  return 0;
362
8
}
363
364
int
365
engageMopDiceTensor(gageContext *gctx, Nrrd *nctenComp[7],
366
                    /* out/in */
367
                    airArray *mop, const Nrrd* ncten) {
368
  static const char me[]="engageMopDiceTensor";
369
  gagePerVolume *pvl;
370
  unsigned int ci;
371
372
16
  if (tenTensorCheck(ncten, nrrdTypeFloat, AIR_TRUE /* want4F */,
373
                     AIR_TRUE /* useBiff */)) {
374
    biffMovef(BKEY, TEN, "%s: didn't get tensor volume", me);
375
    return 1;
376
  }
377
128
  for (ci=0; ci<7; ci++) {
378
56
    NRRD_NEW(nctenComp[ci], mop);
379
56
    if (nrrdSlice(nctenComp[ci], ncten, 0, ci)) {
380
      biffMovef(BKEY, NRRD, "%s: trouble getting component %u", me, ci);
381
      return 1;
382
    }
383
112
    if ( !(pvl = gagePerVolumeNew(gctx, nctenComp[ci], gageKindScl))
384
112
         || gagePerVolumeAttach(gctx, pvl) ) {
385
      biffMovef(BKEY, GAGE, "%s: trouble engaging component %u", me, ci);
386
      return 1;
387
    }
388
  }
389
390
8
  return 0;
391
8
}
392
393
int
394
engageMopDiceDwi(gageContext *gctx, Nrrd ***ndwiCompP,
395
                 /* out/in */
396
                 airArray *mop, const Nrrd* ndwi) {
397
  static const char me[]="mopDiceDwi";
398
  Nrrd **ndwiComp;
399
  size_t dwiNum;
400
16
  char stmp[AIR_STRLEN_SMALL];
401
  gagePerVolume *pvl;
402
  unsigned int ci;
403
404
8
  if (!( 4 == ndwi->dim )) {
405
    biffAddf(BKEY, "%s: wanted 4D volume (not %u)", me, ndwi->dim);
406
    return 1;
407
  }
408

16
  if (!( nrrdKindList == ndwi->axis[0].kind &&
409
8
         nrrdKindSpace == ndwi->axis[1].kind &&
410
8
         nrrdKindSpace == ndwi->axis[2].kind &&
411
8
         nrrdKindSpace == ndwi->axis[3].kind )) {
412
    biffAddf(BKEY, "%s: wanted kinds %s,3x%s, not %s,%s,%s,%s", me,
413
             airEnumStr(nrrdKind, nrrdKindList),
414
             airEnumStr(nrrdKind, nrrdKindSpace),
415
             airEnumStr(nrrdKind, ndwi->axis[0].kind),
416
             airEnumStr(nrrdKind, ndwi->axis[1].kind),
417
             airEnumStr(nrrdKind, ndwi->axis[2].kind),
418
             airEnumStr(nrrdKind, ndwi->axis[3].kind));
419
    return 1;
420
  }
421
8
  dwiNum = ndwi->axis[0].size;
422
8
  if (!(ndwiComp = AIR_CALLOC(dwiNum, Nrrd *))) {
423
    biffAddf(BKEY, "%s: couldn't alloc %s Nrrd*", me,
424
             airSprintSize_t(stmp, dwiNum));
425
    return 1;
426
  }
427
8
  airMopAdd(mop, ndwiComp, airFree, airMopAlways);
428
8
  *ndwiCompP = ndwiComp;
429
192
  for (ci=0; ci<dwiNum; ci++) {
430
88
    NRRD_NEW(ndwiComp[ci], mop);
431
88
    if (nrrdSlice(ndwiComp[ci], ndwi, 0, ci)) {
432
      biffMovef(BKEY, NRRD, "%s: trouble getting component %u", me, ci);
433
      return 1;
434
    }
435
176
    if ( !(pvl = gagePerVolumeNew(gctx, ndwiComp[ci], gageKindScl))
436
176
         || gagePerVolumeAttach(gctx, pvl) ) {
437
      biffMovef(BKEY, GAGE, "%s: trouble engaging component %u", me, ci);
438
      return 1;
439
    }
440
  }
441
8
  return 0;
442
8
}
443
444
typedef struct {
445
  char name[AIR_STRLEN_SMALL];
446
  const double **aptr; /* array of pointers to (const) answers */
447
  gageItemSpec *ispec; /* array of gageItemSpecs (not pointers to them) */
448
  unsigned int *alen;  /* array of answer lengths */
449
  unsigned int *sidx;  /* array of index (within san) of copied answer */
450
  unsigned int anum;   /* lenth of aptr, ispec, alen, sidx arrays */
451
  double *san;         /* single buffer for copy + concating answers */
452
  unsigned int slen;   /* length of single buffer (sum of all alen[i]) */
453
} multiAnswer;
454
455
static void
456
multiAnswerInit(multiAnswer *man) {
457
458
  /* HEY sloppy: not resetting name */
459
512
  man->aptr = airFree(AIR_VOIDP(man->aptr));
460
256
  man->ispec = airFree(man->ispec);
461
256
  man->alen = airFree(man->alen);
462
256
  man->sidx = airFree(man->sidx);
463
256
  man->anum = 0;
464
256
  man->san = airFree(man->san);
465
256
  man->slen = 0;
466
256
}
467
468
static multiAnswer*
469
multiAnswerNew(char *name) {
470
  multiAnswer *man;
471
472
128
  man = AIR_CALLOC(1, multiAnswer);
473
64
  airStrcpy(man->name, AIR_STRLEN_SMALL, name);
474
64
  multiAnswerInit(man);
475
64
  return man;
476
}
477
478
void
479
multiAnswerLenSet(multiAnswer *man, unsigned int anum) {
480
  /*
481
  static const char me[]="multiAnswerLenSet";
482
483
  fprintf(stderr, "!%s: %s hello -> answer number = %u\n", me,
484
          man->name, anum);
485
  */
486
384
  man->aptr = AIR_CALLOC(anum, const double *);
487
192
  man->ispec = AIR_CALLOC(anum, gageItemSpec);
488
192
  man->alen = AIR_CALLOC(anum, unsigned int);
489
192
  man->sidx = AIR_CALLOC(anum, unsigned int);
490
192
  man->anum = anum;
491
  /* don't know answer lengths yet */
492
192
  man->san = airFree(man->san);
493
192
  man->slen = 0;
494
192
  return;
495
}
496
497
static multiAnswer*
498
multiAnswerNix(multiAnswer *man) {
499
500
128
  airFree(AIR_VOIDP(man->aptr));
501
64
  airFree(man->ispec);
502
64
  airFree(man->alen);
503
64
  airFree(man->sidx);
504
64
  airFree(man->san);
505
64
  airFree(man);
506
64
  return NULL;
507
}
508
509
static void
510
multiAnswerAdd(multiAnswer *man, unsigned int ansIdx,
511
               const gageContext *gctx, const gagePerVolume *pvl,
512
               unsigned int item) {
513
  /*
514
  static const char me[]="multiAnswerAdd";
515
516
  fprintf(stderr, "!%s: %s hello (aidx = %u)\n", me, man->name, ansIdx);
517
  */
518
2592
  man->aptr[ansIdx] = gageAnswerPointer(gctx, pvl, item);
519
1296
  man->ispec[ansIdx].kind = pvl->kind;
520
1296
  man->ispec[ansIdx].item = item;
521
1296
  man->alen[ansIdx] = gageAnswerLength(gctx, pvl, item);
522
  /* HEY hack: doing this tally and allocation only on the last time
523
     we're called, presuming calls with increasing ansIdx */
524
1296
  man->slen = 0;
525
1296
  if (ansIdx == man->anum-1) {
526
    unsigned int ai;
527
    /* fprintf(stderr, "!%s: %s hello aidx reached anum-1 = %u\n", me, man->name, man->anum-1); */
528

4464
    for (ai=0; ai<man->anum; ai++) {
529
2784
      man->sidx[ai] = man->slen;
530
1296
      man->slen += man->alen[ai];
531
    }
532
192
    man->san = AIR_CALLOC(man->slen, double);
533
    /*
534
    fprintf(stderr, "!%s: (%s) slen = %u\n", "multiAnswerAdd",
535
            pvl->kind->name, man->slen);
536
    */
537
192
  }
538
1296
  return;
539
}
540
541
static void
542
multiAnswerCollect(multiAnswer *man) {
543
  unsigned int ai;
544
  /*
545
  static const char me[]="multiAnswerCollect";
546
547
  fprintf(stderr, "%s: %s hi\n", me, man->name);
548
  */
549
3920400
  for (ai=0; ai<man->anum; ai++) {
550
    /*
551
    fprintf(stderr, "!%s: (%s/%s) ai=%u/%u  to %p+%u=%p for %u doubles\n", "multiAnswerCollect",
552
            man->ispec[ai].kind->name,
553
            airEnumStr(man->ispec[ai].kind->enm, man->ispec[ai].item),
554
            ai, man->anum, man->san, man->sidx[ai], man->san + man->sidx[ai],
555
            man->alen[ai]);
556
    */
557
1603800
    memcpy(man->san + man->sidx[ai],
558
           man->aptr[ai],
559
           man->alen[ai]*sizeof(double));
560
  }
561
  return;
562
237600
}
563
564
static int
565
multiAnswerCompare(multiAnswer *man1, multiAnswer *man2) {
566
  static const char me[]="multiAnswerCompare";
567
  unsigned int si, slen;
568
569
570
#if 1
571
237600
  if (man1->slen != man2->slen) {
572
    biffAddf(BKEY, "%s: man1->slen %u != man2->slen %u\n", me,
573
             man1->slen, man2->slen);
574
    return 1;
575
  }
576
  slen = man1->slen;
577
#else
578
  slen = AIR_MIN(man1->slen, man2->slen);
579
#endif
580
9385200
  for (si=0; si<slen; si++) {
581
4573800
    if (man1->san[si] != man2->san[si]) {
582
      /* HEY should track down which part of which answer,
583
         in man1 and man2, is different, which was the
584
         purpose of recording ispec and sidx */
585
      biffAddf(BKEY, "%s: man1->san[si] %.17g != man2->san[si] %.17g", me,
586
               man1->san[si], man2->san[si]);
587
      return 1;
588
    }
589
  }
590
118800
  return 0;
591
118800
}
592
593
/*
594
** setting up gageContexts for the first of the two tasks listed above: making
595
** sure per-component information is handled correctly.  NOTE: The combination
596
** of function calls made here is very atypical for a Teem-using program
597
*/
598
static int
599
updateQueryKernelSetTask1(gageContext *gctxComp[KIND_NUM],
600
                          gageContext *gctx[KIND_NUM], int gSetup,
601
                          multiAnswer *manComp[KIND_NUM],
602
                          multiAnswer *man[KIND_NUM],
603
                          NrrdKernel *kpack[3],
604
                          double support) {
605
  static const char me[]="updateQueryKernelSetTask1";
606
48
  double parm1[NRRD_KERNEL_PARMS_NUM], parmV[NRRD_KERNEL_PARMS_NUM];
607
  unsigned int kindIdx;
608
609
  if (4 != KIND_NUM) {
610
    biffAddf(BKEY, "%s: sorry, confused: KIND_NUM %u != 4",
611
             me, KIND_NUM);
612
    return 1;
613
  }
614
24
  parm1[0] = 1.0;
615
24
  parmV[0] = support;
616
24
  if (gSetup) {
617
80
    for (kindIdx=0; kindIdx<KIND_NUM; kindIdx++) {
618
32
      if (0 /* (no-op for formatting) */
619
32
          || gageKernelSet(gctxComp[kindIdx], gageKernel00, kpack[0], parm1)
620
64
          || gageKernelSet(gctxComp[kindIdx], gageKernel11, kpack[1], parm1)
621
64
          || gageKernelSet(gctxComp[kindIdx], gageKernel22, kpack[2], parm1)
622
64
          || gageKernelSet(gctx[kindIdx], gageKernel00, kpack[0], parmV)
623
64
          || gageKernelSet(gctx[kindIdx], gageKernel11, kpack[1], parmV)
624
64
          || gageKernelSet(gctx[kindIdx], gageKernel22, kpack[2], parmV)) {
625
        biffMovef(BKEY, GAGE, "%s: trouble setting kernel (kind %u)",
626
                  me, kindIdx);
627
        return 1;
628
      }
629
    }
630
    /* Note that the contexts for the diced-up volumes of components are always
631
       of kind gageKindScl, and all the items are from the gageScl* enum */
632
80
    for (kindIdx=0; kindIdx<KIND_NUM; kindIdx++) {
633
      unsigned int pvi;
634
      /*
635
        fprintf(stderr, "!%s: gctx[%u] has %u pvls\n", me, kindIdx, gctx[kindIdx]->pvlNum);
636
        fprintf(stderr, "!%s: gctxComp[%u] has %u pvls\n", me, kindIdx, gctxComp[kindIdx]->pvlNum);
637
      */
638
416
      for (pvi=0; pvi<gctxComp[kindIdx]->pvlNum; pvi++) {
639
176
        if (0 /* (no-op for formatting) */
640
176
            || gageQueryItemOn(gctxComp[kindIdx], gctxComp[kindIdx]->pvl[pvi], gageSclValue)
641
352
            || gageQueryItemOn(gctxComp[kindIdx], gctxComp[kindIdx]->pvl[pvi], gageSclGradVec)
642
352
            || gageQueryItemOn(gctxComp[kindIdx], gctxComp[kindIdx]->pvl[pvi], gageSclHessian)) {
643
          biffMovef(BKEY, GAGE, "%s: trouble setting query (kind %u) on pvi %u",
644
                    me, kindIdx, pvi);
645
          return 1;
646
        }
647
      }
648
32
      if (gageUpdate(gctxComp[kindIdx])) {
649
        biffMovef(BKEY, GAGE, "%s: trouble updating comp gctx %u",
650
                  me, kindIdx);
651
        return 1;
652
      }
653
32
    }
654
    /* For the original contexts, we have to use the kind-specific items that
655
       correspond to the values and derivatives */
656
8
    if (0 /* (no-op for formatting) */
657
8
        || gageQueryItemOn(gctx[KI_SCL], gctx[KI_SCL]->pvl[0], gageSclValue)
658
16
        || gageQueryItemOn(gctx[KI_SCL], gctx[KI_SCL]->pvl[0], gageSclGradVec)
659
16
        || gageQueryItemOn(gctx[KI_SCL], gctx[KI_SCL]->pvl[0], gageSclHessian)
660
661
16
        || gageQueryItemOn(gctx[KI_VEC], gctx[KI_VEC]->pvl[0], gageVecVector)
662
16
        || gageQueryItemOn(gctx[KI_VEC], gctx[KI_VEC]->pvl[0], gageVecJacobian)
663
16
        || gageQueryItemOn(gctx[KI_VEC], gctx[KI_VEC]->pvl[0], gageVecHessian)
664
665
16
        || gageQueryItemOn(gctx[KI_TEN], gctx[KI_TEN]->pvl[0], tenGageTensor)
666
16
        || gageQueryItemOn(gctx[KI_TEN], gctx[KI_TEN]->pvl[0], tenGageTensorGrad)
667
16
        || gageQueryItemOn(gctx[KI_TEN], gctx[KI_TEN]->pvl[0], tenGageS)
668
16
        || gageQueryItemOn(gctx[KI_TEN], gctx[KI_TEN]->pvl[0], tenGageSGradVec)
669
16
        || gageQueryItemOn(gctx[KI_TEN], gctx[KI_TEN]->pvl[0], tenGageHessian)
670
16
        || gageQueryItemOn(gctx[KI_TEN], gctx[KI_TEN]->pvl[0], tenGageSHessian)
671
672
16
        || gageQueryItemOn(gctx[KI_DWI], gctx[KI_DWI]->pvl[0], tenDwiGageAll)
673
16
        || gageQueryItemOn(gctx[KI_DWI], gctx[KI_DWI]->pvl[0], tenDwiGageTensorLLS)) {
674
      biffMovef(BKEY, GAGE, "%s: trouble setting item", me);
675
      return 1;
676
    }
677
80
    for (kindIdx=0; kindIdx<KIND_NUM; kindIdx++) {
678
32
      if (gageUpdate(gctx[kindIdx])) {
679
        biffMovef(BKEY, GAGE, "%s: trouble updating single gctx %u",
680
                  me, kindIdx);
681
        return 1;
682
      }
683
    }
684
  } /* if (gSetup) */
685
686
240
  for (kindIdx=0; kindIdx<KIND_NUM; kindIdx++) {
687
96
    multiAnswerInit(man[kindIdx]);
688
96
    multiAnswerInit(manComp[kindIdx]);
689
  }
690
240
  for (kindIdx=0; kindIdx<KIND_NUM; kindIdx++) {
691
    unsigned int pvi;
692
96
    multiAnswerLenSet(manComp[kindIdx], (KI_DWI != kindIdx ? 3 : 1)*gctxComp[kindIdx]->pvlNum);
693
1248
    for (pvi=0; pvi<gctxComp[kindIdx]->pvlNum; pvi++) {
694
1056
      multiAnswerAdd(manComp[kindIdx], pvi,
695
528
                     gctxComp[kindIdx], gctxComp[kindIdx]->pvl[pvi],
696
                     gageSclValue);
697
    }
698
96
    if (KI_DWI != kindIdx) {
699
672
      for (pvi=0; pvi<gctxComp[kindIdx]->pvlNum; pvi++) {
700
528
        multiAnswerAdd(manComp[kindIdx], pvi + gctxComp[kindIdx]->pvlNum,
701
264
                       gctxComp[kindIdx], gctxComp[kindIdx]->pvl[pvi],
702
                       gageSclGradVec);
703
      }
704
672
      for (pvi=0; pvi<gctxComp[kindIdx]->pvlNum; pvi++) {
705
528
        multiAnswerAdd(manComp[kindIdx], pvi + 2*gctxComp[kindIdx]->pvlNum,
706
264
                       gctxComp[kindIdx], gctxComp[kindIdx]->pvl[pvi],
707
                       gageSclHessian);
708
      }
709
    }
710
192
    multiAnswerLenSet(man[kindIdx], KI_DWI != kindIdx ? 3 : 1);
711

192
    switch(kindIdx) {
712
    case KI_SCL:
713
24
      multiAnswerAdd(man[kindIdx], 0, gctx[kindIdx], gctx[kindIdx]->pvl[0], gageSclValue);
714
24
      multiAnswerAdd(man[kindIdx], 1, gctx[kindIdx], gctx[kindIdx]->pvl[0], gageSclGradVec);
715
24
      multiAnswerAdd(man[kindIdx], 2, gctx[kindIdx], gctx[kindIdx]->pvl[0], gageSclHessian);
716
24
      break;
717
    case KI_VEC:
718
24
      multiAnswerAdd(man[kindIdx], 0, gctx[kindIdx], gctx[kindIdx]->pvl[0], gageVecVector);
719
24
      multiAnswerAdd(man[kindIdx], 1, gctx[kindIdx], gctx[kindIdx]->pvl[0], gageVecJacobian);
720
24
      multiAnswerAdd(man[kindIdx], 2, gctx[kindIdx], gctx[kindIdx]->pvl[0], gageVecHessian);
721
24
      break;
722
    case KI_TEN:
723
24
      multiAnswerAdd(man[kindIdx], 0, gctx[kindIdx], gctx[kindIdx]->pvl[0], tenGageTensor);
724
24
      multiAnswerAdd(man[kindIdx], 1, gctx[kindIdx], gctx[kindIdx]->pvl[0], tenGageTensorGrad);
725
24
      multiAnswerAdd(man[kindIdx], 2, gctx[kindIdx], gctx[kindIdx]->pvl[0], tenGageHessian);
726
24
      break;
727
    case KI_DWI:
728
24
      multiAnswerAdd(man[kindIdx], 0, gctx[kindIdx], gctx[kindIdx]->pvl[0], tenDwiGageAll);
729
24
      break;
730
    }
731
  }
732
733
24
  return 0;
734
24
}
735
736
static int
737
probeTask1(gageContext *gctxComp[KIND_NUM],
738
           gageContext *gctx[KIND_NUM],
739
           multiAnswer *manComp[KIND_NUM],
740
           multiAnswer *man[KIND_NUM],
741
           unsigned int probeNum, NrrdKernel *kpack[3],
742
           double ksupport) {
743
  static const char me[]="probeTask1";
744
  unsigned int kindIdx, probeIdx, errNumMax, tenErrNum, sclErrNum;
745
48
  double pos[3], upos[3], minp[3], maxp[3],
746
    tenDiff[7], tenAvg[7], tenErr, tenErrMax,
747
    vecDiff[3], vecAvg[3], vecErr, vecErrMax, vecErrNum,
748
    sclDiff, sclAvg, sclErr, sclErrMax, errNumFrac;
749
  const double *dwiTenEstP,
750
    *tenTenP, *tenTenNormP, *tenTenNormGradP,
751
    *sclSclP, *sclGradP, *vecVecP;
752
753
  ELL_3V_SET(minp, ksupport, ksupport, ksupport);
754
24
  ELL_3V_SET(maxp, volSize[0]-1-ksupport, volSize[1]-1-ksupport, volSize[2]-1-ksupport);
755
756
  /* this is all for task 1.5 */
757
24
  sclSclP         = gageAnswerPointer(gctx[KI_SCL], gctx[KI_SCL]->pvl[0], gageSclValue);
758
24
  sclGradP        = gageAnswerPointer(gctx[KI_SCL], gctx[KI_SCL]->pvl[0], gageSclGradVec);
759
24
  vecVecP         = gageAnswerPointer(gctx[KI_VEC], gctx[KI_VEC]->pvl[0], gageVecVector);
760
24
  tenTenP         = gageAnswerPointer(gctx[KI_TEN], gctx[KI_TEN]->pvl[0], tenGageTensor);
761
24
  tenTenNormP     = gageAnswerPointer(gctx[KI_TEN], gctx[KI_TEN]->pvl[0], tenGageS);
762
24
  tenTenNormGradP = gageAnswerPointer(gctx[KI_TEN], gctx[KI_TEN]->pvl[0], tenGageSGradVec);
763
24
  dwiTenEstP      = gageAnswerPointer(gctx[KI_DWI], gctx[KI_DWI]->pvl[0], tenDwiGageTensorLLS);
764
  tenErrNum = sclErrNum = 0;
765
  vecErrNum = 0.0;
766
  errNumFrac = 0.02;
767
768
24
  if (nrrdKernelBoxSupportDebug == kpack[0]) {
769
    /* not actually here for any derivatives, mainly to check on
770
       tensor esimation (in addition to usual check of multivariate as
771
       set of scalars) */
772
    tenErrMax = 0.0002;
773
3
    vecErrMax = AIR_NAN;
774
    sclErrMax = AIR_NAN;
775
24
  } else if (nrrdKernelCos4SupportDebug == kpack[0]) {
776
12
    tenErrMax = AIR_NAN;
777
    vecErrMax = AIR_NAN;
778
    sclErrMax = AIR_NAN;
779
21
  } else if (nrrdKernelCatmullRomSupportDebug == kpack[0]) {
780
    tenErrMax = 0.14;  /* honestly, not sure how meaningful this test
781
                          is, given how significant we're allowing the
782
                          error to be. might be more meaningful if
783
                          this was with comparison to a pre-computed
784
                          non-linear least-squares fit, instead of the
785
                          (log-based) linear least-squares.  */
786
    vecErrMax = 0.0016;
787
    sclErrMax = 0.0008;
788
  } else {
789
    biffAddf(BKEY, "%s: unexpected kpack[0] %s\n", me, kpack[0]->name);
790
    return 1;
791
  }
792
24
  errNumMax = AIR_CAST(unsigned int, errNumFrac*probeNum);
793
59448
  for (probeIdx=0; probeIdx<probeNum; probeIdx++) {
794
29700
    airHalton(upos, probeIdx+HALTON_BASE, airPrimeList + 0, 3);
795
29700
    pos[0] = AIR_AFFINE(0.0, upos[0], 1.0, minp[0], maxp[0]);
796
29700
    pos[1] = AIR_AFFINE(0.0, upos[1], 1.0, minp[1], maxp[1]);
797
29700
    pos[2] = AIR_AFFINE(0.0, upos[2], 1.0, minp[2], maxp[2]);
798
297000
    for (kindIdx=0; kindIdx<KIND_NUM; kindIdx++) {
799
#define PROBE(gctx, what, pos)                                          \
800
      if (gageProbeSpace((gctx), (pos)[0], (pos)[1], (pos)[2],          \
801
                         AIR_TRUE /* indexSpace */,                     \
802
                         AIR_FALSE /* clamp */)) {                      \
803
        biffAddf(BKEY, "%s: probe (%s) error(%d): %s", me,              \
804
                 what, (gctx)->errNum, (gctx)->errStr);                 \
805
        return 1;                                                       \
806
      }
807
808
118800
      PROBE(gctxComp[kindIdx], "comp", pos);
809
118800
      PROBE(gctx[kindIdx], "single", pos);
810
811
#undef PROBE
812
813
118800
      multiAnswerCollect(man[kindIdx]);
814
118800
      multiAnswerCollect(manComp[kindIdx]);
815
118800
      if (multiAnswerCompare(manComp[kindIdx], man[kindIdx])) {
816
        biffAddf(BKEY, "%s: on point %u of kindIdx %u", me,
817
                 probeIdx, kindIdx);
818
        return 1;
819
      }
820
    }
821
822
    /* see if probed tensors mostly agree with
823
       tensors estimated from probed DWIs */
824
29700
    if (AIR_EXISTS(tenErrMax)) {
825
16200
      TEN_T_SUB(tenDiff, dwiTenEstP, tenTenP);
826
16200
      TEN_T_LERP(tenAvg, 0.5, dwiTenEstP, tenTenP);
827
16200
      tenErr = TEN_T_NORM(tenDiff)/TEN_T_NORM(tenAvg);
828
16200
      if (tenErr > tenErrMax) {
829
132
        tenErrNum++;
830
132
        if (tenErrNum > errNumMax) {
831
          biffAddf(BKEY, "%s: (probe %u) tenErr %g > %g too many times %u > %u",
832
                   me, probeIdx, tenErr, tenErrMax, tenErrNum, errNumMax);
833
          return 1;
834
        }
835
      }
836
    }
837
838
    /* see if invariant gradient learned from tensor volume agrees with
839
       volume of pre-computed invariant gradients, and with
840
       gradient of pre-computed invariants */
841
29700
    if (AIR_EXISTS(vecErrMax)) {
842
11700
      ELL_3V_SUB(vecDiff, sclGradP, vecVecP);
843
11700
      ELL_3V_LERP(vecAvg, 0.5, sclGradP, vecVecP);
844
11700
      vecErr = ELL_3V_LEN(vecDiff)/sqrt(ELL_3V_LEN(vecAvg));
845
11700
      if (vecErr > vecErrMax) {
846
234
        vecErrNum += 0.5;
847
234
        if (vecErrNum > errNumMax) {
848
          biffAddf(BKEY, "%s: (probe %u) (A) vecErr %g > %g too many times %g > %u",
849
                   me, probeIdx, vecErr, vecErrMax, vecErrNum, errNumMax);
850
          return 1;
851
        }
852
      }
853
11700
      ELL_3V_SUB(vecDiff, tenTenNormGradP, vecVecP);
854
11700
      ELL_3V_LERP(vecAvg, 0.5, tenTenNormGradP, vecVecP);
855
11700
      vecErr = ELL_3V_LEN(vecDiff)/sqrt(ELL_3V_LEN(vecAvg));
856
11700
      if (vecErr > vecErrMax) {
857
123
        vecErrNum += 0.5;
858
123
        if (vecErrNum > errNumMax) {
859
          biffAddf(BKEY, "%s: (probe %u) (B) vecErr %g > %g too many times %g > %u",
860
                   me, probeIdx, vecErr, vecErrMax, vecErrNum, errNumMax);
861
          return 1;
862
        }
863
      }
864
    }
865
866
    /* see if invariant learned from tensor volume agrees with
867
       volume of precomputed invariants */
868
29700
    if (AIR_EXISTS(sclErrMax)) {
869
11700
      sclDiff = sclSclP[0] - tenTenNormP[0];
870
11700
      sclAvg = (sclSclP[0] + tenTenNormP[0])/2;
871
11700
      sclErr = AIR_ABS(sclDiff)/sqrt(AIR_ABS(sclAvg));
872
11700
      if (sclErr > sclErrMax) {
873
6
        sclErrNum++;
874
6
        if (sclErrNum > errNumMax) {
875
          biffAddf(BKEY, "%s: (probe %u) (B) sclErr %g > %g too many times %u > %u",
876
                   me, probeIdx, sclErr, sclErrMax, sclErrNum, errNumMax);
877
          return 1;
878
        }
879
      }
880
    }
881
  }
882
883
24
  return 0;
884
24
}
885
886
static const char *testInfo = "for testing gage";
887
888
int
889
main(int argc, const char **argv) {
890
  const char *me;
891
  char *err = NULL;
892
16
  hestOpt *hopt=NULL;
893
  hestParm *hparm;
894
  airArray *mop;
895
896
8
  const gageKind *kind[KIND_NUM];
897
8
  char name[KIND_NUM][AIR_STRLEN_SMALL] = { "scl", "vec", "ten", "dwi" };
898
8
  char nameComp[KIND_NUM][AIR_STRLEN_SMALL] = { "sclComp", "vecComp", "tenComp", "dwiComp" };
899
8
  char *kernS;
900
  gageKind *dwikind = NULL;
901
8
  gageContext *gctxComp[KIND_NUM], *gctxCompCopy[KIND_NUM],
902
    *gctx[KIND_NUM], *gctxCopy[KIND_NUM];
903
8
  multiAnswer *manComp[KIND_NUM], *man[KIND_NUM];
904
8
  Nrrd *nin[KIND_NUM],
905
    /* these are the volumes that are used in gctxComp[] */
906
    *nsclCopy, *nvecComp[3], *nctenComp[7], **ndwiComp,
907
    *ngrad;  /* need access to list of gradients used to make DWIs;
908
                (this is not the gradient of a scalar field) */
909
8
  double bval = 1000, noiseStdv=0.0001, ksupport;
910
8
  unsigned int kindIdx, probeNum,
911
    gradNum = 10; /* small number so that missing one will produce
912
                     a big reconstruction error */
913
8
  NrrdKernel *kpack[3];
914
915
8
  kind[0] = gageKindScl;
916
8
  kind[1] = gageKindVec;
917
8
  kind[2] = tenGageKind;
918
8
  kind[3] = NULL; /* dwi */
919
920
8
  me = argv[0];
921
8
  mop = airMopNew();
922
8
  hparm = hestParmNew();
923
8
  airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways);
924
  /* learn things from hest */
925
8
  hestOptAdd(&hopt, "supp", "r", airTypeDouble, 1, 1, &ksupport, "1.0",
926
             "kernel support");
927
8
  hestOptAdd(&hopt, "pnum", "N", airTypeUInt, 1, 1, &probeNum, "100",
928
             "# of probes");
929
8
  hestOptAdd(&hopt, "k", "kern", airTypeString, 1, 1, &kernS, NULL,
930
             "kernel to use; can be: box, cos, or ctmr");
931
8
  hestParseOrDie(hopt, argc-1, argv+1, hparm, me, testInfo,
932
                 AIR_TRUE, AIR_TRUE, AIR_TRUE);
933
8
  airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways);
934
8
  airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways);
935
936
8
  if (!strcmp(kernS, "box")) {
937
1
    ELL_3V_SET(kpack,
938
               nrrdKernelBoxSupportDebug,
939
               nrrdKernelZero,
940
               nrrdKernelZero);
941
8
  } else if (!strcmp(kernS, "cos")) {
942
4
    ELL_3V_SET(kpack,
943
               nrrdKernelCos4SupportDebug,
944
               nrrdKernelCos4SupportDebugD,
945
               nrrdKernelCos4SupportDebugDD);
946
7
  } else if (!strcmp(kernS, "ctmr")) {
947
3
    ELL_3V_SET(kpack,
948
               nrrdKernelCatmullRomSupportDebug,
949
               nrrdKernelCatmullRomSupportDebugD,
950
               nrrdKernelCatmullRomSupportDebugDD);
951
  } else {
952
    fprintf(stderr, "%s: kernel \"%s\" not recognized", me, kernS);
953
    airMopError(mop); return 1;
954
  }
955
8
  fprintf(stderr, "kpack = %s, %s, %s\n", kpack[0]->name, kpack[1]->name, kpack[2]->name);
956
957
  /* This was a tricky bug: Adding gageContextNix(gctx) to the mop
958
     as soon as a gctx is created makes sense.  But, in the first
959
     version of this code, gageContextNix was added to the mop
960
     BEFORE tenDwiGageKindNix was added, which meant that gageContextNix
961
     was being called AFTER tenDwiGageKindNix.  However, that meant
962
     that existing pvls had their pvl->kind being freed from under them,
963
     but gagePerVolumeNix certainly needs to look at and possibly call
964
     pvl->kind->pvlDataNix in order clean up pvl->data.  *Therefore*,
965
     we have to add tenDwiGageKindNix to the mop prior to gageContextNix,
966
     even though nothing about the (dyanamic) kind has been set yet.
967
     If we had something like smart pointers, then tenDwiGageKindNix
968
     would not have freed something that an extant pvl was using */
969
8
  dwikind = tenDwiGageKindNew();
970
8
  airMopAdd(mop, dwikind, (airMopper)tenDwiGageKindNix, airMopAlways);
971
972
#define GAGE_CTX_NEW(gg, mm)                                      \
973
  (gg) = gageContextNew();                                        \
974
  airMopAdd((mm), (gg), (airMopper)gageContextNix, airMopAlways); \
975
  gageParmSet((gg), gageParmRenormalize, AIR_FALSE);              \
976
  gageParmSet((gg), gageParmCheckIntegrals, AIR_TRUE)
977
978
80
  for (kindIdx=0; kindIdx<KIND_NUM; kindIdx++) {
979
32
    GAGE_CTX_NEW(gctx[kindIdx], mop);
980
32
    GAGE_CTX_NEW(gctxComp[kindIdx], mop);
981
    /*
982
    fprintf(stderr, "%s: kind %s (%u): gctx=%p, gctxComp=%p\n", me,
983
            kindStr[kindIdx], kindIdx,
984
            AIR_VOIDP(gctx[kindIdx]),
985
            AIR_VOIDP(gctxComp[kindIdx]));
986
    */
987
32
    man[kindIdx] = multiAnswerNew(name[kindIdx]);
988
32
    manComp[kindIdx] = multiAnswerNew(nameComp[kindIdx]);
989
32
    airMopAdd(mop, man[kindIdx], (airMopper)multiAnswerNix, airMopAlways);
990
32
    airMopAdd(mop, manComp[kindIdx], (airMopper)multiAnswerNix, airMopAlways);
991
  }
992
#undef GAGE_CTX_NEW
993
994

120
  for (kindIdx=0; kindIdx<KIND_NUM; kindIdx++) {
995
72
    NRRD_NEW(nin[kindIdx], mop);
996
  }
997
8
  NRRD_NEW(ngrad, mop);
998
8
  NRRD_NEW(nsclCopy, mop);
999
24
  if (engageGenTensor(gctx[KI_TEN], nin[KI_TEN],
1000
                      /* out/in */
1001
                      noiseStdv, 42 /* RNG seed, could be a command-line option */,
1002
8
                      volSize[0], volSize[1], volSize[2])
1003
24
      || engageGenScalar(gctx[KI_SCL], nin[KI_SCL],
1004
8
                         gctxComp[KI_SCL], nsclCopy,
1005
                         /* out/in */
1006
8
                         nin[KI_TEN])
1007
24
      || engageGenVector(gctx[KI_VEC], nin[KI_VEC],
1008
                         /* out/in */
1009
8
                         nin[KI_SCL])
1010
      /* engage'ing of nin[KI_DWI] done below */
1011
24
      || genDwi(nin[KI_DWI], ngrad,
1012
                /* out/in */
1013
8
                gradNum /* for B0 */, bval, nin[KI_TEN])
1014
24
      || engageMopDiceVector(gctxComp[KI_VEC], nvecComp,
1015
                             /* out/in */
1016
8
                             mop, nin[KI_VEC])
1017
24
      || engageMopDiceTensor(gctxComp[KI_TEN], nctenComp,
1018
                             /* out/in */
1019
8
                             mop, nin[KI_TEN])
1020
24
      || engageMopDiceDwi(gctxComp[KI_DWI], &ndwiComp,
1021
                          /* out/in */
1022
8
                          mop, nin[KI_DWI])) {
1023
    airMopAdd(mop, err = biffGetDone(BKEY), airFree, airMopAlways);
1024
    fprintf(stderr, "%s: trouble creating volumes:\n%s", me, err);
1025
    airMopError(mop); return 1;
1026
  }
1027
8
  if (tenDwiGageKindSet(dwikind, -1 /* thresh */, 0 /* soft */,
1028
                        bval, 1 /* valueMin */,
1029
                        ngrad, NULL,
1030
                        tenEstimate1MethodLLS,
1031
                        tenEstimate2MethodPeled,
1032
                        424242)) {
1033
    airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways);
1034
    fprintf(stderr, "%s: trouble creating DWI kind:\n%s", me, err);
1035
    airMopError(mop); return 1;
1036
  }
1037
  /* access through kind[] is const, but not through dwikind */
1038
8
  kind[KI_DWI] = dwikind;
1039
  /* engage dwi vol */
1040
  {
1041
    gagePerVolume *pvl;
1042
16
    if ( !(pvl = gagePerVolumeNew(gctx[KI_DWI], nin[KI_DWI], kind[KI_DWI]))
1043
16
         || gagePerVolumeAttach(gctx[KI_DWI], pvl) ) {
1044
      airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways);
1045
      fprintf(stderr, "%s: trouble creating DWI context:\n%s", me, err);
1046
      airMopError(mop); return 1;
1047
    }
1048
    /*
1049
    fprintf(stderr, "%s: %s gctx=%p gets pvl %p\n", me,
1050
            kindStr[KI_DWI], AIR_VOIDP(gctx[KI_DWI]), AIR_VOIDP(pvl));
1051
    */
1052
8
  }
1053
1054
  /* make sure kinds can parse back to themselves */
1055
  /* the messiness here is in part because of problems with the gage
1056
     API, and the weirdness of how meetGageKindParse is either allocating
1057
     something, or not, depending on its input.  There is no way to
1058
     refer to the "name" of a dwi kind without having allocated something. */
1059
80
  for (kindIdx=0; kindIdx<KIND_NUM; kindIdx++) {
1060
32
    if (!(kind[kindIdx]->dynamicAlloc)) {
1061
24
      if (kind[kindIdx] != meetGageKindParse(kind[kindIdx]->name)) {
1062
        fprintf(stderr, "%s: static kind[%u]->name %s wasn't parsed\n", me,
1063
                kindIdx, kind[kindIdx]->name);
1064
        airMopError(mop); return 1;
1065
      }
1066
    } else {
1067
      gageKind *ktmp;
1068
8
      ktmp = meetGageKindParse(kind[kindIdx]->name);
1069
8
      if (!ktmp) {
1070
        fprintf(stderr, "%s: dynamic kind[%u]->name %s wasn't parsed\n", me,
1071
                kindIdx, kind[kindIdx]->name);
1072
        airMopError(mop); return 1;
1073
      }
1074
8
      if (airStrcmp(ktmp->name, kind[kindIdx]->name)) {
1075
        fprintf(stderr, "%s: parsed dynamic kind[%u]->name %s didn't match %s\n", me,
1076
                kindIdx, ktmp->name, kind[kindIdx]->name);
1077
        airMopError(mop); return 1;
1078
      }
1079
8
      if (!airStrcmp(TEN_DWI_GAGE_KIND_NAME, ktmp->name)) {
1080
        /* HEY: there needs to be a generic way of freeing
1081
           a dynamic kind, perhaps a new nixer field in gageKind */
1082
8
        ktmp = tenDwiGageKindNix(ktmp);
1083
8
      }
1084
8
    }
1085
  }
1086
  /*
1087
  for (kindIdx=0; kindIdx<KIND_NUM; kindIdx++) {
1088
    printf("%s: %s (%s) -> (%u) %u\n", me, kind[kindIdx]->name,
1089
           kind[kindIdx]->dynamicAlloc ? "dynamic" : "static",
1090
           kind[kindIdx]->baseDim, kind[kindIdx]->valLen);
1091
  }
1092
  */
1093
1094
  /*
1095
  nrrdSave("tmp-cten.nrrd", nin[KI_TEN], NULL);
1096
  nrrdSave("tmp-scl.nrrd", nin[KI_SCL], NULL);
1097
  nrrdSave("tmp-vec.nrrd", nin[KI_VEC], NULL);
1098
  nrrdSave("tmp-dwi.nrrd", nin[KI_DWI], NULL);
1099
  */
1100
1101
  /* ========================== TASK 1 */
1102
16
  if (updateQueryKernelSetTask1(gctxComp, gctx, AIR_TRUE, manComp, man, kpack, ksupport)
1103
16
      || probeTask1(gctxComp, gctx, manComp, man, probeNum, kpack, ksupport)) {
1104
    airMopAdd(mop, err = biffGetDone(BKEY), airFree, airMopAlways);
1105
    fprintf(stderr, "%s: trouble (orig, orig):\n%s", me, err);
1106
    airMopError(mop); return 1;
1107
  }
1108
  /* testing gageContextCopy on multi-variate volumes */
1109
80
  for (kindIdx=0; kindIdx<KIND_NUM; kindIdx++) {
1110
32
    if (!( gctxCopy[kindIdx] = gageContextCopy(gctx[kindIdx]) )) {
1111
      airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways);
1112
      fprintf(stderr, "%s: trouble w/ multi-variate contexts:\n%s", me, err);
1113
      airMopError(mop); return 1;
1114
    }
1115
32
    airMopAdd(mop, gctxCopy[kindIdx], (airMopper)gageContextNix, airMopAlways);
1116
  }
1117
16
  if (updateQueryKernelSetTask1(gctxComp, gctxCopy, AIR_FALSE, manComp, man, kpack, ksupport)
1118
16
      || probeTask1(gctxComp, gctxCopy, manComp, man, probeNum, kpack, ksupport)) {
1119
    airMopAdd(mop, err = biffGetDone(BKEY), airFree, airMopAlways);
1120
    fprintf(stderr, "%s: trouble (orig, copy):\n%s", me, err);
1121
    airMopError(mop); return 1;
1122
  }
1123
80
  for (kindIdx=0; kindIdx<KIND_NUM; kindIdx++) {
1124
32
    if (!( gctxCompCopy[kindIdx] = gageContextCopy(gctxComp[kindIdx]) )) {
1125
      airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways);
1126
      fprintf(stderr, "%s: trouble w/ component contexts:\n%s", me, err);
1127
      airMopError(mop); return 1;
1128
    }
1129
32
    airMopAdd(mop, gctxCompCopy[kindIdx], (airMopper)gageContextNix, airMopAlways);
1130
  }
1131
16
  if (updateQueryKernelSetTask1(gctxCompCopy, gctxCopy, AIR_FALSE, manComp, man, kpack, ksupport)
1132
16
      || probeTask1(gctxCompCopy, gctxCopy, manComp, man, probeNum, kpack, ksupport)) {
1133
    airMopAdd(mop, err = biffGetDone(BKEY), airFree, airMopAlways);
1134
    fprintf(stderr, "%s: trouble (copy, copy):\n%s", me, err);
1135
    airMopError(mop); return 1;
1136
  }
1137
1138
  /* ========================== TASK 2 */
1139
  /* create scale-space stacks with tent, ctmr, and hermite */
1140
  /* save them, free, and read them back in */
1141
  /* gageContextCopy on stack */
1142
  /* pick a scale in-between tau samples */
1143
  /* for all the tau's half-way between tau samples in scale:
1144
       blur at that tau to get correct values
1145
       check that error with hermite is lower than ctmr is lower than tent */
1146
  /* for all tau samples:
1147
       blur at that tau to (re-)get correct values
1148
       check that everything agrees there */
1149
1150
1151
  /* single probe with high verbosity */
1152
1153
8
  airMopOkay(mop);
1154
8
  return 0;
1155
8
}
1156
#undef NRRD_NEW