File: | src/ten/chan.c |
Location: | line 205, column 7 |
Description: | Value stored to 'val' is never read |
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 | const char * |
28 | tenDWMRIModalityKey = "modality"; |
29 | |
30 | const char * |
31 | tenDWMRIModalityVal = "DWMRI"; |
32 | |
33 | const char * |
34 | tenDWMRINAVal = "n/a"; |
35 | |
36 | const char * |
37 | tenDWMRIBValueKey = "DWMRI_b-value"; |
38 | |
39 | const char * |
40 | tenDWMRIGradKeyFmt = "DWMRI_gradient_%04u"; |
41 | |
42 | const char * |
43 | tenDWMRIBmatKeyFmt = "DWMRI_B-matrix_%04u"; |
44 | |
45 | const char * |
46 | tenDWMRINexKeyFmt = "DWMRI_NEX_%04u"; |
47 | |
48 | const char * |
49 | tenDWMRISkipKeyFmt = "DWMRI_skip_%04u"; |
50 | |
51 | /* |
52 | ******** tenDWMRIKeyValueParse |
53 | ** |
54 | ** Parses through key-value pairs in the NRRD header to determine the |
55 | ** list of diffusion-sensitizing gradient directions, or B-matrices |
56 | ** (depending to what was found), according the NAMIC conventions. |
57 | ** This requires, among other things, that ndwi be have exactly one |
58 | ** axis with kind nrrdKindList (or nrrdKindVector), which is taken to |
59 | ** be the DWI axis. |
60 | ** |
61 | ** Either *ngradP or *nbmatP is set to a newly- allocated nrrd |
62 | ** containing this information, and the other one is set to NULL |
63 | ** The (scalar) b-value is stored in *bP. The image values that are |
64 | ** to be skipped are stored in the *skipP array (allocated here), |
65 | ** the length of that array is stored in *skipNumP. Unlike the skip |
66 | ** array used internally with tenEstimate, this is just a simple 1-D |
67 | ** array; it is not a list of pairs of (index,skipBool). |
68 | */ |
69 | int |
70 | tenDWMRIKeyValueParse(Nrrd **ngradP, Nrrd **nbmatP, double *bP, |
71 | unsigned int **skipP, unsigned int *skipNumP, |
72 | const Nrrd *ndwi) { |
73 | static const char me[]="tenDWMRIKeyValueParse"; |
74 | char tmpKey[AIR_STRLEN_MED(256+1)], |
75 | key[AIR_STRLEN_MED(256+1)], *val; |
76 | const char *keyFmt; |
77 | int dwiAxis; |
78 | unsigned int axi, dwiIdx, dwiNum, valNum, valIdx, parsedNum, |
79 | nexNum, nexIdx, skipIdx, *skipLut; |
80 | Nrrd *ninfo; |
81 | double *info, normMax, norm; |
82 | airArray *mop, *skipArr; |
83 | |
84 | if (!( ngradP && nbmatP && skipP && skipNumP && bP && ndwi )) { |
85 | biffAddf(TENtenBiffKey, "%s: got NULL pointer", me); |
86 | return 1; |
87 | } |
88 | |
89 | /* check modality */ |
90 | val = nrrdKeyValueGet(ndwi, tenDWMRIModalityKey); |
91 | if (!val) { |
92 | biffAddf(TENtenBiffKey, "%s: didn't have \"%s\" key", me, tenDWMRIModalityKey); |
93 | return 1; |
94 | } |
95 | if (strncmp(tenDWMRIModalityVal, val + strspn(val, AIR_WHITESPACE" \t\n\r\v\f"), |
96 | strlen(tenDWMRIModalityVal))) { |
97 | biffAddf(TENtenBiffKey, "%s: \"%s\" value was \"%s\", not \"%s\"", me, |
98 | tenDWMRIModalityKey, val, tenDWMRIModalityVal); |
99 | return 1; |
100 | } |
101 | val = (char *)airFree(val); |
102 | |
103 | /* learn b-value */ |
104 | val = nrrdKeyValueGet(ndwi, tenDWMRIBValueKey); |
105 | if (!val) { |
106 | biffAddf(TENtenBiffKey, "%s: didn't have \"%s\" key", me, tenDWMRIBValueKey); |
107 | return 1; |
108 | } |
109 | if (1 != sscanf(val, "%lg", bP)) { |
110 | biffAddf(TENtenBiffKey, "%s: couldn't parse float from value \"%s\" " |
111 | "for key \"%s\"", me, |
112 | val, tenDWMRIBValueKey); |
113 | return 1; |
114 | } |
115 | val = (char *)airFree(val); |
116 | |
117 | /* find single DWI axis, set dwiNum to its size */ |
118 | dwiAxis = -1; |
119 | for (axi=0; axi<ndwi->dim; axi++) { |
120 | /* the use of nrrdKindVector here is out of deference to how ITK's |
121 | itkNrrdImageIO.cxx uses nrrdKindVector for VECTOR pixels */ |
122 | if (nrrdKindList == ndwi->axis[axi].kind |
123 | || nrrdKindVector == ndwi->axis[axi].kind) { |
124 | if (-1 != dwiAxis) { |
125 | biffAddf(TENtenBiffKey, "%s: already saw %s or %s kind on axis %d", me, |
126 | airEnumStr(nrrdKind, nrrdKindList), |
127 | airEnumStr(nrrdKind, nrrdKindVector), dwiAxis); |
128 | return 1; |
129 | } |
130 | dwiAxis = axi; |
131 | } |
132 | } |
133 | if (-1 == dwiAxis) { |
134 | biffAddf(TENtenBiffKey, "%s: did not see \"%s\" kind on any axis", me, |
135 | airEnumStr(nrrdKind, nrrdKindList)); |
136 | return 1; |
137 | } |
138 | dwiNum = ndwi->axis[dwiAxis].size; |
139 | |
140 | /* figure out if we're parsing gradients or b-matrices */ |
141 | sprintf(tmpKey, tenDWMRIGradKeyFmt, 0)__builtin___sprintf_chk (tmpKey, 0, __builtin_object_size (tmpKey , 2 > 1 ? 1 : 0), tenDWMRIGradKeyFmt, 0); |
142 | val = nrrdKeyValueGet(ndwi, tmpKey); |
143 | if (val) { |
144 | valNum = 3; |
145 | } else { |
146 | valNum = 6; |
147 | sprintf(key, tenDWMRIBmatKeyFmt, 0)__builtin___sprintf_chk (key, 0, __builtin_object_size (key, 2 > 1 ? 1 : 0), tenDWMRIBmatKeyFmt, 0); |
148 | val = nrrdKeyValueGet(ndwi, key); |
149 | if (!val) { |
150 | biffAddf(TENtenBiffKey, "%s: saw neither \"%s\" nor \"%s\" key", me, |
151 | tmpKey, key); |
152 | return 1; |
153 | } |
154 | } |
155 | val = (char *)airFree(val); |
156 | |
157 | /* set up parsing and allocate one of output nrrds */ |
158 | if (3 == valNum) { |
159 | keyFmt = tenDWMRIGradKeyFmt; |
160 | ninfo = *ngradP = nrrdNew(); |
161 | *nbmatP = NULL((void*)0); |
162 | } else { |
163 | keyFmt = tenDWMRIBmatKeyFmt; |
164 | *ngradP = NULL((void*)0); |
165 | ninfo = *nbmatP = nrrdNew(); |
166 | } |
167 | if (nrrdMaybeAlloc_va(ninfo, nrrdTypeDouble, 2, |
168 | AIR_CAST(size_t, valNum)((size_t)(valNum)), |
169 | AIR_CAST(size_t, dwiNum)((size_t)(dwiNum)))) { |
170 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't allocate output", me); |
171 | return 1; |
172 | } |
173 | info = (double *)(ninfo->data); |
174 | |
175 | /* set up skip list recording */ |
176 | mop = airMopNew(); |
177 | skipArr = airArrayNew((void**)skipP, skipNumP, sizeof(unsigned int), 16); |
178 | airMopAdd(mop, skipArr, (airMopper)airArrayNix, airMopAlways); |
179 | skipLut = AIR_CALLOC(dwiNum, unsigned int)(unsigned int*)(calloc((dwiNum), sizeof(unsigned int))); |
180 | airMopAdd(mop, skipLut, airFree, airMopAlways); |
181 | if (!skipLut) { |
182 | biffAddf(TENtenBiffKey, "%s: couldn't allocate skip LUT", me); |
183 | airMopError(mop); return 1; |
184 | } |
185 | |
186 | /* parse values in ninfo */ |
187 | for (dwiIdx=0; dwiIdx<dwiNum; dwiIdx++) { |
188 | sprintf(key, keyFmt, dwiIdx)__builtin___sprintf_chk (key, 0, __builtin_object_size (key, 2 > 1 ? 1 : 0), keyFmt, dwiIdx); |
189 | val = nrrdKeyValueGet(ndwi, key); |
190 | if (!val) { |
191 | biffAddf(TENtenBiffKey, "%s: didn't see \"%s\" key", me, key); |
192 | airMopError(mop); return 1; |
193 | } |
194 | airToLower(val); |
195 | if (!strncmp(tenDWMRINAVal, val + strspn(val, AIR_WHITESPACE" \t\n\r\v\f"), |
196 | strlen(tenDWMRINAVal))) { |
197 | /* have no sensible gradient or B-matrix info here, and must skip */ |
198 | for (valIdx=0; valIdx<valNum; valIdx++) { |
199 | info[valIdx] = AIR_NAN(airFloatQNaN.f); |
200 | } |
201 | skipIdx = airArrayLenIncr(skipArr, 1); |
202 | (*skipP)[skipIdx] = dwiIdx; |
203 | skipLut[dwiIdx] = AIR_TRUE1; |
204 | /* can't have NEX on a skipped gradient or B-matrix */ |
205 | val = (char *)airFree(val); |
Value stored to 'val' is never read | |
206 | sprintf(key, tenDWMRINexKeyFmt, dwiIdx)__builtin___sprintf_chk (key, 0, __builtin_object_size (key, 2 > 1 ? 1 : 0), tenDWMRINexKeyFmt, dwiIdx); |
207 | val = nrrdKeyValueGet(ndwi, key); |
208 | if (val) { |
209 | biffAddf(TENtenBiffKey, "%s: can't have NEX of skipped DWI %u", me, skipIdx); |
210 | airMopError(mop); return 1; |
211 | } |
212 | nexNum = 1; /* for "info +=" below */ |
213 | } else { |
214 | /* we probably do have sensible gradient or B-matrix info */ |
215 | parsedNum = airParseStrD(info, val, AIR_WHITESPACE" \t\n\r\v\f", valNum); |
216 | if (valNum != parsedNum) { |
217 | biffAddf(TENtenBiffKey, "%s: couldn't parse %d floats in value \"%s\" " |
218 | "for key \"%s\" (only got %d)", |
219 | me, valNum, val, key, parsedNum); |
220 | airMopError(mop); return 1; |
221 | } |
222 | val = (char *)airFree(val); |
223 | sprintf(key, tenDWMRINexKeyFmt, dwiIdx)__builtin___sprintf_chk (key, 0, __builtin_object_size (key, 2 > 1 ? 1 : 0), tenDWMRINexKeyFmt, dwiIdx); |
224 | val = nrrdKeyValueGet(ndwi, key); |
225 | if (!val) { |
226 | /* there is no NEX indicated */ |
227 | nexNum = 1; |
228 | } else { |
229 | if (1 != sscanf(val, "%u", &nexNum)) { |
230 | biffAddf(TENtenBiffKey, "%s: couldn't parse integer in value \"%s\" " |
231 | "for key \"%s\"", me, val, key); |
232 | airMopError(mop); return 1; |
233 | } |
234 | val = (char *)airFree(val); |
235 | if (!( nexNum >= 1 )) { |
236 | biffAddf(TENtenBiffKey, "%s: NEX (%d) for DWI %d not >= 1", |
237 | me, nexNum, dwiIdx); |
238 | airMopError(mop); return 1; |
239 | } |
240 | if (!( dwiIdx + nexNum - 1 < dwiNum )) { |
241 | biffAddf(TENtenBiffKey, "%s: NEX %d for DWI %d implies %d DWI > real # DWI %d", |
242 | me, nexNum, dwiIdx, dwiIdx + nexNum, dwiNum); |
243 | airMopError(mop); return 1; |
244 | } |
245 | for (nexIdx=1; nexIdx<nexNum; nexIdx++) { |
246 | sprintf(key, keyFmt, dwiIdx+nexIdx)__builtin___sprintf_chk (key, 0, __builtin_object_size (key, 2 > 1 ? 1 : 0), keyFmt, dwiIdx+nexIdx); |
247 | val = nrrdKeyValueGet(ndwi, key); |
248 | if (val) { |
249 | val = (char *)airFree(val); |
250 | biffAddf(TENtenBiffKey, "%s: shouldn't have key \"%s\" with " |
251 | "NEX %d for DWI %d", me, key, nexNum, dwiIdx); |
252 | airMopError(mop); return 1; |
253 | } |
254 | for (valIdx=0; valIdx<valNum; valIdx++) { |
255 | info[valIdx + valNum*nexIdx] = info[valIdx]; |
256 | } |
257 | } |
258 | dwiIdx += nexNum-1; |
259 | } |
260 | } |
261 | info += valNum*nexNum; |
262 | } |
263 | |
264 | /* perhaps too paranoid: see if there are extra keys, |
265 | which probably implies confusion/mismatch between number of |
266 | gradients and number of values */ |
267 | sprintf(key, keyFmt, dwiIdx)__builtin___sprintf_chk (key, 0, __builtin_object_size (key, 2 > 1 ? 1 : 0), keyFmt, dwiIdx); |
268 | val = nrrdKeyValueGet(ndwi, key); |
269 | if (val) { |
270 | biffAddf(TENtenBiffKey, "%s: saw \"%s\" key, more than required %u keys, " |
271 | "likely mismatch between keys and actual gradients", |
272 | me, key, dwiIdx); |
273 | airMopError(mop); return 1; |
274 | } |
275 | |
276 | /* second pass: see which ones are skipped, even though gradient/B-matrix |
277 | information has been specified */ |
278 | for (dwiIdx=0; dwiIdx<dwiNum; dwiIdx++) { |
279 | sprintf(key, tenDWMRISkipKeyFmt, dwiIdx)__builtin___sprintf_chk (key, 0, __builtin_object_size (key, 2 > 1 ? 1 : 0), tenDWMRISkipKeyFmt, dwiIdx); |
280 | val = nrrdKeyValueGet(ndwi, key); |
281 | if (val) { |
282 | airToLower(val); |
283 | if (!strncmp("true", val + strspn(val, AIR_WHITESPACE" \t\n\r\v\f"), |
284 | strlen("true"))) { |
285 | skipIdx = airArrayLenIncr(skipArr, 1); |
286 | (*skipP)[skipIdx] = dwiIdx; |
287 | skipLut[dwiIdx] = AIR_TRUE1; |
288 | } |
289 | } |
290 | } |
291 | |
292 | /* normalize so that maximal norm is 1.0 */ |
293 | /* Thu Dec 20 03:25:20 CST 2012 this rescaling IS in fact what is |
294 | causing the small discrepency between ngrad before and after |
295 | saving to KVPs. The problem is not related to how the gradient |
296 | vector coefficients are recovered from the string-based |
297 | representation; that is likely bit-for-bit correct. The problem |
298 | is when everything is rescaled by 1.0/normMax: a "normalized" |
299 | vector will not have *exactly* length 1.0. So what can be done |
300 | to prevent pointlessly altering the lengths of vectors that were |
301 | close enough to unit-length? Is there some more 754-savvy |
302 | way of doing this normalization? */ |
303 | normMax = 0; |
304 | info = (double *)(ninfo->data); |
305 | for (dwiIdx=0; dwiIdx<dwiNum; dwiIdx++) { |
306 | if (!skipLut[dwiIdx]) { |
307 | if (3 == valNum) { |
308 | norm = ELL_3V_LEN(info)(sqrt((((info))[0]*((info))[0] + ((info))[1]*((info))[1] + (( info))[2]*((info))[2]))); |
309 | } else { |
310 | norm = sqrt(info[0]*info[0] + 2*info[1]*info[1] + 2*info[2]*info[2] |
311 | + info[3]*info[3] + 2*info[4]*info[4] |
312 | + info[5]*info[5]); |
313 | } |
314 | normMax = AIR_MAX(normMax, norm)((normMax) > (norm) ? (normMax) : (norm)); |
315 | } |
316 | info += valNum; |
317 | } |
318 | info = (double *)(ninfo->data); |
319 | for (dwiIdx=0; dwiIdx<dwiNum; dwiIdx++) { |
320 | if (!skipLut[dwiIdx]) { |
321 | if (3 == valNum) { |
322 | ELL_3V_SCALE(info, 1.0/normMax, info)((info)[0] = (1.0/normMax)*(info)[0], (info)[1] = (1.0/normMax )*(info)[1], (info)[2] = (1.0/normMax)*(info)[2]); |
323 | } else { |
324 | ELL_6V_SCALE(info, 1.0/normMax, info)((info)[0] = (1.0/normMax)*(info)[0], (info)[1] = (1.0/normMax )*(info)[1], (info)[2] = (1.0/normMax)*(info)[2], (info)[3] = (1.0/normMax)*(info)[3], (info)[4] = (1.0/normMax)*(info)[4] , (info)[5] = (1.0/normMax)*(info)[5]); |
325 | } |
326 | } |
327 | info += valNum; |
328 | } |
329 | |
330 | airMopOkay(mop); |
331 | return 0; |
332 | } |
333 | |
334 | /* |
335 | ******** tenBMatrixCalc |
336 | ** |
337 | ** given a list of gradient directions (arbitrary type), contructs the |
338 | ** B-matrix that records how each coefficient of the diffusion tensor |
339 | ** is weighted in the diffusion weighted images. Matrix will be a |
340 | ** 6-by-N 2D array of doubles. |
341 | ** |
342 | ** NOTE 1: The ordering of the elements in each row is (like the ordering |
343 | ** of the tensor elements in all of ten): |
344 | ** |
345 | ** Bxx Bxy Bxz Byy Byz Bzz |
346 | ** |
347 | ** NOTE 2: The off-diagonal elements are NOT pre-multiplied by two. |
348 | */ |
349 | int |
350 | tenBMatrixCalc(Nrrd *nbmat, const Nrrd *_ngrad) { |
351 | static const char me[]="tenBMatrixCalc"; |
352 | Nrrd *ngrad; |
353 | double *bmat, *G; |
354 | int DD, dd; |
355 | airArray *mop; |
356 | |
357 | if (!(nbmat && _ngrad && !tenGradientCheck(_ngrad, nrrdTypeDefault, 1))) { |
358 | biffAddf(TENtenBiffKey, "%s: got NULL pointer or invalid arg", me); |
359 | return 1; |
360 | } |
361 | mop = airMopNew(); |
362 | airMopAdd(mop, ngrad=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); |
363 | if (nrrdConvert(ngrad, _ngrad, nrrdTypeDouble) |
364 | || nrrdMaybeAlloc_va(nbmat, nrrdTypeDouble, 2, |
365 | AIR_CAST(size_t, 6)((size_t)(6)), ngrad->axis[1].size)) { |
366 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: trouble", me); |
367 | airMopError(mop); return 1; |
368 | } |
369 | |
370 | DD = ngrad->axis[1].size; |
371 | G = (double*)(ngrad->data); |
372 | bmat = (double*)(nbmat->data); |
373 | for (dd=0; dd<DD; dd++) { |
374 | ELL_6V_SET(bmat,((bmat)[0]=(G[0]*G[0]), (bmat)[1]=(G[0]*G[1]), (bmat)[2]=(G[0 ]*G[2]), (bmat)[3]=(G[1]*G[1]), (bmat)[4]=(G[1]*G[2]), (bmat) [5]=(G[2]*G[2])) |
375 | G[0]*G[0], G[0]*G[1], G[0]*G[2],((bmat)[0]=(G[0]*G[0]), (bmat)[1]=(G[0]*G[1]), (bmat)[2]=(G[0 ]*G[2]), (bmat)[3]=(G[1]*G[1]), (bmat)[4]=(G[1]*G[2]), (bmat) [5]=(G[2]*G[2])) |
376 | G[1]*G[1], G[1]*G[2],((bmat)[0]=(G[0]*G[0]), (bmat)[1]=(G[0]*G[1]), (bmat)[2]=(G[0 ]*G[2]), (bmat)[3]=(G[1]*G[1]), (bmat)[4]=(G[1]*G[2]), (bmat) [5]=(G[2]*G[2])) |
377 | G[2]*G[2])((bmat)[0]=(G[0]*G[0]), (bmat)[1]=(G[0]*G[1]), (bmat)[2]=(G[0 ]*G[2]), (bmat)[3]=(G[1]*G[1]), (bmat)[4]=(G[1]*G[2]), (bmat) [5]=(G[2]*G[2])); |
378 | G += 3; |
379 | bmat += 6; |
380 | } |
381 | nbmat->axis[0].kind = nrrdKind3DSymMatrix; |
382 | |
383 | airMopOkay(mop); |
384 | return 0; |
385 | } |
386 | |
387 | /* |
388 | ******** tenEMatrixCalc |
389 | ** |
390 | */ |
391 | int |
392 | tenEMatrixCalc(Nrrd *nemat, const Nrrd *_nbmat, int knownB0) { |
393 | static const char me[]="tenEMatrixCalc"; |
394 | Nrrd *nbmat, *ntmp; |
395 | airArray *mop; |
396 | ptrdiff_t padmin[2], padmax[2]; |
397 | unsigned int ri; |
398 | double *bmat; |
399 | |
400 | if (!(nemat && _nbmat)) { |
401 | biffAddf(TENtenBiffKey, "%s: got NULL pointer", me); |
402 | return 1; |
403 | } |
404 | if (tenBMatrixCheck(_nbmat, nrrdTypeDefault, 6)) { |
405 | biffAddf(TENtenBiffKey, "%s: problem with B matrix", me); |
406 | return 1; |
407 | } |
408 | mop = airMopNew(); |
409 | airMopAdd(mop, nbmat=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); |
410 | if (knownB0) { |
411 | if (nrrdConvert(nbmat, _nbmat, nrrdTypeDouble)) { |
412 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't convert given bmat to doubles", me); |
413 | airMopError(mop); return 1; |
414 | } |
415 | } else { |
416 | airMopAdd(mop, ntmp=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); |
417 | if (nrrdConvert(ntmp, _nbmat, nrrdTypeDouble)) { |
418 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't convert given bmat to doubles", me); |
419 | airMopError(mop); return 1; |
420 | } |
421 | ELL_2V_SET(padmin, 0, 0)((padmin)[0]=(0), (padmin)[1]=(0)); |
422 | ELL_2V_SET(padmax, 6, _nbmat->axis[1].size-1)((padmax)[0]=(6), (padmax)[1]=(_nbmat->axis[1].size-1)); |
423 | if (nrrdPad_nva(nbmat, ntmp, padmin, padmax, nrrdBoundaryPad, -1)) { |
424 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't pad given bmat", me); |
425 | airMopError(mop); return 1; |
426 | } |
427 | } |
428 | bmat = (double*)(nbmat->data); |
429 | /* HERE is where the off-diagonal elements get multiplied by 2 */ |
430 | for (ri=0; ri<nbmat->axis[1].size; ri++) { |
431 | bmat[1] *= 2; |
432 | bmat[2] *= 2; |
433 | bmat[4] *= 2; |
434 | bmat += nbmat->axis[0].size; |
435 | } |
436 | if (ell_Nm_pseudo_inv(nemat, nbmat)) { |
437 | biffMovef(TENtenBiffKey, ELLell_biff_key, "%s: trouble pseudo-inverting B-matrix", me); |
438 | airMopError(mop); return 1; |
439 | } |
440 | airMopOkay(mop); |
441 | return 0; |
442 | } |
443 | |
444 | /* |
445 | ******** tenEstimateLinearSingle_d |
446 | ** |
447 | ** estimate one single tensor |
448 | ** |
449 | ** !! requires being passed a pre-allocated double array "vbuf" which is |
450 | ** !! used for intermediate calculations (details below) |
451 | ** |
452 | ** DD is always the length of the dwi[] array |
453 | ** |
454 | ** -------------- IF knownB0 ------------------------- |
455 | ** input: |
456 | ** dwi[0] is the B0 image, dwi[1]..dwi[DD-1] are the (DD-1) DWI values, |
457 | ** emat is the (DD-1)-by-6 estimation matrix, which is the pseudo-inverse |
458 | ** of the B-matrix (after the off-diagonals have been multiplied by 2). |
459 | ** vbuf[] is allocated for (at least) DD-1 doubles (DD is fine) |
460 | ** |
461 | ** output: |
462 | ** ten[0]..ten[6] will be the confidence value followed by the tensor |
463 | ** if B0P, then *B0P is set to the B0 value used in calcs: max(b0,1) |
464 | ** -------------- IF !knownB0 ------------------------- |
465 | ** input: |
466 | ** dwi[0]..dwi[DD-1] are the DD DWI values, emat is the DD-by-7 estimation |
467 | ** matrix. The 7th column is for estimating the B0 image. |
468 | ** vbuf[] is allocated for DD doubles |
469 | ** |
470 | ** output: |
471 | ** ten[0]..ten[6] will be the confidence value followed by the tensor |
472 | ** if B0P, then *B0P is set to estimated B0 value. |
473 | ** ---------------------------------------------------- |
474 | */ |
475 | void |
476 | tenEstimateLinearSingle_d(double *ten, double *B0P, /* output */ |
477 | const double *dwi, const double *emat, /* input .. */ |
478 | double *vbuf, unsigned int DD, int knownB0, |
479 | double thresh, double soft, double b) { |
480 | double logB0, tmp, mean; |
481 | unsigned int ii, jj; |
482 | /* static const char me[]="tenEstimateLinearSingle_d"; */ |
483 | |
484 | if (knownB0) { |
485 | if (B0P) { |
486 | /* we save this as a courtesy */ |
487 | *B0P = AIR_MAX(dwi[0], 1)((dwi[0]) > (1) ? (dwi[0]) : (1)); |
488 | } |
489 | logB0 = log(AIR_MAX(dwi[0], 1)((dwi[0]) > (1) ? (dwi[0]) : (1))); |
490 | mean = 0; |
491 | for (ii=1; ii<DD; ii++) { |
492 | tmp = AIR_MAX(dwi[ii], 1)((dwi[ii]) > (1) ? (dwi[ii]) : (1)); |
493 | mean += tmp; |
494 | vbuf[ii-1] = (logB0 - log(tmp))/b; |
495 | /* if (tenVerbose) { |
496 | fprintf(stderr, "vbuf[%d] = %f\n", ii-1, vbuf[ii-1]); |
497 | } */ |
498 | } |
499 | mean /= DD-1; |
500 | if (soft) { |
501 | ten[0] = AIR_AFFINE(-1, airErf((mean - thresh)/(soft + 0.000001)), 1,( ((double)(1)-(0))*((double)(airErf((mean - thresh)/(soft + 0.000001 )))-(-1)) / ((double)(1)-(-1)) + (0)) |
502 | 0, 1)( ((double)(1)-(0))*((double)(airErf((mean - thresh)/(soft + 0.000001 )))-(-1)) / ((double)(1)-(-1)) + (0)); |
503 | } else { |
504 | ten[0] = mean > thresh; |
505 | } |
506 | for (jj=0; jj<6; jj++) { |
507 | tmp = 0; |
508 | for (ii=0; ii<DD-1; ii++) { |
509 | tmp += emat[ii + (DD-1)*jj]*vbuf[ii]; |
510 | } |
511 | ten[jj+1] = tmp; |
512 | } |
513 | } else { |
514 | /* !knownB0 */ |
515 | mean = 0; |
516 | for (ii=0; ii<DD; ii++) { |
517 | tmp = AIR_MAX(dwi[ii], 1)((dwi[ii]) > (1) ? (dwi[ii]) : (1)); |
518 | mean += tmp; |
519 | vbuf[ii] = -log(tmp)/b; |
520 | } |
521 | mean /= DD; |
522 | if (soft) { |
523 | ten[0] = AIR_AFFINE(-1, airErf((mean - thresh)/(soft + 0.000001)), 1,( ((double)(1)-(0))*((double)(airErf((mean - thresh)/(soft + 0.000001 )))-(-1)) / ((double)(1)-(-1)) + (0)) |
524 | 0, 1)( ((double)(1)-(0))*((double)(airErf((mean - thresh)/(soft + 0.000001 )))-(-1)) / ((double)(1)-(-1)) + (0)); |
525 | } else { |
526 | ten[0] = mean > thresh; |
527 | } |
528 | for (jj=0; jj<7; jj++) { |
529 | tmp = 0; |
530 | for (ii=0; ii<DD; ii++) { |
531 | tmp += emat[ii + DD*jj]*vbuf[ii]; |
532 | } |
533 | if (jj < 6) { |
534 | ten[jj+1] = tmp; |
535 | } else { |
536 | /* we're on seventh row, for finding B0 */ |
537 | if (B0P) { |
538 | *B0P = exp(b*tmp); |
539 | } |
540 | } |
541 | } |
542 | } |
543 | return; |
544 | } |
545 | |
546 | void |
547 | tenEstimateLinearSingle_f(float *_ten, float *_B0P, /* output */ |
548 | const float *_dwi, const double *emat, /* input .. */ |
549 | double *vbuf, unsigned int DD, int knownB0, |
550 | float thresh, float soft, float b) { |
551 | static const char me[]="tenEstimateLinearSingle_f"; |
552 | #define DWI_NUM_MAX256 256 |
553 | double dwi[DWI_NUM_MAX256], ten[7], B0; |
554 | unsigned int dwiIdx; |
555 | |
556 | /* HEY: this is somewhat inelegant .. */ |
557 | if (DD > DWI_NUM_MAX256) { |
558 | fprintf(stderr__stderrp, "%s: PANIC: sorry, DD=%u > compile-time DWI_NUM_MAX=%u\n", |
559 | me, DD, DWI_NUM_MAX256); |
560 | exit(1); |
561 | } |
562 | for (dwiIdx=0; dwiIdx<DD; dwiIdx++) { |
563 | dwi[dwiIdx] = _dwi[dwiIdx]; |
564 | } |
565 | tenEstimateLinearSingle_d(ten, _B0P ? &B0 : NULL((void*)0), |
566 | dwi, emat, |
567 | vbuf, DD, knownB0, |
568 | thresh, soft, b); |
569 | TEN_T_COPY_TT(_ten, float, ten)( (_ten)[0] = ((float)((ten)[0])), (_ten)[1] = ((float)((ten) [1])), (_ten)[2] = ((float)((ten)[2])), (_ten)[3] = ((float)( (ten)[3])), (_ten)[4] = ((float)((ten)[4])), (_ten)[5] = ((float )((ten)[5])), (_ten)[6] = ((float)((ten)[6])) ); |
570 | if (_B0P) { |
571 | *_B0P = AIR_CAST(float, B0)((float)(B0)); |
572 | } |
573 | return; |
574 | } |
575 | |
576 | /* |
577 | ******** tenEstimateLinear3D |
578 | ** |
579 | ** takes an array of DWIs (starting with the B=0 image), joins them up, |
580 | ** and passes it all off to tenEstimateLinear4D |
581 | ** |
582 | ** Note: this will copy per-axis peripheral information from _ndwi[0] |
583 | */ |
584 | int |
585 | tenEstimateLinear3D(Nrrd *nten, Nrrd **nterrP, Nrrd **nB0P, |
586 | const Nrrd *const *_ndwi, unsigned int dwiLen, |
587 | const Nrrd *_nbmat, int knownB0, |
588 | double thresh, double soft, double b) { |
589 | static const char me[]="tenEstimateLinear3D"; |
590 | Nrrd *ndwi; |
591 | airArray *mop; |
592 | int amap[4] = {-1, 0, 1, 2}; |
593 | |
594 | if (!(_ndwi)) { |
595 | biffAddf(TENtenBiffKey, "%s: got NULL pointer", me); |
596 | return 1; |
597 | } |
598 | mop = airMopNew(); |
599 | ndwi = nrrdNew(); |
600 | airMopAdd(mop, ndwi, (airMopper)nrrdNuke, airMopAlways); |
601 | if (nrrdJoin(ndwi, (const Nrrd*const*)_ndwi, dwiLen, 0, AIR_TRUE1)) { |
602 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: trouble joining inputs", me); |
603 | airMopError(mop); return 1; |
604 | } |
605 | |
606 | nrrdAxisInfoCopy(ndwi, _ndwi[0], amap, NRRD_AXIS_INFO_NONE0); |
607 | if (tenEstimateLinear4D(nten, nterrP, nB0P, |
608 | ndwi, _nbmat, knownB0, thresh, soft, b)) { |
609 | biffAddf(TENtenBiffKey, "%s: trouble", me); |
610 | airMopError(mop); return 1; |
611 | } |
612 | |
613 | airMopOkay(mop); |
614 | return 0; |
615 | } |
616 | |
617 | /* |
618 | ******** tenEstimateLinear4D |
619 | ** |
620 | ** given a stack of DWI volumes (ndwi) and the imaging B-matrix used |
621 | ** for acquisiton (_nbmat), computes and stores diffusion tensors in |
622 | ** nten. |
623 | ** |
624 | ** The mean of the diffusion-weighted images is thresholded at "thresh" with |
625 | ** softness parameter "soft". |
626 | ** |
627 | ** This takes the B-matrix (weighting matrix), such as formed by tenBMatrix, |
628 | ** or from a more complete account of the gradients present in an imaging |
629 | ** sequence, and then does the pseudo inverse to get the estimation matrix |
630 | */ |
631 | int |
632 | tenEstimateLinear4D(Nrrd *nten, Nrrd **nterrP, Nrrd **nB0P, |
633 | const Nrrd *ndwi, const Nrrd *_nbmat, int knownB0, |
634 | double thresh, double soft, double b) { |
635 | static const char me[]="tenEstimateLinear4D"; |
636 | Nrrd *nemat, *nbmat, *ncrop, *nhist; |
637 | airArray *mop; |
638 | size_t cmin[4], cmax[4], sx, sy, sz, II, d, DD; |
639 | int E, amap[4]; |
640 | float *ten, *dwi1, *dwi2, *terr, |
641 | _B0, *B0, (*lup)(const void *, size_t); |
642 | double *emat, *bmat, *vbuf; |
643 | NrrdRange *range; |
644 | float te, d1, d2; |
645 | char stmp[2][AIR_STRLEN_SMALL(128+1)]; |
646 | |
647 | if (!(nten && ndwi && _nbmat)) { |
648 | /* nerrP and _NB0P can be NULL */ |
649 | biffAddf(TENtenBiffKey, "%s: got NULL pointer", me); |
650 | return 1; |
651 | } |
652 | if (!( 4 == ndwi->dim && 7 <= ndwi->axis[0].size )) { |
653 | biffAddf(TENtenBiffKey, "%s: dwi should be 4-D array with axis 0 size >= 7", me); |
654 | return 1; |
655 | } |
656 | if (tenBMatrixCheck(_nbmat, nrrdTypeDefault, 6)) { |
657 | biffAddf(TENtenBiffKey, "%s: problem with B matrix", me); |
658 | return 1; |
659 | } |
660 | if (knownB0) { |
661 | if (!( ndwi->axis[0].size == 1 + _nbmat->axis[1].size )) { |
662 | biffAddf(TENtenBiffKey, "%s: (knownB0 == true) # input images (%s) " |
663 | "!= 1 + # B matrix rows (1+%s)", me, |
664 | airSprintSize_t(stmp[0], ndwi->axis[0].size), |
665 | airSprintSize_t(stmp[1], _nbmat->axis[1].size)); |
666 | return 1; |
667 | } |
668 | } else { |
669 | if (!( ndwi->axis[0].size == _nbmat->axis[1].size )) { |
670 | biffAddf(TENtenBiffKey, "%s: (knownB0 == false) # dwi (%s) " |
671 | "!= # B matrix rows (%s)", me, |
672 | airSprintSize_t(stmp[0], ndwi->axis[0].size), |
673 | airSprintSize_t(stmp[1], _nbmat->axis[1].size)); |
674 | return 1; |
675 | } |
676 | } |
677 | |
678 | DD = ndwi->axis[0].size; |
679 | sx = ndwi->axis[1].size; |
680 | sy = ndwi->axis[2].size; |
681 | sz = ndwi->axis[3].size; |
682 | mop = airMopNew(); |
683 | airMopAdd(mop, nbmat=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); |
684 | if (nrrdConvert(nbmat, _nbmat, nrrdTypeDouble)) { |
685 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: trouble converting to doubles", me); |
686 | airMopError(mop); return 1; |
687 | } |
688 | airMopAdd(mop, nemat=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); |
689 | if (tenEMatrixCalc(nemat, nbmat, knownB0)) { |
690 | biffAddf(TENtenBiffKey, "%s: trouble computing estimation matrix", me); |
691 | airMopError(mop); return 1; |
692 | } |
693 | vbuf = AIR_CALLOC(knownB0 ? DD-1 : DD, double)(double*)(calloc((knownB0 ? DD-1 : DD), sizeof(double))); |
694 | dwi1 = AIR_CALLOC(DD, float)(float*)(calloc((DD), sizeof(float))); |
695 | dwi2 = AIR_CALLOC(knownB0 ? DD : DD+1, float)(float*)(calloc((knownB0 ? DD : DD+1), sizeof(float))); |
696 | airMopAdd(mop, vbuf, airFree, airMopAlways); |
697 | airMopAdd(mop, dwi1, airFree, airMopAlways); |
698 | airMopAdd(mop, dwi2, airFree, airMopAlways); |
699 | if (!(vbuf && dwi1 && dwi2)) { |
700 | biffAddf(TENtenBiffKey, "%s: couldn't allocate temp buffers", me); |
701 | airMopError(mop); return 1; |
702 | } |
703 | if (!AIR_EXISTS(thresh)(((int)(!((thresh) - (thresh)))))) { |
704 | airMopAdd(mop, ncrop=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); |
705 | airMopAdd(mop, nhist=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); |
706 | ELL_4V_SET(cmin, knownB0 ? 1 : 0, 0, 0, 0)((cmin)[0] = (knownB0 ? 1 : 0), (cmin)[1] = (0), (cmin)[2] = ( 0), (cmin)[3] = (0)); |
707 | ELL_4V_SET(cmax, DD-1, sx-1, sy-1, sz-1)((cmax)[0] = (DD-1), (cmax)[1] = (sx-1), (cmax)[2] = (sy-1), ( cmax)[3] = (sz-1)); |
708 | E = 0; |
709 | if (!E) E |= nrrdCrop(ncrop, ndwi, cmin, cmax); |
710 | if (!E) range = nrrdRangeNewSet(ncrop, nrrdBlind8BitRangeState); |
711 | if (!E) airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); |
712 | if (!E) E |= nrrdHisto(nhist, ncrop, range, NULL((void*)0), |
713 | (int)AIR_MIN(1024, range->max - range->min + 1)((1024) < (range->max - range->min + 1) ? (1024) : ( range->max - range->min + 1)), |
714 | nrrdTypeFloat); |
715 | if (E) { |
716 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, |
717 | "%s: trouble histograming to find DW threshold", me); |
718 | airMopError(mop); return 1; |
719 | } |
720 | if (_tenFindValley(&thresh, nhist, 0.75, AIR_FALSE0)) { |
721 | biffAddf(TENtenBiffKey, "%s: problem finding DW histogram valley", me); |
722 | airMopError(mop); return 1; |
723 | } |
724 | fprintf(stderr__stderrp, "%s: using %g for DW confidence threshold\n", me, thresh); |
725 | } |
726 | if (nrrdMaybeAlloc_va(nten, nrrdTypeFloat, 4, |
727 | AIR_CAST(size_t, 7)((size_t)(7)), sx, sy, sz)) { |
728 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't allocate output", me); |
729 | airMopError(mop); return 1; |
730 | } |
731 | if (nterrP) { |
732 | if (!(*nterrP)) { |
733 | *nterrP = nrrdNew(); |
734 | } |
735 | if (nrrdMaybeAlloc_va(*nterrP, nrrdTypeFloat, 3, |
736 | sx, sy, sz)) { |
737 | biffAddf(NRRDnrrdBiffKey, "%s: couldn't allocate error output", me); |
738 | airMopError(mop); return 1; |
739 | } |
740 | airMopAdd(mop, nterrP, (airMopper)airSetNull, airMopOnError); |
741 | airMopAdd(mop, *nterrP, (airMopper)nrrdNuke, airMopOnError); |
742 | terr = (float*)((*nterrP)->data); |
743 | } else { |
744 | terr = NULL((void*)0); |
745 | } |
746 | if (nB0P) { |
747 | if (!(*nB0P)) { |
748 | *nB0P = nrrdNew(); |
749 | } |
750 | if (nrrdMaybeAlloc_va(*nB0P, nrrdTypeFloat, 3, |
751 | sx, sy, sz)) { |
752 | biffAddf(NRRDnrrdBiffKey, "%s: couldn't allocate error output", me); |
753 | airMopError(mop); return 1; |
754 | } |
755 | airMopAdd(mop, nB0P, (airMopper)airSetNull, airMopOnError); |
756 | airMopAdd(mop, *nB0P, (airMopper)nrrdNuke, airMopOnError); |
757 | B0 = (float*)((*nB0P)->data); |
758 | } else { |
759 | B0 = NULL((void*)0); |
760 | } |
761 | bmat = (double*)(nbmat->data); |
762 | emat = (double*)(nemat->data); |
763 | ten = (float*)(nten->data); |
764 | lup = nrrdFLookup[ndwi->type]; |
765 | for (II=0; II<sx*sy*sz; II++) { |
766 | /* tenVerbose = (II == 42 + 190*(96 + 196*0)); */ |
767 | for (d=0; d<DD; d++) { |
768 | dwi1[d] = lup(ndwi->data, d + DD*II); |
769 | /* if (tenVerbose) { |
770 | fprintf(stderr, "%s: input dwi1[%d] = %g\n", me, d, dwi1[d]); |
771 | } */ |
772 | } |
773 | tenEstimateLinearSingle_f(ten, &_B0, dwi1, emat, |
774 | vbuf, DD, knownB0, |
775 | AIR_CAST(float, thresh)((float)(thresh)), |
776 | AIR_CAST(float, soft)((float)(soft)), |
777 | AIR_CAST(float, b)((float)(b))); |
778 | if (nB0P) { |
779 | *B0 = _B0; |
780 | } |
781 | /* if (tenVerbose) { |
782 | fprintf(stderr, "%s: output ten = (%g) %g,%g,%g %g,%g %g\n", me, |
783 | ten[0], ten[1], ten[2], ten[3], ten[4], ten[5], ten[6]); |
784 | } */ |
785 | if (nterrP) { |
786 | te = 0; |
787 | if (knownB0) { |
788 | tenSimulateSingle_f(dwi2, _B0, ten, bmat, DD, AIR_CAST(float, b)((float)(b))); |
789 | for (d=1; d<DD; d++) { |
790 | d1 = AIR_MAX(dwi1[d], 1)((dwi1[d]) > (1) ? (dwi1[d]) : (1)); |
791 | d2 = AIR_MAX(dwi2[d], 1)((dwi2[d]) > (1) ? (dwi2[d]) : (1)); |
792 | te += (d1 - d2)*(d1 - d2); |
793 | } |
794 | te /= (DD-1); |
795 | } else { |
796 | tenSimulateSingle_f(dwi2, _B0, ten, bmat, DD+1, AIR_CAST(float, b)((float)(b))); |
797 | for (d=0; d<DD; d++) { |
798 | d1 = AIR_MAX(dwi1[d], 1)((dwi1[d]) > (1) ? (dwi1[d]) : (1)); |
799 | /* tenSimulateSingle_f always puts the B0 in the beginning of |
800 | the dwi vector, but in this case we didn't have it in |
801 | the input dwi vector */ |
802 | d2 = AIR_MAX(dwi2[d+1], 1)((dwi2[d+1]) > (1) ? (dwi2[d+1]) : (1)); |
803 | te += (d1 - d2)*(d1 - d2); |
804 | } |
805 | te /= DD; |
806 | } |
807 | *terr = AIR_CAST(float, sqrt(te))((float)(sqrt(te))); |
808 | terr += 1; |
809 | } |
810 | ten += 7; |
811 | if (B0) { |
812 | B0 += 1; |
813 | } |
814 | } |
815 | /* not our job: tenEigenvalueClamp(nten, nten, 0, AIR_NAN); */ |
816 | |
817 | ELL_4V_SET(amap, -1, 1, 2, 3)((amap)[0] = (-1), (amap)[1] = (1), (amap)[2] = (2), (amap)[3 ] = (3)); |
818 | nrrdAxisInfoCopy(nten, ndwi, amap, NRRD_AXIS_INFO_NONE0); |
819 | nten->axis[0].kind = nrrdKind3DMaskedSymMatrix; |
820 | if (nrrdBasicInfoCopy(nten, ndwi, |
821 | NRRD_BASIC_INFO_ALL((1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<< 5)|(1<<6)|(1<<7)|(1<<8)|(1<<9)|(1<< 10) |(1<<11)|(1<<12)|(1<<13)|(1<<14)| (1<<15)) ^ NRRD_BASIC_INFO_SPACE((1<< 7) | (1<< 8) | (1<< 9) | (1<<10 ) | (1<<11)))) { |
822 | biffAddf(NRRDnrrdBiffKey, "%s:", me); |
823 | return 1; |
824 | } |
825 | |
826 | airMopOkay(mop); |
827 | return 0; |
828 | } |
829 | |
830 | /* |
831 | ******** tenSimulateSingle_f |
832 | ** |
833 | ** given a tensor, simulate the set of diffusion weighted measurements |
834 | ** represented by the given B matrix |
835 | ** |
836 | ** NOTE: the mindset of this function is very much "knownB0==true": |
837 | ** B0 is required as an argument (and its always copied to dwi[0]), |
838 | ** and the given bmat is assumed to have DD-1 rows (similar to how |
839 | ** tenEstimateLinearSingle_f() is set up), and dwi[1] through dwi[DD-1] |
840 | ** are set to the calculated DWIs. |
841 | ** |
842 | ** So: dwi must be allocated for DD values total |
843 | */ |
844 | void |
845 | tenSimulateSingle_f(float *dwi, |
846 | float B0, const float *ten, const double *bmat, |
847 | unsigned int DD, float b) { |
848 | double vv; |
849 | /* this is how we multiply the off-diagonal entries by 2 */ |
850 | double matwght[6] = {1, 2, 2, 1, 2, 1}; |
851 | unsigned int ii, jj; |
852 | |
853 | dwi[0] = B0; |
854 | /* if (tenVerbose) { |
855 | fprintf(stderr, "ten = %g,%g,%g %g,%g %g\n", |
856 | ten[1], ten[2], ten[3], ten[4], ten[5], ten[6]); |
857 | } */ |
858 | for (ii=0; ii<DD-1; ii++) { |
859 | vv = 0; |
860 | for (jj=0; jj<6; jj++) { |
861 | vv += matwght[jj]*bmat[jj + 6*ii]*ten[jj+1]; |
862 | } |
863 | dwi[ii+1] = AIR_CAST(float, AIR_MAX(B0, 1)*exp(-b*vv))((float)(((B0) > (1) ? (B0) : (1))*exp(-b*vv))); |
864 | /* if (tenVerbose) { |
865 | fprintf(stderr, "v[%d] = %g --> dwi = %g\n", ii, vv, dwi[ii+1]); |
866 | } */ |
867 | } |
868 | |
869 | return; |
870 | } |
871 | |
872 | int |
873 | tenSimulate(Nrrd *ndwi, const Nrrd *nT2, const Nrrd *nten, |
874 | const Nrrd *_nbmat, double b) { |
875 | static const char me[]="tenSimulate"; |
876 | size_t II; |
877 | Nrrd *nbmat; |
878 | size_t DD, sx, sy, sz; |
879 | airArray *mop; |
880 | double *bmat; |
881 | float *dwi, *ten, (*lup)(const void *, size_t I); |
882 | char stmp[6][AIR_STRLEN_SMALL(128+1)]; |
883 | |
884 | if (!ndwi || !nT2 || !nten || !_nbmat |
885 | || tenTensorCheck(nten, nrrdTypeFloat, AIR_TRUE1, AIR_TRUE1) |
886 | || tenBMatrixCheck(_nbmat, nrrdTypeDefault, 6)) { |
887 | biffAddf(TENtenBiffKey, "%s: got NULL pointer or invalid args", me); |
888 | return 1; |
889 | } |
890 | mop = airMopNew(); |
891 | airMopAdd(mop, nbmat=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); |
892 | if (nrrdConvert(nbmat, _nbmat, nrrdTypeDouble)) { |
893 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't convert B matrix", me); |
894 | return 1; |
895 | } |
896 | |
897 | DD = nbmat->axis[1].size+1; |
898 | sx = nT2->axis[0].size; |
899 | sy = nT2->axis[1].size; |
900 | sz = nT2->axis[2].size; |
901 | if (!(3 == nT2->dim |
902 | && sx == nten->axis[1].size |
903 | && sy == nten->axis[2].size |
904 | && sz == nten->axis[3].size)) { |
905 | biffAddf(TENtenBiffKey, "%s: dimensions of %u-D T2 volume (%s,%s,%s) " |
906 | "don't match tensor volume (%s,%s,%s)", me, nT2->dim, |
907 | airSprintSize_t(stmp[0], sx), |
908 | airSprintSize_t(stmp[1], sy), |
909 | airSprintSize_t(stmp[2], sz), |
910 | airSprintSize_t(stmp[3], nten->axis[1].size), |
911 | airSprintSize_t(stmp[4], nten->axis[2].size), |
912 | airSprintSize_t(stmp[5], nten->axis[3].size)); |
913 | return 1; |
914 | } |
915 | if (nrrdMaybeAlloc_va(ndwi, nrrdTypeFloat, 4, |
916 | AIR_CAST(size_t, DD)((size_t)(DD)), sx, sy, sz)) { |
917 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't allocate output", me); |
918 | return 1; |
919 | } |
920 | dwi = (float*)(ndwi->data); |
921 | ten = (float*)(nten->data); |
922 | bmat = (double*)(nbmat->data); |
923 | lup = nrrdFLookup[nT2->type]; |
924 | for (II=0; II<(size_t)(sx*sy*sz); II++) { |
925 | /* tenVerbose = (II == 42 + 190*(96 + 196*0)); */ |
926 | tenSimulateSingle_f(dwi, lup(nT2->data, II), ten, bmat, DD, |
927 | AIR_CAST(float, b)((float)(b))); |
928 | dwi += DD; |
929 | ten += 7; |
930 | } |
931 | |
932 | airMopOkay(mop); |
933 | return 0; |
934 | } |
935 | |
936 | |
937 | |
938 | |
939 | |
940 | |
941 | |
942 | |
943 | |
944 | |
945 | |
946 | |
947 | |
948 | |
949 | |
950 | |
951 | |
952 | |
953 | /* old stuff, prior to tenEstimationMatrix .. */ |
954 | |
955 | |
956 | /* |
957 | ******** tenCalcOneTensor1 |
958 | ** |
959 | ** make one diffusion tensor from the measurements at one voxel, based |
960 | ** on the gradient directions used by Andy Alexander |
961 | */ |
962 | void |
963 | tenCalcOneTensor1(float tens[7], float chan[7], |
964 | float thresh, float slope, float b) { |
965 | double c[7], sum, d1, d2, d3, d4, d5, d6; |
966 | |
967 | c[0] = AIR_MAX(chan[0], 1)((chan[0]) > (1) ? (chan[0]) : (1)); |
968 | c[1] = AIR_MAX(chan[1], 1)((chan[1]) > (1) ? (chan[1]) : (1)); |
969 | c[2] = AIR_MAX(chan[2], 1)((chan[2]) > (1) ? (chan[2]) : (1)); |
970 | c[3] = AIR_MAX(chan[3], 1)((chan[3]) > (1) ? (chan[3]) : (1)); |
971 | c[4] = AIR_MAX(chan[4], 1)((chan[4]) > (1) ? (chan[4]) : (1)); |
972 | c[5] = AIR_MAX(chan[5], 1)((chan[5]) > (1) ? (chan[5]) : (1)); |
973 | c[6] = AIR_MAX(chan[6], 1)((chan[6]) > (1) ? (chan[6]) : (1)); |
974 | sum = c[1] + c[2] + c[3] + c[4] + c[5] + c[6]; |
975 | tens[0] = AIR_CAST(float, (1 + airErf(slope*(sum - thresh)))/2.0)((float)((1 + airErf(slope*(sum - thresh)))/2.0)); |
976 | d1 = (log(c[0]) - log(c[1]))/b; |
977 | d2 = (log(c[0]) - log(c[2]))/b; |
978 | d3 = (log(c[0]) - log(c[3]))/b; |
979 | d4 = (log(c[0]) - log(c[4]))/b; |
980 | d5 = (log(c[0]) - log(c[5]))/b; |
981 | d6 = (log(c[0]) - log(c[6]))/b; |
982 | tens[1] = AIR_CAST(float, d1 + d2 - d3 - d4 + d5 + d6)((float)(d1 + d2 - d3 - d4 + d5 + d6)); /* Dxx */ |
983 | tens[2] = AIR_CAST(float, d5 - d6)((float)(d5 - d6)); /* Dxy */ |
984 | tens[3] = AIR_CAST(float, d1 - d2)((float)(d1 - d2)); /* Dxz */ |
985 | tens[4] = AIR_CAST(float, -d1 - d2 + d3 + d4 + d5 + d6)((float)(-d1 - d2 + d3 + d4 + d5 + d6)); /* Dyy */ |
986 | tens[5] = AIR_CAST(float, d3 - d4)((float)(d3 - d4)); /* Dyz */ |
987 | tens[6] = AIR_CAST(float, d1 + d2 + d3 + d4 - d5 - d6)((float)(d1 + d2 + d3 + d4 - d5 - d6)); /* Dzz */ |
988 | return; |
989 | } |
990 | |
991 | /* |
992 | ******** tenCalcOneTensor2 |
993 | ** |
994 | ** using gradient directions used by EK |
995 | */ |
996 | void |
997 | tenCalcOneTensor2(float tens[7], float chan[7], |
998 | float thresh, float slope, float b) { |
999 | double c[7], sum, d1, d2, d3, d4, d5, d6; |
1000 | |
1001 | c[0] = AIR_MAX(chan[0], 1)((chan[0]) > (1) ? (chan[0]) : (1)); |
1002 | c[1] = AIR_MAX(chan[1], 1)((chan[1]) > (1) ? (chan[1]) : (1)); |
1003 | c[2] = AIR_MAX(chan[2], 1)((chan[2]) > (1) ? (chan[2]) : (1)); |
1004 | c[3] = AIR_MAX(chan[3], 1)((chan[3]) > (1) ? (chan[3]) : (1)); |
1005 | c[4] = AIR_MAX(chan[4], 1)((chan[4]) > (1) ? (chan[4]) : (1)); |
1006 | c[5] = AIR_MAX(chan[5], 1)((chan[5]) > (1) ? (chan[5]) : (1)); |
1007 | c[6] = AIR_MAX(chan[6], 1)((chan[6]) > (1) ? (chan[6]) : (1)); |
1008 | sum = c[1] + c[2] + c[3] + c[4] + c[5] + c[6]; |
1009 | tens[0] = AIR_CAST(float, (1 + airErf(slope*(sum - thresh)))/2.0)((float)((1 + airErf(slope*(sum - thresh)))/2.0)); |
1010 | d1 = (log(c[0]) - log(c[1]))/b; |
1011 | d2 = (log(c[0]) - log(c[2]))/b; |
1012 | d3 = (log(c[0]) - log(c[3]))/b; |
1013 | d4 = (log(c[0]) - log(c[4]))/b; |
1014 | d5 = (log(c[0]) - log(c[5]))/b; |
1015 | d6 = (log(c[0]) - log(c[6]))/b; |
1016 | tens[1] = AIR_CAST(float, d1)((float)(d1)); /* Dxx */ |
1017 | tens[2] = AIR_CAST(float, d6 - (d1 + d2)/2)((float)(d6 - (d1 + d2)/2)); /* Dxy */ |
1018 | tens[3] = AIR_CAST(float, d5 - (d1 + d3)/2)((float)(d5 - (d1 + d3)/2)); /* Dxz */ |
1019 | tens[4] = AIR_CAST(float, d2)((float)(d2)); /* Dyy */ |
1020 | tens[5] = AIR_CAST(float, d4 - (d2 + d3)/2)((float)(d4 - (d2 + d3)/2)); /* Dyz */ |
1021 | tens[6] = AIR_CAST(float, d3)((float)(d3)); /* Dzz */ |
1022 | return; |
1023 | } |
1024 | |
1025 | /* |
1026 | ******** tenCalcTensor |
1027 | ** |
1028 | ** Calculate a volume of tensors from measured data |
1029 | */ |
1030 | int |
1031 | tenCalcTensor(Nrrd *nout, Nrrd *nin, int version, |
1032 | float thresh, float slope, float b) { |
1033 | static const char me[] = "tenCalcTensor"; |
1034 | char cmt[128]; |
1035 | float *out, tens[7], chan[7]; |
1036 | size_t I, sx, sy, sz; |
1037 | void (*calcten)(float tens[7], float chan[7], |
1038 | float thresh, float slope, float b); |
1039 | |
1040 | if (!(nout && nin)) { |
1041 | biffAddf(TENtenBiffKey, "%s: got NULL pointer", me); |
1042 | return 1; |
1043 | } |
1044 | if (!( 1 == version || 2 == version )) { |
1045 | biffAddf(TENtenBiffKey, "%s: version should be 1 or 2, not %d", me, version); |
1046 | return 1; |
1047 | } |
1048 | switch (version) { |
1049 | case 1: |
1050 | calcten = tenCalcOneTensor1; |
1051 | break; |
1052 | case 2: |
1053 | calcten = tenCalcOneTensor2; |
1054 | break; |
1055 | default: |
1056 | biffAddf(TENtenBiffKey, "%s: PANIC, version = %d not handled", me, version); |
1057 | return 1; |
1058 | break; |
1059 | } |
1060 | if (tenTensorCheck(nin, nrrdTypeDefault, AIR_TRUE1, AIR_TRUE1)) { |
1061 | biffAddf(TENtenBiffKey, "%s: wasn't given valid tensor nrrd", me); |
1062 | return 1; |
1063 | } |
1064 | sx = nin->axis[1].size; |
1065 | sy = nin->axis[2].size; |
1066 | sz = nin->axis[3].size; |
1067 | if (nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 4, |
1068 | AIR_CAST(size_t, 7)((size_t)(7)), sx, sy, sz)) { |
1069 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't alloc output", me); |
1070 | return 1; |
1071 | } |
1072 | nout->axis[0].label = airStrdup("c,Dxx,Dxy,Dxz,Dyy,Dyz,Dzz"); |
1073 | nout->axis[1].label = airStrdup("x"); |
1074 | nout->axis[2].label = airStrdup("y"); |
1075 | nout->axis[3].label = airStrdup("z"); |
1076 | nout->axis[0].spacing = AIR_NAN(airFloatQNaN.f); |
1077 | if (AIR_EXISTS(nin->axis[1].spacing)(((int)(!((nin->axis[1].spacing) - (nin->axis[1].spacing ))))) && |
1078 | AIR_EXISTS(nin->axis[2].spacing)(((int)(!((nin->axis[2].spacing) - (nin->axis[2].spacing ))))) && |
1079 | AIR_EXISTS(nin->axis[3].spacing)(((int)(!((nin->axis[3].spacing) - (nin->axis[3].spacing )))))) { |
1080 | nout->axis[1].spacing = nin->axis[1].spacing; |
1081 | nout->axis[2].spacing = nin->axis[2].spacing; |
1082 | nout->axis[3].spacing = nin->axis[3].spacing; |
1083 | } |
1084 | else { |
1085 | nout->axis[1].spacing = 1.0; |
1086 | nout->axis[2].spacing = 1.0; |
1087 | nout->axis[3].spacing = 1.0; |
1088 | } |
1089 | sprintf(cmt, "%s: using thresh = %g, slope = %g, b = %g\n",__builtin___sprintf_chk (cmt, 0, __builtin_object_size (cmt, 2 > 1 ? 1 : 0), "%s: using thresh = %g, slope = %g, b = %g\n" , me, thresh, slope, b) |
1090 | me, thresh, slope, b)__builtin___sprintf_chk (cmt, 0, __builtin_object_size (cmt, 2 > 1 ? 1 : 0), "%s: using thresh = %g, slope = %g, b = %g\n" , me, thresh, slope, b); |
1091 | nrrdCommentAdd(nout, cmt); |
1092 | out = (float *)nout->data; |
1093 | for (I=0; I<(size_t)(sx*sy*sz); I++) { |
1094 | if (tenVerbose && !(I % (sx*sy))) { |
1095 | fprintf(stderr__stderrp, "%s: z = %d of %d\n", me, (int)(I/(sx*sy)), (int)sz-1); |
1096 | } |
1097 | chan[0] = nrrdFLookup[nin->type](nin->data, 0 + 7*I); |
1098 | chan[1] = nrrdFLookup[nin->type](nin->data, 1 + 7*I); |
1099 | chan[2] = nrrdFLookup[nin->type](nin->data, 2 + 7*I); |
1100 | chan[3] = nrrdFLookup[nin->type](nin->data, 3 + 7*I); |
1101 | chan[4] = nrrdFLookup[nin->type](nin->data, 4 + 7*I); |
1102 | chan[5] = nrrdFLookup[nin->type](nin->data, 5 + 7*I); |
1103 | chan[6] = nrrdFLookup[nin->type](nin->data, 6 + 7*I); |
1104 | calcten(tens, chan, thresh, slope, b); |
1105 | out[0 + 7*I] = tens[0]; |
1106 | out[1 + 7*I] = tens[1]; |
1107 | out[2 + 7*I] = tens[2]; |
1108 | out[3 + 7*I] = tens[3]; |
1109 | out[4 + 7*I] = tens[4]; |
1110 | out[5 + 7*I] = tens[5]; |
1111 | out[6 + 7*I] = tens[6]; |
1112 | } |
1113 | return 0; |
1114 | } |
1115 |