GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/nrrd/histogram.c Lines: 110 402 27.4 %
Date: 2017-05-26 Branches: 55 254 21.7 %

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
******** nrrdHisto()
29
**
30
** makes a 1D histogram of a given size and type
31
**
32
** pre-NrrdRange policy:
33
** this looks at nin->min and nin->max to see if they are both non-NaN.
34
** If so, it uses these as the range of the histogram, otherwise it
35
** finds the min and max present in the volume.  If nin->min and nin->max
36
** are being used as the histogram range, then values which fall outside
37
** this are ignored (they don't contribute to the histogram).
38
**
39
** post-NrrdRange policy:
40
*/
41
int
42
nrrdHisto(Nrrd *nout, const Nrrd *nin, const NrrdRange *_range,
43
          const Nrrd *nwght, size_t bins, int type) {
44
  static const char me[]="nrrdHisto", func[]="histo";
45
  size_t I, num, idx;
46
  airArray *mop;
47
  NrrdRange *range;
48
  double min, max, eps, val, count, incr, (*lup)(const void *v, size_t I);
49
50
2
  if (!(nin && nout)) {
51
    /* _range and nwght can be NULL */
52
    biffAddf(NRRD, "%s: got NULL pointer", me);
53
    return 1;
54
  }
55
1
  if (nout == nin) {
56
    biffAddf(NRRD, "%s: nout==nin disallowed", me);
57
    return 1;
58
  }
59
1
  if (!(bins > 0)) {
60
    char stmp[AIR_STRLEN_SMALL];
61
    biffAddf(NRRD, "%s: bins value (%s) invalid", me,
62
             airSprintSize_t(stmp, bins));
63
    return 1;
64
  }
65
1
  if (airEnumValCheck(nrrdType, type) || nrrdTypeBlock == type) {
66
    biffAddf(NRRD, "%s: invalid nrrd type %d", me, type);
67
    return 1;
68
  }
69
1
  if (nwght) {
70
    if (nout==nwght) {
71
      biffAddf(NRRD, "%s: nout==nwght disallowed", me);
72
      return 1;
73
    }
74
    if (nrrdTypeBlock == nwght->type) {
75
      biffAddf(NRRD, "%s: nwght type %s invalid", me,
76
               airEnumStr(nrrdType, nrrdTypeBlock));
77
      return 1;
78
    }
79
    if (!nrrdSameSize(nin, nwght, AIR_TRUE)) {
80
      biffAddf(NRRD, "%s: nwght size mismatch with nin", me);
81
      return 1;
82
    }
83
    lup = nrrdDLookup[nwght->type];
84
  } else {
85
    lup = NULL;
86
  }
87
88
1
  if (nrrdMaybeAlloc_va(nout, type, 1, bins)) {
89
    char stmp[AIR_STRLEN_SMALL];
90
    biffAddf(NRRD, "%s: failed to alloc histo array (len %s)", me,
91
             airSprintSize_t(stmp, bins));
92
    return 1;
93
  }
94
1
  mop = airMopNew();
95
  /* nout->axis[0].size set */
96
1
  nout->axis[0].spacing = AIR_NAN;
97
1
  nout->axis[0].thickness = AIR_NAN;
98

2
  if (nout && AIR_EXISTS(nout->axis[0].min) && AIR_EXISTS(nout->axis[0].max)) {
99
    /* HEY: total hack to externally nail down min and max of histogram:
100
       use the min and max already set on axis[0] */
101
    /* HEY: shouldn't this blatent hack be further restricted by also
102
       checking the existence of range->min and range->max ? */
103
    min = nout->axis[0].min;
104
    max = nout->axis[0].max;
105
  } else {
106
1
    if (_range) {
107
      range = nrrdRangeCopy(_range);
108
      nrrdRangeSafeSet(range, nin, nrrdBlind8BitRangeState);
109
    } else {
110
1
      range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeState);
111
    }
112
1
    airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways);
113
1
    min = range->min;
114
1
    max = range->max;
115
1
    nout->axis[0].min = min;
116
1
    nout->axis[0].max = max;
117
  }
118
1
  eps = (min == max ? 1.0 : 0.0);
