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 |