File: | src/ten/tenModel.c |
Location: | line 535, column 5 |
Description: | Potential leak of memory pointed to by 'dwibuff' |
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 | tenModelPrefixStr = "DWMRI_model:"; | |||
29 | ||||
30 | static const tenModel * | |||
31 | str2model(const char *str) { | |||
32 | const tenModel *ret; | |||
33 | ||||
34 | if (!strcmp(str, TEN_MODEL_STR_ZERO"zero")) { | |||
35 | ret = tenModelZero; | |||
36 | } else if (!strcmp(str, TEN_MODEL_STR_B0"b0")) { | |||
37 | ret = tenModelB0; | |||
38 | } else if (!strcmp(str, TEN_MODEL_STR_BALL"ball")) { | |||
39 | ret = tenModelBall; | |||
40 | } else if (!strcmp(str, TEN_MODEL_STR_1STICK"1stick")) { | |||
41 | ret = tenModel1Stick; | |||
42 | } else if (!strcmp(str, TEN_MODEL_STR_1VECTOR2D"1vector2d")) { | |||
43 | ret = tenModel1Vector2D; | |||
44 | } else if (!strcmp(str, TEN_MODEL_STR_1UNIT2D"1unit2d")) { | |||
45 | ret = tenModel1Unit2D; | |||
46 | } else if (!strcmp(str, TEN_MODEL_STR_2UNIT2D"2unit2d")) { | |||
47 | ret = tenModel2Unit2D; | |||
48 | } else if (!strcmp(str, TEN_MODEL_STR_BALL1STICKEMD"ball1stickemd")) { | |||
49 | ret = tenModelBall1StickEMD; | |||
50 | } else if (!strcmp(str, TEN_MODEL_STR_BALL1STICK"ball1stick")) { | |||
51 | ret = tenModelBall1Stick; | |||
52 | } else if (!strcmp(str, TEN_MODEL_STR_BALL1CYLINDER"ball1cylinder")) { | |||
53 | ret = tenModelBall1Cylinder; | |||
54 | } else if (!strcmp(str, TEN_MODEL_STR_1CYLINDER"1cylinder")) { | |||
55 | ret = tenModel1Cylinder; | |||
56 | } else if (!strcmp(str, TEN_MODEL_STR_1TENSOR2"1tensor2")) { | |||
57 | ret = tenModel1Tensor2; | |||
58 | } else { | |||
59 | /* we don't currently have a tenModelUnknown */ | |||
60 | ret = NULL((void*)0); | |||
61 | } | |||
62 | return ret; | |||
63 | } | |||
64 | ||||
65 | int | |||
66 | tenModelParse(const tenModel **model, int *plusB0, | |||
67 | int requirePrefix, const char *_str) { | |||
68 | static const char me[]="tenModelParse"; | |||
69 | char *str, *modstr, *pre; | |||
70 | airArray *mop; | |||
71 | ||||
72 | if (!( model && plusB0 && _str)) { | |||
73 | biffAddf(TENtenBiffKey, "%s: got NULL pointer", me); | |||
74 | return 1; | |||
75 | } | |||
76 | str = airStrdup(_str); | |||
77 | if (!str) { | |||
78 | biffAddf(TENtenBiffKey, "%s: couldn't strdup", me); | |||
79 | return 1; | |||
80 | } | |||
81 | mop = airMopNew(); | |||
82 | airMopAdd(mop, str, airFree, airMopAlways); | |||
83 | pre = strstr(str, tenModelPrefixStr); | |||
84 | if (pre) { | |||
85 | str += strlen(tenModelPrefixStr); | |||
86 | } else { | |||
87 | if (requirePrefix) { | |||
88 | biffAddf(TENtenBiffKey, "%s: didn't see prefix \"%s\" in \"%s\"", me, | |||
89 | tenModelPrefixStr, _str); | |||
90 | airMopError(mop); return 1; | |||
91 | } | |||
92 | } | |||
93 | airToLower(str); /* for sake of "b0" and str2model below */ | |||
94 | ||||
95 | if ((modstr = strchr(str, '+'))) { | |||
96 | *modstr = '\0'; | |||
97 | ++modstr; | |||
98 | if (!strcmp(str, "b0")) { | |||
99 | *plusB0 = AIR_TRUE1; | |||
100 | } else { | |||
101 | biffAddf(TENtenBiffKey, "%s: string (\"%s\") prior to \"+\" not \"b0\"", me, str); | |||
102 | airMopError(mop); return 1; | |||
103 | } | |||
104 | } else { | |||
105 | *plusB0 = AIR_FALSE0; | |||
106 | modstr = str; | |||
107 | } | |||
108 | if (!(*model = str2model(modstr))) { | |||
109 | biffAddf(TENtenBiffKey, "%s: didn't recognize \"%s\" as model", me, modstr); | |||
110 | airMopError(mop); return 1; | |||
111 | } | |||
112 | airMopOkay(mop); | |||
113 | return 0; | |||
114 | } | |||
115 | ||||
116 | int | |||
117 | tenModelFromAxisLearnPossible(const NrrdAxisInfo *axinfo) { | |||
118 | ||||
119 | /* HEY keep in synch with nrrdKind* code below */ | |||
120 | return (nrrdKind3DSymMatrix == axinfo->kind | |||
121 | || nrrdKind3DMaskedSymMatrix == axinfo->kind | |||
122 | || airStrlen(axinfo->label)); | |||
123 | } | |||
124 | ||||
125 | int | |||
126 | tenModelFromAxisLearn(const tenModel **modelP, | |||
127 | int *plusB0, | |||
128 | const NrrdAxisInfo *axinfo) { | |||
129 | static const char me[]="tenModelFromAxisLearn"; | |||
130 | ||||
131 | if (!(modelP && plusB0 && axinfo)) { | |||
132 | biffAddf(TENtenBiffKey, "%s: got NULL pointer", me); | |||
133 | return 1; | |||
134 | } | |||
135 | *plusB0 = AIR_FALSE0; | |||
136 | /* first try to learn model from axis "kind" */ | |||
137 | /* HEY should probably also support 3 vector for stick? */ | |||
138 | /* HEY keep in synch with nrrdKind* code above */ | |||
139 | if (nrrdKind3DSymMatrix == axinfo->kind | |||
140 | || nrrdKind3DMaskedSymMatrix == axinfo->kind) { | |||
141 | *modelP = tenModel1Tensor2; | |||
142 | } else if (airStrlen(axinfo->label)) { | |||
143 | /* try to parse from label */ | |||
144 | if (tenModelParse(modelP, plusB0, AIR_TRUE1, axinfo->label)) { | |||
145 | biffAddf(TENtenBiffKey, "%s: couldn't parse label \"%s\"", me, axinfo->label); | |||
146 | *modelP = NULL((void*)0); | |||
147 | return 1; | |||
148 | } | |||
149 | } else { | |||
150 | biffAddf(TENtenBiffKey, "%s: don't have kind or label info to learn model", me); | |||
151 | *modelP = NULL((void*)0); | |||
152 | return 1; | |||
153 | } | |||
154 | ||||
155 | return 0; | |||
156 | } | |||
157 | ||||
158 | /* | |||
159 | ** If nB0 is given, then those B0 image values will be used. | |||
160 | ** In this case, either the parm vector can be short by one (seems to be | |||
161 | ** missing B0), or the parm vector includes B0, but these will be ignored | |||
162 | ** and over-written with the B0 values from nB0. | |||
163 | ** | |||
164 | ** basic and axis info is derived from _nparm | |||
165 | */ | |||
166 | int | |||
167 | tenModelSimulate(Nrrd *ndwi, int typeOut, | |||
168 | tenExperSpec *espec, | |||
169 | const tenModel *model, | |||
170 | const Nrrd *_nB0, | |||
171 | const Nrrd *_nparm, | |||
172 | int keyValueSet) { | |||
173 | static const char me[]="tenModelSimulate"; | |||
174 | double *ddwi, *parm, (*ins)(void *v, size_t I, double d); | |||
175 | char *dwi; | |||
176 | size_t szOut[NRRD_DIM_MAX16], II, numSamp; | |||
177 | const Nrrd *nB0, /* B0 as doubles */ | |||
178 | *ndparm, /* parm as doubles */ | |||
179 | *ndpparm, /* parm as doubles, padded */ | |||
180 | *nparm; /* final parm as doubles, padded, w/ correct B0 values */ | |||
181 | Nrrd *ntmp; /* non-const pointer for working */ | |||
182 | airArray *mop; | |||
183 | unsigned int gpsze, /* given parm size */ | |||
184 | ii; | |||
185 | int useB0Img, needPad, axmap[NRRD_DIM_MAX16]; | |||
186 | ||||
187 | if (!(ndwi && espec && model /* _nB0 can be NULL */ && _nparm)) { | |||
188 | biffAddf(TENtenBiffKey, "%s: got NULL pointer", me); | |||
189 | return 1; | |||
190 | } | |||
191 | if (!espec->imgNum) { | |||
192 | biffAddf(TENtenBiffKey, "%s: given espec wants 0 images, unset?", me); | |||
193 | return 1; | |||
194 | } | |||
195 | ||||
196 | gpsze = _nparm->axis[0].size; | |||
197 | if (model->parmNum - 1 == gpsze) { | |||
198 | /* got one less than needed parm #, see if we got B0 */ | |||
199 | if (!_nB0) { | |||
200 | biffAddf(TENtenBiffKey, "%s: got %u parms, need %u (for %s), " | |||
201 | "but didn't get B0 vol", | |||
202 | me, gpsze, model->parmNum, model->name); | |||
203 | return 1; | |||
204 | } | |||
205 | useB0Img = AIR_TRUE1; | |||
206 | needPad = AIR_TRUE1; | |||
207 | } else if (model->parmNum != gpsze) { | |||
208 | biffAddf(TENtenBiffKey, "%s: mismatch between getting %u parms, " | |||
209 | "needing %u (for %s)\n", | |||
210 | me, gpsze, model->parmNum, model->name); | |||
211 | return 1; | |||
212 | } else { | |||
213 | /* model->parmNum == gpsze */ | |||
214 | needPad = AIR_FALSE0; | |||
215 | useB0Img = !!_nB0; | |||
216 | } | |||
217 | ||||
218 | mop = airMopNew(); | |||
219 | /* get parms as doubles */ | |||
220 | if (nrrdTypeDouble == _nparm->type) { | |||
221 | ndparm = _nparm; | |||
222 | } else { | |||
223 | ntmp = nrrdNew(); | |||
224 | airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); | |||
225 | if (nrrdConvert(ntmp, _nparm, nrrdTypeDouble)) { | |||
226 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't convert parm to %s", me, | |||
227 | airEnumStr(nrrdType, nrrdTypeDouble)); | |||
228 | airMopError(mop); return 1; | |||
229 | } | |||
230 | ndparm = ntmp; | |||
231 | } | |||
232 | /* get parms the right length */ | |||
233 | if (!needPad) { | |||
234 | ndpparm = ndparm; | |||
235 | } else { | |||
236 | ptrdiff_t min[NRRD_DIM_MAX16], max[NRRD_DIM_MAX16]; | |||
237 | unsigned int ax; | |||
238 | ||||
239 | ntmp = nrrdNew(); | |||
240 | airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); | |||
241 | for (ax=0; ax<ndparm->dim; ax++) { | |||
242 | min[ax] = (!ax ? -1 : 0); | |||
243 | max[ax] = ndparm->axis[ax].size-1; | |||
244 | } | |||
245 | if (nrrdPad_nva(ntmp, ndparm, min, max, nrrdBoundaryBleed, 0.0)) { | |||
246 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't pad", me); | |||
247 | airMopError(mop); return 1; | |||
248 | } | |||
249 | ndpparm = ntmp; | |||
250 | } | |||
251 | /* put in B0 values if needed */ | |||
252 | if (!useB0Img) { | |||
253 | nparm = ndpparm; | |||
254 | } else { | |||
255 | if (nrrdTypeDouble == _nB0->type) { | |||
256 | nB0 = _nB0; | |||
257 | } else { | |||
258 | ntmp = nrrdNew(); | |||
259 | airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); | |||
260 | if (nrrdConvert(ntmp, _nB0, nrrdTypeDouble)) { | |||
261 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't convert B0 to %s", me, | |||
262 | airEnumStr(nrrdType, nrrdTypeDouble)); | |||
263 | airMopError(mop); return 1; | |||
264 | } | |||
265 | nB0 = ntmp; | |||
266 | } | |||
267 | /* HEY: this is mostly likely a waste of memory, | |||
268 | but its all complicated by const-correctness */ | |||
269 | ntmp = nrrdNew(); | |||
270 | airMopAdd(mop, ntmp, (airMopper)nrrdNuke, airMopAlways); | |||
271 | if (nrrdSplice(ntmp, ndpparm, nB0, 0, 0)) { | |||
272 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't splice in B0", me); | |||
273 | airMopError(mop); return 1; | |||
274 | } | |||
275 | nparm = ntmp; | |||
276 | } | |||
277 | ||||
278 | /* allocate output (and set axmap) */ | |||
279 | for (ii=0; ii<nparm->dim; ii++) { | |||
280 | szOut[ii] = (!ii | |||
281 | ? espec->imgNum | |||
282 | : nparm->axis[ii].size); | |||
283 | axmap[ii] = (!ii | |||
284 | ? -1 | |||
285 | : AIR_CAST(int, ii)((int)(ii))); | |||
286 | } | |||
287 | if (nrrdMaybeAlloc_nva(ndwi, typeOut, nparm->dim, szOut)) { | |||
288 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't allocate output", me); | |||
289 | airMopError(mop); return 1; | |||
290 | } | |||
291 | if (!( ddwi = AIR_CALLOC(espec->imgNum, double)(double*)(calloc((espec->imgNum), sizeof(double))))) { | |||
292 | biffAddf(TENtenBiffKey, "%s: couldn't allocate dwi buffer", me); | |||
293 | airMopError(mop); return 1; | |||
294 | } | |||
295 | airMopAdd(mop, ddwi, airFree, airMopAlways); | |||
296 | numSamp = nrrdElementNumber(nparm)/nparm->axis[0].size; | |||
297 | ||||
298 | /* set output */ | |||
299 | ins = nrrdDInsert[typeOut]; | |||
300 | parm = AIR_CAST(double *, nparm->data)((double *)(nparm->data)); | |||
301 | dwi = AIR_CAST(char *, ndwi->data)((char *)(ndwi->data)); | |||
302 | for (II=0; II<numSamp; II++) { | |||
303 | model->simulate(ddwi, parm, espec); | |||
304 | for (ii=0; ii<espec->imgNum; ii++) { | |||
305 | ins(dwi, ii, ddwi[ii]); | |||
306 | } | |||
307 | parm += model->parmNum; | |||
308 | dwi += espec->imgNum*nrrdTypeSize[typeOut]; | |||
309 | } | |||
310 | ||||
311 | if (keyValueSet) { | |||
312 | if (tenDWMRIKeyValueFromExperSpecSet(ndwi, espec)) { | |||
313 | biffAddf(TENtenBiffKey, "%s: trouble", me); | |||
314 | airMopError(mop); return 1; | |||
315 | } | |||
316 | } | |||
317 | ||||
318 | if (nrrdAxisInfoCopy(ndwi, _nparm, axmap, NRRD_AXIS_INFO_SIZE_BIT(1<< 1)) | |||
319 | || nrrdBasicInfoCopy(ndwi, _nparm, | |||
320 | NRRD_BASIC_INFO_DATA_BIT(1<< 1) | |||
321 | | NRRD_BASIC_INFO_TYPE_BIT(1<< 2) | |||
322 | | NRRD_BASIC_INFO_BLOCKSIZE_BIT(1<< 3) | |||
323 | | NRRD_BASIC_INFO_DIMENSION_BIT(1<< 4) | |||
324 | | NRRD_BASIC_INFO_CONTENT_BIT(1<< 5) | |||
325 | | NRRD_BASIC_INFO_COMMENTS_BIT(1<<14) | |||
326 | | (nrrdStateKeyValuePairsPropagate | |||
327 | ? 0 | |||
328 | : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT(1<<15)))) { | |||
329 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't copy axis or basic info", me); | |||
330 | airMopError(mop); return 1; | |||
331 | } | |||
332 | ndwi->axis[0].kind = nrrdKindList; | |||
333 | ||||
334 | airMopOkay(mop); | |||
335 | return 0; | |||
336 | } | |||
337 | ||||
338 | /* | |||
339 | ** _tenModelSqeFitSingle | |||
340 | ** | |||
341 | ** callable function (as opposed to tenModel method) for doing | |||
342 | ** sqe fitting. Returns the sqe at the converged fit location | |||
343 | ** Requires PARM_NUM length buffers testParm and grad | |||
344 | */ | |||
345 | double | |||
346 | _tenModelSqeFitSingle(const tenModel *model, | |||
347 | double *testParm, double *grad, | |||
348 | double *parm, double *convFrac, unsigned int *itersTaken, | |||
349 | const tenExperSpec *espec, | |||
350 | double *dwiBuff, const double *dwiMeas, | |||
351 | const double *parmInit, int knownB0, | |||
352 | unsigned int minIter, unsigned int maxIter, | |||
353 | double convEps, int verbose) { | |||
354 | static const char me[]="_tenModelSqeFitSingle"; | |||
355 | unsigned int iter, subIter; | |||
356 | double step, bak, opp, val, testval, dist, td; | |||
357 | int done; | |||
358 | char pstr[AIR_STRLEN_MED(256+1)]; | |||
359 | ||||
360 | step = 1; | |||
361 | model->copy(parm, parmInit); | |||
362 | val = model->sqe(parm, espec, dwiBuff, dwiMeas, knownB0); | |||
363 | model->sqeGrad(grad, parm, espec, dwiBuff, dwiMeas, knownB0); | |||
364 | if (verbose > 1) { | |||
365 | model->sprint(pstr, parm); | |||
366 | fprintf(stderr__stderrp, "\n"); | |||
367 | fprintf(stderr__stderrp, "%s(%s): minIter = %u, maxIter = %u\n", me, model->name, | |||
368 | minIter, maxIter); | |||
369 | fprintf(stderr__stderrp, "%s(%s): starting at %s -> %g (step %g)\n", me, | |||
370 | model->name, pstr, val, step); | |||
371 | } | |||
372 | ||||
373 | opp = 1.2; /* opportunistic step size increase */ | |||
374 | bak = 0.5; /* scaling back because of bad step */ | |||
375 | iter = 0; | |||
376 | dist = convEps*8; | |||
377 | do { | |||
378 | subIter = 0; | |||
379 | do { | |||
380 | model->step(testParm, -step, grad, parm); | |||
381 | testval = model->sqe(testParm, espec, dwiBuff, dwiMeas, knownB0); | |||
382 | if (verbose > 1) { | |||
383 | model->sprint(pstr, testParm); | |||
384 | fprintf(stderr__stderrp, "%s(%s): (iter %u/%u) tried %s -> %g (step %g)\n", | |||
385 | me, model->name, iter, subIter, pstr, testval, step); | |||
386 | } | |||
387 | if (testval > val) { | |||
388 | step *= bak; | |||
389 | } | |||
390 | subIter++; | |||
391 | } while (testval > val && subIter <= maxIter); | |||
392 | if (subIter > maxIter) { | |||
393 | /* something went wrong with merely trying to find a downhill step; | |||
394 | this has occurred previously when (because of a bug) the | |||
395 | per-parameter bounds put the test location inside the bounding | |||
396 | box while the initial location was outside => could never converge. | |||
397 | Not using biff, so this is one way of trying to signal the problem */ | |||
398 | model->copy(parm, parmInit); | |||
399 | *convFrac = AIR_POS_INF(airFloatPosInf.f); | |||
400 | *itersTaken = maxIter; | |||
401 | return AIR_POS_INF(airFloatPosInf.f); | |||
402 | } | |||
403 | td = model->dist(testParm, parm); | |||
404 | dist = (td + dist)/2; | |||
405 | val = testval; | |||
406 | model->copy(parm, testParm); | |||
407 | model->sqeGrad(grad, parm, espec, dwiBuff, dwiMeas, knownB0); | |||
408 | step *= opp; | |||
409 | iter++; | |||
410 | done = (iter < minIter | |||
411 | ? AIR_FALSE0 | |||
412 | : (iter > maxIter) || dist < convEps); | |||
413 | } while (!done); | |||
414 | *convFrac = dist/convEps; | |||
415 | *itersTaken = iter; | |||
416 | return val; | |||
417 | } | |||
418 | ||||
419 | int | |||
420 | tenModelSqeFit(Nrrd *nparm, | |||
421 | Nrrd **nsqeP, Nrrd **nconvP, Nrrd **niterP, | |||
422 | const tenModel *model, | |||
423 | const tenExperSpec *espec, const Nrrd *ndwi, | |||
424 | int knownB0, int saveB0, int typeOut, | |||
425 | unsigned int minIter, unsigned int maxIter, | |||
426 | unsigned int starts, double convEps, | |||
427 | airRandMTState *_rng, int verbose) { | |||
428 | static const char me[]="tenModelSqeFit"; | |||
429 | char doneStr[13]; | |||
430 | double *ddwi, *dwibuff, sqe, sqeBest, | |||
431 | *dparm, *dparmBest, | |||
432 | (*ins)(void *v, size_t I, double d), | |||
433 | (*lup)(const void *v, size_t I); | |||
434 | airArray *mop; | |||
435 | unsigned int saveParmNum, dwiNum, ii, lablen, itersTaken; | |||
436 | size_t szOut[NRRD_DIM_MAX16], II, numSamp; | |||
437 | int axmap[NRRD_DIM_MAX16], erraxmap[NRRD_DIM_MAX16], fitVerbose; | |||
438 | const char *dwi; | |||
439 | char *parm; | |||
440 | airRandMTState *rng; | |||
441 | Nrrd *nsqe, *nconv, *niter; | |||
442 | ||||
443 | /* nsqeP, nconvP, niterP can be NULL */ | |||
444 | if (!( nparm && model && espec && ndwi )) { | |||
| ||||
445 | biffAddf(TENtenBiffKey, "%s: got NULL pointer", me); | |||
446 | return 1; | |||
447 | } | |||
448 | if (!( starts > 0 )) { | |||
449 | biffAddf(TENtenBiffKey, "%s: need non-zero starts", me); | |||
450 | return 1; | |||
451 | } | |||
452 | if (!( nrrdTypeFloat == typeOut || nrrdTypeDouble == typeOut )) { | |||
453 | biffAddf(TENtenBiffKey, "%s: typeOut must be %s or %s, not %s", me, | |||
454 | airEnumStr(nrrdType, nrrdTypeFloat), | |||
455 | airEnumStr(nrrdType, nrrdTypeDouble), | |||
456 | airEnumStr(nrrdType, typeOut)); | |||
457 | return 1; | |||
458 | } | |||
459 | dwiNum = ndwi->axis[0].size; | |||
460 | if (espec->imgNum != dwiNum) { | |||
461 | biffAddf(TENtenBiffKey, "%s: espec expects %u images but dwi has %u on axis 0", | |||
462 | me, espec->imgNum, AIR_CAST(unsigned int, dwiNum)((unsigned int)(dwiNum))); | |||
463 | return 1; | |||
464 | } | |||
465 | ||||
466 | /* allocate output (and set axmap) */ | |||
467 | dparm = model->alloc(); | |||
468 | dparmBest = model->alloc(); | |||
469 | if (!( dparm && dparmBest )) { | |||
470 | biffAddf(TENtenBiffKey, "%s: couldn't allocate parm vecs", me); | |||
471 | return 1; | |||
472 | } | |||
473 | mop = airMopNew(); | |||
474 | airMopAdd(mop, dparm, airFree, airMopAlways); | |||
475 | airMopAdd(mop, dparmBest, airFree, airMopAlways); | |||
476 | saveParmNum = saveB0 ? model->parmNum : model->parmNum-1; | |||
477 | for (ii=0; ii<ndwi->dim; ii++) { | |||
478 | szOut[ii] = (!ii | |||
479 | ? saveParmNum | |||
480 | : ndwi->axis[ii].size); | |||
481 | axmap[ii] = (!ii | |||
482 | ? -1 | |||
483 | : AIR_CAST(int, ii)((int)(ii))); | |||
484 | if (ii) { | |||
485 | erraxmap[ii-1] = AIR_CAST(int, ii)((int)(ii)); | |||
486 | } | |||
487 | } | |||
488 | if (nrrdMaybeAlloc_nva(nparm, typeOut, ndwi->dim, szOut)) { | |||
489 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't allocate output " | |||
490 | "(saveB0 %d, knownB0 %d)", me, saveB0, knownB0); | |||
491 | airMopError(mop); return 1; | |||
492 | } | |||
493 | if (nsqeP) { | |||
494 | nsqe = *nsqeP; | |||
495 | if (!nsqe) { | |||
496 | nsqe = nrrdNew(); | |||
497 | *nsqeP = nsqe; | |||
498 | } | |||
499 | if (nrrdMaybeAlloc_nva(nsqe, typeOut, ndwi->dim-1, szOut+1)) { | |||
500 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't allocate error output", me); | |||
501 | airMopError(mop); return 1; | |||
502 | } | |||
503 | } else { | |||
504 | nsqe = NULL((void*)0); | |||
505 | } | |||
506 | if (nconvP) { | |||
507 | nconv = *nconvP; | |||
508 | if (!nconv) { | |||
509 | nconv = nrrdNew(); | |||
510 | *nconvP = nconv; | |||
511 | } | |||
512 | if (nrrdMaybeAlloc_nva(nconv, nrrdTypeDouble, ndwi->dim-1, szOut+1)) { | |||
513 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't allocate conv output", me); | |||
514 | airMopError(mop); return 1; | |||
515 | } | |||
516 | } else { | |||
517 | nconv = NULL((void*)0); | |||
518 | } | |||
519 | if (niterP) { | |||
520 | niter = *niterP; | |||
521 | if (!niter) { | |||
522 | niter = nrrdNew(); | |||
523 | *niterP = niter; | |||
524 | } | |||
525 | if (nrrdMaybeAlloc_nva(niter, nrrdTypeUInt, ndwi->dim-1, szOut+1)) { | |||
526 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't allocate iter output", me); | |||
527 | airMopError(mop); return 1; | |||
528 | } | |||
529 | } else { | |||
530 | niter = NULL((void*)0); | |||
531 | } | |||
532 | ddwi = AIR_CALLOC(espec->imgNum, double)(double*)(calloc((espec->imgNum), sizeof(double))); | |||
533 | dwibuff = AIR_CALLOC(espec->imgNum, double)(double*)(calloc((espec->imgNum), sizeof(double))); | |||
534 | if (!(ddwi && dwibuff)) { | |||
535 | biffAddf(TENtenBiffKey, "%s: couldn't allocate dwi buffers", me); | |||
| ||||
536 | airMopError(mop); return 1; | |||
537 | } | |||
538 | airMopAdd(mop, ddwi, airFree, airMopAlways); | |||
539 | airMopAdd(mop, dwibuff, airFree, airMopAlways); | |||
540 | ||||
541 | /* set output */ | |||
542 | if (_rng) { | |||
543 | rng = _rng; | |||
544 | } else { | |||
545 | airRandMTStateGlobalInit(); | |||
546 | rng = airRandMTStateGlobal; | |||
547 | } | |||
548 | numSamp = nrrdElementNumber(ndwi)/ndwi->axis[0].size; | |||
549 | lup = nrrdDLookup[ndwi->type]; | |||
550 | ins = nrrdDInsert[typeOut]; | |||
551 | parm = AIR_CAST(char *, nparm->data)((char *)(nparm->data)); | |||
552 | dwi = AIR_CAST(char *, ndwi->data)((char *)(ndwi->data)); | |||
553 | itersTaken = 0; | |||
554 | if (verbose) { | |||
555 | fprintf(stderr__stderrp, "%s: fitting ... ", me); | |||
556 | fflush(stderr__stderrp); | |||
557 | } | |||
558 | for (II=0; II<numSamp; II++) { | |||
559 | double cvf, convFrac=0; | |||
560 | unsigned int ss, itak; | |||
561 | if (verbose) { | |||
562 | fprintf(stderr__stderrp, "%s", airDoneStr(0, II, numSamp, doneStr)); | |||
563 | fflush(stderr__stderrp); | |||
564 | } | |||
565 | for (ii=0; ii<dwiNum; ii++) { | |||
566 | ddwi[ii] = lup(dwi, ii); | |||
567 | } | |||
568 | sqeBest = DBL_MAX1.7976931348623157e+308; /* forces at least one improvement */ | |||
569 | for (ss=0; ss<starts; ss++) { | |||
570 | /* can add other debugging conditions here */ | |||
571 | fitVerbose = verbose; | |||
572 | if (knownB0) { | |||
573 | dparm[0] = tenExperSpecKnownB0Get(espec, ddwi); | |||
574 | } | |||
575 | model->rand(dparm, rng, knownB0); | |||
576 | sqe = model->sqeFit(dparm, &cvf, &itak, | |||
577 | espec, dwibuff, ddwi, | |||
578 | dparm, knownB0, minIter, maxIter, | |||
579 | convEps, fitVerbose); | |||
580 | if (sqe <= sqeBest) { | |||
581 | sqeBest = sqe; | |||
582 | model->copy(dparmBest, dparm); | |||
583 | itersTaken = itak; | |||
584 | convFrac = cvf; | |||
585 | } | |||
586 | } | |||
587 | for (ii=0; ii<saveParmNum; ii++) { | |||
588 | ins(parm, ii, saveB0 ? dparmBest[ii] : dparmBest[ii+1]); | |||
589 | } | |||
590 | /* save things about fitting into nrrds */ | |||
591 | if (nsqeP) { | |||
592 | ins(nsqe->data, II, sqeBest); | |||
593 | } | |||
594 | if (nconvP) { | |||
595 | nrrdDInsert[nrrdTypeDouble](nconv->data, II, convFrac); | |||
596 | } | |||
597 | if (niterP) { | |||
598 | nrrdDInsert[nrrdTypeUInt](niter->data, II, itersTaken); | |||
599 | } | |||
600 | parm += saveParmNum*nrrdTypeSize[typeOut]; | |||
601 | dwi += espec->imgNum*nrrdTypeSize[ndwi->type]; | |||
602 | } | |||
603 | if (verbose) { | |||
604 | fprintf(stderr__stderrp, "%s\n", airDoneStr(0, II, numSamp, doneStr)); | |||
605 | } | |||
606 | ||||
607 | if (nrrdAxisInfoCopy(nparm, ndwi, axmap, NRRD_AXIS_INFO_SIZE_BIT(1<< 1)) | |||
608 | || nrrdBasicInfoCopy(nparm, ndwi, | |||
609 | NRRD_BASIC_INFO_DATA_BIT(1<< 1) | |||
610 | | NRRD_BASIC_INFO_TYPE_BIT(1<< 2) | |||
611 | | NRRD_BASIC_INFO_BLOCKSIZE_BIT(1<< 3) | |||
612 | | NRRD_BASIC_INFO_DIMENSION_BIT(1<< 4) | |||
613 | | NRRD_BASIC_INFO_CONTENT_BIT(1<< 5) | |||
614 | | NRRD_BASIC_INFO_COMMENTS_BIT(1<<14) | |||
615 | | (nrrdStateKeyValuePairsPropagate | |||
616 | ? 0 | |||
617 | : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT(1<<15)))) { | |||
618 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't copy axis or basic info", me); | |||
619 | airMopError(mop); return 1; | |||
620 | } | |||
621 | if (nsqeP) { | |||
622 | if (nrrdAxisInfoCopy(nsqe, ndwi, erraxmap, NRRD_AXIS_INFO_SIZE_BIT(1<< 1)) | |||
623 | || nrrdBasicInfoCopy(nsqe, ndwi, | |||
624 | NRRD_BASIC_INFO_DATA_BIT(1<< 1) | |||
625 | | NRRD_BASIC_INFO_TYPE_BIT(1<< 2) | |||
626 | | NRRD_BASIC_INFO_BLOCKSIZE_BIT(1<< 3) | |||
627 | | NRRD_BASIC_INFO_DIMENSION_BIT(1<< 4) | |||
628 | | NRRD_BASIC_INFO_CONTENT_BIT(1<< 5) | |||
629 | | NRRD_BASIC_INFO_COMMENTS_BIT(1<<14) | |||
630 | | (nrrdStateKeyValuePairsPropagate | |||
631 | ? 0 | |||
632 | : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT(1<<15)))) { | |||
633 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, | |||
634 | "%s: couldn't copy axis or basic info to error out", me); | |||
635 | airMopError(mop); return 1; | |||
636 | } | |||
637 | } | |||
638 | if (nconvP) { | |||
639 | if (nrrdAxisInfoCopy(nconv, ndwi, erraxmap, NRRD_AXIS_INFO_SIZE_BIT(1<< 1)) | |||
640 | || nrrdBasicInfoCopy(nconv, ndwi, | |||
641 | NRRD_BASIC_INFO_DATA_BIT(1<< 1) | |||
642 | | NRRD_BASIC_INFO_TYPE_BIT(1<< 2) | |||
643 | | NRRD_BASIC_INFO_BLOCKSIZE_BIT(1<< 3) | |||
644 | | NRRD_BASIC_INFO_DIMENSION_BIT(1<< 4) | |||
645 | | NRRD_BASIC_INFO_CONTENT_BIT(1<< 5) | |||
646 | | NRRD_BASIC_INFO_COMMENTS_BIT(1<<14) | |||
647 | | (nrrdStateKeyValuePairsPropagate | |||
648 | ? 0 | |||
649 | : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT(1<<15)))) { | |||
650 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, | |||
651 | "%s: couldn't copy axis or basic info to conv out", me); | |||
652 | airMopError(mop); return 1; | |||
653 | } | |||
654 | } | |||
655 | if (niterP) { | |||
656 | if (nrrdAxisInfoCopy(niter, ndwi, erraxmap, NRRD_AXIS_INFO_SIZE_BIT(1<< 1)) | |||
657 | || nrrdBasicInfoCopy(niter, ndwi, | |||
658 | NRRD_BASIC_INFO_DATA_BIT(1<< 1) | |||
659 | | NRRD_BASIC_INFO_TYPE_BIT(1<< 2) | |||
660 | | NRRD_BASIC_INFO_BLOCKSIZE_BIT(1<< 3) | |||
661 | | NRRD_BASIC_INFO_DIMENSION_BIT(1<< 4) | |||
662 | | NRRD_BASIC_INFO_CONTENT_BIT(1<< 5) | |||
663 | | NRRD_BASIC_INFO_COMMENTS_BIT(1<<14) | |||
664 | | (nrrdStateKeyValuePairsPropagate | |||
665 | ? 0 | |||
666 | : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT(1<<15)))) { | |||
667 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, | |||
668 | "%s: couldn't copy axis or basic info to iter out", me); | |||
669 | airMopError(mop); return 1; | |||
670 | } | |||
671 | } | |||
672 | lablen = (strlen(tenModelPrefixStr) | |||
673 | + (saveB0 ? strlen("B0+") : 0) | |||
674 | + strlen(model->name) | |||
675 | + 1); | |||
676 | nparm->axis[0].label = AIR_CALLOC(lablen, char)(char*)(calloc((lablen), sizeof(char))); | |||
677 | sprintf(nparm->axis[0].label, "%s%s%s",__builtin___sprintf_chk (nparm->axis[0].label, 0, __builtin_object_size (nparm->axis[0].label, 2 > 1 ? 1 : 0), "%s%s%s", tenModelPrefixStr , saveB0 ? "B0+" : "", model->name) | |||
678 | tenModelPrefixStr,__builtin___sprintf_chk (nparm->axis[0].label, 0, __builtin_object_size (nparm->axis[0].label, 2 > 1 ? 1 : 0), "%s%s%s", tenModelPrefixStr , saveB0 ? "B0+" : "", model->name) | |||
679 | saveB0 ? "B0+" : "",__builtin___sprintf_chk (nparm->axis[0].label, 0, __builtin_object_size (nparm->axis[0].label, 2 > 1 ? 1 : 0), "%s%s%s", tenModelPrefixStr , saveB0 ? "B0+" : "", model->name) | |||
680 | model->name)__builtin___sprintf_chk (nparm->axis[0].label, 0, __builtin_object_size (nparm->axis[0].label, 2 > 1 ? 1 : 0), "%s%s%s", tenModelPrefixStr , saveB0 ? "B0+" : "", model->name); | |||
681 | ||||
682 | airMopOkay(mop); | |||
683 | return 0; | |||
684 | } | |||
685 | ||||
686 | int | |||
687 | tenModelNllFit(Nrrd *nparm, Nrrd **nnllP, | |||
688 | const tenModel *model, | |||
689 | const tenExperSpec *espec, const Nrrd *ndwi, | |||
690 | int rician, double sigma, int knownB0) { | |||
691 | ||||
692 | AIR_UNUSED(nparm)(void)(nparm); | |||
693 | AIR_UNUSED(nnllP)(void)(nnllP); | |||
694 | AIR_UNUSED(model)(void)(model); | |||
695 | AIR_UNUSED(espec)(void)(espec); | |||
696 | AIR_UNUSED(ndwi)(void)(ndwi); | |||
697 | AIR_UNUSED(rician)(void)(rician); | |||
698 | AIR_UNUSED(sigma)(void)(sigma); | |||
699 | AIR_UNUSED(knownB0)(void)(knownB0); | |||
700 | ||||
701 | return 0; | |||
702 | } | |||
703 | ||||
704 | /* | |||
705 | ** copy the B0 info if we have it | |||
706 | ** use the same type on the way out. | |||
707 | */ | |||
708 | int | |||
709 | tenModelConvert(Nrrd *nparmDst, int *convRetP, const tenModel *modelDst, | |||
710 | const Nrrd *nparmSrc, const tenModel *_modelSrc) { | |||
711 | static char me[]="tenModelConvert"; | |||
712 | const tenModel *modelSrc; | |||
713 | double *dpdst, *dpsrc, (*lup)(const void *v, size_t I), | |||
714 | (*ins)(void *v, size_t I, double d); | |||
715 | size_t szOut[NRRD_DIM_MAX16], II, NN, tsize; | |||
716 | airArray *mop; | |||
717 | int withB0, axmap[NRRD_DIM_MAX16], convRet=0; | |||
718 | unsigned int parmNumDst, parmNumSrc, ii, lablen; | |||
719 | const char *parmSrc; | |||
720 | char *parmDst; | |||
721 | ||||
722 | if (!( nparmDst && modelDst && nparmSrc )) { | |||
723 | biffAddf(TENtenBiffKey, "%s: got NULL pointer", me); | |||
724 | return 1; | |||
725 | } | |||
726 | if (!_modelSrc) { | |||
727 | /* we have to try to learn the source model from the nrrd */ | |||
728 | if (tenModelFromAxisLearn(&modelSrc, &withB0, nparmSrc->axis + 0)) { | |||
729 | biffAddf(TENtenBiffKey, "%s: couldn't learn model from src nparm", me); | |||
730 | return 1; | |||
731 | } | |||
732 | } else { | |||
733 | modelSrc = _modelSrc; | |||
734 | if (modelSrc->parmNum == nparmSrc->axis[0].size) { | |||
735 | withB0 = AIR_TRUE1; | |||
736 | } if (modelSrc->parmNum-1 == nparmSrc->axis[0].size) { | |||
737 | withB0 = AIR_FALSE0; | |||
738 | } else { | |||
739 | biffAddf(TENtenBiffKey, "%s: axis[0].size %u is not \"%s\" parmnum %u or 1 less", | |||
740 | me, AIR_CAST(unsigned int, nparmSrc->axis[0].size)((unsigned int)(nparmSrc->axis[0].size)), | |||
741 | modelSrc->name, modelSrc->parmNum); | |||
742 | return 1; | |||
743 | } | |||
744 | } | |||
745 | ||||
746 | mop = airMopNew(); | |||
747 | dpdst = modelDst->alloc(); | |||
748 | airMopAdd(mop, dpdst, airFree, airMopAlways); | |||
749 | dpsrc = modelSrc->alloc(); | |||
750 | airMopAdd(mop, dpsrc, airFree, airMopAlways); | |||
751 | lup = nrrdDLookup[nparmSrc->type]; | |||
752 | ins = nrrdDInsert[nparmSrc->type]; | |||
753 | parmNumDst = withB0 ? modelDst->parmNum : modelDst->parmNum-1; | |||
754 | parmNumSrc = nparmSrc->axis[0].size; | |||
755 | for (ii=0; ii<nparmSrc->dim; ii++) { | |||
756 | szOut[ii] = (!ii | |||
757 | ? parmNumDst | |||
758 | : nparmSrc->axis[ii].size); | |||
759 | axmap[ii] = (!ii | |||
760 | ? -1 | |||
761 | : AIR_CAST(int, ii)((int)(ii))); | |||
762 | } | |||
763 | if (nrrdMaybeAlloc_nva(nparmDst, nparmSrc->type, nparmSrc->dim, szOut)) { | |||
764 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't allocate output", me); | |||
765 | airMopError(mop); return 1; | |||
766 | } | |||
767 | ||||
768 | NN = nrrdElementNumber(nparmSrc)/nparmSrc->axis[0].size; | |||
769 | tsize = nrrdTypeSize[nparmSrc->type]; | |||
770 | parmSrc = AIR_CAST(char *, nparmSrc->data)((char *)(nparmSrc->data)); | |||
771 | parmDst = AIR_CAST(char *, nparmDst->data)((char *)(nparmDst->data)); | |||
772 | if (!withB0) { | |||
773 | dpsrc[0] = 0; | |||
774 | } | |||
775 | for (II=0; II<NN; II++) { | |||
776 | for (ii=0; ii<parmNumSrc; ii++) { | |||
777 | dpsrc[withB0 ? ii : ii+1] = lup(parmSrc, ii); | |||
778 | } | |||
779 | convRet = modelDst->convert(dpdst, dpsrc, modelSrc); | |||
780 | if (2 == convRet) { /* HEY should be enum for this value */ | |||
781 | biffAddf(TENtenBiffKey, "%s: error converting from \"%s\" to \"%s\"", me, | |||
782 | modelSrc->name, modelDst->name); | |||
783 | airMopError(mop); return 1; | |||
784 | } | |||
785 | for (ii=0; ii<parmNumDst; ii++) { | |||
786 | ins(parmDst, ii, dpdst[withB0 ? ii : ii+1]); | |||
787 | } | |||
788 | parmSrc += parmNumSrc*tsize; | |||
789 | parmDst += parmNumDst*tsize; | |||
790 | } | |||
791 | if (convRetP) { | |||
792 | *convRetP = convRet; | |||
793 | } | |||
794 | ||||
795 | if (nrrdAxisInfoCopy(nparmDst, nparmSrc, axmap, NRRD_AXIS_INFO_SIZE_BIT(1<< 1)) | |||
796 | || nrrdBasicInfoCopy(nparmDst, nparmSrc, | |||
797 | NRRD_BASIC_INFO_DATA_BIT(1<< 1) | |||
798 | | NRRD_BASIC_INFO_TYPE_BIT(1<< 2) | |||
799 | | NRRD_BASIC_INFO_BLOCKSIZE_BIT(1<< 3) | |||
800 | | NRRD_BASIC_INFO_DIMENSION_BIT(1<< 4) | |||
801 | | NRRD_BASIC_INFO_CONTENT_BIT(1<< 5) | |||
802 | | NRRD_BASIC_INFO_COMMENTS_BIT(1<<14) | |||
803 | | (nrrdStateKeyValuePairsPropagate | |||
804 | ? 0 | |||
805 | : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT(1<<15)))) { | |||
806 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't copy axis or basic info", me); | |||
807 | airMopError(mop); return 1; | |||
808 | } | |||
809 | /* HEY: COPY AND PASTE! from above. perhaps make helper functions? */ | |||
810 | lablen = (strlen(tenModelPrefixStr) | |||
811 | + (withB0 ? strlen("B0+") : 0) | |||
812 | + strlen(modelDst->name) | |||
813 | + 1); | |||
814 | nparmDst->axis[0].label = AIR_CALLOC(lablen, char)(char*)(calloc((lablen), sizeof(char))); | |||
815 | sprintf(nparmDst->axis[0].label, "%s%s%s",__builtin___sprintf_chk (nparmDst->axis[0].label, 0, __builtin_object_size (nparmDst->axis[0].label, 2 > 1 ? 1 : 0), "%s%s%s", tenModelPrefixStr , withB0 ? "B0+" : "", modelDst->name) | |||
816 | tenModelPrefixStr,__builtin___sprintf_chk (nparmDst->axis[0].label, 0, __builtin_object_size (nparmDst->axis[0].label, 2 > 1 ? 1 : 0), "%s%s%s", tenModelPrefixStr , withB0 ? "B0+" : "", modelDst->name) | |||
817 | withB0 ? "B0+" : "",__builtin___sprintf_chk (nparmDst->axis[0].label, 0, __builtin_object_size (nparmDst->axis[0].label, 2 > 1 ? 1 : 0), "%s%s%s", tenModelPrefixStr , withB0 ? "B0+" : "", modelDst->name) | |||
818 | modelDst->name)__builtin___sprintf_chk (nparmDst->axis[0].label, 0, __builtin_object_size (nparmDst->axis[0].label, 2 > 1 ? 1 : 0), "%s%s%s", tenModelPrefixStr , withB0 ? "B0+" : "", modelDst->name); | |||
819 | ||||
820 | airMopOkay(mop); | |||
821 | return 0; | |||
822 | } |