119
1
  nout->axis[0].center = nrrdCenterCell;
120
  /* nout->axis[0].label set below */
121
122
  /* make histogram */
123
1
  num = nrrdElementNumber(nin);
124
80002
  for (I=0; I<num; I++) {
125
40000
    val = nrrdDLookup[nin->type](nin->data, I);
126
40000
    if (AIR_EXISTS(val)) {
127

80000
      if (val < min || val > max+eps) {
128
        /* value is outside range; ignore it */
129
        continue;
130
      }
131

80000
      if (AIR_IN_CL(min, val, max)) {
132
40000
        idx = airIndex(min, val, max+eps, AIR_CAST(unsigned int, bins));
133
        /*
134
        printf("!%s: %d: index(%g, %g, %g, %d) = %d\n",
135
               me, (int)I, min, val, max, bins, idx);
136
        */
137
        /* count is a double in order to simplify clamping the
138
           hit values to the representable range for nout->type */
139
40000
        count = nrrdDLookup[nout->type](nout->data, idx);
140
80000
        incr = nwght ? lup(nwght->data, I) : 1;
141
40000
        count = nrrdDClamp[nout->type](count + incr);
142
40000
        nrrdDInsert[nout->type](nout->data, idx, count);
143
40000
      }
144
    }
145
  }
146
147
1
  if (nrrdContentSet_va(nout, func, nin, "%d", bins)) {
148
    biffAddf(NRRD, "%s:", me);
149
    airMopError(mop); return 1;
150
  }
151
1
  nout->axis[0].label = (char *)airFree(nout->axis[0].label);
152
1
  nout->axis[0].label = (char *)airStrdup(nout->content);
153
1
  if (!nrrdStateKindNoop) {
154
1
    nout->axis[0].kind = nrrdKindDomain;
155
1
  }
156
157
1
  airMopOkay(mop);
158
1
  return 0;
