GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/unrrdu/resample.c Lines: 37 222 16.7 %
Date: 2017-05-26 Branches: 1 178 0.6 %

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 "unrrdu.h"
25
#include "privateUnrrdu.h"
26
27
#define INFO "Filtering and {up,down}sampling with a separable kernel"
28
static const char *_unrrdu_resampleInfoL =
29
(INFO
30
 ". Simplifies access to the NrrdResampleContext functions "
31
 "by assuming (among other things) that the same kernel "
32
 "is used for resampling "
33
 "every axis that is being resampled.  Only required option is "
34
 "\"-s\" to specify which axes to resample and how many "
35
 "output samples to generate.  Resampling kernel \"-k\" defaults "
36
 "to an interpolating cubic, but many other choices are available. "
37
 "By default, resampling an axis resamples the full extent of its "
38
 "samples, but it is possible to offset this range via \"-off\", "
39
 "or to crop and/or pad via \"-min\" and \"-max\". "
40
 "The resampling respects the difference between cell- and "
41
 "node-centered data, but you can over-ride known centering "
42
 "with \"-co\".\n "
43
 "* Uses the many nrrdResample* functions operating on a nrrdResampleContext");
44
45
int
46
unrrdu_resampleMain(int argc, const char **argv, const char *me,
47
                    hestParm *hparm) {
48
2
  hestOpt *opt = NULL;
49
1
  char *out, *err;
50
1
  Nrrd *nin, *nout;
51
1
  int type, bb, pret, norenorm, neb, older, E, defaultCenter,
52
    verbose, overrideCenter, minSet=AIR_FALSE, maxSet=AIR_FALSE,
53
    offSet=AIR_FALSE;
54
1
  unsigned int scaleLen, ai, samplesOut, minLen, maxLen, offLen,
55
    aspRatNum, nonAspRatNum;
56
  airArray *mop;
57
1
  double *scale;
58
1
  double padVal, *min, *max, *off, aspRatScl=AIR_NAN;
59
  NrrdResampleInfo *info;
60
  NrrdResampleContext *rsmc;
61
1
  NrrdKernelSpec *unuk;
62
63
1
  mop = airMopNew();
64
1
  info = nrrdResampleInfoNew();
65
1
  airMopAdd(mop, info, (airMopper)nrrdResampleInfoNix, airMopAlways);
66
1
  hparm->elideSingleOtherDefault = AIR_FALSE;
67
1
  hestOptAdd(&opt, "old", NULL, airTypeInt, 0, 0, &older, NULL,
68
             "instead of using the new nrrdResampleContext implementation, "
69
             "use the old nrrdSpatialResample implementation");
70
1
  hestOptAdd(&opt, "s,size", "sz0", airTypeOther, 1, -1, &scale, NULL,
71
             "For each axis, information about how many samples in output:\n "
72
             "\b\bo \"=\": leave this axis completely untouched: no "
73
             "resampling whatsoever\n "
74
             "\b\bo \"x<float>\": multiply the number of input samples by "
75
             "<float>, and round to the nearest integer, to get the number "
76
             "of output samples.  Use \"x1\" to resample the axis but leave "
77
             "the number of samples unchanged\n "
78
             "\b\bo \"/<float>\": divide number of samples by <float>\n "
79
             "\b\bo \"+=<uint>\", \"-=<uint>\": add <uint> to or subtract "
80
             "<uint> from number input samples to get number output samples\n "
81
             "\b\bo \"s<float>\": assuming that some spacing information is "
82
             "known on this input axis, then set the number of sample so that "
83
             "the given float is the output axis spacing\n "
84
             "\b\bo \"<uint>\": exact number of output samples\n "
85
             "\b\bo \"a\": resample this axis to whatever number of samples "
86
             "preserves the aspect ratio of other resampled axes. Currently "
87
             "needs to be used on all but one of the resampled axes, "
88
             "if at all. ",
89
             &scaleLen, NULL, &unrrduHestScaleCB);
90
1
  hestOptAdd(&opt, "off,offset", "off0", airTypeDouble, 0, -1, &off, "",
91
             "For each axis, an offset or shift to the position (in index "
92
             "space) of the lower end of the sampling domain. "
93
             "Either -off can be used, or -min and -max "
94
             "together, or none of these (so that, by default, the full "
95
             "domain of the axis is resampled).",  &offLen);
96
1
  hestOptAdd(&opt, "min,minimum", "min0", airTypeDouble, 0, -1, &min, "",
97
             "For each axis, the lower end (in index space) of the domain "
98
             "of the resampling. Either -off can be used, or -min and -max "
99
             "together, or none of these (so that, by default, the full "
100
             "domain of the axis is resampled).",  &minLen);
101
1
  hestOptAdd(&opt, "max,maximum", "max0", airTypeDouble, 0, -1, &max, "",
102
             "For each axis, the upper end (in index space) of the domain "
103
             "of the resampling. Either -off can be used, or -min and -max "
104
             "together, or none of these, so that (by default), the full "
105
             "domain of the axis is resampled.",  &maxLen);
106
1
  hestOptAdd(&opt, "k,kernel", "kern", airTypeOther, 1, 1, &unuk,
107
             "cubic:0,0.5",
108
             "The kernel to use for resampling.  "
109
             "Kernels logically live in the input index space for upsampling, "
110
             "and in the output index space for downsampling.  "
111
             "Possibilities include:\n "
112
             "\b\bo \"box\": nearest neighbor interpolation on upsampling, "
113
             "and uniform averaging on downsampling\n "
114
             "\b\bo \"cheap\": nearest neighbor interpolation for upsampling, "
115
             "and non-blurring sub-sampling (pick subset of input samples) "
116
             "on downsampling\n "
117
             "\b\bo \"tent\": linear interpolation\n "
118
             "\b\bo \"cubic:B,C\": Mitchell/Netravali BC-family of "
119
             "cubics:\n "
120
             "\t\t\"cubic:1,0\": B-spline; maximal blurring\n "
121
             "\t\t\"cubic:0,0.5\": Catmull-Rom; good interpolating kernel\n "
122
             "\b\bo \"c4h\": 6-sample-support, C^4 continuous, accurate\n "
123
             "\b\bo \"c4hai\": discrete pre-filter to make c4h interpolate\n "
124
             "\b\bo \"bspl3\", \"bspl5\", \"bspl7\": cubic (same as cubic:1,0), "
125
             "quintic, and 7th order B-spline\n "
126
             "\b\bo \"bspl3ai\", \"bspl5ai\", \"bspl7ai\": discrete pre-filters to make "
127
             "bspl3, bspl5, bspl7 interpolate\n "
128
             "\b\bo \"hann:R\": Hann (cosine bell) windowed sinc, radius R\n "
129
             "\b\bo \"black:R\": Blackman windowed sinc, radius R\n "
130
             "\b\bo \"gauss:S,C\": Gaussian blurring, with standard deviation "
131
             "S and cut-off at C standard deviations\n "
132
             "\b\bo \"dgauss:S,C\": Lindeberg's discrete Gaussian.",
133
1
             NULL, NULL, nrrdHestKernelSpec);
134
1
  hestOptAdd(&opt, "nrn", NULL, airTypeInt, 0, 0, &norenorm, NULL,
135
             "do NOT do per-pass kernel weight renormalization. "
136
             "Doing the renormalization is not a performance hit (hence is "
137
             "enabled by default), and the renormalization is sometimes "
138
             "needed to avoid \"grating\" on non-integral "
139
             "down-sampling.  Disabling the renormalization is needed for "
140
             "correct results with artificially narrow kernels. ");
141
1
  hestOptAdd(&opt, "ne,nonexistent", "behavior", airTypeEnum, 1, 1,
142
             &neb, "noop",
143
             "When resampling floating-point values, how to handle "
144
             "non-existent values within kernel support:\n "
145
             "\b\bo \"noop\": do nothing; let them pollute result\n "
146
             "\b\bo \"renorm\": ignore them and renormalize weights of "
147
             "existent values\n "
148
             "\b\bo \"wght\": ignore them and simply use weights of "
149
             "existent values",
150
1
             NULL, nrrdResampleNonExistent);
151
1
  hestOptAdd(&opt, "b,boundary", "behavior", airTypeEnum, 1, 1, &bb, "bleed",
152
             "How to handle samples beyond the input bounds:\n "
153
             "\b\bo \"pad\": use some specified value\n "
154
             "\b\bo \"bleed\": extend border values outward\n "
155
             "\b\bo \"mirror\": repeated reflections\n "
156
             "\b\bo \"wrap\": wrap-around to other side",
157
1
             NULL, nrrdBoundary);
158
1
  hestOptAdd(&opt, "v,value", "value", airTypeDouble, 1, 1, &padVal, "0.0",
159
             "for \"pad\" boundary behavior, pad with this value");
160
1
  hestOptAdd(&opt, "t,type", "type", airTypeOther, 1, 1, &type, "default",
161
             "type to save OUTPUT as. By default (not using this option), "
162
             "the output type is the same as the input type",
163
             NULL, NULL, &unrrduHestMaybeTypeCB);
164
1
  hestOptAdd(&opt, "cheap", NULL, airTypeInt, 0, 0, &(info->cheap), NULL,
165
             "[DEPRECATED: the \"-k cheap\" option is the new (and more "
166
             "reliable) way to access this functionality. \"-cheap\" is "
167
             "only here for legacy use in combination with \"-old\".]\n "
168
             "When downsampling (reducing number of samples), don't "
169
             "try to do correct filtering by scaling kernel to match "
170
             "new (stretched) index space; keep it in old index space. "
171
             "When used in conjunction with \"-k box\", this can implement "
172
             "subsampling which chooses every Nth value. ");
173
1
  hestOptAdd(&opt, "c,center", "center", airTypeEnum, 1, 1, &defaultCenter,
174
1
             (nrrdCenterCell == nrrdDefaultCenter
175
              ? "cell"
176
              : "node"),
177
             "(not available with \"-old\") "
178
             "default centering of axes when input nrrd "
179
             "axes don't have a known centering: \"cell\" or \"node\" ",
180
1
             NULL, nrrdCenter);
181
1
  hestOptAdd(&opt, "co,center-override", NULL, airTypeInt, 0, 0,
182
             &overrideCenter, NULL,
183
             "(not available with \"-old\") "
184
             "centering info specified via \"-c\" should *over-ride* "
185
             "known centering, rather than simply be used when centering "
186
             "is unknown.");
187
1
  hestOptAdd(&opt, "verbose", "v", airTypeInt, 1, 1, &verbose, "0",
188
             "(not available with \"-old\") verbosity level");
189
1
  OPT_ADD_NIN(nin, "input nrrd");
190
1
  OPT_ADD_NOUT(out, "output nrrd");
191
192
1
  airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways);
