| File: | src/nrrd/filt.c |
| Location: | line 718, column 58 |
| Description: | Array access (from variable 'dd') results in a null pointer dereference |
| 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)((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)( val = (lup)((nin)->data, (idxIn)), airIndex((range)-> min, (val), (range)->max, bins) ) ( \ | |||
| 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))((float)(pow(1.0/wght, r))); | |||
| 84 | wt[radius-r] = AIR_CAST(float, pow(1.0/wght, r))((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)((float)(diam/2 + 1)); | |||
| 118 | memset(hist, 0, bins*sizeof(float))__builtin___memset_chk (hist, 0, bins*sizeof(float), __builtin_object_size (hist, 0)); | |||
| 119 | for (X=0; X<diam; X++) { | |||
| 120 | hist[INDEX(nin, range, lup, X, bins, val)( val = (lup)((nin)->data, (X)), airIndex((range)->min, (val), (range)->max, bins) )]++; | |||
| 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)( ((double)((range->max))-((range->min)))*((double)((idx ))-(0)) / ((double)((bins)-1)-(0)) + ((range->min))); | |||
| 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)( val = (lup)((nin)->data, (X+radius+1)), airIndex((range) ->min, (val), (range)->max, bins) )]++; | |||
| 133 | hist[INDEX(nin, range, lup, X-radius, bins, val)( val = (lup)((nin)->data, (X-radius)), airIndex((range)-> min, (val), (range)->max, bins) )]--; | |||
| 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))__builtin___memset_chk (hist, 0, bins*sizeof(float), __builtin_object_size (hist, 0)); | |||
| 142 | for (I=-radius; I<=radius; I++) { | |||
| 143 | hist[INDEX(nin, range, lup, I+X, bins, val)( val = (lup)((nin)->data, (I+X)), airIndex((range)->min , (val), (range)->max, bins) )] += 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)( ((double)((range->max))-((range->min)))*((double)((idx ))-(0)) / ((double)((bins)-1)-(0)) + ((range->min))); | |||
| 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)((unsigned int)(nin->axis[0].size)); | |||
| 165 | sy = AIR_CAST(unsigned int, nin->axis[1].size)((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)((float)(diam*diam/2 + 1)); | |||
| 170 | for (Y=radius; Y<sy-radius; Y++) { | |||
| 171 | /* initialize histogram */ | |||
| 172 | memset(hist, 0, bins*sizeof(float))__builtin___memset_chk (hist, 0, bins*sizeof(float), __builtin_object_size (hist, 0)); | |||
| 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)( val = (lup)((nin)->data, (X+I + sx*(J+Y))), airIndex((range )->min, (val), (range)->max, bins) )]++; | |||
| 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)( ((double)((range->max))-((range->min)))*((double)((idx ))-(0)) / ((double)((bins)-1)-(0)) + ((range->min))); | |||
| 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)( val = (lup)((nin)->data, (X+radius+1 + sx*(J+Y))), airIndex ((range)->min, (val), (range)->max, bins) )]++; | |||
| 189 | hist[INDEX(nin, range, lup, X-radius + sx*(J+Y), bins, val)( val = (lup)((nin)->data, (X-radius + sx*(J+Y))), airIndex ((range)->min, (val), (range)->max, bins) )]--; | |||
| 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))__builtin___memset_chk (hist, 0, bins*sizeof(float), __builtin_object_size (hist, 0)); | |||
| 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),( val = (lup)((nin)->data, (I+X + sx*(J+Y))), airIndex((range )->min, (val), (range)->max, bins) ) | |||
| 204 | bins, val)( val = (lup)((nin)->data, (I+X + sx*(J+Y))), airIndex((range )->min, (val), (range)->max, bins) )] += 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)( ((double)((range->max))-((range->min)))*((double)((idx ))-(0)) / ((double)((bins)-1)-(0)) + ((range->min))); | |||
| 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)((int)(nin->axis[0].size)); | |||
| 229 | sy = AIR_CAST(int, nin->axis[1].size)((int)(nin->axis[1].size)); | |||
| 230 | sz = AIR_CAST(int, nin->axis[2].size)((int)(nin->axis[2].size)); | |||
| 231 | lup = nrrdDLookup[nin->type]; | |||
| 232 | fprintf(stderr__stderrp, "%s: ... ", me); | |||
| 233 | if (1 == wght) { | |||
| 234 | /* uniform weighting-> can do sliding histogram optimization */ | |||
| 235 | half = AIR_CAST(float, diam*diam*diam/2 + 1)((float)(diam*diam*diam/2 + 1)); | |||
| 236 | fflush(stderr__stderrp); | |||
| 237 | for (Z=radius; Z<sz-radius; Z++) { | |||
| 238 | fprintf(stderr__stderrp, "%s", airDoneStr(radius, Z, sz-radius-1, done)); | |||
| 239 | fflush(stderr__stderrp); | |||
| 240 | for (Y=radius; Y<sy-radius; Y++) { | |||
| 241 | /* initialize histogram */ | |||
| 242 | memset(hist, 0, bins*sizeof(float))__builtin___memset_chk (hist, 0, bins*sizeof(float), __builtin_object_size (hist, 0)); | |||
| 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)),( val = (lup)((nin)->data, (I+X + sx*(J+Y + sy*(K+Z)))), airIndex ((range)->min, (val), (range)->max, bins) ) | |||
| 248 | bins, val)( val = (lup)((nin)->data, (I+X + sx*(J+Y + sy*(K+Z)))), airIndex ((range)->min, (val), (range)->max, bins) )]++; | |||
| 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)( ((double)((range->max))-((range->min)))*((double)((idx ))-(0)) / ((double)((bins)-1)-(0)) + ((range->min))); | |||
| 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)),( val = (lup)((nin)->data, (X+radius+1 + sx*(J+Y + sy*(K+Z )))), airIndex((range)->min, (val), (range)->max, bins) ) | |||
| 262 | bins, val)( val = (lup)((nin)->data, (X+radius+1 + sx*(J+Y + sy*(K+Z )))), airIndex((range)->min, (val), (range)->max, bins) )]++; | |||
| 263 | hist[INDEX(nin, range, lup, X-radius + sx*(J+Y + sy*(K+Z)),( val = (lup)((nin)->data, (X-radius + sx*(J+Y + sy*(K+Z)) )), airIndex((range)->min, (val), (range)->max, bins) ) | |||
| 264 | bins, val)( val = (lup)((nin)->data, (X-radius + sx*(J+Y + sy*(K+Z)) )), airIndex((range)->min, (val), (range)->max, bins) )]--; | |||
| 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__stderrp, "%s", airDoneStr(radius, Z, sz-radius-1, done)); | |||
| 277 | fflush(stderr__stderrp); | |||
| 278 | for (Y=radius; Y<sy-radius; Y++) { | |||
| 279 | for (X=radius; X<sx-radius; X++) { | |||
| 280 | memset(hist, 0, bins*sizeof(float))__builtin___memset_chk (hist, 0, bins*sizeof(float), __builtin_object_size (hist, 0)); | |||
| 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)),( val = (lup)((nin)->data, (I+X + sx*(J+Y + sy*(K+Z)))), airIndex ((range)->min, (val), (range)->max, bins) ) | |||
| 285 | bins, val)( val = (lup)((nin)->data, (I+X + sx*(J+Y + sy*(K+Z)))), airIndex ((range)->min, (val), (range)->max, bins) )] | |||
| 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)( ((double)((range->max))-((range->min)))*((double)((idx ))-(0)) / ((double)((bins)-1)-(0)) + ((range->min))); | |||
| 292 | nrrdDInsert[nout->type](nout->data, X + sx*(Y + sy*Z), val); | |||
| 293 | } | |||
| 294 | } | |||
| 295 | } | |||
| 296 | free(wt); | |||
| 297 | } | |||
| 298 | fprintf(stderr__stderrp, "\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)((int)(nin->axis[0].size)); | |||
| 315 | sx = AIR_CAST(int, nin->axis[1].size)((int)(nin->axis[1].size)); | |||
| 316 | sy = AIR_CAST(int, nin->axis[2].size)((int)(nin->axis[2].size)); | |||
| 317 | sz = AIR_CAST(int, nin->axis[3].size)((int)(nin->axis[3].size)); | |||
| 318 | lup = nrrdDLookup[nin->type]; | |||
| 319 | fprintf(stderr__stderrp, "%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)((float)(diam*diam*diam*diam/2 + 1)); | |||
| 323 | fflush(stderr__stderrp); | |||
| 324 | for (Z=radius; Z<sz-radius; Z++) { | |||
| 325 | fprintf(stderr__stderrp, "%s", airDoneStr(radius, Z, sz-radius-1, done)); | |||
| 326 | fflush(stderr__stderrp); | |||
| 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))__builtin___memset_chk (hist, 0, bins*sizeof(float), __builtin_object_size (hist, 0)); | |||
| 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,( val = (lup)((nin)->data, (H+W + sw*(I+X + sx*(J+Y + sy*( K+Z))))), airIndex((range)->min, (val), (range)->max, bins ) ) | |||
| 337 | H+W + sw*(I+X + sx*(J+Y + sy*(K+Z))),( val = (lup)((nin)->data, (H+W + sw*(I+X + sx*(J+Y + sy*( K+Z))))), airIndex((range)->min, (val), (range)->max, bins ) ) | |||
| 338 | bins, val)( val = (lup)((nin)->data, (H+W + sw*(I+X + sx*(J+Y + sy*( K+Z))))), airIndex((range)->min, (val), (range)->max, bins ) )]++; | |||
| 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)( ((double)((range->max))-((range->min)))*((double)((idx ))-(0)) / ((double)((bins)-1)-(0)) + ((range->min))); | |||
| 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))),( val = (lup)((nin)->data, (W+radius+1 + sw*(I+X + sx*(J+Y + sy*(K+Z))))), airIndex((range)->min, (val), (range)-> max, bins) ) | |||
| 354 | bins, val)( val = (lup)((nin)->data, (W+radius+1 + sw*(I+X + sx*(J+Y + sy*(K+Z))))), airIndex((range)->min, (val), (range)-> max, bins) )]++; | |||
| 355 | hist[INDEX(nin, range, lup, W-radius + sw*(I+X + sx*(J+Y + sy*(K+Z))),( val = (lup)((nin)->data, (W-radius + sw*(I+X + sx*(J+Y + sy*(K+Z))))), airIndex((range)->min, (val), (range)->max , bins) ) | |||
| 356 | bins, val)( val = (lup)((nin)->data, (W-radius + sw*(I+X + sx*(J+Y + sy*(K+Z))))), airIndex((range)->min, (val), (range)->max , bins) )]--; | |||
| 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__stderrp, "%s", airDoneStr(radius, Z, sz-radius-1, done)); | |||
| 371 | fflush(stderr__stderrp); | |||
| 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))__builtin___memset_chk (hist, 0, bins*sizeof(float), __builtin_object_size (hist, 0)); | |||
| 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,( val = (lup)((nin)->data, (H+W + sw*(I+X + sx*(J+Y + sy*( K+Z))))), airIndex((range)->min, (val), (range)->max, bins ) ) | |||
| 381 | H+W + sw*(I+X + sx*(J+Y + sy*(K+Z))),( val = (lup)((nin)->data, (H+W + sw*(I+X + sx*(J+Y + sy*( K+Z))))), airIndex((range)->min, (val), (range)->max, bins ) ) | |||
| 382 | bins, val)( val = (lup)((nin)->data, (H+W + sw*(I+X + sx*(J+Y + sy*( K+Z))))), airIndex((range)->min, (val), (range)->max, bins ) )] | |||
| 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)( ((double)((range->max))-((range->min)))*((double)((idx ))-(0)) / ((double)((bins)-1)-(0)) + ((range->min))); | |||
| 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__stderrp, "\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(NRRDnrrdBiffKey, "%s: got NULL pointer", me); | |||
| 420 | return 1; | |||
| 421 | } | |||
| 422 | if (!(radius >= 1)) { | |||
| 423 | biffAddf(NRRDnrrdBiffKey, "%s: need radius >= 1 (got %d)", me, radius); | |||
| 424 | return 1; | |||
| 425 | } | |||
| 426 | if (!(bins >= 1)) { | |||
| 427 | biffAddf(NRRDnrrdBiffKey, "%s: need bins >= 1 (got %d)", me, bins); | |||
| 428 | return 1; | |||
| 429 | } | |||
| 430 | if (!(AIR_IN_CL(1, _nin->dim, 4)((1) <= (_nin->dim) && (_nin->dim) <= (4) ))) { | |||
| 431 | biffAddf(NRRDnrrdBiffKey, "%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)((unsigned int)(_nin->axis[0].size)); | |||
| 436 | if (_nin->dim > 1) { | |||
| 437 | minsize = AIR_MIN(minsize, AIR_CAST(unsigned int, _nin->axis[1].size))((minsize) < (((unsigned int)(_nin->axis[1].size))) ? ( minsize) : (((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))((minsize) < (((unsigned int)(_nin->axis[2].size))) ? ( minsize) : (((unsigned int)(_nin->axis[2].size)))); | |||
| 441 | } | |||
| 442 | if (!pad && minsize < 2*radius+1) { | |||
| 443 | biffAddf(NRRDnrrdBiffKey, "%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(NRRDnrrdBiffKey, "%s: nout==nin disallowed", me); | |||
| 450 | return 1; | |||
| 451 | } | |||
| 452 | if (nrrdTypeBlock == _nin->type) { | |||
| 453 | biffAddf(NRRDnrrdBiffKey, "%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(NRRDnrrdBiffKey, "%s: trouble padding input", me); | |||
| 465 | airMopError(mop); return 1; | |||
| 466 | } | |||
| 467 | } else { | |||
| 468 | if (nrrdCopy(nin, _nin)) { | |||
| 469 | biffAddf(NRRDnrrdBiffKey, "%s: trouble copying input", me); | |||
| 470 | airMopError(mop); return 1; | |||
| 471 | } | |||
| 472 | nout = _nout; | |||
| 473 | } | |||
| 474 | if (nrrdCopy(nout, nin)) { | |||
| 475 | biffAddf(NRRDnrrdBiffKey, "%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(NRRDnrrdBiffKey, "%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)(((int)(!((wght) - (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(NRRDnrrdBiffKey, "%s: sorry, %d-dimensional median unimplemented", | |||
| 503 | me, nin->dim); | |||
| 504 | airMopError(mop); return 1; | |||
| 505 | } | |||
| 506 | ||||
| 507 | nrrdAxisInfoCopy(nout, nin, NULL((void*)0), NRRD_AXIS_INFO_NONE0); | |||
| 508 | if (nrrdContentSet_va(nout, func, nin, "%d,%d,%g,%d", | |||
| 509 | mode, radius, wght, bins)) { | |||
| 510 | biffAddf(NRRDnrrdBiffKey, "%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(NRRDnrrdBiffKey, "%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_MAX3.40282347e+38F; | |||
| 577 | zz[1] = +FLT_MAX3.40282347e+38F; | |||
| 578 | for (qq=1; qq<len; qq++) { | |||
| 579 | double ss; | |||
| 580 | ss = intx(AIR_CAST(double, qq)((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)((double)(qq)), ff[qq], vv[kk], ff[vv[kk]], spc); | |||
| 587 | } | |||
| 588 | kk++; | |||
| 589 | vv[kk] = AIR_CAST(unsigned int, qq)((unsigned int)(qq)); | |||
| 590 | zz[kk] = ss; | |||
| 591 | zz[kk+1] = +FLT_MAX3.40282347e+38F; | |||
| 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)((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_MAX16+1]; | |||
| 613 | int spcSomeExist, spcSomeNonExist; | |||
| 614 | unsigned int di, *vv; | |||
| 615 | double *dd, *ff, *zz; | |||
| 616 | double spc[NRRD_DIM_MAX16], vector[NRRD_SPACE_DIM_MAX8]; | |||
| 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_FALSE0; | |||
| 631 | spcSomeNonExist = AIR_FALSE0; | |||
| 632 | for (di=0; di<ndist->dim; di++) { | |||
| 633 | nrrdSpacingCalculate(ndist, di, spc + di, vector); | |||
| 634 | spcSomeExist |= AIR_EXISTS(spc[di])(((int)(!((spc[di]) - (spc[di]))))); | |||
| 635 | spcSomeNonExist |= !AIR_EXISTS(spc[di])(((int)(!((spc[di]) - (spc[di]))))); | |||
| 636 | } | |||
| 637 | if (spcSomeExist && spcSomeNonExist) { | |||
| 638 | biffAddf(NRRDnrrdBiffKey, "%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)((sizeMax) > (ndist->axis[di].size) ? (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((void*)0); | |||
| 666 | } | |||
| 667 | if (nrrdCopy(ntmpA, ndist) | |||
| 668 | || (ndist->dim > 2 && nrrdCopy(ntmpB, ndist))) { | |||
| 669 | biffAddf(NRRDnrrdBiffKey, "%s: couldn't allocate image buffers", me); | |||
| 670 | } | |||
| 671 | dd = AIR_CAST(double *, calloc(sizeMax, sizeof(double)))((double *)(calloc(sizeMax, sizeof(double)))); | |||
| 672 | ff = AIR_CAST(double *, calloc(sizeMax, sizeof(double)))((double *)(calloc(sizeMax, sizeof(double)))); | |||
| 673 | zz = AIR_CAST(double *, calloc(sizeMax+1, sizeof(double)))((double *)(calloc(sizeMax+1, sizeof(double)))); | |||
| 674 | vv = AIR_CAST(unsigned int *, calloc(sizeMax, sizeof(unsigned int)))((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(NRRDnrrdBiffKey, "%s: got NULL pointer", me); | |||
| 742 | return 1; | |||
| 743 | } | |||
| 744 | if (nrrdTypeBlock == nin->type) { | |||
| 745 | biffAddf(NRRDnrrdBiffKey, "%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(NRRDnrrdBiffKey, "%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(NRRDnrrdBiffKey, "%s: sorry, selective axis transform not implemented", | |||
| 759 | me); | |||
| 760 | return 1; | |||
| 761 | } | |||
| 762 | if (!AIR_EXISTS(thresh)(((int)(!((thresh) - (thresh)))))) { | |||
| 763 | biffAddf(NRRDnrrdBiffKey, "%s: threshold (%g) doesn't exist", me, thresh); | |||
| 764 | return 1; | |||
| 765 | } | |||
| 766 | ||||
| 767 | if (nrrdConvert(nout, nin, typeOut)) { | |||
| 768 | biffAddf(NRRDnrrdBiffKey, "%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_MAX3.40282347e+38F); | |||
| 781 | } else { | |||
| 782 | bb = bias*(thresh - val); | |||
| 783 | ins(nout->data, ii, val <= thresh ? bb*bb : FLT_MAX3.40282347e+38F); | |||
| 784 | } | |||
| 785 | } | |||
| 786 | ||||
| 787 | if (distanceL2Sqrd(nout, &spcMean)) { | |||
| 788 | biffAddf(NRRDnrrdBiffKey, "%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)((0) > (val - spcMean/2) ? (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(NRRDnrrdBiffKey, "%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(NRRDnrrdBiffKey, "%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(NRRDnrrdBiffKey, "%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(NRRDnrrdBiffKey, "%s: trouble doing or combining transforms", me); | |||
| 864 | airMopError(mop); return 1; | |||
| 865 | } | |||
| 866 | ||||
| 867 | airMopOkay(mop); | |||
| 868 | return 0; | |||
| 869 | } |