File: | src/ten/epireg.c |
Location: | line 1249, column 5 |
Description: | Potential leak of memory pointed to by 'ndwi' |
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 | int | |||
28 | _tenEpiRegSave(const char *fname, Nrrd *nsingle, Nrrd **nmulti, | |||
29 | int len, const char *desc) { | |||
30 | static const char me[]="_tenEpiRegSave"; | |||
31 | Nrrd *nout; | |||
32 | airArray *mop; | |||
33 | ||||
34 | mop = airMopNew(); | |||
35 | if (nsingle) { | |||
36 | nout = nsingle; | |||
37 | } else { | |||
38 | airMopAdd(mop, nout=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
39 | if (nrrdJoin(nout, (const Nrrd*const*)nmulti, len, 0, AIR_TRUE1)) { | |||
40 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't join %s for output", me, desc); | |||
41 | airMopError(mop); return 1; | |||
42 | } | |||
43 | } | |||
44 | if (nrrdSave(fname, nout, NULL((void*)0))) { | |||
45 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: trouble saving %s to \"%s\"", me, desc, fname); | |||
46 | airMopError(mop); return 1; | |||
47 | } | |||
48 | fprintf(stderr__stderrp, "%s: saved %s to \"%s\"\n", me, desc, fname); | |||
49 | airMopOkay(mop); | |||
50 | return 0; | |||
51 | } | |||
52 | ||||
53 | ||||
54 | int | |||
55 | _tenEpiRegCheck(Nrrd **nout, Nrrd **ndwi, unsigned int dwiLen, Nrrd *ngrad, | |||
56 | int reference, | |||
57 | double bwX, double bwY, double DWthr, | |||
58 | const NrrdKernel *kern, double *kparm) { | |||
59 | static const char me[]="_tenEpiRegCheck"; | |||
60 | unsigned int ni; | |||
61 | ||||
62 | AIR_UNUSED(DWthr)(void)(DWthr); | |||
63 | if (!( nout && ndwi && ngrad && kern && kparm )) { | |||
64 | biffAddf(TENtenBiffKey, "%s: got NULL pointer", me); | |||
65 | return 1; | |||
66 | } | |||
67 | if (tenGradientCheck(ngrad, nrrdTypeDefault, 6 /* <-- HEY: not sure */)) { | |||
68 | biffAddf(TENtenBiffKey, "%s: problem with given gradient list", me); | |||
69 | return 1; | |||
70 | } | |||
71 | if (dwiLen != ngrad->axis[1].size) { | |||
72 | char stmp[AIR_STRLEN_SMALL(128+1)]; | |||
73 | biffAddf(TENtenBiffKey, "%s: got %u DWIs, but %s gradient directions", me, | |||
74 | dwiLen, airSprintSize_t(stmp, ngrad->axis[1].size)); | |||
75 | return 1; | |||
76 | } | |||
77 | for (ni=0; ni<dwiLen; ni++) { | |||
78 | if (!nout[ni]) { | |||
79 | biffAddf(TENtenBiffKey, "%s: nout[%d] is NULL", me, ni); | |||
80 | return 1; | |||
81 | } | |||
82 | if (nrrdCheck(ndwi[ni])) { | |||
83 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, | |||
84 | "%s: basic nrrd validity failed on ndwi[%d]", me, ni); | |||
85 | return 1; | |||
86 | } | |||
87 | if (!nrrdSameSize(ndwi[0], ndwi[ni], AIR_TRUE1)) { | |||
88 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: ndwi[%d] is different from ndwi[0]", me, ni); | |||
89 | return 1; | |||
90 | } | |||
91 | } | |||
92 | if (!( 3 == ndwi[0]->dim )) { | |||
93 | biffAddf(TENtenBiffKey, "%s: didn't get a set of 3-D arrays (got %d-D)", me, | |||
94 | ndwi[0]->dim); | |||
95 | return 1; | |||
96 | } | |||
97 | if (!( AIR_IN_CL(-1, reference, (int)dwiLen-1)((-1) <= (reference) && (reference) <= ((int)dwiLen -1)) )) { | |||
98 | biffAddf(TENtenBiffKey, "%s: reference index %d not in valid range [-1,%d]", | |||
99 | me, reference, dwiLen-1); | |||
100 | return 1; | |||
101 | } | |||
102 | if (!( AIR_EXISTS(bwX)(((int)(!((bwX) - (bwX))))) && AIR_EXISTS(bwY)(((int)(!((bwY) - (bwY))))) )) { | |||
103 | biffAddf(TENtenBiffKey, "%s: bwX, bwY don't both exist", me); | |||
104 | return 1; | |||
105 | } | |||
106 | if (!( bwX >= 0 && bwY >= 0 )) { | |||
107 | biffAddf(TENtenBiffKey, "%s: bwX (%g) and bwY (%g) are not both non-negative", | |||
108 | me, bwX, bwY); | |||
109 | return 1; | |||
110 | } | |||
111 | return 0; | |||
112 | } | |||
113 | ||||
114 | /* | |||
115 | ** this assumes that all nblur[i] are valid nrrds, and does nothing | |||
116 | ** to manage them | |||
117 | */ | |||
118 | int | |||
119 | _tenEpiRegBlur(Nrrd **nblur, Nrrd **ndwi, unsigned int dwiLen, | |||
120 | double bwX, double bwY, int verb) { | |||
121 | static const char me[]="_tenEpiRegBlur"; | |||
122 | NrrdResampleInfo *rinfo; | |||
123 | airArray *mop; | |||
124 | size_t ni, sx, sy, sz; | |||
125 | double savemin[2], savemax[2]; | |||
126 | ||||
127 | if (!( bwX || bwY )) { | |||
128 | if (verb) { | |||
129 | fprintf(stderr__stderrp, "%s:\n ", me); fflush(stderr__stderrp); | |||
130 | } | |||
131 | for (ni=0; ni<dwiLen; ni++) { | |||
132 | if (verb) { | |||
133 | fprintf(stderr__stderrp, "%2u ", (unsigned int)ni); fflush(stderr__stderrp); | |||
134 | } | |||
135 | if (nrrdCopy(nblur[ni], ndwi[ni])) { | |||
136 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: trouble copying ndwi[%u]", | |||
137 | me, (unsigned int)ni); | |||
138 | return 1; | |||
139 | } | |||
140 | } | |||
141 | if (verb) { | |||
142 | fprintf(stderr__stderrp, "done\n"); | |||
143 | } | |||
144 | return 0; | |||
145 | } | |||
146 | /* else we need to blur */ | |||
147 | sx = ndwi[0]->axis[0].size; | |||
148 | sy = ndwi[0]->axis[1].size; | |||
149 | sz = ndwi[0]->axis[2].size; | |||
150 | mop = airMopNew(); | |||
151 | rinfo = nrrdResampleInfoNew(); | |||
152 | airMopAdd(mop, rinfo, (airMopper)nrrdResampleInfoNix, airMopAlways); | |||
153 | if (bwX) { | |||
154 | rinfo->kernel[0] = nrrdKernelGaussian; | |||
155 | rinfo->parm[0][0] = bwX; | |||
156 | rinfo->parm[0][1] = 3.0; /* how many stnd devs do we cut-off at */ | |||
157 | } else { | |||
158 | rinfo->kernel[0] = NULL((void*)0); | |||
159 | } | |||
160 | if (bwY) { | |||
161 | rinfo->kernel[1] = nrrdKernelGaussian; | |||
162 | rinfo->parm[1][0] = bwY; | |||
163 | rinfo->parm[1][1] = 3.0; /* how many stnd devs do we cut-off at */ | |||
164 | } else { | |||
165 | rinfo->kernel[1] = NULL((void*)0); | |||
166 | } | |||
167 | rinfo->kernel[2] = NULL((void*)0); | |||
168 | ELL_3V_SET(rinfo->samples, sx, sy, sz)((rinfo->samples)[0] = (sx), (rinfo->samples)[1] = (sy) , (rinfo->samples)[2] = (sz)); | |||
169 | ELL_3V_SET(rinfo->min, 0, 0, 0)((rinfo->min)[0] = (0), (rinfo->min)[1] = (0), (rinfo-> min)[2] = (0)); | |||
170 | ELL_3V_SET(rinfo->max, sx-1, sy-1, sz-1)((rinfo->max)[0] = (sx-1), (rinfo->max)[1] = (sy-1), (rinfo ->max)[2] = (sz-1)); | |||
171 | rinfo->boundary = nrrdBoundaryBleed; | |||
172 | rinfo->type = nrrdTypeDefault; | |||
173 | rinfo->renormalize = AIR_TRUE1; | |||
174 | rinfo->clamp = AIR_TRUE1; | |||
175 | if (verb) { | |||
176 | fprintf(stderr__stderrp, "%s:\n ", me); fflush(stderr__stderrp); | |||
177 | } | |||
178 | for (ni=0; ni<dwiLen; ni++) { | |||
179 | if (verb) { | |||
180 | fprintf(stderr__stderrp, "%2u ", (unsigned int)ni); fflush(stderr__stderrp); | |||
181 | } | |||
182 | savemin[0] = ndwi[ni]->axis[0].min; savemax[0] = ndwi[ni]->axis[0].max; | |||
183 | savemin[1] = ndwi[ni]->axis[1].min; savemax[1] = ndwi[ni]->axis[1].max; | |||
184 | ndwi[ni]->axis[0].min = 0; ndwi[ni]->axis[0].max = sx-1; | |||
185 | ndwi[ni]->axis[1].min = 0; ndwi[ni]->axis[1].max = sy-1; | |||
186 | if (nrrdSpatialResample(nblur[ni], ndwi[ni], rinfo)) { | |||
187 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: trouble blurring ndwi[%u]", | |||
188 | me, (unsigned int)ni); | |||
189 | airMopError(mop); return 1; | |||
190 | } | |||
191 | ndwi[ni]->axis[0].min = savemin[0]; ndwi[ni]->axis[0].max = savemax[0]; | |||
192 | ndwi[ni]->axis[1].min = savemin[1]; ndwi[ni]->axis[1].max = savemax[1]; | |||
193 | } | |||
194 | if (verb) { | |||
195 | fprintf(stderr__stderrp, "done\n"); | |||
196 | } | |||
197 | airMopOkay(mop); | |||
198 | return 0; | |||
199 | } | |||
200 | ||||
201 | int | |||
202 | _tenEpiRegThresholdFind(double *DWthrP, Nrrd **nin, int ninLen, | |||
203 | int save, double expo) { | |||
204 | static const char me[]="_tenEpiRegThresholdFind"; | |||
205 | Nrrd *nhist, *ntmp; | |||
206 | airArray *mop; | |||
207 | int ni, bins, E; | |||
208 | double min=0, max=0; | |||
209 | NrrdRange *range; | |||
210 | ||||
211 | mop = airMopNew(); | |||
212 | airMopAdd(mop, nhist=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
213 | airMopAdd(mop, ntmp=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
214 | ||||
215 | for (ni=0; ni<ninLen; ni++) { | |||
216 | range = nrrdRangeNewSet(nin[ni], nrrdBlind8BitRangeFalse); | |||
217 | if (!ni) { | |||
218 | min = range->min; | |||
219 | max = range->max; | |||
220 | } else { | |||
221 | min = AIR_MIN(min, range->min)((min) < (range->min) ? (min) : (range->min)); | |||
222 | max = AIR_MAX(max, range->max)((max) > (range->max) ? (max) : (range->max)); | |||
223 | } | |||
224 | range = nrrdRangeNix(range); | |||
225 | } | |||
226 | bins = AIR_MIN(1024, (int)(max - min + 1))((1024) < ((int)(max - min + 1)) ? (1024) : ((int)(max - min + 1))); | |||
227 | ntmp->axis[0].min = min; | |||
228 | ntmp->axis[0].max = max; | |||
229 | for (ni=0; ni<ninLen; ni++) { | |||
230 | if (nrrdHisto(ntmp, nin[ni], NULL((void*)0), NULL((void*)0), bins, nrrdTypeFloat)) { | |||
231 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, | |||
232 | "%s: problem forming histogram of DWI %d", me, ni); | |||
233 | airMopError(mop); return 1; | |||
234 | } | |||
235 | if (!ni) { | |||
236 | E = nrrdCopy(nhist, ntmp); | |||
237 | } else { | |||
238 | E = nrrdArithBinaryOp(nhist, nrrdBinaryOpAdd, nhist, ntmp); | |||
239 | } | |||
240 | if (E) { | |||
241 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, | |||
242 | "%s: problem updating histogram sum on DWI %d", | |||
243 | me, ni); | |||
244 | airMopError(mop); return 1; | |||
245 | } | |||
246 | } | |||
247 | if (save) { | |||
248 | nrrdSave("regtmp-dwihist.nrrd", nhist, NULL((void*)0)); | |||
249 | } | |||
250 | /* | |||
251 | if (_tenFindValley(DWthrP, nhist, 0.65, save)) { | |||
252 | biffAddf(TEN, "%s: problem finding DWI histogram valley", me); | |||
253 | airMopError(mop); return 1; | |||
254 | } | |||
255 | */ | |||
256 | if (nrrdHistoThresholdOtsu(DWthrP, nhist, expo)) { | |||
257 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: problem finding DWI threshold", me); | |||
258 | airMopError(mop); return 1; | |||
259 | } | |||
260 | ||||
261 | airMopOkay(mop); | |||
262 | return 0; | |||
263 | } | |||
264 | ||||
265 | int | |||
266 | _tenEpiRegThreshold(Nrrd **nthresh, Nrrd **nblur, unsigned int ninLen, | |||
267 | double DWthr, int verb, int progress, double expo) { | |||
268 | static const char me[]="_tenEpiRegThreshold"; | |||
269 | airArray *mop; | |||
270 | size_t I, sx, sy, sz, ni; | |||
271 | float val; | |||
272 | unsigned char *thr; | |||
273 | ||||
274 | if (!( AIR_EXISTS(DWthr)(((int)(!((DWthr) - (DWthr))))) )) { | |||
275 | if (_tenEpiRegThresholdFind(&DWthr, nblur, ninLen, progress, expo)) { | |||
276 | biffAddf(TENtenBiffKey, "%s: trouble with automatic threshold determination", me); | |||
277 | return 1; | |||
278 | } | |||
279 | fprintf(stderr__stderrp, "%s: using %g for DWI threshold\n", me, DWthr); | |||
280 | } | |||
281 | ||||
282 | mop = airMopNew(); | |||
283 | if (verb) { | |||
284 | fprintf(stderr__stderrp, "%s:\n ", me); fflush(stderr__stderrp); | |||
285 | } | |||
286 | sx = nblur[0]->axis[0].size; | |||
287 | sy = nblur[0]->axis[1].size; | |||
288 | sz = nblur[0]->axis[2].size; | |||
289 | for (ni=0; ni<ninLen; ni++) { | |||
290 | if (verb) { | |||
291 | fprintf(stderr__stderrp, "%2u ", (unsigned int)ni); fflush(stderr__stderrp); | |||
292 | } | |||
293 | if (nrrdMaybeAlloc_va(nthresh[ni], nrrdTypeUChar, 3, | |||
294 | sx, sy, sz)) { | |||
295 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: trouble allocating threshold %u", | |||
296 | me, (unsigned int)ni); | |||
297 | airMopError(mop); return 1; | |||
298 | } | |||
299 | thr = (unsigned char *)(nthresh[ni]->data); | |||
300 | for (I=0; I<sx*sy*sz; I++) { | |||
301 | val = nrrdFLookup[nblur[ni]->type](nblur[ni]->data, I); | |||
302 | val -= AIR_CAST(float, DWthr)((float)(DWthr)); | |||
303 | thr[I] = (val >= 0 ? 1 : 0); | |||
304 | } | |||
305 | } | |||
306 | if (verb) { | |||
307 | fprintf(stderr__stderrp, "done\n"); | |||
308 | } | |||
309 | ||||
310 | airMopOkay(mop); | |||
311 | return 0; | |||
312 | } | |||
313 | ||||
314 | /* | |||
315 | ** _tenEpiRegBB: find the biggest bright CC | |||
316 | */ | |||
317 | int | |||
318 | _tenEpiRegBB(Nrrd *nval, Nrrd *nsize) { | |||
319 | unsigned char *val; | |||
320 | int *size, big; | |||
321 | unsigned int ci; | |||
322 | ||||
323 | val = (unsigned char *)(nval->data); | |||
324 | size = (int *)(nsize->data); | |||
325 | big = 0; | |||
326 | for (ci=0; ci<nsize->axis[0].size; ci++) { | |||
327 | big = val[ci] ? AIR_MAX(big, size[ci])((big) > (size[ci]) ? (big) : (size[ci])) : big; | |||
328 | } | |||
329 | return big; | |||
330 | } | |||
331 | ||||
332 | int | |||
333 | _tenEpiRegCC(Nrrd **nthr, int ninLen, int conny, int verb) { | |||
334 | static const char me[]="_tenEpiRegCC"; | |||
335 | Nrrd *nslc, *ncc, *nval, *nsize; | |||
336 | airArray *mop; | |||
337 | int ni, z, sz, big; | |||
338 | ||||
339 | mop = airMopNew(); | |||
340 | airMopAdd(mop, nslc=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
341 | airMopAdd(mop, nval=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
342 | airMopAdd(mop, ncc=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
343 | airMopAdd(mop, nsize=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
344 | sz = nthr[0]->axis[2].size; | |||
345 | if (verb) { | |||
346 | fprintf(stderr__stderrp, "%s:\n ", me); fflush(stderr__stderrp); | |||
347 | } | |||
348 | for (ni=0; ni<ninLen; ni++) { | |||
349 | if (verb) { | |||
350 | fprintf(stderr__stderrp, "%2d ", ni); fflush(stderr__stderrp); | |||
351 | } | |||
352 | /* for each volume, we find the biggest bright 3-D CC, and merge | |||
353 | down (to dark) all smaller bright pieces. Then, within each | |||
354 | slice, we do 2-D CCs, find the biggest bright CC (size == big), | |||
355 | and merge up (to bright) all small dark pieces, where | |||
356 | (currently) small is big/2 */ | |||
357 | big = -1; | |||
358 | if (nrrdCCFind(ncc, &nval, nthr[ni], nrrdTypeDefault, conny) | |||
359 | || nrrdCCSize(nsize, ncc) | |||
360 | || !(big = _tenEpiRegBB(nval, nsize)) | |||
361 | || nrrdCCMerge(ncc, ncc, nval, -1, big-1, 0, conny) | |||
362 | || nrrdCCRevalue(nthr[ni], ncc, nval)) { | |||
363 | if (big) { | |||
364 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, | |||
365 | "%s: trouble with 3-D processing nthr[%d]", me, ni); | |||
366 | return 1; | |||
367 | } else { | |||
368 | biffAddf(TENtenBiffKey, "%s: got size 0 for biggest bright CC of nthr[%d]", | |||
369 | me, ni); | |||
370 | return 1; | |||
371 | } | |||
372 | } | |||
373 | for (z=0; z<sz; z++) { | |||
374 | big = -1; | |||
375 | if ( nrrdSlice(nslc, nthr[ni], 2, z) | |||
376 | || nrrdCCFind(ncc, &nval, nslc, nrrdTypeDefault, conny) | |||
377 | || nrrdCCSize(nsize, ncc) | |||
378 | || !(big = _tenEpiRegBB(nval, nsize)) | |||
379 | || nrrdCCMerge(ncc, ncc, nval, 1, big/2, 0, conny) | |||
380 | || nrrdCCRevalue(nslc, ncc, nval) | |||
381 | || nrrdSplice(nthr[ni], nthr[ni], nslc, 2, z) ) { | |||
382 | if (big) { | |||
383 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: trouble processing slice %d of nthr[%d]", | |||
384 | me, z, ni); | |||
385 | return 1; | |||
386 | } else { | |||
387 | /* biggest bright CC on this slice had size 0 | |||
388 | <==> there was no bright CC on this slice, move on */ | |||
389 | } | |||
390 | } | |||
391 | } | |||
392 | } | |||
393 | if (verb) { | |||
394 | fprintf(stderr__stderrp, "done\n"); | |||
395 | } | |||
396 | ||||
397 | airMopOkay(mop); | |||
398 | return 0; | |||
399 | } | |||
400 | ||||
401 | #define MEAN_X0 0 | |||
402 | #define MEAN_Y1 1 | |||
403 | #define M_022 2 | |||
404 | #define M_113 3 | |||
405 | #define M_204 4 | |||
406 | ||||
407 | /* | |||
408 | ** _tenEpiRegMoments() | |||
409 | ** | |||
410 | ** the moments are stored in (of course) a nrrd, one scanline per slice, | |||
411 | ** with each scanline containing: | |||
412 | ** | |||
413 | ** 0 1 2 3 4 | |||
414 | ** mean(x) mean(y) M_02 M_11 M_20 | |||
415 | */ | |||
416 | int | |||
417 | _tenEpiRegMoments(Nrrd **nmom, Nrrd **nthresh, unsigned int ninLen, | |||
418 | int verb) { | |||
419 | static const char me[]="_tenEpiRegMoments"; | |||
420 | size_t sx, sy, sz, xi, yi, zi, ni; | |||
421 | double N, mx, my, cx, cy, x, y, M02, M11, M20, *mom; | |||
422 | float val; | |||
423 | unsigned char *thr; | |||
424 | ||||
425 | sx = nthresh[0]->axis[0].size; | |||
426 | sy = nthresh[0]->axis[1].size; | |||
427 | sz = nthresh[0]->axis[2].size; | |||
428 | if (verb) { | |||
429 | fprintf(stderr__stderrp, "%s:\n ", me); fflush(stderr__stderrp); | |||
430 | } | |||
431 | for (ni=0; ni<ninLen; ni++) { | |||
432 | if (verb) { | |||
433 | fprintf(stderr__stderrp, "%2u ", (unsigned int)ni); fflush(stderr__stderrp); | |||
434 | } | |||
435 | if (nrrdMaybeAlloc_va(nmom[ni], nrrdTypeDouble, 2, | |||
436 | AIR_CAST(size_t, 5)((size_t)(5)), sz)) { | |||
437 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, | |||
438 | "%s: couldn't allocate nmom[%u]", me, (unsigned int)ni); | |||
439 | return 1; | |||
440 | } | |||
441 | nrrdAxisInfoSet_va(nmom[ni], nrrdAxisInfoLabel, "mx,my,h,s,t", "z"); | |||
442 | thr = (unsigned char *)(nthresh[ni]->data); | |||
443 | mom = (double *)(nmom[ni]->data); | |||
444 | for (zi=0; zi<sz; zi++) { | |||
445 | /* ------ find mx, my */ | |||
446 | N = 0; | |||
447 | mx = my = 0.0; | |||
448 | for (yi=0; yi<sy; yi++) { | |||
449 | for (xi=0; xi<sx; xi++) { | |||
450 | val = thr[xi + sx*yi]; | |||
451 | N += val; | |||
452 | mx += xi*val; | |||
453 | my += yi*val; | |||
454 | } | |||
455 | } | |||
456 | if (N == sx*sy) { | |||
457 | biffAddf(TENtenBiffKey, "%s: saw only non-zero pixels in nthresh[%u]; " | |||
458 | "DWI hreshold too low?", me, (unsigned int)ni); | |||
459 | return 1; | |||
460 | } | |||
461 | if (N) { | |||
462 | /* there were non-zero pixels */ | |||
463 | mx /= N; | |||
464 | my /= N; | |||
465 | cx = sx/2.0; | |||
466 | cy = sy/2.0; | |||
467 | /* ------ find M02, M11, M20 */ | |||
468 | M02 = M11 = M20 = 0.0; | |||
469 | for (yi=0; yi<sy; yi++) { | |||
470 | for (xi=0; xi<sx; xi++) { | |||
471 | val = thr[xi + sx*yi]; | |||
472 | x = xi - cx; | |||
473 | y = yi - cy; | |||
474 | M02 += y*y*val; | |||
475 | M11 += x*y*val; | |||
476 | M20 += x*x*val; | |||
477 | } | |||
478 | } | |||
479 | M02 /= N; | |||
480 | M11 /= N; | |||
481 | M20 /= N; | |||
482 | /* ------ set output */ | |||
483 | mom[MEAN_X0] = mx; | |||
484 | mom[MEAN_Y1] = my; | |||
485 | mom[M_022] = M02; | |||
486 | mom[M_113] = M11; | |||
487 | mom[M_204] = M20; | |||
488 | } else { | |||
489 | /* there were no non-zero pixels */ | |||
490 | mom[MEAN_X0] = 0; | |||
491 | mom[MEAN_Y1] = 0; | |||
492 | mom[M_022] = 0; | |||
493 | mom[M_113] = 0; | |||
494 | mom[M_204] = 0; | |||
495 | } | |||
496 | thr += sx*sy; | |||
497 | mom += 5; | |||
498 | } | |||
499 | } | |||
500 | if (verb) { | |||
501 | fprintf(stderr__stderrp, "done\n"); | |||
502 | } | |||
503 | ||||
504 | return 0; | |||
505 | } | |||
506 | ||||
507 | /* | |||
508 | ** _tenEpiRegPairXforms | |||
509 | ** | |||
510 | ** uses moment information to compute all pair-wise transforms, which are | |||
511 | ** stored in the 3 x ninLen x ninLen x sizeZ output. If xfr = npxfr->data, | |||
512 | ** xfr[0 + 3*(zi + sz*(A + ninLen*B))] is shear, | |||
513 | ** xfr[1 + " ] is scale, and | |||
514 | ** xfr[2 + " ] is translate in the transform | |||
515 | ** that maps slice zi from volume A to volume B. | |||
516 | */ | |||
517 | int | |||
518 | _tenEpiRegPairXforms(Nrrd *npxfr, Nrrd **nmom, int ninLen) { | |||
519 | static const char me[]="_tenEpiRegPairXforms"; | |||
520 | double *xfr, *A, *B, hh, ss, tt; | |||
521 | int ai, bi, zi, sz; | |||
522 | ||||
523 | sz = nmom[0]->axis[1].size; | |||
524 | if (nrrdMaybeAlloc_va(npxfr, nrrdTypeDouble, 4, | |||
525 | AIR_CAST(size_t, 5)((size_t)(5)), | |||
526 | AIR_CAST(size_t, sz)((size_t)(sz)), | |||
527 | AIR_CAST(size_t, ninLen)((size_t)(ninLen)), | |||
528 | AIR_CAST(size_t, ninLen)((size_t)(ninLen)))) { | |||
529 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't allocate transform nrrd", me); | |||
530 | return 1; | |||
531 | } | |||
532 | nrrdAxisInfoSet_va(npxfr, nrrdAxisInfoLabel, | |||
533 | "mx,my,h,s,t", "zi", "orig", "target"); | |||
534 | xfr = (double *)(npxfr->data); | |||
535 | for (bi=0; bi<ninLen; bi++) { | |||
536 | for (ai=0; ai<ninLen; ai++) { | |||
537 | for (zi=0; zi<sz; zi++) { | |||
538 | A = (double*)(nmom[ai]->data) + 5*zi; | |||
539 | B = (double*)(nmom[bi]->data) + 5*zi; | |||
540 | ss = sqrt((A[M_204]*B[M_022] - B[M_113]*B[M_113]) / | |||
541 | (A[M_204]*A[M_022] - A[M_113]*A[M_113])); | |||
542 | hh = (B[M_113] - ss*A[M_113])/A[M_204]; | |||
543 | tt = B[MEAN_Y1] - A[MEAN_Y1]; | |||
544 | ELL_5V_SET(xfr + 5*(zi + sz*(ai + ninLen*bi)),((xfr + 5*(zi + sz*(ai + ninLen*bi)))[0]=(A[0]), (xfr + 5*(zi + sz*(ai + ninLen*bi)))[1]=(A[1]), (xfr + 5*(zi + sz*(ai + ninLen *bi)))[2]=(hh), (xfr + 5*(zi + sz*(ai + ninLen*bi)))[3]=(ss), (xfr + 5*(zi + sz*(ai + ninLen*bi)))[4]=(tt)) | |||
545 | A[MEAN_X], A[MEAN_Y], hh, ss, tt)((xfr + 5*(zi + sz*(ai + ninLen*bi)))[0]=(A[0]), (xfr + 5*(zi + sz*(ai + ninLen*bi)))[1]=(A[1]), (xfr + 5*(zi + sz*(ai + ninLen *bi)))[2]=(hh), (xfr + 5*(zi + sz*(ai + ninLen*bi)))[3]=(ss), (xfr + 5*(zi + sz*(ai + ninLen*bi)))[4]=(tt)); | |||
546 | } | |||
547 | } | |||
548 | } | |||
549 | return 0; | |||
550 | } | |||
551 | ||||
552 | #define SHEAR2 2 | |||
553 | #define SCALE3 3 | |||
554 | #define TRAN4 4 | |||
555 | ||||
556 | int | |||
557 | _tenEpiRegEstimHST(Nrrd *nhst, Nrrd *npxfr, int ninLen, Nrrd *ngrad) { | |||
558 | static const char me[]="_tenEpiRegEstimHST"; | |||
559 | double *hst, *grad, *mat, *vec, *ans, *pxfr, *gA, *gB; | |||
560 | int z, sz, A, B, npairs, ri; | |||
561 | Nrrd **nmat, *nvec, **ninv, *nans; | |||
562 | airArray *mop; | |||
563 | int order; | |||
564 | ||||
565 | order = 1; | |||
566 | ||||
567 | sz = npxfr->axis[1].size; | |||
568 | npairs = ninLen*(ninLen-1); | |||
569 | ||||
570 | mop = airMopNew(); | |||
571 | nmat = (Nrrd**)calloc(sz, sizeof(Nrrd*)); | |||
572 | ninv = (Nrrd**)calloc(sz, sizeof(Nrrd*)); | |||
573 | airMopAdd(mop, nmat, airFree, airMopAlways); | |||
574 | airMopAdd(mop, ninv, airFree, airMopAlways); | |||
575 | for (z=0; z<sz; z++) { | |||
576 | airMopAdd(mop, nmat[z]=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
577 | if (nrrdMaybeAlloc_va(nmat[z], nrrdTypeDouble, 2, | |||
578 | AIR_CAST(size_t, (1 == order ? 3 : 9))((size_t)((1 == order ? 3 : 9))), | |||
579 | AIR_CAST(size_t, npairs)((size_t)(npairs)))) { | |||
580 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't allocate fitting matrices", me); | |||
581 | airMopError(mop); return 1; | |||
582 | } | |||
583 | airMopAdd(mop, ninv[z]=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
584 | } | |||
585 | airMopAdd(mop, nvec=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
586 | airMopAdd(mop, nans=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
587 | if (nrrdMaybeAlloc_va(nhst, nrrdTypeDouble, 2, | |||
588 | AIR_CAST(size_t, (1 == order ? 9 : 27))((size_t)((1 == order ? 9 : 27))), | |||
589 | AIR_CAST(size_t, sz)((size_t)(sz))) | |||
590 | || nrrdMaybeAlloc_va(nvec, nrrdTypeDouble, 2, | |||
591 | AIR_CAST(size_t, 1)((size_t)(1)), | |||
592 | AIR_CAST(size_t, npairs)((size_t)(npairs)))) { | |||
593 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: couldn't allocate HST nrrd", me); | |||
594 | airMopError(mop); return 1; | |||
595 | } | |||
596 | nrrdAxisInfoSet_va(nhst, nrrdAxisInfoLabel, | |||
597 | (1 == order | |||
598 | ? "Hx,Hy,Hz,Sx,Sy,Sz,Tx,Ty,Tz" | |||
599 | : "HST parms"), "z"); | |||
600 | ||||
601 | /* ------ per-slice: compute model fitting matrix and its pseudo-inverse */ | |||
602 | grad = (double *)(ngrad->data); | |||
603 | for (z=0; z<sz; z++) { | |||
604 | hst = (double *)(nhst->data) + (1 == order ? 9 : 27)*z; | |||
605 | mat = (double *)(nmat[z]->data); | |||
606 | ri = 0; | |||
607 | for (A=0; A<ninLen; A++) { | |||
608 | for (B=0; B<ninLen; B++) { | |||
609 | if (A == B) continue; | |||
610 | pxfr = (double *)(npxfr->data) + 0 + 5*(z + sz*(A + ninLen*B)); | |||
611 | gA = grad + 0 + 3*A; | |||
612 | gB = grad + 0 + 3*B; | |||
613 | if (1 == order) { | |||
614 | ELL_3V_SET(mat + 3*ri,((mat + 3*ri)[0] = (gB[0] - pxfr[3]*gA[0]), (mat + 3*ri)[1] = (gB[1] - pxfr[3]*gA[1]), (mat + 3*ri)[2] = (gB[2] - pxfr[3]* gA[2])) | |||
615 | gB[0] - pxfr[SCALE]*gA[0],((mat + 3*ri)[0] = (gB[0] - pxfr[3]*gA[0]), (mat + 3*ri)[1] = (gB[1] - pxfr[3]*gA[1]), (mat + 3*ri)[2] = (gB[2] - pxfr[3]* gA[2])) | |||
616 | gB[1] - pxfr[SCALE]*gA[1],((mat + 3*ri)[0] = (gB[0] - pxfr[3]*gA[0]), (mat + 3*ri)[1] = (gB[1] - pxfr[3]*gA[1]), (mat + 3*ri)[2] = (gB[2] - pxfr[3]* gA[2])) | |||
617 | gB[2] - pxfr[SCALE]*gA[2])((mat + 3*ri)[0] = (gB[0] - pxfr[3]*gA[0]), (mat + 3*ri)[1] = (gB[1] - pxfr[3]*gA[1]), (mat + 3*ri)[2] = (gB[2] - pxfr[3]* gA[2])); | |||
618 | } else { | |||
619 | ELL_9V_SET(mat + 9*ri,((mat + 9*ri)[0]=(gB[0] - pxfr[3]*gA[0]), (mat + 9*ri)[1]=(gB [1] - pxfr[3]*gA[1]), (mat + 9*ri)[2]=(gB[2] - pxfr[3]*gA[2]) , (mat + 9*ri)[3]=(gB[0]*gB[0]*gB[0] - pxfr[3]*gA[0]*gA[0]*gA [0]), (mat + 9*ri)[4]=(gB[1]*gB[1]*gB[1] - pxfr[3]*gA[1]*gA[1 ]*gA[1]), (mat + 9*ri)[5]=(gB[2]*gB[2]*gB[2] - pxfr[3]*gA[2]* gA[2]*gA[2]), (mat + 9*ri)[6]=(gB[0]*gB[1]*gB[2] - pxfr[3]*gA [0]*gA[1]*gA[2]), (mat + 9*ri)[7]=(1), (mat + 9*ri)[8]=(1)) | |||
620 | gB[0] - pxfr[SCALE]*gA[0],((mat + 9*ri)[0]=(gB[0] - pxfr[3]*gA[0]), (mat + 9*ri)[1]=(gB [1] - pxfr[3]*gA[1]), (mat + 9*ri)[2]=(gB[2] - pxfr[3]*gA[2]) , (mat + 9*ri)[3]=(gB[0]*gB[0]*gB[0] - pxfr[3]*gA[0]*gA[0]*gA [0]), (mat + 9*ri)[4]=(gB[1]*gB[1]*gB[1] - pxfr[3]*gA[1]*gA[1 ]*gA[1]), (mat + 9*ri)[5]=(gB[2]*gB[2]*gB[2] - pxfr[3]*gA[2]* gA[2]*gA[2]), (mat + 9*ri)[6]=(gB[0]*gB[1]*gB[2] - pxfr[3]*gA [0]*gA[1]*gA[2]), (mat + 9*ri)[7]=(1), (mat + 9*ri)[8]=(1)) | |||
621 | gB[1] - pxfr[SCALE]*gA[1],((mat + 9*ri)[0]=(gB[0] - pxfr[3]*gA[0]), (mat + 9*ri)[1]=(gB [1] - pxfr[3]*gA[1]), (mat + 9*ri)[2]=(gB[2] - pxfr[3]*gA[2]) , (mat + 9*ri)[3]=(gB[0]*gB[0]*gB[0] - pxfr[3]*gA[0]*gA[0]*gA [0]), (mat + 9*ri)[4]=(gB[1]*gB[1]*gB[1] - pxfr[3]*gA[1]*gA[1 ]*gA[1]), (mat + 9*ri)[5]=(gB[2]*gB[2]*gB[2] - pxfr[3]*gA[2]* gA[2]*gA[2]), (mat + 9*ri)[6]=(gB[0]*gB[1]*gB[2] - pxfr[3]*gA [0]*gA[1]*gA[2]), (mat + 9*ri)[7]=(1), (mat + 9*ri)[8]=(1)) | |||
622 | gB[2] - pxfr[SCALE]*gA[2],((mat + 9*ri)[0]=(gB[0] - pxfr[3]*gA[0]), (mat + 9*ri)[1]=(gB [1] - pxfr[3]*gA[1]), (mat + 9*ri)[2]=(gB[2] - pxfr[3]*gA[2]) , (mat + 9*ri)[3]=(gB[0]*gB[0]*gB[0] - pxfr[3]*gA[0]*gA[0]*gA [0]), (mat + 9*ri)[4]=(gB[1]*gB[1]*gB[1] - pxfr[3]*gA[1]*gA[1 ]*gA[1]), (mat + 9*ri)[5]=(gB[2]*gB[2]*gB[2] - pxfr[3]*gA[2]* gA[2]*gA[2]), (mat + 9*ri)[6]=(gB[0]*gB[1]*gB[2] - pxfr[3]*gA [0]*gA[1]*gA[2]), (mat + 9*ri)[7]=(1), (mat + 9*ri)[8]=(1)) | |||
623 | gB[0]*gB[0]*gB[0] - pxfr[SCALE]*gA[0]*gA[0]*gA[0],((mat + 9*ri)[0]=(gB[0] - pxfr[3]*gA[0]), (mat + 9*ri)[1]=(gB [1] - pxfr[3]*gA[1]), (mat + 9*ri)[2]=(gB[2] - pxfr[3]*gA[2]) , (mat + 9*ri)[3]=(gB[0]*gB[0]*gB[0] - pxfr[3]*gA[0]*gA[0]*gA [0]), (mat + 9*ri)[4]=(gB[1]*gB[1]*gB[1] - pxfr[3]*gA[1]*gA[1 ]*gA[1]), (mat + 9*ri)[5]=(gB[2]*gB[2]*gB[2] - pxfr[3]*gA[2]* gA[2]*gA[2]), (mat + 9*ri)[6]=(gB[0]*gB[1]*gB[2] - pxfr[3]*gA [0]*gA[1]*gA[2]), (mat + 9*ri)[7]=(1), (mat + 9*ri)[8]=(1)) | |||
624 | gB[1]*gB[1]*gB[1] - pxfr[SCALE]*gA[1]*gA[1]*gA[1],((mat + 9*ri)[0]=(gB[0] - pxfr[3]*gA[0]), (mat + 9*ri)[1]=(gB [1] - pxfr[3]*gA[1]), (mat + 9*ri)[2]=(gB[2] - pxfr[3]*gA[2]) , (mat + 9*ri)[3]=(gB[0]*gB[0]*gB[0] - pxfr[3]*gA[0]*gA[0]*gA [0]), (mat + 9*ri)[4]=(gB[1]*gB[1]*gB[1] - pxfr[3]*gA[1]*gA[1 ]*gA[1]), (mat + 9*ri)[5]=(gB[2]*gB[2]*gB[2] - pxfr[3]*gA[2]* gA[2]*gA[2]), (mat + 9*ri)[6]=(gB[0]*gB[1]*gB[2] - pxfr[3]*gA [0]*gA[1]*gA[2]), (mat + 9*ri)[7]=(1), (mat + 9*ri)[8]=(1)) | |||
625 | gB[2]*gB[2]*gB[2] - pxfr[SCALE]*gA[2]*gA[2]*gA[2],((mat + 9*ri)[0]=(gB[0] - pxfr[3]*gA[0]), (mat + 9*ri)[1]=(gB [1] - pxfr[3]*gA[1]), (mat + 9*ri)[2]=(gB[2] - pxfr[3]*gA[2]) , (mat + 9*ri)[3]=(gB[0]*gB[0]*gB[0] - pxfr[3]*gA[0]*gA[0]*gA [0]), (mat + 9*ri)[4]=(gB[1]*gB[1]*gB[1] - pxfr[3]*gA[1]*gA[1 ]*gA[1]), (mat + 9*ri)[5]=(gB[2]*gB[2]*gB[2] - pxfr[3]*gA[2]* gA[2]*gA[2]), (mat + 9*ri)[6]=(gB[0]*gB[1]*gB[2] - pxfr[3]*gA [0]*gA[1]*gA[2]), (mat + 9*ri)[7]=(1), (mat + 9*ri)[8]=(1)) | |||
626 | gB[0]*gB[1]*gB[2] - pxfr[SCALE]*gA[0]*gA[1]*gA[2],((mat + 9*ri)[0]=(gB[0] - pxfr[3]*gA[0]), (mat + 9*ri)[1]=(gB [1] - pxfr[3]*gA[1]), (mat + 9*ri)[2]=(gB[2] - pxfr[3]*gA[2]) , (mat + 9*ri)[3]=(gB[0]*gB[0]*gB[0] - pxfr[3]*gA[0]*gA[0]*gA [0]), (mat + 9*ri)[4]=(gB[1]*gB[1]*gB[1] - pxfr[3]*gA[1]*gA[1 ]*gA[1]), (mat + 9*ri)[5]=(gB[2]*gB[2]*gB[2] - pxfr[3]*gA[2]* gA[2]*gA[2]), (mat + 9*ri)[6]=(gB[0]*gB[1]*gB[2] - pxfr[3]*gA [0]*gA[1]*gA[2]), (mat + 9*ri)[7]=(1), (mat + 9*ri)[8]=(1)) | |||
627 | 1, 1)((mat + 9*ri)[0]=(gB[0] - pxfr[3]*gA[0]), (mat + 9*ri)[1]=(gB [1] - pxfr[3]*gA[1]), (mat + 9*ri)[2]=(gB[2] - pxfr[3]*gA[2]) , (mat + 9*ri)[3]=(gB[0]*gB[0]*gB[0] - pxfr[3]*gA[0]*gA[0]*gA [0]), (mat + 9*ri)[4]=(gB[1]*gB[1]*gB[1] - pxfr[3]*gA[1]*gA[1 ]*gA[1]), (mat + 9*ri)[5]=(gB[2]*gB[2]*gB[2] - pxfr[3]*gA[2]* gA[2]*gA[2]), (mat + 9*ri)[6]=(gB[0]*gB[1]*gB[2] - pxfr[3]*gA [0]*gA[1]*gA[2]), (mat + 9*ri)[7]=(1), (mat + 9*ri)[8]=(1)); | |||
628 | /* | |||
629 | gB[0]*gB[1] - pxfr[SCALE]*gA[0]*gA[1], | |||
630 | gB[0]*gB[2] - pxfr[SCALE]*gA[0]*gA[2], | |||
631 | gB[1]*gB[2] - pxfr[SCALE]*gA[1]*gA[2]); | |||
632 | */ | |||
633 | } | |||
634 | ri += 1; | |||
635 | } | |||
636 | } | |||
637 | if (nrrdHasNonExist(nmat[z])) { | |||
638 | /* as happens if there were zero slices in the segmentation output */ | |||
639 | if (1 == order) { | |||
640 | ELL_3V_SET(hst + 0*3, 0, 0, 0)((hst + 0*3)[0] = (0), (hst + 0*3)[1] = (0), (hst + 0*3)[2] = (0)); | |||
641 | ELL_3V_SET(hst + 1*3, 0, 0, 0)((hst + 1*3)[0] = (0), (hst + 1*3)[1] = (0), (hst + 1*3)[2] = (0)); | |||
642 | ELL_3V_SET(hst + 2*3, 0, 0, 0)((hst + 2*3)[0] = (0), (hst + 2*3)[1] = (0), (hst + 2*3)[2] = (0)); | |||
643 | } else { | |||
644 | ELL_9V_SET(hst + 0*9, 0, 0, 0, 0, 0, 0, 0, 0, 0)((hst + 0*9)[0]=(0), (hst + 0*9)[1]=(0), (hst + 0*9)[2]=(0), ( hst + 0*9)[3]=(0), (hst + 0*9)[4]=(0), (hst + 0*9)[5]=(0), (hst + 0*9)[6]=(0), (hst + 0*9)[7]=(0), (hst + 0*9)[8]=(0)); | |||
645 | ELL_9V_SET(hst + 1*9, 0, 0, 0, 0, 0, 0, 0, 0, 0)((hst + 1*9)[0]=(0), (hst + 1*9)[1]=(0), (hst + 1*9)[2]=(0), ( hst + 1*9)[3]=(0), (hst + 1*9)[4]=(0), (hst + 1*9)[5]=(0), (hst + 1*9)[6]=(0), (hst + 1*9)[7]=(0), (hst + 1*9)[8]=(0)); | |||
646 | ELL_9V_SET(hst + 2*9, 0, 0, 0, 0, 0, 0, 0, 0, 0)((hst + 2*9)[0]=(0), (hst + 2*9)[1]=(0), (hst + 2*9)[2]=(0), ( hst + 2*9)[3]=(0), (hst + 2*9)[4]=(0), (hst + 2*9)[5]=(0), (hst + 2*9)[6]=(0), (hst + 2*9)[7]=(0), (hst + 2*9)[8]=(0)); | |||
647 | } | |||
648 | } else { | |||
649 | if (ell_Nm_pseudo_inv(ninv[z], nmat[z])) { | |||
650 | biffMovef(TENtenBiffKey, ELLell_biff_key, "%s: trouble estimating model (slice %d)", me, z); | |||
651 | airMopError(mop); return 1; | |||
652 | } | |||
653 | } | |||
654 | } | |||
655 | vec = (double *)(nvec->data); | |||
656 | ||||
657 | /* ------ find Sx, Sy, Sz per slice */ | |||
658 | for (z=0; z<sz; z++) { | |||
659 | if (nrrdHasNonExist(nmat[z])) { | |||
660 | /* we've already zero-ed out this row in the HST nrrd */ | |||
661 | continue; | |||
662 | } | |||
663 | hst = (double *)(nhst->data) + (1 == order ? 9 : 27)*z; | |||
664 | ri = 0; | |||
665 | for (A=0; A<ninLen; A++) { | |||
666 | for (B=0; B<ninLen; B++) { | |||
667 | if (A == B) continue; | |||
668 | pxfr = (double *)(npxfr->data) + 0 + 5*(z + sz*(A + ninLen*B)); | |||
669 | vec[ri] = pxfr[SCALE3] - 1; | |||
670 | ri += 1; | |||
671 | } | |||
672 | } | |||
673 | if (ell_Nm_mul(nans, ninv[z], nvec)) { | |||
674 | biffMovef(TENtenBiffKey, ELLell_biff_key, | |||
675 | "%s: trouble estimating model (slice %d): Sx, Sy, Sz", | |||
676 | me, z); | |||
677 | airMopError(mop); return 1; | |||
678 | } | |||
679 | ans = (double *)(nans->data); | |||
680 | if (1 == order) { | |||
681 | ELL_3V_COPY(hst + 1*3, ans)((hst + 1*3)[0] = (ans)[0], (hst + 1*3)[1] = (ans)[1], (hst + 1*3)[2] = (ans)[2]); | |||
682 | } else { | |||
683 | ELL_9V_COPY(hst + 1*9, ans)((hst + 1*9)[0] = (ans)[0], (hst + 1*9)[1] = (ans)[1], (hst + 1*9)[2] = (ans)[2], (hst + 1*9)[3] = (ans)[3], (hst + 1*9)[4 ] = (ans)[4], (hst + 1*9)[5] = (ans)[5], (hst + 1*9)[6] = (ans )[6], (hst + 1*9)[7] = (ans)[7], (hst + 1*9)[8] = (ans)[8]); | |||
684 | } | |||
685 | } | |||
686 | ||||
687 | /* ------ find Hx, Hy, Hz per slice */ | |||
688 | for (z=0; z<sz; z++) { | |||
689 | if (nrrdHasNonExist(nmat[z])) { | |||
690 | /* we've already zero-ed out this row in the HST nrrd */ | |||
691 | continue; | |||
692 | } | |||
693 | hst = (double *)(nhst->data) + (1 == order ? 9 : 27)*z; | |||
694 | ri = 0; | |||
695 | for (A=0; A<ninLen; A++) { | |||
696 | for (B=0; B<ninLen; B++) { | |||
697 | if (A == B) continue; | |||
698 | pxfr = (double *)(npxfr->data) + 0 + 5*(z + sz*(A + ninLen*B)); | |||
699 | vec[ri] = pxfr[SHEAR2]; | |||
700 | ri += 1; | |||
701 | } | |||
702 | } | |||
703 | if (ell_Nm_mul(nans, ninv[z], nvec)) { | |||
704 | biffAddf(TENtenBiffKey, ELLell_biff_key, "%s: trouble estimating model (slice %d): Hx, Hy, Hz", | |||
705 | me, z); | |||
706 | airMopError(mop); return 1; | |||
707 | } | |||
708 | ans = (double *)(nans->data); | |||
709 | if (1 == order) { | |||
710 | ELL_3V_COPY(hst + 0*3, ans)((hst + 0*3)[0] = (ans)[0], (hst + 0*3)[1] = (ans)[1], (hst + 0*3)[2] = (ans)[2]); | |||
711 | } else { | |||
712 | ELL_9V_COPY(hst + 0*9, ans)((hst + 0*9)[0] = (ans)[0], (hst + 0*9)[1] = (ans)[1], (hst + 0*9)[2] = (ans)[2], (hst + 0*9)[3] = (ans)[3], (hst + 0*9)[4 ] = (ans)[4], (hst + 0*9)[5] = (ans)[5], (hst + 0*9)[6] = (ans )[6], (hst + 0*9)[7] = (ans)[7], (hst + 0*9)[8] = (ans)[8]); | |||
713 | } | |||
714 | } | |||
715 | ||||
716 | /* ------ find Tx, Ty, Tz per slice */ | |||
717 | for (z=0; z<sz; z++) { | |||
718 | if (nrrdHasNonExist(nmat[z])) { | |||
719 | /* we've already zero-ed out this row in the HST nrrd */ | |||
720 | continue; | |||
721 | } | |||
722 | hst = (double *)(nhst->data) + (1 == order ? 9 : 27)*z; | |||
723 | ri = 0; | |||
724 | for (A=0; A<ninLen; A++) { | |||
725 | for (B=0; B<ninLen; B++) { | |||
726 | if (A == B) continue; | |||
727 | pxfr = (double *)(npxfr->data) + 0 + 5*(z + sz*(A + ninLen*B)); | |||
728 | vec[ri] = pxfr[TRAN4]; | |||
729 | ri += 1; | |||
730 | } | |||
731 | } | |||
732 | if (ell_Nm_mul(nans, ninv[z], nvec)) { | |||
733 | biffMovef(TENtenBiffKey, ELLell_biff_key, | |||
734 | "%s: trouble estimating model (slice %d): Tx, Ty, Tz", | |||
735 | me, z); | |||
736 | airMopError(mop); return 1; | |||
737 | } | |||
738 | ans = (double *)(nans->data); | |||
739 | if (1 == order) { | |||
740 | ELL_3V_COPY(hst + 2*3, ans)((hst + 2*3)[0] = (ans)[0], (hst + 2*3)[1] = (ans)[1], (hst + 2*3)[2] = (ans)[2]); | |||
741 | } else { | |||
742 | ELL_9V_COPY(hst + 2*9, ans)((hst + 2*9)[0] = (ans)[0], (hst + 2*9)[1] = (ans)[1], (hst + 2*9)[2] = (ans)[2], (hst + 2*9)[3] = (ans)[3], (hst + 2*9)[4 ] = (ans)[4], (hst + 2*9)[5] = (ans)[5], (hst + 2*9)[6] = (ans )[6], (hst + 2*9)[7] = (ans)[7], (hst + 2*9)[8] = (ans)[8]); | |||
743 | } | |||
744 | } | |||
745 | ||||
746 | airMopOkay(mop); | |||
747 | return 0; | |||
748 | } | |||
749 | ||||
750 | int | |||
751 | _tenEpiRegFitHST(Nrrd *nhst, Nrrd **_ncc, int ninLen, | |||
752 | double goodFrac, int prog, int verb) { | |||
753 | static const char me[]="_tenEpiRegFitHST"; | |||
754 | airArray *mop; | |||
755 | Nrrd *ncc, *ntA, *ntB, *nsd, *nl2; | |||
756 | unsigned int cc, sz, zi, sh, hi; | |||
757 | float *mess, *two, tmp; | |||
758 | double *hst, x, y, xx, xy, mm, bb; | |||
759 | ||||
760 | mop = airMopNew(); | |||
761 | airMopAdd(mop, ncc=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
762 | airMopAdd(mop, ntA=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
763 | airMopAdd(mop, ntB=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
764 | airMopAdd(mop, nsd=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
765 | airMopAdd(mop, nl2=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
766 | /* do SD and L2 projections of the CCs along the DWI axis, | |||
767 | integrate these over the X and Y axes of the slices, | |||
768 | and define per-slice "messiness" as the quotient of the | |||
769 | SD integral with the L2 integral */ | |||
770 | if (verb) { | |||
771 | fprintf(stderr__stderrp, "%s: measuring segmentation uncertainty ... ", me); | |||
772 | fflush(stderr__stderrp); | |||
773 | } | |||
774 | if (nrrdJoin(ncc, (const Nrrd*const*)_ncc, ninLen, 0, AIR_TRUE1) | |||
775 | || nrrdProject(ntA, ncc, 0, nrrdMeasureSD, nrrdTypeFloat) | |||
776 | || nrrdProject(ntB, ntA, 0, nrrdMeasureSum, nrrdTypeFloat) | |||
777 | || nrrdProject(nsd, ntB, 0, nrrdMeasureSum, nrrdTypeFloat) | |||
778 | || nrrdProject(ntA, ncc, 0, nrrdMeasureL2, nrrdTypeFloat) | |||
779 | || nrrdProject(ntB, ntA, 0, nrrdMeasureSum, nrrdTypeFloat) | |||
780 | || nrrdProject(nl2, ntB, 0, nrrdMeasureSum, nrrdTypeFloat) | |||
781 | || nrrdArithBinaryOp(ntA, nrrdBinaryOpDivide, nsd, nl2)) { | |||
782 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: trouble doing CC projections", me); | |||
783 | airMopError(mop); return 1; | |||
784 | } | |||
785 | if (verb) { | |||
786 | fprintf(stderr__stderrp, "done\n"); | |||
787 | } | |||
788 | if (prog && _tenEpiRegSave("regtmp-messy.txt", ntA, | |||
789 | NULL((void*)0), 0, "segmentation uncertainty")) { | |||
790 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: EpiRegSave failed", me); | |||
791 | airMopError(mop); return 1; | |||
792 | } | |||
793 | /* now ntA stores the per-slice messiness */ | |||
794 | mess = AIR_CAST(float*, ntA->data)((float*)(ntA->data)); | |||
795 | ||||
796 | /* allocate an array of 2 floats per slice */ | |||
797 | sz = ntA->axis[0].size; | |||
798 | two = AIR_CAST(float*, calloc(2*sz, sizeof(float)))((float*)(calloc(2*sz, sizeof(float)))); | |||
799 | if (!two) { | |||
800 | biffAddf(TENtenBiffKey, "%s: couldn't allocate tmp buffer", me); | |||
801 | airMopError(mop); return 1; | |||
802 | } | |||
803 | airMopAdd(mop, two, airFree, airMopAlways); | |||
804 | /* initial ordering: messiness, slice index */ | |||
805 | for (zi=0; zi<sz; zi++) { | |||
806 | two[0 + 2*zi] = (AIR_EXISTS(mess[zi])(((int)(!((mess[zi]) - (mess[zi]))))) | |||
807 | ? mess[zi] | |||
808 | : 666); /* don't use empty slices */ | |||
809 | two[1 + 2*zi] = AIR_CAST(float, zi)((float)(zi)); | |||
810 | } | |||
811 | /* sort into ascending messiness */ | |||
812 | qsort(two, zi, 2*sizeof(float), nrrdValCompare[nrrdTypeFloat]); | |||
813 | /* flip ordering while thresholding messiness into usability */ | |||
814 | for (zi=0; zi<sz; zi++) { | |||
815 | tmp = two[1 + 2*zi]; | |||
816 | two[1 + 2*zi] = AIR_AFFINE(0, zi, sz-1, 0, 1)( ((double)(1)-(0))*((double)(zi)-(0)) / ((double)(sz-1)-(0)) + (0)) <= goodFrac ? 1.0f : 0.0f; | |||
817 | two[0 + 2*zi] = tmp; | |||
818 | } | |||
819 | /* sort again, now into ascending slice order */ | |||
820 | qsort(two, zi, 2*sizeof(float), nrrdValCompare[nrrdTypeFloat]); | |||
821 | if (verb) { | |||
822 | fprintf(stderr__stderrp, "%s: using slices", me); | |||
823 | for (zi=0; zi<sz; zi++) { | |||
824 | if (two[1 + 2*zi]) { | |||
825 | fprintf(stderr__stderrp, " %u", zi); | |||
826 | } | |||
827 | } | |||
828 | fprintf(stderr__stderrp, " for fitting\n"); | |||
829 | } | |||
830 | ||||
831 | /* perform fitting for each column in hst (regardless of | |||
832 | whether we're using a 1st or 2nd order model */ | |||
833 | hst = (double*)(nhst->data); | |||
834 | sh = nhst->axis[0].size; | |||
835 | for (hi=0; hi<sh; hi++) { | |||
836 | x = y = xy = xx = 0; | |||
837 | cc = 0; | |||
838 | for (zi=0; zi<sz; zi++) { | |||
839 | if (!two[1 + 2*zi]) | |||
840 | continue; | |||
841 | cc += 1; | |||
842 | x += zi; | |||
843 | xx += zi*zi; | |||
844 | y += hst[hi + sh*zi]; | |||
845 | xy += zi*hst[hi + sh*zi]; | |||
846 | } | |||
847 | x /= cc; xx /= cc; y /= cc; xy /= cc; | |||
848 | mm = (xy - x*y)/(xx - x*x); | |||
849 | bb = y - mm*x; | |||
850 | for (zi=0; zi<sz; zi++) { | |||
851 | hst[hi + sh*zi] = mm*zi + bb; | |||
852 | } | |||
853 | } | |||
854 | ||||
855 | airMopOkay(mop); | |||
856 | return 0; | |||
857 | } | |||
858 | ||||
859 | int | |||
860 | _tenEpiRegGetHST(double *hhP, double *ssP, double *ttP, | |||
861 | int reference, int ni, int zi, | |||
862 | Nrrd *npxfr, Nrrd *nhst, Nrrd *ngrad) { | |||
863 | double *xfr, *hst, *_g, grad[9]; /* big enough for 2nd order */ | |||
864 | int sz, ninLen; | |||
865 | ||||
866 | int order; | |||
867 | ||||
868 | order = 1; | |||
869 | ||||
870 | /* these could also have been passed to us, but we can also discover them */ | |||
871 | sz = npxfr->axis[1].size; | |||
872 | ninLen = npxfr->axis[2].size; | |||
873 | ||||
874 | if (-1 == reference) { | |||
875 | /* we use the estimated H,S,T vectors to determine distortion | |||
876 | as a function of gradient direction, and then invert this */ | |||
877 | _g = (double*)(ngrad->data) + 0 + 3*ni; | |||
878 | if (1 == order) { | |||
879 | hst = (double*)(nhst->data) + 0 + 9*zi; | |||
880 | *hhP = ELL_3V_DOT(_g, hst + 0*3)((_g)[0]*(hst + 0*3)[0] + (_g)[1]*(hst + 0*3)[1] + (_g)[2]*(hst + 0*3)[2]); | |||
881 | *ssP = 1 + ELL_3V_DOT(_g, hst + 1*3)((_g)[0]*(hst + 1*3)[0] + (_g)[1]*(hst + 1*3)[1] + (_g)[2]*(hst + 1*3)[2]); | |||
882 | *ttP = ELL_3V_DOT(_g, hst + 2*3)((_g)[0]*(hst + 2*3)[0] + (_g)[1]*(hst + 2*3)[1] + (_g)[2]*(hst + 2*3)[2]); | |||
883 | } else { | |||
884 | hst = (double*)(nhst->data) + 0 + 27*zi; | |||
885 | ELL_9V_SET(grad, _g[0], _g[1], _g[2],((grad)[0]=(_g[0]), (grad)[1]=(_g[1]), (grad)[2]=(_g[2]), (grad )[3]=(_g[0]*_g[0]*_g[0]), (grad)[4]=(_g[1]*_g[1]*_g[1]), (grad )[5]=(_g[2]*_g[2]*_g[2]), (grad)[6]=(_g[0]*_g[1]*_g[2]), (grad )[7]=(0), (grad)[8]=(0)) | |||
886 | _g[0]*_g[0]*_g[0], _g[1]*_g[1]*_g[1], _g[2]*_g[2]*_g[2],((grad)[0]=(_g[0]), (grad)[1]=(_g[1]), (grad)[2]=(_g[2]), (grad )[3]=(_g[0]*_g[0]*_g[0]), (grad)[4]=(_g[1]*_g[1]*_g[1]), (grad )[5]=(_g[2]*_g[2]*_g[2]), (grad)[6]=(_g[0]*_g[1]*_g[2]), (grad )[7]=(0), (grad)[8]=(0)) | |||
887 | _g[0]*_g[1]*_g[2],((grad)[0]=(_g[0]), (grad)[1]=(_g[1]), (grad)[2]=(_g[2]), (grad )[3]=(_g[0]*_g[0]*_g[0]), (grad)[4]=(_g[1]*_g[1]*_g[1]), (grad )[5]=(_g[2]*_g[2]*_g[2]), (grad)[6]=(_g[0]*_g[1]*_g[2]), (grad )[7]=(0), (grad)[8]=(0)) | |||
888 | 0, 0)((grad)[0]=(_g[0]), (grad)[1]=(_g[1]), (grad)[2]=(_g[2]), (grad )[3]=(_g[0]*_g[0]*_g[0]), (grad)[4]=(_g[1]*_g[1]*_g[1]), (grad )[5]=(_g[2]*_g[2]*_g[2]), (grad)[6]=(_g[0]*_g[1]*_g[2]), (grad )[7]=(0), (grad)[8]=(0)); | |||
889 | /* | |||
890 | _g[0]*_g[1], _g[0]*_g[2], _g[1]*_g[2]); | |||
891 | */ | |||
892 | *hhP = ELL_9V_DOT(grad, hst + 0*9)((grad)[0]*(hst + 0*9)[0] + (grad)[1]*(hst + 0*9)[1] + (grad) [2]*(hst + 0*9)[2] + (grad)[3]*(hst + 0*9)[3] + (grad)[4]*(hst + 0*9)[4] + (grad)[5]*(hst + 0*9)[5] + (grad)[6]*(hst + 0*9) [6] + (grad)[7]*(hst + 0*9)[7] + (grad)[8]*(hst + 0*9)[8]); | |||
893 | *ssP = 1 + ELL_9V_DOT(grad, hst + 1*9)((grad)[0]*(hst + 1*9)[0] + (grad)[1]*(hst + 1*9)[1] + (grad) [2]*(hst + 1*9)[2] + (grad)[3]*(hst + 1*9)[3] + (grad)[4]*(hst + 1*9)[4] + (grad)[5]*(hst + 1*9)[5] + (grad)[6]*(hst + 1*9) [6] + (grad)[7]*(hst + 1*9)[7] + (grad)[8]*(hst + 1*9)[8]); | |||
894 | *ttP = ELL_9V_DOT(grad, hst + 2*9)((grad)[0]*(hst + 2*9)[0] + (grad)[1]*(hst + 2*9)[1] + (grad) [2]*(hst + 2*9)[2] + (grad)[3]*(hst + 2*9)[3] + (grad)[4]*(hst + 2*9)[4] + (grad)[5]*(hst + 2*9)[5] + (grad)[6]*(hst + 2*9) [6] + (grad)[7]*(hst + 2*9)[7] + (grad)[8]*(hst + 2*9)[8]); | |||
895 | } | |||
896 | } else { | |||
897 | /* we register against a specific DWI */ | |||
898 | xfr = (double*)(npxfr->data) + 0 + 5*(zi + sz*(reference + ninLen*ni)); | |||
899 | *hhP = xfr[2]; | |||
900 | *ssP = xfr[3]; | |||
901 | *ttP = xfr[4]; | |||
902 | } | |||
903 | return 0; | |||
904 | } | |||
905 | ||||
906 | /* | |||
907 | ** _tenEpiRegSliceWarp | |||
908 | ** | |||
909 | ** Apply [hh,ss,tt] transform to nin, putting results in nout, but with | |||
910 | ** some trickiness: | |||
911 | ** - nwght and nidx are already allocated to the the weights (type float) | |||
912 | ** and indices for resampling nin with "kern" and "kparm" | |||
913 | ** - nout is already allocated to the correct size and type | |||
914 | ** - nin is type float, but output must be type nout->type | |||
915 | ** - nin is been transposed to have the resampled axis fastest in memory, | |||
916 | ** but nout output will not be transposed | |||
917 | */ | |||
918 | int | |||
919 | _tenEpiRegSliceWarp(Nrrd *nout, Nrrd *nin, Nrrd *nwght, Nrrd *nidx, | |||
920 | const NrrdKernel *kern, double *kparm, | |||
921 | double hh, double ss, double tt, double cx, double cy) { | |||
922 | float *wght, *in, pp, pf, tmp; | |||
923 | int *idx; | |||
924 | unsigned int supp; | |||
925 | size_t sx, sy, xi, yi, pb, pi; | |||
926 | double (*ins)(void *, size_t, double), (*clamp)(double); | |||
927 | ||||
928 | sy = nin->axis[0].size; | |||
929 | sx = nin->axis[1].size; | |||
930 | supp = AIR_CAST(unsigned int, kern->support(kparm))((unsigned int)(kern->support(kparm))); | |||
931 | ins = nrrdDInsert[nout->type]; | |||
932 | clamp = nrrdDClamp[nout->type]; | |||
933 | ||||
934 | in = AIR_CAST(float*, nin->data)((float*)(nin->data)); | |||
935 | for (xi=0; xi<sx; xi++) { | |||
936 | idx = AIR_CAST(int*, nidx->data)((int*)(nidx->data)); | |||
937 | wght = AIR_CAST(float*, nwght->data)((float*)(nwght->data)); | |||
938 | for (yi=0; yi<sy; yi++) { | |||
939 | pp = AIR_CAST(float, hh*(xi - cx) + ss*(yi - cy) + tt + cy)((float)(hh*(xi - cx) + ss*(yi - cy) + tt + cy)); | |||
940 | pb = AIR_CAST(size_t, floor(pp))((size_t)(floor(pp))); | |||
941 | pf = pp - pb; | |||
942 | for (pi=0; pi<2*supp; pi++) { | |||
943 | idx[pi] = AIR_MIN(pb + pi - (supp-1), sy-1)((pb + pi - (supp-1)) < (sy-1) ? (pb + pi - (supp-1)) : (sy -1)); | |||
944 | wght[pi] = pi - (supp-1) - pf; | |||
945 | } | |||
946 | idx += 2*supp; | |||
947 | wght += 2*supp; | |||
948 | } | |||
949 | idx = AIR_CAST(int*, nidx->data)((int*)(nidx->data)); | |||
950 | wght = AIR_CAST(float*, nwght->data)((float*)(nwght->data)); | |||
951 | kern->evalN_f(wght, wght, 2*supp*sy, kparm); | |||
952 | for (yi=0; yi<sy; yi++) { | |||
953 | tmp = 0; | |||
954 | for (pi=0; pi<2*supp; pi++) { | |||
955 | tmp += in[idx[pi]]*wght[pi]; | |||
956 | } | |||
957 | ins(nout->data, xi + sx*yi, clamp(ss*tmp)); | |||
958 | idx += 2*supp; | |||
959 | wght += 2*supp; | |||
960 | } | |||
961 | in += sy; | |||
962 | } | |||
963 | ||||
964 | return 0; | |||
965 | } | |||
966 | ||||
967 | /* | |||
968 | ** _tenEpiRegWarp() | |||
969 | ** | |||
970 | */ | |||
971 | int | |||
972 | _tenEpiRegWarp(Nrrd **ndone, Nrrd *npxfr, Nrrd *nhst, Nrrd *ngrad, | |||
973 | Nrrd **nin, int ninLen, | |||
974 | int reference, const NrrdKernel *kern, double *kparm, | |||
975 | int verb) { | |||
976 | static const char me[]="_tenEpiRegWarp"; | |||
977 | Nrrd *ntmp, *nfin, *nslcA, *nslcB, *nwght, *nidx; | |||
978 | airArray *mop; | |||
979 | int sx, sy, sz, ni, zi, supp; | |||
980 | double hh, ss, tt, cx, cy; | |||
981 | ||||
982 | mop = airMopNew(); | |||
983 | airMopAdd(mop, ntmp=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
984 | airMopAdd(mop, nfin=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
985 | airMopAdd(mop, nslcA=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
986 | airMopAdd(mop, nslcB=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
987 | airMopAdd(mop, nwght=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
988 | airMopAdd(mop, nidx=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
989 | ||||
990 | if (verb) { | |||
991 | fprintf(stderr__stderrp, "%s:\n ", me); fflush(stderr__stderrp); | |||
992 | } | |||
993 | sx = nin[0]->axis[0].size; | |||
994 | sy = nin[0]->axis[1].size; | |||
995 | sz = nin[0]->axis[2].size; | |||
996 | cx = sx/2.0; | |||
997 | cy = sy/2.0; | |||
998 | supp = (int)kern->support(kparm); | |||
999 | if (nrrdMaybeAlloc_va(nwght, nrrdTypeFloat, 2, | |||
1000 | AIR_CAST(size_t, 2*supp)((size_t)(2*supp)), | |||
1001 | AIR_CAST(size_t, sy)((size_t)(sy))) | |||
1002 | || nrrdMaybeAlloc_va(nidx, nrrdTypeInt, 2, | |||
1003 | AIR_CAST(size_t, 2*supp)((size_t)(2*supp)), | |||
1004 | AIR_CAST(size_t, sy)((size_t)(sy)))) { | |||
1005 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: trouble allocating buffers", me); | |||
1006 | airMopError(mop); return 1; | |||
1007 | } | |||
1008 | for (ni=0; ni<ninLen; ni++) { | |||
1009 | if (verb) { | |||
1010 | fprintf(stderr__stderrp, "%2d ", ni); fflush(stderr__stderrp); | |||
1011 | } | |||
1012 | if (nrrdCopy(ndone[ni], nin[ni]) | |||
1013 | || ((!ni) && nrrdSlice(nslcB, ndone[ni], 2, 0)) /* only when 0==ni */ | |||
1014 | || nrrdAxesSwap(ntmp, nin[ni], 0, 1) | |||
1015 | || nrrdConvert(nfin, ntmp, nrrdTypeFloat)) { | |||
1016 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: trouble prepping at ni=%d", me, ni); | |||
1017 | airMopError(mop); return 1; | |||
1018 | } | |||
1019 | for (zi=0; zi<sz; zi++) { | |||
1020 | if (_tenEpiRegGetHST(&hh, &ss, &tt, reference, | |||
1021 | ni, zi, npxfr, nhst, ngrad) | |||
1022 | || nrrdSlice(nslcA, nfin, 2, zi) | |||
1023 | || _tenEpiRegSliceWarp(nslcB, nslcA, nwght, nidx, kern, kparm, | |||
1024 | hh, ss, tt, cx, cy) | |||
1025 | || nrrdSplice(ndone[ni], ndone[ni], nslcB, 2, zi)) { | |||
1026 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: trouble on slice %d if ni=%d", me, zi, ni); | |||
1027 | /* because the _tenEpiReg calls above don't use biff */ | |||
1028 | airMopError(mop); return 1; | |||
1029 | } | |||
1030 | } | |||
1031 | } | |||
1032 | if (verb) { | |||
1033 | fprintf(stderr__stderrp, "done\n"); | |||
1034 | } | |||
1035 | ||||
1036 | airMopOkay(mop); | |||
1037 | return 0; | |||
1038 | } | |||
1039 | ||||
1040 | int | |||
1041 | tenEpiRegister3D(Nrrd **nout, Nrrd **nin, unsigned int ninLen, Nrrd *_ngrad, | |||
1042 | int reference, | |||
1043 | double bwX, double bwY, double fitFrac, | |||
1044 | double DWthr, int doCC, | |||
1045 | const NrrdKernel *kern, double *kparm, | |||
1046 | int progress, int verbose) { | |||
1047 | static const char me[]="tenEpiRegister3D"; | |||
1048 | airArray *mop; | |||
1049 | Nrrd **nbuffA, **nbuffB, *npxfr, *nhst, *ngrad; | |||
1050 | int hack1, hack2; | |||
1051 | unsigned int i; | |||
1052 | ||||
1053 | hack1 = nrrdStateAlwaysSetContent; | |||
1054 | hack2 = nrrdStateDisableContent; | |||
1055 | nrrdStateAlwaysSetContent = AIR_FALSE0; | |||
1056 | nrrdStateDisableContent = AIR_TRUE1; | |||
1057 | ||||
1058 | mop = airMopNew(); | |||
1059 | if (_tenEpiRegCheck(nout, nin, ninLen, _ngrad, reference, | |||
1060 | bwX, bwY, DWthr, | |||
1061 | kern, kparm)) { | |||
1062 | biffAddf(TENtenBiffKey, "%s: trouble with input", me); | |||
1063 | airMopError(mop); return 1; | |||
1064 | } | |||
1065 | ||||
1066 | nbuffA = (Nrrd **)calloc(ninLen, sizeof(Nrrd*)); | |||
1067 | nbuffB = (Nrrd **)calloc(ninLen, sizeof(Nrrd*)); | |||
1068 | if (!( nbuffA && nbuffB )) { | |||
1069 | biffAddf(TENtenBiffKey, "%s: couldn't allocate tmp nrrd pointer arrays", me); | |||
1070 | airMopError(mop); return 1; | |||
1071 | } | |||
1072 | airMopAdd(mop, nbuffA, airFree, airMopAlways); | |||
1073 | airMopAdd(mop, nbuffB, airFree, airMopAlways); | |||
1074 | for (i=0; i<ninLen; i++) { | |||
1075 | airMopAdd(mop, nbuffA[i] = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
1076 | airMopAdd(mop, nbuffB[i] = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
1077 | nrrdAxisInfoCopy(nout[i], nin[i], NULL((void*)0), NRRD_AXIS_INFO_NONE0); | |||
1078 | } | |||
1079 | airMopAdd(mop, npxfr = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
1080 | airMopAdd(mop, nhst = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
1081 | airMopAdd(mop, ngrad = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); | |||
1082 | if (nrrdConvert(ngrad, _ngrad, nrrdTypeDouble)) { | |||
1083 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: trouble converting gradients to doubles", me); | |||
1084 | airMopError(mop); return 1; | |||
1085 | } | |||
1086 | ||||
1087 | /* ------ blur */ | |||
1088 | if (_tenEpiRegBlur(nbuffA, nin, ninLen, bwX, bwY, verbose)) { | |||
1089 | biffAddf(TENtenBiffKey, "%s: trouble %s", me, (bwX || bwY) ? "blurring" : "copying"); | |||
1090 | airMopError(mop); return 1; | |||
1091 | } | |||
1092 | if (progress && _tenEpiRegSave("regtmp-blur.nrrd", NULL((void*)0), | |||
1093 | nbuffA, ninLen, "blurred DWIs")) { | |||
1094 | airMopError(mop); return 1; | |||
1095 | } | |||
1096 | ||||
1097 | /* ------ threshold */ | |||
1098 | if (_tenEpiRegThreshold(nbuffB, nbuffA, ninLen, | |||
1099 | DWthr, verbose, progress, 1.5)) { | |||
1100 | biffAddf(TENtenBiffKey, "%s: trouble thresholding", me); | |||
1101 | airMopError(mop); return 1; | |||
1102 | } | |||
1103 | if (progress && _tenEpiRegSave("regtmp-thresh.nrrd", NULL((void*)0), | |||
1104 | nbuffB, ninLen, "thresholded DWIs")) { | |||
1105 | airMopError(mop); return 1; | |||
1106 | } | |||
1107 | ||||
1108 | /* ------ connected components */ | |||
1109 | if (doCC) { | |||
1110 | if (_tenEpiRegCC(nbuffB, ninLen, 1, verbose)) { | |||
1111 | biffAddf(TENtenBiffKey, "%s: trouble doing connected components", me); | |||
1112 | airMopError(mop); return 1; | |||
1113 | } | |||
1114 | if (progress && _tenEpiRegSave("regtmp-ccs.nrrd", NULL((void*)0), | |||
1115 | nbuffB, ninLen, "connected components")) { | |||
1116 | airMopError(mop); return 1; | |||
1117 | } | |||
1118 | } | |||
1119 | ||||
1120 | /* ------ moments */ | |||
1121 | if (_tenEpiRegMoments(nbuffA, nbuffB, ninLen, verbose)) { | |||
1122 | biffAddf(TENtenBiffKey, "%s: trouble finding moments", me); | |||
1123 | airMopError(mop); return 1; | |||
1124 | } | |||
1125 | if (progress && _tenEpiRegSave("regtmp-mom.nrrd", NULL((void*)0), | |||
1126 | nbuffA, ninLen, "moments")) { | |||
1127 | airMopError(mop); return 1; | |||
1128 | } | |||
1129 | ||||
1130 | /* ------ transforms */ | |||
1131 | if (_tenEpiRegPairXforms(npxfr, nbuffA, ninLen)) { | |||
1132 | biffAddf(TENtenBiffKey, "%s: trouble calculating transforms", me); | |||
1133 | airMopError(mop); return 1; | |||
1134 | } | |||
1135 | if (progress && _tenEpiRegSave("regtmp-pxfr.nrrd", npxfr, | |||
1136 | NULL((void*)0), 0, "pair-wise xforms")) { | |||
1137 | airMopError(mop); return 1; | |||
1138 | } | |||
1139 | ||||
1140 | if (-1 == reference) { | |||
1141 | /* ------ HST estimation */ | |||
1142 | if (_tenEpiRegEstimHST(nhst, npxfr, ninLen, ngrad)) { | |||
1143 | biffAddf(TENtenBiffKey, "%s: trouble estimating HST", me); | |||
1144 | airMopError(mop); return 1; | |||
1145 | } | |||
1146 | if (progress && _tenEpiRegSave("regtmp-hst.txt", nhst, | |||
1147 | NULL((void*)0), 0, "HST estimates")) { | |||
1148 | airMopError(mop); return 1; | |||
1149 | } | |||
1150 | ||||
1151 | if (fitFrac) { | |||
1152 | /* ------ HST parameter fitting */ | |||
1153 | if (_tenEpiRegFitHST(nhst, nbuffB, ninLen, fitFrac, progress, verbose)) { | |||
1154 | biffAddf(TENtenBiffKey, "%s: trouble fitting HST", me); | |||
1155 | airMopError(mop); return 1; | |||
1156 | } | |||
1157 | if (progress && _tenEpiRegSave("regtmp-fit-hst.txt", nhst, | |||
1158 | NULL((void*)0), 0, "fitted HST")) { | |||
1159 | airMopError(mop); return 1; | |||
1160 | } | |||
1161 | } | |||
1162 | } | |||
1163 | ||||
1164 | /* ------ doit */ | |||
1165 | if (_tenEpiRegWarp(nout, npxfr, nhst, ngrad, nin, ninLen, | |||
1166 | reference, kern, kparm, verbose)) { | |||
1167 | biffAddf(TENtenBiffKey, "%s: trouble performing final registration", me); | |||
1168 | airMopError(mop); return 1; | |||
1169 | } | |||
1170 | ||||
1171 | airMopOkay(mop); | |||
1172 | nrrdStateAlwaysSetContent = hack1; | |||
1173 | nrrdStateDisableContent = hack2; | |||
1174 | return 0; | |||
1175 | } | |||
1176 | ||||
1177 | int | |||
1178 | tenEpiRegister4D(Nrrd *_nout, Nrrd *_nin, Nrrd *_ngrad, | |||
1179 | int reference, | |||
1180 | double bwX, double bwY, double fitFrac, | |||
1181 | double DWthr, int doCC, | |||
1182 | const NrrdKernel *kern, double *kparm, | |||
1183 | int progress, int verbose) { | |||
1184 | static const char me[]="tenEpiRegister4D"; | |||
1185 | unsigned int ninIdx, ninLen, | |||
1186 | dwiAx, rangeAxisNum, rangeAxisIdx[NRRD_DIM_MAX16]; | |||
1187 | int dwiIdx; | |||
1188 | Nrrd **nout, **nin, **ndwi, **ndwiOut, *ngrad, *ndwigrad; | |||
1189 | airArray *mop; | |||
1190 | double *grad, *dwigrad, glen; | |||
1191 | ||||
1192 | if (!(_nout && _nin)) { | |||
| ||||
1193 | biffAddf(TENtenBiffKey, "%s: got NULL pointer", me); | |||
1194 | return 1; | |||
1195 | } | |||
1196 | if (4 != _nin->dim) { | |||
1197 | biffAddf(TENtenBiffKey, "%s: need a 4-D input array, not %d-D", me, _nin->dim); | |||
1198 | return 1; | |||
1199 | } | |||
1200 | if (tenGradientCheck(_ngrad, nrrdTypeDefault, 6 /* <-- HEY: not sure */)) { | |||
1201 | biffAddf(TENtenBiffKey, "%s: problem with given gradient list", me); | |||
1202 | return 1; | |||
1203 | } | |||
1204 | ||||
1205 | rangeAxisNum = nrrdRangeAxesGet(_nin, rangeAxisIdx); | |||
1206 | if (0 == rangeAxisNum) { | |||
1207 | /* we fall back on old behavior */ | |||
1208 | dwiAx = 0; | |||
1209 | } else if (1 == rangeAxisNum) { | |||
1210 | /* thankfully there's exactly one range axis */ | |||
1211 | dwiAx = rangeAxisIdx[0]; | |||
1212 | } else { | |||
1213 | biffAddf(TENtenBiffKey, "%s: have %u range axes instead of 1, don't know which " | |||
1214 | "is DWI axis", me, rangeAxisNum); | |||
1215 | return 1; | |||
1216 | } | |||
1217 | ||||
1218 | ninLen = _nin->axis[dwiAx].size; | |||
1219 | /* outdated | |||
1220 | if (!( AIR_IN_CL(6, ninLen, 120) )) { | |||
1221 | biffAddf(TEN, "%s: %u (size of axis %u, and # DWIs) is unreasonable", | |||
1222 | me, ninLen, dwiAx); | |||
1223 | return 1; | |||
1224 | } | |||
1225 | */ | |||
1226 | if (ninLen != _ngrad->axis[1].size) { | |||
1227 | char stmp[AIR_STRLEN_SMALL(128+1)]; | |||
1228 | biffAddf(TENtenBiffKey, "%s: ninLen %u != # grads %s", me, ninLen, | |||
1229 | airSprintSize_t(stmp, _ngrad->axis[1].size)); | |||
1230 | return 1; | |||
1231 | } | |||
1232 | ||||
1233 | mop = airMopNew(); | |||
1234 | ngrad = nrrdNew(); | |||
1235 | ndwigrad = nrrdNew(); | |||
1236 | airMopAdd(mop, ngrad, (airMopper)nrrdNuke, airMopAlways); | |||
1237 | airMopAdd(mop, ndwigrad, (airMopper)nrrdNuke, airMopAlways); | |||
1238 | if (nrrdConvert(ngrad, _ngrad, nrrdTypeDouble) | |||
1239 | || nrrdConvert(ndwigrad, _ngrad, nrrdTypeDouble)) { /* HACK applies */ | |||
1240 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: trouble converting gradients to doubles", me); | |||
1241 | airMopError(mop); return 1; | |||
1242 | } | |||
1243 | ||||
1244 | nin = (Nrrd **)calloc(ninLen, sizeof(Nrrd*)); | |||
1245 | ndwi = (Nrrd **)calloc(ninLen, sizeof(Nrrd*)); | |||
1246 | nout = (Nrrd **)calloc(ninLen, sizeof(Nrrd*)); | |||
1247 | ndwiOut = (Nrrd **)calloc(ninLen, sizeof(Nrrd*)); | |||
1248 | if (!(nin && ndwi && nout && ndwiOut)) { | |||
1249 | biffAddf(TENtenBiffKey, "%s: trouble allocating local arrays", me); | |||
| ||||
1250 | airMopError(mop); return 1; | |||
1251 | } | |||
1252 | airMopAdd(mop, nin, airFree, airMopAlways); | |||
1253 | airMopAdd(mop, ndwi, airFree, airMopAlways); | |||
1254 | airMopAdd(mop, nout, airFree, airMopAlways); | |||
1255 | airMopAdd(mop, ndwiOut, airFree, airMopAlways); | |||
1256 | dwiIdx = -1; | |||
1257 | grad = AIR_CAST(double *, ngrad->data)((double *)(ngrad->data)); | |||
1258 | dwigrad = AIR_CAST(double *, ndwigrad->data)((double *)(ndwigrad->data)); | |||
1259 | for (ninIdx=0; ninIdx<ninLen; ninIdx++) { | |||
1260 | glen = ELL_3V_LEN(grad + 3*ninIdx)(sqrt((((grad + 3*ninIdx))[0]*((grad + 3*ninIdx))[0] + ((grad + 3*ninIdx))[1]*((grad + 3*ninIdx))[1] + ((grad + 3*ninIdx)) [2]*((grad + 3*ninIdx))[2]))); | |||
1261 | if (-1 != reference || glen > 0.0) { | |||
1262 | /* we're not allowed to use only those images that are truly DWIs, | |||
1263 | or, | |||
1264 | (we are so allowed and) this is a true DWI */ | |||
1265 | dwiIdx++; | |||
1266 | ndwi[dwiIdx] = nrrdNew(); | |||
1267 | ndwiOut[dwiIdx] = nrrdNew(); | |||
1268 | airMopAdd(mop, ndwi[dwiIdx], (airMopper)nrrdNuke, airMopAlways); | |||
1269 | airMopAdd(mop, ndwiOut[dwiIdx], (airMopper)nrrdNuke, airMopAlways); | |||
1270 | if (nrrdSlice(ndwi[dwiIdx], _nin, dwiAx, | |||
1271 | AIR_CAST(unsigned int, ninIdx)((unsigned int)(ninIdx)))) { | |||
1272 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: trouble slicing at %d on axis %u", | |||
1273 | me, ninIdx, dwiAx); | |||
1274 | airMopError(mop); return 1; | |||
1275 | } | |||
1276 | /* NOTE: this works because dwiIdx <= ninIdx */ | |||
1277 | ELL_3V_COPY(dwigrad + 3*dwiIdx, grad + 3*ninIdx)((dwigrad + 3*dwiIdx)[0] = (grad + 3*ninIdx)[0], (dwigrad + 3 *dwiIdx)[1] = (grad + 3*ninIdx)[1], (dwigrad + 3*dwiIdx)[2] = (grad + 3*ninIdx)[2]); | |||
1278 | } else { | |||
1279 | /* we are only looking at true DWIs, and this isn't one of them */ | |||
1280 | continue; | |||
1281 | } | |||
1282 | } | |||
1283 | if (-1 == dwiIdx) { | |||
1284 | biffAddf(TENtenBiffKey, "%s: somehow got no DWIs", me); | |||
1285 | airMopError(mop); return 1; | |||
1286 | } | |||
1287 | /* HEY: HACK! */ | |||
1288 | ndwigrad->axis[1].size = 1 + AIR_CAST(unsigned int, dwiIdx)((unsigned int)(dwiIdx)); | |||
1289 | if (tenEpiRegister3D(ndwiOut, ndwi, ndwigrad->axis[1].size, ndwigrad, | |||
1290 | reference, | |||
1291 | bwX, bwY, fitFrac, DWthr, | |||
1292 | doCC, | |||
1293 | kern, kparm, | |||
1294 | progress, verbose)) { | |||
1295 | biffAddf(TENtenBiffKey, "%s: trouble", me); | |||
1296 | airMopError(mop); return 1; | |||
1297 | } | |||
1298 | dwiIdx = -1; | |||
1299 | for (ninIdx=0; ninIdx<ninLen; ninIdx++) { | |||
1300 | glen = ELL_3V_LEN(grad + 3*ninIdx)(sqrt((((grad + 3*ninIdx))[0]*((grad + 3*ninIdx))[0] + ((grad + 3*ninIdx))[1]*((grad + 3*ninIdx))[1] + ((grad + 3*ninIdx)) [2]*((grad + 3*ninIdx))[2]))); | |||
1301 | if (-1 != reference || glen > 0.0) { | |||
1302 | /* we're not allowed to use only those images that are truly DWIs, | |||
1303 | or, | |||
1304 | (we are so allowed and) this is a true DWI */ | |||
1305 | dwiIdx++; | |||
1306 | nout[ninIdx] = ndwiOut[dwiIdx]; | |||
1307 | /* | |||
1308 | fprintf(stderr, "!%s: (A) nout[%u] = %p\n", me, ninIdx, nout[ninIdx]); | |||
1309 | */ | |||
1310 | } else { | |||
1311 | /* we are only looking at true DWIs, and this isn't one of them */ | |||
1312 | nout[ninIdx] = nrrdNew(); | |||
1313 | airMopAdd(mop, nout[ninIdx], (airMopper)nrrdNuke, airMopAlways); | |||
1314 | if (nrrdSlice(nout[ninIdx], _nin, dwiAx, ninIdx)) { | |||
1315 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: trouble slicing at %d on axis %u", | |||
1316 | me, dwiIdx, dwiAx); | |||
1317 | airMopError(mop); return 1; | |||
1318 | } | |||
1319 | /* | |||
1320 | fprintf(stderr, "!%s: (B) nout[%u] = %p\n", me, ninIdx, nout[ninIdx]); | |||
1321 | */ | |||
1322 | } | |||
1323 | } | |||
1324 | if (nrrdJoin(_nout, (const Nrrd*const*)nout, ninLen, dwiAx, AIR_TRUE1)) { | |||
1325 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s: trouble joining output", me); | |||
1326 | airMopError(mop); return 1; | |||
1327 | } | |||
1328 | nrrdAxisInfoCopy(_nout, _nin, NULL((void*)0), NRRD_AXIS_INFO_NONE0); | |||
1329 | if (nrrdBasicInfoCopy(_nout, _nin, | |||
1330 | NRRD_BASIC_INFO_DATA_BIT(1<< 1) | |||
1331 | | NRRD_BASIC_INFO_TYPE_BIT(1<< 2) | |||
1332 | | NRRD_BASIC_INFO_BLOCKSIZE_BIT(1<< 3) | |||
1333 | | NRRD_BASIC_INFO_DIMENSION_BIT(1<< 4) | |||
1334 | | NRRD_BASIC_INFO_CONTENT_BIT(1<< 5) | |||
1335 | | NRRD_BASIC_INFO_COMMENTS_BIT(1<<14))) { | |||
1336 | /* note that we're ALWAYS copying the key/value pairs- its just | |||
1337 | too annoying to have to set nrrdStateKeyValuePairsPropagate | |||
1338 | in order for the DWI-specific key/value pairs to be set */ | |||
1339 | biffMovef(TENtenBiffKey, NRRDnrrdBiffKey, "%s:", me); | |||
1340 | airMopError(mop); return 1; | |||
1341 | } | |||
1342 | ||||
1343 | airMopOkay(mop); | |||
1344 | return 0; | |||
1345 | } |