GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/nrrd/map.c Lines: 96 291 33.0 %
Date: 2017-05-26 Branches: 54 185 29.2 %

Line Branch Exec Source
1
/*
2
  Teem: Tools to process and visualize scientific data and images             .
3
  Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
4
  Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
5
  Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
6
7
  This library is free software; you can redistribute it and/or
8
  modify it under the terms of the GNU Lesser General Public License
9
  (LGPL) as published by the Free Software Foundation; either
10
  version 2.1 of the License, or (at your option) any later version.
11
  The terms of redistributing and/or modifying this software also
12
  include exceptions to the LGPL that facilitate static linking.
13
14
  This library is distributed in the hope that it will be useful,
15
  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
  Lesser General Public License for more details.
18
19
  You should have received a copy of the GNU Lesser General Public License
20
  along with this library; if not, write to Free Software Foundation, Inc.,
21
  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22
*/
23
24
#include "nrrd.h"
25
#include "privateNrrd.h"
26
27
/*
28
** RETIRED: nrrdMinMaxSet()
29
**
30
** Sets nrrd->min and nrrd->max to the extremal (existent) values in
31
** the given nrrd, by calling the appropriate member of nrrdMinMaxExactFind[]
32
**
33
** calling this function will result in nrrd->hasNonExist being set
34
** (because of the nrrdFindMinMax[] functions)
35
**
36
** decided NOT to use biff, so that this is a more distinct alternative to
37
** nrrdMinMaxCleverSet().
38
39
void
40
nrrdMinMaxSet(Nrrd *nrrd) {
41
  NRRD_TYPE_BIGGEST _min, _max;
42
43
  if (nrrd) {
44
    if (!airEnumValCheck(nrrdType, nrrd->type)
45
        && nrrdTypeBlock != nrrd->type) {
46
      nrrdFindMinMax[nrrd->type](&_min, &_max, nrrd);
47
      nrrd->min = nrrdDLoad[nrrd->type](&_min);
48
      nrrd->max = nrrdDLoad[nrrd->type](&_max);
49
    } else {
50
      nrrd->min = nrrd->max = AIR_NAN;
51
    }
52
  }
53
  return;
54
}
55
*/
56
57
/*
58
** RETIRED: nrrdMinMaxCleverSet()
59
**
60
** basically a wrapper around nrrdMinMaxSet(), with bells + whistles:
61
** 1) will call nrrdMinMaxSet only when one of nrrd->min and nrrd->max
62
**    are non-existent, with the end result that only the non-existent
63
**    values are over-written
64
** 2) obeys the nrrdStateClever8BitMinMax global state to short-cut
65
**    finding min and max for 8-bit data.  Values for nrrd->min or
66
**    nrrd->max which were existent to start with are untouched.
67
** 3) reports error if there are no existent values in nrrd (AIR_EXISTS()
68
**    fails on every value)
69
**
70
** Like nrrdMinMaxSet(), this will always set nrrd->hasNonExist.
71
**
72
** Uses biff.
73
74
int
75
nrrdMinMaxCleverSet(Nrrd *nrrd) {
76
  static const char me[]="nrrdMinMaxCleverSet";
77
  double min, max;
78
79
  if (!nrrd) {
80
    biffAddf(NRRD, "%s: got NULL pointer", me);
81
    return 1;
82
  }
83
  if (airEnumValCheck(nrrdType, nrrd->type)) {
84
    biffAddf(NRRD, "%s: input nrrd has invalid type (%d)", me, nrrd->type);
85
    return 1;
86
  }
87
  if (nrrdTypeBlock == nrrd->type) {
88
    sprintf(err, "%s: can't find min/max of type %s", me,
89
            airEnumStr(nrrdType, nrrdTypeBlock));
90
  }
91
  if (AIR_EXISTS(nrrd->min) && AIR_EXISTS(nrrd->max)) {
92
    / * both of min and max already set, so we won't look for those, but
93
       we have to comply with stated behavior of always setting hasNonExist * /
94
    nrrdHasNonExistSet(nrrd);
95
    return 0;
96
  }
97
  if (nrrdStateClever8BitMinMax
98
      && (nrrdTypeChar == nrrd->type || nrrdTypeUChar == nrrd->type)) {
99
    if (nrrdTypeChar == nrrd->type) {
100
      if (!AIR_EXISTS(nrrd->min))
101
        nrrd->min = SCHAR_MIN;
102
      if (!AIR_EXISTS(nrrd->max))
103
        nrrd->max = SCHAR_MAX;
104
    } else {
105
      if (!AIR_EXISTS(nrrd->min))
106
        nrrd->min = 0;
107
      if (!AIR_EXISTS(nrrd->max))
108
        nrrd->max = UCHAR_MAX;
109
    }
110
    nrrdHasNonExistSet(nrrd);
111
    return 0;
112
  }
113
114
  / * at this point we need to find either min and/or max (at least
115
     one of them was non-existent on the way in) * /
116
117
  / * save incoming values in case they exist * /
118
  min = nrrd->min;
119
  max = nrrd->max;
120
  / * this will set nrrd->min, nrrd->max, and hasNonExist * /
121
  nrrdMinMaxSet(nrrd);
122
  if (!( AIR_EXISTS(nrrd->min) && AIR_EXISTS(nrrd->max) )) {
123
    biffAddf(NRRD, "%s: no existent values!", me);
124
    return 1;
125
  }
126
  / * re-enstate the existent incoming min and/or max values * /
127
  if (AIR_EXISTS(min))
128
    nrrd->min = min;
129
  if (AIR_EXISTS(max))
130
    nrrd->max = max;
131
132
  return 0;
133
}
134
*/
135
136
static int
137
clampRoundConvert(Nrrd *nout, const Nrrd *nin, int type,
138
                  int doClamp, int roundDir) {
139
  static const char me[]="clampRoundConvert";
140
132
  char typeS[AIR_STRLEN_SMALL];
141
66
  size_t num, size[NRRD_DIM_MAX];
142
143

132
  if (!( nin && nout
144
66
         && !nrrdCheck(nin)
145
132
         && !airEnumValCheck(nrrdType, type) )) {
146
    biffAddf(NRRD, "%s: invalid args", me);
147
    return 1;
148
  }
149
66
  if (nin->type == nrrdTypeBlock || type == nrrdTypeBlock) {
150
    biffAddf(NRRD, "%s: can't convert to or from nrrd type %s", me,
151
             airEnumStr(nrrdType, nrrdTypeBlock));
152
    return 1;
153
  }
154

74
  if (nout == nin && nrrdTypeSize[type] != nrrdTypeSize[nin->type]) {
155
    biffAddf(NRRD, "%s: nout==nin but input,output type sizes unequal", me);
156
    return 1;
157
  }
158
122
  if (nrrdStateDisallowIntegerNonExist
159

188
      && !nrrdTypeIsIntegral[nin->type] && nrrdTypeIsIntegral[type]) {
160
    /* there's a risk of non-existent values getting converted to
161
       non-sensical integral values */
162
    if (nrrdHasNonExist(nin)) {
163
      biffAddf(NRRD, "%s: can't convert to integral values (%s) with "
164
               "non-existent values in input", me,
165
               airEnumStr(nrrdType, type));
166
      return 1;
167
    }
168
  }
169
170
  /* if we're actually converting to the same type, just do a copy */
171
66
  if (type == nin->type) {
172
41
    if (nout == nin) {
173
      /* nout == nin is allowed if the input and output type are
174
         of the same size, which will certainly be the case if the
175
         input and output types are identical, so there's actually
176
         no work to do */
177
    } else {
178
33
      if (nrrdCopy(nout, nin)) {
179
        biffAddf(NRRD, "%s: couldn't copy input to output", me);
180
        return 1;
181
      }
182
    }
183
  } else {
184
    /* allocate space if necessary */
185
25
    nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size);
186
    /* MUST be nrrdMaybeAlloc_nva (not nrrd Alloc_nva) because we allow
187
       nout==nin if type sizes match */
188
25
    if (nrrdMaybeAlloc_nva(nout, type, nin->dim, size)) {
189
      biffAddf(NRRD, "%s: failed to allocate output", me);
190
      return 1;
191
    }
192
193
    /* call the appropriate converter */
194
25
    num = nrrdElementNumber(nin);
195
25
    if (roundDir) {
196
      _nrrdCastClampRound[nout->type][nin->type](nout->data, nin->data, num,
197
                                                 doClamp, roundDir);
198

50
    } else if (doClamp) {
199
25
      _nrrdClampConv[nout->type][nin->type](nout->data, nin->data, num);
200
    } else {
201
25
      _nrrdConv[nout->type][nin->type](nout->data, nin->data, num);
202
    }
203
25
    nout->blockSize = 0;
204
205
    /* copy peripheral information */
206
25
    nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_NONE);
