USGS

Isis 3.0 Object Programmers' Reference

Home

Histogram.cpp
Go to the documentation of this file.
1 
23 #include "Histogram.h"
24 
25 #include <string>
26 #include <iostream>
27 #include <stdio.h>
28 
29 #include "Brick.h"
30 #include "ControlMeasure.h"
31 #include "ControlNet.h"
32 #include "ControlPoint.h"
33 #include "Message.h"
34 #include "LineManager.h"
35 #include <math.h>
36 
37 using namespace std;
38 namespace Isis {
39 
48  Histogram::Histogram(double minimum, double maximum, int nbins) {
49  SetValidRange(minimum, maximum);
50  SetBinRange(minimum, maximum);
51  SetBins(nbins);
52  }
53 
54 
76  Histogram::Histogram(Cube &cube, int statsBand, Progress *progress,
77  double startSample, double startLine,
78  double endSample, double endLine,
79  int bins, bool addCubeData) {
80  InitializeFromCube(cube, statsBand, progress, bins, startSample, startLine,
81  endSample, endLine);
82 
83  if (addCubeData) {
84  Brick cubeDataBrick((int)(endSample - startSample + 1),
85  1, 1, cube.pixelType());
86 
87  // if band == 0, then we're gathering data for all bands.
88  int startBand = statsBand;
89  int endBand = statsBand;
90 
91  if (statsBand == 0) {
92  startBand = 1;
93  endBand = cube.bandCount();
94  }
95 
96  if (progress != NULL) {
97  progress->SetText("Gathering histogram");
98  progress->SetMaximumSteps(
99  (int)(endLine - startLine + 1) * (int)(endBand - startBand + 1));
100  progress->CheckStatus();
101  }
102 
103  for (int band = startBand; band <= endBand; band++) {
104  for (int line = (int)startLine; line <= endLine; line++) {
105  cubeDataBrick.SetBasePosition(qRound(startSample), line, band);
106  cube.read(cubeDataBrick);
107  AddData(cubeDataBrick.DoubleBuffer(), cubeDataBrick.size());
108  if (progress != NULL) {
109  progress->CheckStatus();
110  }
111  }
112  }
113  }
114  }
115 
116 
125  Histogram::Histogram(ControlNet &net, double(ControlMeasure::*statFunc)() const, int bins) {
126 
127  //check to make sure we have a reasonable number of bins
128  if (bins < 1) {
129  string msg = "The number of Histogram Bins must be greater than 0";
130  throw IException(IException::Programmer, msg, _FILEINFO_);
131  }
132  SetBins(bins);
133 
134  //set the ranges
135  rangesFromNet(net,statFunc);
136 
137  //add all the data to the now setup histogram
138  addMeasureDataFromNet(net,statFunc);
139  }
140 
141 
150  Histogram::Histogram(ControlNet &net, double(ControlMeasure::*statFunc)() const, double binWidth) {
151 
152  //check to make sure we have a reasonable number of bins
153  if (binWidth <= 0 ) {
154  string msg = "The width of Histogram Bins must be greater than 0";
155  throw IException(IException::Programmer, msg, _FILEINFO_);
156  }
157 
158  //get the range of the data
159  rangesFromNet(net,statFunc);
160 
161 
162  //stretch the domain so that it is an even multiple of binWidth
163  //for some reason Histogram makes the end points of the bin range be at the center of
164  //bins. Thus the +/-0.5 forces it to point the bin range at the ends of the bins.
165  SetBinRange(binWidth*(floor(this->ValidMinimum()/binWidth)+0.5),
166  binWidth*(ceil(this->ValidMaximum()/binWidth)-0.5));
167  SetValidRange(binWidth*floor(this->ValidMinimum()/binWidth),
168  binWidth*ceil(this->ValidMaximum()/binWidth));
169 
170  //from the domain of the data and the requested bin width calculate the number of bins
171  double domain = this->ValidMaximum() - this->ValidMinimum();
172  int nBins = int ( ceil(domain/binWidth) );
173  SetBins(nBins);
174 
175  //add all the data to the now setup histogram
176  addMeasureDataFromNet(net,statFunc);
177  }
178 
179 
186  void Histogram::addMeasureDataFromNet(ControlNet &net, double(ControlMeasure::*statFunc)() const) {
187  //get the number of object points
188  int nObjPts = net.GetNumPoints();
189  for (int i=0;i<nObjPts;i++) { //for each Object point
190  const ControlPoint *point = net.GetPoint(i);
191  if (point->IsIgnored()) continue; //if the point is ignored then continue
192 
193  //get the number of measures
194  int nObs = point->GetNumMeasures();
195  for (int j=0;j<nObs;j++) { //for every measure
196  const ControlMeasure *measure = point->GetMeasure(j);
197  if (measure->IsIgnored()) continue;
198  this->AddData((measure->*statFunc)());
199  }
200  }
201  }
202 
203 
211  void Histogram::rangesFromNet(ControlNet &net, double(ControlMeasure::*statFunc)() const) {
212  double min,max,temp;
213  min = DBL_MAX;
214  max = -DBL_MAX;
215 
216  //get the number of object points
217  int nObjPts = net.GetNumPoints();
218  for (int i=0;i<nObjPts;i++) { //for each Object point
219  const ControlPoint *point = net.GetPoint(i);
220  if (point->IsIgnored()) continue; //if the point is ignored then continue
221 
222  //get the number of measures
223  int nObs = point->GetNumMeasures();
224  for (int j=0;j<nObs;j++) { //for every measure
225  const ControlMeasure *measure = point->GetMeasure(j);
226  if (measure->IsIgnored()) continue;
227 
228  temp = (measure->*statFunc)(); //get the data using the passed ControlMeasure acessor function pointer
229  if (temp > max) max = temp;
230  if (temp < min) min = temp;
231  }
232  }
233 
234  //if DBL_MAX's weren't changed there's a problem
235  if (max <= min) {
236  string msg = "The net file appears to have 1 or fewer measures with residual data, "
237  "thus no histogram for this net file can be created;";
238  throw IException(IException::User, msg, _FILEINFO_);
239  }
240 
241  //set up the histogram ranges
242  SetValidRange(min, max);
243  SetBinRange(min, max);
244  }
245 
246 
247  void Histogram::InitializeFromCube(Cube &cube, int statsBand,
248  Progress *progress, int nbins, double startSample, double startLine,
249  double endSample, double endLine) {
250  // Make sure band is valid, 0 is valid (means all bands)
251  if ((statsBand < 0) || (statsBand > cube.bandCount())) {
252  string msg = "Cannot gather histogram for band [" + IString(statsBand) +
253  "]";
254  throw IException(IException::Programmer, msg, _FILEINFO_);
255  }
256 
257  // We need to find the min/max DN value for our histogram bins to be the
258  // correct size.
259  double minDnValue = Null;
260  double maxDnValue = Null;
261 
262  if (cube.pixelType() == UnsignedByte) {
263  // If we can discretely store every data point, then we can use the
264  // possible extent of the data range as our min/max dn values.
265  if (nbins == 0) {
266  minDnValue = 0.0 * cube.multiplier() + cube.base();
267  maxDnValue = 255.0 * cube.multiplier() + cube.base();
268  nbins = 256;
269  }
270  }
271  else if (cube.pixelType() == SignedWord) {
272  if (nbins == 0) {
273  minDnValue = -32768.0 * cube.multiplier() + cube.base();
274  maxDnValue = 32767.0 * cube.multiplier() + cube.base();
275  nbins = 65536;
276  }
277  }
278  else if (cube.pixelType() == Real) {
279  // We can't account for all the possibilities of a double inside of any
280  // data range, so don't guess min/max DN values.
281  if (nbins == 0) {
282  nbins = 65536;
283  }
284  }
285  else {
286  IString msg = "Unsupported pixel type";
287  throw IException(IException::Programmer, msg, _FILEINFO_);
288  }
289 
290  if (startSample == Null)
291  startSample = 1.0;
292 
293  if (endSample == Null)
294  endSample = cube.sampleCount();
295 
296  if (startLine == Null)
297  startLine = 1.0;
298 
299  if (endLine == Null)
300  endLine = cube.lineCount();
301 
302  // If we still need our min/max DN values, find them.
303  if (minDnValue == Null || maxDnValue == Null) {
304 
305  Brick cubeDataBrick((int)(endSample - startSample + 1),
306  1, 1, cube.pixelType());
307  Statistics stats;
308 
309  // if band == 0, then we're gathering stats for all bands. I'm really
310  // not sure that this is correct, a good idea or called from anywhere...
311  // but I don't have time to track down the use case.
312  int startBand = statsBand;
313  int endBand = statsBand;
314 
315  if (statsBand == 0) {
316  startBand = 1;
317  endBand = cube.bandCount();
318  }
319 
320  if (progress != NULL) {
321  progress->SetText("Computing min/max for histogram");
322  progress->SetMaximumSteps(
323  (int)(endLine - startLine + 1) * (int)(endBand - startBand + 1));
324  progress->CheckStatus();
325  }
326 
327  for (int band = startBand; band <= endBand; band++) {
328  for (int line = (int)startLine; line <= endLine; line++) {
329  cubeDataBrick.SetBasePosition(qRound(startSample), line, band);
330  cube.read(cubeDataBrick);
331  stats.AddData(cubeDataBrick.DoubleBuffer(), cubeDataBrick.size());
332  if (progress != NULL) {
333  progress->CheckStatus();
334  }
335  }
336  }
337 
338  if (stats.ValidPixels() == 0) {
339  minDnValue = 0.0;
340  maxDnValue = 1.0;
341  }
342  else {
343  minDnValue = stats.Minimum();
344  maxDnValue = stats.Maximum();
345  }
346  }
347 
348  // Set the bins and range
349  SetBinRange(minDnValue, maxDnValue);
350  SetBins(nbins);
351  }
352 
353 
355  Histogram::~Histogram() {
356  }
357 
358  void Histogram::SetBinRange(double binStart, double binEnd) {
359  if (binEnd < binStart) {
360  string msg = "The binning range start [" + IString(binStart) +
361  " must be less than the end [" + IString(binEnd) + ".";
362  throw IException(IException::Programmer, msg, _FILEINFO_);
363  }
364 
365  p_binRangeStart = binStart;
366  p_binRangeEnd = binEnd;
367  }
368 
369 
371  void Histogram::Reset() {
372  Statistics::Reset();
373  for (int i = 0; i < (int)p_bins.size(); i++) {
374  p_bins[i] = 0;
375  }
376  }
377 
378 
380  void Histogram::SetBins(const int nbins) {
381  p_bins.resize(nbins);
382  Histogram::Reset();
383  }
384 
393  void Histogram::AddData(const double *data,
394  const unsigned int count) {
395  Statistics::AddData(data, count);
396 
397  int nbins = p_bins.size();
398  int index;
399  for (unsigned int i = 0; i < count; i++) {
400  if (IsValidPixel(data[i]) && InRange(data[i])) {
401  if (BinRangeStart() == BinRangeEnd()) {
402  index = 0;
403  }
404  else {
405  index = (int) floor((double)(nbins - 1) / (BinRangeEnd() - BinRangeStart()) *
406  (data[i] - BinRangeStart()) + 0.5);
407  }
408  if (index < 0) index = 0;
409  if (index >= nbins) index = nbins - 1;
410  p_bins[index] += 1;
411  }
412  }
413  }
414 
415 
423  void Histogram::AddData(const double data) {
424  Statistics::AddData(data);
425 
426  int nbins = p_bins.size();
427  int index;
428  if (IsValidPixel(data) && InRange(data)) {
429  if (BinRangeStart() == BinRangeEnd()) {
430  index = 0;
431  }
432  else {
433  index = (int) floor((double)(nbins - 1) / (BinRangeEnd() - BinRangeStart()) *
434  (data - BinRangeStart()) + 0.5);
435  }
436  if (index < 0) index = 0;
437  if (index >= nbins) index = nbins - 1;
438  p_bins[index] += 1;
439  }
440  }
441 
442 
452  void Histogram::RemoveData(const double *data,
453  const unsigned int count) {
454  Statistics::RemoveData(data, count);
455 
456  int nbins = p_bins.size();
457  int index;
458  for (unsigned int i = 0; i < count; i++) {
459  if (IsValidPixel(data[i])) {
460  if (BinRangeStart() == BinRangeEnd()) {
461  index = 0;
462  }
463  else {
464  index = (int) floor((double)(nbins - 1) / (BinRangeEnd() - BinRangeStart()) *
465  (data[i] - BinRangeStart()) + 0.5);
466  }
467  if (index < 0) index = 0;
468  if (index >= nbins) index = nbins - 1;
469  p_bins[index] -= 1;
470  }
471  }
472  }
473 
479  double Histogram::Median() const {
480  return Percent(50.0);
481  }
482 
488  double Histogram::Mode() const {
489  int mode = 0;
490  for (int i = 0; i < (int)p_bins.size(); i++) {
491  if (p_bins[i] > p_bins[mode]) mode = i;
492  }
493 
494  if (p_bins[mode] < 1) return NULL8;
495 
496  return BinMiddle(mode);
497  }
498 
499 
510  double Histogram::Percent(double percent) const {
511  if ((percent < 0.0) || (percent > 100.0)) {
512  string m = "Argument percent outside of the range 0 to 100 in";
513  m += " [Histogram::Percent]";
514  throw IException(IException::Programmer, m, _FILEINFO_);
515  }
516 
517  if (ValidPixels() < 1) return NULL8;
518 
519  BigInt currentPixels = 0;
520  double currentPercent;
521 
522  for (int i = 0; i < (int)p_bins.size(); i++) {
523  currentPixels += p_bins[i];
524  currentPercent = (double) currentPixels / (double) ValidPixels() * 100.0;
525  if (currentPercent >= percent) {
526  return BinMiddle(i);
527  }
528  }
529 
530  return BinMiddle((int)p_bins.size() - 1);
531  }
532 
533 
541  double Histogram::Skew() const {
542  if (ValidPixels() < 1) return NULL8;
543  double sdev = StandardDeviation();
544  if (sdev == 0.0) return 0.0;
545  return 3.0 * (Average() - Median()) / sdev;
546  }
547 
548 
556  BigInt Histogram::BinCount(const int index) const {
557  if ((index < 0) || (index >= (int)p_bins.size())) {
558  QString message = Message::ArraySubscriptNotInRange(index);
559  throw IException(IException::Programmer, message, _FILEINFO_);
560  }
561 
562  return p_bins[index];
563  }
564 
565 
577  void Histogram::BinRange(const int index,
578  double &low, double &high) const {
579  if ((index < 0) || (index >= (int)p_bins.size())) {
580  QString message = Message::ArraySubscriptNotInRange(index);
581  throw IException(IException::Programmer, message, _FILEINFO_);
582  }
583 
584  double binSize = (BinRangeEnd() - BinRangeStart()) / (double)(p_bins.size() - 1);
585  low = BinRangeStart() - binSize / 2.0 + binSize * (double) index;
586  high = low + binSize;
587  }
588 
589 
598  double Histogram::BinMiddle(const int index) const {
599  if ((index < 0) || (index >= (int)p_bins.size())) {
600  QString message = Message::ArraySubscriptNotInRange(index);
601  throw IException(IException::Programmer, message, _FILEINFO_);
602  }
603 
604  double low, high;
605  BinRange(index, low, high);
606  return (low + high) / 2.0;
607  }
608 
609 
617  double Histogram::BinSize() const {
618  double low, high;
619  BinRange(0, low, high);
620  return high - low;
621  }
622 
623 
629  int Histogram::Bins() const {
630  return (int)p_bins.size();
631  }
632 
633 
639  BigInt Histogram::MaxBinCount() const {
640 
641  BigInt maxBinCount = 0;
642  for (int i = 0; i < (int)p_bins.size(); i++) {
643  if (p_bins[i] > maxBinCount) maxBinCount = p_bins[i];
644  }
645 
646  return maxBinCount;
647  }
648 }