GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/nrrd/filt.c Lines: 0 388 0.0 %
Date: 2017-05-26 Branches: 0 275 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
int
28
_nrrdCM_median(const float *hist, float half) {
29
  float sum = 0;
30
  const float *hpt;
31
32
  hpt = hist;
33
34
  while(sum < half)
35
    sum += *hpt++;
36
37
  return AIR_CAST(int, hpt - 1 - hist);
38
}
39
40
int
41
_nrrdCM_mode(const float *hist, int bins) {
42
  float max;
43
  int i, mi;
44
45
  mi = -1;
46
  max = 0;
47
  for (i=0; i<bins; i++) {
48
    if (hist[i] && (!max || hist[i] > max) ) {
49
      max = hist[i];
50
      mi = i;
51
    }
52
  }
53
54
  return mi;
55
}
56
57
#define INDEX(nin, range, lup, idxIn, bins, val) ( \
58
  val = (lup)((nin)->data, (idxIn)), \
59
  airIndex((range)->min, (val), (range)->max, bins) \
60
)
61
62
void
63
_nrrdCM_printhist(const float *hist, int bins, const char *desc) {
64
  int i;
65
66
  printf("%s:\n", desc);
67
  for (i=0; i<bins; i++) {
68
    if (hist[i]) {
69
      printf("   %d: %g\n", i, hist[i]);
70
    }
71
  }
72
}
73
74
float *
75
_nrrdCM_wtAlloc(int radius, float wght) {
76
  float *wt, sum;
77
  int diam, r;
78
79
  diam = 2*radius + 1;
80
  wt = (float *)calloc(diam, sizeof(float));
81
  wt[radius] = 1.0;
82
  for (r=1; r<=radius; r++) {
83
    wt[radius+r] = AIR_CAST(float, pow(1.0/wght, r));
84
    wt[radius-r] = AIR_CAST(float, pow(1.0/wght, r));
85
  }
86
  sum = 0.0;
87
  for (r=0; r<diam; r++) {
88
    sum += wt[r];
89
  }
90
  for (r=0; r<diam; r++) {
91
    wt[r] /= sum;
92
  }
93
  /*
94
  for (r=0; r<diam; r++) {
95
    fprintf(stderr, "!%s: wt[%d] = %g\n", "_nrrdCM_wtAlloc", r, wt[r]);
96
  }
97
  */
98
  return wt;
99
}
100
101
void
102
_nrrdCheapMedian1D(Nrrd *nout, const Nrrd *nin, const NrrdRange *range,
103
                   int radius, float wght,
104
                   int bins, int mode, float *hist) {
105
  /* static const char me[]="_nrrdCheapMedian1D"; */
106
  size_t num;
107
  int X, I, idx, diam;
108
  float half, *wt;
109
  double val, (*lup)(const void *, size_t);
110
111
  diam = 2*radius + 1;
112
  lup = nrrdDLookup[nin->type];
113
  num = nrrdElementNumber(nin);
114
  if (1 == wght) {
115
    /* uniform weighting-> can do sliding histogram optimization */
116
    /* initialize histogram */
117
    half = AIR_CAST(float, diam/2 + 1);
118
    memset(hist, 0, bins*sizeof(float));
119
    for (X=0; X<diam; X++) {
120
      hist[INDEX(nin, range, lup, X, bins, val)]++;
121
    }
122
    /* _nrrdCM_printhist(hist, bins, "after init"); */
123
    /* find median at each point using existing histogram */
124
    for (X=radius; X<(int)num-radius; X++) {
125
      /* _nrrdCM_printhist(hist, bins, "----------"); */
126
      idx = mode ? _nrrdCM_mode(hist, bins) : _nrrdCM_median(hist, half);
127
      val = NRRD_NODE_POS(range->min, range->max, bins, idx);
128
      /* printf(" median idx = %d -> val = %g\n", idx, val); */
129
      nrrdDInsert[nout->type](nout->data, X, val);
130
      /* probably update histogram for next iteration */
131
      if (X < (int)num-radius-1) {
132
        hist[INDEX(nin, range, lup, X+radius+1, bins, val)]++;
133
        hist[INDEX(nin, range, lup, X-radius, bins, val)]--;
134
      }
135
    }
136
  } else {
137
    /* non-uniform weighting --> slow and stupid */
138
    wt = _nrrdCM_wtAlloc(radius, wght);
139
    half = 0.5;
140
    for (X=radius; X<(int)num-radius; X++) {
141
      memset(hist, 0, bins*sizeof(float));
142
      for (I=-radius; I<=radius; I++) {
143
        hist[INDEX(nin, range, lup, I+X, bins, val)] += wt[I+radius];
144
      }
145
      idx = mode ? _nrrdCM_mode(hist, bins) : _nrrdCM_median(hist, half);
146
      val = NRRD_NODE_POS(range->min, range->max, bins, idx);
147
      nrrdDInsert[nout->type](nout->data, X, val);
148
    }
149
    free(wt);
150
  }
151
}
152
153
void
154
_nrrdCheapMedian2D(Nrrd *nout, const Nrrd *nin, const NrrdRange *range,
155
                   int radius, float wght,
156
                   int bins, int mode, float *hist) {
157
  /* static const char me[]="_nrrdCheapMedian2D"; */
158
  int X, Y, I, J;
159
  int sx, sy, idx, diam;
160
  float half, *wt;
161
  double val, (*lup)(const void *, size_t);
162
163
  diam = 2*radius + 1;
164
  sx = AIR_CAST(unsigned int, nin->axis[0].size);
165
  sy = AIR_CAST(unsigned int, nin->axis[1].size);
166
  lup = nrrdDLookup[nin->type];
167
  if (1 == wght) {
168
    /* uniform weighting-> can do sliding histogram optimization */
169
    half = AIR_CAST(float, diam*diam/2 + 1);
170
    for (Y=radius; Y<sy-radius; Y++) {
171
      /* initialize histogram */
172
      memset(hist, 0, bins*sizeof(float));
173
      X = radius;
174
      for (J=-radius; J<=radius; J++) {
175
        for (I=-radius; I<=radius; I++) {
176
          hist[INDEX(nin, range, lup, X+I + sx*(J+Y), bins, val)]++;
177
        }
178
      }
179
      /* _nrrdCM_printhist(hist, bins, "after init"); */
180
      /* find median at each point using existing histogram */
181
      for (X=radius; X<sx-radius; X++) {
182
        idx = mode ? _nrrdCM_mode(hist, bins) : _nrrdCM_median(hist, half);
183
        val = NRRD_NODE_POS(range->min, range->max, bins, idx);
184
        nrrdDInsert[nout->type](nout->data, X + sx*Y, val);
185
        /* probably update histogram for next iteration */
186
        if (X < sx-radius-1) {
187
          for (J=-radius; J<=radius; J++) {
188
            hist[INDEX(nin, range, lup, X+radius+1 + sx*(J+Y), bins, val)]++;
189
            hist[INDEX(nin, range, lup, X-radius + sx*(J+Y), bins, val)]--;
190
          }
191
        }
192
      }
193
    }
194
  } else {
195
    /* non-uniform weighting --> slow and stupid */
196
    wt = _nrrdCM_wtAlloc(radius, wght);
197
    half = 0.5;
198
    for (Y=radius; Y<sy-radius; Y++) {
199
      for (X=radius; X<sx-radius; X++) {
200
        memset(hist, 0, bins*sizeof(float));
201
        for (J=-radius; J<=radius; J++) {
202
          for (I=-radius; I<=radius; I++) {
203
            hist[INDEX(nin, range, lup, I+X + sx*(J+Y),
204
                       bins, val)] += wt[I+radius]*wt[J+radius];
205
          }
206
        }
207
        idx = mode ? _nrrdCM_mode(hist, bins) : _nrrdCM_median(hist, half);
208
        val = NRRD_NODE_POS(range->min, range->max, bins, idx);
209
        nrrdDInsert[nout->type](nout->data, X + sx*Y, val);
210
      }
211
    }
212
    free(wt);
213
  }
214
}
215
216
void
217
_nrrdCheapMedian3D(Nrrd *nout, const Nrrd *nin, const NrrdRange *range,
218
                   int radius, float wght,
219
                   int bins, int mode, float *hist) {
220
  static const char me[]="_nrrdCheapMedian3D";
221
  char done[13];
222
  int X, Y, Z, I, J, K;
223
  int sx, sy, sz, idx, diam;
224
  float half, *wt;
225
  double val, (*lup)(const void *, size_t);
226
227
  diam = 2*radius + 1;
228
  sx = AIR_CAST(int, nin->axis[0].size);
229
  sy = AIR_CAST(int, nin->axis[1].size);
230
  sz = AIR_CAST(int, nin->axis[2].size);
231
  lup = nrrdDLookup[nin->type];
232
  fprintf(stderr, "%s: ...       ", me);
233
  if (1 == wght) {
234
    /* uniform weighting-> can do sliding histogram optimization */
235
    half = AIR_CAST(float, diam*diam*diam/2 + 1);
236
    fflush(stderr);
237
    for (Z=radius; Z<sz-radius; Z++) {
238
      fprintf(stderr, "%s", airDoneStr(radius, Z, sz-radius-1, done));
239
      fflush(stderr);
240
      for (Y=radius; Y<sy-radius; Y++) {
241
        /* initialize histogram */
242
        memset(hist, 0, bins*sizeof(float));
243
        X = radius;
244
        for (K=-radius; K<=radius; K++) {
245
          for (J=-radius; J<=radius; J++) {
246
            for (I=-radius; I<=radius; I++) {
247
              hist[INDEX(nin, range, lup, I+X + sx*(J+Y + sy*(K+Z)),
248
                         bins, val)]++;
249
            }
250
          }
251
        }
252
        /* find median at each point using existing histogram */
253
        for (X=radius; X<sx-radius; X++) {
254
          idx = mode ? _nrrdCM_mode(hist, bins) : _nrrdCM_median(hist, half);
255
          val = NRRD_NODE_POS(range->min, range->max, bins, idx);
256
          nrrdDInsert[nout->type](nout->data, X + sx*(Y + sy*Z), val);
257
          /* probably update histogram for next iteration */
258
          if (X < sx-radius-1) {
259
            for (K=-radius; K<=radius; K++) {
260
              for (J=-radius; J<=radius; J++) {
261
                hist[INDEX(nin, range, lup, X+radius+1 + sx*(J+Y + sy*(K+Z)),
262
                           bins, val)]++;
263
                hist[INDEX(nin, range, lup, X-radius + sx*(J+Y + sy*(K+Z)),
264
                           bins, val)]--;
265
              }
266
            }
267
          }
268
        }
269
      }
270
    }
271
  } else {
272
    /* non-uniform weighting --> slow and stupid */
273
    wt = _nrrdCM_wtAlloc(radius, wght);
274
    half = 0.5;
275
    for (Z=radius; Z<sz-radius; Z++) {
276
      fprintf(stderr, "%s", airDoneStr(radius, Z, sz-radius-1, done));
277
      fflush(stderr);
278
      for (Y=radius; Y<sy-radius; Y++) {
279
        for (X=radius; X<sx-radius; X++) {
280
          memset(hist, 0, bins*sizeof(float));
281
          for (K=-radius; K<=radius; K++) {
282
            for (J=-radius; J<=radius; J++) {
283
              for (I=-radius; I<=radius; I++) {
284
                hist[INDEX(nin, range, lup, I+X + sx*(J+Y + sy*(K+Z)),
285
                           bins, val)]
286
                  += wt[I+radius]*wt[J+radius]*wt[K+radius];
287
              }
288
            }
289
          }
290
          idx = mode ? _nrrdCM_mode(hist, bins) : _nrrdCM_median(hist, half);
291
          val = NRRD_NODE_POS(range->min, range->max, bins, idx);
292
          nrrdDInsert[nout->type](nout->data, X + sx*(Y + sy*Z), val);
293
        }
294
      }
295
    }
296
    free(wt);
297
  }
298
  fprintf(stderr, "\b\b\b\b\b\b  done\n");
299
}
300
301
/* HEY: total copy-and-paste from _nrrdCheapMedian3D */
302
void
303
_nrrdCheapMedian4D(Nrrd *nout, const Nrrd *nin, const NrrdRange *range,
304
                   int radius, float wght,
305
                   int bins, int mode, float *hist) {
306
  static const char me[]="_nrrdCheapMedian4D";
307
  char done[13];
308
  int W, X, Y, Z, H, I, J, K;
309
  int sw, sx, sy, sz, idx, diam;
310
  float half, *wt;
311
  double val, (*lup)(const void *, size_t);
312
313
  diam = 2*radius + 1;
314
  sw = AIR_CAST(int, nin->axis[0].size);
315
  sx = AIR_CAST(int, nin->axis[1].size);
316
  sy = AIR_CAST(int, nin->axis[2].size);
317
  sz = AIR_CAST(int, nin->axis[3].size);
318
  lup = nrrdDLookup[nin->type];
319
  fprintf(stderr, "%s: ...       ", me);
320
  if (1 == wght) {
321
    /* uniform weighting-> can do sliding histogram optimization */
322
    half = AIR_CAST(float, diam*diam*diam*diam/2 + 1);
323
    fflush(stderr);
324
    for (Z=radius; Z<sz-radius; Z++) {
325
      fprintf(stderr, "%s", airDoneStr(radius, Z, sz-radius-1, done));
326
      fflush(stderr);
327
      for (Y=radius; Y<sy-radius; Y++) {
328
        for (X=radius; X<sx-radius; X++) {
329
          /* initialize histogram */
330
          memset(hist, 0, bins*sizeof(float));
331
          W = radius;
332
          for (K=-radius; K<=radius; K++) {
333
            for (J=-radius; J<=radius; J++) {
334
              for (I=-radius; I<=radius; I++) {
335
                for (H=-radius; H<=radius; H++) {
336
                  hist[INDEX(nin, range, lup,
337
                             H+W + sw*(I+X + sx*(J+Y + sy*(K+Z))),
338
                             bins, val)]++;
339
                }
340
              }
341
            }
342
          }
343
          /* find median at each point using existing histogram */
344
          for (W=radius; W<sw-radius; W++) {
345
            idx = mode ? _nrrdCM_mode(hist, bins) : _nrrdCM_median(hist, half);
346
            val = NRRD_NODE_POS(range->min, range->max, bins, idx);
347
            nrrdDInsert[nout->type](nout->data, W + sw*(X + sx*(Y + sy*Z)), val);
348
            /* probably update histogram for next iteration */
349
            if (W < sw-radius-1) {
350
              for (K=-radius; K<=radius; K++) {
351
                for (J=-radius; J<=radius; J++) {
352
                  for (I=-radius; I<=radius; I++) {
353
                    hist[INDEX(nin, range, lup, W+radius+1 + sw*(I+X + sx*(J+Y + sy*(K+Z))),
354
                               bins, val)]++;
355
                    hist[INDEX(nin, range, lup, W-radius + sw*(I+X + sx*(J+Y + sy*(K+Z))),
356
                               bins, val)]--;
357
                  }
358
                }
359
              }
360
            }
361
          }
362
        }
363
      }
364
    }
365
  } else {
366
    /* non-uniform weighting --> slow and stupid */
367
    wt = _nrrdCM_wtAlloc(radius, wght);
368
    half = 0.5;
369
    for (Z=radius; Z<sz-radius; Z++) {
370
      fprintf(stderr, "%s", airDoneStr(radius, Z, sz-radius-1, done));
371
      fflush(stderr);
372
      for (Y=radius; Y<sy-radius; Y++) {
373
        for (X=radius; X<sx-radius; X++) {
374
          for (W=radius; W<sw-radius; W++) {
375
            memset(hist, 0, bins*sizeof(float));
376
            for (K=-radius; K<=radius; K++) {
377
              for (J=-radius; J<=radius; J++) {
378
                for (I=-radius; I<=radius; I++) {
379
                  for (H=-radius; H<=radius; H++) {
380
                    hist[INDEX(nin, range, lup,
381
                               H+W + sw*(I+X + sx*(J+Y + sy*(K+Z))),
382
                               bins, val)]
383
                      += wt[H+radius]*wt[I+radius]*wt[J+radius]*wt[K+radius];
384
                  }
385
                }
386
              }
387
            }
388
            idx = mode ? _nrrdCM_mode(hist, bins) : _nrrdCM_median(hist, half);
389
            val = NRRD_NODE_POS(range->min, range->max, bins, idx);
390
            nrrdDInsert[nout->type](nout->data, W + sw*(X + sx*(Y + sy*Z)), val);
391
          }
392
        }
393
      }
394
    }
395
    free(wt);
396
  }
397
  fprintf(stderr, "\b\b\b\b\b\b  done\n");
398
}
399
400
/*
401
******** nrrdCheapMedian
402
**
403
** histogram-based median or mode filtering
404
** !mode: median filtering
405
** mode: mode filtering
406
*/
407
int
408
nrrdCheapMedian(Nrrd *_nout, const Nrrd *_nin,
409
                int pad, int mode,
410
                unsigned int radius, float wght, unsigned int bins) {
411
  static const char me[]="nrrdCheapMedian", func[]="cmedian";
412
  NrrdRange *range;
413
  float *hist;
414
  Nrrd *nout, *nin;
415
  airArray *mop;
416
  unsigned int minsize;
417
418
  if (!(_nin && _nout)) {
419
    biffAddf(NRRD, "%s: got NULL pointer", me);
420
    return 1;
421
  }
422
  if (!(radius >= 1)) {
423
    biffAddf(NRRD, "%s: need radius >= 1 (got %d)", me, radius);
424
    return 1;
425
  }
426
  if (!(bins >= 1)) {
427
    biffAddf(NRRD, "%s: need bins >= 1 (got %d)", me, bins);
428
    return 1;
429
  }
430
  if (!(AIR_IN_CL(1, _nin->dim, 4))) {
431
    biffAddf(NRRD, "%s: sorry, can only handle dim 1, 2, 3 (not %d)",
432
             me, _nin->dim);
433
    return 1;
434
  }
435
  minsize = AIR_CAST(unsigned int, _nin->axis[0].size);
436
  if (_nin->dim > 1) {
437
    minsize = AIR_MIN(minsize, AIR_CAST(unsigned int, _nin->axis[1].size));
438
  }
439
  if (_nin->dim > 2) {
440
    minsize = AIR_MIN(minsize, AIR_CAST(unsigned int, _nin->axis[2].size));
441
  }
442
  if (!pad && minsize < 2*radius+1) {
443
    biffAddf(NRRD, "%s: minimum nrrd size (%d) smaller than filtering "
444
             "window size (%d) with radius %d; must enable padding", me,
445
             minsize, 2*radius+1, radius);
446
    return 1;
447
  }
448
  if (_nout == _nin) {
449
    biffAddf(NRRD, "%s: nout==nin disallowed", me);
450
    return 1;
451
  }
452
  if (nrrdTypeBlock == _nin->type) {
453
    biffAddf(NRRD, "%s: can't filter nrrd type %s", me,
454
             airEnumStr(nrrdType, nrrdTypeBlock));
455
    return 1;
456
  }
457
458
  mop = airMopNew();
459
  /* set nin based on _nin */
460
  airMopAdd(mop, nin=nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
461
  if (pad) {
462
    airMopAdd(mop, nout=nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
463
    if (nrrdSimplePad_va(nin, _nin, radius, nrrdBoundaryBleed)) {
464
      biffAddf(NRRD, "%s: trouble padding input", me);
465
      airMopError(mop); return 1;
466
    }
467
  } else {
468
    if (nrrdCopy(nin, _nin)) {
469
      biffAddf(NRRD, "%s: trouble copying input", me);
470
      airMopError(mop); return 1;
471
    }
472
    nout = _nout;
473
  }
474
  if (nrrdCopy(nout, nin)) {
475
    biffAddf(NRRD, "%s: failed to create initial copy of input", me);
476
    airMopError(mop); return 1;
477
  }
478
  range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeFalse);
479
  airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways);
480
  if (!(hist = (float*)calloc(bins, sizeof(float)))) {
481
    biffAddf(NRRD, "%s: couldn't allocate histogram (%d bins)", me, bins);
482
    airMopError(mop); return 1;
483
  }
484
  airMopAdd(mop, hist, airFree, airMopAlways);
485
  if (!AIR_EXISTS(wght)) {
486
    wght = 1.0;
487
  }
488
  switch (nin->dim) {
489
  case 1:
490
    _nrrdCheapMedian1D(nout, nin, range, radius, wght, bins, mode, hist);
491
    break;
492
  case 2:
493
    _nrrdCheapMedian2D(nout, nin, range, radius, wght, bins, mode, hist);
494
    break;
495
  case 3:
496
    _nrrdCheapMedian3D(nout, nin, range, radius, wght, bins, mode, hist);
497
    break;
498
  case 4:
499
    _nrrdCheapMedian4D(nout, nin, range, radius, wght, bins, mode, hist);
500
    break;
501
  default:
502
    biffAddf(NRRD, "%s: sorry, %d-dimensional median unimplemented",
503
             me, nin->dim);
504
    airMopError(mop); return 1;
505
  }
506
507
  nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_NONE);