207
25
    sprintf(typeS, "(%s)", airEnumStr(nrrdType, nout->type));
208
25
    if (nrrdContentSet_va(nout, typeS, nin, "")) {
209
      biffAddf(NRRD, "%s:", me);
210
      return 1;
211
    }
212
    /* the min and max have probably changed if there was a conversion
213
       to integral values, or to a lower precision representation */
214
25
    if (nrrdBasicInfoCopy(nout, nin,
215
                          NRRD_BASIC_INFO_DATA_BIT
216
                          | NRRD_BASIC_INFO_TYPE_BIT
217
                          | NRRD_BASIC_INFO_BLOCKSIZE_BIT
218
                          | NRRD_BASIC_INFO_DIMENSION_BIT
219
                          | NRRD_BASIC_INFO_CONTENT_BIT
220
                          | NRRD_BASIC_INFO_COMMENTS_BIT
221
25
                          | (nrrdStateKeyValuePairsPropagate
222
                             ? 0
223
                             : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) {
224
      biffAddf(NRRD, "%s:", me);
225
      return 1;
226
    }
227
  }
228
66
  return 0;
229
66
}
230
231
/*
232
******** nrrdConvert()
233
**
234
** copies values from one type of nrrd to another, without any
235
** transformation, except what you get with a cast.  The point is to
236
** make available on Nrrds the exact same behavior as you have in C
237
** with casts and assignments.
238
*/
239
int
240
nrrdConvert(Nrrd *nout, const Nrrd *nin, int type) {
241
  static const char me[]="nrrdConvert";
242
243
132
  if (clampRoundConvert(nout, nin, type,
244
                        AIR_FALSE /* clamp */,
245
                        0 /* round */)) {
246
    biffAddf(NRRD, "%s: trouble", me);
247
    return 1;
248
  }
249
66
  return 0;
250
66
}
251
252
/*
253
******** nrrdClampConvert()
254
**
255
** same as nrrdConvert, but with clamping to output value representation range
256
** so as to avoid wrap-around artifacts
257
**
258
** HEY: WARNING: may have loss of data when processing long long
259
** (either signed or unsigned)
260
*/
261
int
262
nrrdClampConvert(Nrrd *nout, const Nrrd *nin, int type) {
263
  static const char me[]="nrrdClampConvert";
264
265
  if (clampRoundConvert(nout, nin, type,
266
                        AIR_TRUE  /* clamp */,
267
                        0 /* round */)) {
268
    biffAddf(NRRD, "%s: trouble", me);
269
    return 1;
270
  }
271
  return 0;
272
}
273
274
/*
275
******** nrrdCastClampRound()
276
**
277
** For (maybe) doing rounding to integer, then (maybe) clamping
278
** and then casting.  The maybe-ness of rounding and clamping
279
** in this function is inconsistent with the certainty of clamping
280
** nrrdClampConvert, so this function name may be regretted.
281
**
282
** NOTE! Rounding is not performed when outType is for float
283
** or double!  That logic is implemented here.
284
**
285
** And warning same as above:
286
** HEY: WARNING: may have loss of data when processing long long
287
** (either signed or unsigned)
288
*/
289
int
290
nrrdCastClampRound(Nrrd *nout, const Nrrd *nin, int outType,
291
                   int doClamp, int roundDir) {
292
  static const char me[]="nrrdCastClampRound";
293
294
  if (clampRoundConvert(nout, nin, outType, doClamp,
295
                        nrrdTypeIsIntegral[outType] ? roundDir : 0)) {
296
    biffAddf(NRRD, "%s: trouble", me);
297
    return 1;
298
  }
299
  return 0;
300
}
301
302
/*
303
******** nrrdQuantize()
304
**
305
** convert values to 8, 16, or 32 bit unsigned quantities
306
** by mapping the value range delimited by the nrrd's min
307
** and max to the representable range
308
**
309
** NOTE: for the time being, this uses a "double" as the intermediate
310
** value holder, which may mean needless loss of precision
311
*/
312
int
313
nrrdQuantize(Nrrd *nout, const Nrrd *nin, const NrrdRange *_range,
314
             unsigned int bits) {
315
  static const char me[]="nrrdQuantize", func[]="quantize";
316
  double valIn, minIn, maxIn, eps;
317
  int type=nrrdTypeUnknown;
318
2
  size_t I, num, size[NRRD_DIM_MAX];
319
  unsigned char *outUC;
320
  unsigned short *outUS;
321
  unsigned int *outUI;
322
  airArray *mop;
323
  NrrdRange *range;
324
325
1
  if (!(nin && nout)) {
326
    biffAddf(NRRD, "%s: got NULL pointer", me);
327
    return 1;
328
  }
329
1
  if (nrrdTypeBlock == nin->type) {
330
    biffAddf(NRRD, "%s: can't quantize type %s", me,
331
             airEnumStr(nrrdType, nrrdTypeBlock));
332
    return 1;
333
  }
334
335
  /* determine nrrd type from number of bits */
336

1
  switch (bits) {
337
1
  case 8:  type = nrrdTypeUChar;  break;
338
  case 16: type = nrrdTypeUShort; break;
339
  case 32: type = nrrdTypeUInt;   break;
340
  default:
341
    biffAddf(NRRD, "%s: bits has to be 8, 16, or 32 (not %d)", me, bits);
342
    return 1;
343
  }
344

1
  if (nout == nin && nrrdTypeSize[type] != nrrdTypeSize[nin->type]) {
345
    biffAddf(NRRD, "%s: nout==nin but input,output type sizes unequal", me);
346
    return 1;
347
  }
348
1
  mop = airMopNew();
349
1
  if (_range) {
350
    range = nrrdRangeCopy(_range);
351
    nrrdRangeSafeSet(range, nin, nrrdBlind8BitRangeState);
352
  } else {
353
1
    range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeState);
354
  }
