USGS

Isis 3.0 Object Programmers' Reference

Home

Histogram.cpp

Go to the documentation of this file.
00001 
00023 #include "Histogram.h"
00024 
00025 #include <string>
00026 #include <iostream>
00027 #include <stdio.h>
00028 
00029 #include "Brick.h"
00030 #include "ControlMeasure.h"
00031 #include "ControlNet.h"
00032 #include "ControlPoint.h"
00033 #include "Message.h"
00034 #include "LineManager.h"
00035 #include <math.h>
00036 
00037 using namespace std;
00038 namespace Isis {
00039 
00048   Histogram::Histogram(double minimum, double maximum, int nbins) {
00049     SetValidRange(minimum, maximum);
00050     SetBinRange(minimum, maximum);
00051     SetBins(nbins);
00052   }
00053 
00054 
00076   Histogram::Histogram(Cube &cube, int statsBand, Progress *progress,
00077       double startSample, double startLine,
00078       double endSample, double endLine,
00079       int bins, bool addCubeData) {
00080     InitializeFromCube(cube, statsBand, progress, bins, startSample, startLine,
00081                        endSample, endLine);
00082 
00083     if (addCubeData) {
00084       Brick cubeDataBrick((int)(endSample - startSample + 1),
00085                           1, 1, cube.pixelType());
00086 
00087       // if band == 0, then we're gathering data for all bands.
00088       int startBand = statsBand;
00089       int endBand = statsBand;
00090 
00091       if (statsBand == 0) {
00092         startBand = 1;
00093         endBand = cube.bandCount();
00094       }
00095 
00096       if (progress != NULL) {
00097         progress->SetText("Gathering histogram");
00098         progress->SetMaximumSteps(
00099           (int)(endLine - startLine + 1) * (int)(endBand - startBand + 1));
00100         progress->CheckStatus();
00101       }
00102 
00103       for (int band = startBand; band <= endBand; band++) {
00104         for (int line = (int)startLine; line <= endLine; line++) {
00105           cubeDataBrick.SetBasePosition(qRound(startSample), line, band);
00106           cube.read(cubeDataBrick);
00107           AddData(cubeDataBrick.DoubleBuffer(), cubeDataBrick.size());
00108           if (progress != NULL) {
00109             progress->CheckStatus();
00110           }
00111         }
00112       }
00113     }
00114   }
00115 
00116 
00125   Histogram::Histogram(ControlNet &net, double(ControlMeasure::*statFunc)() const, int bins) {
00126     
00127     //check to make sure we have a reasonable number of bins
00128     if (bins < 1) {
00129       string msg = "The number of Histogram Bins must be greater than 0";
00130       throw IException(IException::Programmer, msg, _FILEINFO_);
00131     }
00132     SetBins(bins);
00133 
00134     //set the ranges
00135     rangesFromNet(net,statFunc);
00136 
00137     //add all the data to the now setup histogram
00138     addMeasureDataFromNet(net,statFunc);
00139   }
00140 
00141 
00150   Histogram::Histogram(ControlNet &net, double(ControlMeasure::*statFunc)() const, double binWidth) {
00151     
00152     //check to make sure we have a reasonable number of bins
00153     if (binWidth <= 0 ) {
00154       string msg = "The width of Histogram Bins must be greater than 0";
00155       throw IException(IException::Programmer, msg, _FILEINFO_);
00156     }
00157 
00158    //get the range of the data
00159    rangesFromNet(net,statFunc);
00160 
00161    
00162    //stretch the domain so that it is an even multiple of binWidth
00163      //for some reason Histogram makes the end points of the bin range be at the center of
00164      //bins.  Thus the +/-0.5 forces it to point the bin range at the ends of the bins.
00165    SetBinRange(binWidth*(floor(this->ValidMinimum()/binWidth)+0.5),
00166                binWidth*(ceil(this->ValidMaximum()/binWidth)-0.5));
00167    SetValidRange(binWidth*floor(this->ValidMinimum()/binWidth),
00168                  binWidth*ceil(this->ValidMaximum()/binWidth));
00169 
00170    //from the domain of the data and the requested bin width calculate the number of bins
00171    double domain = this->ValidMaximum() - this->ValidMinimum();
00172    int nBins = int ( ceil(domain/binWidth) );
00173    SetBins(nBins);
00174 
00175    //add all the data to the now setup histogram
00176    addMeasureDataFromNet(net,statFunc);
00177   }
00178 
00179 
00186   void Histogram::addMeasureDataFromNet(ControlNet &net, double(ControlMeasure::*statFunc)() const) {
00187     //get the number of object points
00188     int nObjPts =  net.GetNumPoints();
00189     for (int i=0;i<nObjPts;i++) { //for each Object point
00190       const ControlPoint *point = net.GetPoint(i);
00191       if (point->IsIgnored()) continue;  //if the point is ignored then continue
00192 
00193       //get the number of measures
00194       int nObs = point->GetNumMeasures();
00195       for (int j=0;j<nObs;j++) {  //for every measure
00196         const ControlMeasure *measure = point->GetMeasure(j);
00197         if (measure->IsIgnored())  continue;
00198         this->AddData((measure->*statFunc)());
00199       }
00200     }
00201   }
00202 
00203 
00211   void Histogram::rangesFromNet(ControlNet &net, double(ControlMeasure::*statFunc)() const) {
00212     double min,max,temp;
00213     min =  DBL_MAX;
00214     max = -DBL_MAX;
00215 
00216     //get the number of object points
00217     int nObjPts =  net.GetNumPoints();
00218     for (int i=0;i<nObjPts;i++) { //for each Object point
00219       const ControlPoint *point = net.GetPoint(i);
00220       if (point->IsIgnored()) continue;  //if the point is ignored then continue
00221 
00222       //get the number of measures
00223       int nObs = point->GetNumMeasures();
00224       for (int j=0;j<nObs;j++) {  //for every measure
00225         const ControlMeasure *measure = point->GetMeasure(j);
00226         if (measure->IsIgnored())  continue;
00227 
00228         temp = (measure->*statFunc)();  //get the data using the passed ControlMeasure acessor function pointer
00229         if (temp > max) max = temp;
00230         if (temp < min) min = temp;
00231       }
00232     }
00233 
00234     //if DBL_MAX's weren't changed there's a problem
00235     if (max <= min) {
00236       string msg = "The net file appears to have 1 or fewer measures with residual data, "
00237                    "thus no histogram for this net file can be created;";
00238       throw IException(IException::User, msg, _FILEINFO_);
00239     }
00240 
00241     //set up the histogram ranges
00242     SetValidRange(min, max);
00243     SetBinRange(min, max);
00244   }
00245 
00246 
00247   void Histogram::InitializeFromCube(Cube &cube, int statsBand,
00248       Progress *progress, int nbins, double startSample, double startLine,
00249       double endSample, double endLine) {
00250     // Make sure band is valid, 0 is valid (means all bands)
00251     if ((statsBand < 0) || (statsBand > cube.bandCount())) {
00252       string msg = "Cannot gather histogram for band [" + IString(statsBand) +
00253           "]";
00254       throw IException(IException::Programmer, msg, _FILEINFO_);
00255     }
00256 
00257     // We need to find the min/max DN value for our histogram bins to be the
00258     //   correct size.
00259     double minDnValue = Null;
00260     double maxDnValue = Null;
00261 
00262     if (cube.pixelType() == UnsignedByte) {
00263       // If we can discretely store every data point, then we can use the
00264       //   possible extent of the data range as our min/max dn values.
00265       if (nbins == 0) {
00266         minDnValue = 0.0 * cube.multiplier() + cube.base();
00267         maxDnValue = 255.0 * cube.multiplier() + cube.base();
00268         nbins = 256;
00269       }
00270     }
00271     else if (cube.pixelType() == SignedWord) {
00272       if (nbins == 0) {
00273         minDnValue = -32768.0 * cube.multiplier() + cube.base();
00274         maxDnValue = 32767.0 * cube.multiplier() + cube.base();
00275         nbins = 65536;
00276       }
00277     }
00278     else if (cube.pixelType() == Real) {
00279       // We can't account for all the possibilities of a double inside of any
00280       //   data range, so don't guess min/max DN values.
00281       if (nbins == 0) {
00282         nbins = 65536;
00283       }
00284     }
00285     else {
00286       IString msg = "Unsupported pixel type";
00287       throw IException(IException::Programmer, msg, _FILEINFO_);
00288     }
00289 
00290     if (startSample == Null)
00291       startSample = 1.0;
00292 
00293     if (endSample == Null)
00294       endSample = cube.sampleCount();
00295 
00296     if (startLine == Null)
00297       startLine = 1.0;
00298 
00299     if (endLine == Null)
00300       endLine = cube.lineCount();
00301 
00302     // If we still need our min/max DN values, find them.
00303     if (minDnValue == Null || maxDnValue == Null) {
00304 
00305       Brick cubeDataBrick((int)(endSample - startSample + 1),
00306                           1, 1, cube.pixelType());
00307       Statistics stats;
00308 
00309       // if band == 0, then we're gathering stats for all bands. I'm really
00310       //   not sure that this is correct, a good idea or called from anywhere...
00311       //   but I don't have time to track down the use case.
00312       int startBand = statsBand;
00313       int endBand = statsBand;
00314 
00315       if (statsBand == 0) {
00316         startBand = 1;
00317         endBand = cube.bandCount();
00318       }
00319 
00320       if (progress != NULL) {
00321         progress->SetText("Computing min/max for histogram");
00322         progress->SetMaximumSteps(
00323           (int)(endLine - startLine + 1) * (int)(endBand - startBand + 1));
00324         progress->CheckStatus();
00325       }
00326 
00327       for (int band = startBand; band <= endBand; band++) {
00328         for (int line = (int)startLine; line <= endLine; line++) {
00329           cubeDataBrick.SetBasePosition(qRound(startSample), line, band);
00330           cube.read(cubeDataBrick);
00331           stats.AddData(cubeDataBrick.DoubleBuffer(), cubeDataBrick.size());
00332           if (progress != NULL) {
00333             progress->CheckStatus();
00334           }
00335         }
00336       }
00337 
00338       if (stats.ValidPixels() == 0) {
00339         minDnValue = 0.0;
00340         maxDnValue = 1.0;
00341       }
00342       else {
00343         minDnValue = stats.Minimum();
00344         maxDnValue = stats.Maximum();
00345       }
00346     }
00347 
00348     // Set the bins and range
00349     SetBinRange(minDnValue, maxDnValue);
00350     SetBins(nbins);
00351   }
00352 
00353 
00355   Histogram::~Histogram() {
00356   }
00357 
00358   void Histogram::SetBinRange(double binStart, double binEnd) {
00359     if (binEnd < binStart) {
00360       string msg = "The binning range start [" + IString(binStart) +
00361                    " must be less than the end [" + IString(binEnd) + ".";
00362       throw IException(IException::Programmer, msg, _FILEINFO_);
00363     }
00364 
00365     p_binRangeStart = binStart;
00366     p_binRangeEnd = binEnd;
00367   }
00368 
00369 
00371   void Histogram::Reset() {
00372     Statistics::Reset();
00373     for (int i = 0; i < (int)p_bins.size(); i++) {
00374       p_bins[i] = 0;
00375     }
00376   }
00377 
00378 
00380   void Histogram::SetBins(const int nbins) {
00381     p_bins.resize(nbins);
00382     Histogram::Reset();
00383   }
00384 
00393   void Histogram::AddData(const double *data,
00394                           const unsigned int count) {
00395     Statistics::AddData(data, count);
00396 
00397     int nbins = p_bins.size();
00398     int index;
00399     for (unsigned int i = 0; i < count; i++) {
00400       if (IsValidPixel(data[i]) && InRange(data[i])) {
00401         if (BinRangeStart() == BinRangeEnd()) {
00402           index = 0;
00403         }
00404         else {
00405           index = (int) floor((double)(nbins - 1) / (BinRangeEnd() - BinRangeStart()) *
00406                               (data[i] - BinRangeStart()) + 0.5);
00407         }
00408         if (index < 0) index = 0;
00409         if (index >= nbins) index = nbins - 1;
00410         p_bins[index] += 1;
00411       }
00412     }
00413   }
00414 
00415 
00423   void Histogram::AddData(const double data) {
00424     Statistics::AddData(data);
00425 
00426     int nbins = p_bins.size();
00427     int index;
00428     if (IsValidPixel(data) && InRange(data)) {
00429       if (BinRangeStart() == BinRangeEnd()) {
00430         index = 0;
00431       }
00432       else {
00433         index = (int) floor((double)(nbins - 1) / (BinRangeEnd() - BinRangeStart()) *
00434                             (data - BinRangeStart()) + 0.5);
00435       }
00436       if (index < 0) index = 0;
00437       if (index >= nbins) index = nbins - 1;
00438       p_bins[index] += 1;
00439     }
00440   }
00441 
00442 
00452   void Histogram::RemoveData(const double *data,
00453                              const unsigned int count) {
00454     Statistics::RemoveData(data, count);
00455 
00456     int nbins = p_bins.size();
00457     int index;
00458     for (unsigned int i = 0; i < count; i++) {
00459       if (IsValidPixel(data[i])) {
00460         if (BinRangeStart() == BinRangeEnd()) {
00461           index = 0;
00462         }
00463         else {
00464           index = (int) floor((double)(nbins - 1) / (BinRangeEnd() - BinRangeStart()) *
00465                               (data[i] - BinRangeStart()) + 0.5);
00466         }
00467         if (index < 0) index = 0;
00468         if (index >= nbins) index = nbins - 1;
00469         p_bins[index] -= 1;
00470       }
00471     }
00472   }
00473 
00479   double Histogram::Median() const {
00480     return Percent(50.0);
00481   }
00482 
00488   double Histogram::Mode() const {
00489     int mode = 0;
00490     for (int i = 0; i < (int)p_bins.size(); i++) {
00491       if (p_bins[i] > p_bins[mode]) mode = i;
00492     }
00493 
00494     if (p_bins[mode] < 1) return NULL8;
00495 
00496     return BinMiddle(mode);
00497   }
00498 
00499 
00510   double Histogram::Percent(double percent) const {
00511     if ((percent < 0.0) || (percent > 100.0)) {
00512       string m = "Argument percent outside of the range 0 to 100 in";
00513       m += " [Histogram::Percent]";
00514       throw IException(IException::Programmer, m, _FILEINFO_);
00515     }
00516 
00517     if (ValidPixels() < 1) return NULL8;
00518 
00519     BigInt currentPixels = 0;
00520     double currentPercent;
00521 
00522     for (int i = 0; i < (int)p_bins.size(); i++) {
00523       currentPixels += p_bins[i];
00524       currentPercent = (double) currentPixels / (double) ValidPixels() * 100.0;
00525       if (currentPercent >= percent) {
00526         return BinMiddle(i);
00527       }
00528     }
00529 
00530     return BinMiddle((int)p_bins.size() - 1);
00531   }
00532 
00533 
00541   double Histogram::Skew() const {
00542     if (ValidPixels() < 1) return NULL8;
00543     double sdev = StandardDeviation();
00544     if (sdev == 0.0) return 0.0;
00545     return 3.0 * (Average() - Median()) / sdev;
00546   }
00547 
00548 
00556   BigInt Histogram::BinCount(const int index) const {
00557     if ((index < 0) || (index >= (int)p_bins.size())) {
00558       QString message = Message::ArraySubscriptNotInRange(index);
00559       throw IException(IException::Programmer, message, _FILEINFO_);
00560     }
00561 
00562     return p_bins[index];
00563   }
00564 
00565 
00577   void Histogram::BinRange(const int index,
00578                            double &low, double &high) const {
00579     if ((index < 0) || (index >= (int)p_bins.size())) {
00580       QString message = Message::ArraySubscriptNotInRange(index);
00581       throw IException(IException::Programmer, message, _FILEINFO_);
00582     }
00583 
00584     double binSize = (BinRangeEnd() - BinRangeStart()) / (double)(p_bins.size() - 1);
00585     low = BinRangeStart() - binSize / 2.0 + binSize * (double) index;
00586     high = low + binSize;
00587   }
00588 
00589 
00598   double Histogram::BinMiddle(const int index) const {
00599     if ((index < 0) || (index >= (int)p_bins.size())) {
00600       QString message = Message::ArraySubscriptNotInRange(index);
00601       throw IException(IException::Programmer, message, _FILEINFO_);
00602     }
00603 
00604     double low, high;
00605     BinRange(index, low, high);
00606     return (low + high) / 2.0;
00607   }
00608 
00609 
00617   double Histogram::BinSize() const {
00618     double low, high;
00619     BinRange(0, low, high);
00620     return high - low;
00621   }
00622 
00623 
00629   int Histogram::Bins() const {
00630     return (int)p_bins.size();
00631   }
00632 
00633 
00639   BigInt Histogram::MaxBinCount() const {
00640 
00641     BigInt maxBinCount = 0;
00642     for (int i = 0; i < (int)p_bins.size(); i++) {
00643       if (p_bins[i] > maxBinCount) maxBinCount = p_bins[i];
00644     }
00645 
00646     return maxBinCount;
00647   }
00648 }