USGS

Isis 3.0 Object Programmers' Reference

Home

AutoReg.cpp
1 
20 #include "AutoReg.h"
21 #include "Buffer.h"
22 #include "Centroid.h"
23 #include "Chip.h"
24 #include "FileName.h"
25 #include "Histogram.h"
26 #include "IException.h"
27 #include "Interpolator.h"
28 #include "LeastSquares.h"
29 #include "Matrix.h"
30 #include "PixelType.h"
31 #include "Plugin.h"
32 #include "PolynomialBivariate.h"
33 #include "Pvl.h"
34 
35 using namespace std;
36 namespace Isis {
79  AutoReg::AutoReg(Pvl &pvl) {
80  p_template = pvl.findObject("AutoRegistration");
81 
82  // Set default parameters
83  p_patternChip.SetSize(3, 3);
84  p_searchChip.SetSize(5, 5);
85  p_fitChip.SetSize(5, 5);
86  p_reducedPatternChip.SetSize(3, 3);
87  p_reducedSearchChip.SetSize(5, 5);
88  p_reducedFitChip.SetSize(5, 5);
89  p_gradientFilterType = None;
90 
91  SetPatternValidPercent(50.0);
92  SetSubsearchValidPercent(50.0);
93  SetPatternZScoreMinimum(1.0);
94  SetTolerance(Isis::Null);
95 
96  SetSubPixelAccuracy(true);
97  SetSurfaceModelDistanceTolerance(1.5);
98  SetSurfaceModelWindowSize(5);
99 
100  SetReductionFactor(1);
101 
102  // Clear statistics
103  //TODO: Delete these after control net refactor.
104  p_totalRegistrations = 0;
105  p_pixelSuccesses = 0;
106  p_subpixelSuccesses = 0;
107  p_patternChipNotEnoughValidDataCount = 0;
108  p_patternZScoreNotMetCount = 0;
109  p_fitChipNoDataCount = 0;
110  p_fitChipToleranceNotMetCount = 0;
111  p_surfaceModelNotEnoughValidDataCount = 0;
112  p_surfaceModelSolutionInvalidCount = 0;
113  p_surfaceModelDistanceInvalidCount = 0;
114 
115  p_sampMovement = 0.;
116  p_lineMovement = 0.;
117 
118  Init();
119  Parse(pvl);
120  }
121 
127  void AutoReg::Init() {
128  // Set computed parameters to NULL so we don't use values from a previous
129  // run
130  p_zScoreMin = Isis::Null;
131  p_zScoreMax = Isis::Null;
132  p_goodnessOfFit = Isis::Null;
133 
134  p_bestSamp = 0;
135  p_bestLine = 0;
136  p_bestFit = Isis::Null;
137 
138  // --------------------------------------------------
139  // Nulling out the fit chip
140  // --------------------------------------------------
141  for(int line = 1; line <= p_fitChip.Lines(); line++) {
142  for(int samp = 1; samp <= p_fitChip.Samples(); samp++) {
143  p_fitChip.SetValue(samp, line, Isis::Null);
144  }
145  }
146  // --------------------------------------------------
147  // Nulling out the reduced pattern chip
148  // --------------------------------------------------
149  for(int line = 1; line <= p_reducedPatternChip.Lines(); line++) {
150  for(int samp = 1; samp <= p_reducedPatternChip.Samples(); samp++) {
151  p_reducedPatternChip.SetValue(samp, line, Isis::Null);
152  }
153  }
154  // --------------------------------------------------
155  // Nulling out the reduced search chip
156  // --------------------------------------------------
157  for(int line = 1; line <= p_reducedSearchChip.Lines(); line++) {
158  for(int samp = 1; samp <= p_reducedSearchChip.Samples(); samp++) {
159  p_reducedSearchChip.SetValue(samp, line, Isis::Null);
160  }
161  }
162 
163  }
164 
166  AutoReg::~AutoReg() {
167 
168  }
169 
207  void AutoReg::Parse(Pvl &pvl) {
208  try {
209  // Get info from Algorithm group
210  PvlGroup &algo = pvl.findGroup("Algorithm", Pvl::Traverse);
211  SetTolerance(algo["Tolerance"]);
212  if(algo.hasKeyword("ChipInterpolator")) {
213  SetChipInterpolator((QString)algo["ChipInterpolator"]);
214  }
215 
216  if(algo.hasKeyword("SubpixelAccuracy")) {
217  SetSubPixelAccuracy((QString)algo["SubpixelAccuracy"] == "True");
218  }
219 
220  if(algo.hasKeyword("ReductionFactor")) {
221  SetReductionFactor((int)algo["ReductionFactor"]);
222  }
223 
224  if (algo.hasKeyword("Gradient")) {
225  SetGradientFilterType((QString)algo["Gradient"]);
226  }
227 
228  // Setup the pattern chip
229  PvlGroup &pchip = pvl.findGroup("PatternChip", Pvl::Traverse);
230  PatternChip()->SetSize((int)pchip["Samples"], (int)pchip["Lines"]);
231 
232  double minimum = Isis::ValidMinimum;
233  double maximum = Isis::ValidMaximum;
234  if(pchip.hasKeyword("ValidMinimum")) minimum = pchip["ValidMinimum"];
235  if(pchip.hasKeyword("ValidMaximum")) maximum = pchip["ValidMaximum"];
236  PatternChip()->SetValidRange(minimum, maximum);
237 
238  if(pchip.hasKeyword("MinimumZScore")) {
239  SetPatternZScoreMinimum((double)pchip["MinimumZScore"]);
240  }
241  if(pchip.hasKeyword("ValidPercent")) {
242  SetPatternValidPercent((double)pchip["ValidPercent"]);
243  }
244 
245  // Setup the search chip
246  PvlGroup &schip = pvl.findGroup("SearchChip", Pvl::Traverse);
247  SearchChip()->SetSize((int)schip["Samples"], (int)schip["Lines"]);
248 
249  minimum = Isis::ValidMinimum;
250  maximum = Isis::ValidMaximum;
251  if(schip.hasKeyword("ValidMinimum")) minimum = schip["ValidMinimum"];
252  if(schip.hasKeyword("ValidMaximum")) maximum = schip["ValidMaximum"];
253  SearchChip()->SetValidRange(minimum, maximum);
254  if(schip.hasKeyword("SubchipValidPercent")) {
255  SetSubsearchValidPercent((double)schip["SubchipValidPercent"]);
256  }
257 
258  // Setup surface model
259  PvlObject ar = pvl.findObject("AutoRegistration");
260  if(ar.hasGroup("SurfaceModel")) {
261  PvlGroup &smodel = ar.findGroup("SurfaceModel", Pvl::Traverse);
262  if(smodel.hasKeyword("DistanceTolerance")) {
263  SetSurfaceModelDistanceTolerance((double)smodel["DistanceTolerance"]);
264  }
265 
266  if(smodel.hasKeyword("WindowSize")) {
267  SetSurfaceModelWindowSize((int)smodel["WindowSize"]);
268  }
269  }
270 
271  }
272  catch(IException &e) {
273  QString msg = "Improper format for AutoReg PVL [" + pvl.fileName() + "]";
274  throw IException(e, IException::User, msg, _FILEINFO_);
275  }
276  return;
277  }
278 
286  void AutoReg::SetGradientFilterType(const QString &gradientFilterType) {
287  if (gradientFilterType == "None") {
288  p_gradientFilterType = None;
289  }
290  else if (gradientFilterType == "Sobel") {
291  p_gradientFilterType = Sobel;
292  }
293  else {
294  throw IException(IException::User,
295  "Invalid Gradient type. Cannot use ["
296  + gradientFilterType + "] to filter chip",
297  _FILEINFO_);
298  }
299  }
300 
301 
302  QString AutoReg::GradientFilterString() const {
303  switch (p_gradientFilterType) {
304  case None: return "None";
305  case Sobel: return "Sobel";
306  default: throw IException(
307  IException::Programmer,
308  "AutoReg only allows Sobel gradient filter or None",
309  _FILEINFO_);
310  }
311  }
312 
313 
325  void AutoReg::SetSubPixelAccuracy(bool on) {
326  p_subpixelAccuracy = on;
327  }
328 
352  void AutoReg::SetPatternValidPercent(const double percent) {
353  if((percent <= 0.0) || (percent > 100.0)) {
354  string msg = "Invalid value for PatternChip ValidPercent ["
355  + IString(percent)
356  + "]. Must be greater than 0.0 and less than or equal to 100.0 (Default is 50.0).";
357  throw IException(IException::User, msg, _FILEINFO_);
358  }
359  p_patternValidPercent = percent;
360  }
361 
362 
380  void AutoReg::SetSubsearchValidPercent(const double percent) {
381  if((percent <= 0.0) || (percent > 100.0)) {
382  string msg = "Invalid value for SearchChip SubchipValidPercent ["
383  + IString(percent) + "]"
384  + "]. Must be greater than 0.0 and less than or equal to 100.0 (Default is 50.0).";
385  throw IException(IException::User, msg, _FILEINFO_);
386  }
387  p_subsearchValidPercent = percent;
388  }
389 
390 
407  void AutoReg::SetPatternZScoreMinimum(double minimum) {
408  if(minimum <= 0.0) {
409  string msg = "Invalid value for PatternChip MinimumZScore ["
410  + IString(minimum)
411  + "]. Must be greater than 0.0. (Default is 1.0).";
412  throw IException(IException::User, msg, _FILEINFO_);
413  }
414  p_minimumPatternZScore = minimum;
415  }
416 
417 
429  void AutoReg::SetTolerance(const double tolerance) {
430  p_tolerance = tolerance;
431  }
432 
453  void AutoReg::SetChipInterpolator(const QString &interpolator) {
454 
456  if(interpolator == "NearestNeighborType") {
457  itype = Isis::Interpolator::NearestNeighborType;
458  }
459  else if(interpolator == "BiLinearType") {
460  itype = Isis::Interpolator::BiLinearType;
461  }
462  else if(interpolator == "CubicConvolutionType") {
463  itype = Isis::Interpolator::CubicConvolutionType;
464  }
465  else {
466  throw IException(IException::User,
467  "Invalid Interpolator type. Cannot use ["
468  + interpolator + "] to load chip",
469  _FILEINFO_);
470  }
471 
472  // Set pattern and search chips to use this interpolator type when reading data from cube
473  p_patternChip.SetReadInterpolator(itype);
474  p_searchChip.SetReadInterpolator(itype);
475  p_reducedPatternChip.SetReadInterpolator(itype);
476  p_reducedSearchChip.SetReadInterpolator(itype);
477 
478  }
479 
493  void AutoReg::SetSurfaceModelWindowSize(int size) {
494  if(size % 2 != 1 || size < 3) {
495  string msg = "Invalid value for SurfaceModel WindowSize ["
496  + IString(size) + "]. Must be an odd number greater than or equal to 3";
497  throw IException(IException::User, msg, _FILEINFO_);
498  }
499  p_windowSize = size;
500  }
501 
514  void AutoReg::SetSurfaceModelDistanceTolerance(double distance) {
515  if(distance <= 0.0) {
516  string msg = "Invalid value for SurfaceModel DistanceTolerance ["
517  + IString(distance) + "]. Must greater than 0.0.";
518  throw IException(IException::User, msg, _FILEINFO_);
519  }
520  p_distanceTolerance = distance;
521  }
522 
523 
534  void AutoReg::SetReductionFactor(int factor) {
535  if(factor < 1) {
536  string msg = "Invalid value for Algorithm ReductionFactor ["
537  + IString(factor) + "]. Must greater than or equal to 1.";
538  throw IException(IException::User, msg, _FILEINFO_);
539  }
540  p_reduceFactor = factor;
541  }
542 
552  Chip AutoReg::Reduce(Chip &chip, int reductionFactor) {
553  Chip rChip((int)chip.Samples() / reductionFactor,
554  (int)chip.Lines() / reductionFactor);
555  if((int)rChip.Samples() < 1 || (int)rChip.Lines() < 1) {
556  return chip;
557  }
558 
559  // ----------------------------------
560  // Fill the reduced Chip with nulls.
561  // ----------------------------------
562  for(int line = 1; line <= rChip.Lines(); line++) {
563  for(int samp = 1; samp <= rChip.Samples(); samp++) {
564  rChip.SetValue(samp, line, Isis::Null);
565  }
566  }
567 
568  Statistics stats;
569  for(int l = 1; l <= rChip.Lines(); l++) {
570  int istartLine = (l - 1) * reductionFactor + 1;
571  int iendLine = istartLine + reductionFactor - 1;
572  for(int s = 1; s <= rChip.Samples(); s++) {
573 
574  int istartSamp = (s - 1) * reductionFactor + 1;
575  int iendSamp = istartSamp + reductionFactor - 1;
576 
577  stats.Reset();
578  for(int line = istartLine; line < iendLine; line++) {
579  for(int sample = istartSamp; sample < iendSamp; sample++) {
580  stats.AddData(chip.GetValue(sample, line));
581  }
582  }
583  rChip.SetValue(s, l, stats.Average());
584  }
585  }
586  return rChip;
587  }
588 
589 
600  AutoReg::RegisterStatus AutoReg::Register() {
601  // The search chip must be bigger than the pattern chip by N pixels in
602  // both directions for a successful surface model
603  int N = p_windowSize / 2 + 1;
604 
605  if(p_searchChip.Samples() < p_patternChip.Samples() + N) {
606  string msg = "Search chips samples [";
607  msg += IString(p_searchChip.Samples()) + "] must be at ";
608  msg += "least [" + IString(N) + "] pixels wider than the pattern chip samples [";
609  msg += IString(p_patternChip.Samples()) + "] for successful surface modeling";
610  throw IException(IException::User, msg, _FILEINFO_);
611  }
612 
613  if(p_searchChip.Lines() < p_patternChip.Lines() + N) {
614  string msg = "Search chips lines [";
615  msg += IString(p_searchChip.Lines()) + "] must be at ";
616  msg += "least [" + IString(N) + "] pixels taller than the pattern chip lines [";
617  msg += IString(p_patternChip.Lines()) + "] for successful surface modeling";
618  throw IException(IException::User, msg, _FILEINFO_);
619  }
620 
621  Init();
622  p_totalRegistrations++;
623 
624  // Create copies of the search and pattern chips and run a gradient filter
625  // over them before attempting to perform a match. We do this so that
626  // multiple calls to this method won't result in having a gradient filter
627  // applied multiple times to the same chip.
628  Chip gradientPatternChip(p_patternChip);
629  Chip gradientSearchChip(p_searchChip);
630  ApplyGradientFilter(gradientPatternChip);
631  ApplyGradientFilter(gradientSearchChip);
632 
633  // See if the pattern chip has enough good data
634  if(!gradientPatternChip.IsValid(p_patternValidPercent)) {
635  p_patternChipNotEnoughValidDataCount++;
636  p_registrationStatus = PatternChipNotEnoughValidData;
637  return PatternChipNotEnoughValidData;
638  }
639 
640  if(!ComputeChipZScore(gradientPatternChip)) {
641  p_patternZScoreNotMetCount++;
642  p_registrationStatus = PatternZScoreNotMet;
643  return PatternZScoreNotMet;
644  }
645 
665  int startSamp = (gradientPatternChip.Samples() - 1) / 2 + 1;
666  int startLine = (gradientPatternChip.Lines() - 1) / 2 + 1;
667  int endSamp = gradientSearchChip.Samples() - startSamp + 1;
668  int endLine = gradientSearchChip.Lines() - startLine + 1;
669 
670  // ----------------------------------------------------------------------
671  // Before we attempt to apply the reduction factor, we need to make sure
672  // we won't produce a chip of a bad size.
673  // ----------------------------------------------------------------------
674  if(gradientPatternChip.Samples() / p_reduceFactor < 2 || gradientPatternChip.Lines() / p_reduceFactor < 2) {
675  string msg = "Reduction factor is too large";
676  throw IException(IException::User, msg, _FILEINFO_);
677  }
678 
679  // Establish the center search tack point as best pixel to start for the
680  // adaptive algorithm prior to reduction.
681  int bestSearchSamp = gradientSearchChip.TackSample();
682  int bestSearchLine = gradientSearchChip.TackLine();
683 
684  // ---------------------------------------------------------------------
685  // if the reduction factor is still not equal to one, then we go ahead
686  // with the reduction of the chips and call Match to get the first
687  // estimate of the best line/sample.
688  // ---------------------------------------------------------------------
689  if(p_reduceFactor != 1) {
690  p_reducedPatternChip.SetSize((int)gradientPatternChip.Samples() / p_reduceFactor,
691  (int)gradientPatternChip.Lines() / p_reduceFactor);
692 
693  // ----------------------------------
694  // Fill the reduced Chip with nulls.
695  // ----------------------------------
696  for(int line = 1; line <= p_reducedPatternChip.Lines(); line++) {
697  for(int samp = 1; samp <= p_reducedPatternChip.Samples(); samp++) {
698  p_reducedPatternChip.SetValue(samp, line, Isis::Null);
699  }
700  }
701 
702  p_reducedPatternChip = Reduce(gradientPatternChip, p_reduceFactor);
703  if(!ComputeChipZScore(p_reducedPatternChip)) {
704  p_patternZScoreNotMetCount++;
705  p_registrationStatus = PatternZScoreNotMet;
706  return PatternZScoreNotMet;
707  }
708 
709  p_reducedSearchChip = Reduce(gradientSearchChip, p_reduceFactor);
710  int reducedStartSamp = (p_reducedPatternChip.Samples() - 1) / 2 + 1;
711  int reducedEndSamp = p_reducedSearchChip.Samples() - reducedStartSamp + 1;
712  int reducedStartLine = (p_reducedPatternChip.Lines() - 1) / 2 + 1;
713  int reducedEndLine = p_reducedSearchChip.Lines() - reducedStartLine + 1;
714 
715  Match(p_reducedSearchChip, p_reducedPatternChip, p_reducedFitChip,
716  reducedStartSamp, reducedEndSamp, reducedStartLine, reducedEndLine);
717 
718  if(p_bestFit == Isis::Null) {
719  p_fitChipNoDataCount++;
720  p_registrationStatus = FitChipNoData;
721  return FitChipNoData;
722  }
723 
724  // ------------------------------------------------------
725  // p_bestSamp and p_bestLine are set in Match() which is
726  // called above.
727  // -----------------------------------------------------
728  int bs = (p_bestSamp - 1) * p_reduceFactor + ((p_reduceFactor - 1) / 2) + 1;
729  int bl = (p_bestLine - 1) * p_reduceFactor + ((p_reduceFactor - 1) / 2) + 1;
730 
731  // ---------------------------------------------------------------
732  // Now we grow our window size according to the reduction factor.
733  // And we grow around where the first call Match() told us was the
734  // best line/sample.
735  // ---------------------------------------------------------------
736  int newstartSamp = bs - p_reduceFactor - p_windowSize - 1;
737  int newendSamp = bs + p_reduceFactor + p_windowSize + 1;
738  int newstartLine = bl - p_reduceFactor - p_windowSize - 1;
739  int newendLine = bl + p_reduceFactor + p_windowSize + 1;
740 
741  if(newstartLine < startLine) newstartLine = startLine;
742  if(newendSamp > endSamp) newendSamp = endSamp;
743  if(newstartSamp < startSamp) newstartSamp = startSamp;
744  if(newendLine > endLine) newendLine = endLine;
745 
746  startSamp = newstartSamp;
747  endSamp = newendSamp;
748  startLine = newstartLine;
749  endLine = newendLine;
750  // We have found a good pixel in the reduction chip, but we
751  // don't want to use its position, so reset in prep. for
752  // non-adaptive registration. Save it off for the adaptive algorithm.
753  bestSearchSamp = bs;
754  bestSearchLine = bl;
755  p_bestSamp = 0;
756  p_bestLine = 0;
757  p_bestFit = Isis::Null;
758  }
759 
760  p_registrationStatus = Registration(gradientSearchChip, gradientPatternChip,
761  p_fitChip, startSamp, startLine, endSamp, endLine,
762  bestSearchSamp, bestSearchLine);
763 
764  gradientSearchChip.SetChipPosition(p_chipSample, p_chipLine);
765  p_searchChip.SetChipPosition(p_chipSample, p_chipLine);
766  p_cubeSample = gradientSearchChip.CubeSample();
767  p_cubeLine = gradientSearchChip.CubeLine();
768 
769  // Save off the gradient search and pattern chips if we used a gradient
770  // filter.
771  if (p_gradientFilterType != None) {
772  p_gradientSearchChip = gradientSearchChip;
773  p_gradientPatternChip = gradientPatternChip;
774  }
775 
776  p_goodnessOfFit = p_bestFit;
777 
778  if (Success()) {
779  if (p_registrationStatus == AutoReg::SuccessSubPixel)
780  p_subpixelSuccesses++;
781  else
782  p_pixelSuccesses++;
783  }
784 
785  return p_registrationStatus;
786  }
787 
788 
822  AutoReg::RegisterStatus AutoReg::Registration(Chip &sChip, Chip &pChip,
823  Chip &fChip, int startSamp, int startLine, int endSamp, int endLine,
824  int bestSamp, int bestLine) {
825 
826  // Not adaptive, continue with slower search traverse
827  Match(sChip, pChip, fChip, startSamp, endSamp, startLine, endLine);
828 
829  // Check to see if we went through the fit chip and never got a fit at
830  // any location.
831  if (p_bestFit == Isis::Null) {
832  p_fitChipNoDataCount++;
833  p_registrationStatus = FitChipNoData;
834  return FitChipNoData;
835  }
836 
837  // Now see if we satisified the goodness of fit tolerance
838  if (!CompareFits(p_bestFit, Tolerance())) {
839  p_fitChipToleranceNotMetCount++;
840  p_registrationStatus = FitChipToleranceNotMet;
841  return FitChipToleranceNotMet;
842  }
843 
844  // Try to fit a model for sub-pixel accuracy if necessary
845  if (p_subpixelAccuracy && !IsIdeal(p_bestFit)) {
846  Chip window(p_windowSize, p_windowSize);
847  fChip.Extract(p_bestSamp, p_bestLine, window);
848  window.SetChipPosition(p_windowSize / 2 + 1, p_windowSize / 2 + 1);
849 
850  // Make sure more than 2/3 of the data in the window is valid. Otherwise,
851  // we are likely too close to the edge.
852  if (!window.IsValid(100.0 * 2.1 / 3.0)) {
853  p_surfaceModelNotEnoughValidDataCount++;
854  p_registrationStatus = SurfaceModelNotEnoughValidData;
855  return SurfaceModelNotEnoughValidData;
856  }
857 
858  // Now that we know we have enough data to model the surface we call
859  // SetSubpixelPosition() to get the sub-pixel accuracy we are looking for.
860  bool computedSubPixel = SetSubpixelPosition(window);
861  if (!computedSubPixel)
862  return p_registrationStatus;
863 
864  // See if the surface model solution moved too far from our whole pixel
865  // solution
866  p_sampMovement = fabs(p_bestSamp - p_chipSample);
867  p_lineMovement = fabs(p_bestLine - p_chipLine);
868  if (p_sampMovement > p_distanceTolerance ||
869  p_lineMovement > p_distanceTolerance) {
870 
871  p_surfaceModelDistanceInvalidCount++;
872  p_registrationStatus = SurfaceModelDistanceInvalid;
873  return SurfaceModelDistanceInvalid;
874  }
875 
876  p_registrationStatus = SuccessSubPixel;
877  }
878  else {
879  p_chipSample = p_bestSamp;
880  p_chipLine = p_bestLine;
881  p_registrationStatus = SuccessPixel;
882  }
883 
884  return p_registrationStatus;
885  }
886 
887 
898  bool AutoReg::ComputeChipZScore(Chip &chip) {
899  Statistics patternStats;
900  for(int i = 0; i < chip.Samples(); i++) {
901  double pixels[chip.Lines()];
902  for(int j = 0; j < chip.Lines(); j++) {
903  pixels[j] = chip.GetValue(i + 1, j + 1);
904  }
905  patternStats.AddData(pixels, chip.Lines());
906  }
907 
908  // If it does not pass, return
909  p_zScoreMin = patternStats.ZScore(patternStats.Minimum());
910  p_zScoreMax = patternStats.ZScore(patternStats.Maximum());
911 
912  // p_zScoreMin is made negative here so as to make it the equivalent of
913  // taking the absolute value (because p_zScoreMin is guaranteed to be
914  // negative)
915  if (p_zScoreMax < p_minimumPatternZScore && -p_zScoreMin < p_minimumPatternZScore) {
916  return false;
917  }
918  else {
919  return true;
920  }
921  }
922 
930  void AutoReg::ApplyGradientFilter(Chip &chip) {
931  if (p_gradientFilterType == None) {
932  return;
933  }
934 
935  // Use a different subchip size depending on which gradient filter is
936  // being applied.
937  int subChipWidth;
938  if (p_gradientFilterType == Sobel) {
939  subChipWidth = 3;
940  }
941  else {
942  // Perform extra sanity check.
943  string msg =
944  "No rule to set sub-chip width for selected Gradient Filter Type.";
945  throw IException(IException::Programmer, msg, _FILEINFO_);
946  }
947 
948  // Create a new chip to hold output during processing.
949  Chip filteredChip(chip.Samples(), chip.Lines());
950 
951  // Move the subchip through the chip, extracting the contents into a buffer
952  // of the same shape. This simulates the processing of a cube by boxcar,
953  // but since that can only operate on cubes, this functionality had to be
954  // replicated for use on chips.
955  for (int line = 1; line <= chip.Lines(); line++) {
956  for (int sample = 1; sample <= chip.Samples(); sample++) {
957  Chip subChip = chip.Extract(subChipWidth, subChipWidth,
958  sample, line);
959 
960  // Fill a buffer with subchip's contents. Since we'll never be storing
961  // raw bytes in the buffer, we don't care about the pixel type.
962  Buffer buffer(subChipWidth, subChipWidth, 1, Isis::None);
963  double *doubleBuffer = buffer.DoubleBuffer();
964  int bufferIndex = 0;
965  for (int subChipLine = 1; subChipLine <= subChip.Lines();
966  subChipLine++) {
967  for (int subChipSample = 1; subChipSample <= subChip.Samples();
968  subChipSample++) {
969  doubleBuffer[bufferIndex] = subChip.GetValue(subChipSample,
970  subChipLine);
971  bufferIndex++;
972  }
973  }
974 
975  // Calculate gradient based on contents in buffer and insert it into
976  // output chip.
977  double newPixelValue = 0;
978  if (p_gradientFilterType == Sobel) {
979  SobelGradient(buffer, newPixelValue);
980  }
981  filteredChip.SetValue(sample, line, newPixelValue);
982  }
983  }
984 
985  // Copy the data from the filtered chip back into the original chip.
986  for (int line = 1; line <= filteredChip.Lines(); line++) {
987  for (int sample = 1; sample <= filteredChip.Samples(); sample++) {
988  chip.SetValue(sample, line, filteredChip.GetValue(sample, line));
989  }
990  }
991  }
992 
993 
1003  void AutoReg::SobelGradient(Buffer &in, double &v) {
1004  bool specials = false;
1005  for(int i = 0; i < in.size(); ++i) {
1006  if(IsSpecial(in[i])) {
1007  specials = true;
1008  }
1009  }
1010  if(specials) {
1011  v = Isis::Null;
1012  return;
1013  }
1014  v = abs((in[0] + 2 * in[1] + in[2]) - (in[6] + 2 * in[7] + in[8])) +
1015  abs((in[2] + 2 * in[5] + in[8]) - (in[0] + 2 * in[3] + in[6]));
1016  }
1017 
1034  void AutoReg::Match(Chip &sChip, Chip &pChip, Chip &fChip, int startSamp, int endSamp, int startLine, int endLine) {
1035  // Sanity check. Should have been caught by the two previous tests
1036  if(startSamp == endSamp && startLine == endLine) {
1037  string msg = "StartSample [" + IString(startSamp) + "] = EndSample ["
1038  + IString(endSamp) + "] and StartLine [" + IString(startLine) + " = EndLine ["
1039  + IString(endLine) + "].";
1040  throw IException(IException::Programmer, msg, _FILEINFO_);
1041  }
1042 
1043  // Ok create a fit chip whose size is the same as the search chip
1044  // and then fill it with nulls
1045  fChip.SetSize(sChip.Samples(), sChip.Lines());
1046  for(int line = 1; line <= fChip.Lines(); line++) {
1047  for(int samp = 1; samp <= fChip.Samples(); samp++) {
1048  fChip.SetValue(samp, line, Isis::Null);
1049  }
1050  }
1051 
1052  // Create a chip the same size as the pattern chip.
1053  Chip subsearch(pChip.Samples(), pChip.Lines());
1054 
1055  for(int line = startLine; line <= endLine; line++) {
1056  for(int samp = startSamp; samp <= endSamp; samp++) {
1057  // Extract the subsearch chip and make sure it has enough valid data
1058  sChip.Extract(samp, line, subsearch);
1059 
1060 // if(!subsearch.IsValid(p_patternValidPercent)) continue;
1061  if(!subsearch.IsValid(p_subsearchValidPercent)) continue;
1062 
1063  // Try to match the two subchips
1064  double fit = MatchAlgorithm(pChip, subsearch);
1065 
1066  // If we had a fit save off information about that fit
1067  if(fit != Isis::Null) {
1068  fChip.SetValue(samp, line, fit);
1069  if((p_bestFit == Isis::Null) || CompareFits(fit, p_bestFit)) {
1070  p_bestFit = fit;
1071  p_bestSamp = samp;
1072  p_bestLine = line;
1073  }
1074  }
1075  }
1076  }
1077  }
1078 
1079 
1090  bool AutoReg::SetSubpixelPosition(Chip &window) {
1091  // The best correlation will be at the center of the window
1092  // if it's smaller than the edge DN's invert the chip DNs
1093  double samples = window.Samples();
1094  double lines= window.Lines();
1095  double bestDN = window.GetValue(window.ChipSample(), window.ChipLine());
1096  if (bestDN < window.GetValue(1, 1)) {
1097  for (int s=1; s <= samples; s++)
1098  for (int l=1; l <= lines; l++)
1099  window.SetValue(s, l, 1.0/window.GetValue(s, l)); //invert all the window DN's
1100  bestDN = 1 / bestDN;
1101  }
1102 
1103  // Find the greatest edge DN
1104  double greatestEdgeDn = 0.0;
1105  for (int s = 1; s <= samples; s++) {
1106  greatestEdgeDn = max(window.GetValue(s, 1), greatestEdgeDn);
1107  greatestEdgeDn = max(window.GetValue(s, lines), greatestEdgeDn);
1108  }
1109  for (int l = 2; l <= lines - 1; l++) {
1110  greatestEdgeDn = max(window.GetValue(1, l), greatestEdgeDn);
1111  greatestEdgeDn = max(window.GetValue(samples, l), greatestEdgeDn);
1112  }
1113 
1114  //This is a small shift so the the centroid doesn't reach the edge, add 20%
1115  // of the difference between the hightest edge DN and the max DN to the highest edge DN
1116  //The 20% shift added here is somewhat arbitrary, but was choosen because it worked well
1117  // for the maximum correlation tests we did. For new area based algorithms we may want
1118  // to revist this. Possible make it a function of the match type
1119  double temp = greatestEdgeDn + 0.2 * (bestDN - greatestEdgeDn);
1120 
1121  Centroid floodFill;
1122  floodFill.setDNRange(temp, 1e100);
1123 
1124  Chip selectionChip(window);
1125  floodFill.select(&window, &selectionChip);
1126 
1127  double windowSample;
1128  double windowLine;
1129  floodFill.centerOfMassWeighted(
1130  &window, &selectionChip, &windowSample, &windowLine);
1131 
1132  int offsetS = p_bestSamp - window.ChipSample();
1133  int offsetL = p_bestLine - window.ChipLine();
1134  p_chipSample = windowSample + offsetS;
1135  p_chipLine = windowLine + offsetL;
1136 
1137  if (p_chipSample != p_chipSample) {
1138  p_surfaceModelSolutionInvalidCount++;
1139  return false; //this should never happen, but just in case...
1140  }
1141 
1142  return true;
1143  }
1144 
1145 
1155  bool AutoReg::CompareFits(double fit1, double fit2) {
1156  return(std::fabs(fit1 - IdealFit()) <= std::fabs(fit2 - IdealFit()));
1157  }
1158 
1165  bool AutoReg::IsIdeal(double fit) {
1166  return(std::fabs(IdealFit() - fit) < 0.00001);
1167  }
1168 
1169 
1180  Pvl AutoReg::RegistrationStatistics() {
1181  Pvl pvl;
1182  PvlGroup stats("AutoRegStatistics");
1183  stats += Isis::PvlKeyword("Total", toString(p_totalRegistrations));
1184  stats += Isis::PvlKeyword("Successful", toString(p_pixelSuccesses + p_subpixelSuccesses));
1185  stats += Isis::PvlKeyword("Failure", toString(p_totalRegistrations - (p_pixelSuccesses + p_subpixelSuccesses)));
1186  pvl.addGroup(stats);
1187 
1188  PvlGroup successes("Successes");
1189  successes += PvlKeyword("SuccessPixel", toString(p_pixelSuccesses));
1190  successes += PvlKeyword("SuccessSubPixel", toString(p_subpixelSuccesses));
1191  pvl.addGroup(successes);
1192 
1193  PvlGroup grp("PatternChipFailures");
1194  grp += PvlKeyword("PatternNotEnoughValidData", toString(p_patternChipNotEnoughValidDataCount));
1195  grp += PvlKeyword("PatternZScoreNotMet", toString(p_patternZScoreNotMetCount));
1196  pvl.addGroup(grp);
1197 
1198  PvlGroup fit("FitChipFailures");
1199  fit += PvlKeyword("FitChipNoData", toString(p_fitChipNoDataCount));
1200  fit += PvlKeyword("FitChipToleranceNotMet", toString(p_fitChipToleranceNotMetCount));
1201  pvl.addGroup(fit);
1202 
1203  PvlGroup model("SurfaceModelFailures");
1204  model += PvlKeyword("SurfaceModelNotEnoughValidData", toString(p_surfaceModelNotEnoughValidDataCount));
1205  model += PvlKeyword("SurfaceModelSolutionInvalid", toString(p_surfaceModelSolutionInvalidCount));
1206  model += PvlKeyword("SurfaceModelDistanceInvalid", toString(p_surfaceModelDistanceInvalidCount));
1207  pvl.addGroup(model);
1208 
1209  return (AlgorithmStatistics(pvl));
1210  }
1211 
1219  PvlGroup AutoReg::RegTemplate() {
1220  PvlGroup reg("AutoRegistration");
1221 
1222  PvlGroup &algo = p_template.findGroup("Algorithm", Pvl::Traverse);
1223  reg += PvlKeyword("Algorithm", algo["Name"][0]);
1224  reg += PvlKeyword("Tolerance", algo["Tolerance"][0]);
1225  if(algo.hasKeyword("SubpixelAccuracy")) {
1226  reg += PvlKeyword("SubpixelAccuracy", algo["SubpixelAccuracy"][0]);
1227  }
1228  if(algo.hasKeyword("ReductionFactor")) {
1229  reg += PvlKeyword("ReductionFactor", algo["ReductionFactor"][0]);
1230  }
1231  if(algo.hasKeyword("Gradient")) {
1232  reg += PvlKeyword("Gradient", algo["Gradient"][0]);
1233  }
1234 
1235  PvlGroup &pchip = p_template.findGroup("PatternChip", Pvl::Traverse);
1236  reg += PvlKeyword("PatternSamples", pchip["Samples"][0]);
1237  reg += PvlKeyword("PatternLines", pchip["Lines"][0]);
1238  if(pchip.hasKeyword("ValidMinimum")) {
1239  reg += PvlKeyword("PatternMinimum", pchip["ValidMinimum"][0]);
1240  }
1241  if(pchip.hasKeyword("ValidMaximum")) {
1242  reg += PvlKeyword("PatternMaximum", pchip["ValidMaximum"][0]);
1243  }
1244  if(pchip.hasKeyword("MinimumZScore")) {
1245  reg += PvlKeyword("MinimumZScore", pchip["MinimumZScore"][0]);
1246  }
1247  if(pchip.hasKeyword("ValidPercent")) {
1248  SetPatternValidPercent((double)pchip["ValidPercent"]);
1249  reg += PvlKeyword("ValidPercent", pchip["ValidPercent"][0]);
1250  }
1251 
1252  PvlGroup &schip = p_template.findGroup("SearchChip", Pvl::Traverse);
1253  reg += PvlKeyword("SearchSamples", schip["Samples"][0]);
1254  reg += PvlKeyword("SearchLines", schip["Lines"][0]);
1255  if(schip.hasKeyword("ValidMinimum")) {
1256  reg += PvlKeyword("SearchMinimum", schip["ValidMinimum"][0]);
1257  }
1258  if(schip.hasKeyword("ValidMaximum")) {
1259  reg += PvlKeyword("SearchMaximum", schip["ValidMaximum"][0]);
1260  }
1261  if(schip.hasKeyword("SubchipValidPercent")) {
1262  SetSubsearchValidPercent((double)schip["SubchipValidPercent"]);
1263  reg += PvlKeyword("SubchipValidPercent", schip["SubchipValidPercent"][0]);
1264  }
1265 
1266  if(p_template.hasGroup("SurfaceModel")) {
1267  PvlGroup &smodel = p_template.findGroup("SurfaceModel", Pvl::Traverse);
1268  if(smodel.hasKeyword("DistanceTolerance")) {
1269  reg += PvlKeyword("DistanceTolerance", smodel["DistanceTolerance"][0]);
1270  }
1271 
1272  if(smodel.hasKeyword("WindowSize")) {
1273  reg += PvlKeyword("WindowSize", smodel["WindowSize"][0]);
1274  }
1275  }
1276 
1277  return reg;
1278  }
1279 
1280 
1292  PvlGroup AutoReg::UpdatedTemplate() {
1293  PvlGroup reg("AutoRegistration");
1294 
1295  reg += PvlKeyword("Algorithm", AlgorithmName());
1296  reg += PvlKeyword("Tolerance", toString(Tolerance()));
1297  reg += PvlKeyword("SubpixelAccuracy",
1298  SubPixelAccuracy() ? "True" : "False");
1299  reg += PvlKeyword("ReductionFactor", toString(ReductionFactor()));
1300  reg += PvlKeyword("Gradient", GradientFilterString());
1301 
1302  Chip *pattern = PatternChip();
1303  reg += PvlKeyword("PatternSamples", toString(pattern->Samples()));
1304  reg += PvlKeyword("PatternLines", toString(pattern->Lines()));
1305  reg += PvlKeyword("MinimumZScore", toString(MinimumZScore()));
1306  reg += PvlKeyword("ValidPercent", toString(PatternValidPercent()));
1307  // TODO Chip needs accessors to valid minimum and maximum
1308 
1309  Chip *search = SearchChip();
1310  reg += PvlKeyword("SearchSamples", toString(search->Samples()));
1311  reg += PvlKeyword("SearchLines", toString(search->Lines()));
1312  reg += PvlKeyword("SubchipValidPercent", toString(SubsearchValidPercent()));
1313  // TODO Chip needs accessors to valid minimum and maximum
1314 
1315  if (SubPixelAccuracy()) {
1316  reg += PvlKeyword("DistanceTolerance", toString(DistanceTolerance()));
1317  reg += PvlKeyword("WindowSize", toString(WindowSize()));
1318  }
1319 
1320  return reg;
1321  }
1322 }