Isis 3 Programmer Reference
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 #include "SpecialPixel.h"
32 
33 #include <iostream>
34 #include <math.h>
35 #include <stdio.h>
36 #include <string>
37 
38 using namespace std;
39 
40 namespace Isis {
41 
50  Histogram::Histogram(double minimum, double maximum, int nbins) {
51 
52  SetValidRange(minimum, maximum);
53  //SetBinRange(minimum, maximum);
54  SetBins(nbins);
55  }
56 
57 
79  Histogram::Histogram(Cube &cube, int statsBand, Progress *progress,
80  double startSample, double startLine,
81  double endSample, double endLine,
82  int bins, bool addCubeData) {
83 
84  InitializeFromCube(cube, statsBand, progress, bins, startSample, startLine,
85  endSample, endLine);
86 
87  if (addCubeData) {
88  Brick cubeDataBrick((int)(endSample - startSample + 1),
89  1, 1, cube.pixelType());
90 
91  // if band == 0, then we're gathering data for all bands.
92  int startBand = statsBand;
93  int endBand = statsBand;
94 
95  if (statsBand == 0) {
96  startBand = 1;
97  endBand = cube.bandCount();
98  }
99 
100  if (progress != NULL) {
101  progress->SetText("Gathering histogram");
102  progress->SetMaximumSteps(
103  (int)(endLine - startLine + 1) * (int)(endBand - startBand + 1));
104  progress->CheckStatus();
105  }
106 
107  for (int band = startBand; band <= endBand; band++) {
108  for (int line = (int)startLine; line <= endLine; line++) {
109  cubeDataBrick.SetBasePosition(qRound(startSample), line, band);
110  cube.read(cubeDataBrick);
111  AddData(cubeDataBrick.DoubleBuffer(), cubeDataBrick.size());
112  if (progress != NULL) {
113  progress->CheckStatus();
114  }
115  }
116  }
117  }
118  }
119 
120 
130  Histogram::Histogram(ControlNet &net, double(ControlMeasure::*statFunc)() const, int bins) {
131 
132  //check to make sure we have a reasonable number of bins
133  if (bins < 1) {
134  string msg = "The number of Histogram Bins must be greater than 0";
135  throw IException(IException::Programmer, msg, _FILEINFO_);
136  }
137  SetBins(bins);
138 
139  //set the ranges
140  rangesFromNet(net,statFunc);
141 
142  //add all the data to the now setup histogram
143  addMeasureDataFromNet(net,statFunc);
144  }
145 
146 
156  Histogram::Histogram(ControlNet &net, double(ControlMeasure::*statFunc)() const,
157  double binWidth) {
158 
159  //check to make sure we have a reasonable number of bins
160  if (binWidth <= 0 ) {
161  string msg = "The width of Histogram Bins must be greater than 0";
162  throw IException(IException::Programmer, msg, _FILEINFO_);
163  }
164 
165  //get the range of the data
166 
167 
168  rangesFromNet(net,statFunc);
169 
170 
171  //stretch the domain so that it is an even multiple of binWidth
172  //for some reason Histogram makes the end points of the bin range be at the center of
173  //bins. Thus the +/-0.5 forces it to point the bin range at the ends of the bins.
174  //SetBinRange(binWidth*( floor(this->ValidMinimum()/binWidth )+0.5),
175  // binWidth*(ceil( this->ValidMaximum()/binWidth )-0.5) );
176 
177 
178  //Keep an eye on this to see if it breaks anything. Also, I need to create
179  //a dataset to give this constructor for the unit test.
180 
181  //tjw: SetValidRange is moved into SetBinRange
182  //SetValidRange(binWidth*floor(this->ValidMinimum()/binWidth),
183  // binWidth*ceil(this->ValidMaximum()/binWidth));
184 
185  //from the domain of the data and the requested bin width calculate the number of bins
186  double domain = this->ValidMaximum() - this->ValidMinimum();
187  int nBins = int ( ceil(domain/binWidth) );
188  SetBins(nBins);
189 
190  //add all the data to the now setup histogram
191  addMeasureDataFromNet(net,statFunc);
192  }
193 
194 
202  void Histogram::addMeasureDataFromNet(ControlNet &net,
203  double(ControlMeasure::*statFunc)() const) {
204 
205  //get the number of object points
206  int nObjPts = net.GetNumPoints();
207  for (int i=0; i<nObjPts; i++) { //for each Object point
208  const ControlPoint *point = net.GetPoint(i);
209  if (point->IsIgnored() ) continue; //if the point is ignored then continue
210 
211  //get the number of measures
212  int nObs = point->GetNumMeasures();
213  for (int j=0; j<nObs; j++) { //for every measure
214 
215  const ControlMeasure *measure = point->GetMeasure(j);
216 
217  if (measure->IsIgnored()) continue;
218 
219  this->AddData((measure->*statFunc)());
220  }
221  }
222  }
223 
224 
233  void Histogram::rangesFromNet(ControlNet &net, double(ControlMeasure::*statFunc)() const) {
234  double min,max,temp;
235  min = DBL_MAX;
236  max = -DBL_MAX;
237 
238  //get the number of object points
239  int nObjPts = net.GetNumPoints();
240  for (int i=0; i<nObjPts; i++) { //for each Object point
241 
242  const ControlPoint *point = net.GetPoint(i);
243  if (point->IsIgnored()) continue; //if the point is ignored then continue
244 
245  //get the number of measures
246  int nObs = point->GetNumMeasures();
247 
248  for (int j=0; j<nObs; j++) { //for every measure
249 
250  const ControlMeasure *measure = point->GetMeasure(j);
251  if (measure->IsIgnored()) continue;
252 
253  temp = (measure->*statFunc)(); //get the data using the passed ControlMeasure acessor function pointer
254  if ( !IsSpecial(temp) && temp > max ) max = temp;
255  if ( !IsSpecial(temp) && temp < min ) min = temp;
256  }
257  }
258 
259  //if DBL_MAX's weren't changed there's a problem
260  if (max <= min) {
261  string msg = "The net file appears to have 1 or fewer measures with residual data, "
262  "thus no histogram for this net file can be created;";
263  throw IException(IException::User, msg, _FILEINFO_);
264  }
265 
266  //set up the histogram ranges
267 
268  SetValidRange(min, max);
269  //SetBinRange(min, max);
270  }
271 
272 
273  void Histogram::InitializeFromCube(Cube &cube, int statsBand,
274  Progress *progress, int nbins, double startSample, double startLine,
275  double endSample, double endLine) {
276 
277  // Make sure band is valid, 0 is valid (means all bands)
278  if ( (statsBand < 0) || (statsBand > cube.bandCount() ) ) {
279  string msg = "Cannot gather histogram for band [" + IString(statsBand) +
280  "]";
281  throw IException(IException::Programmer, msg, _FILEINFO_);
282  }
283 
284  // We need to find the min/max DN value for our histogram bins to be the
285  // correct size.
286  double minDnValue = Null;
287  double maxDnValue = Null;
288 
289  if (cube.pixelType() == UnsignedByte) {
290  // If we can discretely store every data point, then we can use the
291  // possible extent of the data range as our min/max dn values.
292  if (nbins == 0) {
293  minDnValue = 0.0 * cube.multiplier() + cube.base();
294  maxDnValue = 255.0 * cube.multiplier() + cube.base();
295  nbins = 256;
296  }
297  }
298  else if (cube.pixelType() == UnsignedWord) {
299  if (nbins == 0) {
300  minDnValue = 0.0 * cube.multiplier() + cube.base();
301  maxDnValue = 65535.0 * cube.multiplier() + cube.base();
302  nbins = 65536;
303  }
304  }
305  else if (cube.pixelType() == SignedWord) {
306  if (nbins == 0) {
307  minDnValue = -32768.0 * cube.multiplier() + cube.base();
308  maxDnValue = 32767.0 * cube.multiplier() + cube.base();
309  nbins = 65536;
310  }
311  }
312  // 32-bit data covers too big of a range of values to use
313  // the min and max possible values to set our value range.
314  // So, just set the number of bins and then later we will
315  // compute the min and max value in the actual cube.
316  else if (cube.pixelType() == UnsignedInteger ||
317  cube.pixelType() == SignedInteger ||
318  cube.pixelType() == Real) {
319  if (nbins == 0) {
320  nbins = 65536;
321  }
322  }
323  else {
324  IString msg = "Unsupported pixel type";
325  throw IException(IException::Programmer, msg, _FILEINFO_);
326  }
327 
328  if (startSample == Null)
329  startSample = 1.0;
330 
331  if (endSample == Null)
332  endSample = cube.sampleCount();
333 
334  if (startLine == Null)
335  startLine = 1.0;
336 
337  if (endLine == Null)
338  endLine = cube.lineCount();
339 
340  // If we still need our min/max DN values, find them.
341  if (minDnValue == Null || maxDnValue == Null) {
342 
343  Brick cubeDataBrick((int)(endSample - startSample + 1),
344  1, 1, cube.pixelType() );
345  Statistics stats;
346 
347  // if band == 0, then we're gathering stats for all bands. I'm really
348  // not sure that this is correct, a good idea or called from anywhere...
349  // but I don't have time to track down the use case.
350  int startBand = statsBand;
351  int endBand = statsBand;
352 
353  if (statsBand == 0) {
354  startBand = 1;
355  endBand = cube.bandCount();
356  }
357 
358  if (progress != NULL) {
359 
360  progress->SetText("Computing min/max for histogram");
361  progress->SetMaximumSteps(
362  (int)(endLine - startLine + 1) * (int)(endBand - startBand + 1) );
363  progress->CheckStatus();
364  }
365 
366  for (int band = startBand; band <= endBand; band++) {
367  for (int line = (int)startLine; line <= endLine; line++) {
368 
369  cubeDataBrick.SetBasePosition(qRound(startSample), line, band);
370  cube.read(cubeDataBrick);
371  stats.AddData(cubeDataBrick.DoubleBuffer(), cubeDataBrick.size());
372 
373  if (progress != NULL) {
374  progress->CheckStatus();
375  }
376  }
377  }
378 
379  if (stats.ValidPixels() == 0) {
380  minDnValue = 0.0;
381  maxDnValue = 1.0;
382  }
383  else {
384  minDnValue = stats.Minimum();
385  maxDnValue = stats.Maximum();
386  }
387  }
388 
389  // Set the bins and range
390  SetValidRange(minDnValue, maxDnValue);
391  SetBins(nbins);
392  }
393 
394 
396  Histogram::~Histogram() {
397  }
398 
399  //2015-08-24, Tyler Wilson: Added Statistics::SetValidRange call to SetBinRange
400  //So the two functions do not have to be called together when setting
401  //up a histogram
402 
413  void Histogram::SetValidRange(double binStart, double binEnd) {
414  if (binEnd < binStart) {
415  string msg = "The binning range start [" + IString(binStart) + "]"
416  " must be less than the end [" + IString(binEnd) + "].";
417  throw IException(IException::Programmer, msg, _FILEINFO_);
418  }
419 
420  //(tjw): Flush the data buffers. Since we are setting
421  //the statistical range for the data, any data loaded
422  //before this call is useless.
423  Reset();
424  Isis::Statistics::SetValidRange(binStart,binEnd);
425  p_binRangeStart = binStart;
426  p_binRangeEnd = binEnd;
427  }
428 
429 
431  void Histogram::Reset() {
432  Statistics::Reset();
433  for (int i = 0; i < (int)p_bins.size(); i++) {
434  p_bins[i] = 0;
435  }
436  }
437 
438 
440  void Histogram::SetBins(const int nbins) {
441  p_bins.resize(nbins);
442  Histogram::Reset();
443  }
444 
453  void Histogram::AddData(const double *data,
454  const unsigned int count) {
455  Statistics::AddData(data, count);
456 
457  int nbins = p_bins.size();
458  int index;
459  for (unsigned int i = 0; i < count; i++) {
460  if (IsValidPixel(data[i]) && InRange(data[i]) ) {
461  if (BinRangeStart() == BinRangeEnd() ) {
462  index = 0;
463  }
464  else {
465  index = (int) floor((double)(nbins - 1) / (BinRangeEnd() - BinRangeStart()) *
466  (data[i] - BinRangeStart() ) + 0.5);
467  }
468  if (index < 0) index = 0;
469  if (index >= nbins) index = nbins - 1;
470  p_bins[index] += 1;
471  }
472  }
473  }
474 
475 
483  void Histogram::AddData(const double data) {
484  Statistics::AddData(data);
485 
486  int nbins = p_bins.size();
487  int index;
488  if (IsValidPixel(data) && InRange(data) ) {
489  if (BinRangeStart() == BinRangeEnd() ) {
490  index = 0;
491  }
492  else {
493  index = (int) floor((double)(nbins - 1) / (BinRangeEnd() - BinRangeStart() ) *
494  (data - BinRangeStart() ) + 0.5);
495  }
496  if (index < 0) index = 0;
497  if (index >= nbins) index = nbins - 1;
498  p_bins[index] += 1;
499  }
500  }
501 
502 
512  void Histogram::RemoveData(const double *data,
513  const unsigned int count) {
514  Statistics::RemoveData(data, count);
515 
516  int nbins = p_bins.size();
517  int index;
518  for (unsigned int i = 0; i < count; i++) {
519  if (IsValidPixel(data[i]) ) {
520 
521  if (BinRangeStart() == BinRangeEnd() ) {
522  index = 0;
523  }
524  else {
525  index = (int) floor((double)(nbins - 1) / (BinRangeEnd() - BinRangeStart()) *
526  (data[i] - BinRangeStart()) + 0.5);
527  }
528  if (index < 0) index = 0;
529  if (index >= nbins) index = nbins - 1;
530  p_bins[index] -= 1;
531  }
532  }
533  }
534 
540  double Histogram::Median() const {
541  return Percent(50.0);
542  }
543 
549  double Histogram::Mode() const {
550  int mode = 0;
551  for (int i = 0; i < (int)p_bins.size(); i++) {
552 
553  if (p_bins[i] > p_bins[mode]) mode = i;
554  }
555 
556  if (p_bins[mode] < 1) return NULL8;
557 
558  return BinMiddle(mode);
559  }
560 
561 
572  double Histogram::Percent(double percent) const {
573  if ( (percent < 0.0) || (percent > 100.0) ) {
574  string m = "Argument percent outside of the range 0 to 100 in";
575  m += " [Histogram::Percent]";
576  throw IException(IException::Programmer, m, _FILEINFO_);
577  }
578 
579  if (ValidPixels() < 1) return NULL8;
580 
581  BigInt currentPixels = 0;
582  double currentPercent;
583 
584  for (int i = 0; i < (int)p_bins.size(); i++) {
585 
586  currentPixels += p_bins[i];
587  currentPercent = (double) currentPixels / (double) ValidPixels() * 100.0;
588 
589  if (currentPercent >= percent) {
590  return BinMiddle(i);
591  }
592  }
593 
594  return BinMiddle( (int)p_bins.size() - 1);
595  }
596 
597 
605  double Histogram::Skew() const {
606 
607  if (ValidPixels() < 1) return NULL8;
608 
609  double sdev = StandardDeviation();
610 
611  if (sdev == 0.0) return 0.0;
612 
613  return 3.0 * (Average() - Median()) / sdev;
614  }
615 
616 
624  BigInt Histogram::BinCount(const int index) const {
625 
626  if ( (index < 0) || (index >= (int)p_bins.size() ) ) {
627 
628  QString message = Message::ArraySubscriptNotInRange(index);
629 
630  throw IException(IException::Programmer, message, _FILEINFO_);
631  }
632 
633  return p_bins[index];
634  }
635 
636 
648  void Histogram::BinRange(const int index,
649  double &low, double &high) const {
650  if ( (index < 0) || (index >= (int)p_bins.size() ) ) {
651 
652  QString message = Message::ArraySubscriptNotInRange(index);
653  throw IException(IException::Programmer, message, _FILEINFO_);
654  }
655 
656  double binSize = (BinRangeEnd() - BinRangeStart()) / (double)(p_bins.size() - 1);
657  low = BinRangeStart() - binSize / 2.0 + binSize * (double) index;
658  high = low + binSize;
659  }
660 
661 
670  double Histogram::BinMiddle(const int index) const {
671  if ( (index < 0) || (index >= (int)p_bins.size() ) ) {
672 
673  QString message = Message::ArraySubscriptNotInRange(index);
674 
675  throw IException(IException::Programmer, message, _FILEINFO_);
676  }
677 
678  double low, high;
679  BinRange(index, low, high);
680  return (low + high) / 2.0;
681  }
682 
683 
691  double Histogram::BinSize() const {
692 
693  double low, high;
694  BinRange(0, low, high);
695  return high - low;
696  }
697 
698 
704  int Histogram::Bins() const {
705  return (int)p_bins.size();
706  }
707 
708 
714  BigInt Histogram::MaxBinCount() const {
715 
716  BigInt maxBinCount = 0;
717  for (int i = 0; i < (int)p_bins.size(); i++) {
718  if (p_bins[i] > maxBinCount) maxBinCount = p_bins[i];
719  }
720 
721  return maxBinCount;
722  }
723 }
long long int BigInt
Big int.
Definition: Constants.h:65
const double Null
Value for an Isis Null pixel.
Definition: SpecialPixel.h:110
void SetMaximumSteps(const int steps)
This sets the maximum number of steps in the process.
Definition: Progress.cpp:101
const double ValidMinimum
The minimum valid double value for Isis pixels.
Definition: SpecialPixel.h:102
const ControlMeasure * GetMeasure(QString serialNumber) const
Get a control measure based on its cube&#39;s serial number.
double base() const
Returns the base value for converting 8-bit/16-bit pixels to 32-bit.
Definition: Cube.cpp:1142
int sampleCount() const
Definition: Cube.cpp:1452
Buffer for containing a three dimensional section of an image.
Definition: Brick.h:61
Namespace for the standard library.
bool IsValidPixel(const double d)
Returns if the input pixel is valid.
Definition: SpecialPixel.h:238
void CheckStatus()
Checks and updates the status.
Definition: Progress.cpp:121
int GetNumPoints() const
Return the number of control points in the network.
Program progress reporter.
Definition: Progress.h:58
a control network
Definition: ControlNet.h:271
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:40
void read(Blob &blob) const
This method will read data from the specified Blob object.
Definition: Cube.cpp:724
bool IsSpecial(const double d)
Returns if the input pixel is special.
Definition: SpecialPixel.h:212
A single control point.
Definition: ControlPoint.h:369
PixelType pixelType() const
Definition: Cube.cpp:1403
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:137
int lineCount() const
Definition: Cube.cpp:1379
Isis exception class.
Definition: IException.h:107
Adds specific functionality to C++ strings.
Definition: IString.h:181
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
virtual int bandCount() const
Returns the number of virtual bands for the cube.
Definition: Cube.cpp:1125
a control measurement
Functor for reduce using average functionality.
Definition: Reduce.h:102
double multiplier() const
Returns the multiplier value for converting 8-bit/16-bit pixels to 32-bit.
Definition: Cube.cpp:1393
IO Handler for Isis Cubes.
Definition: Cube.h:170