159
1
}
160
161
int
162
nrrdHistoCheck(const Nrrd *nhist) {
163
  static const char me[]="nrrdHistoCheck";
164
165
2
  if (!nhist) {
166
    biffAddf(NRRD, "%s: got NULL pointer", me);
167
    return 1;
168
  }
169
1
  if (nrrdTypeBlock == nhist->type) {
170
    biffAddf(NRRD, "%s: has non-scalar %s type",
171
             me, airEnumStr(nrrdType, nrrdTypeBlock));
172
    return 1;
173
  }
174
1
  if (nrrdHasNonExist(nhist)) {
175
    biffAddf(NRRD, "%s: has non-existent values", me);
176
    return 1;
177
  }
178
1
  if (1 != nhist->dim) {
179
    biffAddf(NRRD, "%s: dim == %u != 1",
180
             me, nhist->dim);
181
    return 1;
182
  }
183
1
  if (!(nhist->axis[0].size > 1)) {
184
    biffAddf(NRRD, "%s: has single sample along sole axis", me);
185
    return 1;
186
  }
187
188
1
  return 0;
189
1
}
190
191
int
192
nrrdHistoDraw(Nrrd *nout, const Nrrd *nin,
193
              size_t sy, int showLog, double max) {
194
  static const char me[]="nrrdHistoDraw", func[]="dhisto";
195
2
  char cmt[AIR_STRLEN_MED], stmp[AIR_STRLEN_SMALL];
196
1
  unsigned int ki, numticks, *linY, *logY, tick, *ticks;
197
  double hits, maxhits, usemaxhits;
198
  unsigned char *pgmData;
199
  airArray *mop;
200
  int E;
201
  size_t sx, xi, yi, maxhitidx;
202
203
1
  if (!(nin && nout && sy > 0)) {
204
    biffAddf(NRRD, "%s: invalid args", me);
205
    return 1;
206
  }
207
1
  if (nout == nin) {
208
    biffAddf(NRRD, "%s: nout==nin disallowed", me);
209
    return 1;
210
  }
211
1
  if (nrrdHistoCheck(nin)) {
212
    biffAddf(NRRD, "%s: input nrrd not a histogram", me);
213
    return 1;
214
  }
215
1
  sx = nin->axis[0].size;
216
1
  nrrdBasicInfoInit(nout, NRRD_BASIC_INFO_DATA_BIT);
217
1
  if (nrrdMaybeAlloc_va(nout, nrrdTypeUChar, 2, sx, sy)) {
218
    biffAddf(NRRD, "%s: failed to allocate histogram image", me);
219
    return 1;
220
  }
221
  /* perhaps I should be using nrrdAxisInfoCopy */
222
1
  nout->axis[0].spacing = nout->axis[1].spacing = AIR_NAN;
223
1
  nout->axis[0].thickness = nout->axis[1].thickness = AIR_NAN;
224
1
  nout->axis[0].min = nin->axis[0].min;
225
1
  nout->axis[0].max = nin->axis[0].max;
226
1
  nout->axis[0].center = nout->axis[1].center = nrrdCenterCell;
227
1
  nout->axis[0].label = (char *)airStrdup(nin->axis[0].label);
228
1
  nout->axis[1].label = (char *)airFree(nout->axis[1].label);
229
1
  pgmData = (unsigned char *)nout->data;
230
  maxhits = 0.0;
231
  maxhitidx = 0;
232
2002
  for (xi=0; xi<sx; xi++) {
233
1000
    hits = nrrdDLookup[nin->type](nin->data, xi);
234
1000
    if (maxhits < hits) {
235
      maxhits = hits;
236
      maxhitidx = xi;
237
37
    }
238
  }
239
1
  if (AIR_EXISTS(max) && max > 0) {
240
    usemaxhits = max;
241
  } else {
242
    usemaxhits = maxhits;
243
  }
244
1
  nout->axis[1].min = usemaxhits;
245
1
  nout->axis[1].max = 0;
246
1
  numticks = AIR_CAST(unsigned int, log10(usemaxhits + 1));
247
1
  mop = airMopNew();
248
1
  ticks = AIR_CALLOC(numticks, unsigned int);
249
1
  airMopMem(mop, &ticks, airMopAlways);
250
1
  linY = AIR_CALLOC(sx, unsigned int);
251
1
  airMopMem(mop, &linY, airMopAlways);
252
1
  logY = AIR_CALLOC(sx, unsigned int);
253
1
  airMopMem(mop, &logY, airMopAlways);
254
1
  if (!(ticks && linY && logY)) {
255
    biffAddf(NRRD, "%s: failed to allocate temp arrays", me);
256
    airMopError(mop); return 1;
257
  }
258
6
  for (ki=0; ki<numticks; ki++) {
259
4
    ticks[ki] = airIndex(0, log10(pow(10,ki+1) + 1), log10(usemaxhits+1),
260
2
                         AIR_CAST(unsigned int, sy));
261
  }
262
2002
  for (xi=0; xi<sx; xi++) {
263
1000
    hits = nrrdDLookup[nin->type](nin->data, xi);
264
1000
    linY[xi] = airIndex(0, hits, usemaxhits,
265
1000
                        AIR_CAST(unsigned int, sy));
266
1000
    logY[xi] = airIndex(0, log10(hits+1), log10(usemaxhits+1),
267
                        AIR_CAST(unsigned int, sy));
268
    /* printf("%d -> %d,%d", x, linY[x], logY[x]); */
269
  }
270
2002
  for (yi=0; yi<sy; yi++) {
271
    tick = 0;
272
6000
    for (ki=0; ki<numticks; ki++)
273
2000
      tick |= ticks[ki] == yi;
274
2002000
    for (xi=0; xi<sx; xi++) {
275
1000000
      pgmData[xi + sx*(sy-1-yi)] =
276
2000000
        (2 == showLog    /* HACK: draw log curve, but not log tick marks */
277
         ? (yi >= logY[xi]
278
            ? 0                   /* above log curve                     */
279
            : (yi >= linY[xi]
280
               ? 128              /* below log curve, above normal curve */
281
               : 255              /* below log curve, below normal curve */
282
               )
283
            )
284
1000000
         : (!showLog
285
            ? (yi >= linY[xi] ? 0 : 255)
286
1501084
            : (yi >= logY[xi]     /* above log curve                     */
287
501084
               ? (!tick ? 0       /*                    not on tick mark */
288
                  : 255)          /*                    on tick mark     */
289
751284
               : (yi >= linY[xi]  /* below log curve, above normal curve */
290
252368
                  ? (!tick ? 128  /*                    not on tick mark */
291
                     : 0)         /*                    on tick mark     */
292
                  :255            /* below log curve, below normal curve */
293
                  )
294
               )
295
            )
296
         );
297
    }
298
  }
299
  E = 0;
300
1
  sprintf(cmt, "min value: %g\n", nout->axis[0].min);
301
2
  if (!E) E |= nrrdCommentAdd(nout, cmt);
302
1
  sprintf(cmt, "max value: %g\n", nout->axis[0].max);
303
2
  if (!E) E |= nrrdCommentAdd(nout, cmt);
304
1
  sprintf(cmt, "max hits: %g, in bin %s, around value %g\n", maxhits,
305
          airSprintSize_t(stmp, maxhitidx),
306
          nrrdAxisInfoPos(nout, 0, AIR_CAST(double, maxhitidx)));
307
2
  if (!E) E |= nrrdCommentAdd(nout, cmt);
308
2
  if (!E) E |= nrrdContentSet_va(nout, func, nin, "%s",
309
1
                                 airSprintSize_t(stmp, sy));
310
1
  if (E) {
311
    biffAddf(NRRD, "%s:", me);
312
    airMopError(mop); return 1;
313
  }
314
315
  /* bye */
316
1
  airMopOkay(mop);
317
1
  return 0;
318
1
}
319
320
/*
321
******** nrrdHistoAxis
322
**
323
** replace scanlines along one scanline with a histogram of the scanline
324
**
325
** this looks at nin->min and nin->max to see if they are both non-NaN.
326
** If so, it uses these as the range of the histogram, otherwise it
327
** finds the min and max present in the volume
328
**
329
** By its very nature, and by the simplicity of this implemention,
330
** this can be a slow process due to terrible memory locality.  User
331
** may want to permute axes before and after this, but that can be
332
** slow too...
333
*/
334
int
335
nrrdHistoAxis(Nrrd *nout, const Nrrd *nin, const NrrdRange *_range,
336
              unsigned int hax, size_t bins, int type) {
337
  static const char me[]="nrrdHistoAxis", func[]="histax";
338
  int map[NRRD_DIM_MAX];
339
  unsigned int ai, hidx;
340
  size_t szIn[NRRD_DIM_MAX], szOut[NRRD_DIM_MAX], size[NRRD_DIM_MAX],
341
    coordIn[NRRD_DIM_MAX], coordOut[NRRD_DIM_MAX];
342
  size_t I, hI, num;
343
  double val, count;
344
  airArray *mop;
345
  NrrdRange *range;
346
347
  if (!(nin && nout)) {
348
    biffAddf(NRRD, "%s: got NULL pointer", me);
349
    return 1;
350
  }
351
  if (nout == nin) {
352
    biffAddf(NRRD, "%s: nout==nin disallowed", me);
353
    return 1;
354
  }
355
  if (!(bins > 0)) {
356
    char stmp[AIR_STRLEN_SMALL];
357
    biffAddf(NRRD, "%s: bins value (%s) invalid", me,
358
             airSprintSize_t(stmp, bins));
359
    return 1;
360
  }
361
  if (airEnumValCheck(nrrdType, type) || nrrdTypeBlock == type) {
362
    biffAddf(NRRD, "%s: invalid nrrd type %d", me, type);
363
    return 1;
364
  }
365
  if (!( hax <= nin->dim-1 )) {
366
    biffAddf(NRRD, "%s: axis %d is not in range [0,%d]",
367
             me, hax, nin->dim-1);
368
    return 1;
369
  }
370
  mop = airMopNew();
371
  if (_range) {
372
    range = nrrdRangeCopy(_range);
373
    nrrdRangeSafeSet(range, nin, nrrdBlind8BitRangeState);
374
  } else {
375
    range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeState);
376
  }
