Loading [MathJax]/jax/output/NativeMML/config.js
Isis 3 Programmer Reference
AutoReg.cpp
1 
6 /* SPDX-License-Identifier: CC0-1.0 */
7 #include "AutoReg.h"
8 #include "Buffer.h"
9 #include "Centroid.h"
10 #include "Chip.h"
11 #include "FileName.h"
12 #include "Histogram.h"
13 #include "IException.h"
14 #include "Interpolator.h"
15 #include "LeastSquares.h"
16 #include "Matrix.h"
17 #include "PixelType.h"
18 #include "Plugin.h"
19 #include "PolynomialBivariate.h"
20 #include "Pvl.h"
21 
22 using namespace std;
23 namespace Isis {
66  AutoReg::AutoReg(Pvl &pvl) {
67  p_template = pvl.findObject("AutoRegistration");
68 
69  // Set default parameters
70  p_patternChip.SetSize(3, 3);
71  p_searchChip.SetSize(5, 5);
72  p_fitChip.SetSize(5, 5);
73  p_reducedPatternChip.SetSize(3, 3);
74  p_reducedSearchChip.SetSize(5, 5);
75  p_reducedFitChip.SetSize(5, 5);
76  p_gradientFilterType = None;
77 
78  SetPatternValidPercent(50.0);
79  SetSubsearchValidPercent(50.0);
80  SetPatternZScoreMinimum(1.0);
81  SetTolerance(Isis::Null);
82 
83  SetSubPixelAccuracy(true);
84  SetSurfaceModelDistanceTolerance(1.5);
85  SetSurfaceModelWindowSize(5);
86 
87  SetReductionFactor(1);
88 
89  // Clear statistics
90  //TODO: Delete these after control net refactor.
91  p_totalRegistrations = 0;
92  p_pixelSuccesses = 0;
93  p_subpixelSuccesses = 0;
94  p_patternChipNotEnoughValidDataCount = 0;
95  p_patternZScoreNotMetCount = 0;
96  p_fitChipNoDataCount = 0;
97  p_fitChipToleranceNotMetCount = 0;
98  p_surfaceModelNotEnoughValidDataCount = 0;
99  p_surfaceModelSolutionInvalidCount = 0;
100  p_surfaceModelDistanceInvalidCount = 0;
101 
102  p_sampMovement = 0.;
103  p_lineMovement = 0.;
104 
105  Init();
106  Parse(pvl);
107  }
108 
114  void AutoReg::Init() {
115  // Set computed parameters to NULL so we don't use values from a previous
116  // run
117  p_zScoreMin = Isis::Null;
118  p_zScoreMax = Isis::Null;
119  p_goodnessOfFit = Isis::Null;
120 
121  p_bestSamp = 0;
122  p_bestLine = 0;
123  p_bestFit = Isis::Null;
124 
125  // --------------------------------------------------
126  // Nulling out the fit chip
127  // --------------------------------------------------
128  for(int line = 1; line <= p_fitChip.Lines(); line++) {
129  for(int samp = 1; samp <= p_fitChip.Samples(); samp++) {
130  p_fitChip.SetValue(samp, line, Isis::Null);
131  }
132  }
133  // --------------------------------------------------
134  // Nulling out the reduced pattern chip
135  // --------------------------------------------------
136  for(int line = 1; line <= p_reducedPatternChip.Lines(); line++) {
137  for(int samp = 1; samp <= p_reducedPatternChip.Samples(); samp++) {
138  p_reducedPatternChip.SetValue(samp, line, Isis::Null);
139  }
140  }
141  // --------------------------------------------------
142  // Nulling out the reduced search chip
143  // --------------------------------------------------
144  for(int line = 1; line <= p_reducedSearchChip.Lines(); line++) {
145  for(int samp = 1; samp <= p_reducedSearchChip.Samples(); samp++) {
146  p_reducedSearchChip.SetValue(samp, line, Isis::Null);
147  }
148  }
149 
150  }
151 
153  AutoReg::~AutoReg() {
154 
155  }
156 
194  void AutoReg::Parse(Pvl &pvl) {
195  try {
196  // Get info from Algorithm group
197  PvlGroup &algo = pvl.findGroup("Algorithm", Pvl::Traverse);
198  SetTolerance(algo["Tolerance"]);
199  if(algo.hasKeyword("ChipInterpolator")) {
200  SetChipInterpolator((QString)algo["ChipInterpolator"]);
201  }
202 
203  if(algo.hasKeyword("SubpixelAccuracy")) {
204  SetSubPixelAccuracy((QString)algo["SubpixelAccuracy"] == "True");
205  }
206 
207  if(algo.hasKeyword("ReductionFactor")) {
208  SetReductionFactor((int)algo["ReductionFactor"]);
209  }
210 
211  if (algo.hasKeyword("Gradient")) {
212  SetGradientFilterType((QString)algo["Gradient"]);
213  }
214 
215  // Setup the pattern chip
216  PvlGroup &pchip = pvl.findGroup("PatternChip", Pvl::Traverse);
217  PatternChip()->SetSize((int)pchip["Samples"], (int)pchip["Lines"]);
218 
219  double minimum = Isis::ValidMinimum;
220  double maximum = Isis::ValidMaximum;
221  if(pchip.hasKeyword("ValidMinimum")) minimum = pchip["ValidMinimum"];
222  if(pchip.hasKeyword("ValidMaximum")) maximum = pchip["ValidMaximum"];
223  PatternChip()->SetValidRange(minimum, maximum);
224 
225  if(pchip.hasKeyword("MinimumZScore")) {
226  SetPatternZScoreMinimum((double)pchip["MinimumZScore"]);
227  }
228  if(pchip.hasKeyword("ValidPercent")) {
229  SetPatternValidPercent((double)pchip["ValidPercent"]);
230  }
231 
232  // Setup the search chip
233  PvlGroup &schip = pvl.findGroup("SearchChip", Pvl::Traverse);
234  SearchChip()->SetSize((int)schip["Samples"], (int)schip["Lines"]);
235 
236  minimum = Isis::ValidMinimum;
237  maximum = Isis::ValidMaximum;
238  if(schip.hasKeyword("ValidMinimum")) minimum = schip["ValidMinimum"];
239  if(schip.hasKeyword("ValidMaximum")) maximum = schip["ValidMaximum"];
240  SearchChip()->SetValidRange(minimum, maximum);
241  if(schip.hasKeyword("SubchipValidPercent")) {
242  SetSubsearchValidPercent((double)schip["SubchipValidPercent"]);
243  }
244 
245  // Setup surface model
246  PvlObject ar = pvl.findObject("AutoRegistration");
247  if(ar.hasGroup("SurfaceModel")) {
248  PvlGroup &smodel = ar.findGroup("SurfaceModel", Pvl::Traverse);
249  if(smodel.hasKeyword("DistanceTolerance")) {
250  SetSurfaceModelDistanceTolerance((double)smodel["DistanceTolerance"]);
251  }
252 
253  if(smodel.hasKeyword("WindowSize")) {
254  SetSurfaceModelWindowSize((int)smodel["WindowSize"]);
255  }
256  }
257 
258  }
259  catch(IException &e) {
260  QString msg = "Improper format for AutoReg PVL [" + pvl.fileName() + "]";
261  throw IException(e, IException::User, msg, _FILEINFO_);
262  }
263  return;
264  }
265 
273  void AutoReg::SetGradientFilterType(const QString &gradientFilterType) {
274  if (gradientFilterType == "None") {
275  p_gradientFilterType = None;
276  }
277  else if (gradientFilterType == "Sobel") {
278  p_gradientFilterType = Sobel;
279  }
280  else {
281  throw IException(IException::User,
282  "Invalid Gradient type. Cannot use ["
283  + gradientFilterType + "] to filter chip",
284  _FILEINFO_);
285  }
286  }
287 
288 
289  QString AutoReg::GradientFilterString() const {
290  switch (p_gradientFilterType) {
291  case None: return "None";
292  case Sobel: return "Sobel";
293  default: throw IException(
294  IException::Programmer,
295  "AutoReg only allows Sobel gradient filter or None",
296  _FILEINFO_);
297  }
298  }
299 
300 
312  void AutoReg::SetSubPixelAccuracy(bool on) {
313  p_subpixelAccuracy = on;
314  }
315 
339  void AutoReg::SetPatternValidPercent(const double percent) {
340  if((percent <= 0.0) || (percent > 100.0)) {
341  string msg = "Invalid value for PatternChip ValidPercent ["
342  + IString(percent)
343  + "]. Must be greater than 0.0 and less than or equal to 100.0 (Default is 50.0).";
344  throw IException(IException::User, msg, _FILEINFO_);
345  }
346  p_patternValidPercent = percent;
347  }
348 
349 
367  void AutoReg::SetSubsearchValidPercent(const double percent) {
368  if((percent <= 0.0) || (percent > 100.0)) {
369  string msg = "Invalid value for SearchChip SubchipValidPercent ["
370  + IString(percent) + "]"
371  + "]. Must be greater than 0.0 and less than or equal to 100.0 (Default is 50.0).";
372  throw IException(IException::User, msg, _FILEINFO_);
373  }
374  p_subsearchValidPercent = percent;
375  }
376 
377 
394  void AutoReg::SetPatternZScoreMinimum(double minimum) {
395  if(minimum <= 0.0) {
396  string msg = "Invalid value for PatternChip MinimumZScore ["
397  + IString(minimum)
398  + "]. Must be greater than 0.0. (Default is 1.0).";
399  throw IException(IException::User, msg, _FILEINFO_);
400  }
401  p_minimumPatternZScore = minimum;
402  }
403 
404 
416  void AutoReg::SetTolerance(const double tolerance) {
417  p_tolerance = tolerance;
418  }
419 
440  void AutoReg::SetChipInterpolator(const QString &interpolator) {
441 
443  if(interpolator == "NearestNeighborType") {
444  itype = Isis::Interpolator::NearestNeighborType;
445  }
446  else if(interpolator == "BiLinearType") {
447  itype = Isis::Interpolator::BiLinearType;
448  }
449  else if(interpolator == "CubicConvolutionType") {
450  itype = Isis::Interpolator::CubicConvolutionType;
451  }
452  else {
453  throw IException(IException::User,
454  "Invalid Interpolator type. Cannot use ["
455  + interpolator + "] to load chip",
456  _FILEINFO_);
457  }
458 
459  // Set pattern and search chips to use this interpolator type when reading data from cube
460  p_patternChip.SetReadInterpolator(itype);
461  p_searchChip.SetReadInterpolator(itype);
462  p_reducedPatternChip.SetReadInterpolator(itype);
463  p_reducedSearchChip.SetReadInterpolator(itype);
464 
465  }
466 
480  void AutoReg::SetSurfaceModelWindowSize(int size) {
481  if(size % 2 != 1 || size < 3) {
482  string msg = "Invalid value for SurfaceModel WindowSize ["
483  + IString(size) + "]. Must be an odd number greater than or equal to 3";
484  throw IException(IException::User, msg, _FILEINFO_);
485  }
486  p_windowSize = size;
487  }
488 
501  void AutoReg::SetSurfaceModelDistanceTolerance(double distance) {
502  if(distance <= 0.0) {
503  string msg = "Invalid value for SurfaceModel DistanceTolerance ["
504  + IString(distance) + "]. Must greater than 0.0.";
505  throw IException(IException::User, msg, _FILEINFO_);
506  }
507  p_distanceTolerance = distance;
508  }
509 
510 
521  void AutoReg::SetReductionFactor(int factor) {
522  if(factor < 1) {
523  string msg = "Invalid value for Algorithm ReductionFactor ["
524  + IString(factor) + "]. Must greater than or equal to 1.";
525  throw IException(IException::User, msg, _FILEINFO_);
526  }
527  p_reduceFactor = factor;
528  }
529 
539  Chip AutoReg::Reduce(Chip &chip, int reductionFactor) {
540  Chip rChip((int)chip.Samples() / reductionFactor,
541  (int)chip.Lines() / reductionFactor);
542  if((int)rChip.Samples() < 1 || (int)rChip.Lines() < 1) {
543  return chip;
544  }
545 
546  // ----------------------------------
547  // Fill the reduced Chip with nulls.
548  // ----------------------------------
549  for(int line = 1; line <= rChip.Lines(); line++) {
550  for(int samp = 1; samp <= rChip.Samples(); samp++) {
551  rChip.SetValue(samp, line, Isis::Null);
552  }
553  }
554 
555  Statistics stats;
556  for(int l = 1; l <= rChip.Lines(); l++) {
557  int istartLine = (l - 1) * reductionFactor + 1;
558  int iendLine = istartLine + reductionFactor - 1;
559  for(int s = 1; s <= rChip.Samples(); s++) {
560 
561  int istartSamp = (s - 1) * reductionFactor + 1;
562  int iendSamp = istartSamp + reductionFactor - 1;
563 
564  stats.Reset();
565  for(int line = istartLine; line < iendLine; line++) {
566  for(int sample = istartSamp; sample < iendSamp; sample++) {
567  stats.AddData(chip.GetValue(sample, line));
568  }
569  }
570  rChip.SetValue(s, l, stats.Average());
571  }
572  }
573  return rChip;
574  }
575 
576 
587  AutoReg::RegisterStatus AutoReg::Register() {
588  // The search chip must be bigger than the pattern chip by N pixels in
589  // both directions for a successful surface model
590  int N = p_windowSize / 2 + 1;
591 
592  if(p_searchChip.Samples() < p_patternChip.Samples() + N) {
593  string msg = "Search chips samples [";
594  msg += IString(p_searchChip.Samples()) + "] must be at ";
595  msg += "least [" + IString(N) + "] pixels wider than the pattern chip samples [";
596  msg += IString(p_patternChip.Samples()) + "] for successful surface modeling";
597  throw IException(IException::User, msg, _FILEINFO_);
598  }
599 
600  if(p_searchChip.Lines() < p_patternChip.Lines() + N) {
601  string msg = "Search chips lines [";
602  msg += IString(p_searchChip.Lines()) + "] must be at ";
603  msg += "least [" + IString(N) + "] pixels taller than the pattern chip lines [";
604  msg += IString(p_patternChip.Lines()) + "] for successful surface modeling";
605  throw IException(IException::User, msg, _FILEINFO_);
606  }
607 
608  Init();
609  p_totalRegistrations++;
610 
611  // Create copies of the search and pattern chips and run a gradient filter
612  // over them before attempting to perform a match. We do this so that
613  // multiple calls to this method won't result in having a gradient filter
614  // applied multiple times to the same chip.
615  Chip gradientPatternChip(p_patternChip);
616  Chip gradientSearchChip(p_searchChip);
617  ApplyGradientFilter(gradientPatternChip);
618  ApplyGradientFilter(gradientSearchChip);
619 
620  // See if the pattern chip has enough good data
621  if(!gradientPatternChip.IsValid(p_patternValidPercent)) {
622  p_patternChipNotEnoughValidDataCount++;
623  p_registrationStatus = PatternChipNotEnoughValidData;
624  return PatternChipNotEnoughValidData;
625  }
626 
627  if(!ComputeChipZScore(gradientPatternChip)) {
628  p_patternZScoreNotMetCount++;
629  p_registrationStatus = PatternZScoreNotMet;
630  return PatternZScoreNotMet;
631  }
632 
652  int startSamp = (gradientPatternChip.Samples() - 1) / 2 + 1;
653  int startLine = (gradientPatternChip.Lines() - 1) / 2 + 1;
654  int endSamp = gradientSearchChip.Samples() - startSamp + 1;
655  int endLine = gradientSearchChip.Lines() - startLine + 1;
656 
657  // ----------------------------------------------------------------------
658  // Before we attempt to apply the reduction factor, we need to make sure
659  // we won't produce a chip of a bad size.
660  // ----------------------------------------------------------------------
661  if (p_reduceFactor != 1) {
662  if(gradientPatternChip.Samples() / p_reduceFactor < 2 || gradientPatternChip.Lines() / p_reduceFactor < 2) {
663  string msg = "Reduction factor is too large";
664  throw IException(IException::User, msg, _FILEINFO_);
665  }
666  }
667 
668  // Establish the center search tack point as best pixel to start for the
669  // adaptive algorithm prior to reduction.
670  int bestSearchSamp = gradientSearchChip.TackSample();
671  int bestSearchLine = gradientSearchChip.TackLine();
672 
673  // ---------------------------------------------------------------------
674  // if the reduction factor is still not equal to one, then we go ahead
675  // with the reduction of the chips and call Match to get the first
676  // estimate of the best line/sample.
677  // ---------------------------------------------------------------------
678  if(p_reduceFactor != 1) {
679  p_reducedPatternChip.SetSize((int)gradientPatternChip.Samples() / p_reduceFactor,
680  (int)gradientPatternChip.Lines() / p_reduceFactor);
681 
682  // ----------------------------------
683  // Fill the reduced Chip with nulls.
684  // ----------------------------------
685  for(int line = 1; line <= p_reducedPatternChip.Lines(); line++) {
686  for(int samp = 1; samp <= p_reducedPatternChip.Samples(); samp++) {
687  p_reducedPatternChip.SetValue(samp, line, Isis::Null);
688  }
689  }
690 
691  p_reducedPatternChip = Reduce(gradientPatternChip, p_reduceFactor);
692  if(!ComputeChipZScore(p_reducedPatternChip)) {
693  p_patternZScoreNotMetCount++;
694  p_registrationStatus = PatternZScoreNotMet;
695  return PatternZScoreNotMet;
696  }
697 
698  p_reducedSearchChip = Reduce(gradientSearchChip, p_reduceFactor);
699  int reducedStartSamp = (p_reducedPatternChip.Samples() - 1) / 2 + 1;
700  int reducedEndSamp = p_reducedSearchChip.Samples() - reducedStartSamp + 1;
701  int reducedStartLine = (p_reducedPatternChip.Lines() - 1) / 2 + 1;
702  int reducedEndLine = p_reducedSearchChip.Lines() - reducedStartLine + 1;
703 
704  Match(p_reducedSearchChip, p_reducedPatternChip, p_reducedFitChip,
705  reducedStartSamp, reducedEndSamp, reducedStartLine, reducedEndLine);
706 
707  if(p_bestFit == Isis::Null) {
708  p_fitChipNoDataCount++;
709  p_registrationStatus = FitChipNoData;
710  return FitChipNoData;
711  }
712 
713  // ------------------------------------------------------
714  // p_bestSamp and p_bestLine are set in Match() which is
715  // called above.
716  // -----------------------------------------------------
717  int bs = (p_bestSamp - 1) * p_reduceFactor + ((p_reduceFactor - 1) / 2) + 1;
718  int bl = (p_bestLine - 1) * p_reduceFactor + ((p_reduceFactor - 1) / 2) + 1;
719 
720  // ---------------------------------------------------------------
721  // Now we grow our window size according to the reduction factor.
722  // And we grow around where the first call Match() told us was the
723  // best line/sample.
724  // ---------------------------------------------------------------
725  int newstartSamp = bs - p_reduceFactor - p_windowSize - 1;
726  int newendSamp = bs + p_reduceFactor + p_windowSize + 1;
727  int newstartLine = bl - p_reduceFactor - p_windowSize - 1;
728  int newendLine = bl + p_reduceFactor + p_windowSize + 1;
729 
730  if(newstartLine < startLine) newstartLine = startLine;
731  if(newendSamp > endSamp) newendSamp = endSamp;
732  if(newstartSamp < startSamp) newstartSamp = startSamp;
733  if(newendLine > endLine) newendLine = endLine;
734 
735  startSamp = newstartSamp;
736  endSamp = newendSamp;
737  startLine = newstartLine;
738  endLine = newendLine;
739  // We have found a good pixel in the reduction chip, but we
740  // don't want to use its position, so reset in prep. for
741  // non-adaptive registration. Save it off for the adaptive algorithm.
742  bestSearchSamp = bs;
743  bestSearchLine = bl;
744  p_bestSamp = 0;
745  p_bestLine = 0;
746  p_bestFit = Isis::Null;
747  }
748 
749  p_registrationStatus = Registration(gradientSearchChip, gradientPatternChip,
750  p_fitChip, startSamp, startLine, endSamp, endLine,
751  bestSearchSamp, bestSearchLine);
752 
753  gradientSearchChip.SetChipPosition(p_chipSample, p_chipLine);
754  p_searchChip.SetChipPosition(p_chipSample, p_chipLine);
755  p_cubeSample = gradientSearchChip.CubeSample();
756  p_cubeLine = gradientSearchChip.CubeLine();
757 
758  // Save off the gradient search and pattern chips if we used a gradient
759  // filter.
760  if (p_gradientFilterType != None) {
761  p_gradientSearchChip = gradientSearchChip;
762  p_gradientPatternChip = gradientPatternChip;
763  }
764 
765  p_goodnessOfFit = p_bestFit;
766 
767  if (Success()) {
768  if (p_registrationStatus == AutoReg::SuccessSubPixel)
769  p_subpixelSuccesses++;
770  else
771  p_pixelSuccesses++;
772  }
773 
774  return p_registrationStatus;
775  }
776 
777 
811  AutoReg::RegisterStatus AutoReg::Registration(Chip &sChip, Chip &pChip,
812  Chip &fChip, int startSamp, int startLine, int endSamp, int endLine,
813  int bestSamp, int bestLine) {
814 
815  // Not adaptive, continue with slower search traverse
816  Match(sChip, pChip, fChip, startSamp, endSamp, startLine, endLine);
817 
818  // Check to see if we went through the fit chip and never got a fit at
819  // any location.
820  if (p_bestFit == Isis::Null) {
821  p_fitChipNoDataCount++;
822  p_registrationStatus = FitChipNoData;
823  return FitChipNoData;
824  }
825 
826  // Now see if we satisified the goodness of fit tolerance
827  if (!CompareFits(p_bestFit, Tolerance())) {
828  p_fitChipToleranceNotMetCount++;
829  p_registrationStatus = FitChipToleranceNotMet;
830  return FitChipToleranceNotMet;
831  }
832 
833  // Try to fit a model for sub-pixel accuracy if necessary
834  if (p_subpixelAccuracy && !IsIdeal(p_bestFit)) {
835  Chip window(p_windowSize, p_windowSize);
836  fChip.Extract(p_bestSamp, p_bestLine, window);
837  window.SetChipPosition(p_windowSize / 2 + 1, p_windowSize / 2 + 1);
838 
839  // Make sure more than 2/3 of the data in the window is valid. Otherwise,
840  // we are likely too close to the edge.
841  if (!window.IsValid(100.0 * 2.1 / 3.0)) {
842  p_surfaceModelNotEnoughValidDataCount++;
843  p_registrationStatus = SurfaceModelNotEnoughValidData;
844  p_chipSample = p_bestSamp;
845  p_chipLine = p_bestLine;
846  return SurfaceModelNotEnoughValidData;
847  }
848 
849  // Now that we know we have enough data to model the surface we call
850  // SetSubpixelPosition() to get the sub-pixel accuracy we are looking for.
851  bool computedSubPixel = SetSubpixelPosition(window);
852  if (!computedSubPixel) {
853  p_chipSample = p_bestSamp;
854  p_chipLine = p_bestLine;
855  p_registrationStatus = SurfaceModelSolutionInvalid;
856  return SurfaceModelSolutionInvalid;
857  }
858 
859  // See if the surface model solution moved too far from our whole pixel
860  // solution
861  p_sampMovement = fabs(p_bestSamp - p_chipSample);
862  p_lineMovement = fabs(p_bestLine - p_chipLine);
863  if (p_sampMovement > p_distanceTolerance ||
864  p_lineMovement > p_distanceTolerance) {
865 
866  p_surfaceModelDistanceInvalidCount++;
867  p_registrationStatus = SurfaceModelDistanceInvalid;
868  p_chipSample = p_bestSamp;
869  p_chipLine = p_bestLine;
870  return SurfaceModelDistanceInvalid;
871  }
872 
873  p_registrationStatus = SuccessSubPixel;
874  return SuccessSubPixel;
875  }
876  else {
877  p_chipSample = p_bestSamp;
878  p_chipLine = p_bestLine;
879  p_registrationStatus = SuccessPixel;
880  return SuccessPixel;
881  }
882  }
883 
884 
895  bool AutoReg::ComputeChipZScore(Chip &chip) {
896  Statistics patternStats;
897  for(int i = 0; i < chip.Samples(); i++) {
898  double pixels[chip.Lines()];
899  for(int j = 0; j < chip.Lines(); j++) {
900  pixels[j] = chip.GetValue(i + 1, j + 1);
901  }
902  patternStats.AddData(pixels, chip.Lines());
903  }
904 
905  // If it does not pass, return
906  p_zScoreMin = patternStats.ZScore(patternStats.Minimum());
907  p_zScoreMax = patternStats.ZScore(patternStats.Maximum());
908 
909  // p_zScoreMin is made negative here so as to make it the equivalent of
910  // taking the absolute value (because p_zScoreMin is guaranteed to be
911  // negative)
912  if (p_zScoreMax < p_minimumPatternZScore && -p_zScoreMin < p_minimumPatternZScore) {
913  return false;
914  }
915  else {
916  return true;
917  }
918  }
919 
927  void AutoReg::ApplyGradientFilter(Chip &chip) {
928  if (p_gradientFilterType == None) {
929  return;
930  }
931 
932  // Use a different subchip size depending on which gradient filter is
933  // being applied.
934  int subChipWidth;
935  if (p_gradientFilterType == Sobel) {
936  subChipWidth = 3;
937  }
938  else {
939  // Perform extra sanity check.
940  string msg =
941  "No rule to set sub-chip width for selected Gradient Filter Type.";
942  throw IException(IException::Programmer, msg, _FILEINFO_);
943  }
944 
945  // Create a new chip to hold output during processing.
946  Chip filteredChip(chip.Samples(), chip.Lines());
947 
948  // Move the subchip through the chip, extracting the contents into a buffer
949  // of the same shape. This simulates the processing of a cube by boxcar,
950  // but since that can only operate on cubes, this functionality had to be
951  // replicated for use on chips.
952  for (int line = 1; line <= chip.Lines(); line++) {
953  for (int sample = 1; sample <= chip.Samples(); sample++) {
954  Chip subChip = chip.Extract(subChipWidth, subChipWidth,
955  sample, line);
956 
957  // Fill a buffer with subchip's contents. Since we'll never be storing
958  // raw bytes in the buffer, we don't care about the pixel type.
959  Buffer buffer(subChipWidth, subChipWidth, 1, Isis::None);
960  double *doubleBuffer = buffer.DoubleBuffer();
961  int bufferIndex = 0;
962  for (int subChipLine = 1; subChipLine <= subChip.Lines();
963  subChipLine++) {
964  for (int subChipSample = 1; subChipSample <= subChip.Samples();
965  subChipSample++) {
966  doubleBuffer[bufferIndex] = subChip.GetValue(subChipSample,
967  subChipLine);
968  bufferIndex++;
969  }
970  }
971 
972  // Calculate gradient based on contents in buffer and insert it into
973  // output chip.
974  double newPixelValue = 0;
975  if (p_gradientFilterType == Sobel) {
976  SobelGradient(buffer, newPixelValue);
977  }
978  filteredChip.SetValue(sample, line, newPixelValue);
979  }
980  }
981 
982  // Copy the data from the filtered chip back into the original chip.
983  for (int line = 1; line <= filteredChip.Lines(); line++) {
984  for (int sample = 1; sample <= filteredChip.Samples(); sample++) {
985  chip.SetValue(sample, line, filteredChip.GetValue(sample, line));
986  }
987  }
988  }
989 
990 
1000  void AutoReg::SobelGradient(Buffer &in, double &v) {
1001  bool specials = false;
1002  for(int i = 0; i < in.size(); ++i) {
1003  if(IsSpecial(in[i])) {
1004  specials = true;
1005  }
1006  }
1007  if(specials) {
1008  v = Isis::Null;
1009  return;
1010  }
1011  v = abs((in[0] + 2 * in[1] + in[2]) - (in[6] + 2 * in[7] + in[8])) +
1012  abs((in[2] + 2 * in[5] + in[8]) - (in[0] + 2 * in[3] + in[6]));
1013  }
1014 
1031  void AutoReg::Match(Chip &sChip, Chip &pChip, Chip &fChip, int startSamp, int endSamp, int startLine, int endLine) {
1032  // Sanity check. Should have been caught by the two previous tests
1033  if(startSamp == endSamp && startLine == endLine) {
1034  string msg = "StartSample [" + IString(startSamp) + "] = EndSample ["
1035  + IString(endSamp) + "] and StartLine [" + IString(startLine) + " = EndLine ["
1036  + IString(endLine) + "].";
1037  throw IException(IException::Programmer, msg, _FILEINFO_);
1038  }
1039 
1040  // Ok create a fit chip whose size is the same as the search chip
1041  // and then fill it with nulls
1042  fChip.SetSize(sChip.Samples(), sChip.Lines());
1043  for(int line = 1; line <= fChip.Lines(); line++) {
1044  for(int samp = 1; samp <= fChip.Samples(); samp++) {
1045  fChip.SetValue(samp, line, Isis::Null);
1046  }
1047  }
1048 
1049  // Create a chip the same size as the pattern chip.
1050  Chip subsearch(pChip.Samples(), pChip.Lines());
1051 
1052  for(int line = startLine; line <= endLine; line++) {
1053  for(int samp = startSamp; samp <= endSamp; samp++) {
1054  // Extract the subsearch chip and make sure it has enough valid data
1055  sChip.Extract(samp, line, subsearch);
1056 
1057 // if(!subsearch.IsValid(p_patternValidPercent)) continue;
1058  if(!subsearch.IsValid(p_subsearchValidPercent)) continue;
1059 
1060  // Try to match the two subchips
1061  double fit = MatchAlgorithm(pChip, subsearch);
1062 
1063  // If we had a fit save off information about that fit
1064  if(fit != Isis::Null) {
1065  fChip.SetValue(samp, line, fit);
1066  if((p_bestFit == Isis::Null) || CompareFits(fit, p_bestFit)) {
1067  p_bestFit = fit;
1068  p_bestSamp = samp;
1069  p_bestLine = line;
1070  }
1071  }
1072  }
1073  }
1074  }
1075 
1076 
1087  bool AutoReg::SetSubpixelPosition(Chip &window) {
1088  // The best correlation will be at the center of the window
1089  // if it's smaller than the edge DN's invert the chip DNs
1090  double samples = window.Samples();
1091  double lines= window.Lines();
1092  double bestDN = window.GetValue(window.ChipSample(), window.ChipLine());
1093  if (bestDN < window.GetValue(1, 1)) {
1094  for (int s=1; s <= samples; s++)
1095  for (int l=1; l <= lines; l++)
1096  window.SetValue(s, l, 1.0/window.GetValue(s, l)); //invert all the window DN's
1097  bestDN = 1 / bestDN;
1098  }
1099 
1100  // Find the greatest edge DN
1101  double greatestEdgeDn = 0.0;
1102  for (int s = 1; s <= samples; s++) {
1103  greatestEdgeDn = max(window.GetValue(s, 1), greatestEdgeDn);
1104  greatestEdgeDn = max(window.GetValue(s, lines), greatestEdgeDn);
1105  }
1106  for (int l = 2; l <= lines - 1; l++) {
1107  greatestEdgeDn = max(window.GetValue(1, l), greatestEdgeDn);
1108  greatestEdgeDn = max(window.GetValue(samples, l), greatestEdgeDn);
1109  }
1110 
1111  //This is a small shift so the the centroid doesn't reach the edge, add 20%
1112  // of the difference between the hightest edge DN and the max DN to the highest edge DN
1113  //The 20% shift added here is somewhat arbitrary, but was choosen because it worked well
1114  // for the maximum correlation tests we did. For new area based algorithms we may want
1115  // to revist this. Possible make it a function of the match type
1116  double temp = greatestEdgeDn + 0.2 * (bestDN - greatestEdgeDn);
1117 
1118  Centroid floodFill;
1119  floodFill.setDNRange(temp, 1e100);
1120 
1121  Chip selectionChip(window);
1122  floodFill.select(&window, &selectionChip);
1123 
1124  double windowSample;
1125  double windowLine;
1126  floodFill.centerOfMassWeighted(
1127  &window, &selectionChip, &windowSample, &windowLine);
1128 
1129  int offsetS = p_bestSamp - window.ChipSample();
1130  int offsetL = p_bestLine - window.ChipLine();
1131  p_chipSample = windowSample + offsetS;
1132  p_chipLine = windowLine + offsetL;
1133 
1134  if (p_chipSample != p_chipSample) {
1135  p_surfaceModelSolutionInvalidCount++;
1136  return false; //this should never happen, but just in case...
1137  }
1138 
1139  return true;
1140  }
1141 
1142 
1152  bool AutoReg::CompareFits(double fit1, double fit2) {
1153  return(std::fabs(fit1 - IdealFit()) <= std::fabs(fit2 - IdealFit()));
1154  }
1155 
1162  bool AutoReg::IsIdeal(double fit) {
1163  return(std::fabs(IdealFit() - fit) < 0.00001);
1164  }
1165 
1166 
1177  Pvl AutoReg::RegistrationStatistics() {
1178  Pvl pvl;
1179  PvlGroup stats("AutoRegStatistics");
1180  stats += Isis::PvlKeyword("Total", toString(p_totalRegistrations));
1181  stats += Isis::PvlKeyword("Successful", toString(p_pixelSuccesses + p_subpixelSuccesses));
1182  stats += Isis::PvlKeyword("Failure", toString(p_totalRegistrations - (p_pixelSuccesses + p_subpixelSuccesses)));
1183  pvl.addGroup(stats);
1184 
1185  PvlGroup successes("Successes");
1186  successes += PvlKeyword("SuccessPixel", toString(p_pixelSuccesses));
1187  successes += PvlKeyword("SuccessSubPixel", toString(p_subpixelSuccesses));
1188  pvl.addGroup(successes);
1189 
1190  PvlGroup grp("PatternChipFailures");
1191  grp += PvlKeyword("PatternNotEnoughValidData", toString(p_patternChipNotEnoughValidDataCount));
1192  grp += PvlKeyword("PatternZScoreNotMet", toString(p_patternZScoreNotMetCount));
1193  pvl.addGroup(grp);
1194 
1195  PvlGroup fit("FitChipFailures");
1196  fit += PvlKeyword("FitChipNoData", toString(p_fitChipNoDataCount));
1197  fit += PvlKeyword("FitChipToleranceNotMet", toString(p_fitChipToleranceNotMetCount));
1198  pvl.addGroup(fit);
1199 
1200  PvlGroup model("SurfaceModelFailures");
1201  model += PvlKeyword("SurfaceModelNotEnoughValidData", toString(p_surfaceModelNotEnoughValidDataCount));
1202  model += PvlKeyword("SurfaceModelSolutionInvalid", toString(p_surfaceModelSolutionInvalidCount));
1203  model += PvlKeyword("SurfaceModelDistanceInvalid", toString(p_surfaceModelDistanceInvalidCount));
1204  pvl.addGroup(model);
1205 
1206  return (AlgorithmStatistics(pvl));
1207  }
1208 
1216  PvlGroup AutoReg::RegTemplate() {
1217  PvlGroup reg("AutoRegistration");
1218 
1219  PvlGroup &algo = p_template.findGroup("Algorithm", Pvl::Traverse);
1220  reg += PvlKeyword("Algorithm", algo["Name"][0]);
1221  reg += PvlKeyword("Tolerance", algo["Tolerance"][0]);
1222  if(algo.hasKeyword("SubpixelAccuracy")) {
1223  reg += PvlKeyword("SubpixelAccuracy", algo["SubpixelAccuracy"][0]);
1224  }
1225  if(algo.hasKeyword("ReductionFactor")) {
1226  reg += PvlKeyword("ReductionFactor", algo["ReductionFactor"][0]);
1227  }
1228  if(algo.hasKeyword("Gradient")) {
1229  reg += PvlKeyword("Gradient", algo["Gradient"][0]);
1230  }
1231 
1232  PvlGroup &pchip = p_template.findGroup("PatternChip", Pvl::Traverse);
1233  reg += PvlKeyword("PatternSamples", pchip["Samples"][0]);
1234  reg += PvlKeyword("PatternLines", pchip["Lines"][0]);
1235  if(pchip.hasKeyword("ValidMinimum")) {
1236  reg += PvlKeyword("PatternMinimum", pchip["ValidMinimum"][0]);
1237  }
1238  if(pchip.hasKeyword("ValidMaximum")) {
1239  reg += PvlKeyword("PatternMaximum", pchip["ValidMaximum"][0]);
1240  }
1241  if(pchip.hasKeyword("MinimumZScore")) {
1242  reg += PvlKeyword("MinimumZScore", pchip["MinimumZScore"][0]);
1243  }
1244  if(pchip.hasKeyword("ValidPercent")) {
1245  SetPatternValidPercent((double)pchip["ValidPercent"]);
1246  reg += PvlKeyword("ValidPercent", pchip["ValidPercent"][0]);
1247  }
1248 
1249  PvlGroup &schip = p_template.findGroup("SearchChip", Pvl::Traverse);
1250  reg += PvlKeyword("SearchSamples", schip["Samples"][0]);
1251  reg += PvlKeyword("SearchLines", schip["Lines"][0]);
1252  if(schip.hasKeyword("ValidMinimum")) {
1253  reg += PvlKeyword("SearchMinimum", schip["ValidMinimum"][0]);
1254  }
1255  if(schip.hasKeyword("ValidMaximum")) {
1256  reg += PvlKeyword("SearchMaximum", schip["ValidMaximum"][0]);
1257  }
1258  if(schip.hasKeyword("SubchipValidPercent")) {
1259  SetSubsearchValidPercent((double)schip["SubchipValidPercent"]);
1260  reg += PvlKeyword("SubchipValidPercent", schip["SubchipValidPercent"][0]);
1261  }
1262 
1263  if(p_template.hasGroup("SurfaceModel")) {
1264  PvlGroup &smodel = p_template.findGroup("SurfaceModel", Pvl::Traverse);
1265  if(smodel.hasKeyword("DistanceTolerance")) {
1266  reg += PvlKeyword("DistanceTolerance", smodel["DistanceTolerance"][0]);
1267  }
1268 
1269  if(smodel.hasKeyword("WindowSize")) {
1270  reg += PvlKeyword("WindowSize", smodel["WindowSize"][0]);
1271  }
1272  }
1273 
1274  return reg;
1275  }
1276 
1277 
1289  PvlGroup AutoReg::UpdatedTemplate() {
1290  PvlGroup reg("AutoRegistration");
1291 
1292  reg += PvlKeyword("Algorithm", AlgorithmName());
1293  reg += PvlKeyword("Tolerance", toString(Tolerance()));
1294  reg += PvlKeyword("SubpixelAccuracy",
1295  SubPixelAccuracy() ? "True" : "False");
1296  reg += PvlKeyword("ReductionFactor", toString(ReductionFactor()));
1297  reg += PvlKeyword("Gradient", GradientFilterString());
1298 
1299  Chip *pattern = PatternChip();
1300  reg += PvlKeyword("PatternSamples", toString(pattern->Samples()));
1301  reg += PvlKeyword("PatternLines", toString(pattern->Lines()));
1302  reg += PvlKeyword("MinimumZScore", toString(MinimumZScore()));
1303  reg += PvlKeyword("ValidPercent", toString(PatternValidPercent()));
1304  // TODO Chip needs accessors to valid minimum and maximum
1305 
1306  Chip *search = SearchChip();
1307  reg += PvlKeyword("SearchSamples", toString(search->Samples()));
1308  reg += PvlKeyword("SearchLines", toString(search->Lines()));
1309  reg += PvlKeyword("SubchipValidPercent", toString(SubsearchValidPercent()));
1310  // TODO Chip needs accessors to valid minimum and maximum
1311 
1312  if (SubPixelAccuracy()) {
1313  reg += PvlKeyword("DistanceTolerance", toString(DistanceTolerance()));
1314  reg += PvlKeyword("WindowSize", toString(WindowSize()));
1315  }
1316 
1317  return reg;
1318  }
1319 }
1320 
Isis::ValidMaximum
const double ValidMaximum
The maximum valid double value for Isis pixels.
Definition: SpecialPixel.h:122
Isis::Statistics
This class is used to accumulate statistics on double arrays.
Definition: Statistics.h:94
Isis::PvlObject::findGroup
PvlGroupIterator findGroup(const QString &name, PvlGroupIterator beg, PvlGroupIterator end)
Find a group with the specified name, within these indexes.
Definition: PvlObject.h:129
Isis::Statistics::AddData
void AddData(const double *data, const unsigned int count)
Add an array of doubles to the accumulators and counters.
Definition: Statistics.cpp:141
Isis::PvlObject
Contains Pvl Groups and Pvl Objects.
Definition: PvlObject.h:61
Isis::Centroid
Selection class derived from the Pure Virtual Parent Class for all Selection classes.
Definition: Centroid.h:31
Isis::PvlKeyword
A single keyword-value pair.
Definition: PvlKeyword.h:82
Isis::Chip::SetSize
void SetSize(const int samples, const int lines)
Change the size of the Chip.
Definition: Chip.cpp:134
Isis::Buffer::DoubleBuffer
double * DoubleBuffer() const
Returns the value of the shape buffer.
Definition: Buffer.h:138
Isis::Chip::GetValue
double GetValue(int sample, int line)
Loads a Chip with a value.
Definition: Chip.h:145
Isis::Statistics::Maximum
double Maximum() const
Returns the absolute maximum double found in all data passed through the AddData method.
Definition: Statistics.cpp:403
Isis::PvlObject::hasGroup
bool hasGroup(const QString &name) const
Returns a boolean value based on whether the object has the specified group or not.
Definition: PvlObject.h:210
Isis::Statistics::Reset
void Reset()
Reset all accumulators and counters to zero.
Definition: Statistics.cpp:113
Isis::Chip::ChipSample
double ChipSample() const
Definition: Chip.h:219
Isis::PvlContainer::hasKeyword
bool hasKeyword(const QString &name) const
Check to see if a keyword exists.
Definition: PvlContainer.cpp:159
Isis::Pvl
Container for cube-like labels.
Definition: Pvl.h:119
Isis::Chip::CubeSample
double CubeSample() const
Definition: Chip.h:203
Isis::Chip::Extract
Chip Extract(int samples, int lines, int samp, int line)
Extract a sub-chip from a chip.
Definition: Chip.cpp:727
Isis::Chip::SetValue
void SetValue(int sample, int line, const double &value)
Sets a value in the chip.
Definition: Chip.h:126
Isis::toString
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition: IString.cpp:211
Isis::IsSpecial
bool IsSpecial(const double d)
Returns if the input pixel is special.
Definition: SpecialPixel.h:197
Isis::Buffer
Buffer for reading and writing cube data.
Definition: Buffer.h:53
Isis::Chip::TackSample
int TackSample() const
This method returns a chip's fixed tack sample; the middle of the chip.
Definition: Chip.h:176
Isis::Chip::CubeLine
double CubeLine() const
Definition: Chip.h:210
Isis::Chip::Lines
int Lines() const
Definition: Chip.h:106
Isis::PvlGroup
Contains multiple PvlContainers.
Definition: PvlGroup.h:41
Isis::Chip::TackLine
int TackLine() const
This method returns a chip's fixed tack line; the middle of the chip.
Definition: Chip.h:187
Isis::Interpolator::interpType
interpType
The interpolator type, including: None, Nearest Neighbor, BiLinear or Cubic Convultion.
Definition: Interpolator.h:40
Isis::Chip::ChipLine
double ChipLine() const
Definition: Chip.h:226
Isis::Statistics::Minimum
double Minimum() const
Returns the absolute minimum double found in all data passed through the AddData method.
Definition: Statistics.cpp:382
Isis::Reduce
Reduce the pixel dimensions of an image.
Definition: Reduce.h:47
Isis::PvlObject::findObject
PvlObjectIterator findObject(const QString &name, PvlObjectIterator beg, PvlObjectIterator end)
Find the index of object with a specified name, between two indexes.
Definition: PvlObject.h:274
Isis::Statistics::ZScore
double ZScore(const double value) const
This method returns the better of the z-score of the given value.
Definition: Statistics.cpp:649
Isis::PvlContainer::fileName
QString fileName() const
Returns the filename used to initialise the Pvl object.
Definition: PvlContainer.h:232
Isis::IException
Isis exception class.
Definition: IException.h:91
Isis::AutoReg::RegisterStatus
RegisterStatus
Enumeration of the Register() method's return status.
Definition: AutoReg.h:179
Isis::Null
const double Null
Value for an Isis Null pixel.
Definition: SpecialPixel.h:95
Isis::PvlObject::addGroup
void addGroup(const Isis::PvlGroup &group)
Add a group to the object.
Definition: PvlObject.h:186
Isis::Statistics::Average
double Average() const
Computes and returns the average.
Definition: Statistics.cpp:300
Isis::ValidMinimum
const double ValidMinimum
The minimum valid double value for Isis pixels.
Definition: SpecialPixel.h:87
Isis::Chip::IsValid
bool IsValid(int sample, int line)
Definition: Chip.h:240
std
Namespace for the standard library.
Isis::Centroid::setDNRange
int setDNRange(double minimumDN, double maximumDN)
Set the range of the DNs.
Definition: Centroid.cpp:126
Isis::Chip
A small chip of data used for pattern matching.
Definition: Chip.h:86
Isis::Chip::Samples
int Samples() const
Definition: Chip.h:99
Isis::Centroid::select
int select(Chip *inputChip, Chip *selectionChip)
Given a range of DN this function creates a biniary chip for all continuous pixels that have the DN w...
Definition: Centroid.cpp:30
Isis::Buffer::size
int size() const
Returns the total number of pixels in the shape buffer.
Definition: Buffer.h:97
Isis::IString
Adds specific functionality to C++ strings.
Definition: IString.h:165
Isis::Chip::SetChipPosition
void SetChipPosition(const double sample, const double line)
Compute the position of the cube given a chip coordinate.
Definition: Chip.cpp:643
Isis
This is free and unencumbered software released into the public domain.
Definition: Apollo.h:16

U.S. Department of the Interior | U.S. Geological Survey
ISIS | Privacy & Disclaimers | Astrogeology Research Program
To contact us, please post comments and questions on the USGS Astrogeology Discussion Board
To report a bug, or suggest a feature go to: ISIS Github
File Modified: 07/13/2023 15:16:08