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 "ten.h" |
25 |
|
|
#include "privateTen.h" |
26 |
|
|
|
27 |
|
|
#if TEEM_LEVMAR |
28 |
|
|
#include <levmar.h> |
29 |
|
|
#endif |
30 |
|
|
|
31 |
|
|
/* --------------------------------------------------------------------- */ |
32 |
|
|
|
33 |
|
|
const char * |
34 |
|
|
_tenDwiGageStr[] = { |
35 |
|
|
"(unknown tenDwiGage)", |
36 |
|
|
"all", |
37 |
|
|
"b0", |
38 |
|
|
"jdwi", |
39 |
|
|
"adc", |
40 |
|
|
"mdwi", |
41 |
|
|
"tlls", |
42 |
|
|
"tllserr", |
43 |
|
|
"tllserrlog", |
44 |
|
|
"tllslike", |
45 |
|
|
"twls", |
46 |
|
|
"twlserr", |
47 |
|
|
"twlserrlog", |
48 |
|
|
"twlslike", |
49 |
|
|
"tnls", |
50 |
|
|
"tnlserr", |
51 |
|
|
"tnlserrlog", |
52 |
|
|
"tnlslike", |
53 |
|
|
"tmle", |
54 |
|
|
"tmleerr", |
55 |
|
|
"tmleerrlog", |
56 |
|
|
"tmlelike", |
57 |
|
|
"t", |
58 |
|
|
"terr", |
59 |
|
|
"terrlog", |
60 |
|
|
"tlike", |
61 |
|
|
"c", |
62 |
|
|
"fa", |
63 |
|
|
"adwie", |
64 |
|
|
"2qs", |
65 |
|
|
"2qserr", |
66 |
|
|
"2qsnerr", |
67 |
|
|
"2peled", |
68 |
|
|
"2pelederr", |
69 |
|
|
"2pelednerr", |
70 |
|
|
"2peledlminfo", |
71 |
|
|
}; |
72 |
|
|
|
73 |
|
|
const int |
74 |
|
|
_tenDwiGageVal[] = { |
75 |
|
|
tenDwiGageUnknown, |
76 |
|
|
tenDwiGageAll, |
77 |
|
|
tenDwiGageB0, |
78 |
|
|
tenDwiGageJustDWI, |
79 |
|
|
tenDwiGageADC, |
80 |
|
|
tenDwiGageMeanDWIValue, |
81 |
|
|
tenDwiGageTensorLLS, |
82 |
|
|
tenDwiGageTensorLLSError, |
83 |
|
|
tenDwiGageTensorLLSErrorLog, |
84 |
|
|
tenDwiGageTensorLLSLikelihood, |
85 |
|
|
tenDwiGageTensorWLS, |
86 |
|
|
tenDwiGageTensorWLSError, |
87 |
|
|
tenDwiGageTensorWLSErrorLog, |
88 |
|
|
tenDwiGageTensorWLSLikelihood, |
89 |
|
|
tenDwiGageTensorNLS, |
90 |
|
|
tenDwiGageTensorNLSError, |
91 |
|
|
tenDwiGageTensorNLSErrorLog, |
92 |
|
|
tenDwiGageTensorNLSLikelihood, |
93 |
|
|
tenDwiGageTensorMLE, |
94 |
|
|
tenDwiGageTensorMLEError, |
95 |
|
|
tenDwiGageTensorMLEErrorLog, |
96 |
|
|
tenDwiGageTensorMLELikelihood, |
97 |
|
|
tenDwiGageTensor, |
98 |
|
|
tenDwiGageTensorError, |
99 |
|
|
tenDwiGageTensorErrorLog, |
100 |
|
|
tenDwiGageTensorLikelihood, |
101 |
|
|
tenDwiGageConfidence, |
102 |
|
|
tenDwiGageFA, |
103 |
|
|
tenDwiGageTensorAllDWIError, |
104 |
|
|
tenDwiGage2TensorQSeg, |
105 |
|
|
tenDwiGage2TensorQSegError, |
106 |
|
|
tenDwiGage2TensorQSegAndError, |
107 |
|
|
tenDwiGage2TensorPeled, |
108 |
|
|
tenDwiGage2TensorPeledError, |
109 |
|
|
tenDwiGage2TensorPeledAndError, |
110 |
|
|
tenDwiGage2TensorPeledLevmarInfo |
111 |
|
|
}; |
112 |
|
|
|
113 |
|
|
const airEnum |
114 |
|
|
_tenDwiGage = { |
115 |
|
|
"tenDwiGage", |
116 |
|
|
TEN_DWI_GAGE_ITEM_MAX, |
117 |
|
|
_tenDwiGageStr, _tenDwiGageVal, |
118 |
|
|
NULL, |
119 |
|
|
NULL, NULL, |
120 |
|
|
AIR_FALSE |
121 |
|
|
}; |
122 |
|
|
const airEnum *const |
123 |
|
|
tenDwiGage = &_tenDwiGage; |
124 |
|
|
|
125 |
|
|
/* --------------------------------------------------------------------- */ |
126 |
|
|
|
127 |
|
|
gageItemEntry |
128 |
|
|
_tenDwiGageTable[TEN_DWI_GAGE_ITEM_MAX+1] = { |
129 |
|
|
/* enum value len,deriv, prereqs, parent item, parent index, needData */ |
130 |
|
|
{tenDwiGageUnknown, 0, 0, {0}, 0, 0, AIR_TRUE}, |
131 |
|
|
/* len == 0 for tenDwiGage{All,JustDWI,ADC} means "learn later at run-time" */ |
132 |
|
|
{tenDwiGageAll, 0, 0, {0}, 0, 0, AIR_TRUE}, |
133 |
|
|
{tenDwiGageB0, 1, 0, {tenDwiGageAll}, tenDwiGageAll, 0, AIR_TRUE}, |
134 |
|
|
{tenDwiGageJustDWI, 0, 0, {tenDwiGageAll}, tenDwiGageAll, 1, AIR_TRUE}, |
135 |
|
|
{tenDwiGageADC, 0, 0, {tenDwiGageB0, tenDwiGageJustDWI}, 0, 0, AIR_TRUE}, |
136 |
|
|
|
137 |
|
|
{tenDwiGageMeanDWIValue, 1, 0, {tenDwiGageAll}, 0, 0, AIR_TRUE}, |
138 |
|
|
|
139 |
|
|
{tenDwiGageTensorLLS, 7, 0, {tenDwiGageAll, tenDwiGageMeanDWIValue}, 0, 0, AIR_TRUE}, |
140 |
|
|
{tenDwiGageTensorLLSError, 1, 0, {tenDwiGageTensorLLS}, 0, 0, AIR_TRUE}, |
141 |
|
|
{tenDwiGageTensorLLSErrorLog, 1, 0, {tenDwiGageTensorLLS}, 0, 0, AIR_TRUE}, |
142 |
|
|
{tenDwiGageTensorLLSLikelihood, 1, 0, {tenDwiGageTensorLLS}, 0, 0, AIR_TRUE}, |
143 |
|
|
|
144 |
|
|
{tenDwiGageTensorWLS, 7, 0, {tenDwiGageAll, tenDwiGageMeanDWIValue}, 0, 0, AIR_TRUE}, |
145 |
|
|
{tenDwiGageTensorWLSError, 1, 0, {tenDwiGageTensorWLS}, 0, 0, AIR_TRUE}, |
146 |
|
|
{tenDwiGageTensorWLSErrorLog, 1, 0, {tenDwiGageTensorWLS}, 0, 0, AIR_TRUE}, |
147 |
|
|
{tenDwiGageTensorWLSLikelihood, 1, 0, {tenDwiGageTensorWLS}, 0, 0, AIR_TRUE}, |
148 |
|
|
|
149 |
|
|
{tenDwiGageTensorNLS, 7, 0, {tenDwiGageAll, tenDwiGageMeanDWIValue}, 0, 0, AIR_TRUE}, |
150 |
|
|
{tenDwiGageTensorNLSError, 1, 0, {tenDwiGageTensorNLS}, 0, 0, AIR_TRUE}, |
151 |
|
|
{tenDwiGageTensorNLSErrorLog, 1, 0, {tenDwiGageTensorNLS}, 0, 0, AIR_TRUE}, |
152 |
|
|
{tenDwiGageTensorNLSLikelihood, 1, 0, {tenDwiGageTensorNLS}, 0, 0, AIR_TRUE}, |
153 |
|
|
|
154 |
|
|
{tenDwiGageTensorMLE, 7, 0, {tenDwiGageAll, tenDwiGageMeanDWIValue}, 0, 0, AIR_TRUE}, |
155 |
|
|
{tenDwiGageTensorMLEError, 1, 0, {tenDwiGageTensorMLE}, 0, 0, AIR_TRUE}, |
156 |
|
|
{tenDwiGageTensorMLEErrorLog, 1, 0, {tenDwiGageTensorMLE}, 0, 0, AIR_TRUE}, |
157 |
|
|
{tenDwiGageTensorMLELikelihood, 1, 0, {tenDwiGageTensorMLE}, 0, 0, AIR_TRUE}, |
158 |
|
|
|
159 |
|
|
/* these are NOT sub-items: they are copies, as controlled by the |
160 |
|
|
kind->data, but not the query: the query can't capture the kind |
161 |
|
|
of dependency implemented by having a dynamic kind */ |
162 |
|
|
{tenDwiGageTensor, 7, 0, {0}, /* 0 == "learn later at run time" */ 0, 0, AIR_TRUE}, |
163 |
|
|
{tenDwiGageTensorError, 1, 0, {0}, 0, 0, AIR_TRUE}, |
164 |
|
|
{tenDwiGageTensorErrorLog, 1, 0, {0}, 0, 0, AIR_TRUE}, |
165 |
|
|
{tenDwiGageTensorLikelihood, 1, 0, {0}, 0, 0, AIR_TRUE}, |
166 |
|
|
|
167 |
|
|
/* back to normal non-run-time items */ |
168 |
|
|
{tenDwiGageConfidence, 1, 0, {tenDwiGageTensor}, tenDwiGageTensor, 0, AIR_TRUE}, |
169 |
|
|
{tenDwiGageFA, 1, 0, {tenDwiGageTensor}, 0, 0, AIR_TRUE}, |
170 |
|
|
|
171 |
|
|
{tenDwiGageTensorAllDWIError, 0, 0, {tenDwiGageTensor, tenDwiGageJustDWI}, 0, 0, AIR_TRUE}, |
172 |
|
|
|
173 |
|
|
/* it actually doesn't make sense for tenDwiGage2TensorQSegAndError to be the parent, |
174 |
|
|
because of the situations where you want the q-seg result, but don't care about error */ |
175 |
|
|
{tenDwiGage2TensorQSeg, 14, 0, {tenDwiGageAll}, 0, 0, AIR_TRUE}, |
176 |
|
|
{tenDwiGage2TensorQSegError, 1, 0, {tenDwiGageAll, tenDwiGage2TensorQSeg}, 0, 0, AIR_TRUE}, |
177 |
|
|
{tenDwiGage2TensorQSegAndError, 15, 0, {tenDwiGage2TensorQSeg, tenDwiGage2TensorQSegError}, 0, 0, AIR_TRUE}, |
178 |
|
|
|
179 |
|
|
{tenDwiGage2TensorPeled, 14, 0, {tenDwiGageAll}, 0, 0, AIR_TRUE}, |
180 |
|
|
{tenDwiGage2TensorPeledError, 1, 0, {tenDwiGageAll, tenDwiGage2TensorPeled}, 0, 0, AIR_TRUE}, |
181 |
|
|
{tenDwiGage2TensorPeledAndError, 15, 0, {tenDwiGage2TensorPeled, tenDwiGage2TensorPeledError}, 0, 0, AIR_TRUE}, |
182 |
|
|
|
183 |
|
|
{tenDwiGage2TensorPeledLevmarInfo, 5, 0, {tenDwiGage2TensorPeled}, 0, 0, AIR_TRUE} |
184 |
|
|
}; |
185 |
|
|
|
186 |
|
|
void |
187 |
|
|
_tenDwiGageIv3Print(FILE *file, gageContext *ctx, gagePerVolume *pvl) { |
188 |
|
|
static const char me[]="_tenDwiGageIv3Print"; |
189 |
|
|
|
190 |
|
|
AIR_UNUSED(ctx); |
191 |
|
|
AIR_UNUSED(pvl); |
192 |
|
|
fprintf(file, "%s: sorry, unimplemented\n", me); |
193 |
|
|
return; |
194 |
|
|
} |
195 |
|
|
|
196 |
|
|
void |
197 |
|
|
_tenDwiGageFilter(gageContext *ctx, gagePerVolume *pvl) { |
198 |
|
|
static const char me[]="_tenDwiGageFilter"; |
199 |
|
|
double *fw00, *fw11, *fw22, *dwi; |
200 |
|
59400 |
int fd, needD[3]={AIR_TRUE, AIR_FALSE, AIR_FALSE}; |
201 |
|
|
/* tenDwiGageKindData *kindData; */ |
202 |
|
29700 |
gageScl3PFilter_t *filter[5] = {NULL, gageScl3PFilter2, gageScl3PFilter4, |
203 |
|
|
gageScl3PFilter6, gageScl3PFilter8}; |
204 |
|
|
unsigned int J, dwiNum; |
205 |
|
|
|
206 |
|
29700 |
fd = 2*ctx->radius; |
207 |
|
29700 |
dwi = pvl->directAnswer[tenDwiGageAll]; |
208 |
|
|
/* kindData = AIR_CAST(tenDwiGageKindData *, pvl->kind->data); */ |
209 |
|
29700 |
dwiNum = pvl->kind->valLen; |
210 |
✗✓ |
29700 |
if (!ctx->parm.k3pack) { |
211 |
|
|
fprintf(stderr, "%s: sorry, 6pack filtering not implemented\n", me); |
212 |
|
|
return; |
213 |
|
|
} |
214 |
|
29700 |
fw00 = ctx->fw + fd*3*gageKernel00; |
215 |
|
29700 |
fw11 = ctx->fw + fd*3*gageKernel11; |
216 |
|
29700 |
fw22 = ctx->fw + fd*3*gageKernel22; |
217 |
|
|
/* HEY: these will have to be updated if there is ever any use for |
218 |
|
|
derivatives in DWIs: can't pass NULL pointers for gradient info. |
219 |
|
|
The unusual use of a hard-coded local needD is because there |
220 |
|
|
currently isn't allocated space in the tenDwiGage kind (which is |
221 |
|
|
unusual for its dynamic allocation) for DWI derivatives */ |
222 |
✓✓ |
29700 |
if (fd <= 8) { |
223 |
✓✓ |
538200 |
for (J=0; J<dwiNum; J++) { |
224 |
|
514800 |
filter[ctx->radius](ctx->shape, pvl->iv3 + J*fd*fd*fd, |
225 |
|
257400 |
pvl->iv2 + J*fd*fd, |
226 |
|
257400 |
pvl->iv1 + J*fd, |
227 |
|
|
fw00, fw11, fw22, |
228 |
|
257400 |
dwi + J, NULL, NULL, |
229 |
|
257400 |
needD); |
230 |
|
|
} |
231 |
|
|
} else { |
232 |
✓✓ |
144900 |
for (J=0; J<dwiNum; J++) { |
233 |
|
138600 |
gageScl3PFilterN(ctx->shape, fd, pvl->iv3 + J*fd*fd*fd, |
234 |
|
69300 |
pvl->iv2 + J*fd*fd, pvl->iv1 + J*fd, |
235 |
|
|
fw00, fw11, fw22, |
236 |
|
69300 |
dwi + J, NULL, NULL, |
237 |
|
69300 |
needD); |
238 |
|
|
} |
239 |
|
|
} |
240 |
|
|
|
241 |
|
29700 |
return; |
242 |
|
29700 |
} |
243 |
|
|
|
244 |
|
|
/* Returns the Akaike Information Criterion */ |
245 |
|
|
|
246 |
|
|
/* |
247 |
|
|
** residual: is the variance |
248 |
|
|
** n: number of observations: number of DWI's in our case |
249 |
|
|
** k: number of parameters: number of tensor components in our case |
250 |
|
|
*/ |
251 |
|
|
double |
252 |
|
|
_tenComputeAIC(double residual, int n, int k) { |
253 |
|
|
double AIC = 0; |
254 |
|
|
|
255 |
|
|
if (residual == 0) { |
256 |
|
|
return 0; |
257 |
|
|
} |
258 |
|
|
|
259 |
|
|
/* AIC, RSS used when doing regression */ |
260 |
|
|
AIC = 2*k + n*log(residual); |
261 |
|
|
/* Always use bias adjustment */ |
262 |
|
|
/* if (n/k < 40) { */ |
263 |
|
|
AIC = AIC + ((2*k*(k + 1))/(n - k - 1)); |
264 |
|
|
/* } */ |
265 |
|
|
|
266 |
|
|
return AIC; |
267 |
|
|
} |
268 |
|
|
|
269 |
|
|
/* Form a 2D tensor from the parameters */ |
270 |
|
|
void |
271 |
|
|
_tenPeledRotate2D(double ten[7], double lam1, double lam3, double phi) { |
272 |
|
|
double cc, ss, d3, d1, d2; |
273 |
|
|
|
274 |
|
|
cc = cos(phi); |
275 |
|
|
ss = sin(phi); |
276 |
|
|
d1 = cc*cc*lam1 + ss*ss*lam3; |
277 |
|
|
d3 = cc*ss*(lam1 - lam3); |
278 |
|
|
d2 = ss*ss*lam1 + cc*cc*lam3; |
279 |
|
|
|
280 |
|
|
TEN_T_SET(ten, 1.0, d1, d3, 0, d2, 0, lam3); |
281 |
|
|
return; |
282 |
|
|
} |
283 |
|
|
|
284 |
|
|
/* The main callback function that is iterated during levmar */ |
285 |
|
|
|
286 |
|
|
/* vector pp of parameters is as follows: |
287 |
|
|
** pp[0]: principal eigenvalue |
288 |
|
|
** pp[1]: fraction of 1st tensor |
289 |
|
|
** pp[2]: phi for 1st tensor |
290 |
|
|
** pp[3]: phi for 2nd tensor |
291 |
|
|
*/ |
292 |
|
|
void |
293 |
|
|
_tenLevmarPeledCB(double *pp, double *xx, int mm, int nn, void *_pvlData) { |
294 |
|
|
/* static const char me[]="_tenLevmarPeledCB"; */ |
295 |
|
|
double tenA[7], tenB[7]; |
296 |
|
|
int ii; |
297 |
|
|
tenDwiGagePvlData *pvlData; |
298 |
|
|
double *egrad; |
299 |
|
|
|
300 |
|
|
AIR_UNUSED(mm); |
301 |
|
|
pvlData = AIR_CAST(tenDwiGagePvlData *, _pvlData); |
302 |
|
|
|
303 |
|
|
/* Form the tensors using the estimated parms */ |
304 |
|
|
_tenPeledRotate2D(tenA, pp[0], pvlData->ten1Eval[2], pp[2]); |
305 |
|
|
_tenPeledRotate2D(tenB, pp[0], pvlData->ten1Eval[2], pp[3]); |
306 |
|
|
|
307 |
|
|
egrad = AIR_CAST(double *, pvlData->nten1EigenGrads->data); |
308 |
|
|
/* skip past b0 gradient, HEY: not general purpose */ |
309 |
|
|
egrad += 3; |
310 |
|
|
for (ii=0; ii<nn; ii++) { |
311 |
|
|
double argA, argB, sigA, sigB; |
312 |
|
|
argA = -pvlData->tec1->bValue*TEN_T3V_CONTR(tenA, egrad + 3*ii); |
313 |
|
|
argB = -pvlData->tec1->bValue*TEN_T3V_CONTR(tenB, egrad + 3*ii); |
314 |
|
|
if (pvlData->levmarUseFastExp) { |
315 |
|
|
sigA = airFastExp(argA); |
316 |
|
|
sigB = airFastExp(argB); |
317 |
|
|
} else { |
318 |
|
|
sigA = exp(argA); |
319 |
|
|
sigB = exp(argB); |
320 |
|
|
} |
321 |
|
|
xx[ii] = pvlData->tec1->knownB0*(pp[1]*sigA + (1-pp[1])*sigB); |
322 |
|
|
} |
323 |
|
|
return; |
324 |
|
|
} |
325 |
|
|
|
326 |
|
|
void |
327 |
|
|
_tenDwiGageAnswer(gageContext *ctx, gagePerVolume *pvl) { |
328 |
|
|
static const char me[]="_tenDwiGageAnswer"; |
329 |
|
|
unsigned int dwiIdx; |
330 |
|
|
tenDwiGageKindData *kindData; |
331 |
|
|
tenDwiGagePvlData *pvlData; |
332 |
|
59400 |
double *dwiAll, dwiMean=0, tentmp[7]; |
333 |
|
|
|
334 |
|
29700 |
kindData = AIR_CAST(tenDwiGageKindData *, pvl->kind->data); |
335 |
|
29700 |
pvlData = AIR_CAST(tenDwiGagePvlData *, pvl->data); |
336 |
|
|
|
337 |
|
29700 |
dwiAll = pvl->directAnswer[tenDwiGageAll]; |
338 |
✓✗ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageAll)) { |
339 |
|
|
/* done if doV */ |
340 |
✗✓ |
29700 |
if (ctx->verbose) { |
341 |
|
|
for (dwiIdx=0; dwiIdx<pvl->kind->valLen; dwiIdx++) { |
342 |
|
|
fprintf(stderr, "%s(%d+%g,%d+%g,%d+%g): dwi[%u] = %g\n", me, |
343 |
|
|
ctx->point.idx[0], ctx->point.frac[0], |
344 |
|
|
ctx->point.idx[1], ctx->point.frac[1], |
345 |
|
|
ctx->point.idx[2], ctx->point.frac[2], |
346 |
|
|
dwiIdx, dwiAll[dwiIdx]); |
347 |
|
|
} |
348 |
|
|
fprintf(stderr, "%s: type(ngrad) = %d = %s\n", me, |
349 |
|
|
kindData->ngrad->type, |
350 |
|
|
airEnumStr(nrrdType, kindData->ngrad->type)); |
351 |
|
|
} |
352 |
|
|
} |
353 |
|
|
|
354 |
|
|
/* |
355 |
|
|
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageB0)) { |
356 |
|
|
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageJustDWI)) { |
357 |
|
|
done if doV |
358 |
|
|
} |
359 |
|
|
*/ |
360 |
|
|
/* HEY this isn't valid for multiple b-values */ |
361 |
✗✓ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageADC)) { |
362 |
|
|
double logdwi, logb0; |
363 |
|
|
logb0 = log(AIR_MAX(kindData->valueMin, |
364 |
|
|
pvl->directAnswer[tenDwiGageB0][0])); |
365 |
|
|
for (dwiIdx=1; dwiIdx<pvl->kind->valLen; dwiIdx++) { |
366 |
|
|
logdwi = log(AIR_MAX(kindData->valueMin, |
367 |
|
|
pvl->directAnswer[tenDwiGageJustDWI][dwiIdx-1])); |
368 |
|
|
pvl->directAnswer[tenDwiGageADC][dwiIdx-1] |
369 |
|
|
= (logb0 - logdwi)/kindData->bval; |
370 |
|
|
} |
371 |
|
|
} |
372 |
✓✗ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageMeanDWIValue)) { |
373 |
|
|
dwiMean = 0; |
374 |
✓✓ |
653400 |
for (dwiIdx=1; dwiIdx<pvl->kind->valLen; dwiIdx++) { |
375 |
|
297000 |
dwiMean += dwiAll[dwiIdx]; |
376 |
|
|
} |
377 |
|
29700 |
dwiMean /= pvl->kind->valLen; |
378 |
|
29700 |
pvl->directAnswer[tenDwiGageMeanDWIValue][0] = dwiMean; |
379 |
|
29700 |
} |
380 |
|
|
|
381 |
|
|
/* note: the gage interface to tenEstimate functionality |
382 |
|
|
allows you exactly one kind of tensor estimation (per kind), |
383 |
|
|
so the function call to do the estimation is actually |
384 |
|
|
repeated over and over again; the copy into the answer |
385 |
|
|
buffer is what changes... */ |
386 |
✓✗ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLLS)) { |
387 |
|
29700 |
tenEstimate1TensorSingle_d(pvlData->tec1, tentmp, dwiAll); |
388 |
|
29700 |
TEN_T_COPY(pvl->directAnswer[tenDwiGageTensorLLS], tentmp); |
389 |
|
29700 |
} |
390 |
✗✓ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLLSError)) { |
391 |
|
|
pvl->directAnswer[tenDwiGageTensorLLSError][0] = pvlData->tec1->errorDwi; |
392 |
|
|
} |
393 |
✗✓ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLLSErrorLog)) { |
394 |
|
|
pvl->directAnswer[tenDwiGageTensorLLSErrorLog][0] |
395 |
|
|
= pvlData->tec1->errorLogDwi; |
396 |
|
|
} |
397 |
✗✓ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorWLS)) { |
398 |
|
|
tenEstimate1TensorSingle_d(pvlData->tec1, tentmp, dwiAll); |
399 |
|
|
TEN_T_COPY(pvl->directAnswer[tenDwiGageTensorWLS], tentmp); |
400 |
|
|
} |
401 |
✗✓ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorNLS)) { |
402 |
|
|
tenEstimate1TensorSingle_d(pvlData->tec1, tentmp, dwiAll); |
403 |
|
|
TEN_T_COPY(pvl->directAnswer[tenDwiGageTensorNLS], tentmp); |
404 |
|
|
} |
405 |
✗✓ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorMLE)) { |
406 |
|
|
tenEstimate1TensorSingle_d(pvlData->tec1, tentmp, dwiAll); |
407 |
|
|
TEN_T_COPY(pvl->directAnswer[tenDwiGageTensorMLE], tentmp); |
408 |
|
|
} |
409 |
|
|
/* HEY: have to implement all the different kinds of errors */ |
410 |
|
|
|
411 |
|
|
/* BEGIN sneakiness ........ */ |
412 |
✗✓ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensor)) { |
413 |
|
|
gageItemEntry *item; |
414 |
|
|
item = pvl->kind->table + tenDwiGageTensor; |
415 |
|
|
TEN_T_COPY(pvl->directAnswer[tenDwiGageTensor], |
416 |
|
|
pvl->directAnswer[item->prereq[0]]); |
417 |
|
|
} |
418 |
✗✓ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorError)) { |
419 |
|
|
gageItemEntry *item; |
420 |
|
|
item = pvl->kind->table + tenDwiGageTensorError; |
421 |
|
|
pvl->directAnswer[tenDwiGageTensorError][0] |
422 |
|
|
= pvl->directAnswer[item->prereq[0]][0]; |
423 |
|
|
} |
424 |
✗✓ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorErrorLog)) { |
425 |
|
|
gageItemEntry *item; |
426 |
|
|
item = pvl->kind->table + tenDwiGageTensorErrorLog; |
427 |
|
|
pvl->directAnswer[tenDwiGageTensorErrorLog][0] |
428 |
|
|
= pvl->directAnswer[item->prereq[0]][0]; |
429 |
|
|
} |
430 |
✗✓ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLikelihood)) { |
431 |
|
|
gageItemEntry *item; |
432 |
|
|
item = pvl->kind->table + tenDwiGageTensorLikelihood; |
433 |
|
|
pvl->directAnswer[tenDwiGageTensorLikelihood][0] |
434 |
|
|
= pvl->directAnswer[item->prereq[0]][0]; |
435 |
|
|
} |
436 |
|
|
/* END sneakiness ........ */ |
437 |
|
|
|
438 |
✗✓ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageFA)) { |
439 |
|
|
pvl->directAnswer[tenDwiGageFA][0] |
440 |
|
|
= pvl->directAnswer[tenDwiGageTensor][0] |
441 |
|
|
* tenAnisoTen_d(pvl->directAnswer[tenDwiGageTensor], |
442 |
|
|
tenAniso_FA); |
443 |
|
|
} |
444 |
|
|
|
445 |
✗✓ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorAllDWIError)) { |
446 |
|
|
const double *grads; |
447 |
|
|
int gradcount; |
448 |
|
|
double *ten, d; |
449 |
|
|
int i; |
450 |
|
|
|
451 |
|
|
/* HEY: should switch to tenEstimate-based DWI simulation */ |
452 |
|
|
ten = pvl->directAnswer[tenDwiGageTensor]; |
453 |
|
|
gradcount = pvl->kind->valLen -1; /* Dont count b0 */ |
454 |
|
|
grads = ((const double*) kindData->ngrad->data) +3; /* Ignore b0 grad */ |
455 |
|
|
for( i=0; i < gradcount; i++ ) { |
456 |
|
|
d = dwiAll[0]*exp(- pvlData->tec1->bValue |
457 |
|
|
* TEN_T3V_CONTR(ten, grads + 3*i)); |
458 |
|
|
pvl->directAnswer[tenDwiGageTensorAllDWIError][i] = dwiAll[i+1] - d; |
459 |
|
|
} |
460 |
|
|
} |
461 |
|
|
|
462 |
✗✓ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorQSeg)) { |
463 |
|
|
const double *grads; |
464 |
|
|
int gradcount; |
465 |
|
|
double *twoten; |
466 |
|
|
unsigned int valIdx, E; |
467 |
|
|
|
468 |
|
|
twoten = pvl->directAnswer[tenDwiGage2TensorQSeg]; |
469 |
|
|
|
470 |
|
|
gradcount = pvl->kind->valLen -1; /* Dont count b0 */ |
471 |
|
|
grads = ((const double*) kindData->ngrad->data) +3; /* Ignore b0 grad */ |
472 |
|
|
if (dwiAll[0] != 0) { /* S0 = 0 */ |
473 |
|
|
_tenQball(pvlData->tec2->bValue, gradcount, dwiAll, grads, |
474 |
|
|
pvlData->qvals); |
475 |
|
|
_tenQvals2points(gradcount, pvlData->qvals, grads, pvlData->qpoints); |
476 |
|
|
_tenSegsamp2(gradcount, pvlData->qvals, grads, pvlData->qpoints, |
477 |
|
|
pvlData->wght + 1, pvlData->dists ); |
478 |
|
|
} else { |
479 |
|
|
/* stupid; should really return right here since data is garbage */ |
480 |
|
|
for (valIdx=1; valIdx < AIR_CAST(unsigned int, gradcount+1); valIdx++) { |
481 |
|
|
pvlData->wght[valIdx] = valIdx % 2; |
482 |
|
|
} |
483 |
|
|
} |
484 |
|
|
|
485 |
|
|
E = 0; |
486 |
|
|
for (valIdx=1; valIdx<pvl->kind->valLen; valIdx++) { |
487 |
|
|
if (!E) E |= tenEstimateSkipSet(pvlData->tec2, valIdx, |
488 |
|
|
pvlData->wght[valIdx]); |
489 |
|
|
} |
490 |
|
|
if (!E) E |= tenEstimateUpdate(pvlData->tec2); |
491 |
|
|
if (!E) E |= tenEstimate1TensorSingle_d(pvlData->tec2, |
492 |
|
|
twoten + 0, dwiAll); |
493 |
|
|
for (valIdx=1; valIdx<pvl->kind->valLen; valIdx++) { |
494 |
|
|
if (!E) E |= tenEstimateSkipSet(pvlData->tec2, valIdx, |
495 |
|
|
1 - pvlData->wght[valIdx]); |
496 |
|
|
} |
497 |
|
|
if (!E) E |= tenEstimateUpdate(pvlData->tec2); |
498 |
|
|
if (!E) E |= tenEstimate1TensorSingle_d(pvlData->tec2, |
499 |
|
|
twoten + 7, dwiAll); |
500 |
|
|
if (E) { |
501 |
|
|
char *terr; |
502 |
|
|
terr = biffGetDone(TEN); |
503 |
|
|
fprintf(stderr, "%s: (trouble) %s\n", me, terr); |
504 |
|
|
free(terr); |
505 |
|
|
} |
506 |
|
|
|
507 |
|
|
/* hack: confidence for two-tensor fit */ |
508 |
|
|
twoten[0] = (twoten[0] + twoten[7])/2; |
509 |
|
|
twoten[7] = 0.5; /* fraction that is the first tensor (initial value) */ |
510 |
|
|
/* twoten[1 .. 6] = first tensor */ |
511 |
|
|
/* twoten[8 .. 13] = second tensor */ |
512 |
|
|
|
513 |
|
|
/* Compute fraction between tensors if not garbage in this voxel */ |
514 |
|
|
if (twoten[0] > 0.5) { |
515 |
|
|
double exp0,exp1,d,e=0,g=0, a=0,b=0; |
516 |
|
|
int i; |
517 |
|
|
|
518 |
|
|
for( i=0; i < gradcount; i++ ) { |
519 |
|
|
exp0 = exp(-pvlData->tec2->bValue * TEN_T3V_CONTR(twoten + 0, |
520 |
|
|
grads + 3*i)); |
521 |
|
|
exp1 = exp(-pvlData->tec2->bValue * TEN_T3V_CONTR(twoten + 7, |
522 |
|
|
grads + 3*i)); |
523 |
|
|
|
524 |
|
|
d = dwiAll[i+1] / dwiAll[0]; |
525 |
|
|
e = exp0 - exp1; |
526 |
|
|
g = d - exp1; |
527 |
|
|
|
528 |
|
|
a += .5*e*e; |
529 |
|
|
b += e*g; |
530 |
|
|
} |
531 |
|
|
|
532 |
|
|
twoten[7] = AIR_CLAMP(0, 0.5*(b/a), 1); |
533 |
|
|
} |
534 |
|
|
} |
535 |
✗✓ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorQSegError)) { |
536 |
|
|
const double *grads; |
537 |
|
|
int gradcount; |
538 |
|
|
double *twoten, d; |
539 |
|
|
int i; |
540 |
|
|
|
541 |
|
|
/* HEY: should switch to tenEstimate-based DWI simulation */ |
542 |
|
|
if (dwiAll[0] != 0) { /* S0 = 0 */ |
543 |
|
|
twoten = pvl->directAnswer[tenDwiGage2TensorQSeg]; |
544 |
|
|
gradcount = pvl->kind->valLen -1; /* Dont count b0 */ |
545 |
|
|
grads = ((const double*) kindData->ngrad->data) +3; /* Ignore b0 grad */ |
546 |
|
|
|
547 |
|
|
pvl->directAnswer[tenDwiGage2TensorQSegError][0] = 0; |
548 |
|
|
for( i=0; i < gradcount; i++ ) { |
549 |
|
|
d = twoten[7]*exp(-pvlData->tec2->bValue * TEN_T3V_CONTR(twoten + 0, |
550 |
|
|
grads + 3*i)); |
551 |
|
|
d += (1 - twoten[7])*exp(-pvlData->tec2->bValue |
552 |
|
|
*TEN_T3V_CONTR(twoten + 7, grads + 3*i)); |
553 |
|
|
d = dwiAll[i+1]/dwiAll[0] - d; |
554 |
|
|
pvl->directAnswer[tenDwiGage2TensorQSegError][0] += d*d; |
555 |
|
|
} |
556 |
|
|
pvl->directAnswer[tenDwiGage2TensorQSegError][0] = |
557 |
|
|
sqrt( pvl->directAnswer[tenDwiGage2TensorQSegError][0] ); |
558 |
|
|
} else { |
559 |
|
|
/* HEY: COMPLETELY WRONG!! An error is not defined! */ |
560 |
|
|
pvl->directAnswer[tenDwiGage2TensorQSegError][0] = 0; |
561 |
|
|
} |
562 |
|
|
/* printf("%f\n",pvl->directAnswer[tenDwiGage2TensorQSegError][0]); */ |
563 |
|
|
} |
564 |
✗✓ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorQSegAndError)) { |
565 |
|
|
double *twoten, *err, *twotenerr; |
566 |
|
|
|
567 |
|
|
twoten = pvl->directAnswer[tenDwiGage2TensorQSeg]; |
568 |
|
|
err = pvl->directAnswer[tenDwiGage2TensorQSegError]; |
569 |
|
|
twotenerr = pvl->directAnswer[tenDwiGage2TensorQSegAndError]; |
570 |
|
|
TEN_T_COPY(twotenerr + 0, twoten + 0); |
571 |
|
|
TEN_T_COPY(twotenerr + 7, twoten + 7); |
572 |
|
|
twotenerr[14] = err[0]; |
573 |
|
|
} |
574 |
|
|
|
575 |
✗✓ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorPeled)) { |
576 |
|
|
#if TEEM_LEVMAR |
577 |
|
|
#define PARAMS 4 |
578 |
|
|
double *twoTen, Cp /* , residual, AICSingFit, AICTwoFit */; |
579 |
|
|
/* Vars for the NLLS */ |
580 |
|
|
double guess[PARAMS], loBnd[PARAMS], upBnd[PARAMS], |
581 |
|
|
opts[LM_OPTS_SZ], *grad, *egrad, tenA[7], tenB[7], |
582 |
|
|
matA[9], matB[9], matTmp[9], rott[9]; |
583 |
|
|
unsigned int gi; |
584 |
|
|
int lmret; |
585 |
|
|
|
586 |
|
|
/* Pointer to the location where the two tensor will be written */ |
587 |
|
|
twoTen = pvl->directAnswer[tenDwiGage2TensorPeled]; |
588 |
|
|
/* Estimate the DWI error, error is given as standard deviation */ |
589 |
|
|
pvlData->tec1->recordErrorDwi = AIR_FALSE; |
590 |
|
|
/* Estimate the single tensor */ |
591 |
|
|
tenEstimate1TensorSingle_d(pvlData->tec1, pvlData->ten1, dwiAll); |
592 |
|
|
/* Get the eigenValues and eigen vectors for this tensor */ |
593 |
|
|
tenEigensolve_d(pvlData->ten1Eval, pvlData->ten1Evec, pvlData->ten1); |
594 |
|
|
/* Get westins Cp */ |
595 |
|
|
Cp = tenAnisoEval_d(pvlData->ten1Eval, tenAniso_Cp1); |
596 |
|
|
|
597 |
|
|
/* Only do two-tensor fitting if CP is greater or equal to than a |
598 |
|
|
user-defined threshold */ |
599 |
|
|
if (Cp >= pvlData->levmarMinCp) { |
600 |
|
|
/* Calculate the residual, need the variance to sqr it */ |
601 |
|
|
/* residual = pvlData->tec1->errorDwi*pvlData->tec1->errorDwi; */ |
602 |
|
|
/* Calculate the AIC for single tensor fit */ |
603 |
|
|
/* AICSingFit = _tenComputeAIC(residual, pvlData->tec1->dwiNum, 6); */ |
604 |
|
|
|
605 |
|
|
/* the CP-based test is gone; caller's responsibility */ |
606 |
|
|
|
607 |
|
|
/* rotate DW gradients by inverse of eigenvector column matrix |
608 |
|
|
and place into pvlData->nten1EigenGrads (which has been |
609 |
|
|
allocated by _tenDwiGagePvlDataNew()) */ |
610 |
|
|
grad = AIR_CAST(double *, kindData->ngrad->data); |
611 |
|
|
egrad = AIR_CAST(double *, pvlData->nten1EigenGrads->data); |
612 |
|
|
for (gi=0; gi<kindData->ngrad->axis[1].size; gi++) { |
613 |
|
|
/* yes, this is also transforming some zero-length (B0) gradients; |
614 |
|
|
that's harmless */ |
615 |
|
|
ELL_3MV_MUL(egrad, pvlData->ten1Evec, grad); |
616 |
|
|
grad += 3; |
617 |
|
|
egrad += 3; |
618 |
|
|
} |
619 |
|
|
|
620 |
|
|
/* Lower and upper bounds for the NLLS routine */ |
621 |
|
|
loBnd[0] = 0.0; |
622 |
|
|
loBnd[1] = 0.0; |
623 |
|
|
loBnd[2] = -AIR_PI/2; |
624 |
|
|
loBnd[3] = -AIR_PI/2; |
625 |
|
|
upBnd[0] = pvlData->ten1Eval[0]*5; |
626 |
|
|
upBnd[1] = 1.0; |
627 |
|
|
upBnd[2] = AIR_PI/2; |
628 |
|
|
upBnd[3] = AIR_PI/2; |
629 |
|
|
/* Starting point for the NLLS */ |
630 |
|
|
guess[0] = pvlData->ten1Eval[0]; |
631 |
|
|
guess[1] = 0.5; |
632 |
|
|
|
633 |
|
|
guess[2] = AIR_PI/4; |
634 |
|
|
guess[3] = -AIR_PI/4; |
635 |
|
|
/* |
636 |
|
|
guess[2] = AIR_AFFINE(0, airDrandMT_r(pvlData->randState), 1, |
637 |
|
|
AIR_PI/6, AIR_PI/3); |
638 |
|
|
guess[3] = AIR_AFFINE(0, airDrandMT_r(pvlData->randState), 1, |
639 |
|
|
-AIR_PI/6, -AIR_PI/3); |
640 |
|
|
*/ |
641 |
|
|
/* Fill in the constraints for the LM optimization, |
642 |
|
|
the threshold of error difference */ |
643 |
|
|
opts[0] = pvlData->levmarTau; |
644 |
|
|
opts[1] = pvlData->levmarEps1; |
645 |
|
|
opts[2] = pvlData->levmarEps2; |
646 |
|
|
opts[3] = pvlData->levmarEps3; |
647 |
|
|
/* Very imp to set this opt, note that only forward |
648 |
|
|
differences are used to approx Jacobian */ |
649 |
|
|
opts[4] = pvlData->levmarDelta; |
650 |
|
|
|
651 |
|
|
/* run NLLS, results are stored back into guess[] */ |
652 |
|
|
pvlData->levmarUseFastExp = AIR_FALSE; |
653 |
|
|
lmret = dlevmar_bc_dif(_tenLevmarPeledCB, guess, pvlData->tec1->dwi, |
654 |
|
|
PARAMS, pvlData->tec1->dwiNum, loBnd, upBnd, |
655 |
|
|
NULL, pvlData->levmarMaxIter, opts, |
656 |
|
|
pvlData->levmarInfo, |
657 |
|
|
NULL, NULL, pvlData); |
658 |
|
|
if (-1 == lmret) { |
659 |
|
|
ctx->errNum = 1; |
660 |
|
|
sprintf(ctx->errStr, "%s: dlevmar_bc_dif() failed!", me); |
661 |
|
|
} else { |
662 |
|
|
/* Get the AIC for the two tensor fit, use the levmarinfo |
663 |
|
|
to get the residual */ |
664 |
|
|
/* |
665 |
|
|
residual = pvlData->levmarInfo[1]/pvlData->tec1->dwiNum; |
666 |
|
|
AICTwoFit = _tenComputeAIC(residual, pvlData->tec1->dwiNum, 12); |
667 |
|
|
*/ |
668 |
|
|
/* Form the tensors using the estimated pp, returned in guess */ |
669 |
|
|
_tenPeledRotate2D(tenA, guess[0], pvlData->ten1Eval[2], guess[2]); |
670 |
|
|
_tenPeledRotate2D(tenB, guess[0], pvlData->ten1Eval[2], guess[3]); |
671 |
|
|
TEN_T2M(matA, tenA); |
672 |
|
|
TEN_T2M(matB, tenB); |
673 |
|
|
|
674 |
|
|
ELL_3M_TRANSPOSE(rott, pvlData->ten1Evec); |
675 |
|
|
ELL_3M_MUL(matTmp, matA, pvlData->ten1Evec); |
676 |
|
|
ELL_3M_MUL(matA, rott, matTmp); |
677 |
|
|
ELL_3M_MUL(matTmp, matB, pvlData->ten1Evec); |
678 |
|
|
ELL_3M_MUL(matB, rott, matTmp); |
679 |
|
|
|
680 |
|
|
/* Copy two two tensors */ |
681 |
|
|
/* guess[1] is population fraction of first tensor */ |
682 |
|
|
if (guess[1] > 0.5) { |
683 |
|
|
twoTen[7] = guess[1]; |
684 |
|
|
TEN_M2T(twoTen + 0, matA); |
685 |
|
|
TEN_M2T(twoTen + 7, matB); |
686 |
|
|
} else { |
687 |
|
|
twoTen[7] = 1 - guess[1]; |
688 |
|
|
TEN_M2T(twoTen + 0, matB); |
689 |
|
|
TEN_M2T(twoTen + 7, matA); |
690 |
|
|
} |
691 |
|
|
twoTen[0] = 1; |
692 |
|
|
} |
693 |
|
|
} else { |
694 |
|
|
/* its too planar- just do single tensor fit */ |
695 |
|
|
TEN_T_COPY(twoTen, pvlData->ten1); |
696 |
|
|
TEN_T_SET(twoTen + 7, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); |
697 |
|
|
} |
698 |
|
|
#undef PARAMS |
699 |
|
|
#else |
700 |
|
|
double *twoTen; |
701 |
|
|
twoTen = pvl->directAnswer[tenDwiGage2TensorPeled]; |
702 |
|
|
TEN_T_SET(twoTen + 0, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, |
703 |
|
|
AIR_NAN, AIR_NAN, AIR_NAN); |
704 |
|
|
TEN_T_SET(twoTen + 7, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, |
705 |
|
|
AIR_NAN, AIR_NAN, AIR_NAN); |
706 |
|
|
fprintf(stderr, "%s: sorry, not compiled with TEEM_LEVMAR\n", me); |
707 |
|
|
#endif |
708 |
|
|
} |
709 |
✗✓ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorPeledError)) { |
710 |
|
|
double *info; |
711 |
|
|
info = pvlData->levmarInfo; |
712 |
|
|
pvl->directAnswer[tenDwiGage2TensorPeledError][0] = 0; |
713 |
|
|
|
714 |
|
|
if (info[1] > 0) { |
715 |
|
|
/* Returning the standard deviation */ |
716 |
|
|
pvl->directAnswer[tenDwiGage2TensorPeledError][0] = |
717 |
|
|
sqrt(info[1]/pvlData->tec1->dwiNum); |
718 |
|
|
} |
719 |
|
|
} |
720 |
✗✓ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorPeledAndError)) { |
721 |
|
|
double *twoten, *err, *twotenerr; |
722 |
|
|
/* HEY cut and paste */ |
723 |
|
|
twoten = pvl->directAnswer[tenDwiGage2TensorPeled]; |
724 |
|
|
err = pvl->directAnswer[tenDwiGage2TensorPeledError]; |
725 |
|
|
twotenerr = pvl->directAnswer[tenDwiGage2TensorPeledAndError]; |
726 |
|
|
TEN_T_COPY(twotenerr + 0, twoten + 0); |
727 |
|
|
TEN_T_COPY(twotenerr + 7, twoten + 7); |
728 |
|
|
twotenerr[14] = err[0]; |
729 |
|
|
} |
730 |
|
|
|
731 |
✗✓ |
29700 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorPeledLevmarInfo)) { |
732 |
|
|
double *info; |
733 |
|
|
unsigned int ii, alen; |
734 |
|
|
alen = gageKindAnswerLength(pvl->kind, tenDwiGage2TensorPeledLevmarInfo); |
735 |
|
|
info = pvl->directAnswer[tenDwiGage2TensorPeledLevmarInfo]; |
736 |
|
|
for (ii=0; ii<alen; ii++) { |
737 |
|
|
info[ii] = pvlData->levmarInfo[ii]; |
738 |
|
|
} |
739 |
|
|
} |
740 |
|
|
|
741 |
|
|
return; |
742 |
|
29700 |
} |
743 |
|
|
|
744 |
|
|
/* --------------------- pvlData */ |
745 |
|
|
|
746 |
|
|
/* note use of the GAGE biff key */ |
747 |
|
|
void * |
748 |
|
|
_tenDwiGagePvlDataNew(const gageKind *kind) { |
749 |
|
|
static const char me[]="_tenDwiGagePvlDataNew"; |
750 |
|
|
tenDwiGagePvlData *pvlData; |
751 |
|
|
tenDwiGageKindData *kindData; |
752 |
|
|
const int segcount = 2; |
753 |
|
|
unsigned int num; |
754 |
|
|
int E; |
755 |
|
|
|
756 |
✗✓ |
32 |
if (tenDwiGageKindCheck(kind)) { |
757 |
|
|
biffMovef(GAGE, TEN, "%s: kindData not ready for use", me); |
758 |
|
|
return NULL; |
759 |
|
|
} |
760 |
|
16 |
kindData = AIR_CAST(tenDwiGageKindData *, kind->data); |
761 |
|
|
|
762 |
|
16 |
pvlData = AIR_CALLOC(1, tenDwiGagePvlData); |
763 |
✗✓ |
16 |
if (!pvlData) { |
764 |
|
|
biffAddf(GAGE, "%s: couldn't allocate pvl data!", me); |
765 |
|
|
return NULL; |
766 |
|
|
} |
767 |
|
16 |
pvlData->tec1 = tenEstimateContextNew(); |
768 |
|
16 |
pvlData->tec2 = tenEstimateContextNew(); |
769 |
✓✓ |
96 |
for (num=1; num<=2; num++) { |
770 |
|
|
tenEstimateContext *tec; |
771 |
✓✓ |
96 |
tec = (1 == num ? pvlData->tec1 : pvlData->tec2); |
772 |
|
|
E = 0; |
773 |
✓✗ |
64 |
if (!E) tenEstimateVerboseSet(tec, 0); |
774 |
✓✗ |
64 |
if (!E) tenEstimateNegEvalShiftSet(tec, AIR_FALSE); |
775 |
✓✗✓✓
|
128 |
if (!E) E |= tenEstimateMethodSet(tec, 1 == num |
776 |
|
16 |
? kindData->est1Method |
777 |
|
16 |
: kindData->est2Method); |
778 |
✓✗ |
64 |
if (!E) E |= tenEstimateValueMinSet(tec, kindData->valueMin); |
779 |
✓✗✓✗
|
64 |
if (kindData->ngrad->data) { |
780 |
✓✗ |
96 |
if (!E) E |= tenEstimateGradientsSet(tec, kindData->ngrad, |
781 |
|
32 |
kindData->bval, AIR_FALSE); |
782 |
|
|
} else { |
783 |
|
|
if (!E) E |= tenEstimateBMatricesSet(tec, kindData->nbmat, |
784 |
|
|
kindData->bval, AIR_FALSE); |
785 |
|
|
} |
786 |
✓✗ |
64 |
if (!E) E |= tenEstimateThresholdSet(tec, |
787 |
|
32 |
kindData->thresh, kindData->soft); |
788 |
✓✗ |
64 |
if (!E) E |= tenEstimateUpdate(tec); |
789 |
✗✓ |
32 |
if (E) { |
790 |
|
|
biffMovef(GAGE, TEN, "%s: trouble setting %u estimation", me, num); |
791 |
|
|
return NULL; |
792 |
|
|
} |
793 |
|
32 |
} |
794 |
|
16 |
pvlData->vbuf = AIR_CALLOC(kind->valLen, double); |
795 |
|
16 |
pvlData->wght = AIR_CALLOC(kind->valLen, unsigned int); |
796 |
|
|
/* HEY: this is where we act on the the assumption about |
797 |
|
|
having val[0] be T2 baseline and all subsequent val[i] be DWIs */ |
798 |
|
16 |
pvlData->wght[0] = 1; |
799 |
|
16 |
pvlData->qvals = AIR_CALLOC(kind->valLen-1, double); |
800 |
|
16 |
pvlData->qpoints = AIR_CALLOC(3*(kind->valLen-1), double); |
801 |
|
16 |
pvlData->dists = AIR_CALLOC(segcount*(kind->valLen-1), double); |
802 |
|
16 |
pvlData->weights = AIR_CALLOC(segcount*(kind->valLen-1), double); |
803 |
|
|
|
804 |
✓✗ |
16 |
if (kindData->ngrad->data) { |
805 |
|
16 |
pvlData->nten1EigenGrads = nrrdNew(); |
806 |
|
|
/* this is for allocation only; values will get over-written */ |
807 |
|
16 |
nrrdCopy(pvlData->nten1EigenGrads, kindData->ngrad); |
808 |
|
16 |
} else { |
809 |
|
|
/* HEY: currently don't handle general B-matrices here */ |
810 |
|
|
pvlData->nten1EigenGrads = NULL; |
811 |
|
|
} |
812 |
|
|
|
813 |
|
16 |
pvlData->randSeed = kindData->randSeed; |
814 |
|
16 |
pvlData->randState = airRandMTStateNew(pvlData->randSeed); |
815 |
|
|
|
816 |
|
|
/* initialize single-tensor info to all NaNs */ |
817 |
|
16 |
TEN_T_SET(pvlData->ten1, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, |
818 |
|
|
AIR_NAN, AIR_NAN, AIR_NAN); |
819 |
|
16 |
ELL_3V_SET(pvlData->ten1Evec + 0, AIR_NAN, AIR_NAN, AIR_NAN); |
820 |
|
16 |
ELL_3V_SET(pvlData->ten1Evec + 3, AIR_NAN, AIR_NAN, AIR_NAN); |
821 |
|
16 |
ELL_3V_SET(pvlData->ten1Evec + 6, AIR_NAN, AIR_NAN, AIR_NAN); |
822 |
|
16 |
ELL_3V_SET(pvlData->ten1Eval, AIR_NAN, AIR_NAN, AIR_NAN); |
823 |
|
|
|
824 |
|
|
/* here's an okay spot to check our compile-time assumptions |
825 |
|
|
about the levmar library */ |
826 |
|
|
#if TEEM_LEVMAR |
827 |
|
|
/* this is needed to make sure that the tenDwiGage2TensorPeledLevmarInfo |
828 |
|
|
item definition above is valid */ |
829 |
|
|
if (5 != LM_OPTS_SZ) { |
830 |
|
|
biffAddf(GAGE, "%s: LM_OPTS_SZ (%d) != expected 5\n", me, LM_OPTS_SZ); |
831 |
|
|
return NULL; |
832 |
|
|
} |
833 |
|
|
#endif |
834 |
|
16 |
pvlData->levmarUseFastExp = AIR_FALSE; |
835 |
|
16 |
pvlData->levmarMaxIter = 200; |
836 |
|
16 |
pvlData->levmarTau = 1E-03; /* LM_INIT_MU; */ |
837 |
|
16 |
pvlData->levmarEps1 = 1E-8; |
838 |
|
16 |
pvlData->levmarEps2 = 1E-8; |
839 |
|
16 |
pvlData->levmarEps3 = 1E-8; |
840 |
|
16 |
pvlData->levmarDelta = 1E-8; |
841 |
|
16 |
pvlData->levmarMinCp = 0.1; |
842 |
|
|
|
843 |
|
|
/* pvlData->levmarInfo[] is output; not initialized */ |
844 |
|
|
|
845 |
|
16 |
return AIR_CAST(void *, pvlData); |
846 |
|
16 |
} |
847 |
|
|
|
848 |
|
|
void * |
849 |
|
|
_tenDwiGagePvlDataCopy(const gageKind *kind, const void *_pvlDataOld) { |
850 |
|
|
tenDwiGagePvlData *pvlDataOld, *pvlDataNew; |
851 |
|
|
|
852 |
|
16 |
pvlDataOld = AIR_CAST(tenDwiGagePvlData *, _pvlDataOld); |
853 |
|
8 |
pvlDataNew = AIR_CAST(tenDwiGagePvlData *, _tenDwiGagePvlDataNew(kind)); |
854 |
|
|
|
855 |
|
|
/* HEY: no error checking? */ |
856 |
✓✗ |
8 |
if (pvlDataOld->nten1EigenGrads) { |
857 |
|
8 |
nrrdCopy(pvlDataNew->nten1EigenGrads, pvlDataOld->nten1EigenGrads); |
858 |
|
8 |
} |
859 |
|
|
/* need to copy randState or randSeed? */ |
860 |
|
|
|
861 |
|
8 |
TEN_T_COPY(pvlDataNew->ten1, pvlDataOld->ten1); |
862 |
|
8 |
ELL_3M_COPY(pvlDataNew->ten1Evec, pvlDataOld->ten1Evec); |
863 |
|
8 |
ELL_3V_COPY(pvlDataNew->ten1Eval, pvlDataOld->ten1Eval); |
864 |
|
|
|
865 |
|
8 |
pvlDataNew->levmarUseFastExp = pvlDataOld->levmarUseFastExp; |
866 |
|
8 |
pvlDataNew->levmarMaxIter = pvlDataOld->levmarMaxIter; |
867 |
|
8 |
pvlDataNew->levmarTau = pvlDataOld->levmarTau; |
868 |
|
8 |
pvlDataNew->levmarEps1 = pvlDataOld->levmarEps1; |
869 |
|
8 |
pvlDataNew->levmarEps2 = pvlDataOld->levmarEps2; |
870 |
|
8 |
pvlDataNew->levmarEps3 = pvlDataOld->levmarEps3; |
871 |
|
8 |
pvlDataNew->levmarDelta = pvlDataOld->levmarDelta; |
872 |
|
8 |
pvlDataNew->levmarMinCp = pvlDataOld->levmarMinCp; |
873 |
|
|
/* pvlData->levmarInfo[] is output; not copied */ |
874 |
|
|
|
875 |
|
8 |
return pvlDataNew; |
876 |
|
|
} |
877 |
|
|
|
878 |
|
|
int |
879 |
|
|
_tenDwiGagePvlDataUpdate(const gageKind *kind, |
880 |
|
|
const gageContext *ctx, |
881 |
|
|
const gagePerVolume *pvl, const void *_pvlData) { |
882 |
|
|
/* static const char me[]="_tenDwiGagePvlDataUpdate"; */ |
883 |
|
|
tenDwiGagePvlData *pvlData; |
884 |
|
|
|
885 |
|
|
AIR_UNUSED(ctx); |
886 |
|
16 |
pvlData = AIR_CAST(tenDwiGagePvlData *, _pvlData); |
887 |
|
|
AIR_UNUSED(kind); |
888 |
✗✓ |
16 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLLSError) |
889 |
✓✗ |
16 |
|| GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorWLSError) |
890 |
✓✗ |
16 |
|| GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorNLSError) |
891 |
✓✗ |
16 |
|| GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorMLEError)) { |
892 |
|
|
pvlData->tec1->recordErrorDwi = AIR_TRUE; |
893 |
|
|
} else { |
894 |
|
8 |
pvlData->tec1->recordErrorDwi = AIR_FALSE; |
895 |
|
|
} |
896 |
✗✓ |
16 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLLSErrorLog) |
897 |
✓✗ |
16 |
|| GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorWLSErrorLog) |
898 |
✓✗ |
16 |
|| GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorNLSErrorLog) |
899 |
✓✗ |
16 |
|| GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorMLEErrorLog)) { |
900 |
|
|
pvlData->tec1->recordErrorLogDwi = AIR_TRUE; |
901 |
|
|
} else { |
902 |
|
8 |
pvlData->tec1->recordErrorLogDwi = AIR_FALSE; |
903 |
|
|
} |
904 |
✗✓ |
16 |
if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLLSLikelihood) |
905 |
✓✗ |
16 |
|| GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorWLSLikelihood) |
906 |
✓✗ |
16 |
|| GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorNLSLikelihood) |
907 |
✓✗ |
16 |
|| GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorMLELikelihood)) { |
908 |
|
|
pvlData->tec1->recordLikelihoodDwi = AIR_TRUE; |
909 |
|
|
} else { |
910 |
|
8 |
pvlData->tec1->recordLikelihoodDwi = AIR_FALSE; |
911 |
|
|
} |
912 |
|
|
/* |
913 |
|
|
fprintf(stderr, "%s: record %d %d %d\n", me, |
914 |
|
|
pvlData->tec1->recordErrorDwi, |
915 |
|
|
pvlData->tec1->recordErrorLogDwi, |
916 |
|
|
pvlData->tec1->recordLikelihoodDwi); |
917 |
|
|
*/ |
918 |
|
8 |
return 0; |
919 |
|
|
} |
920 |
|
|
|
921 |
|
|
void * |
922 |
|
|
_tenDwiGagePvlDataNix(const gageKind *kind, void *_pvlData) { |
923 |
|
|
tenDwiGagePvlData *pvlData; |
924 |
|
|
|
925 |
|
|
AIR_UNUSED(kind); |
926 |
|
32 |
pvlData = AIR_CAST(tenDwiGagePvlData *, _pvlData); |
927 |
✓✗ |
16 |
if (pvlData) { |
928 |
|
16 |
tenEstimateContextNix(pvlData->tec1); |
929 |
|
16 |
tenEstimateContextNix(pvlData->tec2); |
930 |
|
16 |
airFree(pvlData->vbuf); |
931 |
|
16 |
airFree(pvlData->wght); |
932 |
|
16 |
airFree(pvlData->qvals); |
933 |
|
16 |
airFree(pvlData->qpoints); |
934 |
|
16 |
airFree(pvlData->dists); |
935 |
|
16 |
airFree(pvlData->weights); |
936 |
|
16 |
nrrdNuke(pvlData->nten1EigenGrads); |
937 |
|
16 |
airRandMTStateNix(pvlData->randState); |
938 |
|
16 |
airFree(pvlData); |
939 |
|
16 |
} |
940 |
|
16 |
return NULL; |
941 |
|
|
} |
942 |
|
|
|
943 |
|
|
/* --------------------- kindData */ |
944 |
|
|
|
945 |
|
|
tenDwiGageKindData* |
946 |
|
|
tenDwiGageKindDataNew(void) { |
947 |
|
|
tenDwiGageKindData *ret; |
948 |
|
|
|
949 |
|
32 |
ret = AIR_CALLOC(1, tenDwiGageKindData); |
950 |
✓✗ |
16 |
if (ret) { |
951 |
|
|
/* it may be that only one of these is actually filled */ |
952 |
|
16 |
ret->ngrad = nrrdNew(); |
953 |
|
16 |
ret->nbmat = nrrdNew(); |
954 |
|
16 |
ret->thresh = ret->soft = ret->bval = AIR_NAN; |
955 |
|
16 |
ret->est1Method = tenEstimate1MethodUnknown; |
956 |
|
16 |
ret->est2Method = tenEstimate2MethodUnknown; |
957 |
|
16 |
ret->randSeed = 42; |
958 |
|
16 |
} |
959 |
|
16 |
return ret; |
960 |
|
|
} |
961 |
|
|
|
962 |
|
|
tenDwiGageKindData* |
963 |
|
|
tenDwiGageKindDataNix(tenDwiGageKindData *kindData) { |
964 |
|
|
|
965 |
✓✗ |
32 |
if (kindData) { |
966 |
|
16 |
nrrdNuke(kindData->ngrad); |
967 |
|
16 |
nrrdNuke(kindData->nbmat); |
968 |
|
16 |
airFree(kindData); |
969 |
|
16 |
} |
970 |
|
16 |
return NULL; |
971 |
|
|
} |
972 |
|
|
|
973 |
|
|
/* --------------------- dwiKind, and dwiKind->data setting*/ |
974 |
|
|
|
975 |
|
|
/* |
976 |
|
|
** Because this kind has to be dynamically allocated, |
977 |
|
|
** this is not the kind, but just the template for it |
978 |
|
|
** HEY: having a const public version of this could be a |
979 |
|
|
** nice way of having a way of referring to the dwiKind |
980 |
|
|
** without having to allocate it each time |
981 |
|
|
*/ |
982 |
|
|
gageKind |
983 |
|
|
_tenDwiGageKindTmpl = { |
984 |
|
|
AIR_TRUE, /* dynamically allocated */ |
985 |
|
|
TEN_DWI_GAGE_KIND_NAME, |
986 |
|
|
&_tenDwiGage, |
987 |
|
|
1, |
988 |
|
|
0, /* NOT: set later by tenDwiGageKindSet() */ |
989 |
|
|
TEN_DWI_GAGE_ITEM_MAX, |
990 |
|
|
NULL, /* NOT: modified copy of _tenDwiGageTable, |
991 |
|
|
allocated by tenDwiGageKindNew(), and |
992 |
|
|
set by _tenDwiGageKindSet() */ |
993 |
|
|
_tenDwiGageIv3Print, |
994 |
|
|
_tenDwiGageFilter, |
995 |
|
|
_tenDwiGageAnswer, |
996 |
|
|
_tenDwiGagePvlDataNew, |
997 |
|
|
_tenDwiGagePvlDataCopy, |
998 |
|
|
_tenDwiGagePvlDataNix, |
999 |
|
|
_tenDwiGagePvlDataUpdate, |
1000 |
|
|
NULL /* NOT: allocated by tenDwiGageKindNew(), |
1001 |
|
|
insides set by tenDwiGageKindSet() */ |
1002 |
|
|
}; |
1003 |
|
|
|
1004 |
|
|
gageKind * |
1005 |
|
|
tenDwiGageKindNew() { |
1006 |
|
|
gageKind *kind; |
1007 |
|
|
|
1008 |
|
32 |
kind = AIR_CALLOC(1, gageKind); |
1009 |
✓✗ |
16 |
if (kind) { |
1010 |
|
16 |
memcpy(kind, &_tenDwiGageKindTmpl, sizeof(gageKind)); |
1011 |
|
16 |
kind->valLen = 0; /* still has to be set later */ |
1012 |
|
16 |
kind->table = AIR_CAST(gageItemEntry *, |
1013 |
|
|
malloc(sizeof(_tenDwiGageTable))); |
1014 |
|
16 |
memcpy(kind->table, _tenDwiGageTable, sizeof(_tenDwiGageTable)); |
1015 |
|
16 |
kind->data = AIR_CAST(void *, tenDwiGageKindDataNew()); |
1016 |
|
16 |
} |
1017 |
|
16 |
return kind; |
1018 |
|
|
} |
1019 |
|
|
|
1020 |
|
|
gageKind * |
1021 |
|
|
tenDwiGageKindNix(gageKind *kind) { |
1022 |
|
|
|
1023 |
✓✗ |
32 |
if (kind) { |
1024 |
|
16 |
airFree(kind->table); |
1025 |
|
16 |
tenDwiGageKindDataNix(AIR_CAST(tenDwiGageKindData *, kind->data)); |
1026 |
|
16 |
airFree(kind); |
1027 |
|
16 |
} |
1028 |
|
16 |
return NULL; |
1029 |
|
|
} |
1030 |
|
|
|
1031 |
|
|
/* |
1032 |
|
|
** NOTE: this sets information in both the kind and kindData |
1033 |
|
|
*/ |
1034 |
|
|
int |
1035 |
|
|
tenDwiGageKindSet(gageKind *dwiKind, |
1036 |
|
|
double thresh, double soft, double bval, double valueMin, |
1037 |
|
|
const Nrrd *ngrad, |
1038 |
|
|
const Nrrd *nbmat, |
1039 |
|
|
int e1method, int e2method, |
1040 |
|
|
unsigned int randSeed) { |
1041 |
|
|
static const char me[]="tenDwiGageKindSet"; |
1042 |
|
|
tenDwiGageKindData *kindData; |
1043 |
|
|
double grad[3], (*lup)(const void *, size_t); |
1044 |
|
|
unsigned int gi; |
1045 |
|
|
|
1046 |
✗✓ |
16 |
if (!dwiKind) { |
1047 |
|
|
biffAddf(TEN, "%s: got NULL pointer", me); |
1048 |
|
|
return 0; |
1049 |
|
|
} |
1050 |
✗✓ |
8 |
if (!( !!(ngrad) ^ !!(nbmat) )) { |
1051 |
|
|
biffAddf(TEN, "%s: need exactly one non-NULL in {ngrad,nbmat}", me); |
1052 |
|
|
return 1; |
1053 |
|
|
} |
1054 |
✗✓ |
8 |
if (nbmat) { |
1055 |
|
|
biffAddf(TEN, "%s: sorry, B-matrices temporarily disabled", me); |
1056 |
|
|
return 1; |
1057 |
|
|
} |
1058 |
|
|
/* (used for detecting errors in losslessly writing/reading |
1059 |
|
|
a gradient set) |
1060 |
|
|
{ |
1061 |
|
|
fprintf(stderr, "!%s: saving ngrad.nrrd\n", me); |
1062 |
|
|
if (ngrad) { |
1063 |
|
|
nrrdSave("ngrad.nrrd", ngrad, NULL); |
1064 |
|
|
} |
1065 |
|
|
} |
1066 |
|
|
*/ |
1067 |
|
|
|
1068 |
✗✓ |
8 |
if (tenGradientCheck(ngrad, nrrdTypeDefault, 7)) { |
1069 |
|
|
biffAddf(TEN, "%s: problem with given gradients", me); |
1070 |
|
|
return 1; |
1071 |
|
|
} |
1072 |
|
|
/* make sure that gradient lengths are as expected */ |
1073 |
|
8 |
lup = nrrdDLookup[ngrad->type]; |
1074 |
|
8 |
grad[0] = lup(ngrad->data, 0); |
1075 |
|
8 |
grad[1] = lup(ngrad->data, 1); |
1076 |
|
8 |
grad[2] = lup(ngrad->data, 2); |
1077 |
✗✓ |
8 |
if (0.0 != ELL_3V_LEN(grad)) { |
1078 |
|
|
biffAddf(TEN, "%s: sorry, currently need grad[0] to be len 0 (not %g)", |
1079 |
|
|
me, ELL_3V_LEN(grad)); |
1080 |
|
|
return 1; |
1081 |
|
|
} |
1082 |
✓✓ |
176 |
for (gi=1; gi<ngrad->axis[1].size; gi++) { |
1083 |
|
80 |
grad[0] = lup(ngrad->data, 0 + 3*gi); |
1084 |
|
80 |
grad[1] = lup(ngrad->data, 1 + 3*gi); |
1085 |
|
80 |
grad[2] = lup(ngrad->data, 2 + 3*gi); |
1086 |
✗✓ |
80 |
if (0.0 == ELL_3V_LEN(grad)) { |
1087 |
|
|
biffAddf(TEN, "%s: sorry, all but first gradient must be non-zero " |
1088 |
|
|
"(%u is zero)", me, gi); |
1089 |
|
|
return 1; |
1090 |
|
|
} |
1091 |
|
|
} |
1092 |
✗✓ |
8 |
if (airEnumValCheck(tenEstimate1Method, e1method)) { |
1093 |
|
|
biffAddf(TEN, "%s: e1method %d is not a valid %s", me, |
1094 |
|
|
e1method, tenEstimate1Method->name); |
1095 |
|
|
return 1; |
1096 |
|
|
} |
1097 |
✗✓ |
8 |
if (airEnumValCheck(tenEstimate2Method, e2method)) { |
1098 |
|
|
biffAddf(TEN, "%s: emethod %d is not a valid %s", me, |
1099 |
|
|
e2method, tenEstimate2Method->name); |
1100 |
|
|
return 1; |
1101 |
|
|
} |
1102 |
|
|
|
1103 |
|
8 |
kindData = AIR_CAST(tenDwiGageKindData *, dwiKind->data); |
1104 |
✗✓ |
8 |
if (nrrdConvert(kindData->ngrad, ngrad, nrrdTypeDouble)) { |
1105 |
|
|
biffMovef(TEN, NRRD, "%s: trouble converting", me); |
1106 |
|
|
return 1; |
1107 |
|
|
} |
1108 |
|
8 |
dwiKind->valLen = kindData->ngrad->axis[1].size; |
1109 |
|
|
|
1110 |
|
|
/* fixing up the item table ... */ |
1111 |
|
8 |
dwiKind->table[tenDwiGageAll].answerLength = dwiKind->valLen; |
1112 |
|
8 |
dwiKind->table[tenDwiGageJustDWI].answerLength = dwiKind->valLen - 1; |
1113 |
|
8 |
dwiKind->table[tenDwiGageADC].answerLength = dwiKind->valLen - 1; |
1114 |
|
8 |
dwiKind->table[tenDwiGageTensorAllDWIError].answerLength = |
1115 |
|
8 |
dwiKind->valLen - 1; |
1116 |
✓✗✗✗ ✗ |
8 |
switch (e1method) { |
1117 |
|
|
case tenEstimate1MethodLLS: |
1118 |
|
8 |
dwiKind->table[tenDwiGageTensor].prereq[0] |
1119 |
|
8 |
= tenDwiGageTensorLLS; |
1120 |
|
8 |
dwiKind->table[tenDwiGageTensorError].prereq[0] |
1121 |
|
8 |
= tenDwiGageTensorLLSError; |
1122 |
|
8 |
dwiKind->table[tenDwiGageTensorErrorLog].prereq[0] |
1123 |
|
8 |
= tenDwiGageTensorLLSErrorLog; |
1124 |
|
8 |
dwiKind->table[tenDwiGageTensorLikelihood].prereq[0] |
1125 |
|
8 |
= tenDwiGageTensorLLSLikelihood; |
1126 |
|
8 |
break; |
1127 |
|
|
case tenEstimate1MethodWLS: |
1128 |
|
|
dwiKind->table[tenDwiGageTensor].prereq[0] |
1129 |
|
|
= tenDwiGageTensorWLS; |
1130 |
|
|
dwiKind->table[tenDwiGageTensorError].prereq[0] |
1131 |
|
|
= tenDwiGageTensorWLSError; |
1132 |
|
|
dwiKind->table[tenDwiGageTensorErrorLog].prereq[0] |
1133 |
|
|
= tenDwiGageTensorWLSErrorLog; |
1134 |
|
|
dwiKind->table[tenDwiGageTensorLikelihood].prereq[0] |
1135 |
|
|
= tenDwiGageTensorWLSLikelihood; |
1136 |
|
|
break; |
1137 |
|
|
case tenEstimate1MethodNLS: |
1138 |
|
|
dwiKind->table[tenDwiGageTensor].prereq[0] |
1139 |
|
|
= tenDwiGageTensorNLS; |
1140 |
|
|
dwiKind->table[tenDwiGageTensorError].prereq[0] |
1141 |
|
|
= tenDwiGageTensorNLSError; |
1142 |
|
|
dwiKind->table[tenDwiGageTensorErrorLog].prereq[0] |
1143 |
|
|
= tenDwiGageTensorNLSErrorLog; |
1144 |
|
|
dwiKind->table[tenDwiGageTensorLikelihood].prereq[0] |
1145 |
|
|
= tenDwiGageTensorNLSLikelihood; |
1146 |
|
|
break; |
1147 |
|
|
case tenEstimate1MethodMLE: |
1148 |
|
|
dwiKind->table[tenDwiGageTensor].prereq[0] |
1149 |
|
|
= tenDwiGageTensorMLE; |
1150 |
|
|
dwiKind->table[tenDwiGageTensorError].prereq[0] |
1151 |
|
|
= tenDwiGageTensorMLEError; |
1152 |
|
|
dwiKind->table[tenDwiGageTensorErrorLog].prereq[0] |
1153 |
|
|
= tenDwiGageTensorMLEErrorLog; |
1154 |
|
|
dwiKind->table[tenDwiGageTensorLikelihood].prereq[0] |
1155 |
|
|
= tenDwiGageTensorMLELikelihood; |
1156 |
|
|
break; |
1157 |
|
|
default: |
1158 |
|
|
biffAddf(TEN, "%s: unimplemented %s: %s (%d)", me, |
1159 |
|
|
tenEstimate1Method->name, |
1160 |
|
|
airEnumStr(tenEstimate1Method, e1method), e1method); |
1161 |
|
|
return 1; |
1162 |
|
|
break; |
1163 |
|
|
} |
1164 |
|
8 |
kindData->thresh = thresh; |
1165 |
|
8 |
kindData->soft = soft; |
1166 |
|
8 |
kindData->bval = bval; |
1167 |
|
8 |
kindData->valueMin = valueMin; |
1168 |
|
8 |
kindData->est1Method = e1method; |
1169 |
|
8 |
kindData->est2Method = e2method; |
1170 |
|
8 |
kindData->randSeed = randSeed; |
1171 |
|
8 |
return 0; |
1172 |
|
8 |
} |
1173 |
|
|
|
1174 |
|
|
int |
1175 |
|
|
tenDwiGageKindCheck(const gageKind *kind) { |
1176 |
|
|
static const char me[]="tenDwiGageKindCheck"; |
1177 |
|
|
|
1178 |
✗✓ |
32 |
if (!kind) { |
1179 |
|
|
biffAddf(TEN, "%s: got NULL pointer", me); |
1180 |
|
|
return 1; |
1181 |
|
|
} |
1182 |
✗✓ |
16 |
if (strcmp(kind->name, TEN_DWI_GAGE_KIND_NAME)) { |
1183 |
|
|
biffAddf(TEN, "%s: got \"%s\" kind, not \"%s\"", me, |
1184 |
|
|
kind->name, TEN_DWI_GAGE_KIND_NAME); |
1185 |
|
|
return 1; |
1186 |
|
|
} |
1187 |
✗✓ |
16 |
if (0 == kind->valLen) { |
1188 |
|
|
biffAddf(TEN, "%s: don't yet know valLen", me); |
1189 |
|
|
return 1; |
1190 |
|
|
} |
1191 |
✗✓ |
16 |
if (!kind->data) { |
1192 |
|
|
biffAddf(TEN, "%s: kind->data is NULL", me); |
1193 |
|
|
return 1; |
1194 |
|
|
} |
1195 |
|
16 |
return 0; |
1196 |
|
16 |
} |