File: | src/nrrd/resampleNrrd.c |
Location: | line 404, column 9 |
Description: | Potential leak of memory pointed to by 'indx' |
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 "nrrd.h" | |||
25 | #include "privateNrrd.h" | |||
26 | ||||
27 | /* | |||
28 | (this was written before airMopSub ... ) | |||
29 | learned: if you start using airMop stuff, and you register a free, but | |||
30 | then you free the memory yourself, YOU HAVE GOT TO register a NULL in | |||
31 | place of the original free. The next malloc may end up at the same | |||
32 | address as what you just freed, and if you want this memory to NOT be | |||
33 | mopped up, then you'll be confused with the original registered free | |||
34 | goes into effect and mops it up for you, even though YOU NEVER | |||
35 | REGISTERED a free for the second malloc. If you want simple stupid | |||
36 | tools, you have to treat them accordingly (be extremely careful with | |||
37 | fire). | |||
38 | ||||
39 | learned: well, duh. The reason to use: | |||
40 | ||||
41 | for (I=0; I<numOut; I++) { | |||
42 | ||||
43 | instead of | |||
44 | ||||
45 | for (I=0; I<=numOut-1; I++) { | |||
46 | ||||
47 | is that if numOut is of an unsigned type and has value 0, then these | |||
48 | two will have very different results! | |||
49 | ||||
50 | */ | |||
51 | ||||
52 | int | |||
53 | nrrdSimpleResample(Nrrd *nout, const Nrrd *nin, | |||
54 | const NrrdKernel *kernel, const double *parm, | |||
55 | const size_t *samples, const double *scalings) { | |||
56 | static const char me[]="nrrdSimpleResample"; | |||
57 | NrrdResampleInfo *info; | |||
58 | int p, np, center; | |||
59 | unsigned ai; | |||
60 | ||||
61 | if (!(nout && nin && kernel && (samples || scalings))) { | |||
62 | biffAddf(NRRDnrrdBiffKey, "%s: not NULL pointer", me); | |||
63 | return 1; | |||
64 | } | |||
65 | if (!(info = nrrdResampleInfoNew())) { | |||
66 | biffAddf(NRRDnrrdBiffKey, "%s: can't allocate resample info struct", me); | |||
67 | return 1; | |||
68 | } | |||
69 | ||||
70 | np = kernel->numParm; | |||
71 | for (ai=0; ai<nin->dim; ai++) { | |||
72 | double axmin, axmax; | |||
73 | info->kernel[ai] = kernel; | |||
74 | if (samples) { | |||
75 | info->samples[ai] = samples[ai]; | |||
76 | } else { | |||
77 | center = _nrrdCenter(nin->axis[ai].center); | |||
78 | if (nrrdCenterCell == center) { | |||
79 | info->samples[ai] = (size_t)(nin->axis[ai].size*scalings[ai]); | |||
80 | } else { | |||
81 | info->samples[ai] = (size_t)((nin->axis[ai].size - 1) | |||
82 | *scalings[ai]) + 1; | |||
83 | } | |||
84 | } | |||
85 | for (p=0; p<np; p++) | |||
86 | info->parm[ai][p] = parm[p]; | |||
87 | /* set the min/max for this axis if not already set to something */ | |||
88 | if (!( AIR_EXISTS(nin->axis[ai].min)(((int)(!((nin->axis[ai].min) - (nin->axis[ai].min))))) && AIR_EXISTS(nin->axis[ai].max)(((int)(!((nin->axis[ai].max) - (nin->axis[ai].max))))) )) { | |||
89 | /* HEY: started as copy/paste of body of nrrdAxisInfoMinMaxSet, | |||
90 | because we wanted to enable const-correctness, and this | |||
91 | function had previously been setting per-axis min/max in | |||
92 | *input* when it hasn't been already set */ | |||
93 | double spacing; | |||
94 | center = _nrrdCenter2(nin->axis[ai].center, nrrdDefaultCenter); | |||
95 | spacing = nin->axis[ai].spacing; | |||
96 | if (!AIR_EXISTS(spacing)(((int)(!((spacing) - (spacing)))))) | |||
97 | spacing = nrrdDefaultSpacing; | |||
98 | if (nrrdCenterCell == center) { | |||
99 | axmin = 0; | |||
100 | axmax = spacing*nin->axis[ai].size; | |||
101 | } else { | |||
102 | axmin = 0; | |||
103 | axmax = spacing*(nin->axis[ai].size - 1); | |||
104 | } | |||
105 | } else { | |||
106 | axmin = nin->axis[ai].min; | |||
107 | axmax = nin->axis[ai].max; | |||
108 | } | |||
109 | info->min[ai] = axmin; | |||
110 | info->max[ai] = axmax; | |||
111 | } | |||
112 | /* we go with the defaults (enstated by _nrrdResampleInfoInit()) | |||
113 | for all the remaining fields */ | |||
114 | ||||
115 | if (nrrdSpatialResample(nout, nin, info)) { | |||
116 | biffAddf(NRRDnrrdBiffKey, "%s:", me); | |||
117 | return 1; | |||
118 | } | |||
119 | ||||
120 | info = nrrdResampleInfoNix(info); | |||
121 | return 0; | |||
122 | } | |||
123 | ||||
124 | /* | |||
125 | ** _nrrdResampleCheckInfo() | |||
126 | ** | |||
127 | ** checks validity of given NrrdResampleInfo *info: | |||
128 | ** - all required parameters exist | |||
129 | ** - both min[d] and max[d] for all axes d | |||
130 | */ | |||
131 | int | |||
132 | _nrrdResampleCheckInfo(const Nrrd *nin, const NrrdResampleInfo *info) { | |||
133 | static const char me[] = "_nrrdResampleCheckInfo"; | |||
134 | const NrrdKernel *k; | |||
135 | int center, p, np; | |||
136 | unsigned int ai, minsmp; | |||
137 | char stmp[2][AIR_STRLEN_SMALL(128+1)]; | |||
138 | ||||
139 | if (nrrdTypeBlock == nin->type || nrrdTypeBlock == info->type) { | |||
140 | biffAddf(NRRDnrrdBiffKey, "%s: can't resample to or from type %s", me, | |||
141 | airEnumStr(nrrdType, nrrdTypeBlock)); | |||
142 | return 1; | |||
143 | } | |||
144 | if (nrrdBoundaryUnknown == info->boundary) { | |||
145 | biffAddf(NRRDnrrdBiffKey, "%s: didn't set boundary behavior\n", me); | |||
146 | return 1; | |||
147 | } | |||
148 | if (nrrdBoundaryPad == info->boundary && !AIR_EXISTS(info->padValue)(((int)(!((info->padValue) - (info->padValue)))))) { | |||
149 | biffAddf(NRRDnrrdBiffKey, | |||
150 | "%s: asked for boundary padding, but no pad value set\n", me); | |||
151 | return 1; | |||
152 | } | |||
153 | for (ai=0; ai<nin->dim; ai++) { | |||
154 | k = info->kernel[ai]; | |||
155 | /* we only care about the axes being resampled */ | |||
156 | if (!k) | |||
157 | continue; | |||
158 | if (!(info->samples[ai] > 0)) { | |||
159 | biffAddf(NRRDnrrdBiffKey, "%s: axis %d # samples (%s) invalid", me, ai, | |||
160 | airSprintSize_t(stmp[0], info->samples[ai])); | |||
161 | return 1; | |||
162 | } | |||
163 | if (!( AIR_EXISTS(nin->axis[ai].min)(((int)(!((nin->axis[ai].min) - (nin->axis[ai].min))))) && AIR_EXISTS(nin->axis[ai].max)(((int)(!((nin->axis[ai].max) - (nin->axis[ai].max))))) )) { | |||
164 | biffAddf(NRRDnrrdBiffKey, "%s: input nrrd's axis %d min,max have not " | |||
165 | "both been set", me, ai); | |||
166 | return 1; | |||
167 | } | |||
168 | if (!( AIR_EXISTS(info->min[ai])(((int)(!((info->min[ai]) - (info->min[ai]))))) && AIR_EXISTS(info->max[ai])(((int)(!((info->max[ai]) - (info->max[ai]))))) )) { | |||
169 | biffAddf(NRRDnrrdBiffKey, "%s: info's axis %d min,max not both set", me, ai); | |||
170 | return 1; | |||
171 | } | |||
172 | np = k->numParm; | |||
173 | for (p=0; p<np; p++) { | |||
174 | if (!AIR_EXISTS(info->parm[ai][p])(((int)(!((info->parm[ai][p]) - (info->parm[ai][p])))))) { | |||
175 | biffAddf(NRRDnrrdBiffKey, "%s: didn't set parameter %d (of %d) for axis %d\n", | |||
176 | me, p, np, ai); | |||
177 | return 1; | |||
178 | } | |||
179 | } | |||
180 | center = _nrrdCenter(nin->axis[ai].center); | |||
181 | minsmp = nrrdCenterCell == center ? 1 : 2; | |||
182 | if (!( nin->axis[ai].size >= minsmp && info->samples[ai] >= minsmp )) { | |||
183 | biffAddf(NRRDnrrdBiffKey, "%s: axis %d # input samples (%s) or output samples (%s) " | |||
184 | " invalid for %s centering", me, ai, | |||
185 | airSprintSize_t(stmp[0], nin->axis[ai].size), | |||
186 | airSprintSize_t(stmp[1], info->samples[ai]), | |||
187 | airEnumStr(nrrdCenter, center)); | |||
188 | return 1; | |||
189 | } | |||
190 | } | |||
191 | return 0; | |||
192 | } | |||
193 | ||||
194 | /* | |||
195 | ** _nrrdResampleComputePermute() | |||
196 | ** | |||
197 | ** figures out information related to how the axes in a nrrd are | |||
198 | ** permuted during resampling: permute, topRax, botRax, passes, ax[][], sz[][] | |||
199 | */ | |||
200 | void | |||
201 | _nrrdResampleComputePermute(unsigned int permute[], | |||
202 | unsigned int ax[NRRD_DIM_MAX16][NRRD_DIM_MAX16], | |||
203 | size_t sz[NRRD_DIM_MAX16][NRRD_DIM_MAX16], | |||
204 | int *topRax, | |||
205 | int *botRax, | |||
206 | unsigned int *passes, | |||
207 | const Nrrd *nin, | |||
208 | const NrrdResampleInfo *info) { | |||
209 | /* char me[]="_nrrdResampleComputePermute"; */ | |||
210 | unsigned int bi, ai, pi; | |||
211 | ||||
212 | /* what are the first (top) and last (bottom) axes being resampled? */ | |||
213 | *topRax = *botRax = -1; | |||
214 | for (ai=0; ai<nin->dim; ai++) { | |||
215 | if (info->kernel[ai]) { | |||
216 | if (*topRax < 0) { | |||
217 | *topRax = ai; | |||
218 | } | |||
219 | *botRax = ai; | |||
220 | } | |||
221 | } | |||
222 | ||||
223 | /* figure out total number of passes needed, and construct the | |||
224 | permute[] array. permute[i] = j means that the axis in position | |||
225 | i of the old array will be in position j of the new one | |||
226 | (permute[] answers "where do I put this", not "what do I put here"). | |||
227 | */ | |||
228 | *passes = bi = 0; | |||
229 | for (ai=0; ai<nin->dim; ai++) { | |||
230 | if (info->kernel[ai]) { | |||
231 | do { | |||
232 | bi = AIR_MOD((int)bi+1, (int)nin->dim)(((int)bi+1)%((int)nin->dim) >= 0 ? ((int)bi+1)%((int)nin ->dim) : (int)nin->dim + ((int)bi+1)%((int)nin->dim) ); /* HEY scrutinize casts */ | |||
233 | } while (!info->kernel[bi]); | |||
234 | permute[bi] = ai; | |||
235 | *passes += 1; | |||
236 | } else { | |||
237 | permute[ai] = ai; | |||
238 | bi += bi == ai; | |||
239 | } | |||
240 | } | |||
241 | permute[nin->dim] = nin->dim; | |||
242 | if (!*passes) { | |||
243 | /* none of the kernels was non-NULL */ | |||
244 | return; | |||
245 | } | |||
246 | ||||
247 | /* | |||
248 | fprintf(stderr, "%s: permute:\n", me); | |||
249 | for (d=0; d<nin->dim; d++) { | |||
250 | fprintf(stderr, " permute[%d] = %d\n", d, permute[ai]); | |||
251 | } | |||
252 | */ | |||
253 | ||||
254 | /* create array of how the axes will be arranged in each pass ("ax"), | |||
255 | and create array of how big each axes is in each pass ("sz"). | |||
256 | The input to pass i will have axis layout described in ax[i] and | |||
257 | axis sizes described in sz[i] */ | |||
258 | for (ai=0; ai<nin->dim; ai++) { | |||
259 | ax[0][ai] = ai; | |||
260 | sz[0][ai] = nin->axis[ai].size; | |||
261 | } | |||
262 | for (pi=0; pi<*passes; pi++) { | |||
263 | for (ai=0; ai<nin->dim; ai++) { | |||
264 | ax[pi+1][permute[ai]] = ax[pi][ai]; | |||
265 | if (ai == (unsigned int)*topRax) { /* HEY scrutinize casts */ | |||
266 | /* this is the axis which is getting resampled, | |||
267 | so the number of samples is potentially changing */ | |||
268 | sz[pi+1][permute[ai]] = (info->kernel[ax[pi][ai]] | |||
269 | ? info->samples[ax[pi][ai]] | |||
270 | : sz[pi][ai]); | |||
271 | } else { | |||
272 | /* this axis is just a shuffled version of the | |||
273 | previous axis; no resampling this pass. | |||
274 | Note: this case also includes axes which aren't | |||
275 | getting resampled whatsoever */ | |||
276 | sz[pi+1][permute[ai]] = sz[pi][ai]; | |||
277 | } | |||
278 | } | |||
279 | } | |||
280 | ||||
281 | return; | |||
282 | } | |||
283 | ||||
284 | /* | |||
285 | ** _nrrdResampleMakeWeightIndex() | |||
286 | ** | |||
287 | ** _allocate_ and fill the arrays of indices and weights that are | |||
288 | ** needed to process all the scanlines along a given axis; also | |||
289 | ** be so kind as to set the sampling ratio (<1: downsampling, | |||
290 | ** new sample spacing larger, >1: upsampling, new sample spacing smaller) | |||
291 | ** | |||
292 | ** returns "dotLen", the number of input samples which are required | |||
293 | ** for resampling this axis, or 0 if there was an error. Uses biff. | |||
294 | */ | |||
295 | int | |||
296 | _nrrdResampleMakeWeightIndex(nrrdResample_t **weightP, | |||
297 | int **indexP, double *ratioP, | |||
298 | const Nrrd *nin, const NrrdResampleInfo *info, | |||
299 | unsigned int ai) { | |||
300 | static const char me[]="_nrrdResampleMakeWeightIndex"; | |||
301 | int sizeIn, sizeOut, center, dotLen, halfLen, *indx, base, idx; | |||
302 | nrrdResample_t minIn, maxIn, minOut, maxOut, spcIn, spcOut, | |||
303 | ratio, support, integral, pos, idxD, wght; | |||
304 | nrrdResample_t *weight; | |||
305 | double parm[NRRD_KERNEL_PARMS_NUM8]; | |||
306 | ||||
307 | int e, i; | |||
308 | ||||
309 | if (!(info->kernel[ai])) { | |||
| ||||
310 | biffAddf(NRRDnrrdBiffKey, "%s: don't see a kernel for dimension %d", me, ai); | |||
311 | *weightP = NULL((void*)0); *indexP = NULL((void*)0); return 0; | |||
312 | } | |||
313 | ||||
314 | center = _nrrdCenter(nin->axis[ai].center); | |||
315 | sizeIn = AIR_CAST(int, nin->axis[ai].size)((int)(nin->axis[ai].size)); | |||
316 | sizeOut = AIR_CAST(int, info->samples[ai])((int)(info->samples[ai])); | |||
317 | minIn = AIR_CAST(nrrdResample_t, nin->axis[ai].min)((nrrdResample_t)(nin->axis[ai].min)); | |||
318 | maxIn = AIR_CAST(nrrdResample_t, nin->axis[ai].max)((nrrdResample_t)(nin->axis[ai].max)); | |||
319 | minOut = AIR_CAST(nrrdResample_t, info->min[ai])((nrrdResample_t)(info->min[ai])); | |||
320 | maxOut = AIR_CAST(nrrdResample_t, info->max[ai])((nrrdResample_t)(info->max[ai])); | |||
321 | spcIn = NRRD_SPACING(center, minIn, maxIn, sizeIn)(nrrdCenterCell == center ? ((maxIn) - (minIn))/((double)(sizeIn )) : ((maxIn) - (minIn))/(((double)((sizeIn)- 1)))); | |||
322 | spcOut = NRRD_SPACING(center, minOut, maxOut, sizeOut)(nrrdCenterCell == center ? ((maxOut) - (minOut))/((double)(sizeOut )) : ((maxOut) - (minOut))/(((double)((sizeOut)- 1)))); | |||
323 | *ratioP = ratio = spcIn/spcOut; | |||
324 | support = AIR_CAST(nrrdResample_t,((nrrdResample_t)(info->kernel[ai]->support(info->parm [ai]))) | |||
325 | info->kernel[ai]->support(info->parm[ai]))((nrrdResample_t)(info->kernel[ai]->support(info->parm [ai]))); | |||
326 | integral = AIR_CAST(nrrdResample_t,((nrrdResample_t)(info->kernel[ai]->integral(info->parm [ai]))) | |||
327 | info->kernel[ai]->integral(info->parm[ai]))((nrrdResample_t)(info->kernel[ai]->integral(info->parm [ai]))); | |||
328 | /* | |||
329 | fprintf(stderr, | |||
330 | "!%s(%d): size{In,Out} = %d, %d, support = %f; ratio = %f\n", | |||
331 | me, d, sizeIn, sizeOut, support, ratio); | |||
332 | */ | |||
333 | if (ratio > 1) { | |||
334 | /* if upsampling, we need only as many samples as needed for | |||
335 | interpolation with the given kernel */ | |||
336 | dotLen = (int)(2*ceil(support)); | |||
337 | } else { | |||
338 | /* if downsampling, we need to use all the samples covered by | |||
339 | the stretched out version of the kernel */ | |||
340 | if (info->cheap) { | |||
341 | dotLen = (int)(2*ceil(support)); | |||
342 | } else { | |||
343 | dotLen = (int)(2*ceil(support/ratio)); | |||
344 | } | |||
345 | } | |||
346 | /* | |||
347 | fprintf(stderr, "!%s(%d): dotLen = %d\n", me, d, dotLen); | |||
348 | */ | |||
349 | ||||
350 | weight = AIR_CALLOC(sizeOut*dotLen, nrrdResample_t)(nrrdResample_t*)(calloc((sizeOut*dotLen), sizeof(nrrdResample_t ))); | |||
351 | if (!weight) { | |||
352 | biffAddf(NRRDnrrdBiffKey, "%s: can't allocate weight array", me); | |||
353 | *weightP = NULL((void*)0); *indexP = NULL((void*)0); return 0; | |||
354 | } | |||
355 | indx = AIR_CALLOC(sizeOut*dotLen, int)(int*)(calloc((sizeOut*dotLen), sizeof(int))); | |||
356 | if (!indx) { | |||
357 | biffAddf(NRRDnrrdBiffKey, "%s: can't allocate index arrays", me); | |||
358 | *weightP = NULL((void*)0); *indexP = NULL((void*)0); return 0; | |||
359 | } | |||
360 | ||||
361 | /* calculate sample locations and do first pass on indices */ | |||
362 | halfLen = dotLen/2; | |||
363 | for (i=0; i<sizeOut; i++) { | |||
364 | pos = AIR_CAST(nrrdResample_t,((nrrdResample_t)((nrrdCenterCell == (center) ? ( ((double)(( (maxOut)))-(((minOut))))*((double)(((i)) + 0.5)-(0)) / ((double )(((sizeOut)))-(0)) + (((minOut)))) : ( ((double)(((maxOut))) -(((minOut))))*((double)(((i)))-(0)) / ((double)(((sizeOut))- 1)-(0)) + (((minOut))))))) | |||
365 | NRRD_POS(center, minOut, maxOut, sizeOut, i))((nrrdResample_t)((nrrdCenterCell == (center) ? ( ((double)(( (maxOut)))-(((minOut))))*((double)(((i)) + 0.5)-(0)) / ((double )(((sizeOut)))-(0)) + (((minOut)))) : ( ((double)(((maxOut))) -(((minOut))))*((double)(((i)))-(0)) / ((double)(((sizeOut))- 1)-(0)) + (((minOut))))))); | |||
366 | idxD = AIR_CAST(nrrdResample_t,((nrrdResample_t)((nrrdCenterCell == (center) ? (( ((double)( ((sizeIn)))-(0))*((double)(((pos)))-(((minIn)))) / ((double)( ((maxIn)))-(((minIn)))) + (0)) - 0.5) : ( ((double)(((sizeIn) )-1)-(0))*((double)(((pos)))-(((minIn)))) / ((double)(((maxIn )))-(((minIn)))) + (0))))) | |||
367 | NRRD_IDX(center, minIn, maxIn, sizeIn, pos))((nrrdResample_t)((nrrdCenterCell == (center) ? (( ((double)( ((sizeIn)))-(0))*((double)(((pos)))-(((minIn)))) / ((double)( ((maxIn)))-(((minIn)))) + (0)) - 0.5) : ( ((double)(((sizeIn) )-1)-(0))*((double)(((pos)))-(((minIn)))) / ((double)(((maxIn )))-(((minIn)))) + (0))))); | |||
368 | base = (int)floor(idxD) - halfLen + 1; | |||
369 | for (e=0; e<dotLen; e++) { | |||
370 | indx[e + dotLen*i] = base + e; | |||
371 | weight[e + dotLen*i] = idxD - indx[e + dotLen*i]; | |||
372 | } | |||
373 | /* ******** | |||
374 | if (!i) { | |||
375 | fprintf(stderr, "%s: sample locations:\n", me); | |||
376 | } | |||
377 | fprintf(stderr, "%s: %d (sample locations)\n ", me, i); | |||
378 | for (e=0; e<dotLen; e++) { | |||
379 | fprintf(stderr, "%d/%g ", indx[e + dotLen*i], weight[e + dotLen*i]); | |||
380 | } | |||
381 | fprintf(stderr, "\n"); | |||
382 | ******** */ | |||
383 | } | |||
384 | ||||
385 | /* figure out what to do with the out-of-range indices */ | |||
386 | for (i=0; i<dotLen*sizeOut; i++) { | |||
387 | idx = indx[i]; | |||
388 | if (!AIR_IN_CL(0, idx, sizeIn-1)((0) <= (idx) && (idx) <= (sizeIn-1))) { | |||
389 | switch(info->boundary) { | |||
390 | case nrrdBoundaryPad: | |||
391 | case nrrdBoundaryWeight: /* this will be further handled later */ | |||
392 | idx = sizeIn; | |||
393 | break; | |||
394 | case nrrdBoundaryBleed: | |||
395 | idx = AIR_CLAMP(0, idx, sizeIn-1)((idx) < (0) ? (0) : ((idx) > (sizeIn-1) ? (sizeIn-1) : (idx))); | |||
396 | break; | |||
397 | case nrrdBoundaryWrap: | |||
398 | idx = AIR_MOD(idx, sizeIn)((idx)%(sizeIn) >= 0 ? (idx)%(sizeIn) : sizeIn + (idx)%(sizeIn )); | |||
399 | break; | |||
400 | case nrrdBoundaryMirror: | |||
401 | idx = _nrrdMirror_32(sizeIn, idx); | |||
402 | break; | |||
403 | default: | |||
404 | biffAddf(NRRDnrrdBiffKey, "%s: boundary behavior %d unknown/unimplemented", | |||
| ||||
405 | me, info->boundary); | |||
406 | *weightP = NULL((void*)0); *indexP = NULL((void*)0); return 0; | |||
407 | } | |||
408 | indx[i] = idx; | |||
409 | } | |||
410 | } | |||
411 | ||||
412 | /* run the sample locations through the chosen kernel. We play a | |||
413 | sneaky trick on the kernel parameter 0 in case of downsampling | |||
414 | to create the blurring of the old index space, but only if !cheap */ | |||
415 | memcpy(parm, info->parm[ai], NRRD_KERNEL_PARMS_NUM*sizeof(double))__builtin___memcpy_chk (parm, info->parm[ai], 8*sizeof(double ), __builtin_object_size (parm, 0)); | |||
416 | if (ratio < 1 && !(info->cheap)) { | |||
417 | parm[0] /= ratio; | |||
418 | } | |||
419 | info->kernel[ai]->EVALNevalN_d(weight, weight, dotLen*sizeOut, parm); | |||
420 | ||||
421 | /* ******** | |||
422 | for (i=0; i<sizeOut; i++) { | |||
423 | fprintf(stderr, "%s: %d (sample weights)\n ", me, i); | |||
424 | for (e=0; e<dotLen; e++) { | |||
425 | fprintf(stderr, "%d/%g ", indx[e + dotLen*i], weight[e + dotLen*i]); | |||
426 | } | |||
427 | fprintf(stderr, "\n"); | |||
428 | } | |||
429 | ******** */ | |||
430 | ||||
431 | if (nrrdBoundaryWeight == info->boundary) { | |||
432 | if (integral) { | |||
433 | /* above, we set to sizeIn all the indices that were out of | |||
434 | range. We now use that to determine the sum of the weights | |||
435 | for the indices that were in-range */ | |||
436 | for (i=0; i<sizeOut; i++) { | |||
437 | wght = 0; | |||
438 | for (e=0; e<dotLen; e++) { | |||
439 | if (sizeIn != indx[e + dotLen*i]) { | |||
440 | wght += weight[e + dotLen*i]; | |||
441 | } | |||
442 | } | |||
443 | for (e=0; e<dotLen; e++) { | |||
444 | idx = indx[e + dotLen*i]; | |||
445 | if (sizeIn != idx) { | |||
446 | weight[e + dotLen*i] *= integral/wght; | |||
447 | } else { | |||
448 | weight[e + dotLen*i] = 0; | |||
449 | } | |||
450 | } | |||
451 | } | |||
452 | } | |||
453 | } else { | |||
454 | /* try to remove ripple/grating on downsampling */ | |||
455 | /* if (ratio < 1 && info->renormalize && integral) { */ | |||
456 | if (info->renormalize && integral) { | |||
457 | for (i=0; i<sizeOut; i++) { | |||
458 | wght = 0; | |||
459 | for (e=0; e<dotLen; e++) { | |||
460 | wght += weight[e + dotLen*i]; | |||
461 | } | |||
462 | if (wght) { | |||
463 | for (e=0; e<dotLen; e++) { | |||
464 | /* this used to normalize the weights so that they summed | |||
465 | to integral ("*= integral/wght"), which meant that if | |||
466 | you use a very truncated Gaussian, then your over-all | |||
467 | image brightness goes down. This seems very contrary | |||
468 | to the whole point of renormalization. */ | |||
469 | weight[e + dotLen*i] *= AIR_CAST(nrrdResample_t, 1.0/wght)((nrrdResample_t)(1.0/wght)); | |||
470 | } | |||
471 | } | |||
472 | } | |||
473 | } | |||
474 | } | |||
475 | /* ******** | |||
476 | fprintf(stderr, "%s: sample weights:\n", me); | |||
477 | for (i=0; i<sizeOut; i++) { | |||
478 | fprintf(stderr, "%s: %d\n ", me, i); | |||
479 | wght = 0; | |||
480 | for (e=0; e<dotLen; e++) { | |||
481 | fprintf(stderr, "%d/%g ", indx[e + dotLen*i], weight[e + dotLen*i]); | |||
482 | wght += weight[e + dotLen*i]; | |||
483 | } | |||
484 | fprintf(stderr, " (sum = %g)\n", wght); | |||
485 | } | |||
486 | ******** */ | |||
487 | ||||
488 | *weightP = weight; | |||
489 | *indexP = indx; | |||
490 | /* | |||
491 | fprintf(stderr, "!%s: dotLen = %d\n", me, dotLen); | |||
492 | */ | |||
493 | return dotLen; | |||
494 | } | |||
495 | ||||
496 | /* | |||
497 | ******** nrrdSpatialResample() | |||
498 | ** | |||
499 | ** general-purpose array-resampler: resamples a nrrd of any type | |||
500 | ** (except block) and any dimension along any or all of its axes, with | |||
501 | ** any combination of up- or down-sampling along the axes, with any | |||
502 | ** kernel (specified by callback), with potentially a different kernel | |||
503 | ** for each axis. Whether or not to resample along axis d is | |||
504 | ** controlled by the non-NULL-ity of info->kernel[ai]. Where to sample | |||
505 | ** on the axis is controlled by info->min[ai] and info->max[ai]; these | |||
506 | ** specify a range of "positions" aka "world space" positions, as | |||
507 | ** determined by the per-axis min and max of the input nrrd, which must | |||
508 | ** be set for every resampled axis. | |||
509 | ** | |||
510 | ** we cyclically permute those axes being resampled, and never touch | |||
511 | ** the position (in axis ordering) of axes along which we are not | |||
512 | ** resampling. This strategy is certainly not the most intelligent | |||
513 | ** one possible, but it does mean that the axis along which we're | |||
514 | ** currently resampling-- the one along which we'll have to look at | |||
515 | ** multiple adjecent samples-- is that resampling axis which is | |||
516 | ** currently most contiguous in memory. It may make sense to precede | |||
517 | ** the resampling with an axis permutation which bubbles all the | |||
518 | ** resampled axes to the front (most contiguous) end of the axis list, | |||
519 | ** and then puts them back in place afterwards, depending on the cost | |||
520 | ** of such axis permutation overhead. | |||
521 | */ | |||
522 | int | |||
523 | nrrdSpatialResample(Nrrd *nout, const Nrrd *nin, | |||
524 | const NrrdResampleInfo *info) { | |||
525 | static const char me[]="nrrdSpatialResample", func[]="resample"; | |||
526 | nrrdResample_t | |||
527 | *array[NRRD_DIM_MAX16], /* intermediate copies of the input data | |||
528 | undergoing resampling; we don't need a full- | |||
529 | fledged nrrd for these. Only about two of | |||
530 | these arrays will be allocated at a time; | |||
531 | intermediate results will be free()d when not | |||
532 | needed */ | |||
533 | *_inVec, /* current input vector being resampled; | |||
534 | not necessarily contiguous in memory | |||
535 | (if strideIn != 1) */ | |||
536 | *inVec, /* buffer for input vector; contiguous */ | |||
537 | *_outVec; /* output vector in context of volume; | |||
538 | never contiguous */ | |||
539 | double tmpF; | |||
540 | double ratio, /* factor by which or up or downsampled */ | |||
541 | ratios[NRRD_DIM_MAX16]; /* record of "ratio" for all resampled axes, | |||
542 | used to compute new spacing in output */ | |||
543 | ||||
544 | Nrrd *floatNin; /* if the input nrrd type is not nrrdResample_t, | |||
545 | then we convert it and keep it here */ | |||
546 | unsigned int ai, | |||
547 | pi, /* current pass */ | |||
548 | topLax, | |||
549 | permute[NRRD_DIM_MAX16], /* how to permute axes of last pass to get | |||
550 | axes for current pass */ | |||
551 | ax[NRRD_DIM_MAX16+1][NRRD_DIM_MAX16], /* axis ordering on each pass */ | |||
552 | passes; /* # of passes needed to resample all axes */ | |||
553 | int i, s, e, | |||
554 | topRax, /* the lowest index of an axis which is | |||
555 | resampled. If all axes are being resampled, | |||
556 | then this is 0. If for some reason the | |||
557 | "x" axis (fastest stride) is not being | |||
558 | resampled, but "y" is, then topRax is 1 */ | |||
559 | botRax, /* index of highest axis being resampled */ | |||
560 | typeIn, typeOut; /* types of input and output of resampling */ | |||
561 | size_t sz[NRRD_DIM_MAX16+1][NRRD_DIM_MAX16]; | |||
562 | /* how many samples along each | |||
563 | axis, changing on each pass */ | |||
564 | ||||
565 | /* all these variables have to do with the spacing of elements in | |||
566 | memory for the current pass of resampling, and they (except | |||
567 | strideIn) are re-set at the beginning of each pass */ | |||
568 | nrrdResample_t | |||
569 | *weight; /* sample weights */ | |||
570 | unsigned int ci[NRRD_DIM_MAX16+1], | |||
571 | co[NRRD_DIM_MAX16+1]; | |||
572 | int | |||
573 | sizeIn, sizeOut, /* lengths of input and output vectors */ | |||
574 | dotLen, /* # input samples to dot with weights to get | |||
575 | one output sample */ | |||
576 | doRound, /* actually do rounding on output: we DO NOT | |||
577 | round when info->round but the output | |||
578 | type is not integral */ | |||
579 | *indx; /* dotLen*sizeOut 2D array of input indices */ | |||
580 | size_t | |||
581 | I, /* swiss-army int */ | |||
582 | strideIn, /* the stride between samples in the input | |||
583 | "scanline" being resampled */ | |||
584 | strideOut, /* stride between samples in output | |||
585 | "scanline" from resampling */ | |||
586 | L, LI, LO, numLines, /* top secret */ | |||
587 | numOut; /* # of _samples_, total, in output volume; | |||
588 | this is for allocating the output */ | |||
589 | airArray *mop; /* for cleaning up */ | |||
590 | ||||
591 | if (!(nout && nin && info)) { | |||
592 | biffAddf(NRRDnrrdBiffKey, "%s: got NULL pointer", me); | |||
593 | return 1; | |||
594 | } | |||
595 | if (nrrdBoundaryUnknown == info->boundary) { | |||
596 | biffAddf(NRRDnrrdBiffKey, "%s: need to specify a boundary behavior", me); | |||
597 | return 1; | |||
598 | } | |||
599 | ||||
600 | typeIn = nin->type; | |||
601 | typeOut = nrrdTypeDefault == info->type ? typeIn : info->type; | |||
602 | ||||
603 | if (_nrrdResampleCheckInfo(nin, info)) { | |||
604 | biffAddf(NRRDnrrdBiffKey, "%s: problem with arguments", me); | |||
605 | return 1; | |||
606 | } | |||
607 | ||||
608 | _nrrdResampleComputePermute(permute, ax, sz, | |||
609 | &topRax, &botRax, &passes, | |||
610 | nin, info); | |||
611 | topLax = topRax ? 0 : 1; | |||
612 | ||||
613 | /* not sure where else to put this: | |||
614 | (want to put it before 0 == passes branch) | |||
615 | We have to assume some centering when doing resampling, and it would | |||
616 | be stupid to not record it in the outgoing nrrd, since the value of | |||
617 | nrrdDefaultCenter could always change. */ | |||
618 | for (ai=0; ai<nin->dim; ai++) { | |||
619 | if (info->kernel[ai]) { | |||
620 | nout->axis[ai].center = _nrrdCenter(nin->axis[ai].center); | |||
621 | } | |||
622 | } | |||
623 | ||||
624 | if (0 == passes) { | |||
625 | /* actually, no resampling was desired. Copy input to output, | |||
626 | but with the clamping that we normally do at the end of resampling */ | |||
627 | nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, sz[0]); | |||
628 | if (nrrdMaybeAlloc_nva(nout, typeOut, nin->dim, sz[0])) { | |||
629 | biffAddf(NRRDnrrdBiffKey, "%s: couldn't allocate output", me); | |||
630 | return 1; | |||
631 | } | |||
632 | numOut = nrrdElementNumber(nout); | |||
633 | for (I=0; I<numOut; I++) { | |||
634 | tmpF = nrrdDLookup[nin->type](nin->data, I); | |||
635 | tmpF = nrrdDClamp[typeOut](tmpF); | |||
636 | nrrdDInsert[typeOut](nout->data, I, tmpF); | |||
637 | } | |||
638 | nrrdAxisInfoCopy(nout, nin, NULL((void*)0), NRRD_AXIS_INFO_NONE0); | |||
639 | /* HEY: need to create textual representation of resampling parameters */ | |||
640 | if (nrrdContentSet_va(nout, func, nin, "")) { | |||
641 | biffAddf(NRRDnrrdBiffKey, "%s:", me); | |||
642 | return 1; | |||
643 | } | |||
644 | if (nrrdBasicInfoCopy(nout, nin, | |||
645 | NRRD_BASIC_INFO_DATA_BIT(1<< 1) | |||
646 | | NRRD_BASIC_INFO_TYPE_BIT(1<< 2) | |||
647 | | NRRD_BASIC_INFO_BLOCKSIZE_BIT(1<< 3) | |||
648 | | NRRD_BASIC_INFO_DIMENSION_BIT(1<< 4) | |||
649 | | NRRD_BASIC_INFO_CONTENT_BIT(1<< 5) | |||
650 | | NRRD_BASIC_INFO_COMMENTS_BIT(1<<14) | |||
651 | | (nrrdStateKeyValuePairsPropagate | |||
652 | ? 0 | |||
653 | : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT(1<<15)))) { | |||
654 | biffAddf(NRRDnrrdBiffKey, "%s:", me); | |||
655 | return 1; | |||
656 | } | |||
657 | return 0; | |||
658 | } | |||
659 | ||||
660 | mop = airMopNew(); | |||
661 | /* convert input nrrd to nrrdResample_t if necessary */ | |||
662 | if (nrrdResample_nrrdTypenrrdTypeDouble != typeIn) { | |||
663 | if (nrrdConvert(floatNin = nrrdNew(), nin, nrrdResample_nrrdTypenrrdTypeDouble)) { | |||
664 | biffAddf(NRRDnrrdBiffKey, "%s: couldn't create float copy of input", me); | |||
665 | airMopError(mop); return 1; | |||
666 | } | |||
667 | array[0] = (nrrdResample_t*)floatNin->data; | |||
668 | airMopAdd(mop, floatNin, (airMopper)nrrdNuke, airMopAlways); | |||
669 | } else { | |||
670 | floatNin = NULL((void*)0); | |||
671 | array[0] = (nrrdResample_t*)nin->data; | |||
672 | } | |||
673 | ||||
674 | /* compute strideIn; this is actually the same for every pass | |||
675 | because (strictly speaking) in every pass we are resampling | |||
676 | the same axis, and axes with lower indices are constant length */ | |||
677 | strideIn = 1; | |||
678 | for (ai=0; ai<(unsigned int)topRax; ai++) { /* HEY scrutinize casts */ | |||
679 | strideIn *= nin->axis[ai].size; | |||
680 | } | |||
681 | /* printf("%s: strideIn = " _AIR_SIZE_T_CNV "\n", me, strideIn); */ | |||
682 | ||||
683 | /* go! */ | |||
684 | for (pi=0; pi<passes; pi++) { | |||
685 | /* | |||
686 | printf("%s: --- pass %d --- \n", me, pi); | |||
687 | */ | |||
688 | numLines = strideOut = 1; | |||
689 | for (ai=0; ai<nin->dim; ai++) { | |||
690 | if (ai < (unsigned int)botRax) { /* HEY scrutinize cast */ | |||
691 | strideOut *= sz[pi+1][ai]; | |||
692 | } | |||
693 | if (ai != (unsigned int)topRax) { /* HEY scrutinize cast */ | |||
694 | numLines *= sz[pi][ai]; | |||
695 | } | |||
696 | } | |||
697 | sizeIn = AIR_CAST(int, sz[pi][topRax])((int)(sz[pi][topRax])); | |||
698 | sizeOut = AIR_CAST(int, sz[pi+1][botRax])((int)(sz[pi+1][botRax])); | |||
699 | numOut = numLines*sizeOut; | |||
700 | /* for the rest of the loop body, d is the original "dimension" | |||
701 | for the axis being resampled */ | |||
702 | ai = ax[pi][topRax]; | |||
703 | /* printf("%s(%d): numOut = " _AIR_SIZE_T_CNV "\n", me, pi, numOut); */ | |||
704 | /* printf("%s(%d): numLines = " _AIR_SIZE_T_CNV "\n", me, pi, numLines); */ | |||
705 | /* printf("%s(%d): stride: In=%d, Out=%d\n", me, pi, */ | |||
706 | /* (int)strideIn, (int)strideOut); */ | |||
707 | /* printf("%s(%d): sizeIn = %d\n", me, pi, sizeIn); */ | |||
708 | /* printf("%s(%d): sizeOut = %d\n", me, pi, sizeOut); */ | |||
709 | ||||
710 | /* we can free the input to the previous pass | |||
711 | (if its not the given data) */ | |||
712 | if (pi > 0) { | |||
713 | if (pi == 1) { | |||
714 | if (array[0] != nin->data) { | |||
715 | airMopSub(mop, floatNin, (airMopper)nrrdNuke); | |||
716 | floatNin = nrrdNuke(floatNin); | |||
717 | array[0] = NULL((void*)0); | |||
718 | /* | |||
719 | printf("%s: pi %d: freeing array[0]\n", me, pi); | |||
720 | */ | |||
721 | } | |||
722 | } else { | |||
723 | airMopSub(mop, array[pi-1], airFree); | |||
724 | array[pi-1] = (nrrdResample_t*)airFree(array[pi-1]); | |||
725 | /* | |||
726 | printf("%s: pi %d: freeing array[%d]\n", me, pi, pi-1); | |||
727 | */ | |||
728 | } | |||
729 | } | |||
730 | ||||
731 | /* allocate output volume */ | |||
732 | array[pi+1] = (nrrdResample_t*)calloc(numOut, sizeof(nrrdResample_t)); | |||
733 | if (!array[pi+1]) { | |||
734 | char stmp[AIR_STRLEN_SMALL(128+1)]; | |||
735 | biffAddf(NRRDnrrdBiffKey, "%s: couldn't create array of %s nrrdResample_t's for " | |||
736 | "output of pass %d", me, airSprintSize_t(stmp, numOut), pi); | |||
737 | airMopError(mop); return 1; | |||
738 | } | |||
739 | airMopAdd(mop, array[pi+1], airFree, airMopAlways); | |||
740 | /* | |||
741 | printf("%s: allocated array[%d]\n", me, pi+1); | |||
742 | */ | |||
743 | ||||
744 | /* allocate contiguous input scanline buffer, we alloc one more | |||
745 | than needed to provide a place for the pad value. That is, in | |||
746 | fact, the over-riding reason to copy a scanline to a local | |||
747 | array: so that there is a simple consistent (non-branchy) way | |||
748 | to incorporate the pad values */ | |||
749 | inVec = (nrrdResample_t *)calloc(sizeIn+1, sizeof(nrrdResample_t)); | |||
750 | airMopAdd(mop, inVec, airFree, airMopAlways); | |||
751 | inVec[sizeIn] = AIR_CAST(nrrdResample_t, info->padValue)((nrrdResample_t)(info->padValue)); | |||
752 | ||||
753 | dotLen = _nrrdResampleMakeWeightIndex(&weight, &indx, &ratio, | |||
754 | nin, info, ai); | |||
755 | if (!dotLen) { | |||
756 | biffAddf(NRRDnrrdBiffKey, "%s: trouble creating weight and index vector arrays", | |||
757 | me); | |||
758 | airMopError(mop); return 1; | |||
759 | } | |||
760 | ratios[ai] = ratio; | |||
761 | airMopAdd(mop, weight, airFree, airMopAlways); | |||
762 | airMopAdd(mop, indx, airFree, airMopAlways); | |||
763 | ||||
764 | /* the skinny: resample all the scanlines */ | |||
765 | _inVec = array[pi]; | |||
766 | _outVec = array[pi+1]; | |||
767 | memset(ci, 0, (NRRD_DIM_MAX+1)*sizeof(int))__builtin___memset_chk (ci, 0, (16 +1)*sizeof(int), __builtin_object_size (ci, 0)); | |||
768 | memset(co, 0, (NRRD_DIM_MAX+1)*sizeof(int))__builtin___memset_chk (co, 0, (16 +1)*sizeof(int), __builtin_object_size (co, 0)); | |||
769 | for (L=0; L<numLines; L++) { | |||
770 | /* calculate the index to get to input and output scanlines, | |||
771 | according the coordinates of the start of the scanline */ | |||
772 | NRRD_INDEX_GEN(LI, ci, sz[pi], nin->dim){ unsigned int ddd = (nin->dim); (LI) = 0; while (ddd) { ddd --; (LI) = (ci)[ddd] + (sz[pi])[ddd]*(LI); } }; | |||
773 | NRRD_INDEX_GEN(LO, co, sz[pi+1], nin->dim){ unsigned int ddd = (nin->dim); (LO) = 0; while (ddd) { ddd --; (LO) = (co)[ddd] + (sz[pi+1])[ddd]*(LO); } }; | |||
774 | _inVec = array[pi] + LI; | |||
775 | _outVec = array[pi+1] + LO; | |||
776 | ||||
777 | /* read input scanline into contiguous array */ | |||
778 | for (i=0; i<sizeIn; i++) { | |||
779 | inVec[i] = _inVec[i*strideIn]; | |||
780 | } | |||
781 | ||||
782 | /* do the weighting */ | |||
783 | for (i=0; i<sizeOut; i++) { | |||
784 | tmpF = 0.0; | |||
785 | /* | |||
786 | fprintf(stderr, "%s: i = %d (tmpF=0)\n", me, (int)i); | |||
787 | */ | |||
788 | for (s=0; s<dotLen; s++) { | |||
789 | tmpF += inVec[indx[s + dotLen*i]]*weight[s + dotLen*i]; | |||
790 | /* | |||
791 | fprintf(stderr, " tmpF += %g*%g == %g\n", | |||
792 | inVec[indx[s + dotLen*i]], weight[s + dotLen*i], tmpF); | |||
793 | */ | |||
794 | } | |||
795 | _outVec[i*strideOut] = tmpF; | |||
796 | /* | |||
797 | fprintf(stderr, "--> out[%d] = %g\n", | |||
798 | i*strideOut, _outVec[i*strideOut]); | |||
799 | */ | |||
800 | } | |||
801 | ||||
802 | /* update the coordinates for the scanline starts. We don't | |||
803 | use the usual NRRD_COORD macros because we're subject to | |||
804 | the unusual constraint that ci[topRax] and co[permute[topRax]] | |||
805 | must stay exactly zero */ | |||
806 | e = topLax; | |||
807 | ci[e]++; | |||
808 | co[permute[e]]++; | |||
809 | while (L < numLines-1 && ci[e] == sz[pi][e]) { | |||
810 | ci[e] = co[permute[e]] = 0; | |||
811 | e++; | |||
812 | e += e == topRax; | |||
813 | ci[e]++; | |||
814 | co[permute[e]]++; | |||
815 | } | |||
816 | } | |||
817 | ||||
818 | /* pass-specific clean up */ | |||
819 | airMopSub(mop, weight, airFree); | |||
820 | airMopSub(mop, indx, airFree); | |||
821 | airMopSub(mop, inVec, airFree); | |||
822 | weight = (nrrdResample_t*)airFree(weight); | |||
823 | indx = (int*)airFree(indx); | |||
824 | inVec = (nrrdResample_t*)airFree(inVec); | |||
825 | } | |||
826 | ||||
827 | /* clean up second-to-last array and scanline buffers */ | |||
828 | if (passes > 1) { | |||
829 | airMopSub(mop, array[passes-1], airFree); | |||
830 | array[passes-1] = (nrrdResample_t*)airFree(array[passes-1]); | |||
831 | /* | |||
832 | printf("%s: now freeing array[%d]\n", me, passes-1); | |||
833 | */ | |||
834 | } else if (array[passes-1] != nin->data) { | |||
835 | airMopSub(mop, floatNin, (airMopper)nrrdNuke); | |||
836 | floatNin = nrrdNuke(floatNin); | |||
837 | } | |||
838 | array[passes-1] = NULL((void*)0); | |||
839 | ||||
840 | /* create output nrrd and set axis info */ | |||
841 | if (nrrdMaybeAlloc_nva(nout, typeOut, nin->dim, sz[passes])) { | |||
842 | biffAddf(NRRDnrrdBiffKey, "%s: couldn't allocate final output nrrd", me); | |||
843 | airMopError(mop); return 1; | |||
844 | } | |||
845 | airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopOnError); | |||
846 | nrrdAxisInfoCopy(nout, nin, NULL((void*)0), | |||
847 | (NRRD_AXIS_INFO_SIZE_BIT(1<< 1) | |||
848 | | NRRD_AXIS_INFO_MIN_BIT(1<< 4) | |||
849 | | NRRD_AXIS_INFO_MAX_BIT(1<< 5) | |||
850 | | NRRD_AXIS_INFO_SPACING_BIT(1<< 2) | |||
851 | | NRRD_AXIS_INFO_SPACEDIRECTION_BIT(1<< 6) /* see below */ | |||
852 | | NRRD_AXIS_INFO_THICKNESS_BIT(1<< 3) | |||
853 | | NRRD_AXIS_INFO_KIND_BIT(1<< 8))); | |||
854 | for (ai=0; ai<nin->dim; ai++) { | |||
855 | if (info->kernel[ai]) { | |||
856 | /* we do resample this axis */ | |||
857 | nout->axis[ai].spacing = nin->axis[ai].spacing/ratios[ai]; | |||
858 | /* no way to usefully update thickness: we could be doing blurring | |||
859 | but maintaining the number of samples: thickness increases, or | |||
860 | we could be downsampling, in which the relationship between the | |||
861 | sampled and the skipped regions of space becomes complicated: | |||
862 | no single scalar can represent it, or we could be upsampling, | |||
863 | in which the notion of "skip" could be rendered meaningless */ | |||
864 | nout->axis[ai].thickness = AIR_NAN(airFloatQNaN.f); | |||
865 | nout->axis[ai].min = info->min[ai]; | |||
866 | nout->axis[ai].max = info->max[ai]; | |||
867 | /* | |||
868 | HEY: this is currently a bug: all this code was written long | |||
869 | before there were space directions, so min/max are always | |||
870 | set, regardless of whethere there are incoming space directions | |||
871 | which then disallows output space directions on the same axes | |||
872 | _nrrdSpaceVecScale(nout->axis[ai].spaceDirection, | |||
873 | 1.0/ratios[ai], nin->axis[ai].spaceDirection); | |||
874 | */ | |||
875 | nout->axis[ai].kind = _nrrdKindAltered(nin->axis[ai].kind, AIR_TRUE1); | |||
876 | } else { | |||
877 | /* this axis remains untouched */ | |||
878 | nout->axis[ai].min = nin->axis[ai].min; | |||
879 | nout->axis[ai].max = nin->axis[ai].max; | |||
880 | nout->axis[ai].spacing = nin->axis[ai].spacing; | |||
881 | nout->axis[ai].thickness = nin->axis[ai].thickness; | |||
882 | nout->axis[ai].kind = nin->axis[ai].kind; | |||
883 | } | |||
884 | } | |||
885 | /* HEY: need to create textual representation of resampling parameters */ | |||
886 | if (nrrdContentSet_va(nout, func, nin, "")) { | |||
887 | biffAddf(NRRDnrrdBiffKey, "%s:", me); | |||
888 | return 1; | |||
889 | } | |||
890 | ||||
891 | /* copy the resampling final result into the output nrrd, maybe | |||
892 | rounding as we go to make sure that 254.9999 is saved as 255 | |||
893 | in uchar output, and maybe clamping as we go to insure that | |||
894 | integral results don't have unexpected wrap-around. */ | |||
895 | if (info->round) { | |||
896 | if (nrrdTypeInt == typeOut || | |||
897 | nrrdTypeUInt == typeOut || | |||
898 | nrrdTypeLLong == typeOut || | |||
899 | nrrdTypeULLong == typeOut) { | |||
900 | fprintf(stderr__stderrp, "%s: WARNING: possible erroneous output with " | |||
901 | "rounding of %s output type due to int-based implementation " | |||
902 | "of rounding\n", me, airEnumStr(nrrdType, typeOut)); | |||
903 | } | |||
904 | doRound = nrrdTypeIsIntegral[typeOut]; | |||
905 | } else { | |||
906 | doRound = AIR_FALSE0; | |||
907 | } | |||
908 | numOut = nrrdElementNumber(nout); | |||
909 | for (I=0; I<numOut; I++) { | |||
910 | tmpF = array[passes][I]; | |||
911 | if (doRound) { | |||
912 | tmpF = AIR_CAST(nrrdResample_t, AIR_ROUNDUP(tmpF))((nrrdResample_t)(((int)(floor((tmpF)+0.5))))); | |||
913 | } | |||
914 | if (info->clamp) { | |||
915 | tmpF = nrrdDClamp[typeOut](tmpF); | |||
916 | } | |||
917 | nrrdDInsert[typeOut](nout->data, I, tmpF); | |||
918 | } | |||
919 | ||||
920 | if (nrrdBasicInfoCopy(nout, nin, | |||
921 | NRRD_BASIC_INFO_DATA_BIT(1<< 1) | |||
922 | | NRRD_BASIC_INFO_TYPE_BIT(1<< 2) | |||
923 | | NRRD_BASIC_INFO_BLOCKSIZE_BIT(1<< 3) | |||
924 | | NRRD_BASIC_INFO_DIMENSION_BIT(1<< 4) | |||
925 | | NRRD_BASIC_INFO_CONTENT_BIT(1<< 5) | |||
926 | | NRRD_BASIC_INFO_COMMENTS_BIT(1<<14) | |||
927 | | (nrrdStateKeyValuePairsPropagate | |||
928 | ? 0 | |||
929 | : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT(1<<15)))) { | |||
930 | biffAddf(NRRDnrrdBiffKey, "%s:", me); | |||
931 | return 1; | |||
932 | } | |||
933 | ||||
934 | /* enough already */ | |||
935 | airMopOkay(mop); | |||
936 | return 0; | |||
937 | } |