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 is a largely a re-write of the functionality in |
29 |
|
|
** nrrdSpatialResample(), but with some improvements. The big API |
30 |
|
|
** change is that everything happens in a nrrdResampleContext, and no |
31 |
|
|
** fields in this need to be directly set (except for rsmc->verbose). |
32 |
|
|
** |
33 |
|
|
** The big behavior/API change is that the range along the axis that |
34 |
|
|
** is resampled is now defined in terms of index space, instead of |
35 |
|
|
** axis-aligned scaled index space (what used to be called "world |
36 |
|
|
** space", prior to general orientation). This means that if you |
37 |
|
|
** want the whole range resampled, you use [-0.5,size-0.5] for cell- |
38 |
|
|
** centered, and [0,size-1] for node-centered. One function helpful |
39 |
|
|
** dealing with this is nrrdResampleRangeFullSet(). |
40 |
|
|
** |
41 |
|
|
** Other improvements: |
42 |
|
|
** -- ability to quickly resample a different nrrd with the same |
43 |
|
|
** sizes and kernels as with a previous (all useful state and |
44 |
|
|
** allocations of intermediate resampling results is preserved |
45 |
|
|
** in the nrrdResampleContext). To resample a second nrrd, |
46 |
|
|
** you'd only need: |
47 |
|
|
** nrrdResampleInputSet(rsmc, nin2); |
48 |
|
|
** nrrdResampleExecute(rsmc, nout2); |
49 |
|
|
** -- correct handling general orientation (space directions and |
50 |
|
|
** space origin). This was impossible in the old resampler |
51 |
|
|
** because of how its logic was hard-wired to the axis-aligned |
52 |
|
|
** world space defined by the per-axis min and max. |
53 |
|
|
** -- correct handling of "cheap" downsampling, with the use of |
54 |
|
|
** the new nrrdKernelCheap |
55 |
|
|
** -- smaller memory footprint (smarter about freeing intermediate |
56 |
|
|
** resampling results) |
57 |
|
|
*/ |
58 |
|
|
|
59 |
|
|
enum { |
60 |
|
|
flagUnknown, /* 0 */ |
61 |
|
|
flagDefaultCenter, /* 1 */ |
62 |
|
|
flagInput, /* 2 */ |
63 |
|
|
flagOverrideCenters, /* 3 */ |
64 |
|
|
flagInputDimension, /* 4 */ |
65 |
|
|
flagInputCenters, /* 5 */ |
66 |
|
|
flagInputSizes, /* 6 */ |
67 |
|
|
flagKernels, /* 7 */ |
68 |
|
|
flagSamples, /* 8 */ |
69 |
|
|
flagRanges, /* 9 */ |
70 |
|
|
flagBoundary, /* 10 */ |
71 |
|
|
flagLineAllocate, /* 11 */ |
72 |
|
|
flagLineFill, /* 12 */ |
73 |
|
|
flagVectorAllocate, /* 13 */ |
74 |
|
|
flagPermutation, /* 14 */ |
75 |
|
|
flagVectorFill, /* 15 */ |
76 |
|
|
flagClamp, /* 16 */ |
77 |
|
|
flagRound, /* 17 */ |
78 |
|
|
flagTypeOut, /* 18 */ |
79 |
|
|
flagPadValue, /* 19 */ |
80 |
|
|
flagRenormalize, /* 20 */ |
81 |
|
|
flagNonExistent, /* 21 */ |
82 |
|
|
flagLast |
83 |
|
|
}; |
84 |
|
|
#define FLAG_MAX 21 |
85 |
|
|
|
86 |
|
|
void |
87 |
|
|
nrrdResampleContextInit(NrrdResampleContext *rsmc) { |
88 |
|
|
unsigned int axIdx, axJdx, kpIdx, flagIdx; |
89 |
|
|
NrrdResampleAxis *axis; |
90 |
|
|
|
91 |
|
|
if (rsmc) { |
92 |
|
|
rsmc->nin = NULL; |
93 |
|
|
rsmc->boundary = nrrdDefaultResampleBoundary; |
94 |
|
|
rsmc->typeOut = nrrdDefaultResampleType; |
95 |
|
|
rsmc->renormalize = nrrdDefaultResampleRenormalize; |
96 |
|
|
rsmc->roundlast = nrrdDefaultResampleRound; |
97 |
|
|
rsmc->clamp = nrrdDefaultResampleClamp; |
98 |
|
|
rsmc->defaultCenter = nrrdDefaultCenter; |
99 |
|
|
rsmc->nonExistent = nrrdDefaultResampleNonExistent; |
100 |
|
|
rsmc->padValue = nrrdDefaultResamplePadValue; |
101 |
|
|
rsmc->dim = 0; |
102 |
|
|
rsmc->passNum = AIR_CAST(unsigned int, -1); /* 4294967295 */ |
103 |
|
|
rsmc->topRax = AIR_CAST(unsigned int, -1); |
104 |
|
|
rsmc->botRax = AIR_CAST(unsigned int, -1); |
105 |
|
|
for (axIdx=0; axIdx<NRRD_DIM_MAX; axIdx++) { |
106 |
|
|
rsmc->permute[axIdx] = AIR_CAST(unsigned int, -1); |
107 |
|
|
rsmc->passAxis[axIdx] = AIR_CAST(unsigned int, -1); |
108 |
|
|
} |
109 |
|
|
for (axIdx=0; axIdx<NRRD_DIM_MAX+1; axIdx++) { |
110 |
|
|
axis = rsmc->axis + axIdx; |
111 |
|
|
axis->kernel = NULL; |
112 |
|
|
axis->kparm[0] = nrrdDefaultKernelParm0; |
113 |
|
|
for (kpIdx=1; kpIdx<NRRD_KERNEL_PARMS_NUM; kpIdx++) { |
114 |
|
|
axis->kparm[kpIdx] = AIR_NAN; |
115 |
|
|
} |
116 |
|
|
axis->min = axis->max = AIR_NAN; |
117 |
|
|
axis->samples = AIR_CAST(unsigned int, -1); |
118 |
|
|
axis->overrideCenter = nrrdCenterUnknown; |
119 |
|
|
axis->center = nrrdCenterUnknown; |
120 |
|
|
axis->sizeIn = AIR_CAST(unsigned int, -1); |
121 |
|
|
axis->axIdx = axIdx; /* never changes */ |
122 |
|
|
axis->passIdx = AIR_CAST(unsigned int, -1); |
123 |
|
|
for (axJdx=0; axJdx<NRRD_DIM_MAX; axJdx++) { |
124 |
|
|
axis->sizePerm[axJdx] = AIR_CAST(size_t, -1); |
125 |
|
|
axis->axisPerm[axJdx] = AIR_CAST(unsigned int, -1); |
126 |
|
|
} |
127 |
|
|
axis->ratio = AIR_NAN; |
128 |
|
|
axis->nrsmp = NULL; /* these are nrrdNew()'d as needed */ |
129 |
|
|
axis->nline = nrrdNew(); |
130 |
|
|
axis->nindex = nrrdNew(); |
131 |
|
|
axis->nweight = nrrdNew(); |
132 |
|
|
} |
133 |
|
|
/* initialize flags to all true */ |
134 |
|
|
for (flagIdx=0; flagIdx<=FLAG_MAX; flagIdx++) { |
135 |
|
|
rsmc->flag[flagIdx] = AIR_TRUE; |
136 |
|
|
} |
137 |
|
|
rsmc->time = 0.0; |
138 |
|
|
} |
139 |
|
|
return; |
140 |
|
|
} |
141 |
|
|
|
142 |
|
|
NrrdResampleContext * |
143 |
|
|
nrrdResampleContextNew() { |
144 |
|
|
NrrdResampleContext *rsmc; |
145 |
|
|
|
146 |
|
|
rsmc = AIR_CALLOC(1, NrrdResampleContext); |
147 |
|
|
if (rsmc) { |
148 |
|
|
rsmc->flag = AIR_CALLOC(1+FLAG_MAX, int); |
149 |
|
|
nrrdResampleContextInit(rsmc); |
150 |
|
|
} |
151 |
|
|
return rsmc; |
152 |
|
|
} |
153 |
|
|
|
154 |
|
|
NrrdResampleContext * |
155 |
|
|
nrrdResampleContextNix(NrrdResampleContext *rsmc) { |
156 |
|
|
unsigned int axIdx; |
157 |
|
|
|
158 |
|
|
if (rsmc) { |
159 |
|
|
for (axIdx=0; axIdx<NRRD_DIM_MAX+1; axIdx++) { |
160 |
|
|
/* nrsmp should have been cleaned up by _nrrdResampleOutputUpdate() */ |
161 |
|
|
nrrdNuke(rsmc->axis[axIdx].nline); |
162 |
|
|
nrrdNuke(rsmc->axis[axIdx].nindex); |
163 |
|
|
nrrdNuke(rsmc->axis[axIdx].nweight); |
164 |
|
|
} |
165 |
|
|
airFree(rsmc->flag); |
166 |
|
|
airFree(rsmc); |
167 |
|
|
} |
168 |
|
|
return NULL; |
169 |
|
|
} |
170 |
|
|
|
171 |
|
|
int |
172 |
|
|
nrrdResampleDefaultCenterSet(NrrdResampleContext *rsmc, |
173 |
|
|
int center) { |
174 |
|
|
static const char me[]="nrrdResampleDefaultCenterSet"; |
175 |
|
|
|
176 |
|
|
if (!( rsmc )) { |
177 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
178 |
|
|
return 1; |
179 |
|
|
} |
180 |
|
|
if (!(nrrdCenterNode == center |
181 |
|
|
|| nrrdCenterCell == center)) { |
182 |
|
|
biffAddf(NRRD, "%s: got invalid center (%d)", me, center); |
183 |
|
|
return 1; |
184 |
|
|
} |
185 |
|
|
|
186 |
|
|
if (center != rsmc->defaultCenter) { |
187 |
|
|
rsmc->defaultCenter = center; |
188 |
|
|
rsmc->flag[flagDefaultCenter] = AIR_TRUE; |
189 |
|
|
} |
190 |
|
|
|
191 |
|
|
return 0; |
192 |
|
|
} |
193 |
|
|
|
194 |
|
|
int |
195 |
|
|
nrrdResampleNonExistentSet(NrrdResampleContext *rsmc, |
196 |
|
|
int nonExist) { |
197 |
|
|
static const char me[]="nrrdResampleNonExistentSet"; |
198 |
|
|
|
199 |
|
|
if (!( rsmc )) { |
200 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
201 |
|
|
return 1; |
202 |
|
|
} |
203 |
|
|
if (airEnumValCheck(nrrdResampleNonExistent, nonExist)) { |
204 |
|
|
biffAddf(NRRD, "%s: didn't get valid non-existent behavior (%d)", |
205 |
|
|
me, nonExist); |
206 |
|
|
return 1; |
207 |
|
|
} |
208 |
|
|
|
209 |
|
|
if (nonExist != rsmc->nonExistent) { |
210 |
|
|
rsmc->nonExistent = nonExist; |
211 |
|
|
rsmc->flag[flagNonExistent] = AIR_TRUE; |
212 |
|
|
} |
213 |
|
|
return 0; |
214 |
|
|
} |
215 |
|
|
|
216 |
|
|
#define NRRD_RESAMPLE_INPUT_SET_BODY \ |
217 |
|
|
unsigned int axIdx, kpIdx; \ |
218 |
|
|
\ |
219 |
|
|
if (!( rsmc && nin )) { \ |
220 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); \ |
221 |
|
|
return 1; \ |
222 |
|
|
} \ |
223 |
|
|
if (nrrdCheck(nin)) { \ |
224 |
|
|
biffAddf(NRRD, "%s: problems with given nrrd", me); \ |
225 |
|
|
return 1; \ |
226 |
|
|
} \ |
227 |
|
|
if (nrrdTypeBlock == nin->type) { \ |
228 |
|
|
biffAddf(NRRD, "%s: can't resample from type %s", me, \ |
229 |
|
|
airEnumStr(nrrdType, nrrdTypeBlock)); \ |
230 |
|
|
return 1; \ |
231 |
|
|
} \ |
232 |
|
|
\ |
233 |
|
|
rsmc->nin = nin; \ |
234 |
|
|
rsmc->flag[flagInput] = AIR_TRUE; \ |
235 |
|
|
\ |
236 |
|
|
/* per-axis information should be invalidated at this point, because \ |
237 |
|
|
if we defer the invalidation to later ...Update() calls, it will \ |
238 |
|
|
clobber the effects of intervening calls to the likes of \ |
239 |
|
|
...KernelSet(), ...SampleSet(), and so on */ \ |
240 |
|
|
if (rsmc->dim != nin->dim) { \ |
241 |
|
|
for (axIdx=0; axIdx<NRRD_DIM_MAX; axIdx++) { \ |
242 |
|
|
rsmc->axis[axIdx].center = nrrdCenterUnknown; \ |
243 |
|
|
rsmc->axis[axIdx].sizeIn = 0; \ |
244 |
|
|
rsmc->axis[axIdx].kernel = NULL; \ |
245 |
|
|
rsmc->axis[axIdx].kparm[0] = nrrdDefaultKernelParm0; \ |
246 |
|
|
for (kpIdx=1; kpIdx<NRRD_KERNEL_PARMS_NUM; kpIdx++) { \ |
247 |
|
|
rsmc->axis[axIdx].kparm[kpIdx] = AIR_NAN; \ |
248 |
|
|
} \ |
249 |
|
|
rsmc->axis[axIdx].samples = 0; \ |
250 |
|
|
rsmc->axis[axIdx].min = AIR_NAN; \ |
251 |
|
|
rsmc->axis[axIdx].max = AIR_NAN; \ |
252 |
|
|
} \ |
253 |
|
|
} \ |
254 |
|
|
\ |
255 |
|
|
return 0 |
256 |
|
|
|
257 |
|
|
|
258 |
|
|
int |
259 |
|
|
nrrdResampleNrrdSet(NrrdResampleContext *rsmc, const Nrrd *nin) { |
260 |
|
|
static const char me[]="nrrdResampleNrrdSet"; |
261 |
|
|
|
262 |
|
|
NRRD_RESAMPLE_INPUT_SET_BODY; |
263 |
|
|
} |
264 |
|
|
|
265 |
|
|
int |
266 |
|
|
nrrdResampleInputSet(NrrdResampleContext *rsmc, const Nrrd *nin) { |
267 |
|
|
static const char me[]="nrrdResampleInputSet"; |
268 |
|
|
|
269 |
|
|
NRRD_RESAMPLE_INPUT_SET_BODY; |
270 |
|
|
} |
271 |
|
|
|
272 |
|
|
#define PER_AXIS_ERROR_CHECK \ |
273 |
|
|
if (!rsmc) { \ |
274 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); \ |
275 |
|
|
return 1; \ |
276 |
|
|
} \ |
277 |
|
|
if (!rsmc->nin) { \ |
278 |
|
|
biffAddf(NRRD, "%s: haven't set input nrrd yet", me); \ |
279 |
|
|
return 1; \ |
280 |
|
|
} \ |
281 |
|
|
if (!( axIdx < rsmc->nin->dim )) { \ |
282 |
|
|
biffAddf(NRRD, "%s: axis %u >= nin->dim %u", \ |
283 |
|
|
me, axIdx, rsmc->nin->dim); \ |
284 |
|
|
return 1; \ |
285 |
|
|
} |
286 |
|
|
|
287 |
|
|
int |
288 |
|
|
nrrdResampleKernelSet(NrrdResampleContext *rsmc, unsigned int axIdx, |
289 |
|
|
const NrrdKernel *kernel, |
290 |
|
|
double kparm[NRRD_KERNEL_PARMS_NUM]) { |
291 |
|
|
static const char me[]="nrrdResampleKernelSet"; |
292 |
|
|
unsigned int kpIdx; |
293 |
|
|
|
294 |
|
|
PER_AXIS_ERROR_CHECK; |
295 |
|
|
|
296 |
|
|
rsmc->axis[axIdx].kernel = kernel; |
297 |
|
|
if (kernel) { |
298 |
|
|
for (kpIdx=0; kpIdx<kernel->numParm; kpIdx++) { |
299 |
|
|
rsmc->axis[axIdx].kparm[kpIdx] = kparm[kpIdx]; |
300 |
|
|
} |
301 |
|
|
if (rsmc->verbose) { |
302 |
|
|
char kstr[AIR_STRLEN_LARGE]; |
303 |
|
|
NrrdKernelSpec ksp; |
304 |
|
|
nrrdKernelSpecSet(&ksp, rsmc->axis[axIdx].kernel, |
305 |
|
|
rsmc->axis[axIdx].kparm); |
306 |
|
|
nrrdKernelSpecSprint(kstr, &ksp); |
307 |
|
|
fprintf(stderr, "%s: axis %u kernel %s\n", me, axIdx, kstr); |
308 |
|
|
} |
309 |
|
|
} |
310 |
|
|
rsmc->flag[flagKernels] = AIR_TRUE; |
311 |
|
|
|
312 |
|
|
return 0; |
313 |
|
|
} |
314 |
|
|
|
315 |
|
|
int |
316 |
|
|
nrrdResampleSamplesSet(NrrdResampleContext *rsmc, |
317 |
|
|
unsigned int axIdx, |
318 |
|
|
size_t samples) { |
319 |
|
|
static const char me[]="nrrdResampleSamplesSet"; |
320 |
|
|
|
321 |
|
|
PER_AXIS_ERROR_CHECK; |
322 |
|
|
|
323 |
|
|
if (rsmc->axis[axIdx].samples != samples) { |
324 |
|
|
if (rsmc->verbose) { |
325 |
|
|
fprintf(stderr, "%s: axis %u samples %u --> %u\n", me, axIdx, |
326 |
|
|
AIR_CAST(unsigned int, rsmc->axis[axIdx].samples), |
327 |
|
|
AIR_CAST(unsigned int, samples)); |
328 |
|
|
} |
329 |
|
|
rsmc->axis[axIdx].samples = samples; |
330 |
|
|
rsmc->flag[flagSamples] = AIR_TRUE; |
331 |
|
|
} |
332 |
|
|
|
333 |
|
|
return 0; |
334 |
|
|
} |
335 |
|
|
|
336 |
|
|
int |
337 |
|
|
nrrdResampleRangeSet(NrrdResampleContext *rsmc, |
338 |
|
|
unsigned int axIdx, |
339 |
|
|
double min, double max) { |
340 |
|
|
static const char me[]="nrrdResampleRangeSet"; |
341 |
|
|
|
342 |
|
|
PER_AXIS_ERROR_CHECK; |
343 |
|
|
if (!(AIR_EXISTS(min) && AIR_EXISTS(max) && min != max)) { |
344 |
|
|
biffAddf(NRRD, "%s: need min != max and both to exist", me); |
345 |
|
|
return 1; |
346 |
|
|
} |
347 |
|
|
if (!(rsmc->axis[axIdx].min == min |
348 |
|
|
&& rsmc->axis[axIdx].max == max)) { |
349 |
|
|
rsmc->axis[axIdx].min = min; |
350 |
|
|
rsmc->axis[axIdx].max = max; |
351 |
|
|
rsmc->flag[flagRanges] = AIR_TRUE; |
352 |
|
|
} |
353 |
|
|
|
354 |
|
|
return 0; |
355 |
|
|
} |
356 |
|
|
|
357 |
|
|
int |
358 |
|
|
nrrdResampleOverrideCenterSet(NrrdResampleContext *rsmc, |
359 |
|
|
unsigned int axIdx, |
360 |
|
|
int center) { |
361 |
|
|
static const char me[]="nrrdResampleOverrideCenterSet"; |
362 |
|
|
|
363 |
|
|
PER_AXIS_ERROR_CHECK; |
364 |
|
|
if (center) { |
365 |
|
|
/* we do allow passing nrrdCenterUnknown, to turn off override */ |
366 |
|
|
if (airEnumValCheck(nrrdCenter, center)) { |
367 |
|
|
biffAddf(NRRD, "%s: didn't get valid centering (%d)", me, center); |
368 |
|
|
return 1; |
369 |
|
|
} |
370 |
|
|
} |
371 |
|
|
if (center != rsmc->axis[axIdx].overrideCenter) { |
372 |
|
|
rsmc->axis[axIdx].overrideCenter = center; |
373 |
|
|
rsmc->flag[flagOverrideCenters] = AIR_TRUE; |
374 |
|
|
} |
375 |
|
|
|
376 |
|
|
return 0; |
377 |
|
|
} |
378 |
|
|
|
379 |
|
|
void |
380 |
|
|
_nrrdResampleMinMaxFull(double *minP, double *maxP, |
381 |
|
|
int center, size_t size) { |
382 |
|
|
if (nrrdCenterCell == center) { |
383 |
|
|
*minP = -0.5; |
384 |
|
|
*maxP = size - 0.5; |
385 |
|
|
} else { |
386 |
|
|
*minP = 0.0; |
387 |
|
|
*maxP = size - 1.0; |
388 |
|
|
} |
389 |
|
|
} |
390 |
|
|
|
391 |
|
|
int |
392 |
|
|
nrrdResampleRangeFullSet(NrrdResampleContext *rsmc, |
393 |
|
|
unsigned int axIdx) { |
394 |
|
|
static const char me[]="nrrdResampleRangeFullSet"; |
395 |
|
|
double min, max; |
396 |
|
|
int center; |
397 |
|
|
|
398 |
|
|
PER_AXIS_ERROR_CHECK; |
399 |
|
|
|
400 |
|
|
/* HEY trick is to figure out the axis's centering, and to |
401 |
|
|
make sure its the same code as used elsewhere */ |
402 |
|
|
center = (rsmc->axis[axIdx].overrideCenter |
403 |
|
|
? rsmc->axis[axIdx].overrideCenter |
404 |
|
|
: (rsmc->nin->axis[axIdx].center |
405 |
|
|
? rsmc->nin->axis[axIdx].center |
406 |
|
|
: rsmc->defaultCenter)); |
407 |
|
|
_nrrdResampleMinMaxFull(&min, &max, center, rsmc->nin->axis[axIdx].size); |
408 |
|
|
if (!(rsmc->axis[axIdx].min == min |
409 |
|
|
&& rsmc->axis[axIdx].max == max)) { |
410 |
|
|
rsmc->axis[axIdx].min = min; |
411 |
|
|
rsmc->axis[axIdx].max = max; |
412 |
|
|
rsmc->flag[flagRanges] = AIR_TRUE; |
413 |
|
|
} |
414 |
|
|
|
415 |
|
|
return 0; |
416 |
|
|
} |
417 |
|
|
|
418 |
|
|
int |
419 |
|
|
nrrdResampleBoundarySet(NrrdResampleContext *rsmc, |
420 |
|
|
int boundary) { |
421 |
|
|
static const char me[]="nrrdResampleBoundarySet"; |
422 |
|
|
|
423 |
|
|
if (!rsmc) { |
424 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
425 |
|
|
return 1; |
426 |
|
|
} |
427 |
|
|
if (airEnumValCheck(nrrdBoundary, boundary)) { |
428 |
|
|
biffAddf(NRRD, "%s: invalid boundary %d", me, boundary); |
429 |
|
|
return 1; |
430 |
|
|
} |
431 |
|
|
|
432 |
|
|
if (rsmc->boundary != boundary) { |
433 |
|
|
rsmc->boundary = boundary; |
434 |
|
|
rsmc->flag[flagBoundary] = AIR_TRUE; |
435 |
|
|
} |
436 |
|
|
|
437 |
|
|
return 0; |
438 |
|
|
} |
439 |
|
|
|
440 |
|
|
int |
441 |
|
|
nrrdResamplePadValueSet(NrrdResampleContext *rsmc, |
442 |
|
|
double padValue) { |
443 |
|
|
static const char me[]="nrrdResamplePadValueSet"; |
444 |
|
|
|
445 |
|
|
if (!rsmc) { |
446 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
447 |
|
|
return 1; |
448 |
|
|
} |
449 |
|
|
|
450 |
|
|
if (rsmc->padValue != padValue) { |
451 |
|
|
rsmc->padValue = padValue; |
452 |
|
|
rsmc->flag[flagPadValue] = AIR_TRUE; |
453 |
|
|
} |
454 |
|
|
|
455 |
|
|
return 0; |
456 |
|
|
} |
457 |
|
|
|
458 |
|
|
int |
459 |
|
|
nrrdResampleBoundarySpecSet(NrrdResampleContext *rsmc, |
460 |
|
|
const NrrdBoundarySpec *bspec) { |
461 |
|
|
static const char me[]="nrrdResampleBoundarySpecSet"; |
462 |
|
|
|
463 |
|
|
if (!(rsmc && bspec)) { |
464 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
465 |
|
|
return 1; |
466 |
|
|
} |
467 |
|
|
|
468 |
|
|
if (rsmc->boundary != bspec->boundary) { |
469 |
|
|
rsmc->boundary = bspec->boundary; |
470 |
|
|
rsmc->flag[flagBoundary] = AIR_TRUE; |
471 |
|
|
} |
472 |
|
|
if (rsmc->padValue != bspec->padValue) { |
473 |
|
|
rsmc->padValue = bspec->padValue; |
474 |
|
|
rsmc->flag[flagPadValue] = AIR_TRUE; |
475 |
|
|
} |
476 |
|
|
|
477 |
|
|
return 0; |
478 |
|
|
} |
479 |
|
|
|
480 |
|
|
int |
481 |
|
|
nrrdResampleRenormalizeSet(NrrdResampleContext *rsmc, |
482 |
|
|
int renormalize) { |
483 |
|
|
static const char me[]="nrrdResampleRenormalizeSet"; |
484 |
|
|
|
485 |
|
|
if (!rsmc) { |
486 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
487 |
|
|
return 1; |
488 |
|
|
} |
489 |
|
|
|
490 |
|
|
if (rsmc->renormalize != renormalize) { |
491 |
|
|
rsmc->renormalize = renormalize; |
492 |
|
|
rsmc->flag[flagRenormalize] = AIR_TRUE; |
493 |
|
|
} |
494 |
|
|
|
495 |
|
|
return 0; |
496 |
|
|
} |
497 |
|
|
|
498 |
|
|
int |
499 |
|
|
nrrdResampleTypeOutSet(NrrdResampleContext *rsmc, |
500 |
|
|
int type) { |
501 |
|
|
static const char me[]="nrrdResampleTypeOutSet"; |
502 |
|
|
|
503 |
|
|
if (!rsmc) { |
504 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
505 |
|
|
return 1; |
506 |
|
|
} |
507 |
|
|
if (nrrdTypeDefault != type && airEnumValCheck(nrrdType, type)) { |
508 |
|
|
biffAddf(NRRD, "%s: invalid type %d", me, type); |
509 |
|
|
return 1; |
510 |
|
|
} |
511 |
|
|
if (nrrdTypeBlock == type) { |
512 |
|
|
biffAddf(NRRD, "%s: can't output %s type", me, |
513 |
|
|
airEnumStr(nrrdType, nrrdTypeBlock)); |
514 |
|
|
return 1; |
515 |
|
|
} |
516 |
|
|
|
517 |
|
|
if (rsmc->typeOut != type) { |
518 |
|
|
rsmc->typeOut = type; |
519 |
|
|
rsmc->flag[flagTypeOut] = AIR_TRUE; |
520 |
|
|
} |
521 |
|
|
|
522 |
|
|
return 0; |
523 |
|
|
} |
524 |
|
|
|
525 |
|
|
int |
526 |
|
|
nrrdResampleRoundSet(NrrdResampleContext *rsmc, |
527 |
|
|
int roundlast) { |
528 |
|
|
static const char me[]="nrrdResampleRoundSet"; |
529 |
|
|
|
530 |
|
|
if (!rsmc) { |
531 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
532 |
|
|
return 1; |
533 |
|
|
} |
534 |
|
|
|
535 |
|
|
if (rsmc->roundlast != roundlast) { |
536 |
|
|
rsmc->roundlast = roundlast; |
537 |
|
|
rsmc->flag[flagRound] = AIR_TRUE; |
538 |
|
|
} |
539 |
|
|
|
540 |
|
|
return 0; |
541 |
|
|
} |
542 |
|
|
|
543 |
|
|
int |
544 |
|
|
nrrdResampleClampSet(NrrdResampleContext *rsmc, |
545 |
|
|
int clamp) { |
546 |
|
|
static const char me[]="nrrdResampleClampSet"; |
547 |
|
|
|
548 |
|
|
if (!rsmc) { |
549 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
550 |
|
|
return 1; |
551 |
|
|
} |
552 |
|
|
|
553 |
|
|
if (rsmc->clamp != clamp) { |
554 |
|
|
rsmc->clamp = clamp; |
555 |
|
|
rsmc->flag[flagClamp] = AIR_TRUE; |
556 |
|
|
} |
557 |
|
|
|
558 |
|
|
return 0; |
559 |
|
|
} |
560 |
|
|
|
561 |
|
|
int |
562 |
|
|
_nrrdResampleInputDimensionUpdate(NrrdResampleContext *rsmc) { |
563 |
|
|
|
564 |
|
|
if (rsmc->flag[flagInput]) { |
565 |
|
|
if (rsmc->dim != rsmc->nin->dim) { |
566 |
|
|
rsmc->dim = rsmc->nin->dim; |
567 |
|
|
rsmc->flag[flagInputDimension] = AIR_TRUE; |
568 |
|
|
} |
569 |
|
|
} |
570 |
|
|
return 0; |
571 |
|
|
} |
572 |
|
|
|
573 |
|
|
int |
574 |
|
|
_nrrdResampleInputCentersUpdate(NrrdResampleContext *rsmc) { |
575 |
|
|
unsigned int axIdx; |
576 |
|
|
int center; |
577 |
|
|
|
578 |
|
|
if (rsmc->flag[flagOverrideCenters] |
579 |
|
|
|| rsmc->flag[flagDefaultCenter] |
580 |
|
|
|| rsmc->flag[flagInputDimension] |
581 |
|
|
|| rsmc->flag[flagInput]) { |
582 |
|
|
for (axIdx=0; axIdx<NRRD_DIM_MAX; axIdx++) { |
583 |
|
|
center = (rsmc->axis[axIdx].overrideCenter |
584 |
|
|
? rsmc->axis[axIdx].overrideCenter |
585 |
|
|
: (rsmc->nin->axis[axIdx].center |
586 |
|
|
? rsmc->nin->axis[axIdx].center |
587 |
|
|
: rsmc->defaultCenter)); |
588 |
|
|
if (rsmc->axis[axIdx].center != center) { |
589 |
|
|
rsmc->axis[axIdx].center = center; |
590 |
|
|
rsmc->flag[flagInputCenters] = AIR_TRUE; |
591 |
|
|
} |
592 |
|
|
} |
593 |
|
|
rsmc->flag[flagOverrideCenters] = AIR_FALSE; |
594 |
|
|
rsmc->flag[flagDefaultCenter] = AIR_FALSE; |
595 |
|
|
} |
596 |
|
|
|
597 |
|
|
return 0; |
598 |
|
|
} |
599 |
|
|
|
600 |
|
|
int |
601 |
|
|
_nrrdResampleInputSizesUpdate(NrrdResampleContext *rsmc) { |
602 |
|
|
unsigned int axIdx; |
603 |
|
|
|
604 |
|
|
if (rsmc->flag[flagInputDimension] |
605 |
|
|
|| rsmc->flag[flagInput]) { |
606 |
|
|
for (axIdx=0; axIdx<rsmc->dim; axIdx++) { |
607 |
|
|
if (rsmc->axis[axIdx].sizeIn != rsmc->nin->axis[axIdx].size) { |
608 |
|
|
rsmc->axis[axIdx].sizeIn = rsmc->nin->axis[axIdx].size; |
609 |
|
|
rsmc->flag[flagInputSizes] = AIR_TRUE; |
610 |
|
|
} |
611 |
|
|
} |
612 |
|
|
rsmc->flag[flagInputDimension] = AIR_FALSE; |
613 |
|
|
} |
614 |
|
|
|
615 |
|
|
return 0; |
616 |
|
|
} |
617 |
|
|
|
618 |
|
|
int |
619 |
|
|
_nrrdResampleLineAllocateUpdate(NrrdResampleContext *rsmc) { |
620 |
|
|
static const char me[]="_nrrdResampleLineAllocateUpdate"; |
621 |
|
|
unsigned int axIdx; |
622 |
|
|
NrrdResampleAxis *axis; |
623 |
|
|
|
624 |
|
|
if (rsmc->flag[flagInputSizes] |
625 |
|
|
|| rsmc->flag[flagKernels]) { |
626 |
|
|
for (axIdx=0; axIdx<rsmc->dim; axIdx++) { |
627 |
|
|
axis = rsmc->axis + axIdx; |
628 |
|
|
if (!axis->kernel) { |
629 |
|
|
nrrdEmpty(axis->nline); |
630 |
|
|
} else { |
631 |
|
|
if (nrrdMaybeAlloc_va(axis->nline, nrrdResample_nt, 1, |
632 |
|
|
AIR_CAST(size_t, 1 + axis->sizeIn))) { |
633 |
|
|
biffAddf(NRRD, "%s: couldn't allocate scanline buffer", me); |
634 |
|
|
return 1; |
635 |
|
|
} |
636 |
|
|
} |
637 |
|
|
} |
638 |
|
|
rsmc->flag[flagLineAllocate] = AIR_TRUE; |
639 |
|
|
} |
640 |
|
|
return 0; |
641 |
|
|
} |
642 |
|
|
|
643 |
|
|
int |
644 |
|
|
_nrrdResampleVectorAllocateUpdate(NrrdResampleContext *rsmc) { |
645 |
|
|
static const char me[]="_nrrdResampleVectorAllocateUpdate"; |
646 |
|
|
unsigned int axIdx, kpIdx, dotLen, minSamples; |
647 |
|
|
nrrdResample_t spacingOut, support; |
648 |
|
|
NrrdResampleAxis *axis; |
649 |
|
|
|
650 |
|
|
if (rsmc->flag[flagKernels] |
651 |
|
|
|| rsmc->flag[flagSamples] |
652 |
|
|
|| rsmc->flag[flagRanges]) { |
653 |
|
|
for (axIdx=0; axIdx<rsmc->dim; axIdx++) { |
654 |
|
|
axis = rsmc->axis + axIdx; |
655 |
|
|
if (!axis->kernel) { |
656 |
|
|
/* no resampling on this axis */ |
657 |
|
|
continue; |
658 |
|
|
} |
659 |
|
|
/* check user-set parameters */ |
660 |
|
|
if (!( AIR_EXISTS(axis->min) && AIR_EXISTS(axis->max) )) { |
661 |
|
|
biffAddf(NRRD, "%s: don't have min, max set on axis %u", me, axIdx); |
662 |
|
|
return 1; |
663 |
|
|
} |
664 |
|
|
for (kpIdx=0; kpIdx<axis->kernel->numParm; kpIdx++) { |
665 |
|
|
if (!AIR_EXISTS(axis->kparm[kpIdx])) { |
666 |
|
|
biffAddf(NRRD, "%s: didn't set kernel parm %u on axis %u", |
667 |
|
|
me, kpIdx, axIdx); |
668 |
|
|
return 1; |
669 |
|
|
} |
670 |
|
|
} |
671 |
|
|
minSamples = (nrrdCenterCell == axis->center ? 1 : 2); |
672 |
|
|
if (!( axis->samples >= minSamples )) { |
673 |
|
|
biffAddf(NRRD, "%s: need at least %u output samples (not %u) for " |
674 |
|
|
"%s-centered sampling along axis %u", me, minSamples, |
675 |
|
|
AIR_CAST(unsigned int, axis->samples), |
676 |
|
|
airEnumStr(nrrdCenter, axis->center), axIdx); |
677 |
|
|
return 1; |
678 |
|
|
} |
679 |
|
|
/* compute support (spacingIn == 1.0 by definition) */ |
680 |
|
|
spacingOut = AIR_CAST(nrrdResample_t, ((axis->max - axis->min) |
681 |
|
|
/ (nrrdCenterCell == axis->center |
682 |
|
|
? axis->samples |
683 |
|
|
: axis->samples - 1))); |
684 |
|
|
axis->ratio = 1.0/spacingOut; |
685 |
|
|
support = AIR_CAST(nrrdResample_t, axis->kernel->support(axis->kparm)); |
686 |
|
|
if (axis->ratio > 1) { |
687 |
|
|
/* if upsampling, we need only as many samples as needed for |
688 |
|
|
interpolation with the given kernel */ |
689 |
|
|
dotLen = (int)(2*ceil(support)); |
690 |
|
|
} else { |
691 |
|
|
/* if downsampling, we need to use all the samples covered by |
692 |
|
|
the stretched out version of the kernel */ |
693 |
|
|
dotLen = (int)(2*ceil(support/axis->ratio)); |
694 |
|
|
} |
695 |
|
|
/* some kernels can report zero support when they're basically |
696 |
|
|
delta functions */ |
697 |
|
|
dotLen = AIR_MAX(2, dotLen); |
698 |
|
|
if (nrrdMaybeAlloc_va(axis->nweight, nrrdResample_nt, 2, |
699 |
|
|
AIR_CAST(size_t, dotLen), |
700 |
|
|
AIR_CAST(size_t, axis->samples)) |
701 |
|
|
|| nrrdMaybeAlloc_va(axis->nindex, nrrdTypeInt, 2, |
702 |
|
|
AIR_CAST(size_t, dotLen), |
703 |
|
|
AIR_CAST(size_t, axis->samples))) { |
704 |
|
|
biffAddf(NRRD, "%s: trouble allocating index and weighting vectors", |
705 |
|
|
me); |
706 |
|
|
return 1; |
707 |
|
|
} |
708 |
|
|
} |
709 |
|
|
rsmc->flag[flagRanges] = AIR_FALSE; |
710 |
|
|
rsmc->flag[flagVectorAllocate] = AIR_TRUE; |
711 |
|
|
} |
712 |
|
|
|
713 |
|
|
return 0; |
714 |
|
|
} |
715 |
|
|
|
716 |
|
|
int |
717 |
|
|
_nrrdResampleLineFillUpdate(NrrdResampleContext *rsmc) { |
718 |
|
|
unsigned int axIdx; |
719 |
|
|
NrrdResampleAxis *axis; |
720 |
|
|
nrrdResample_t *line; |
721 |
|
|
|
722 |
|
|
if (rsmc->flag[flagPadValue] |
723 |
|
|
|| rsmc->flag[flagLineAllocate]) { |
724 |
|
|
|
725 |
|
|
for (axIdx=0; axIdx<rsmc->dim; axIdx++) { |
726 |
|
|
axis = rsmc->axis + axIdx; |
727 |
|
|
if (axis->kernel) { |
728 |
|
|
line = (nrrdResample_t*)(axis->nline->data); |
729 |
|
|
line[axis->sizeIn] = AIR_CAST(nrrdResample_t, rsmc->padValue); |
730 |
|
|
} |
731 |
|
|
} |
732 |
|
|
|
733 |
|
|
rsmc->flag[flagPadValue] = AIR_FALSE; |
734 |
|
|
rsmc->flag[flagLineAllocate] = AIR_FALSE; |
735 |
|
|
rsmc->flag[flagLineFill] = AIR_TRUE; |
736 |
|
|
} |
737 |
|
|
return 0; |
738 |
|
|
} |
739 |
|
|
|
740 |
|
|
int |
741 |
|
|
_nrrdResampleVectorFillUpdate(NrrdResampleContext *rsmc) { |
742 |
|
|
static const char me[]="_nrrdResampleVectorFillUpdate"; |
743 |
|
|
unsigned int axIdx, dotIdx, dotLen, halfLen, smpIdx, kpIdx; |
744 |
|
|
int *indexData, tmp, base, rawIdx; |
745 |
|
|
nrrdResample_t *weightData, idx, integral; |
746 |
|
|
NrrdResampleAxis *axis; |
747 |
|
|
double kparm[NRRD_KERNEL_PARMS_NUM]; |
748 |
|
|
|
749 |
|
|
if (rsmc->flag[flagRenormalize] |
750 |
|
|
|| rsmc->flag[flagBoundary] |
751 |
|
|
|| rsmc->flag[flagInputCenters] |
752 |
|
|
|| rsmc->flag[flagInputSizes] |
753 |
|
|
|| rsmc->flag[flagVectorAllocate]) { |
754 |
|
|
if (rsmc->verbose) { |
755 |
|
|
for (axIdx=0; axIdx<rsmc->dim; axIdx++) { |
756 |
|
|
if (rsmc->axis[axIdx].kernel) { |
757 |
|
|
fprintf(stderr, "%s: axis %u: %s-centering\n", me, axIdx, |
758 |
|
|
airEnumStr(nrrdCenter, rsmc->axis[axIdx].center)); |
759 |
|
|
} |
760 |
|
|
} |
761 |
|
|
} |
762 |
|
|
|
763 |
|
|
for (axIdx=0; axIdx<rsmc->dim; axIdx++) { |
764 |
|
|
axis = rsmc->axis + axIdx; |
765 |
|
|
if (!axis->kernel) { |
766 |
|
|
/* no resampling on this axis */ |
767 |
|
|
continue; |
768 |
|
|
} |
769 |
|
|
|
770 |
|
|
/* calculate sample locations and do first pass on indices */ |
771 |
|
|
indexData = (int *)axis->nindex->data; |
772 |
|
|
weightData = (nrrdResample_t *)axis->nweight->data; |
773 |
|
|
dotLen = AIR_CAST(unsigned int, axis->nweight->axis[0].size); |
774 |
|
|
halfLen = dotLen/2; |
775 |
|
|
for (smpIdx=0; smpIdx<axis->samples; smpIdx++) { |
776 |
|
|
idx = AIR_CAST(nrrdResample_t, |
777 |
|
|
(nrrdCenterCell == axis->center |
778 |
|
|
? AIR_AFFINE(-0.5, smpIdx, axis->samples-0.5, |
779 |
|
|
axis->min, axis->max) |
780 |
|
|
: AIR_AFFINE(0.0, smpIdx, axis->samples-1.0, |
781 |
|
|
axis->min, axis->max))); |
782 |
|
|
base = (int)floor(idx) - halfLen + 1; |
783 |
|
|
for (dotIdx=0; dotIdx<dotLen; dotIdx++) { |
784 |
|
|
tmp = indexData[dotIdx + dotLen*smpIdx] = base + dotIdx; |
785 |
|
|
weightData[dotIdx + dotLen*smpIdx] = idx - tmp; |
786 |
|
|
} |
787 |
|
|
if (rsmc->verbose) { |
788 |
|
|
if (!smpIdx) { |
789 |
|
|
fprintf(stderr, "%s: smpIdx=%u -> idx=%g -> base=%d\n", me, |
790 |
|
|
smpIdx, idx, base); |
791 |
|
|
fprintf(stderr, "%s: sample locations:\n", me); |
792 |
|
|
} |
793 |
|
|
fprintf(stderr, "%s: %d (sample locations)\n ", me, smpIdx); |
794 |
|
|
for (dotIdx=0; dotIdx<dotLen; dotIdx++) { |
795 |
|
|
fprintf(stderr, "%d/%g ", |
796 |
|
|
indexData[dotIdx + dotLen*smpIdx], |
797 |
|
|
weightData[dotIdx + dotLen*smpIdx]); |
798 |
|
|
} |
799 |
|
|
fprintf(stderr, "\n"); |
800 |
|
|
} |
801 |
|
|
} |
802 |
|
|
|
803 |
|
|
/* figure out what to do with the out-of-range indices */ |
804 |
|
|
for (dotIdx=0; dotIdx<dotLen*axis->samples; dotIdx++) { |
805 |
|
|
rawIdx = indexData[dotIdx]; |
806 |
|
|
if (!AIR_IN_CL(0, rawIdx, AIR_CAST(int, axis->sizeIn)-1)) { |
807 |
|
|
switch(rsmc->boundary) { |
808 |
|
|
case nrrdBoundaryPad: |
809 |
|
|
case nrrdBoundaryWeight: /* this will be further handled later */ |
810 |
|
|
rawIdx = AIR_CAST(int, axis->sizeIn); |
811 |
|
|
break; |
812 |
|
|
case nrrdBoundaryBleed: |
813 |
|
|
rawIdx = AIR_CLAMP(0, rawIdx, AIR_CAST(int, axis->sizeIn)-1); |
814 |
|
|
break; |
815 |
|
|
case nrrdBoundaryWrap: |
816 |
|
|
rawIdx = AIR_MOD(rawIdx, AIR_CAST(int, axis->sizeIn)); |
817 |
|
|
break; |
818 |
|
|
case nrrdBoundaryMirror: |
819 |
|
|
rawIdx = _nrrdMirror_32(AIR_CAST(unsigned int, axis->sizeIn), rawIdx); |
820 |
|
|
break; |
821 |
|
|
default: |
822 |
|
|
biffAddf(NRRD, "%s: boundary behavior %d unknown/unimplemented", |
823 |
|
|
me, rsmc->boundary); |
824 |
|
|
return 0; |
825 |
|
|
} |
826 |
|
|
indexData[dotIdx] = rawIdx; |
827 |
|
|
} |
828 |
|
|
} |
829 |
|
|
|
830 |
|
|
/* Wow - there is a big rift here between old conventions for how |
831 |
|
|
NrrdKernels were defined, versus the newer practice of creating |
832 |
|
|
parameter-free kernels. The "sneaky trick" code below for changing |
833 |
|
|
parm[0] only works if the kernel actually looks at parm[0]! So at |
834 |
|
|
least for the parameter-free kernels (and maybe other kernels, but |
835 |
|
|
HEY there's no principled way of knowing!) we have to do what we |
836 |
|
|
probably should have been done all along: simulating the kernel |
837 |
|
|
scaling by pre-processing the evaluation locations and |
838 |
|
|
post-processing the kernel weights */ |
839 |
|
|
if (0 == axis->kernel->numParm) { |
840 |
|
|
size_t nn, ii; |
841 |
|
|
double ratio; |
842 |
|
|
nn = dotLen*axis->samples; |
843 |
|
|
ratio = axis->ratio; |
844 |
|
|
if (ratio < 1) { |
845 |
|
|
for (ii=0; ii<nn; ii++) { |
846 |
|
|
weightData[ii] *= ratio; |
847 |
|
|
} |
848 |
|
|
} |
849 |
|
|
axis->kernel->EVALN(weightData, weightData, nn, axis->kparm); |
850 |
|
|
if (ratio < 1) { |
851 |
|
|
for (ii=0; ii<nn; ii++) { |
852 |
|
|
weightData[ii] *= ratio; |
853 |
|
|
} |
854 |
|
|
} |
855 |
|
|
} else { |
856 |
|
|
/* run the sample locations through the chosen kernel. We play a |
857 |
|
|
sneaky trick on the kernel parameter 0 in case of downsampling |
858 |
|
|
to create the blurring of the old index space */ |
859 |
|
|
kparm[0] = (axis->ratio < 1 |
860 |
|
|
? axis->kparm[0] / axis->ratio |
861 |
|
|
: axis->kparm[0]); |
862 |
|
|
for (kpIdx=1; kpIdx<NRRD_KERNEL_PARMS_NUM; kpIdx++) { |
863 |
|
|
kparm[kpIdx] = axis->kparm[kpIdx]; |
864 |
|
|
} |
865 |
|
|
axis->kernel->EVALN(weightData, weightData, |
866 |
|
|
dotLen*axis->samples, kparm); |
867 |
|
|
|
868 |
|
|
/* special handling of "cheap" kernel */ |
869 |
|
|
if (nrrdKernelCheap == axis->kernel) { |
870 |
|
|
for (smpIdx=0; smpIdx<axis->samples; smpIdx++) { |
871 |
|
|
nrrdResample_t dist, minDist; |
872 |
|
|
int minIdx, minSet; |
873 |
|
|
minIdx = indexData[0 + dotLen*smpIdx]; |
874 |
|
|
minDist = weightData[0 + dotLen*smpIdx]; |
875 |
|
|
/* find sample closest to origin */ |
876 |
|
|
for (dotIdx=1; dotIdx<dotLen; dotIdx++) { |
877 |
|
|
dist = weightData[dotIdx + dotLen*smpIdx]; |
878 |
|
|
if (dist < minDist) { |
879 |
|
|
minDist = dist; |
880 |
|
|
minIdx = indexData[dotIdx + dotLen*smpIdx]; |
881 |
|
|
} |
882 |
|
|
} |
883 |
|
|
/* set kernel weights to select sample closest to origin */ |
884 |
|
|
minSet = AIR_FALSE; |
885 |
|
|
for (dotIdx=0; dotIdx<dotLen; dotIdx++) { |
886 |
|
|
if (minIdx == indexData[dotIdx + dotLen*smpIdx] && !minSet) { |
887 |
|
|
weightData[dotIdx + dotLen*smpIdx] = 1.0; |
888 |
|
|
minSet = AIR_TRUE; |
889 |
|
|
} else { |
890 |
|
|
weightData[dotIdx + dotLen*smpIdx] = 0.0; |
891 |
|
|
} |
892 |
|
|
} |
893 |
|
|
} |
894 |
|
|
} |
895 |
|
|
} |
896 |
|
|
|
897 |
|
|
if (rsmc->verbose) { |
898 |
|
|
fprintf(stderr, "%s: axis %u sample weights:\n", me, axIdx); |
899 |
|
|
for (smpIdx=0; smpIdx<axis->samples; smpIdx++) { |
900 |
|
|
fprintf(stderr, "%s: %d (sample weights)\n ", me, smpIdx); |
901 |
|
|
for (dotIdx=0; dotIdx<dotLen; dotIdx++) { |
902 |
|
|
fprintf(stderr, "%d/%g ", indexData[dotIdx + dotLen*smpIdx], |
903 |
|
|
weightData[dotIdx + dotLen*smpIdx]); |
904 |
|
|
} |
905 |
|
|
fprintf(stderr, "\n"); |
906 |
|
|
} |
907 |
|
|
} |
908 |
|
|
|
909 |
|
|
/* final fixes on weighting values */ |
910 |
|
|
integral = AIR_CAST(nrrdResample_t, axis->kernel->integral(axis->kparm)); |
911 |
|
|
if (nrrdBoundaryWeight == rsmc->boundary) { |
912 |
|
|
if (integral) { |
913 |
|
|
/* above, we set to axis->sizeIn all the indices that were out of |
914 |
|
|
range. We now use that to determine the sum of the weights |
915 |
|
|
for the indices that were in-range */ |
916 |
|
|
for (smpIdx=0; smpIdx<axis->samples; smpIdx++) { |
917 |
|
|
nrrdResample_t wght = 0; |
918 |
|
|
for (dotIdx=0; dotIdx<dotLen; dotIdx++) { |
919 |
|
|
if (AIR_CAST(int, axis->sizeIn) |
920 |
|
|
!= indexData[dotIdx + dotLen*smpIdx]) { |
921 |
|
|
wght += weightData[dotIdx + dotLen*smpIdx]; |
922 |
|
|
} |
923 |
|
|
} |
924 |
|
|
for (dotIdx=0; dotIdx<dotLen; dotIdx++) { |
925 |
|
|
if (AIR_CAST(int, axis->sizeIn) |
926 |
|
|
!= indexData[dotIdx + dotLen*smpIdx]) { |
927 |
|
|
weightData[dotIdx + dotLen*smpIdx] *= integral/wght; |
928 |
|
|
} else { |
929 |
|
|
weightData[dotIdx + dotLen*smpIdx] = 0; |
930 |
|
|
} |
931 |
|
|
} |
932 |
|
|
} |
933 |
|
|
} |
934 |
|
|
} else { |
935 |
|
|
/* try to remove ripple/grating on downsampling, and errors in |
936 |
|
|
weighting on upsampling when using kernels that are not |
937 |
|
|
first-order accurate */ |
938 |
|
|
if (rsmc->renormalize && integral) { |
939 |
|
|
for (smpIdx=0; smpIdx<axis->samples; smpIdx++) { |
940 |
|
|
nrrdResample_t wght = 0; |
941 |
|
|
for (dotIdx=0; dotIdx<dotLen; dotIdx++) { |
942 |
|
|
wght += weightData[dotIdx + dotLen*smpIdx]; |
943 |
|
|
} |
944 |
|
|
if (wght) { |
945 |
|
|
for (dotIdx=0; dotIdx<dotLen; dotIdx++) { |
946 |
|
|
/* this used to normalize the weights so that they summed |
947 |
|
|
to integral ("*= integral/wght"), which meant that if |
948 |
|
|
you use a very truncated Gaussian, then your over-all |
949 |
|
|
image brightness goes down. This seems very contrary |
950 |
|
|
to the whole point of renormalization. */ |
951 |
|
|
weightData[dotIdx + dotLen*smpIdx] *= |
952 |
|
|
AIR_CAST(nrrdResample_t, 1.0/wght); |
953 |
|
|
} |
954 |
|
|
} |
955 |
|
|
} |
956 |
|
|
} |
957 |
|
|
} |
958 |
|
|
|
959 |
|
|
if (rsmc->verbose) { |
960 |
|
|
fprintf(stderr, "%s: axis %u post-correction sample weights:\n", |
961 |
|
|
me, axIdx); |
962 |
|
|
for (smpIdx=0; smpIdx<axis->samples; smpIdx++) { |
963 |
|
|
fprintf(stderr, "%s: %d (sample weights)\n ", me, smpIdx); |
964 |
|
|
for (dotIdx=0; dotIdx<dotLen; dotIdx++) { |
965 |
|
|
fprintf(stderr, "%d/%g ", indexData[dotIdx + dotLen*smpIdx], |
966 |
|
|
weightData[dotIdx + dotLen*smpIdx]); |
967 |
|
|
} |
968 |
|
|
fprintf(stderr, "\n"); |
969 |
|
|
} |
970 |
|
|
} |
971 |
|
|
} |
972 |
|
|
rsmc->flag[flagRenormalize] = AIR_FALSE; |
973 |
|
|
rsmc->flag[flagBoundary] = AIR_FALSE; |
974 |
|
|
rsmc->flag[flagInputCenters] = AIR_FALSE; |
975 |
|
|
rsmc->flag[flagVectorAllocate] = AIR_FALSE; |
976 |
|
|
rsmc->flag[flagVectorFill] = AIR_TRUE; |
977 |
|
|
} |
978 |
|
|
|
979 |
|
|
return 0; |
980 |
|
|
} |
981 |
|
|
|
982 |
|
|
int |
983 |
|
|
_nrrdResamplePermutationUpdate(NrrdResampleContext *rsmc) { |
984 |
|
|
static const char me[]="_nrrdResamplePermutationUpdate"; |
985 |
|
|
unsigned int axIdx, passIdx, currTop, lastTop, fromTop, toTop; |
986 |
|
|
int bi; |
987 |
|
|
|
988 |
|
|
if (rsmc->flag[flagInputSizes] |
989 |
|
|
|| rsmc->flag[flagKernels] |
990 |
|
|
|| rsmc->flag[flagSamples]) { |
991 |
|
|
|
992 |
|
|
rsmc->topRax = rsmc->botRax = AIR_CAST(unsigned int, -1); |
993 |
|
|
for (axIdx=0; axIdx<rsmc->dim; axIdx++) { |
994 |
|
|
if (rsmc->axis[axIdx].kernel) { |
995 |
|
|
if (AIR_CAST(unsigned int, -1) == rsmc->topRax) { |
996 |
|
|
rsmc->topRax = axIdx; |
997 |
|
|
} |
998 |
|
|
rsmc->botRax = axIdx; |
999 |
|
|
} |
1000 |
|
|
} |
1001 |
|
|
if (rsmc->verbose) { |
1002 |
|
|
fprintf(stderr, "%s: topRax = %u (%d); botRax = %u (%d)\n", me, |
1003 |
|
|
rsmc->topRax, AIR_CAST(int, rsmc->topRax), |
1004 |
|
|
rsmc->botRax, AIR_CAST(int, rsmc->botRax)); |
1005 |
|
|
} |
1006 |
|
|
|
1007 |
|
|
/* figure out total number of passes needed, and construct the |
1008 |
|
|
permute[] array. permute[i] = j means that the axis in position |
1009 |
|
|
i of the old array will be in position j of the new one |
1010 |
|
|
(permute[] answers "where do I put this", not "what got put here"). |
1011 |
|
|
*/ |
1012 |
|
|
rsmc->passNum = 0; |
1013 |
|
|
bi = 0; |
1014 |
|
|
for (axIdx=0; axIdx<rsmc->dim; axIdx++) { |
1015 |
|
|
if (rsmc->axis[axIdx].kernel) { |
1016 |
|
|
do { |
1017 |
|
|
bi = AIR_MOD(bi+1, AIR_CAST(int, rsmc->dim)); |
1018 |
|
|
} while (!rsmc->axis[bi].kernel); |
1019 |
|
|
rsmc->permute[bi] = axIdx; |
1020 |
|
|
rsmc->passNum += 1; |
1021 |
|
|
} else { |
1022 |
|
|
rsmc->permute[axIdx] = axIdx; |
1023 |
|
|
bi += bi == AIR_CAST(int, axIdx); |
1024 |
|
|
} |
1025 |
|
|
} |
1026 |
|
|
rsmc->permute[rsmc->dim] = rsmc->dim; /* HEY: what is this for? */ |
1027 |
|
|
|
1028 |
|
|
if (rsmc->passNum) { |
1029 |
|
|
toTop = AIR_CAST(unsigned int, -1); |
1030 |
|
|
for (axIdx=0; axIdx<rsmc->dim; axIdx++) { |
1031 |
|
|
/* this will always "break" somewhere */ |
1032 |
|
|
if (rsmc->topRax == rsmc->permute[axIdx]) { |
1033 |
|
|
toTop = axIdx; |
1034 |
|
|
break; |
1035 |
|
|
} |
1036 |
|
|
} |
1037 |
|
|
fromTop = rsmc->permute[rsmc->topRax]; |
1038 |
|
|
|
1039 |
|
|
if (rsmc->verbose) { |
1040 |
|
|
fprintf(stderr, "%s: passNum = %u; permute =\n ", |
1041 |
|
|
me, rsmc->passNum); |
1042 |
|
|
for (axIdx=0; axIdx<rsmc->dim; axIdx++) { |
1043 |
|
|
fprintf(stderr, "%u ", rsmc->permute[axIdx]); |
1044 |
|
|
} |
1045 |
|
|
fprintf(stderr, "\n"); |
1046 |
|
|
fprintf(stderr, "%s: toTop = %u; fromTop = %u\n", me, toTop, fromTop); |
1047 |
|
|
} |
1048 |
|
|
|
1049 |
|
|
/* create array of how the axes will be arranged in each pass ("ax"), |
1050 |
|
|
and create array of how big each axes is in each pass ("sz"). |
1051 |
|
|
The input to pass i will have axis layout described in ax[i] and |
1052 |
|
|
axis sizes described in sz[i] */ |
1053 |
|
|
passIdx = 0; |
1054 |
|
|
currTop = rsmc->topRax; |
1055 |
|
|
rsmc->passAxis[passIdx] = currTop; |
1056 |
|
|
rsmc->axis[currTop].passIdx = passIdx; |
1057 |
|
|
for (axIdx=0; axIdx<rsmc->dim; axIdx++) { |
1058 |
|
|
rsmc->axis[currTop].axisPerm[axIdx] = axIdx; |
1059 |
|
|
rsmc->axis[currTop].sizePerm[axIdx] = rsmc->axis[axIdx].sizeIn; |
1060 |
|
|
} |
1061 |
|
|
for (passIdx=1; passIdx<rsmc->passNum+1; passIdx++) { |
1062 |
|
|
lastTop = currTop; |
1063 |
|
|
currTop = (passIdx<rsmc->passNum |
1064 |
|
|
? rsmc->axis[currTop].axisPerm[toTop] |
1065 |
|
|
: NRRD_DIM_MAX); |
1066 |
|
|
rsmc->passAxis[passIdx] = currTop; |
1067 |
|
|
rsmc->axis[currTop].passIdx = passIdx; |
1068 |
|
|
for (axIdx=0; axIdx<rsmc->dim; axIdx++) { |
1069 |
|
|
rsmc->axis[currTop].axisPerm[rsmc->permute[axIdx]] |
1070 |
|
|
= rsmc->axis[lastTop].axisPerm[axIdx]; |
1071 |
|
|
rsmc->axis[currTop].sizePerm[rsmc->permute[axIdx]] |
1072 |
|
|
= rsmc->axis[lastTop].sizePerm[axIdx]; |
1073 |
|
|
/* modify the one size corresponding to the resampled axis */ |
1074 |
|
|
rsmc->axis[currTop].sizePerm[fromTop] = rsmc->axis[lastTop].samples; |
1075 |
|
|
} |
1076 |
|
|
} |
1077 |
|
|
if (rsmc->verbose) { |
1078 |
|
|
NrrdResampleAxis *axis; |
1079 |
|
|
fprintf(stderr, "%s: axis and size permutations:\n", me); |
1080 |
|
|
for (passIdx=0; passIdx<rsmc->passNum+1; passIdx++) { |
1081 |
|
|
axis = rsmc->axis + rsmc->passAxis[passIdx]; |
1082 |
|
|
fprintf(stderr, "----- pass[%u=?=%u] @ %u %s:\n", passIdx, |
1083 |
|
|
axis->passIdx, rsmc->passAxis[passIdx], |
1084 |
|
|
(passIdx<rsmc->passNum ? "" : "(output of final pass)")); |
1085 |
|
|
if (!passIdx) { |
1086 |
|
|
fprintf(stderr, "resampling: "); |
1087 |
|
|
for (axIdx=0; axIdx<rsmc->dim; axIdx++) { |
1088 |
|
|
fprintf(stderr, "%s ", rsmc->axis[axIdx].kernel ? " XX" : " "); |
1089 |
|
|
} |
1090 |
|
|
fprintf(stderr, "\n"); |
1091 |
|
|
} |
1092 |
|
|
fprintf(stderr, " axes: "); |
1093 |
|
|
for (axIdx=0; axIdx<rsmc->dim; axIdx++) { |
1094 |
|
|
fprintf(stderr, "%3u ", axis->axisPerm[axIdx]); |
1095 |
|
|
} |
1096 |
|
|
fprintf(stderr, "\n"); |
1097 |
|
|
fprintf(stderr, " sizes: "); |
1098 |
|
|
for (axIdx=0; axIdx<rsmc->dim; axIdx++) { |
1099 |
|
|
fprintf(stderr, "%3u ", |
1100 |
|
|
AIR_CAST(unsigned int, axis->sizePerm[axIdx])); |
1101 |
|
|
} |
1102 |
|
|
fprintf(stderr, "\n"); |
1103 |
|
|
} |
1104 |
|
|
fprintf(stderr, "\n"); |
1105 |
|
|
} |
1106 |
|
|
} |
1107 |
|
|
|
1108 |
|
|
rsmc->flag[flagInputSizes] = AIR_FALSE; |
1109 |
|
|
rsmc->flag[flagKernels] = AIR_FALSE; |
1110 |
|
|
rsmc->flag[flagSamples] = AIR_FALSE; |
1111 |
|
|
rsmc->flag[flagPermutation] = AIR_TRUE; |
1112 |
|
|
} |
1113 |
|
|
|
1114 |
|
|
return 0; |
1115 |
|
|
} |
1116 |
|
|
|
1117 |
|
|
/* Copy input to output, but with the optional clamping and rounding */ |
1118 |
|
|
int |
1119 |
|
|
_nrrdResampleTrivial(NrrdResampleContext *rsmc, Nrrd *nout, |
1120 |
|
|
int typeOut, int doRound, |
1121 |
|
|
nrrdResample_t (*lup)(const void *, size_t), |
1122 |
|
|
nrrdResample_t (*clamp)(nrrdResample_t), |
1123 |
|
|
nrrdResample_t (*ins)(void *, size_t, nrrdResample_t)) { |
1124 |
|
|
static const char me[]="_nrrdResampleTrivial"; |
1125 |
|
|
size_t size[NRRD_DIM_MAX], valNum, valIdx; |
1126 |
|
|
nrrdResample_t val; |
1127 |
|
|
const void *dataIn; |
1128 |
|
|
void *dataOut; |
1129 |
|
|
|
1130 |
|
|
nrrdAxisInfoGet_nva(rsmc->nin, nrrdAxisInfoSize, size); |
1131 |
|
|
if (nrrdMaybeAlloc_nva(nout, typeOut, rsmc->nin->dim, size)) { |
1132 |
|
|
biffAddf(NRRD, "%s: couldn't allocate output", me); |
1133 |
|
|
return 1; |
1134 |
|
|
} |
1135 |
|
|
valNum = nrrdElementNumber(rsmc->nin); |
1136 |
|
|
dataIn = rsmc->nin->data; |
1137 |
|
|
dataOut = nout->data; |
1138 |
|
|
for (valIdx=0; valIdx<valNum; valIdx++) { |
1139 |
|
|
val = lup(dataIn, valIdx); |
1140 |
|
|
if (doRound) { |
1141 |
|
|
val = AIR_CAST(nrrdResample_t, AIR_ROUNDUP(val)); |
1142 |
|
|
} |
1143 |
|
|
if (rsmc->clamp) { |
1144 |
|
|
val = clamp(val); |
1145 |
|
|
} |
1146 |
|
|
ins(dataOut, valIdx, val); |
1147 |
|
|
} |
1148 |
|
|
|
1149 |
|
|
return 0; |
1150 |
|
|
} |
1151 |
|
|
|
1152 |
|
|
int |
1153 |
|
|
_nrrdResampleCore(NrrdResampleContext *rsmc, Nrrd *nout, |
1154 |
|
|
int typeOut, int doRound, |
1155 |
|
|
nrrdResample_t (*lup)(const void *, size_t), |
1156 |
|
|
nrrdResample_t (*clamp)(nrrdResample_t), |
1157 |
|
|
nrrdResample_t (*ins)(void *, size_t, nrrdResample_t)) { |
1158 |
|
|
static const char me[]="_nrrdResampleCore"; |
1159 |
|
|
unsigned int axIdx, passIdx; |
1160 |
|
|
size_t strideIn, strideOut, lineNum, lineIdx, |
1161 |
|
|
coordIn[NRRD_DIM_MAX], coordOut[NRRD_DIM_MAX]; |
1162 |
|
|
nrrdResample_t *line, *weight, *rsmpIn, *rsmpOut; |
1163 |
|
|
int *indx; |
1164 |
|
|
const void *dataIn; |
1165 |
|
|
void *dataOut; |
1166 |
|
|
NrrdResampleAxis *axisIn, *axisOut; |
1167 |
|
|
airArray *mop; |
1168 |
|
|
|
1169 |
|
|
/* NOTE: there was an odd memory leak here with normal operation (no |
1170 |
|
|
errors), because the final airMopOkay() was missing, but quick |
1171 |
|
|
attempts at resolving it pre-Teem-1.9 release were not successful |
1172 |
|
|
(surprisingly, commenting out the airMopSub's led to a segfault). |
1173 |
|
|
So, the airMopAdd which is supposed to manage the per-axis |
1174 |
|
|
resampling result is commented out, and there are no leaks and |
1175 |
|
|
no segfaults with normal operation, which is good enough for now */ |
1176 |
|
|
|
1177 |
|
|
/* compute strideIn; this is constant across passes because all |
1178 |
|
|
passes resample topRax, and axes with lower indices have |
1179 |
|
|
constant length. */ |
1180 |
|
|
strideIn = 1; |
1181 |
|
|
for (axIdx=0; axIdx<rsmc->topRax; axIdx++) { |
1182 |
|
|
strideIn *= rsmc->axis[axIdx].sizeIn; |
1183 |
|
|
} |
1184 |
|
|
|
1185 |
|
|
mop = airMopNew(); |
1186 |
|
|
for (passIdx=0; passIdx<rsmc->passNum; passIdx++) { |
1187 |
|
|
if (rsmc->verbose) { |
1188 |
|
|
fprintf(stderr, "%s: -------------- pass %u/%u \n", |
1189 |
|
|
me, passIdx, rsmc->passNum); |
1190 |
|
|
} |
1191 |
|
|
|
1192 |
|
|
/* calculate pass-specific size, stride, and number info */ |
1193 |
|
|
axisIn = rsmc->axis + rsmc->passAxis[passIdx]; |
1194 |
|
|
axisOut = rsmc->axis + rsmc->passAxis[passIdx+1]; |
1195 |
|
|
lineNum = strideOut = 1; |
1196 |
|
|
for (axIdx=0; axIdx<rsmc->dim; axIdx++) { |
1197 |
|
|
if (axIdx < rsmc->botRax) { |
1198 |
|
|
strideOut *= axisOut->sizePerm[axIdx]; |
1199 |
|
|
} |
1200 |
|
|
if (axIdx != rsmc->topRax) { |
1201 |
|
|
lineNum *= axisIn->sizePerm[axIdx]; |
1202 |
|
|
} |
1203 |
|
|
} |
1204 |
|
|
if (rsmc->verbose) { |
1205 |
|
|
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; |
1206 |
|
|
fprintf(stderr, "%s(%u): lineNum = %s\n", me, passIdx, |
1207 |
|
|
airSprintSize_t(stmp1, lineNum)); |
1208 |
|
|
fprintf(stderr, "%s(%u): strideIn = %s, strideOut = %s\n", me, passIdx, |
1209 |
|
|
airSprintSize_t(stmp1, strideIn), |
1210 |
|
|
airSprintSize_t(stmp2, strideOut)); |
1211 |
|
|
} |
1212 |
|
|
|
1213 |
|
|
/* allocate output for this pass */ |
1214 |
|
|
if (passIdx < rsmc->passNum-1) { |
1215 |
|
|
axisOut->nrsmp = nrrdNew(); |
1216 |
|
|
/* see NOTE above! |
1217 |
|
|
airMopAdd(mop, axisOut->nrsmp, (airMopper)nrrdNuke, airMopAlways); */ |
1218 |
|
|
if (nrrdMaybeAlloc_nva(axisOut->nrsmp, nrrdResample_nt, rsmc->dim, |
1219 |
|
|
axisOut->sizePerm)) { |
1220 |
|
|
biffAddf(NRRD, "%s: trouble allocating output of pass %u", me, |
1221 |
|
|
passIdx); |
1222 |
|
|
airMopError(mop); return 1; |
1223 |
|
|
} |
1224 |
|
|
if (rsmc->verbose) { |
1225 |
|
|
fprintf(stderr, "%s: allocated pass %u/%u output nrrd @ %p/%p " |
1226 |
|
|
"(on axis %u)\n", me, passIdx, axisIn->passIdx, |
1227 |
|
|
AIR_CAST(void*, axisOut->nrsmp), |
1228 |
|
|
AIR_CAST(void*, axisOut->nrsmp->data), axisOut->axIdx); |
1229 |
|
|
} |
1230 |
|
|
} else { |
1231 |
|
|
if (nrrdMaybeAlloc_nva(nout, typeOut, rsmc->dim, axisOut->sizePerm)) { |
1232 |
|
|
biffAddf(NRRD, "%s: trouble allocating final output", me); |
1233 |
|
|
airMopError(mop); return 1; |
1234 |
|
|
} |
1235 |
|
|
if (rsmc->verbose) { |
1236 |
|
|
fprintf(stderr, "%s: allocated final pass %u output nrrd @ %p/%p\n", |
1237 |
|
|
me, passIdx, |
1238 |
|
|
AIR_CAST(void*, nout), |
1239 |
|
|
AIR_CAST(void*, nout->data)); |
1240 |
|
|
} |
1241 |
|
|
} |
1242 |
|
|
|
1243 |
|
|
/* set up data pointers */ |
1244 |
|
|
if (0 == passIdx) { |
1245 |
|
|
rsmpIn = NULL; |
1246 |
|
|
dataIn = rsmc->nin->data; |
1247 |
|
|
} else { |
1248 |
|
|
rsmpIn = (nrrdResample_t *)(axisIn->nrsmp->data); |
1249 |
|
|
dataIn = NULL; |
1250 |
|
|
} |
1251 |
|
|
if (passIdx < rsmc->passNum-1) { |
1252 |
|
|
rsmpOut = (nrrdResample_t *)(axisOut->nrsmp->data); |
1253 |
|
|
dataOut = NULL; |
1254 |
|
|
} else { |
1255 |
|
|
rsmpOut = NULL; |
1256 |
|
|
dataOut = nout->data; |
1257 |
|
|
} |
1258 |
|
|
|
1259 |
|
|
line = (nrrdResample_t *)(axisIn->nline->data); |
1260 |
|
|
indx = (int *)(axisIn->nindex->data); |
1261 |
|
|
weight = (nrrdResample_t *)(axisIn->nweight->data); |
1262 |
|
|
if (rsmc->verbose) { |
1263 |
|
|
fprintf(stderr, "%s: {rsmp,data}In = %p/%p; {rsmp,data}Out = %p/%p\n", |
1264 |
|
|
me, rsmpIn, dataIn, rsmpOut, dataOut); |
1265 |
|
|
fprintf(stderr, "%s: line = %p; indx = %p; weight = %p\n", |
1266 |
|
|
me, line, indx, weight); |
1267 |
|
|
} |
1268 |
|
|
|
1269 |
|
|
/* the skinny */ |
1270 |
|
|
for (axIdx=0; axIdx<rsmc->dim; axIdx++) { |
1271 |
|
|
coordIn[axIdx] = 0; |
1272 |
|
|
coordOut[axIdx] = 0; |
1273 |
|
|
} |
1274 |
|
|
for (lineIdx=0; lineIdx<lineNum; lineIdx++) { |
1275 |
|
|
size_t smpIdx, dotIdx, dotLen, indexIn, indexOut; |
1276 |
|
|
|
1277 |
|
|
/* calculate the (linear) indices of the beginnings of |
1278 |
|
|
the input and output scanlines */ |
1279 |
|
|
NRRD_INDEX_GEN(indexIn, coordIn, axisIn->sizePerm, rsmc->dim); |
1280 |
|
|
NRRD_INDEX_GEN(indexOut, coordOut, axisOut->sizePerm, rsmc->dim); |
1281 |
|
|
|
1282 |
|
|
/* read input scanline into scanline buffer */ |
1283 |
|
|
if (0 == passIdx) { |
1284 |
|
|
for (smpIdx=0; smpIdx<axisIn->sizeIn; smpIdx++) { |
1285 |
|
|
line[smpIdx] = lup(dataIn, smpIdx*strideIn + indexIn); |
1286 |
|
|
} |
1287 |
|
|
} else { |
1288 |
|
|
for (smpIdx=0; smpIdx<axisIn->sizeIn; smpIdx++) { |
1289 |
|
|
line[smpIdx] = rsmpIn[smpIdx*strideIn + indexIn]; |
1290 |
|
|
} |
1291 |
|
|
} |
1292 |
|
|
/* do the bloody convolution and save the output value */ |
1293 |
|
|
dotLen = axisIn->nweight->axis[0].size; |
1294 |
|
|
for (smpIdx=0; smpIdx<axisIn->samples; smpIdx++) { |
1295 |
|
|
double val; |
1296 |
|
|
val = 0.0; |
1297 |
|
|
if (nrrdResampleNonExistentNoop != rsmc->nonExistent) { |
1298 |
|
|
double wsum; |
1299 |
|
|
wsum = 0.0; |
1300 |
|
|
for (dotIdx=0; dotIdx<dotLen; dotIdx++) { |
1301 |
|
|
double tmpV, tmpW; |
1302 |
|
|
tmpV = line[indx[dotIdx + dotLen*smpIdx]]; |
1303 |
|
|
if (AIR_EXISTS(tmpV)) { |
1304 |
|
|
tmpW = weight[dotIdx + dotLen*smpIdx]; |
1305 |
|
|
val += tmpV*tmpW; |
1306 |
|
|
wsum += tmpW; |
1307 |
|
|
} |
1308 |
|
|
} |
1309 |
|
|
if (wsum) { |
1310 |
|
|
if (nrrdResampleNonExistentRenormalize == rsmc->nonExistent) { |
1311 |
|
|
val /= wsum; |
1312 |
|
|
} |
1313 |
|
|
/* else nrrdResampleNonExistentWeight: leave as is */ |
1314 |
|
|
} else { |
1315 |
|
|
val = AIR_NAN; |
1316 |
|
|
} |
1317 |
|
|
} else { |
1318 |
|
|
/* nrrdResampleNonExistentNoop: do convolution sum |
1319 |
|
|
w/out worries about value existance */ |
1320 |
|
|
for (dotIdx=0; dotIdx<dotLen; dotIdx++) { |
1321 |
|
|
val += (line[indx[dotIdx + dotLen*smpIdx]] |
1322 |
|
|
* weight[dotIdx + dotLen*smpIdx]); |
1323 |
|
|
} |
1324 |
|
|
} |
1325 |
|
|
if (passIdx < rsmc->passNum-1) { |
1326 |
|
|
rsmpOut[smpIdx*strideOut + indexOut] = val; |
1327 |
|
|
} else { |
1328 |
|
|
if (doRound) { |
1329 |
|
|
val = AIR_CAST(nrrdResample_t, AIR_ROUNDUP(val)); |
1330 |
|
|
} |
1331 |
|
|
if (rsmc->clamp) { |
1332 |
|
|
val = clamp(val); |
1333 |
|
|
} |
1334 |
|
|
ins(dataOut, smpIdx*strideOut + indexOut, val); |
1335 |
|
|
} |
1336 |
|
|
} |
1337 |
|
|
|
1338 |
|
|
/* as long as there's another line to be processed, increment the |
1339 |
|
|
coordinates for the scanline starts. We don't use the usual |
1340 |
|
|
NRRD_COORD macros because we're subject to the unusual constraint |
1341 |
|
|
that coordIn[topRax] and coordOut[permute[topRax]] must stay == 0 */ |
1342 |
|
|
if (lineIdx < lineNum-1) { |
1343 |
|
|
axIdx = rsmc->topRax ? 0 : 1; |
1344 |
|
|
coordIn[axIdx]++; |
1345 |
|
|
coordOut[rsmc->permute[axIdx]]++; |
1346 |
|
|
while (coordIn[axIdx] == axisIn->sizePerm[axIdx]) { |
1347 |
|
|
coordIn[axIdx] = coordOut[rsmc->permute[axIdx]] = 0; |
1348 |
|
|
axIdx++; |
1349 |
|
|
axIdx += axIdx == rsmc->topRax; |
1350 |
|
|
coordIn[axIdx]++; |
1351 |
|
|
coordOut[rsmc->permute[axIdx]]++; |
1352 |
|
|
} |
1353 |
|
|
} |
1354 |
|
|
} |
1355 |
|
|
|
1356 |
|
|
/* (maybe) free input to this pass, now that we're done with it */ |
1357 |
|
|
if (axisIn->nrsmp) { |
1358 |
|
|
if (rsmc->verbose) { |
1359 |
|
|
fprintf(stderr, "%s: nrrdNuke(%p) pass %u input (on axis %u)\n", |
1360 |
|
|
me, AIR_CAST(void*, axisIn->nrsmp), axisIn->passIdx, |
1361 |
|
|
axisIn->axIdx); |
1362 |
|
|
} |
1363 |
|
|
axisIn->nrsmp = nrrdNuke(axisIn->nrsmp); |
1364 |
|
|
/* airMopSub(mop, axisIn->nrsmp, (airMopper)nrrdNuke); */ |
1365 |
|
|
} |
1366 |
|
|
} /* for passIdx */ |
1367 |
|
|
|
1368 |
|
|
airMopOkay(mop); |
1369 |
|
|
return 0; |
1370 |
|
|
} |
1371 |
|
|
|
1372 |
|
|
int |
1373 |
|
|
_nrrdResampleOutputUpdate(NrrdResampleContext *rsmc, Nrrd *nout, |
1374 |
|
|
const char *func) { |
1375 |
|
|
static const char me[]="_nrrdResampleOutputUpdate"; |
1376 |
|
|
#if NRRD_RESAMPLE_FLOAT |
1377 |
|
|
float (*lup)(const void *, size_t), |
1378 |
|
|
(*clamp)(float), (*ins)(void *, size_t, float); |
1379 |
|
|
#else |
1380 |
|
|
double (*lup)(const void *, size_t), |
1381 |
|
|
(*clamp)(double), (*ins)(void *, size_t, double); |
1382 |
|
|
#endif |
1383 |
|
|
unsigned int axIdx; |
1384 |
|
|
int typeOut, doRound; |
1385 |
|
|
|
1386 |
|
|
if (rsmc->flag[flagClamp] |
1387 |
|
|
|| rsmc->flag[flagNonExistent] |
1388 |
|
|
|| rsmc->flag[flagRound] |
1389 |
|
|
|| rsmc->flag[flagTypeOut] |
1390 |
|
|
|| rsmc->flag[flagLineFill] |
1391 |
|
|
|| rsmc->flag[flagVectorFill] |
1392 |
|
|
|| rsmc->flag[flagPermutation] |
1393 |
|
|
|| rsmc->flag[flagInput]) { |
1394 |
|
|
|
1395 |
|
|
typeOut = (nrrdTypeDefault == rsmc->typeOut |
1396 |
|
|
? rsmc->nin->type |
1397 |
|
|
: rsmc->typeOut); |
1398 |
|
|
doRound = rsmc->roundlast && nrrdTypeIsIntegral[typeOut]; |
1399 |
|
|
if (doRound && (nrrdTypeInt == typeOut |
1400 |
|
|
|| nrrdTypeUInt == typeOut |
1401 |
|
|
|| nrrdTypeLLong == typeOut |
1402 |
|
|
|| nrrdTypeULLong == typeOut)) { |
1403 |
|
|
fprintf(stderr, "%s: WARNING: possible erroneous output with " |
1404 |
|
|
"rounding of %s output type due to int-based implementation " |
1405 |
|
|
"of rounding\n", me, airEnumStr(nrrdType, typeOut)); |
1406 |
|
|
} |
1407 |
|
|
|
1408 |
|
|
#if NRRD_RESAMPLE_FLOAT |
1409 |
|
|
lup = nrrdFLookup[rsmc->nin->type]; |
1410 |
|
|
clamp = nrrdFClamp[typeOut]; |
1411 |
|
|
ins = nrrdFInsert[typeOut]; |
1412 |
|
|
#else |
1413 |
|
|
lup = nrrdDLookup[rsmc->nin->type]; |
1414 |
|
|
clamp = nrrdDClamp[typeOut]; |
1415 |
|
|
ins = nrrdDInsert[typeOut]; |
1416 |
|
|
#endif |
1417 |
|
|
|
1418 |
|
|
if (0 == rsmc->passNum) { |
1419 |
|
|
if (_nrrdResampleTrivial(rsmc, nout, typeOut, doRound, |
1420 |
|
|
lup, clamp, ins)) { |
1421 |
|
|
biffAddf(NRRD, "%s: trouble", me); |
1422 |
|
|
return 1; |
1423 |
|
|
} |
1424 |
|
|
} else { |
1425 |
|
|
if (_nrrdResampleCore(rsmc, nout, typeOut, doRound, |
1426 |
|
|
lup, clamp, ins)) { |
1427 |
|
|
biffAddf(NRRD, "%s: trouble", me); |
1428 |
|
|
return 1; |
1429 |
|
|
} |
1430 |
|
|
} |
1431 |
|
|
|
1432 |
|
|
/* HEY: need to create textual representation of resampling parameters */ |
1433 |
|
|
if (nrrdContentSet_va(nout, func, rsmc->nin, "")) { |
1434 |
|
|
biffAddf(NRRD, "%s:", me); |
1435 |
|
|
return 1; |
1436 |
|
|
} |
1437 |
|
|
|
1438 |
|
|
/* start work of updating space origin */ |
1439 |
|
|
nrrdSpaceVecCopy(nout->spaceOrigin, rsmc->nin->spaceOrigin); |
1440 |
|
|
for (axIdx=0; axIdx<rsmc->dim; axIdx++) { |
1441 |
|
|
if (rsmc->axis[axIdx].kernel) { |
1442 |
|
|
/* this axis was resampled */ |
1443 |
|
|
double minIdxFull, maxIdxFull, |
1444 |
|
|
zeroPos; /* actually its in continuous index space ... */ |
1445 |
|
|
_nrrdAxisInfoCopy(nout->axis + axIdx, rsmc->nin->axis + axIdx, |
1446 |
|
|
(NRRD_AXIS_INFO_SIZE_BIT |
1447 |
|
|
| NRRD_AXIS_INFO_SPACING_BIT |
1448 |
|
|
| NRRD_AXIS_INFO_THICKNESS_BIT |
1449 |
|
|
| NRRD_AXIS_INFO_MIN_BIT |
1450 |
|
|
| NRRD_AXIS_INFO_MAX_BIT |
1451 |
|
|
| NRRD_AXIS_INFO_SPACEDIRECTION_BIT |
1452 |
|
|
| NRRD_AXIS_INFO_CENTER_BIT |
1453 |
|
|
| NRRD_AXIS_INFO_KIND_BIT)); |
1454 |
|
|
/* now set all the per-axis fields we just abstained from copying */ |
1455 |
|
|
/* size was already set */ |
1456 |
|
|
nout->axis[axIdx].spacing = (rsmc->nin->axis[axIdx].spacing |
1457 |
|
|
/ rsmc->axis[axIdx].ratio); |
1458 |
|
|
/* for now, we don't attempt to modify thickness */ |
1459 |
|
|
nout->axis[axIdx].thickness = AIR_NAN; |
1460 |
|
|
/* We had to assume a specific centering when doing resampling */ |
1461 |
|
|
nout->axis[axIdx].center = rsmc->axis[axIdx].center; |
1462 |
|
|
_nrrdResampleMinMaxFull(&minIdxFull, &maxIdxFull, |
1463 |
|
|
rsmc->axis[axIdx].center, |
1464 |
|
|
rsmc->nin->axis[axIdx].size); |
1465 |
|
|
nout->axis[axIdx].min = AIR_AFFINE(minIdxFull, |
1466 |
|
|
rsmc->axis[axIdx].min, |
1467 |
|
|
maxIdxFull, |
1468 |
|
|
rsmc->nin->axis[axIdx].min, |
1469 |
|
|
rsmc->nin->axis[axIdx].max); |
1470 |
|
|
nout->axis[axIdx].max = AIR_AFFINE(minIdxFull, |
1471 |
|
|
rsmc->axis[axIdx].max, |
1472 |
|
|
maxIdxFull, |
1473 |
|
|
rsmc->nin->axis[axIdx].min, |
1474 |
|
|
rsmc->nin->axis[axIdx].max); |
1475 |
|
|
nrrdSpaceVecScale(nout->axis[axIdx].spaceDirection, |
1476 |
|
|
1.0/rsmc->axis[axIdx].ratio, |
1477 |
|
|
rsmc->nin->axis[axIdx].spaceDirection); |
1478 |
|
|
nout->axis[axIdx].kind = _nrrdKindAltered(rsmc->nin->axis[axIdx].kind, |
1479 |
|
|
AIR_TRUE); |
1480 |
|
|
/* space origin may have translated along this axis; |
1481 |
|
|
only do this if the axis was already spatial */ |
1482 |
|
|
if (AIR_EXISTS(rsmc->nin->axis[axIdx].spaceDirection[0])) { |
1483 |
|
|
zeroPos = NRRD_POS(nout->axis[axIdx].center, |
1484 |
|
|
rsmc->axis[axIdx].min, |
1485 |
|
|
rsmc->axis[axIdx].max, |
1486 |
|
|
rsmc->axis[axIdx].samples, |
1487 |
|
|
0); |
1488 |
|
|
nrrdSpaceVecScaleAdd2(nout->spaceOrigin, |
1489 |
|
|
1.0, nout->spaceOrigin, |
1490 |
|
|
zeroPos, |
1491 |
|
|
rsmc->nin->axis[axIdx].spaceDirection); |
1492 |
|
|
} |
1493 |
|
|
} else { |
1494 |
|
|
/* no resampling; this axis totally unchanged */ |
1495 |
|
|
_nrrdAxisInfoCopy(nout->axis + axIdx, rsmc->nin->axis + axIdx, |
1496 |
|
|
NRRD_AXIS_INFO_NONE); |
1497 |
|
|
/* also: the space origin has not translated along this axis */ |
1498 |
|
|
} |
1499 |
|
|
} |
1500 |
|
|
if (nrrdBasicInfoCopy(nout, rsmc->nin, |
1501 |
|
|
NRRD_BASIC_INFO_DATA_BIT |
1502 |
|
|
| NRRD_BASIC_INFO_TYPE_BIT |
1503 |
|
|
| NRRD_BASIC_INFO_BLOCKSIZE_BIT |
1504 |
|
|
| NRRD_BASIC_INFO_DIMENSION_BIT |
1505 |
|
|
| NRRD_BASIC_INFO_SPACEORIGIN_BIT |
1506 |
|
|
| NRRD_BASIC_INFO_CONTENT_BIT |
1507 |
|
|
| NRRD_BASIC_INFO_COMMENTS_BIT |
1508 |
|
|
| (nrrdStateKeyValuePairsPropagate |
1509 |
|
|
? 0 |
1510 |
|
|
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) { |
1511 |
|
|
biffAddf(NRRD, "%s:", me); |
1512 |
|
|
return 1; |
1513 |
|
|
} |
1514 |
|
|
|
1515 |
|
|
|
1516 |
|
|
rsmc->flag[flagClamp] = AIR_FALSE; |
1517 |
|
|
rsmc->flag[flagNonExistent] = AIR_FALSE; |
1518 |
|
|
rsmc->flag[flagRound] = AIR_FALSE; |
1519 |
|
|
rsmc->flag[flagTypeOut] = AIR_FALSE; |
1520 |
|
|
rsmc->flag[flagLineFill] = AIR_FALSE; |
1521 |
|
|
rsmc->flag[flagVectorFill] = AIR_FALSE; |
1522 |
|
|
rsmc->flag[flagPermutation] = AIR_FALSE; |
1523 |
|
|
rsmc->flag[flagInput] = AIR_FALSE; |
1524 |
|
|
} |
1525 |
|
|
|
1526 |
|
|
return 0; |
1527 |
|
|
} |
1528 |
|
|
|
1529 |
|
|
int |
1530 |
|
|
nrrdResampleExecute(NrrdResampleContext *rsmc, Nrrd *nout) { |
1531 |
|
|
static const char me[]="nrrdResampleExecute", func[]="resample"; |
1532 |
|
|
double time0; |
1533 |
|
|
|
1534 |
|
|
if (!(rsmc && nout)) { |
1535 |
|
|
biffAddf(NRRD, "%s: got NULL pointer", me); |
1536 |
|
|
return 1; |
1537 |
|
|
} |
1538 |
|
|
|
1539 |
|
|
/* any other error checking? Do we need a _nrrdResampleContextCheck() ? */ |
1540 |
|
|
if (nrrdBoundaryPad == rsmc->boundary && !AIR_EXISTS(rsmc->padValue)) { |
1541 |
|
|
biffAddf(NRRD, "%s: asked for boundary padding, " |
1542 |
|
|
"but no pad value set", me); |
1543 |
|
|
return 1; |
1544 |
|
|
} |
1545 |
|
|
|
1546 |
|
|
time0 = airTime(); |
1547 |
|
|
if (_nrrdResampleInputDimensionUpdate(rsmc) |
1548 |
|
|
|| _nrrdResampleInputCentersUpdate(rsmc) |
1549 |
|
|
|| _nrrdResampleInputSizesUpdate(rsmc) |
1550 |
|
|
|| _nrrdResampleLineAllocateUpdate(rsmc) |
1551 |
|
|
|| _nrrdResampleVectorAllocateUpdate(rsmc) |
1552 |
|
|
|| _nrrdResampleLineFillUpdate(rsmc) |
1553 |
|
|
|| _nrrdResampleVectorFillUpdate(rsmc) |
1554 |
|
|
|| _nrrdResamplePermutationUpdate(rsmc) |
1555 |
|
|
|| _nrrdResampleOutputUpdate(rsmc, nout, func)) { |
1556 |
|
|
biffAddf(NRRD, "%s: trouble resampling", me); |
1557 |
|
|
return 1; |
1558 |
|
|
} |
1559 |
|
|
rsmc->time = airTime() - time0; |
1560 |
|
|
|
1561 |
|
|
return 0; |
1562 |
|
|
} |