193
194
2
  USAGE(_unrrdu_resampleInfoL);
195
  PARSE();
196
  airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways);
197
198
  nout = nrrdNew();
199
  airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways);
200
201
  if (scaleLen != nin->dim) {
202
    fprintf(stderr, "%s: # sampling sizes %d != input nrrd dimension %d\n",
203
            me, scaleLen, nin->dim);
204
    airMopError(mop);
205
    return 1;
206
  }
207
  if (!older) {
208
    if (offLen >= 1) {
209
      /* seems to want to set off[] */
210
      if (offLen != scaleLen) {
211
        fprintf(stderr, "%s: offLen %u != scaleLen %u\n", me,
212
                offLen, scaleLen);
213
        airMopError(mop);
214
        return 1;
215
      }
216
      for (ai=0; ai<offLen; ai++) {
217
        if (unrrduScaleNothing != (int)(scale[0 + 2*ai])
218
            && !AIR_EXISTS(off[ai])) {
219
          fprintf(stderr, "%s: off[%u] %g doesn't exist\n", me,
220
                  ai, off[ai]);
221
          airMopError(mop);
222
          return 1;
223
        }
224
      }
225
      offSet = AIR_TRUE;
226
    } else {
227
      offSet = AIR_FALSE;
228
    }
229
    if (minLen >= 1 && AIR_EXISTS(min[0])) { /* HEY copy and paste */
230
      /* seems to want to set min[] */
231
      if (minLen != scaleLen) {
232
        fprintf(stderr, "%s: minLen %u != scaleLen %u\n", me,
233
                minLen, scaleLen);
234
        airMopError(mop);
235
        return 1;
236
      }
237
      for (ai=0; ai<minLen; ai++) {
238
        if (unrrduScaleNothing != (int)(scale[0 + 2*ai])
239
            && !AIR_EXISTS(min[ai])) {
240
          fprintf(stderr, "%s: min[%u] %g doesn't exist\n", me,
241
                  ai, min[ai]);
242
          airMopError(mop);
243
          return 1;
244
        }
245
      }
246
      minSet = AIR_TRUE;
247
    } else {
248
      minSet = AIR_FALSE;
249
    }
250
    if (maxLen >= 1 && AIR_EXISTS(max[0])) { /* HEY copy and paste */
251
      /* seems to want to set max[] */
252
      if (maxLen != scaleLen) {
253
        fprintf(stderr, "%s: maxLen %u != scaleLen %u\n", me,
254
                maxLen, scaleLen);
255
        airMopError(mop);
256
        return 1;
257
      }
258
      for (ai=0; ai<maxLen; ai++) {
259
        if (unrrduScaleNothing != (int)(scale[0 + 2*ai])
260
            && !AIR_EXISTS(max[ai])) {
261
          fprintf(stderr, "%s: max[%u] %g doesn't exist\n", me,
262
                  ai, max[ai]);
263
          airMopError(mop);
264
          return 1;
265
        }
266
      }
267
      maxSet = AIR_TRUE;
268
    } else {
269
      maxSet = AIR_FALSE;
270
    }
271
    if (!( (minSet && maxSet) || (!minSet && !maxSet) )) {
272
      fprintf(stderr, "%s: need -min and -max to be set consistently\n", me);
273
      airMopError(mop);
274
      return 1;
275
    }
276
    if (minSet && offSet) {
277
      fprintf(stderr, "%s: can't use -off with -min and -max\n", me);
278
      airMopError(mop);
279
      return 1;
280
    }
281
    aspRatNum = nonAspRatNum = 0;
282
    for (ai=0; ai<nin->dim; ai++) {
283
      int dowhat = AIR_CAST(int, scale[0 + 2*ai]);
284
      if (!(unrrduScaleNothing == dowhat)) {
285
        if (unrrduScaleAspectRatio == dowhat) {
286
          aspRatNum++;
287
        } else {
288
          nonAspRatNum++;
289
        }
290
      }
291
    }
292
    if (aspRatNum) {
293
      if (1 != nonAspRatNum) {
294
        fprintf(stderr, "%s: sorry, aspect-ratio-preserving "
295
                "resampling must currently be used on all but one "
296
                "(not %u) resampled axis, if any\n", me,
297
                nonAspRatNum);
298
        airMopError(mop);
299
        return 1;
300
      }
301
    }
302
303
    rsmc = nrrdResampleContextNew();
304
    rsmc->verbose = verbose;
305
    airMopAdd(mop, rsmc, (airMopper)nrrdResampleContextNix, airMopAlways);
306
    E = AIR_FALSE;
307
    if (!E) E |= nrrdResampleDefaultCenterSet(rsmc, defaultCenter);
308
    if (!E) E |= nrrdResampleInputSet(rsmc, nin);
309
    for (ai=0; ai<nin->dim; ai++) {
310
      double spin, spout, svec[NRRD_SPACE_DIM_MAX];
311
      int spstat;
312
      int dowhat = AIR_CAST(int, scale[0 + 2*ai]);
313
      switch(dowhat) {
314
      case unrrduScaleNothing:
315
        /* no resampling */
316
        if (!E) E |= nrrdResampleKernelSet(rsmc, ai, NULL, NULL);
317
        break;
318
      case unrrduScaleMultiply:
319
      case unrrduScaleDivide:
320
      case unrrduScaleAdd:
321
      case unrrduScaleSubtract:
322
        /* scaling of input # samples */
323
        if (defaultCenter && overrideCenter) {
324
          if (!E) E |= nrrdResampleOverrideCenterSet(rsmc, ai, defaultCenter);
325
        }
326
        if (!E) E |= nrrdResampleKernelSet(rsmc, ai, unuk->kernel, unuk->parm);
327
        switch(dowhat) {
328
          unsigned int incr;
329
          char stmp[AIR_STRLEN_SMALL];
330
        case unrrduScaleMultiply:
331
          samplesOut = AIR_ROUNDUP(nin->axis[ai].size*scale[1 + 2*ai]);
332
          break;
333
        case unrrduScaleDivide:
334
          samplesOut = AIR_ROUNDUP(nin->axis[ai].size/scale[1 + 2*ai]);
335
          break;
336
        case unrrduScaleAdd:
337
          samplesOut = nin->axis[ai].size + AIR_CAST(unsigned int, scale[1 + 2*ai]);
338
          break;
339
        case unrrduScaleSubtract:
340
          incr = AIR_CAST(unsigned int, scale[1 + 2*ai]);
341
          if (nin->axis[ai].size - 1 < incr) {
342
            fprintf(stderr, "%s: can't subtract %u from axis size %s\n",
343
                    me, incr, airSprintSize_t(stmp, nin->axis[ai].size));
344
            airMopError(mop);
345
            return 1;
346
          }
347
          samplesOut = nin->axis[ai].size - incr;
348
          break;
349
        }
350
        aspRatScl = AIR_CAST(double, samplesOut)/nin->axis[ai].size;
351
        if (!E) E |= nrrdResampleSamplesSet(rsmc, ai, samplesOut);
352
        break;
353
      case unrrduScaleSpacingTarget:
354
        /* wants the output spacing to be something particular */
355
        spstat = nrrdSpacingCalculate(nin, ai, &spin, svec);
356
        spout = scale[1 + 2*ai];
357
        switch (spstat) {
358
        case nrrdSpacingStatusUnknown:
359
        case nrrdSpacingStatusNone:
360
          fprintf(stderr, "%s: want to set output axis %u spacing (to %g), "
361
                  "but can't find input axis spacing\n",
362
                  me, ai, spout);
363
          airMopError(mop);
364
          return 1;
365
          break;
366
        case nrrdSpacingStatusScalarNoSpace:
367
        case nrrdSpacingStatusScalarWithSpace:
368
        case nrrdSpacingStatusDirection:
369
          if (!AIR_EXISTS(spin)) {
370
          fprintf(stderr, "%s: want to set output axis %u spacing (to %g), "
371
                  "but can't input axis spacing was %g\n",
372
                  me, ai, spout, spin);
373
            airMopError(mop);
374
            return 1;
375
          }
376
          samplesOut = AIR_ROUNDUP(nin->axis[ai].size*spin/spout);
377
          break;
378
        }
379
        aspRatScl = AIR_CAST(double, samplesOut)/nin->axis[ai].size;
380
        if (defaultCenter && overrideCenter) {
381
          if (!E) E |= nrrdResampleOverrideCenterSet(rsmc, ai, defaultCenter);
382
        }
383
        if (!E) E |= nrrdResampleKernelSet(rsmc, ai, unuk->kernel, unuk->parm);
384
        if (!E) E |= nrrdResampleSamplesSet(rsmc, ai, samplesOut);
385
        break;
386
      case unrrduScaleExact:
387
        /* explicit # of samples */
388
        if (defaultCenter && overrideCenter) {
389
          if (!E) E |= nrrdResampleOverrideCenterSet(rsmc, ai, defaultCenter);
390
        }
391
        if (!E) E |= nrrdResampleKernelSet(rsmc, ai, unuk->kernel, unuk->parm);
392
        samplesOut = (size_t)scale[1 + 2*ai];
393
        aspRatScl = AIR_CAST(double, samplesOut)/nin->axis[ai].size;
394
        if (!E) E |= nrrdResampleSamplesSet(rsmc, ai, samplesOut);
395
        break;
396
      case unrrduScaleAspectRatio:
397
        /* wants aspect-ratio preserving, but may not know # samples yet */
398
        if (defaultCenter && overrideCenter) {
399
          if (!E) E |= nrrdResampleOverrideCenterSet(rsmc, ai, defaultCenter);
400
        }
401
        if (!E) E |= nrrdResampleKernelSet(rsmc, ai, unuk->kernel, unuk->parm);
402
        /* will set samples later, after aspRatScl has been set */
403
        break;
404
      default:
405
        fprintf(stderr, "%s: sorry, unrecognized unrrduScale value %d\n",
406
                me, dowhat);
407
        airMopError(mop);
408
        return 1;
409
      }
410
      if (minSet && maxSet) {
411
        if (!E) E |= nrrdResampleRangeSet(rsmc, ai, min[ai], max[ai]);
412
      } else {
413
        if (!E) E |= nrrdResampleRangeFullSet(rsmc, ai);
414
        if (offSet) {
415
          /* HEY: this is a hack; We're reading out the information from
416
             determined by nrrdResampleRangeFullSet, and benefitting from
417
             the fact that it set one of the flags that are processed by
418
             nrrdResampleExecute() */
419
          rsmc->axis[ai].min += off[ai];
420
          rsmc->axis[ai].max += off[ai];
421
        }
422
      }
423
    }
424
    if (!E && aspRatNum) {
425
      if (!AIR_EXISTS(aspRatScl)) {
426
        fprintf(stderr, "%s: confusion, should have learned scaling "
427
                "of aspect-ratio-preserving resampling by now", me);
428
        airMopError(mop);
429
        return 1;
430
      }
431
      for (ai=0; ai<nin->dim; ai++) {
432
        int dowhat = AIR_CAST(int, scale[0 + 2*ai]);
433
        if (unrrduScaleAspectRatio == dowhat) {
434
          samplesOut = AIR_ROUNDUP(nin->axis[ai].size*aspRatScl);
435
          if (!E) E |= nrrdResampleSamplesSet(rsmc, ai, samplesOut);
436
        }
437
      }
438
    }
439
    if (!E) E |= nrrdResampleBoundarySet(rsmc, bb);
440
    if (!E) E |= nrrdResampleTypeOutSet(rsmc, type);
441
    if (!E) E |= nrrdResamplePadValueSet(rsmc, padVal);
442
    if (!E) E |= nrrdResampleRenormalizeSet(rsmc, !norenorm);
443
    if (!E) E |= nrrdResampleNonExistentSet(rsmc, neb);
444
    if (!E) E |= nrrdResampleExecute(rsmc, nout);
445
    if (E) {
446
      airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways);
447
      fprintf(stderr, "%s: error resampling nrrd:\n%s", me, err);
448
      airMopError(mop);
449
      return 1;
450
    }
