|
Isis 3.0 Object Programmers' Reference |
Home |
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 }