377
  airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways);
378
  nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size);
379
  size[hax] = bins;
380
  if (nrrdMaybeAlloc_nva(nout, type, nin->dim, size)) {
381
    biffAddf(NRRD, "%s: failed to alloc output nrrd", me);
382
    airMopError(mop); return 1;
383
  }
384
385
  /* copy axis information */
386
  for (ai=0; ai<nin->dim; ai++) {
387
    map[ai] = ai != hax ? (int)ai : -1;
388
  }
389
  nrrdAxisInfoCopy(nout, nin, map, NRRD_AXIS_INFO_NONE);
390
  /* axis hax now has to be set manually */
391
  nout->axis[hax].size = bins;
392
  nout->axis[hax].spacing = AIR_NAN; /* min and max convey the information */
393
  nout->axis[hax].thickness = AIR_NAN;
394
  nout->axis[hax].min = range->min;
395
  nout->axis[hax].max = range->max;
396
  nout->axis[hax].center = nrrdCenterCell;
397
  if (nin->axis[hax].label) {
398
    nout->axis[hax].label = AIR_CALLOC(strlen("histax()")
399
                                       + strlen(nin->axis[hax].label)
400
                                       + 1, char);
401
    if (nout->axis[hax].label) {
402
      sprintf(nout->axis[hax].label, "histax(%s)", nin->axis[hax].label);
403
    } else {
404
      biffAddf(NRRD, "%s: couldn't allocate output label", me);
405
      airMopError(mop); return 1;
406
    }
407
  } else {
408
    nout->axis[hax].label = NULL;
409
  }