451
  } else {
452
    for (ai=0; ai<nin->dim; ai++) {
453
      int dowhat = AIR_CAST(int, scale[0 + 2*ai]);
454
      /* this may be over-written below */
455
      info->kernel[ai] = unuk->kernel;
456
      switch(dowhat) {
457
      case unrrduScaleNothing:
458
        /* no resampling */
459
        info->kernel[ai] = NULL;
460
        break;
461
      case unrrduScaleMultiply:
462
        /* scaling of input # samples */
463
        info->samples[ai] = AIR_ROUNDUP(scale[1 + 2*ai]*nin->axis[ai].size);
464
        break;
465
      case unrrduScaleExact:
466
        /* explicit # of samples */
467
        info->samples[ai] = (size_t)scale[1 + 2*ai];
468
        break;
469
      default:
470
        fprintf(stderr, "%s: sorry, unrecognized unrrduScale value %d\n",
471
                me, dowhat);
472
        airMopError(mop);
473
        return 1;
474
      }
475
      memcpy(info->parm[ai], unuk->parm,
476
             NRRD_KERNEL_PARMS_NUM*sizeof(*unuk->parm));
477
      if (info->kernel[ai] &&
478
          (!( AIR_EXISTS(nin->axis[ai].min)
479
              && AIR_EXISTS(nin->axis[ai].max))) ) {
480
        nrrdAxisInfoMinMaxSet(nin, ai,
481
                              (nin->axis[ai].center
482
                               ? nin->axis[ai].center
483
                               : nrrdDefaultCenter));
484
      }
485
      info->min[ai] = nin->axis[ai].min;
486
      info->max[ai] = nin->axis[ai].max;
487
    }
488
    info->boundary = bb;
489
    info->type = type;
490
    info->padValue = padVal;
491
    info->renormalize = !norenorm;
492
    if (nrrdSpatialResample(nout, nin, info)) {
493
      airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways);
494
      fprintf(stderr, "%s: error resampling nrrd:\n%s", me, err);
495
      airMopError(mop);
496
      return 1;
497
    }
498
  }
499
500
501
  SAVE(out, nout, NULL);
502
503
  airMopOkay(mop);
504
  return 0;
505
1
}
506
507
UNRRDU_CMD(resample, INFO);