355
1
  airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways);
356

2
  if (nrrdStateDisallowIntegerNonExist && range->hasNonExist) {
357
    biffAddf(NRRD, "%s: can't quantize non-existent values "
358
             "(NaN, +/-inf)", me);
359
    airMopError(mop); return 1;
360
  }
361
362
  /* allocate space if necessary */
363
1
  nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size);
364
  /* MUST be nrrdMaybeAlloc_nva (not nrrd Alloc_nva) because we allow
365
     nout==nin if type sizes match */
366
1
  if (nrrdMaybeAlloc_nva(nout, type, nin->dim, size)) {
367
    biffAddf(NRRD, "%s: failed to create output", me);
368
    airMopError(mop); return 1;
369
  }
370
371
  /* the skinny */
372
2
  num = nrrdElementNumber(nin);
373
2
  minIn = range->min;
374
2
  maxIn = range->max;
375
2
  eps = (minIn == maxIn ? 1.0 : 0.0);
376
2
  outUC = (unsigned char*)nout->data;
377
2
  outUS = (unsigned short*)nout->data;
378
2
  outUI = (unsigned int*)nout->data;
379

2
  switch(bits) {
380
  case 8:
381
256202
    for (I=0; I<num; I++) {
382
128100
      valIn = nrrdDLookup[nin->type](nin->data, I);
383
128100
      outUC[I] = airIndexClamp(minIn, valIn, maxIn+eps, 1 << 8);
384
    }
385
    break;
386
  case 16:
387
    for (I=0; I<num; I++) {
388
      valIn = nrrdDLookup[nin->type](nin->data, I);
389
      outUS[I] = airIndexClamp(minIn, valIn, maxIn+eps, 1 << 16);
390
    }
391
    break;
392
  case 32:
393
    for (I=0; I<num; I++) {
394
      valIn = nrrdDLookup[nin->type](nin->data, I);
395
      outUI[I] = AIR_CAST(unsigned int,
396
                          airIndexClampULL(minIn, valIn, maxIn+eps,
397
                                           AIR_ULLONG(1) << 32));
398
    }
399
    break;
400
  }