410
  if (!nrrdStateKindNoop) {
411
    nout->axis[hax].kind = nrrdKindDomain;
412
  }
413
414
  /* the skinny: we traverse the input samples in linear order, and
415
     increment the bin in the histogram for the scanline we're in.
416
     This is not terribly clever, and the memory locality is a
417
     disaster */
418
  nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, szIn);
419
  nrrdAxisInfoGet_nva(nout, nrrdAxisInfoSize, szOut);
420
  memset(coordIn, 0, NRRD_DIM_MAX*sizeof(size_t));
421
  num = nrrdElementNumber(nin);
422
  for (I=0; I<num; I++) {
423
    /* get input nrrd value and compute its histogram index */
424
    val = nrrdDLookup[nin->type](nin->data, I);
425
    if (AIR_EXISTS(val) && AIR_IN_CL(range->min, val, range->max)) {
426
      hidx = airIndex(range->min, val, range->max, AIR_CAST(unsigned int, bins));
427
      memcpy(coordOut, coordIn, nin->dim*sizeof(size_t));
428
      coordOut[hax] = (unsigned int)hidx;
429
      NRRD_INDEX_GEN(hI, coordOut, szOut, nout->dim);
430
      count = nrrdDLookup[nout->type](nout->data, hI);
431
      count = nrrdDClamp[nout->type](count + 1);
432
      nrrdDInsert[nout->type](nout->data, hI, count);
433
    }
434
    NRRD_COORD_INCR(coordIn, szIn, nin->dim, 0);
435
  }
436
437
  if (nrrdContentSet_va(nout, func, nin, "%d,%d", hax, bins)) {
438
    biffAddf(NRRD, "%s:", me);
439
    airMopError(mop); return 1;
440
  }
441
  nrrdBasicInfoInit(nout, (NRRD_BASIC_INFO_DATA_BIT
442
                           | NRRD_BASIC_INFO_TYPE_BIT
443
                           | NRRD_BASIC_INFO_DIMENSION_BIT
444
                           | 0 /* what? */ ));
445
  airMopOkay(mop);
446
  return 0;
