USGS

Isis 3.0 Object Programmers' Reference

Home

AutoReg.cpp

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 }