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