401
402
  /* set information in new volume */
403
1
  if (nout != nin) {
404
1
    nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_NONE);
405
1
  }
406
1
  if (nrrdContentSet_va(nout, func, nin, "%d", bits)) {
407
    biffAddf(NRRD, "%s:", me);
408
    airMopError(mop); return 1;
409
  }
410
1
  if (nrrdBasicInfoCopy(nout, nin,
411
                        NRRD_BASIC_INFO_DATA_BIT
412
                        | NRRD_BASIC_INFO_TYPE_BIT
413
                        | NRRD_BASIC_INFO_BLOCKSIZE_BIT
414
                        | NRRD_BASIC_INFO_DIMENSION_BIT
415
                        | NRRD_BASIC_INFO_CONTENT_BIT
416
                        | NRRD_BASIC_INFO_OLDMIN_BIT
417
                        | NRRD_BASIC_INFO_OLDMAX_BIT
418
                        | NRRD_BASIC_INFO_COMMENTS_BIT
419
1
                        | (nrrdStateKeyValuePairsPropagate
420
                           ? 0
421
                           : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) {
422
    biffAddf(NRRD, "%s:", me);
423
    airMopError(mop); return 1;
424
  }
425
1
  nout->oldMin = minIn;
426
1
  nout->oldMax = maxIn;
427
1
  nout->blockSize = 0;
428
429
1
  airMopOkay(mop);
430
1
  return 0;
431
1
}
432
433
/*
434
** _nrrdTypeNumberOfValues[]
435
**
436
** only meaningful for integral values, and only correct for
437
** 32-bit values; tells the number of different integral values that
438
** can be represented by the type
439
**
440
** HEY: this used to be public, but that was stopped when it was clear
441
** that only the following function was using the array.  The reason
442
** for the array is questionable, and the implementation below
443
** should be re-evaluated.
444
*/
445
static const double
446
_nrrdTypeNumberOfValues[NRRD_TYPE_MAX+1] = {
447
  0,                         /* unknown */
448
  UCHAR_MAX+1,               /* char */
449
  UCHAR_MAX+1,               /* unsigned char */
450
  USHRT_MAX+1,               /* short */
451
  USHRT_MAX+1,               /* unsigned short */
452
  (double)UINT_MAX+1,        /* int */
453
  (double)UINT_MAX+1,        /* unsigned int */
454
  (double)NRRD_ULLONG_MAX+1, /* long long */
455
  (double)NRRD_ULLONG_MAX+1, /* unsigned long long */
456
  0,                         /* float */
457
  0,                         /* double */
458
  0                          /* punt */
459
};
460
461
/*
462
******** nrrdUnquantize()
463
**
464
** try to recover floating point values from a quantized nrrd,
465
** using the oldMin and oldMax values, if they exist.  If they
466
** don't exist, the output range will be 0.0 to 1.0.  However,
467
** because we're using NRRD_CELL_POS to recover values,
468
** the output values will never be exactly 0.0 to 1.0 (or oldMin
469
** to oldMax).  In unsigned char data, for instance, the value
470
** V will be mapped to:
471
** NRRD_CELL_POS(0.0, 1.0, 256, V) ==
472
** AIR_AFFINE(0, V + 0.5, 256, 0.0, 1.0) ==
473
** ((double)(1.0)-(0.0))*((double)(V+0.5)-(0))/((double)(256)-(0)) + (0.0) ==
474
** (1.0)*(V+0.5) / (256.0) + (0.0) ==
475
** (V+0.5)/256
476
** so a 0 will be mapped to 1/512 = 0.00195
477
*/
478
int
479
nrrdUnquantize(Nrrd *nout, const Nrrd *nin, int type) {
480
  static const char me[]="nrrdUnquantize", func[]="unquantize";
481
  float *outF;
482
  double *outD, minIn, numValIn, minOut, maxOut, valIn;
483
2
  size_t NN, II, size[NRRD_DIM_MAX];
484
485
1
  if (!(nout && nin)) {
486
    biffAddf(NRRD, "%s: got NULL pointer", me);
487
    return 1;
488
  }
489
1
  if (airEnumValCheck(nrrdType, type)) {
490
    biffAddf(NRRD, "%s: don't recognize type %d\n", me, type);
491
    return 1;
492
  }
493
1
  if (!( type == nrrdTypeFloat || type == nrrdTypeDouble )) {
494
    biffAddf(NRRD, "%s: output type must be %s or %s (not %s)", me,
495
             airEnumStr(nrrdType, nrrdTypeFloat),
496
             airEnumStr(nrrdType, nrrdTypeDouble),
497
             airEnumStr(nrrdType, type));
498
    return 1;
499
  }
500
1
  if (nrrdTypeBlock == nin->type) {
501
    biffAddf(NRRD, "%s: can't unquantize type %s", me,
502
             airEnumStr(nrrdType, nrrdTypeBlock));
503
    return 1;
504
  }
505
1
  if (!nrrdTypeIsIntegral[nin->type]) {
506
    biffAddf(NRRD, "%s: can only unquantize integral types, not %s", me,
507
             airEnumStr(nrrdType, nin->type));
508
    return 1;
509
  }
510

1
  if (nout == nin && nrrdTypeSize[type] != nrrdTypeSize[nin->type]) {
511
    biffAddf(NRRD, "%s: nout==nin but input,output type sizes unequal", me);
512
    return 1;
513
  }
514
515
1
  nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size);
516
1
  if (nrrdMaybeAlloc_nva(nout, type, nin->dim, size)) {
517
    biffAddf(NRRD, "%s: failed to create output", me);
518
    return 1;
519
  }
520
1
  minIn = nrrdTypeMin[nin->type];
521
1
  numValIn = _nrrdTypeNumberOfValues[nin->type];
522

2
  if (AIR_EXISTS(nin->oldMin) && AIR_EXISTS(nin->oldMax)) {
523
    minOut = nin->oldMin;
524
    maxOut = nin->oldMax;
525
1
  } else {
526
    minOut = 0.0;
527
    maxOut = 1.0;
528
  }
529
2
  outF = (float*)nout->data;
530
2
  outD = (double*)nout->data;
531
2
  NN = nrrdElementNumber(nin);
532
2
  switch(type) {
533
  case nrrdTypeFloat:
534
    for (II=0; II<NN; II++) {
535
      valIn = minIn + nrrdDLookup[nin->type](nin->data, II);
536
      outF[II] = AIR_CAST(float,
537
                          NRRD_CELL_POS(minOut, maxOut, numValIn, valIn));
538
    }
539
    break;
540
  case nrrdTypeDouble:
541
256202
    for (II=0; II<NN; II++) {
542
128100
      valIn = minIn + nrrdDLookup[nin->type](nin->data, II);
543
128100
      outD[II] = NRRD_CELL_POS(minOut, maxOut, numValIn, valIn);
544
    }
545
    break;
546
  }
547
548
  /* set information in new volume */
549
1
  if (nout != nin) {
550
1
    nrrdAxisInfoCopy(nout, nin, NULL, NRRD_AXIS_INFO_NONE);
551
1
  }
552
1
  if (nrrdContentSet_va(nout, func, nin, "")) {
553
    biffAddf(NRRD, "%s:", me);
554
    return 1;
555
  }
556
1
  if (nrrdBasicInfoCopy(nout, nin,
557
                        NRRD_BASIC_INFO_DATA_BIT
558
                        | NRRD_BASIC_INFO_TYPE_BIT
559
                        | NRRD_BASIC_INFO_BLOCKSIZE_BIT
560
                        | NRRD_BASIC_INFO_DIMENSION_BIT
561
                        | NRRD_BASIC_INFO_CONTENT_BIT
562
                        | NRRD_BASIC_INFO_OLDMIN_BIT
563
                        | NRRD_BASIC_INFO_OLDMAX_BIT
564
                        | NRRD_BASIC_INFO_COMMENTS_BIT
565
1
                        | (nrrdStateKeyValuePairsPropagate
566
                           ? 0
567
                           : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) {
568
    biffAddf(NRRD, "%s:", me);
569
    return 1;
570
  }
571
1
  nout->oldMin = nout->oldMax = AIR_NAN;
572
1
  nout->blockSize = 0;
573
1
  return 0;
574
1
}
575
576
577
/*
578
** _nrrdHistoEqCompare()
579
**
580
** used by nrrdHistoEq in smart mode to sort the "steady" array
581
** in _descending_ order
582
*/
583
int
584
_nrrdHistoEqCompare(const void *a, const void *b) {
585
586
  return *((const unsigned int*)b) - *((const unsigned int*)a);
587
}
588
589
/*
590
******** nrrdHistoEq()
591
**
592
** performs histogram equalization on given nrrd, treating it as a
593
** big one-dimensional array.  The procedure is as follows:
594
** - create a histogram of nrrd (using "bins" bins)
595
** - integrate the histogram, and normalize and shift this so it is
596
**   a monotonically increasing function from min to max, where
597
**   (min,max) is the range of values in the nrrd
598
** - map the values in the nrrd through the adjusted histogram integral
599
**
600
** If the histogram of the given nrrd is already as flat as can be,
601
** the histogram integral will increase linearly, and the adjusted
602
** histogram integral should be close to the identity function, so
603
** the values shouldn't change much.
604
**
605
** If the nhistP arg is non-NULL, then it is set to point to
606
** the histogram that was used for calculation. Otherwise this
607
** histogram is deleted on return.
608
**
609
** This is all that is done normally, when "smart" is == 0.  In
610
** "smart" mode (activated by setting "smart" to something greater
611
** than 0), the histogram is analyzed during its creation to detect if
612
** there are a few bins which keep getting hit with the same value
613
** over and over.  It may be desirable to ignore these bins in the
614
** histogram integral because they may not contain any useful
615
** information, and so they should not effect how values are
616
** re-mapped.  The value of "smart" is the number of bins that will be
617
** ignored.  For instance, use the value 1 if the problem with naive
618
** histogram equalization is a large amount of background (which is
619
** exactly one fixed value).
620
*/
621
int
622
nrrdHistoEq(Nrrd *nout, const Nrrd *nin, Nrrd **nmapP,
623
            unsigned int bins, unsigned int smart, float amount) {
624
  static const char me[]="nrrdHistoEq", func[]="heq";
625
  Nrrd *nhist, *nmap;
626
  double val, min, max, *last = NULL, *ycoord = NULL;
627
  int *respect = NULL, lort;
628
  unsigned int *hist, *steady = NULL, idx, hirt;
629
  size_t I, num;
630
  airArray *mop;
631
  NrrdRange *range;
632
  unsigned bii;
633
634
  if (!(nout && nin)) {
635
    biffAddf(NRRD, "%s: got NULL pointer", me);
636
    return 1;
637
  }
638
  if (nrrdTypeBlock == nin->type) {
639
    biffAddf(NRRD, "%s: can't histogram equalize type %s", me,
640
             airEnumStr(nrrdType, nrrdTypeBlock));
641
    return 1;
642
  }
643
  if (!(bins > 4)) {
644
    biffAddf(NRRD, "%s: need # bins > 4 (not %d)", me, bins);
645
    return 1;
646
  }
647
  /* we start by simply copying, because the only thing we're
648
     changing is the values themselves, and all peripheral
649
     information is unchanged by this value remapping */
650
  if (nout != nin) {
651
    if (nrrdCopy(nout, nin)) {
652
      biffAddf(NRRD, "%s:", me);
653
      return 1;
654
    }
655
  }
656
657
  mop = airMopNew();
658
  if (nmapP) {
659
    airMopAdd(mop, nmapP, (airMopper)airSetNull, airMopOnError);
660
  }
661
  num = nrrdElementNumber(nin);
662
  if (smart <= 0) {
663
    nhist = nrrdNew();
664
    if (nrrdHisto(nhist, nin, NULL, NULL, bins, nrrdTypeUInt)) {
665
      biffAddf(NRRD, "%s: failed to create histogram", me);
666
      airMopError(mop); return 1;
667
    }
668
    airMopAdd(mop, nhist, (airMopper)nrrdNuke, airMopAlways);
669
    hist = (unsigned int*)nhist->data;
670
    min = nhist->axis[0].min;
671
    max = nhist->axis[0].max;
672
  } else {
673
    /* for "smart" mode, we have to some extra work while creating the
674
       histogram to look for bins incessantly hit with the exact same
675
       value */
676
    if (nrrdMaybeAlloc_va(nhist=nrrdNew(), nrrdTypeUInt, 1,
677
                          AIR_CAST(size_t, bins))) {
678
      biffAddf(NRRD, "%s: failed to allocate histogram", me);
679
      airMopError(mop); return 1;
680
    }
681
    airMopAdd(mop, nhist, (airMopper)nrrdNuke, airMopAlways);
682
    hist = (unsigned int*)nhist->data;
683
    nhist->axis[0].size = bins;
684
    /* allocate the respect, steady, and last arrays */
685
    respect = (int*)calloc(bins, sizeof(int));
686
    steady = (unsigned int*)calloc(2*bins, sizeof(unsigned int));
687
    last = (double*)calloc(bins, sizeof(double));
688
    airMopMem(mop, &respect, airMopAlways);
689
    airMopMem(mop, &steady, airMopAlways);
690
    airMopMem(mop, &last, airMopAlways);
691
    if (!(respect && steady && last)) {
692
      biffAddf(NRRD, "%s: couldn't allocate smart arrays", me);
693
      airMopError(mop); return 1;
694
    }
695
    /* steady[0 + 2*bii] == how many times has bin bii seen the same value
696
       steady[1 + 2*bii] == bii (steady will be rearranged by qsort()) */
697
    for (bii=0; bii<bins; bii++) {
698
      last[bii] = AIR_NAN;
699
      respect[bii] = 1;
700
      steady[1 + 2*bii] = bii;
701
    }
702
    /* now create the histogram */
703
    range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeState);
704
    airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways);
705
    if (range->min == range->max) {
706
      biffAddf(NRRD, "%s: invalid min and max in nrrd.  "
707
               "Min and max are equivalent (min,max = %g).", me, range->min);
708
      airMopError(mop); return 1;
709
    }
710
    min = range->min;
711
    max = range->max;
712
    for (I=0; I<num; I++) {
713
      val = nrrdDLookup[nin->type](nin->data, I);
714
      if (AIR_EXISTS(val)) {
715
        idx = airIndex(min, val, max, bins);
716
        ++hist[idx];
717
        if (AIR_EXISTS(last[idx])) {
718
          steady[0 + 2*idx] = (last[idx] == val
719
                               ? 1 + steady[0 + 2*idx]
720
                               : 0);
721
        }
722
        last[idx] = val;
723
      }
724
    }
725
    /* now sort the steady array */
726
    qsort(steady, bins, 2*sizeof(unsigned int), _nrrdHistoEqCompare);
727
    /* we ignore some of the bins according to "smart" arg */
728
    for (bii=0; bii<smart; bii++) {
729
      respect[steady[1+2*bii]] = 0;
730
      /* printf("%s: disrespecting bin %d\n", me, steady[1+2*bii]); */
731
    }
732
  }
733
  if (nrrdMaybeAlloc_va(nmap=nrrdNew(), nrrdTypeDouble, 1,
734
                        AIR_CAST(size_t, bins+1))) {
735
    biffAddf(NRRD, "%s: failed to create map nrrd", me);
736
    airMopError(mop); return 1;
737
  }
738
  airMopAdd(mop, nmap, (airMopper)nrrdNuke,
739
            nmapP ? airMopOnError : airMopAlways);
740
  ycoord = AIR_CAST(double*, nmap->data);
741
  nmap->axis[0].min = min;
742
  nmap->axis[0].max = max;
743
744
  /* integrate the histogram then normalize it */
745
  for (bii=0; bii<=bins; bii++) {
746
    if (bii == 0) {
747
      ycoord[bii] = 0;
748
    } else {
749
      ycoord[bii] = ycoord[bii-1] + hist[bii-1]*(smart
750
                                                 ? respect[bii-1]
751
                                                 : 1);
752
    }
753
  }
754
  /* if we've done smart, the integral will have little flat spots
755
     where we ignored hits in the histogram.  That means the mapping
756
     will actually be lower at that value than it should be.  In
757
     truth, we should be using an irregular mapping for this, and the
758
     control points at the ignored bins should just be missing.  So we
759
     have to do this silliness to raise those control points in the
760
     regular map. */
761
  if (smart) {
762
    /* there are bins+1 control points, with indices 0 to bins.
763
       We'll fix control points 1 to bins-1.  ycoord[bii] is too low
764
       if hist[bii-1] was not respected (!respect[bii-1]) */
765
    for (bii=1; bii<=bins-1; bii++) {
766
      if (!respect[bii-1]) {
767
        /* lort and hirt will bracket the index of the bad control point
768
           with points corresponding either to respected bins or the
769
           endpoints of the histogram */
770
        for (lort=bii; lort>=1 && !respect[lort-1]; lort--)
771
          ;
772
        for (hirt=bii; hirt<bins && !respect[hirt-1]; hirt++)
773
          ;
774
        ycoord[bii] = AIR_AFFINE(lort, bii, hirt,
775
                                 ycoord[lort], ycoord[hirt]);
776
      }
777
    }
778
    /* the very last control point has to be handled differently */
779
    if (!respect[bins-1]) {
780
      ycoord[bins] += ycoord[bins-1] - ycoord[bins-2];
781
    }
782
  }
783
  /* rescale the histogram integration to span the original
784
     value range, and affect the influence of "amount" */
785
  for (bii=0; bii<=bins; bii++) {
786
    ycoord[bii] = AIR_AFFINE(0.0, ycoord[bii], ycoord[bins], min, max);
787
    ycoord[bii] = AIR_AFFINE(0.0, amount, 1.0,
788
                             AIR_AFFINE(0, bii, bins, min, max),
789
                             ycoord[bii]);
790
  }
791
792
  /* map the nrrd values through the normalized histogram integral */
793
  if (nrrdApply1DRegMap(nout, nin, NULL, nmap, nin->type, AIR_FALSE)) {
794
    biffAddf(NRRD, "%s: problem remapping", me);
795
    airMopError(mop); return 1;
796
  }
797
  /*
798
  for (I=0; I<num; I++) {
799
    val = nrrdDLookup[nin->type](nin->data, I);
800
    if (AIR_EXISTS(val)) {
801
      AIR_INDEX(min, val, max, bins, idx);
802
      val = AIR_AFFINE(xcoord[idx], val, xcoord[idx+1],
803
                       ycoord[idx], ycoord[idx+1]);
804
    }
805
    nrrdDInsert[nout->type](nout->data, I, val);
806
  }
807
  */
808
809
  /* if user is interested, set pointer to map nrrd,
810
     otherwise it will be nixed by airMop */
811
  if (nmapP) {
812
    *nmapP = nmap;
813
  }
814
815
  /* fiddling with content is the only thing we'll do */
816
  if (nrrdContentSet_va(nout, func, nin, "%d,%d", bins, smart)) {
817
    biffAddf(NRRD, "%s:", me);
818
    airMopError(mop); return 1;
819
  }
820
  if (nrrdBasicInfoCopy(nout, nin,
821
                        NRRD_BASIC_INFO_DATA_BIT
822
                        | NRRD_BASIC_INFO_TYPE_BIT
823
                        | NRRD_BASIC_INFO_BLOCKSIZE_BIT
824
                        | NRRD_BASIC_INFO_DIMENSION_BIT
825
                        | NRRD_BASIC_INFO_CONTENT_BIT
826
                        | NRRD_BASIC_INFO_COMMENTS_BIT
827
                        | (nrrdStateKeyValuePairsPropagate
828
                           ? 0
829
                           : NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT))) {
830
    biffAddf(NRRD, "%s:", me);
831
    airMopError(mop); return 1;
832
  }
833
834
  airMopOkay(mop);
835
  return 0;
836
}