508
  if (nrrdContentSet_va(nout, func, nin, "%d,%d,%g,%d",
509
                        mode, radius, wght, bins)) {
510
    biffAddf(NRRD, "%s:", me);
511
    airMopError(mop); return 1;
512
  }
513
  /* basic info handled by nrrdCopy above */
514
515
  /* set _nout based on nout */
516
  if (pad) {
517
    if (nrrdSimpleCrop(_nout, nout, radius)) {
518
      biffAddf(NRRD, "%s: trouble cropping output", me);
519
      airMopError(mop); return 1;
520
    }
521
  } else {
522
    /* we've already set output in _nout == nout */
523
  }
524
525
  airMopOkay(mop);
526
  return 0;
527
}
528
529
/*
530
** returns intersection of parabolas c(x) = spc^2 (x - xi)^2 + yi
531
*/
532
static double
533
intx(double xx0, double yy0, double xx1, double yy1, double spc) {
534
  double ss;
535
536
  ss = spc*spc;
537
  return (yy1/ss + xx1*xx1 - (yy0/ss + xx0*xx0))/(2*(xx1 - xx0));
538
}
539
540
/*
541
** squared-distance transform of single scanline
542
**
543
** based on code published with:
544
** Distance Transforms of Sampled Functions
545
** Pedro F. Felzenszwalb and Daniel P. Huttenlocher
546
** Cornell Computing and Information Science TR2004-1963
547
**
548
** dd: output (pre-allocated for "len")
549
** ff: input function (pre-allocated for "len")
550
** zz: buffer (pre-allocated for "len"+1) for locations of
551
**     boundaries between parabolas
552
** vv: buffer (pre-allocated for "len") for locations of
553
**     parabolas in lower envelope
554
**
555
** The major modification from the published method is the inclusion
556
** of the "spc" parameter that gives the inter-sample spacing, so that
557
** the multi-dimensional version can work on non-isotropic samples.
558
**
559
** FLT_MIN and FLT_MAX are used to be consistent with the
560
** initialization in nrrdDistanceL2(), which uses FLT_MAX
561
** to be compatible with the case of using floats
562
*/
563
static void
564
distanceL2Sqrd1D(double *dd, const double *ff,
565
                 double *zz, unsigned int *vv,
566
                 size_t len, double spc) {
567
  size_t kk, qq;
568
569
  if (!( dd && ff && zz && vv && len > 0 )) {
570
    /* error */
571
    return;
572
  }
573
574
  kk = 0;
575
  vv[0] = 0;
576
  zz[0] = -FLT_MAX;
577
  zz[1] = +FLT_MAX;
578
  for (qq=1; qq<len; qq++) {
579
    double ss;
580
    ss = intx(AIR_CAST(double, qq), ff[qq], vv[kk], ff[vv[kk]], spc);
581
    /* while (ss <= zz[kk]) {
582
    ** HEY this can have kk going to -1 and into memory errors!
583
    */
584
    while (ss <= zz[kk] && kk) {
585
      kk--;
586
      ss = intx(AIR_CAST(double, qq), ff[qq], vv[kk], ff[vv[kk]], spc);
587
    }
588
    kk++;
589
    vv[kk] = AIR_CAST(unsigned int, qq);
590
    zz[kk] = ss;
591
    zz[kk+1] = +FLT_MAX;
592
  }
593
594
  kk = 0;
595
  for (qq=00; qq<len; qq++) {
596
    double dx;
597
    while (zz[kk+1] < qq) {
598
      kk++;
599
    }
600
    /* cast to avoid overflow weirdness on the unsigned ints */
601
    dx = AIR_CAST(double, qq) - vv[kk];
602
    dd[qq] = spc*spc*dx*dx + ff[vv[kk]];
603
  }
604
605
  return;
606
}
607
608
static int
609
distanceL2Sqrd(Nrrd *ndist, double *spcMean) {
610
  static const char me[]="distanceL2Sqrd";
611
  size_t sizeMax;           /* max size of all axes */
612
  Nrrd *ntmpA, *ntmpB, *npass[NRRD_DIM_MAX+1];
613
  int spcSomeExist, spcSomeNonExist;
614
  unsigned int di, *vv;
615
  double *dd, *ff, *zz;
616
  double spc[NRRD_DIM_MAX], vector[NRRD_SPACE_DIM_MAX];
617
  double (*lup)(const void *, size_t), (*ins)(void *, size_t, double);
618
  airArray *mop;
619
620
  if (!( nrrdTypeFloat == ndist->type || nrrdTypeDouble == ndist->type )) {
621
    /* MWC: This error condition was/is being ignored. */
622
    /*    biffAddf(NRRD, "%s: sorry, can only process type %s or %s (not %s)",
623
                  me,
624
                  airEnumStr(nrrdType, nrrdTypeFloat),
625
                  airEnumStr(nrrdType, nrrdTypeDouble),
626
                  airEnumStr(nrrdType, ndist->type));
627
    */
628
  }
629
630
  spcSomeExist = AIR_FALSE;
631
  spcSomeNonExist = AIR_FALSE;
632
  for (di=0; di<ndist->dim; di++) {
633
    nrrdSpacingCalculate(ndist, di, spc + di, vector);
634
    spcSomeExist |= AIR_EXISTS(spc[di]);
635
    spcSomeNonExist |= !AIR_EXISTS(spc[di]);
636
  }
637
  if (spcSomeExist && spcSomeNonExist) {
638
    biffAddf(NRRD, "%s: axis spacings must all exist or all non-exist", me);
639
    return 1;
640
  }
641
  if (!spcSomeExist) {
642
    for (di=0; di<ndist->dim; di++) {
643
      spc[di] = 1.0;
644
    }
645
  }
646
  *spcMean = 0;
647
  for (di=0; di<ndist->dim; di++) {
648
    *spcMean += spc[di];
649
  }
650
  *spcMean /= ndist->dim;
651
652
  sizeMax = 0;
653
  for (di=0; di<ndist->dim; di++) {
654
    sizeMax = AIR_MAX(sizeMax, ndist->axis[di].size);
655
  }
656
657
  /* create mop and allocate tmp buffers */
658
  mop = airMopNew();
659
  ntmpA = nrrdNew();
660
  airMopAdd(mop, ntmpA, (airMopper)nrrdNuke, airMopAlways);
661
  if (ndist->dim > 2) {
662
    ntmpB = nrrdNew();
663
    airMopAdd(mop, ntmpB, (airMopper)nrrdNuke, airMopAlways);
664
  } else {
665
    ntmpB = NULL;
666
  }
667
  if (nrrdCopy(ntmpA, ndist)
668
      || (ndist->dim > 2 && nrrdCopy(ntmpB, ndist))) {
669
    biffAddf(NRRD, "%s: couldn't allocate image buffers", me);
670
  }
671
  dd = AIR_CAST(double *, calloc(sizeMax, sizeof(double)));
672
  ff = AIR_CAST(double *, calloc(sizeMax, sizeof(double)));
673
  zz = AIR_CAST(double *, calloc(sizeMax+1, sizeof(double)));
674
  vv = AIR_CAST(unsigned int *, calloc(sizeMax, sizeof(unsigned int)));
675
  airMopAdd(mop, dd, airFree, airMopAlways);
676
  airMopAdd(mop, ff, airFree, airMopAlways);
677
  airMopAdd(mop, zz, airFree, airMopAlways);
678
  airMopAdd(mop, vv, airFree, airMopAlways);
679
  if (!( dd && ff && zz && vv )) {
680
    /* MWC: This error condition was/is being ignored. */
681
    /* biffAddf(NRRD, "%s: couldn't allocate scanline buffers", me); */
682
  }
683
684
  /* set up array of buffers */
685
  npass[0] = ndist;
686
  for (di=1; di<ndist->dim; di++) {
687
    npass[di] = (di % 2) ? ntmpA : ntmpB;
688
  }
689
  npass[ndist->dim] = ndist;
690
691
  /* run the multiple passes */
692
  /* what makes the indexing here so simple is that by assuming that
693
     we're processing every axis, the input to any given pass can be
694
     logically considered a 2-D image (a list of scanlines), where the
695
     second axis is the merge of all input axes but the first.  With
696
     the rotational shuffle of axes through passes, the initial axis
697
     and the set of other axes swap places, so its like the 2-D image
698
     is being transposed.  NOTE: the Nrrds that were allocated as
699
     buffers are really being mis-used, in that the axis sizes and
700
     raster ordering of what we're storing there is *not* the same as
701
     told by axis[].size */
702
  lup = nrrdDLookup[ndist->type];
703
  ins = nrrdDInsert[ndist->type];
704
  for (di=0; di<ndist->dim; di++) {
705
    size_t lineIdx, lineNum, valIdx, valNum;
706
707
    valNum = ndist->axis[di].size;
708
    lineNum = nrrdElementNumber(ndist)/valNum;
709
    for (lineIdx=0; lineIdx<lineNum; lineIdx++) {
710
      /* read input scanline into ff */
711
      for (valIdx=0; valIdx<valNum; valIdx++) {
712
        ff[valIdx] = lup(npass[di]->data, valIdx + valNum*lineIdx);
713
      }
714
      /* do the transform */
715
      distanceL2Sqrd1D(dd, ff, zz, vv, valNum, spc[di]);
716
      /* write dd to output scanline */
717
      for (valIdx=0; valIdx<valNum; valIdx++) {
718
        ins(npass[di+1]->data, lineIdx + lineNum*valIdx, dd[valIdx]);
719
      }
720
    }
721
  }
722
723
  airMopOkay(mop);
724
  return 0;
725
}
726
727
/*
728
** helper function for distance transforms, is called by things that want to do
729
** specific kinds of transforms.
730
*/
731
static int
732
_distanceBase(Nrrd *nout, const Nrrd *nin,
733
              int typeOut, const int *axisDo,
734
              double thresh, double bias, int insideHigher) {
735
  static const char me[]="_distanceBase";
736
  size_t ii, nn;
737
  double (*lup)(const void *, size_t), (*ins)(void *, size_t, double);
738
  double spcMean;
739
740
  if (!( nout && nin )) {
741
    biffAddf(NRRD, "%s: got NULL pointer", me);
742
    return 1;
743
  }
744
  if (nrrdTypeBlock == nin->type) {
745
    biffAddf(NRRD, "%s: need scalar type for distance transform (not %s)",
746
             me, airEnumStr(nrrdType, nrrdTypeBlock));
747
    return 1;
748
  }
749
  if (!( nrrdTypeDouble == typeOut || nrrdTypeFloat == typeOut )) {
750
    biffAddf(NRRD, "%s: sorry, can only transform to type %s or %s "
751
             "(not %s)", me,
752
             airEnumStr(nrrdType, nrrdTypeFloat),
753
             airEnumStr(nrrdType, nrrdTypeDouble),
754
             airEnumStr(nrrdType, typeOut));
755
    return 1;
756
  }
757
  if (axisDo) {
758
    biffAddf(NRRD, "%s: sorry, selective axis transform not implemented",
759
             me);
760
    return 1;
761
  }
762
  if (!AIR_EXISTS(thresh)) {
763
    biffAddf(NRRD, "%s: threshold (%g) doesn't exist", me, thresh);
764
    return 1;
765
  }
766
767
  if (nrrdConvert(nout, nin, typeOut)) {
768
    biffAddf(NRRD, "%s: couldn't allocate output", me);
769
    return 1;
770
  }
771
  lup = nrrdDLookup[nout->type];
772
  ins = nrrdDInsert[nout->type];
773
774
  nn = nrrdElementNumber(nout);
775
  for (ii=0; ii<nn; ii++) {
776
    double val, bb;
777
    val = lup(nout->data, ii);
778
    if (insideHigher) {
779
      bb = bias*(val - thresh);
780
      ins(nout->data, ii, val > thresh ? bb*bb : FLT_MAX);
781
    } else {
782
      bb = bias*(thresh - val);
783
      ins(nout->data, ii, val <= thresh ? bb*bb : FLT_MAX);
784
    }
785
  }
786
787
  if (distanceL2Sqrd(nout, &spcMean)) {
788
    biffAddf(NRRD, "%s: trouble doing transform", me);
789
    return 1;
790
  }
791
792
  for (ii=0; ii<nn; ii++) {
793
    double val;
794
    val = sqrt(lup(nout->data, ii));
795
    /* here's where the distance is tweaked downwards by half a sample width */
796
    ins(nout->data, ii, AIR_MAX(0, val - spcMean/2));
797
  }
798
799
  return 0;
800
}
801
802
/*
803
******** nrrdDistanceL2
804
**
805
** computes euclidean (L2) distance transform of input image, after
806
** thresholding at "thresh".
807
**
808
** NOTE: the output of this is slightly offset from what one might
809
** expect; decreased by half of the average (over all axes) sample
810
** spacing.  The reason for this is so that when the transform is
811
** applied to the inverted image and negated, to create a full
812
** signed distance map, the transition from interior to exterior
813
** distance values is smooth.  Without this trick, there is a
814
** small little plateau at the transition.
815
*/
816
int
817
nrrdDistanceL2(Nrrd *nout, const Nrrd *nin,
818
               int typeOut, const int *axisDo,
819
               double thresh, int insideHigher) {
820
  static const char me[]="nrrdDistanceL2";
821
822
  if (_distanceBase(nout, nin, typeOut, axisDo, thresh, 0, insideHigher)) {
823
    biffAddf(NRRD, "%s: trouble doing distance transform", me);
824
    return 1;
825
  }
826
  return 0;
827
}
828
829
int
830
nrrdDistanceL2Biased(Nrrd *nout, const Nrrd *nin,
831
                     int typeOut, const int *axisDo,
832
                     double thresh, double bias, int insideHigher) {
833
  static const char me[]="nrrdDistanceL2Biased";
834
835
  if (_distanceBase(nout, nin, typeOut, axisDo, thresh, bias, insideHigher)) {
836
    biffAddf(NRRD, "%s: trouble doing distance transform", me);
837
    return 1;
838
  }
839
  return 0;
840
}
841
842
int
843
nrrdDistanceL2Signed(Nrrd *nout, const Nrrd *nin,
844
                     int typeOut, const int *axisDo,
845
                     double thresh, int insideHigher) {
846
  static const char me[]="nrrdDistanceL2Signed";
847
  airArray *mop;
848
  Nrrd *ninv;
849
850
  if (!(nout && nin)) {
851
    biffAddf(NRRD, "%s: got NULL pointer", me);
852
    return 1;
853
  }
854
855
  mop = airMopNew();
856
  ninv = nrrdNew();
857
  airMopAdd(mop, ninv, (airMopper)nrrdNuke, airMopAlways);
858
859
  if (nrrdDistanceL2(nout, nin, typeOut, axisDo, thresh, insideHigher)
860
      || nrrdDistanceL2(ninv, nin, typeOut, axisDo, thresh, !insideHigher)
861
      || nrrdArithUnaryOp(ninv, nrrdUnaryOpNegative, ninv)
862
      || nrrdArithBinaryOp(nout, nrrdBinaryOpAdd, nout, ninv)) {
863
    biffAddf(NRRD, "%s: trouble doing or combining transforms", me);
864
    airMopError(mop); return 1;
865
  }
866
867
  airMopOkay(mop);
868
  return 0;
869
}