GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/nrrd/resampleContext.c Lines: 0 699 0.0 %
Date: 2017-05-26 Branches: 0 574 0.0 %

Line Branch Exec Source
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
}