Isis 3.0 Programmer Reference
Back | Home
Histogram.cpp
Go to the documentation of this file.
1 
23 #include "Histogram.h"
24 
25 #include "Brick.h"
26 #include "ControlMeasure.h"
27 #include "ControlNet.h"
28 #include "ControlPoint.h"
29 #include "LineManager.h"
30 #include "Message.h"
31 
32 #include <iostream>
33 #include <math.h>
34 #include <stdio.h>
35 #include <string>
36 
37 using namespace std;
38 
39 namespace Isis {
40 
49  Histogram::Histogram(double minimum, double maximum, int nbins) {
50 
51  SetValidRange(minimum, maximum);
52  //SetBinRange(minimum, maximum);
53  SetBins(nbins);
54  }
55 
56 
78  Histogram::Histogram(Cube &cube, int statsBand, Progress *progress,
79  double startSample, double startLine,
80  double endSample, double endLine,
81  int bins, bool addCubeData) {
82 
83  InitializeFromCube(cube, statsBand, progress, bins, startSample, startLine,
84  endSample, endLine);
85 
86  if (addCubeData) {
87  Brick cubeDataBrick((int)(endSample - startSample + 1),
88  1, 1, cube.pixelType());
89 
90  // if band == 0, then we're gathering data for all bands.
91  int startBand = statsBand;
92  int endBand = statsBand;
93 
94  if (statsBand == 0) {
95  startBand = 1;
96  endBand = cube.bandCount();
97  }
98 
99  if (progress != NULL) {
100  progress->SetText("Gathering histogram");
101  progress->SetMaximumSteps(
102  (int)(endLine - startLine + 1) * (int)(endBand - startBand + 1));
103  progress->CheckStatus();
104  }
105 
106  for (int band = startBand; band <= endBand; band++) {
107  for (int line = (int)startLine; line <= endLine; line++) {
108  cubeDataBrick.SetBasePosition(qRound(startSample), line, band);
109  cube.read(cubeDataBrick);
110  AddData(cubeDataBrick.DoubleBuffer(), cubeDataBrick.size());
111  if (progress != NULL) {
112  progress->CheckStatus();
113  }
114  }
115  }
116  }
117  }
118 
119 
129  Histogram::Histogram(ControlNet &net, double(ControlMeasure::*statFunc)() const, int bins) {
130 
131  //check to make sure we have a reasonable number of bins
132  if (bins < 1) {
133  string msg = "The number of Histogram Bins must be greater than 0";
134  throw IException(IException::Programmer, msg, _FILEINFO_);
135  }
136  SetBins(bins);
137 
138  //set the ranges
139  rangesFromNet(net,statFunc);
140 
141  //add all the data to the now setup histogram
142  addMeasureDataFromNet(net,statFunc);
143  }
144 
145 
155  Histogram::Histogram(ControlNet &net, double(ControlMeasure::*statFunc)() const,
156  double binWidth) {
157 
158  //check to make sure we have a reasonable number of bins
159  if (binWidth <= 0 ) {
160  string msg = "The width of Histogram Bins must be greater than 0";
161  throw IException(IException::Programmer, msg, _FILEINFO_);
162  }
163 
164  //get the range of the data
165 
166 
167  rangesFromNet(net,statFunc);
168 
169 
170  //stretch the domain so that it is an even multiple of binWidth
171  //for some reason Histogram makes the end points of the bin range be at the center of
172  //bins. Thus the +/-0.5 forces it to point the bin range at the ends of the bins.
173  //SetBinRange(binWidth*( floor(this->ValidMinimum()/binWidth )+0.5),
174  // binWidth*(ceil( this->ValidMaximum()/binWidth )-0.5) );
175 
176 
177  //Keep an eye on this to see if it breaks anything. Also, I need to create
178  //a dataset to give this constructor for the unit test.
179 
180  //tjw: SetValidRange is moved into SetBinRange
181  //SetValidRange(binWidth*floor(this->ValidMinimum()/binWidth),
182  // binWidth*ceil(this->ValidMaximum()/binWidth));
183 
184  //from the domain of the data and the requested bin width calculate the number of bins
185  double domain = this->ValidMaximum() - this->ValidMinimum();
186  int nBins = int ( ceil(domain/binWidth) );
187  SetBins(nBins);
188 
189  //add all the data to the now setup histogram
190  addMeasureDataFromNet(net,statFunc);
191  }
192 
193 
201  void Histogram::addMeasureDataFromNet(ControlNet &net,
202  double(ControlMeasure::*statFunc)() const) {
203 
204  //get the number of object points
205  int nObjPts = net.GetNumPoints();
206  for (int i=0; i<nObjPts; i++) { //for each Object point
207  const ControlPoint *point = net.GetPoint(i);
208  if (point->IsIgnored() ) continue; //if the point is ignored then continue
209 
210  //get the number of measures
211  int nObs = point->GetNumMeasures();
212  for (int j=0; j<nObs; j++) { //for every measure
213 
214  const ControlMeasure *measure = point->GetMeasure(j);
215 
216  if (measure->IsIgnored()) continue;
217 
218  this->AddData((measure->*statFunc)());
219  }
220  }
221  }
222 
223 
232  void Histogram::rangesFromNet(ControlNet &net, double(ControlMeasure::*statFunc)() const) {
233  double min,max,temp;
234  min = DBL_MAX;
235  max = -DBL_MAX;
236 
237  //get the number of object points
238  int nObjPts = net.GetNumPoints();
239  for (int i=0; i<nObjPts; i++) { //for each Object point
240 
241  const ControlPoint *point = net.GetPoint(i);
242  if (point->IsIgnored()) continue; //if the point is ignored then continue
243 
244  //get the number of measures
245  int nObs = point->GetNumMeasures();
246 
247  for (int j=0; j<nObs; j++) { //for every measure
248 
249  const ControlMeasure *measure = point->GetMeasure(j);
250  if (measure->IsIgnored()) continue;
251 
252  temp = (measure->*statFunc)(); //get the data using the passed ControlMeasure acessor function pointer
253  if (temp > max) max = temp;
254  if (temp < min) min = temp;
255  }
256  }
257 
258  //if DBL_MAX's weren't changed there's a problem
259  if (max <= min) {
260  string msg = "The net file appears to have 1 or fewer measures with residual data, "
261  "thus no histogram for this net file can be created;";
262  throw IException(IException::User, msg, _FILEINFO_);
263  }
264 
265  //set up the histogram ranges
266 
267  SetValidRange(min, max);
268  //SetBinRange(min, max);
269  }
270 
271 
272  void Histogram::InitializeFromCube(Cube &cube, int statsBand,
273  Progress *progress, int nbins, double startSample, double startLine,
274  double endSample, double endLine) {
275 
276  // Make sure band is valid, 0 is valid (means all bands)
277  if ( (statsBand < 0) || (statsBand > cube.bandCount() ) ) {
278  string msg = "Cannot gather histogram for band [" + IString(statsBand) +
279  "]";
280  throw IException(IException::Programmer, msg, _FILEINFO_);
281  }
282 
283  // We need to find the min/max DN value for our histogram bins to be the
284  // correct size.
285  double minDnValue = Null;
286  double maxDnValue = Null;
287 
288  if (cube.pixelType() == UnsignedByte) {
289  // If we can discretely store every data point, then we can use the
290  // possible extent of the data range as our min/max dn values.
291  if (nbins == 0) {
292  minDnValue = 0.0 * cube.multiplier() + cube.base();
293  maxDnValue = 255.0 * cube.multiplier() + cube.base();
294  nbins = 256;
295  }
296  }
297  else if (cube.pixelType() == UnsignedWord) {
298  if (nbins == 0) {
299  minDnValue = 0.0 * cube.multiplier() + cube.base();
300  maxDnValue = 65535.0 * cube.multiplier() + cube.base();
301  nbins = 65536;
302  }
303  }
304  else if (cube.pixelType() == SignedWord) {
305  if (nbins == 0) {
306  minDnValue = -32768.0 * cube.multiplier() + cube.base();
307  maxDnValue = 32767.0 * cube.multiplier() + cube.base();
308  nbins = 65536;
309  }
310  }
311  else if (cube.pixelType() == Real) {
312  // We can't account for all the possibilities of a double inside of any
313  // data range, so don't guess min/max DN values.
314  if (nbins == 0) {
315  nbins = 65536;
316  }
317  }
318  else {
319  IString msg = "Unsupported pixel type";
320  throw IException(IException::Programmer, msg, _FILEINFO_);
321  }
322 
323  if (startSample == Null)
324  startSample = 1.0;
325 
326  if (endSample == Null)
327  endSample = cube.sampleCount();
328 
329  if (startLine == Null)
330  startLine = 1.0;
331 
332  if (endLine == Null)
333  endLine = cube.lineCount();
334 
335  // If we still need our min/max DN values, find them.
336  if (minDnValue == Null || maxDnValue == Null) {
337 
338  Brick cubeDataBrick((int)(endSample - startSample + 1),
339  1, 1, cube.pixelType() );
340  Statistics stats;
341 
342  // if band == 0, then we're gathering stats for all bands. I'm really
343  // not sure that this is correct, a good idea or called from anywhere...
344  // but I don't have time to track down the use case.
345  int startBand = statsBand;
346  int endBand = statsBand;
347 
348  if (statsBand == 0) {
349  startBand = 1;
350  endBand = cube.bandCount();
351  }
352 
353  if (progress != NULL) {
354 
355  progress->SetText("Computing min/max for histogram");
356  progress->SetMaximumSteps(
357  (int)(endLine - startLine + 1) * (int)(endBand - startBand + 1) );
358  progress->CheckStatus();
359  }
360 
361  for (int band = startBand; band <= endBand; band++) {
362  for (int line = (int)startLine; line <= endLine; line++) {
363 
364  cubeDataBrick.SetBasePosition(qRound(startSample), line, band);
365  cube.read(cubeDataBrick);
366  stats.AddData(cubeDataBrick.DoubleBuffer(), cubeDataBrick.size());
367 
368  if (progress != NULL) {
369  progress->CheckStatus();
370  }
371  }
372  }
373 
374  if (stats.ValidPixels() == 0) {
375  minDnValue = 0.0;
376  maxDnValue = 1.0;
377  }
378  else {
379  minDnValue = stats.Minimum();
380  maxDnValue = stats.Maximum();
381  }
382  }
383 
384  // Set the bins and range
385  SetValidRange(minDnValue, maxDnValue);
386  SetBins(nbins);
387  }
388 
389 
391  Histogram::~Histogram() {
392  }
393 
394  //2015-08-24, Tyler Wilson: Added Statistics::SetValidRange call to SetBinRange
395  //So the two functions do not have to be called together when setting
396  //up a histogram
397 
408  void Histogram::SetValidRange(double binStart, double binEnd) {
409  if (binEnd < binStart) {
410  string msg = "The binning range start [" + IString(binStart) + "]"
411  " must be less than the end [" + IString(binEnd) + "].";
412  throw IException(IException::Programmer, msg, _FILEINFO_);
413  }
414 
415  //(tjw): Flush the data buffers. Since we are setting
416  //the statistical range for the data, any data loaded
417  //before this call is useless.
418  Reset();
419  Isis::Statistics::SetValidRange(binStart,binEnd);
420  p_binRangeStart = binStart;
421  p_binRangeEnd = binEnd;
422  }
423 
424 
426  void Histogram::Reset() {
427  Statistics::Reset();
428  for (int i = 0; i < (int)p_bins.size(); i++) {
429  p_bins[i] = 0;
430  }
431  }
432 
433 
435  void Histogram::SetBins(const int nbins) {
436  p_bins.resize(nbins);
437  Histogram::Reset();
438  }
439 
448  void Histogram::AddData(const double *data,
449  const unsigned int count) {
450  Statistics::AddData(data, count);
451 
452  int nbins = p_bins.size();
453  int index;
454  for (unsigned int i = 0; i < count; i++) {
455  if (IsValidPixel(data[i]) && InRange(data[i]) ) {
456  if (BinRangeStart() == BinRangeEnd() ) {
457  index = 0;
458  }
459  else {
460  index = (int) floor((double)(nbins - 1) / (BinRangeEnd() - BinRangeStart()) *
461  (data[i] - BinRangeStart() ) + 0.5);
462  }
463  if (index < 0) index = 0;
464  if (index >= nbins) index = nbins - 1;
465  p_bins[index] += 1;
466  }
467  }
468  }
469 
470 
478  void Histogram::AddData(const double data) {
479  Statistics::AddData(data);
480 
481  int nbins = p_bins.size();
482  int index;
483  if (IsValidPixel(data) && InRange(data) ) {
484  if (BinRangeStart() == BinRangeEnd() ) {
485  index = 0;
486  }
487  else {
488  index = (int) floor((double)(nbins - 1) / (BinRangeEnd() - BinRangeStart() ) *
489  (data - BinRangeStart() ) + 0.5);
490  }
491  if (index < 0) index = 0;
492  if (index >= nbins) index = nbins - 1;
493  p_bins[index] += 1;
494  }
495  }
496 
497 
507  void Histogram::RemoveData(const double *data,
508  const unsigned int count) {
509  Statistics::RemoveData(data, count);
510 
511  int nbins = p_bins.size();
512  int index;
513  for (unsigned int i = 0; i < count; i++) {
514  if (IsValidPixel(data[i]) ) {
515 
516  if (BinRangeStart() == BinRangeEnd() ) {
517  index = 0;
518  }
519  else {
520  index = (int) floor((double)(nbins - 1) / (BinRangeEnd() - BinRangeStart()) *
521  (data[i] - BinRangeStart()) + 0.5);
522  }
523  if (index < 0) index = 0;
524  if (index >= nbins) index = nbins - 1;
525  p_bins[index] -= 1;
526  }
527  }
528  }
529 
535  double Histogram::Median() const {
536  return Percent(50.0);
537  }
538 
544  double Histogram::Mode() const {
545  int mode = 0;
546  for (int i = 0; i < (int)p_bins.size(); i++) {
547 
548  if (p_bins[i] > p_bins[mode]) mode = i;
549  }
550 
551  if (p_bins[mode] < 1) return NULL8;
552 
553  return BinMiddle(mode);
554  }
555 
556 
567  double Histogram::Percent(double percent) const {
568  if ( (percent < 0.0) || (percent > 100.0) ) {
569  string m = "Argument percent outside of the range 0 to 100 in";
570  m += " [Histogram::Percent]";
571  throw IException(IException::Programmer, m, _FILEINFO_);
572  }
573 
574  if (ValidPixels() < 1) return NULL8;
575 
576  BigInt currentPixels = 0;
577  double currentPercent;
578 
579  for (int i = 0; i < (int)p_bins.size(); i++) {
580 
581  currentPixels += p_bins[i];
582  currentPercent = (double) currentPixels / (double) ValidPixels() * 100.0;
583 
584  if (currentPercent >= percent) {
585  return BinMiddle(i);
586  }
587  }
588 
589  return BinMiddle( (int)p_bins.size() - 1);
590  }
591 
592 
600  double Histogram::Skew() const {
601 
602  if (ValidPixels() < 1) return NULL8;
603 
604  double sdev = StandardDeviation();
605 
606  if (sdev == 0.0) return 0.0;
607 
608  return 3.0 * (Average() - Median()) / sdev;
609  }
610 
611 
619  BigInt Histogram::BinCount(const int index) const {
620 
621  if ( (index < 0) || (index >= (int)p_bins.size() ) ) {
622 
623  QString message = Message::ArraySubscriptNotInRange(index);
624 
625  throw IException(IException::Programmer, message, _FILEINFO_);
626  }
627 
628  return p_bins[index];
629  }
630 
631 
643  void Histogram::BinRange(const int index,
644  double &low, double &high) const {
645  if ( (index < 0) || (index >= (int)p_bins.size() ) ) {
646 
647  QString message = Message::ArraySubscriptNotInRange(index);
648  throw IException(IException::Programmer, message, _FILEINFO_);
649  }
650 
651  double binSize = (BinRangeEnd() - BinRangeStart()) / (double)(p_bins.size() - 1);
652  low = BinRangeStart() - binSize / 2.0 + binSize * (double) index;
653  high = low + binSize;
654  }
655 
656 
665  double Histogram::BinMiddle(const int index) const {
666  if ( (index < 0) || (index >= (int)p_bins.size() ) ) {
667 
668  QString message = Message::ArraySubscriptNotInRange(index);
669 
670  throw IException(IException::Programmer, message, _FILEINFO_);
671  }
672 
673  double low, high;
674  BinRange(index, low, high);
675  return (low + high) / 2.0;
676  }
677 
678 
686  double Histogram::BinSize() const {
687 
688  double low, high;
689  BinRange(0, low, high);
690  return high - low;
691  }
692 
693 
699  int Histogram::Bins() const {
700  return (int)p_bins.size();
701  }
702 
703 
709  BigInt Histogram::MaxBinCount() const {
710 
711  BigInt maxBinCount = 0;
712  for (int i = 0; i < (int)p_bins.size(); i++) {
713  if (p_bins[i] > maxBinCount) maxBinCount = p_bins[i];
714  }
715 
716  return maxBinCount;
717  }
718 }
const double Null
Value for an Isis Null pixel.
Definition: SpecialPixel.h:109
void SetMaximumSteps(const int steps)
This sets the maximum number of steps in the process.
Definition: Progress.cpp:101
PixelType pixelType() const
Definition: Cube.cpp:1355
const double ValidMinimum
The minimum valid double value for Isis pixels.
Definition: SpecialPixel.h:101
double multiplier() const
Returns the multiplier value for converting 8-bit/16-bit pixels to 32-bit.
Definition: Cube.cpp:1345
Buffer for containing a three dimensional section of an image.
Definition: Brick.h:60
int GetNumPoints() const
Return the number of control points in the network.
bool IsValidPixel(const double d)
Returns if the input pixel is valid.
Definition: SpecialPixel.h:225
void read(Blob &blob) const
This method will read data from the specified Blob object.
Definition: Cube.cpp:686
void CheckStatus()
Checks and updates the status.
Definition: Progress.cpp:121
Program progress reporter.
Definition: Progress.h:58
int sampleCount() const
Definition: Cube.cpp:1404
a control network
Definition: ControlNet.h:207
void SetText(const QString &text)
Changes the value of the text string reported just before 0% processed.
Definition: Progress.cpp:77
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:38
int bandCount() const
Returns the number of virtual bands for the cube.
Definition: Cube.cpp:1077
A single control point.
Definition: ControlPoint.h:339
double base() const
Returns the base value for converting 8-bit/16-bit pixels to 32-bit.
Definition: Cube.cpp:1094
QString ArraySubscriptNotInRange(int index)
This error should be used when an Isis object or application is checking array bounds and the legal r...
const double ValidMaximum
The maximum valid double value for Isis pixels.
Definition: SpecialPixel.h:136
Isis exception class.
Definition: IException.h:99
Adds specific functionality to C++ strings.
Definition: IString.h:179
a control measurement
int lineCount() const
Definition: Cube.cpp:1331
const ControlMeasure * GetMeasure(QString serialNumber) const
Get a control measure based on its cube&#39;s serial number.
Functor for reduce using average functionality.
Definition: Reduce.h:102
IO Handler for Isis Cubes.
Definition: Cube.h:158

U.S. Department of the Interior | U.S. Geological Survey
ISIS | Privacy & Disclaimers | Astrogeology Research Program
To contact us, please post comments and questions on the ISIS Support Center
File Modified: 07/12/2023 23:19:38