|
Isis 3.0 Object Programmers' Reference |
Home |
00001 00020 #include "AutoReg.h" 00021 #include "Buffer.h" 00022 #include "Centroid.h" 00023 #include "Chip.h" 00024 #include "FileName.h" 00025 #include "Histogram.h" 00026 #include "IException.h" 00027 #include "Interpolator.h" 00028 #include "LeastSquares.h" 00029 #include "Matrix.h" 00030 #include "PixelType.h" 00031 #include "Plugin.h" 00032 #include "PolynomialBivariate.h" 00033 #include "Pvl.h" 00034 00035 using namespace std; 00036 namespace Isis { 00079 AutoReg::AutoReg(Pvl &pvl) { 00080 p_template = pvl.FindObject("AutoRegistration"); 00081 00082 // Set default parameters 00083 p_patternChip.SetSize(3, 3); 00084 p_searchChip.SetSize(5, 5); 00085 p_fitChip.SetSize(5, 5); 00086 p_reducedPatternChip.SetSize(3, 3); 00087 p_reducedSearchChip.SetSize(5, 5); 00088 p_reducedFitChip.SetSize(5, 5); 00089 p_gradientFilterType = None; 00090 00091 SetPatternValidPercent(50.0); 00092 SetSubsearchValidPercent(50.0); 00093 SetPatternZScoreMinimum(1.0); 00094 SetTolerance(Isis::Null); 00095 00096 SetSubPixelAccuracy(true); 00097 SetSurfaceModelDistanceTolerance(1.5); 00098 SetSurfaceModelWindowSize(5); 00099 00100 SetReductionFactor(1); 00101 00102 // Clear statistics 00103 //TODO: Delete these after control net refactor. 00104 p_totalRegistrations = 0; 00105 p_pixelSuccesses = 0; 00106 p_subpixelSuccesses = 0; 00107 p_patternChipNotEnoughValidDataCount = 0; 00108 p_patternZScoreNotMetCount = 0; 00109 p_fitChipNoDataCount = 0; 00110 p_fitChipToleranceNotMetCount = 0; 00111 p_surfaceModelNotEnoughValidDataCount = 0; 00112 p_surfaceModelSolutionInvalidCount = 0; 00113 p_surfaceModelDistanceInvalidCount = 0; 00114 00115 p_sampMovement = 0.; 00116 p_lineMovement = 0.; 00117 00118 Init(); 00119 Parse(pvl); 00120 } 00121 00127 void AutoReg::Init() { 00128 // Set computed parameters to NULL so we don't use values from a previous 00129 // run 00130 p_zScoreMin = Isis::Null; 00131 p_zScoreMax = Isis::Null; 00132 p_goodnessOfFit = Isis::Null; 00133 00134 p_bestSamp = 0; 00135 p_bestLine = 0; 00136 p_bestFit = Isis::Null; 00137 00138 // -------------------------------------------------- 00139 // Nulling out the fit chip 00140 // -------------------------------------------------- 00141 for(int line = 1; line <= p_fitChip.Lines(); line++) { 00142 for(int samp = 1; samp <= p_fitChip.Samples(); samp++) { 00143 p_fitChip.SetValue(samp, line, Isis::Null); 00144 } 00145 } 00146 // -------------------------------------------------- 00147 // Nulling out the reduced pattern chip 00148 // -------------------------------------------------- 00149 for(int line = 1; line <= p_reducedPatternChip.Lines(); line++) { 00150 for(int samp = 1; samp <= p_reducedPatternChip.Samples(); samp++) { 00151 p_reducedPatternChip.SetValue(samp, line, Isis::Null); 00152 } 00153 } 00154 // -------------------------------------------------- 00155 // Nulling out the reduced search chip 00156 // -------------------------------------------------- 00157 for(int line = 1; line <= p_reducedSearchChip.Lines(); line++) { 00158 for(int samp = 1; samp <= p_reducedSearchChip.Samples(); samp++) { 00159 p_reducedSearchChip.SetValue(samp, line, Isis::Null); 00160 } 00161 } 00162 00163 } 00164 00166 AutoReg::~AutoReg() { 00167 00168 } 00169 00207 void AutoReg::Parse(Pvl &pvl) { 00208 try { 00209 // Get info from Algorithm group 00210 PvlGroup &algo = pvl.FindGroup("Algorithm", Pvl::Traverse); 00211 SetTolerance(algo["Tolerance"]); 00212 if(algo.HasKeyword("ChipInterpolator")) { 00213 SetChipInterpolator((QString)algo["ChipInterpolator"]); 00214 } 00215 00216 if(algo.HasKeyword("SubpixelAccuracy")) { 00217 SetSubPixelAccuracy((QString)algo["SubpixelAccuracy"] == "True"); 00218 } 00219 00220 if(algo.HasKeyword("ReductionFactor")) { 00221 SetReductionFactor((int)algo["ReductionFactor"]); 00222 } 00223 00224 if (algo.HasKeyword("Gradient")) { 00225 SetGradientFilterType((QString)algo["Gradient"]); 00226 } 00227 00228 // Setup the pattern chip 00229 PvlGroup &pchip = pvl.FindGroup("PatternChip", Pvl::Traverse); 00230 PatternChip()->SetSize((int)pchip["Samples"], (int)pchip["Lines"]); 00231 00232 double minimum = Isis::ValidMinimum; 00233 double maximum = Isis::ValidMaximum; 00234 if(pchip.HasKeyword("ValidMinimum")) minimum = pchip["ValidMinimum"]; 00235 if(pchip.HasKeyword("ValidMaximum")) maximum = pchip["ValidMaximum"]; 00236 PatternChip()->SetValidRange(minimum, maximum); 00237 00238 if(pchip.HasKeyword("MinimumZScore")) { 00239 SetPatternZScoreMinimum((double)pchip["MinimumZScore"]); 00240 } 00241 if(pchip.HasKeyword("ValidPercent")) { 00242 SetPatternValidPercent((double)pchip["ValidPercent"]); 00243 } 00244 00245 // Setup the search chip 00246 PvlGroup &schip = pvl.FindGroup("SearchChip", Pvl::Traverse); 00247 SearchChip()->SetSize((int)schip["Samples"], (int)schip["Lines"]); 00248 00249 minimum = Isis::ValidMinimum; 00250 maximum = Isis::ValidMaximum; 00251 if(schip.HasKeyword("ValidMinimum")) minimum = schip["ValidMinimum"]; 00252 if(schip.HasKeyword("ValidMaximum")) maximum = schip["ValidMaximum"]; 00253 SearchChip()->SetValidRange(minimum, maximum); 00254 if(schip.HasKeyword("SubchipValidPercent")) { 00255 SetSubsearchValidPercent((double)schip["SubchipValidPercent"]); 00256 } 00257 00258 // Setup surface model 00259 PvlObject ar = pvl.FindObject("AutoRegistration"); 00260 if(ar.HasGroup("SurfaceModel")) { 00261 PvlGroup &smodel = ar.FindGroup("SurfaceModel", Pvl::Traverse); 00262 if(smodel.HasKeyword("DistanceTolerance")) { 00263 SetSurfaceModelDistanceTolerance((double)smodel["DistanceTolerance"]); 00264 } 00265 00266 if(smodel.HasKeyword("WindowSize")) { 00267 SetSurfaceModelWindowSize((int)smodel["WindowSize"]); 00268 } 00269 } 00270 00271 } 00272 catch(IException &e) { 00273 QString msg = "Improper format for AutoReg PVL [" + pvl.FileName() + "]"; 00274 throw IException(e, IException::User, msg, _FILEINFO_); 00275 } 00276 return; 00277 } 00278 00286 void AutoReg::SetGradientFilterType(const QString &gradientFilterType) { 00287 if (gradientFilterType == "None") { 00288 p_gradientFilterType = None; 00289 } 00290 else if (gradientFilterType == "Sobel") { 00291 p_gradientFilterType = Sobel; 00292 } 00293 else { 00294 throw IException(IException::User, 00295 "Invalid Gradient type. Cannot use [" 00296 + gradientFilterType + "] to filter chip", 00297 _FILEINFO_); 00298 } 00299 } 00300 00301 00302 QString AutoReg::GradientFilterString() const { 00303 switch (p_gradientFilterType) { 00304 case None: return "None"; 00305 case Sobel: return "Sobel"; 00306 default: throw IException( 00307 IException::Programmer, 00308 "AutoReg only allows Sobel gradient filter or None", 00309 _FILEINFO_); 00310 } 00311 } 00312 00313 00325 void AutoReg::SetSubPixelAccuracy(bool on) { 00326 p_subpixelAccuracy = on; 00327 } 00328 00352 void AutoReg::SetPatternValidPercent(const double percent) { 00353 if((percent <= 0.0) || (percent > 100.0)) { 00354 string msg = "Invalid value for PatternChip ValidPercent [" 00355 + IString(percent) 00356 + "]. Must be greater than 0.0 and less than or equal to 100.0 (Default is 50.0)."; 00357 throw IException(IException::User, msg, _FILEINFO_); 00358 } 00359 p_patternValidPercent = percent; 00360 } 00361 00362 00380 void AutoReg::SetSubsearchValidPercent(const double percent) { 00381 if((percent <= 0.0) || (percent > 100.0)) { 00382 string msg = "Invalid value for SearchChip SubchipValidPercent [" 00383 + IString(percent) + "]" 00384 + "]. Must be greater than 0.0 and less than or equal to 100.0 (Default is 50.0)."; 00385 throw IException(IException::User, msg, _FILEINFO_); 00386 } 00387 p_subsearchValidPercent = percent; 00388 } 00389 00390 00407 void AutoReg::SetPatternZScoreMinimum(double minimum) { 00408 if(minimum <= 0.0) { 00409 string msg = "Invalid value for PatternChip MinimumZScore [" 00410 + IString(minimum) 00411 + "]. Must be greater than 0.0. (Default is 1.0)."; 00412 throw IException(IException::User, msg, _FILEINFO_); 00413 } 00414 p_minimumPatternZScore = minimum; 00415 } 00416 00417 00429 void AutoReg::SetTolerance(const double tolerance) { 00430 p_tolerance = tolerance; 00431 } 00432 00453 void AutoReg::SetChipInterpolator(const QString &interpolator) { 00454 00455 Isis::Interpolator::interpType itype; 00456 if(interpolator == "NearestNeighborType") { 00457 itype = Isis::Interpolator::NearestNeighborType; 00458 } 00459 else if(interpolator == "BiLinearType") { 00460 itype = Isis::Interpolator::BiLinearType; 00461 } 00462 else if(interpolator == "CubicConvolutionType") { 00463 itype = Isis::Interpolator::CubicConvolutionType; 00464 } 00465 else { 00466 throw IException(IException::User, 00467 "Invalid Interpolator type. Cannot use [" 00468 + interpolator + "] to load chip", 00469 _FILEINFO_); 00470 } 00471 00472 // Set pattern and search chips to use this interpolator type when reading data from cube 00473 p_patternChip.SetReadInterpolator(itype); 00474 p_searchChip.SetReadInterpolator(itype); 00475 p_reducedPatternChip.SetReadInterpolator(itype); 00476 p_reducedSearchChip.SetReadInterpolator(itype); 00477 00478 } 00479 00493 void AutoReg::SetSurfaceModelWindowSize(int size) { 00494 if(size % 2 != 1 || size < 3) { 00495 string msg = "Invalid value for SurfaceModel WindowSize [" 00496 + IString(size) + "]. Must be an odd number greater than or equal to 3"; 00497 throw IException(IException::User, msg, _FILEINFO_); 00498 } 00499 p_windowSize = size; 00500 } 00501 00514 void AutoReg::SetSurfaceModelDistanceTolerance(double distance) { 00515 if(distance <= 0.0) { 00516 string msg = "Invalid value for SurfaceModel DistanceTolerance [" 00517 + IString(distance) + "]. Must greater than 0.0."; 00518 throw IException(IException::User, msg, _FILEINFO_); 00519 } 00520 p_distanceTolerance = distance; 00521 } 00522 00523 00534 void AutoReg::SetReductionFactor(int factor) { 00535 if(factor < 1) { 00536 string msg = "Invalid value for Algorithm ReductionFactor [" 00537 + IString(factor) + "]. Must greater than or equal to 1."; 00538 throw IException(IException::User, msg, _FILEINFO_); 00539 } 00540 p_reduceFactor = factor; 00541 } 00542 00552 Chip AutoReg::Reduce(Chip &chip, int reductionFactor) { 00553 Chip rChip((int)chip.Samples() / reductionFactor, 00554 (int)chip.Lines() / reductionFactor); 00555 if((int)rChip.Samples() < 1 || (int)rChip.Lines() < 1) { 00556 return chip; 00557 } 00558 00559 // ---------------------------------- 00560 // Fill the reduced Chip with nulls. 00561 // ---------------------------------- 00562 for(int line = 1; line <= rChip.Lines(); line++) { 00563 for(int samp = 1; samp <= rChip.Samples(); samp++) { 00564 rChip.SetValue(samp, line, Isis::Null); 00565 } 00566 } 00567 00568 Statistics stats; 00569 for(int l = 1; l <= rChip.Lines(); l++) { 00570 int istartLine = (l - 1) * reductionFactor + 1; 00571 int iendLine = istartLine + reductionFactor - 1; 00572 for(int s = 1; s <= rChip.Samples(); s++) { 00573 00574 int istartSamp = (s - 1) * reductionFactor + 1; 00575 int iendSamp = istartSamp + reductionFactor - 1; 00576 00577 stats.Reset(); 00578 for(int line = istartLine; line < iendLine; line++) { 00579 for(int sample = istartSamp; sample < iendSamp; sample++) { 00580 stats.AddData(chip.GetValue(sample, line)); 00581 } 00582 } 00583 rChip.SetValue(s, l, stats.Average()); 00584 } 00585 } 00586 return rChip; 00587 } 00588 00589 00600 AutoReg::RegisterStatus AutoReg::Register() { 00601 // The search chip must be bigger than the pattern chip by N pixels in 00602 // both directions for a successful surface model 00603 int N = p_windowSize / 2 + 1; 00604 00605 if(p_searchChip.Samples() < p_patternChip.Samples() + N) { 00606 string msg = "Search chips samples ["; 00607 msg += IString(p_searchChip.Samples()) + "] must be at "; 00608 msg += "least [" + IString(N) + "] pixels wider than the pattern chip samples ["; 00609 msg += IString(p_patternChip.Samples()) + "] for successful surface modeling"; 00610 throw IException(IException::User, msg, _FILEINFO_); 00611 } 00612 00613 if(p_searchChip.Lines() < p_patternChip.Lines() + N) { 00614 string msg = "Search chips lines ["; 00615 msg += IString(p_searchChip.Lines()) + "] must be at "; 00616 msg += "least [" + IString(N) + "] pixels taller than the pattern chip lines ["; 00617 msg += IString(p_patternChip.Lines()) + "] for successful surface modeling"; 00618 throw IException(IException::User, msg, _FILEINFO_); 00619 } 00620 00621 Init(); 00622 p_totalRegistrations++; 00623 00624 // Create copies of the search and pattern chips and run a gradient filter 00625 // over them before attempting to perform a match. We do this so that 00626 // multiple calls to this method won't result in having a gradient filter 00627 // applied multiple times to the same chip. 00628 Chip gradientPatternChip(p_patternChip); 00629 Chip gradientSearchChip(p_searchChip); 00630 ApplyGradientFilter(gradientPatternChip); 00631 ApplyGradientFilter(gradientSearchChip); 00632 00633 // See if the pattern chip has enough good data 00634 if(!gradientPatternChip.IsValid(p_patternValidPercent)) { 00635 p_patternChipNotEnoughValidDataCount++; 00636 p_registrationStatus = PatternChipNotEnoughValidData; 00637 return PatternChipNotEnoughValidData; 00638 } 00639 00640 if(!ComputeChipZScore(gradientPatternChip)) { 00641 p_patternZScoreNotMetCount++; 00642 p_registrationStatus = PatternZScoreNotMet; 00643 return PatternZScoreNotMet; 00644 } 00645 00665 int startSamp = (gradientPatternChip.Samples() - 1) / 2 + 1; 00666 int startLine = (gradientPatternChip.Lines() - 1) / 2 + 1; 00667 int endSamp = gradientSearchChip.Samples() - startSamp + 1; 00668 int endLine = gradientSearchChip.Lines() - startLine + 1; 00669 00670 // ---------------------------------------------------------------------- 00671 // Before we attempt to apply the reduction factor, we need to make sure 00672 // we won't produce a chip of a bad size. 00673 // ---------------------------------------------------------------------- 00674 if(gradientPatternChip.Samples() / p_reduceFactor < 2 || gradientPatternChip.Lines() / p_reduceFactor < 2) { 00675 string msg = "Reduction factor is too large"; 00676 throw IException(IException::User, msg, _FILEINFO_); 00677 } 00678 00679 // Establish the center search tack point as best pixel to start for the 00680 // adaptive algorithm prior to reduction. 00681 int bestSearchSamp = gradientSearchChip.TackSample(); 00682 int bestSearchLine = gradientSearchChip.TackLine(); 00683 00684 // --------------------------------------------------------------------- 00685 // if the reduction factor is still not equal to one, then we go ahead 00686 // with the reduction of the chips and call Match to get the first 00687 // estimate of the best line/sample. 00688 // --------------------------------------------------------------------- 00689 if(p_reduceFactor != 1) { 00690 p_reducedPatternChip.SetSize((int)gradientPatternChip.Samples() / p_reduceFactor, 00691 (int)gradientPatternChip.Lines() / p_reduceFactor); 00692 00693 // ---------------------------------- 00694 // Fill the reduced Chip with nulls. 00695 // ---------------------------------- 00696 for(int line = 1; line <= p_reducedPatternChip.Lines(); line++) { 00697 for(int samp = 1; samp <= p_reducedPatternChip.Samples(); samp++) { 00698 p_reducedPatternChip.SetValue(samp, line, Isis::Null); 00699 } 00700 } 00701 00702 p_reducedPatternChip = Reduce(gradientPatternChip, p_reduceFactor); 00703 if(!ComputeChipZScore(p_reducedPatternChip)) { 00704 p_patternZScoreNotMetCount++; 00705 p_registrationStatus = PatternZScoreNotMet; 00706 return PatternZScoreNotMet; 00707 } 00708 00709 p_reducedSearchChip = Reduce(gradientSearchChip, p_reduceFactor); 00710 int reducedStartSamp = (p_reducedPatternChip.Samples() - 1) / 2 + 1; 00711 int reducedEndSamp = p_reducedSearchChip.Samples() - reducedStartSamp + 1; 00712 int reducedStartLine = (p_reducedPatternChip.Lines() - 1) / 2 + 1; 00713 int reducedEndLine = p_reducedSearchChip.Lines() - reducedStartLine + 1; 00714 00715 Match(p_reducedSearchChip, p_reducedPatternChip, p_reducedFitChip, 00716 reducedStartSamp, reducedEndSamp, reducedStartLine, reducedEndLine); 00717 00718 if(p_bestFit == Isis::Null) { 00719 p_fitChipNoDataCount++; 00720 p_registrationStatus = FitChipNoData; 00721 return FitChipNoData; 00722 } 00723 00724 // ------------------------------------------------------ 00725 // p_bestSamp and p_bestLine are set in Match() which is 00726 // called above. 00727 // ----------------------------------------------------- 00728 int bs = (p_bestSamp - 1) * p_reduceFactor + ((p_reduceFactor - 1) / 2) + 1; 00729 int bl = (p_bestLine - 1) * p_reduceFactor + ((p_reduceFactor - 1) / 2) + 1; 00730 00731 // --------------------------------------------------------------- 00732 // Now we grow our window size according to the reduction factor. 00733 // And we grow around where the first call Match() told us was the 00734 // best line/sample. 00735 // --------------------------------------------------------------- 00736 int newstartSamp = bs - p_reduceFactor - p_windowSize - 1; 00737 int newendSamp = bs + p_reduceFactor + p_windowSize + 1; 00738 int newstartLine = bl - p_reduceFactor - p_windowSize - 1; 00739 int newendLine = bl + p_reduceFactor + p_windowSize + 1; 00740 00741 if(newstartLine < startLine) newstartLine = startLine; 00742 if(newendSamp > endSamp) newendSamp = endSamp; 00743 if(newstartSamp < startSamp) newstartSamp = startSamp; 00744 if(newendLine > endLine) newendLine = endLine; 00745 00746 startSamp = newstartSamp; 00747 endSamp = newendSamp; 00748 startLine = newstartLine; 00749 endLine = newendLine; 00750 // We have found a good pixel in the reduction chip, but we 00751 // don't want to use its position, so reset in prep. for 00752 // non-adaptive registration. Save it off for the adaptive algorithm. 00753 bestSearchSamp = bs; 00754 bestSearchLine = bl; 00755 p_bestSamp = 0; 00756 p_bestLine = 0; 00757 p_bestFit = Isis::Null; 00758 } 00759 00760 p_registrationStatus = Registration(gradientSearchChip, gradientPatternChip, 00761 p_fitChip, startSamp, startLine, endSamp, endLine, 00762 bestSearchSamp, bestSearchLine); 00763 00764 gradientSearchChip.SetChipPosition(p_chipSample, p_chipLine); 00765 p_searchChip.SetChipPosition(p_chipSample, p_chipLine); 00766 p_cubeSample = gradientSearchChip.CubeSample(); 00767 p_cubeLine = gradientSearchChip.CubeLine(); 00768 00769 // Save off the gradient search and pattern chips if we used a gradient 00770 // filter. 00771 if (p_gradientFilterType != None) { 00772 p_gradientSearchChip = gradientSearchChip; 00773 p_gradientPatternChip = gradientPatternChip; 00774 } 00775 00776 p_goodnessOfFit = p_bestFit; 00777 00778 if (Success()) { 00779 if (p_registrationStatus == AutoReg::SuccessSubPixel) 00780 p_subpixelSuccesses++; 00781 else 00782 p_pixelSuccesses++; 00783 } 00784 00785 return p_registrationStatus; 00786 } 00787 00788 00822 AutoReg::RegisterStatus AutoReg::Registration(Chip &sChip, Chip &pChip, 00823 Chip &fChip, int startSamp, int startLine, int endSamp, int endLine, 00824 int bestSamp, int bestLine) { 00825 00826 // Not adaptive, continue with slower search traverse 00827 Match(sChip, pChip, fChip, startSamp, endSamp, startLine, endLine); 00828 00829 // Check to see if we went through the fit chip and never got a fit at 00830 // any location. 00831 if (p_bestFit == Isis::Null) { 00832 p_fitChipNoDataCount++; 00833 p_registrationStatus = FitChipNoData; 00834 return FitChipNoData; 00835 } 00836 00837 // Now see if we satisified the goodness of fit tolerance 00838 if (!CompareFits(p_bestFit, Tolerance())) { 00839 p_fitChipToleranceNotMetCount++; 00840 p_registrationStatus = FitChipToleranceNotMet; 00841 return FitChipToleranceNotMet; 00842 } 00843 00844 // Try to fit a model for sub-pixel accuracy if necessary 00845 if (p_subpixelAccuracy && !IsIdeal(p_bestFit)) { 00846 Chip window(p_windowSize, p_windowSize); 00847 fChip.Extract(p_bestSamp, p_bestLine, window); 00848 window.SetChipPosition(p_windowSize / 2 + 1, p_windowSize / 2 + 1); 00849 00850 // Make sure more than 2/3 of the data in the window is valid. Otherwise, 00851 // we are likely too close to the edge. 00852 if (!window.IsValid(100.0 * 2.1 / 3.0)) { 00853 p_surfaceModelNotEnoughValidDataCount++; 00854 p_registrationStatus = SurfaceModelNotEnoughValidData; 00855 return SurfaceModelNotEnoughValidData; 00856 } 00857 00858 // Now that we know we have enough data to model the surface we call 00859 // SetSubpixelPosition() to get the sub-pixel accuracy we are looking for. 00860 bool computedSubPixel = SetSubpixelPosition(window); 00861 if (!computedSubPixel) 00862 return p_registrationStatus; 00863 00864 // See if the surface model solution moved too far from our whole pixel 00865 // solution 00866 p_sampMovement = fabs(p_bestSamp - p_chipSample); 00867 p_lineMovement = fabs(p_bestLine - p_chipLine); 00868 if (p_sampMovement > p_distanceTolerance || 00869 p_lineMovement > p_distanceTolerance) { 00870 00871 p_surfaceModelDistanceInvalidCount++; 00872 p_registrationStatus = SurfaceModelDistanceInvalid; 00873 return SurfaceModelDistanceInvalid; 00874 } 00875 00876 p_registrationStatus = SuccessSubPixel; 00877 } 00878 else { 00879 p_chipSample = p_bestSamp; 00880 p_chipLine = p_bestLine; 00881 p_registrationStatus = SuccessPixel; 00882 } 00883 00884 return p_registrationStatus; 00885 } 00886 00887 00898 bool AutoReg::ComputeChipZScore(Chip &chip) { 00899 Statistics patternStats; 00900 for(int i = 0; i < chip.Samples(); i++) { 00901 double pixels[chip.Lines()]; 00902 for(int j = 0; j < chip.Lines(); j++) { 00903 pixels[j] = chip.GetValue(i + 1, j + 1); 00904 } 00905 patternStats.AddData(pixels, chip.Lines()); 00906 } 00907 00908 // If it does not pass, return 00909 p_zScoreMin = patternStats.ZScore(patternStats.Minimum()); 00910 p_zScoreMax = patternStats.ZScore(patternStats.Maximum()); 00911 00912 // p_zScoreMin is made negative here so as to make it the equivalent of 00913 // taking the absolute value (because p_zScoreMin is guaranteed to be 00914 // negative) 00915 if (p_zScoreMax < p_minimumPatternZScore && -p_zScoreMin < p_minimumPatternZScore) { 00916 return false; 00917 } 00918 else { 00919 return true; 00920 } 00921 } 00922 00930 void AutoReg::ApplyGradientFilter(Chip &chip) { 00931 if (p_gradientFilterType == None) { 00932 return; 00933 } 00934 00935 // Use a different subchip size depending on which gradient filter is 00936 // being applied. 00937 int subChipWidth; 00938 if (p_gradientFilterType == Sobel) { 00939 subChipWidth = 3; 00940 } 00941 else { 00942 // Perform extra sanity check. 00943 string msg = 00944 "No rule to set sub-chip width for selected Gradient Filter Type."; 00945 throw IException(IException::Programmer, msg, _FILEINFO_); 00946 } 00947 00948 // Create a new chip to hold output during processing. 00949 Chip filteredChip(chip.Samples(), chip.Lines()); 00950 00951 // Move the subchip through the chip, extracting the contents into a buffer 00952 // of the same shape. This simulates the processing of a cube by boxcar, 00953 // but since that can only operate on cubes, this functionality had to be 00954 // replicated for use on chips. 00955 for (int line = 1; line <= chip.Lines(); line++) { 00956 for (int sample = 1; sample <= chip.Samples(); sample++) { 00957 Chip subChip = chip.Extract(subChipWidth, subChipWidth, 00958 sample, line); 00959 00960 // Fill a buffer with subchip's contents. Since we'll never be storing 00961 // raw bytes in the buffer, we don't care about the pixel type. 00962 Buffer buffer(subChipWidth, subChipWidth, 1, Isis::None); 00963 double *doubleBuffer = buffer.DoubleBuffer(); 00964 int bufferIndex = 0; 00965 for (int subChipLine = 1; subChipLine <= subChip.Lines(); 00966 subChipLine++) { 00967 for (int subChipSample = 1; subChipSample <= subChip.Samples(); 00968 subChipSample++) { 00969 doubleBuffer[bufferIndex] = subChip.GetValue(subChipSample, 00970 subChipLine); 00971 bufferIndex++; 00972 } 00973 } 00974 00975 // Calculate gradient based on contents in buffer and insert it into 00976 // output chip. 00977 double newPixelValue = 0; 00978 if (p_gradientFilterType == Sobel) { 00979 SobelGradient(buffer, newPixelValue); 00980 } 00981 filteredChip.SetValue(sample, line, newPixelValue); 00982 } 00983 } 00984 00985 // Copy the data from the filtered chip back into the original chip. 00986 for (int line = 1; line <= filteredChip.Lines(); line++) { 00987 for (int sample = 1; sample <= filteredChip.Samples(); sample++) { 00988 chip.SetValue(sample, line, filteredChip.GetValue(sample, line)); 00989 } 00990 } 00991 } 00992 00993 01003 void AutoReg::SobelGradient(Buffer &in, double &v) { 01004 bool specials = false; 01005 for(int i = 0; i < in.size(); ++i) { 01006 if(IsSpecial(in[i])) { 01007 specials = true; 01008 } 01009 } 01010 if(specials) { 01011 v = Isis::Null; 01012 return; 01013 } 01014 v = abs((in[0] + 2 * in[1] + in[2]) - (in[6] + 2 * in[7] + in[8])) + 01015 abs((in[2] + 2 * in[5] + in[8]) - (in[0] + 2 * in[3] + in[6])); 01016 } 01017 01034 void AutoReg::Match(Chip &sChip, Chip &pChip, Chip &fChip, int startSamp, int endSamp, int startLine, int endLine) { 01035 // Sanity check. Should have been caught by the two previous tests 01036 if(startSamp == endSamp && startLine == endLine) { 01037 string msg = "StartSample [" + IString(startSamp) + "] = EndSample [" 01038 + IString(endSamp) + "] and StartLine [" + IString(startLine) + " = EndLine [" 01039 + IString(endLine) + "]."; 01040 throw IException(IException::Programmer, msg, _FILEINFO_); 01041 } 01042 01043 // Ok create a fit chip whose size is the same as the search chip 01044 // and then fill it with nulls 01045 fChip.SetSize(sChip.Samples(), sChip.Lines()); 01046 for(int line = 1; line <= fChip.Lines(); line++) { 01047 for(int samp = 1; samp <= fChip.Samples(); samp++) { 01048 fChip.SetValue(samp, line, Isis::Null); 01049 } 01050 } 01051 01052 // Create a chip the same size as the pattern chip. 01053 Chip subsearch(pChip.Samples(), pChip.Lines()); 01054 01055 for(int line = startLine; line <= endLine; line++) { 01056 for(int samp = startSamp; samp <= endSamp; samp++) { 01057 // Extract the subsearch chip and make sure it has enough valid data 01058 sChip.Extract(samp, line, subsearch); 01059 01060 // if(!subsearch.IsValid(p_patternValidPercent)) continue; 01061 if(!subsearch.IsValid(p_subsearchValidPercent)) continue; 01062 01063 // Try to match the two subchips 01064 double fit = MatchAlgorithm(pChip, subsearch); 01065 01066 // If we had a fit save off information about that fit 01067 if(fit != Isis::Null) { 01068 fChip.SetValue(samp, line, fit); 01069 if((p_bestFit == Isis::Null) || CompareFits(fit, p_bestFit)) { 01070 p_bestFit = fit; 01071 p_bestSamp = samp; 01072 p_bestLine = line; 01073 } 01074 } 01075 } 01076 } 01077 } 01078 01079 01090 bool AutoReg::SetSubpixelPosition(Chip &window) { 01091 // The best correlation will be at the center of the window 01092 // if it's smaller than the edge DN's invert the chip DNs 01093 double samples = window.Samples(); 01094 double lines= window.Lines(); 01095 double bestDN = window.GetValue(window.ChipSample(), window.ChipLine()); 01096 if (bestDN < window.GetValue(1, 1)) { 01097 for (int s=1; s <= samples; s++) 01098 for (int l=1; l <= lines; l++) 01099 window.SetValue(s, l, 1.0/window.GetValue(s, l)); //invert all the window DN's 01100 bestDN = 1 / bestDN; 01101 } 01102 01103 // Find the greatest edge DN 01104 double greatestEdgeDn = 0.0; 01105 for (int s = 1; s <= samples; s++) { 01106 greatestEdgeDn = max(window.GetValue(s, 1), greatestEdgeDn); 01107 greatestEdgeDn = max(window.GetValue(s, lines), greatestEdgeDn); 01108 } 01109 for (int l = 2; l <= lines - 1; l++) { 01110 greatestEdgeDn = max(window.GetValue(1, l), greatestEdgeDn); 01111 greatestEdgeDn = max(window.GetValue(samples, l), greatestEdgeDn); 01112 } 01113 01114 //This is a small shift so the the centroid doesn't reach the edge, add 20% 01115 // of the difference between the hightest edge DN and the max DN to the highest edge DN 01116 //The 20% shift added here is somewhat arbitrary, but was choosen because it worked well 01117 // for the maximum correlation tests we did. For new area based algorithms we may want 01118 // to revist this. Possible make it a function of the match type 01119 double temp = greatestEdgeDn + 0.2 * (bestDN - greatestEdgeDn); 01120 01121 Centroid floodFill; 01122 floodFill.setDNRange(temp, 1e100); 01123 01124 Chip selectionChip(window); 01125 floodFill.select(&window, &selectionChip); 01126 01127 double windowSample; 01128 double windowLine; 01129 floodFill.centerOfMassWeighted( 01130 &window, &selectionChip, &windowSample, &windowLine); 01131 01132 int offsetS = p_bestSamp - window.ChipSample(); 01133 int offsetL = p_bestLine - window.ChipLine(); 01134 p_chipSample = windowSample + offsetS; 01135 p_chipLine = windowLine + offsetL; 01136 01137 if (p_chipSample != p_chipSample) { 01138 p_surfaceModelSolutionInvalidCount++; 01139 return false; //this should never happen, but just in case... 01140 } 01141 01142 return true; 01143 } 01144 01145 01155 bool AutoReg::CompareFits(double fit1, double fit2) { 01156 return(std::fabs(fit1 - IdealFit()) <= std::fabs(fit2 - IdealFit())); 01157 } 01158 01165 bool AutoReg::IsIdeal(double fit) { 01166 return(std::fabs(IdealFit() - fit) < 0.00001); 01167 } 01168 01169 01180 Pvl AutoReg::RegistrationStatistics() { 01181 Pvl pvl; 01182 PvlGroup stats("AutoRegStatistics"); 01183 stats += Isis::PvlKeyword("Total", toString(p_totalRegistrations)); 01184 stats += Isis::PvlKeyword("Successful", toString(p_pixelSuccesses + p_subpixelSuccesses)); 01185 stats += Isis::PvlKeyword("Failure", toString(p_totalRegistrations - (p_pixelSuccesses + p_subpixelSuccesses))); 01186 pvl.AddGroup(stats); 01187 01188 PvlGroup successes("Successes"); 01189 successes += PvlKeyword("SuccessPixel", toString(p_pixelSuccesses)); 01190 successes += PvlKeyword("SuccessSubPixel", toString(p_subpixelSuccesses)); 01191 pvl.AddGroup(successes); 01192 01193 PvlGroup grp("PatternChipFailures"); 01194 grp += PvlKeyword("PatternNotEnoughValidData", toString(p_patternChipNotEnoughValidDataCount)); 01195 grp += PvlKeyword("PatternZScoreNotMet", toString(p_patternZScoreNotMetCount)); 01196 pvl.AddGroup(grp); 01197 01198 PvlGroup fit("FitChipFailures"); 01199 fit += PvlKeyword("FitChipNoData", toString(p_fitChipNoDataCount)); 01200 fit += PvlKeyword("FitChipToleranceNotMet", toString(p_fitChipToleranceNotMetCount)); 01201 pvl.AddGroup(fit); 01202 01203 PvlGroup model("SurfaceModelFailures"); 01204 model += PvlKeyword("SurfaceModelNotEnoughValidData", toString(p_surfaceModelNotEnoughValidDataCount)); 01205 model += PvlKeyword("SurfaceModelSolutionInvalid", toString(p_surfaceModelSolutionInvalidCount)); 01206 model += PvlKeyword("SurfaceModelDistanceInvalid", toString(p_surfaceModelDistanceInvalidCount)); 01207 pvl.AddGroup(model); 01208 01209 return (AlgorithmStatistics(pvl)); 01210 } 01211 01219 PvlGroup AutoReg::RegTemplate() { 01220 PvlGroup reg("AutoRegistration"); 01221 01222 PvlGroup &algo = p_template.FindGroup("Algorithm", Pvl::Traverse); 01223 reg += PvlKeyword("Algorithm", algo["Name"][0]); 01224 reg += PvlKeyword("Tolerance", algo["Tolerance"][0]); 01225 if(algo.HasKeyword("SubpixelAccuracy")) { 01226 reg += PvlKeyword("SubpixelAccuracy", algo["SubpixelAccuracy"][0]); 01227 } 01228 if(algo.HasKeyword("ReductionFactor")) { 01229 reg += PvlKeyword("ReductionFactor", algo["ReductionFactor"][0]); 01230 } 01231 if(algo.HasKeyword("Gradient")) { 01232 reg += PvlKeyword("Gradient", algo["Gradient"][0]); 01233 } 01234 01235 PvlGroup &pchip = p_template.FindGroup("PatternChip", Pvl::Traverse); 01236 reg += PvlKeyword("PatternSamples", pchip["Samples"][0]); 01237 reg += PvlKeyword("PatternLines", pchip["Lines"][0]); 01238 if(pchip.HasKeyword("ValidMinimum")) { 01239 reg += PvlKeyword("PatternMinimum", pchip["ValidMinimum"][0]); 01240 } 01241 if(pchip.HasKeyword("ValidMaximum")) { 01242 reg += PvlKeyword("PatternMaximum", pchip["ValidMaximum"][0]); 01243 } 01244 if(pchip.HasKeyword("MinimumZScore")) { 01245 reg += PvlKeyword("MinimumZScore", pchip["MinimumZScore"][0]); 01246 } 01247 if(pchip.HasKeyword("ValidPercent")) { 01248 SetPatternValidPercent((double)pchip["ValidPercent"]); 01249 reg += PvlKeyword("ValidPercent", pchip["ValidPercent"][0]); 01250 } 01251 01252 PvlGroup &schip = p_template.FindGroup("SearchChip", Pvl::Traverse); 01253 reg += PvlKeyword("SearchSamples", schip["Samples"][0]); 01254 reg += PvlKeyword("SearchLines", schip["Lines"][0]); 01255 if(schip.HasKeyword("ValidMinimum")) { 01256 reg += PvlKeyword("SearchMinimum", schip["ValidMinimum"][0]); 01257 } 01258 if(schip.HasKeyword("ValidMaximum")) { 01259 reg += PvlKeyword("SearchMaximum", schip["ValidMaximum"][0]); 01260 } 01261 if(schip.HasKeyword("SubchipValidPercent")) { 01262 SetSubsearchValidPercent((double)schip["SubchipValidPercent"]); 01263 reg += PvlKeyword("SubchipValidPercent", schip["SubchipValidPercent"][0]); 01264 } 01265 01266 if(p_template.HasGroup("SurfaceModel")) { 01267 PvlGroup &smodel = p_template.FindGroup("SurfaceModel", Pvl::Traverse); 01268 if(smodel.HasKeyword("DistanceTolerance")) { 01269 reg += PvlKeyword("DistanceTolerance", smodel["DistanceTolerance"][0]); 01270 } 01271 01272 if(smodel.HasKeyword("WindowSize")) { 01273 reg += PvlKeyword("WindowSize", smodel["WindowSize"][0]); 01274 } 01275 } 01276 01277 return reg; 01278 } 01279 01280 01292 PvlGroup AutoReg::UpdatedTemplate() { 01293 PvlGroup reg("AutoRegistration"); 01294 01295 reg += PvlKeyword("Algorithm", AlgorithmName()); 01296 reg += PvlKeyword("Tolerance", toString(Tolerance())); 01297 reg += PvlKeyword("SubpixelAccuracy", 01298 SubPixelAccuracy() ? "True" : "False"); 01299 reg += PvlKeyword("ReductionFactor", toString(ReductionFactor())); 01300 reg += PvlKeyword("Gradient", GradientFilterString()); 01301 01302 Chip *pattern = PatternChip(); 01303 reg += PvlKeyword("PatternSamples", toString(pattern->Samples())); 01304 reg += PvlKeyword("PatternLines", toString(pattern->Lines())); 01305 reg += PvlKeyword("MinimumZScore", toString(MinimumZScore())); 01306 reg += PvlKeyword("ValidPercent", toString(PatternValidPercent())); 01307 // TODO Chip needs accessors to valid minimum and maximum 01308 01309 Chip *search = SearchChip(); 01310 reg += PvlKeyword("SearchSamples", toString(search->Samples())); 01311 reg += PvlKeyword("SearchLines", toString(search->Lines())); 01312 reg += PvlKeyword("SubchipValidPercent", toString(SubsearchValidPercent())); 01313 // TODO Chip needs accessors to valid minimum and maximum 01314 01315 if (SubPixelAccuracy()) { 01316 reg += PvlKeyword("DistanceTolerance", toString(DistanceTolerance())); 01317 reg += PvlKeyword("WindowSize", toString(WindowSize())); 01318 } 01319 01320 return reg; 01321 } 01322 }