447
}
448
449
int
450
nrrdHistoJoint(Nrrd *nout, const Nrrd *const *nin,
451
               const NrrdRange *const *_range, unsigned int numNin,
452
               const Nrrd *nwght, const size_t *bins,
453
               int type, const int *clamp) {
454
  static const char me[]="nrrdHistoJoint", func[]="jhisto";
455
  int skip, hadContent;
456
  double val, count, incr, (*lup)(const void *v, size_t I),
457
    rmin[NRRD_DIM_MAX], rmax[NRRD_DIM_MAX];
458
  size_t Iin, Iout, numEl, coord[NRRD_DIM_MAX], totalContentStrlen;
459
  airArray *mop;
460
  NrrdRange **range;
461
  unsigned int nii, ai;
462
463
  /* error checking */
464
  /* nwght can be NULL -> weighting is constant 1.0 */
465
  if (!(nout && nin && bins && clamp)) {
466
    biffAddf(NRRD, "%s: got NULL pointer (%p, %p, %p, %p)", me,
467
             AIR_VOIDP(nout), AIR_CVOIDP(nin),
468
             AIR_CVOIDP(bins), AIR_CVOIDP(clamp));
469
    return 1;
470
  }
471
  if (!(numNin >= 1)) {
472
    biffAddf(NRRD, "%s: need numNin >= 1 (not %d)", me, numNin);
473
    return 1;
474
  }
475
  if (numNin > NRRD_DIM_MAX) {
476
    biffAddf(NRRD, "%s: can only deal with up to %d nrrds (not %d)", me,
477
             NRRD_DIM_MAX, numNin);
478
    return 1;
479
  }
480
  for (nii=0; nii<numNin; nii++) {
481
    if (!(nin[nii])) {
482
      biffAddf(NRRD, "%s: input nrrd #%u NULL", me, nii);
483
      return 1;
484
    }
485
    if (nout==nin[nii]) {
486
      biffAddf(NRRD, "%s: nout==nin[%d] disallowed", me, nii);
487
      return 1;
488
    }
489
    if (nrrdTypeBlock == nin[nii]->type) {
490
      biffAddf(NRRD, "%s: nin[%d] type %s invalid", me, nii,
491
               airEnumStr(nrrdType, nrrdTypeBlock));
492
      return 1;
493
    }
494
  }
495
  if (airEnumValCheck(nrrdType, type) || nrrdTypeBlock == type) {
496
    biffAddf(NRRD, "%s: invalid nrrd type %d", me, type);
497
    return 1;
498
  }
499
  mop = airMopNew();
500
  range = AIR_CALLOC(numNin, NrrdRange*);
501
  airMopAdd(mop, range, airFree, airMopAlways);
502
  for (ai=0; ai<numNin; ai++) {
503
    if (_range && _range[ai]) {
504
      range[ai] = nrrdRangeCopy(_range[ai]);
505
      nrrdRangeSafeSet(range[ai], nin[ai], nrrdBlind8BitRangeState);
506
    } else {
507
      range[ai] = nrrdRangeNewSet(nin[ai], nrrdBlind8BitRangeState);
508
    }
509
    airMopAdd(mop, range[ai], (airMopper)nrrdRangeNix, airMopAlways);
510
  }
511
  for (ai=0; ai<numNin; ai++) {
512
    if (!nin[ai]) {
513
      biffAddf(NRRD, "%s: input nrrd[%d] NULL", me, ai);
514
      return 1;
515
    }
516
    if (!(bins[ai] >= 1)) {
517
      char stmp[AIR_STRLEN_SMALL];
518
      biffAddf(NRRD, "%s: need bins[%u] >= 1 (not %s)", me, ai,
519
               airSprintSize_t(stmp, bins[ai]));
520
      return 1;
521
    }
522
    if (ai && !nrrdSameSize(nin[0], nin[ai], AIR_TRUE)) {
523
      biffAddf(NRRD, "%s: nin[0] (n1) mismatch with nin[%u] (n2)", me, ai);
524
      return 1;
525
    }
526
  }
527
528
  /* check nwght */
529
  if (nwght) {
530
    char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
531
    if (nout==nwght) {
532
      biffAddf(NRRD, "%s: nout==nwght disallowed", me);
533
      return 1;
534
    }
535
    if (nrrdTypeBlock == nwght->type) {
536
      biffAddf(NRRD, "%s: nwght type %s invalid", me,
537
               airEnumStr(nrrdType, nrrdTypeBlock));
538
      return 1;
539
    }
540
    if (nrrdElementNumber(nin[0]) != nrrdElementNumber(nwght)) {
541
      biffAddf(NRRD, "%s: element # in nwght %s != nin[0] %s", me,
542
               airSprintSize_t(stmp2, nrrdElementNumber(nin[0])),
543
               airSprintSize_t(stmp1, nrrdElementNumber(nwght)));
544
      return 1;
545
    }
546
    lup = nrrdDLookup[nwght->type];
547
  } else {
548
    lup = NULL;
549
  }
550
551
  /* allocate output nrrd */
552
  if (nrrdMaybeAlloc_nva(nout, type, numNin, bins)) {
553
    biffAddf(NRRD, "%s: couldn't allocate output histogram", me);
554
    return 1;
555
  }
556
  hadContent = 0;
557
  totalContentStrlen = 0;
558
  for (ai=0; ai<numNin; ai++) {
559
    nout->axis[ai].size = bins[ai];
560
    nout->axis[ai].spacing = AIR_NAN;
561
    nout->axis[ai].thickness = AIR_NAN;
562
    nout->axis[ai].min = range[ai]->min;
563
    nout->axis[ai].max = range[ai]->max;
564
    nout->axis[ai].center = nrrdCenterCell;
565
    if (!nrrdStateKindNoop) {
566
      nout->axis[ai].kind = nrrdKindDomain;
567
    }
568
    if (nin[ai]->content) {
569
      hadContent = 1;
570
      totalContentStrlen += strlen(nin[ai]->content);
571
      nout->axis[ai].label = AIR_CALLOC(strlen("histo(,)")
572
                                        + strlen(nin[ai]->content)
573
                                        + 11
574
                                        + 1, char);
575
      if (nout->axis[ai].label) {
576
        char stmp[AIR_STRLEN_SMALL];
577
        sprintf(nout->axis[ai].label, "histo(%s,%s)", nin[ai]->content,
578
                airSprintSize_t(stmp, bins[ai]));
579
      } else {
580
        biffAddf(NRRD, "%s: couldn't allocate output label #%u", me, ai);
581
        return 1;
582
      }
583
    } else {
584
      nout->axis[ai].label = (char *)airFree(nout->axis[ai].label);
585
      totalContentStrlen += 2;
586
    }
587
  }
588
589
  /* the skinny */
590
  for (ai=0; ai<numNin; ai++) {
591
    if (range[ai]->min <= range[ai]->max) {
592
      rmin[ai] = range[ai]->min;
593
      rmax[ai] = range[ai]->max;
594
    } else {
595
      rmin[ai] = range[ai]->max;
596
      rmax[ai] = range[ai]->min;
597
    }
598
  }
599
  numEl = nrrdElementNumber(nin[0]);
600
  for (Iin=0; Iin<numEl; Iin++) {
601
    skip = 0;
602
    for (ai=0; ai<numNin; ai++) {
603
      val = nrrdDLookup[nin[ai]->type](nin[ai]->data, Iin);
604
      if (!AIR_EXISTS(val)) {
605
        /* coordinate d in the joint histo can't be determined
606
           if nin[ai] has a non-existent value here */
607
        skip = 1;
608
        break;
609
      }
610
      if (!AIR_IN_CL(rmin[ai], val, rmax[ai])) {
611
        if (clamp[ai]) {
612
          val = AIR_CLAMP(rmin[ai], val, rmax[ai]);
613
        } else {
614
          skip = 1;
615
          break;
616
        }
617
      }
618
      coord[ai] = AIR_CAST(size_t, airIndexClampULL(range[ai]->min,
619
                                                    val,
620
                                                    range[ai]->max,
621
                                                    bins[ai]));
622
    }
623
    if (skip) {
624
      continue;
625
    }
626
    NRRD_INDEX_GEN(Iout, coord, bins, numNin);
627
    count = nrrdDLookup[nout->type](nout->data, Iout);
628
    incr = nwght ? lup(nwght->data, Iin) : 1.0;
629
    count = nrrdDClamp[nout->type](count + incr);
630
    nrrdDInsert[nout->type](nout->data, Iout, count);
631
  }
632
633
  /* HEY: switch to nrrdContentSet_va? */
634
  if (hadContent) {
635
    nout->content = AIR_CALLOC(strlen(func) + strlen("()")
636
                               + numNin*strlen(",")
637
                               + totalContentStrlen
638
                               + 1, char);
639
    if (nout->content) {
640
      size_t len;
641
      sprintf(nout->content, "%s(", func);
642
      for (ai=0; ai<numNin; ai++) {
643
        len = strlen(nout->content);
644
        strcpy(nout->content + len,
645
               nin[ai]->content ? nin[ai]->content : "?");
646
        len = strlen(nout->content);
647
        nout->content[len] = ai < numNin-1 ? ',' : ')';
648
      }
649
      nout->content[len+1] = '\0';
650
    } else {
651
      biffAddf(NRRD, "%s: couldn't allocate output content", me);
652
      return 1;
653
    }
654
  }
655
656
  airMopOkay(mop);
657
  return 0;
658
}
659
660
/*
661
******** nrrdHistoThresholdOtsu
662
**
663
** does simple Otsu tresholding of a histogram, with a variable exponont.
664
** When "expo" is 2.0, it computes variance; lower values probably represent
665
** greater insensitivities to outliers. Idea from ...
666
*/
667
int
668
nrrdHistoThresholdOtsu(double *threshP, const Nrrd *_nhist, double expo) {
669
  static const char me[]="nrrdHistoThresholdOtsu";
670
  unsigned int histLen, histIdx, maxIdx;
671
  Nrrd *nhist, *nbvar;
672
  double *hist, *bvar, thresh, num0, num1, mean0, mean1,
673
    onum0, onum1, omean0, omean1, max;
674
  airArray *mop;
675
676
  if (!(threshP && _nhist)) {
677
    biffAddf(NRRD, "%s: got NULL pointer", me);
678
    return 1;
679
  }
680
  if (nrrdHistoCheck(_nhist)) {
681
    biffAddf(NRRD, "%s: input nrrd not a histogram", me);
682
    return 1;
683
  }
684
685
  mop = airMopNew();
686
  airMopAdd(mop, nhist = nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
687
  airMopAdd(mop, nbvar = nrrdNew(), (airMopper)nrrdNuke, airMopAlways);
688
  if (nrrdConvert(nhist, _nhist, nrrdTypeDouble)
689
      || nrrdCopy(nbvar, nhist)) {
690
    biffAddf(NRRD, "%s: making local copies", me);
691
    airMopError(mop); return 1;
692
  }
693
  hist = AIR_CAST(double*, nhist->data);
694
  bvar = AIR_CAST(double*, nbvar->data);
695
696
  histLen = AIR_CAST(unsigned int, nhist->axis[0].size);
697
  num1 = mean1 = 0;
698
  for (histIdx=0; histIdx<histLen; histIdx++) {
699
    num1 += hist[histIdx];
700
    mean1 += hist[histIdx]*histIdx;
701
  }
702
  if (num1) {
703
    num0 = 0;
704
    mean0 = 0;
705
    mean1 /= num1;
706
    for (histIdx=0; histIdx<histLen; histIdx++) {
707
      if (histIdx) {
708
        onum0 = num0;
709
        onum1 = num1;
710
        omean0 = mean0;
711
        omean1 = mean1;
712
        num0 = onum0 + hist[histIdx-1];
713
        num1 = onum1 - hist[histIdx-1];
714
        mean0 = (omean0*onum0 + hist[histIdx-1]*(histIdx-1)) / num0;
715
        mean1 = (omean1*onum1 - hist[histIdx-1]*(histIdx-1)) / num1;
716
      }
717
      bvar[histIdx] = num0*num1*airSgnPow(mean1 - mean0, expo);
718
    }
719
    max = bvar[0];
720
    maxIdx = 0;
721
    for (histIdx=1; histIdx<histLen; histIdx++) {
722
      if (bvar[histIdx] > max) {
723
        max = bvar[histIdx];
724
        maxIdx = histIdx;
725
      }
726
    }
727
    thresh = maxIdx;
728
  } else {
729
    thresh = histLen/2;
730
  }
731
732
  if (AIR_EXISTS(nhist->axis[0].min) && AIR_EXISTS(nhist->axis[0].max)) {
733
    thresh = NRRD_CELL_POS(nhist->axis[0].min, nhist->axis[0].max,
734
                           histLen, thresh);
735
  }
736
  *threshP = thresh;
737
  airMopOkay(mop);
738
  return 0;
739
}