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(NRRD, "%s: not NULL pointer", me); |
63 |
|
|
return 1; |
64 |
|
|
} |
65 |
|
|
if (!(info = nrrdResampleInfoNew())) { |
66 |
|
|
biffAddf(NRRD, "%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) && AIR_EXISTS(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)) |
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(NRRD, "%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]; |
138 |
|
|
|
139 |
|
|
if (nrrdTypeBlock == nin->type || nrrdTypeBlock == info->type) { |
140 |
|
|
biffAddf(NRRD, "%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(NRRD, "%s: didn't set boundary behavior\n", me); |
146 |
|
|
return 1; |
147 |
|
|
} |
148 |
|
|
if (nrrdBoundaryPad == info->boundary && !AIR_EXISTS(info->padValue)) { |
149 |
|
|
biffAddf(NRRD, |
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(NRRD, "%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) && AIR_EXISTS(nin->axis[ai].max) )) { |
164 |
|
|
biffAddf(NRRD, "%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]) && AIR_EXISTS(info->max[ai]) )) { |
169 |
|
|
biffAddf(NRRD, "%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])) { |
175 |
|
|
biffAddf(NRRD, "%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(NRRD, "%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_MAX][NRRD_DIM_MAX], |
203 |
|
|
size_t sz[NRRD_DIM_MAX][NRRD_DIM_MAX], |
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); /* 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_NUM]; |
306 |
|
|
|
307 |
|
|
int e, i; |
308 |
|
|
|
309 |
|
|
if (!(info->kernel[ai])) { |
310 |
|
|
biffAddf(NRRD, "%s: don't see a kernel for dimension %d", me, ai); |
311 |
|
|
*weightP = NULL; *indexP = NULL; return 0; |
312 |
|
|
} |
313 |
|
|
|
314 |
|
|
center = _nrrdCenter(nin->axis[ai].center); |
315 |
|
|
sizeIn = AIR_CAST(int, nin->axis[ai].size); |
316 |
|
|
sizeOut = AIR_CAST(int, info->samples[ai]); |
317 |
|
|
minIn = AIR_CAST(nrrdResample_t, nin->axis[ai].min); |
318 |
|
|
maxIn = AIR_CAST(nrrdResample_t, nin->axis[ai].max); |
319 |
|
|
minOut = AIR_CAST(nrrdResample_t, info->min[ai]); |
320 |
|
|
maxOut = AIR_CAST(nrrdResample_t, info->max[ai]); |
321 |
|
|
spcIn = NRRD_SPACING(center, minIn, maxIn, sizeIn); |
322 |
|
|
spcOut = NRRD_SPACING(center, minOut, maxOut, sizeOut); |
323 |
|
|
*ratioP = ratio = spcIn/spcOut; |
324 |
|
|
support = AIR_CAST(nrrdResample_t, |
325 |
|
|
info->kernel[ai]->support(info->parm[ai])); |
326 |
|
|
integral = AIR_CAST(nrrdResample_t, |
327 |
|
|
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); |
351 |
|
|
if (!weight) { |
352 |
|
|
biffAddf(NRRD, "%s: can't allocate weight array", me); |
353 |
|
|
*weightP = NULL; *indexP = NULL; return 0; |
354 |
|
|
} |
355 |
|
|
indx = AIR_CALLOC(sizeOut*dotLen, int); |
356 |
|
|
if (!indx) { |
357 |
|
|
biffAddf(NRRD, "%s: can't allocate index arrays", me); |
358 |
|
|
*weightP = NULL; *indexP = NULL; 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, |
365 |
|
|
NRRD_POS(center, minOut, maxOut, sizeOut, i)); |
366 |
|
|
idxD = AIR_CAST(nrrdResample_t, |
367 |
|
|
NRRD_IDX(center, minIn, maxIn, sizeIn, pos)); |
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)) { |
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); |
396 |
|
|
break; |
397 |
|
|
case nrrdBoundaryWrap: |
398 |
|
|
idx = AIR_MOD(idx, sizeIn); |
399 |
|
|
break; |
400 |
|
|
case nrrdBoundaryMirror: |
401 |
|
|
idx = _nrrdMirror_32(sizeIn, idx); |
402 |
|
|
break; |
403 |
|
|
default: |
404 |
|
|
biffAddf(NRRD, "%s: boundary behavior %d unknown/unimplemented", |
405 |
|
|
me, info->boundary); |
406 |
|
|
*weightP = NULL; *indexP = NULL; 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)); |
416 |
|
|
if (ratio < 1 && !(info->cheap)) { |
417 |
|
|
parm[0] /= ratio; |
418 |
|
|
} |
419 |
|
|
info->kernel[ai]->EVALN(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); |
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_MAX], /* 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_MAX]; /* 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_MAX], /* how to permute axes of last pass to get |
550 |
|
|
axes for current pass */ |
551 |
|
|
ax[NRRD_DIM_MAX+1][NRRD_DIM_MAX], /* 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_MAX+1][NRRD_DIM_MAX]; |
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_MAX+1], |
571 |
|
|
co[NRRD_DIM_MAX+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(NRRD, "%s: got NULL pointer", me); |
593 |
|
|
return 1; |
594 |
|
|
} |
595 |
|
|
if (nrrdBoundaryUnknown == info->boundary) { |
596 |
|
|
biffAddf(NRRD, "%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(NRRD, "%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(NRRD, "%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, NRRD_AXIS_INFO_NONE); |
639 |
|
|
/* HEY: need to create textual representation of resampling parameters */ |
640 |
|
|
if (nrrdContentSet_va(nout, func, nin, "")) { |
641 |
|
|
biffAddf(NRRD, "%s:", me); |
642 |
|
|
return 1; |
643 |
|
|
} |
644 |
|
|
if (nrrdBasicInfoCopy(nout, nin, |
645 |
|
|
NRRD_BASIC_INFO_DATA_BIT |
646 |
|
|
| NRRD_BASIC_INFO_TYPE_BIT |
647 |
|
|
| NRRD_BASIC_INFO_BLOCKSIZE_BIT |
648 |
|
|
| NRRD_BASIC_INFO_DIMENSION_BIT |
649 |
|
|
| NRRD_BASIC_INFO_CONTENT_BIT |
650 |
|
|
| NRRD_BASIC_INFO_COMMENTS_BIT |
651 |
|
|
| (nrrdStateKeyValuePairsPropagate |
652 |
|
|
? 0 |
653 |
|
|
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { |
654 |
|
|
biffAddf(NRRD, "%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_nrrdType != typeIn) { |
663 |
|
|
if (nrrdConvert(floatNin = nrrdNew(), nin, nrrdResample_nrrdType)) { |
664 |
|
|
biffAddf(NRRD, "%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; |
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]); |
698 |
|
|
sizeOut = AIR_CAST(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; |
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]; |
735 |
|
|
biffAddf(NRRD, "%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); |
752 |
|
|
|
753 |
|
|
dotLen = _nrrdResampleMakeWeightIndex(&weight, &indx, &ratio, |
754 |
|
|
nin, info, ai); |
755 |
|
|
if (!dotLen) { |
756 |
|
|
biffAddf(NRRD, "%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)); |
768 |
|
|
memset(co, 0, (NRRD_DIM_MAX+1)*sizeof(int)); |
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); |
773 |
|
|
NRRD_INDEX_GEN(LO, co, sz[pi+1], nin->dim); |
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; |
839 |
|
|
|
840 |
|
|
/* create output nrrd and set axis info */ |
841 |
|
|
if (nrrdMaybeAlloc_nva(nout, typeOut, nin->dim, sz[passes])) { |
842 |
|
|
biffAddf(NRRD, "%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, |
847 |
|
|
(NRRD_AXIS_INFO_SIZE_BIT |
848 |
|
|
| NRRD_AXIS_INFO_MIN_BIT |
849 |
|
|
| NRRD_AXIS_INFO_MAX_BIT |
850 |
|
|
| NRRD_AXIS_INFO_SPACING_BIT |
851 |
|
|
| NRRD_AXIS_INFO_SPACEDIRECTION_BIT /* see below */ |
852 |
|
|
| NRRD_AXIS_INFO_THICKNESS_BIT |
853 |
|
|
| NRRD_AXIS_INFO_KIND_BIT)); |
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; |
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_TRUE); |
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(NRRD, "%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, "%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_FALSE; |
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)); |
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 |
922 |
|
|
| NRRD_BASIC_INFO_TYPE_BIT |
923 |
|
|
| NRRD_BASIC_INFO_BLOCKSIZE_BIT |
924 |
|
|
| NRRD_BASIC_INFO_DIMENSION_BIT |
925 |
|
|
| NRRD_BASIC_INFO_CONTENT_BIT |
926 |
|
|
| NRRD_BASIC_INFO_COMMENTS_BIT |
927 |
|
|
| (nrrdStateKeyValuePairsPropagate |
928 |
|
|
? 0 |
929 |
|
|
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { |
930 |
|
|
biffAddf(NRRD, "%s:", me); |
931 |
|
|
return 1; |
932 |
|
|
} |
933 |
|
|
|
934 |
|
|
/* enough already */ |
935 |
|
|
airMopOkay(mop); |
936 |
|
|
return 0; |
937 |
|
|
} |