Isis 3 Programmer Reference
BundleAdjust.cpp
1 #include "BundleAdjust.h"
2 
3 // std lib
4 #include <iomanip>
5 #include <iostream>
6 #include <sstream>
7 
8 // qt lib
9 #include <QCoreApplication>
10 #include <QDebug>
11 #include <QFile>
12 #include <QMutex>
13 
14 // boost lib
15 #include <boost/lexical_cast.hpp>
16 #include <boost/numeric/ublas/io.hpp>
17 #include <boost/numeric/ublas/matrix_sparse.hpp>
18 #include <boost/numeric/ublas/vector_proxy.hpp>
19 
20 // Isis lib
21 #include "Application.h"
22 #include "BundleObservation.h"
24 #include "BundleResults.h"
25 #include "BundleSettings.h"
26 #include "BundleSolutionInfo.h"
27 #include "BundleTargetBody.h"
28 #include "Camera.h"
29 #include "CameraDetectorMap.h"
30 #include "CameraDistortionMap.h"
31 #include "CameraFocalPlaneMap.h"
32 #include "CameraGroundMap.h"
33 #include "Control.h"
34 #include "ControlPoint.h"
35 #include "CorrelationMatrix.h"
36 #include "Distance.h"
37 #include "ImageList.h"
38 #include "iTime.h"
39 #include "Latitude.h"
40 #include "Longitude.h"
42 #include "SpecialPixel.h"
43 #include "StatCumProbDistDynCalc.h"
44 #include "SurfacePoint.h"
45 #include "Target.h"
46 
47 using namespace boost::numeric::ublas;
48 using namespace Isis;
49 
50 namespace Isis {
51 
52 
62  static void cholmodErrorHandler(int nStatus,
63  const char* file,
64  int nLineNo,
65  const char* message) {
66  QString errlog;
67 
68  errlog = "SPARSE: ";
69  errlog += message;
70 
71  PvlGroup gp(errlog);
72 
73  gp += PvlKeyword("File",file);
74  gp += PvlKeyword("Line_Number", toString(nLineNo));
75  gp += PvlKeyword("Status", toString(nStatus));
76 
77 // Application::Log(gp);
78 
79  errlog += ". (See print.prt for details)";
80 
81 // throw IException(IException::Unknown, errlog, _FILEINFO_);
82  }
83 
84 
94  BundleAdjust::BundleAdjust(BundleSettingsQsp bundleSettings,
95  const QString &cnetFile,
96  const QString &cubeList,
97  bool printSummary) {
98  m_abort = false;
99  Progress progress;
100  // initialize constructor dependent settings...
101  // m_printSummary, m_cleanUp, m_cnetFileName, m_controlNet,
102  // m_serialNumberList, m_bundleSettings
103  m_printSummary = printSummary;
104  m_cleanUp = true;
105  m_cnetFileName = cnetFile;
106  try {
107  m_controlNet = ControlNetQsp( new ControlNet(cnetFile, &progress,
108  bundleSettings->controlPointCoordTypeReports()) );
109  }
110  catch (IException &e) {
111  throw;
112  }
113  m_bundleResults.setOutputControlNet(m_controlNet);
114  m_serialNumberList = new SerialNumberList(cubeList);
115  m_bundleSettings = bundleSettings;
116  m_bundleTargetBody = bundleSettings->bundleTargetBody();
117 
118  init(&progress);
119  }
120 
121 
132  BundleAdjust::BundleAdjust(BundleSettingsQsp bundleSettings,
133  QString &cnetFile,
134  SerialNumberList &snlist,
135  bool printSummary) {
136  // initialize constructor dependent settings...
137  // m_printSummary, m_cleanUp, m_cnetFileName, m_controlNet,
138  // m_serialNumberList, m_bundleSettings
139  m_abort = false;
140  Progress progress;
141  m_printSummary = printSummary;
142  m_cleanUp = false;
143  m_cnetFileName = cnetFile;
144  try {
145  m_controlNet = ControlNetQsp( new ControlNet(cnetFile, &progress) );
146  }
147  catch (IException &e) {
148  throw;
149  }
150  m_bundleResults.setOutputControlNet(m_controlNet);
151  m_serialNumberList = &snlist;
152  m_bundleSettings = bundleSettings;
153  m_bundleTargetBody = bundleSettings->bundleTargetBody();
154 
155  init();
156  }
157 
158 
169  BundleAdjust::BundleAdjust(BundleSettingsQsp bundleSettings,
170  Control &cnet,
171  SerialNumberList &snlist,
172  bool printSummary) {
173  // initialize constructor dependent settings...
174  // m_printSummary, m_cleanUp, m_cnetFileName, m_controlNet,
175  // m_serialNumberList, m_bundleSettings
176  m_abort = false;
177  Progress progress;
178  m_printSummary = printSummary;
179  m_cleanUp = false;
180  m_cnetFileName = cnet.fileName();
181  try {
182  m_controlNet = ControlNetQsp( new ControlNet(cnet.fileName(), &progress) );
183  }
184  catch (IException &e) {
185  throw;
186  }
187  m_bundleResults.setOutputControlNet(m_controlNet);
188  m_serialNumberList = &snlist;
189  m_bundleSettings = bundleSettings;
190  m_bundleTargetBody = bundleSettings->bundleTargetBody();
191 
192  init();
193  }
194 
195 
206  BundleAdjust::BundleAdjust(BundleSettingsQsp bundleSettings,
207  ControlNet &cnet,
208  SerialNumberList &snlist,
209  bool printSummary) {
210  // initialize constructor dependent settings...
211  // m_printSummary, m_cleanUp, m_cnetFileName, m_controlNet,
212  // m_serialNumberList, m_bundleSettings
213  m_abort = false;
214  m_printSummary = printSummary;
215  m_cleanUp = false;
216  m_cnetFileName = "";
217  try {
218  m_controlNet = ControlNetQsp( new ControlNet(cnet) );
219  }
220  catch (IException &e) {
221  throw;
222  }
223  m_bundleResults.setOutputControlNet(m_controlNet);
224  m_serialNumberList = &snlist;
225  m_bundleSettings = bundleSettings;
226  m_bundleTargetBody = bundleSettings->bundleTargetBody();
227 
228  init();
229  }
230 
231 
240  BundleAdjust::BundleAdjust(BundleSettingsQsp bundleSettings,
241  ControlNetQsp cnet,
242  const QString &cubeList,
243  bool printSummary) {
244  m_abort = false;
245  m_printSummary = printSummary;
246  m_cleanUp = false;
247  m_cnetFileName = "";
248  try {
249  m_controlNet = cnet;
250  }
251  catch (IException &e) {
252  throw;
253  }
254  m_bundleResults.setOutputControlNet(m_controlNet);
255  m_serialNumberList = new SerialNumberList(cubeList);
256  m_bundleSettings = bundleSettings;
257  m_bundleTargetBody = bundleSettings->bundleTargetBody();
258 
259  init();
260  }
261 
262 
272  BundleAdjust::BundleAdjust(BundleSettingsQsp bundleSettings,
273  Control &control,
274  QList<ImageList *> imgLists,
275  bool printSummary) {
276  m_bundleSettings = bundleSettings;
277 
278  m_abort = false;
279  try {
280  m_controlNet = ControlNetQsp( new ControlNet(control.fileName()) );
281  }
282  catch (IException &e) {
283  throw;
284  }
285  m_bundleResults.setOutputControlNet(m_controlNet);
286 
287  m_imageLists = imgLists;
288 
289  // this is too slow and we need to get rid of the serial number list anyway
290  // should be unnecessary as Image class has serial number
291  // could hang on to image list until creating BundleObservations?
292  m_serialNumberList = new SerialNumberList;
293 
294  foreach (ImageList *imgList, imgLists) {
295  foreach (Image *image, *imgList) {
296  m_serialNumberList->add(image->fileName());
297 // m_serialNumberList->add(image->serialNumber(), image->fileName());
298  }
299  }
300 
301  m_bundleTargetBody = bundleSettings->bundleTargetBody();
302 
303  m_printSummary = printSummary;
304 
305  m_cleanUp = false;
306  m_cnetFileName = control.fileName();
307 
308  init();
309  }
310 
311 
320  BundleAdjust::~BundleAdjust() {
321  // If we have ownership
322  if (m_cleanUp) {
323  delete m_serialNumberList;
324  }
325 
326  freeCHOLMODLibraryVariables();
327 
328  }
329 
330 
362  void BundleAdjust::init(Progress *progress) {
363  emit(statusUpdate("Initialization"));
364  m_previousNumberImagePartials = 0;
365 
366  // initialize
367  //
368  // JWB
369  // - some of these not originally initialized.. better values???
370  m_iteration = 0;
371  m_rank = 0;
372  m_iterationSummary = "";
373 
374  // Get the cameras set up for all images
375  // NOTE - THIS IS NOT THE SAME AS "setImage" as called in BundleAdjust::computePartials
376  // this call only does initializations; sets measure's camera pointer, etc
377  // RENAME????????????
378  m_controlNet->SetImages(*m_serialNumberList, progress);
379 
380  // clear JigsawRejected flags
381  m_controlNet->ClearJigsawRejected();
382 
383  // initialize held variables
384  int numImages = m_serialNumberList->size();
385 
386  // matrix stuff
387  m_normalInverse.clear();
388  m_RHS.clear();
389  m_imageSolution.clear();
390 
391  // we don't want to call initializeCHOLMODLibraryVariables() here since mRank=0
392  // m_cholmodCommon, m_sparseNormals are not initialized
393  m_L = NULL;
394  m_cholmodNormal = NULL;
395  m_cholmodTriplet = NULL;
396 
397  // should we initialize objects m_xResiduals, m_yResiduals, m_xyResiduals
398 
399  // TESTING
400  // TODO: code below should go into a separate method???
401  // set up BundleObservations and assign solve settings for each from BundleSettings class
402  for (int i = 0; i < numImages; i++) {
403 
404  Camera *camera = m_controlNet->Camera(i);
405  QString observationNumber = m_serialNumberList->observationNumber(i);
406  QString instrumentId = m_serialNumberList->spacecraftInstrumentId(i);
407  QString serialNumber = m_serialNumberList->serialNumber(i);
408  QString fileName = m_serialNumberList->fileName(i);
409 
410  // create a new BundleImage and add to new (or existing if observation mode is on)
411  // BundleObservation
412  BundleImageQsp image = BundleImageQsp(new BundleImage(camera, serialNumber, fileName));
413 
414  if (!image) {
415  QString msg = "In BundleAdjust::init(): image " + fileName + "is null." + "\n";
416  throw IException(IException::Programmer, msg, _FILEINFO_);
417  }
418 
419  BundleObservationQsp observation =
420  m_bundleObservations.addNew(image, observationNumber, instrumentId, m_bundleSettings);
421 
422  if (!observation) {
423  QString msg = "In BundleAdjust::init(): observation "
424  + observationNumber + "is null." + "\n";
425  throw IException(IException::Programmer, msg, _FILEINFO_);
426  }
427  }
428 
429  // initialize exterior orientation (spice) for all BundleImages in all BundleObservations
430  // TODO!!! - should these initializations just be done when we add the new observation above?
431  m_bundleObservations.initializeExteriorOrientation();
432 
433  if (m_bundleSettings->solveTargetBody()) {
434  m_bundleObservations.initializeBodyRotation();
435  }
436 
437  // set up vector of BundleControlPoints
438  int numControlPoints = m_controlNet->GetNumPoints();
439  for (int i = 0; i < numControlPoints; i++) {
440  ControlPoint *point = m_controlNet->GetPoint(i);
441  if (point->IsIgnored()) {
442  continue;
443  }
444 
445  BundleControlPointQsp bundleControlPoint(new BundleControlPoint
446  (m_bundleSettings, point));
447  m_bundleControlPoints.append(bundleControlPoint);
448 
449  // set parent observation for each BundleMeasure
450 
451  int numMeasures = bundleControlPoint->size();
452  for (int j=0; j < numMeasures; j++) {
453  BundleMeasureQsp measure = bundleControlPoint->at(j);
454  QString cubeSerialNumber = measure->cubeSerialNumber();
455 
456  BundleObservationQsp observation =
457  m_bundleObservations.observationByCubeSerialNumber(cubeSerialNumber);
458  BundleImageQsp image = observation->imageByCubeSerialNumber(cubeSerialNumber);
459 
460  measure->setParentObservation(observation);
461  measure->setParentImage(image);
462  }
463  }
464 
465  //===========================================================================================//
466  //==== Use the bundle settings to initialize more member variables and set up solutions =====//
467  //===========================================================================================//
468 
469  // TODO: Need to have some validation code to make sure everything is
470  // on the up-and-up with the control network. Add checks for multiple
471  // networks, images without any points, and points on images removed from
472  // the control net (when we start adding software to remove points with high
473  // residuals) and ?. For "deltack" a single measure on a point is allowed
474  // so skip the test.
475  if (m_bundleSettings->validateNetwork()) {
476  validateNetwork();
477  }
478  m_bundleResults.maximumLikelihoodSetUp(m_bundleSettings->maximumLikelihoodEstimatorModels());
479 
480  //===========================================================================================//
481  //=============== End Bundle Settings =======================================================//
482  //===========================================================================================//
483 
484  //===========================================================================================//
485  //======================== initialize matrices and more parameters ==========================//
486  //===========================================================================================//
487 
488  // size of reduced normals matrix
489 
490  // TODO
491  // this should be determined from BundleSettings
492  // m_rank will be the sum of observation, target, and self-cal parameters
493  // TODO
494  m_rank = m_bundleObservations.numberParameters();
495 
496  if (m_bundleSettings->solveTargetBody()) {
497  m_rank += m_bundleSettings->numberTargetBodyParameters();
498  }
499 
500  int num3DPoints = m_bundleControlPoints.size();
501 
502  m_bundleResults.setNumberUnknownParameters(m_rank + 3 * num3DPoints);
503 
504  m_imageSolution.resize(m_rank);
505 
506  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
507  // initializations for cholmod
508  initializeCHOLMODLibraryVariables();
509 
510  // initialize normal equations matrix
511  initializeNormalEquationsMatrix();
512  }
513 
514 
534  bool BundleAdjust::validateNetwork() {
535 
536  outputBundleStatus("\nValidating network...");
537 
538  int imagesWithInsufficientMeasures = 0;
539  QString msg = "Images with one or less measures:\n";
540  int numObservations = m_bundleObservations.size();
541  for (int i = 0; i < numObservations; i++) {
542  int numImages = m_bundleObservations.at(i)->size();
543  for (int j = 0; j < numImages; j++) {
544  BundleImageQsp bundleImage = m_bundleObservations.at(i)->at(j);
545  int numMeasures = m_controlNet->
546  GetNumberOfValidMeasuresInImage(bundleImage->serialNumber());
547 
548  if (numMeasures > 1) {
549  continue;
550  }
551 
552  imagesWithInsufficientMeasures++;
553  msg += bundleImage->fileName() + ": " + toString(numMeasures) + "\n";
554  }
555  }
556 
557  if ( imagesWithInsufficientMeasures > 0 ) {
558  throw IException(IException::User, msg, _FILEINFO_);
559  }
560 
561  outputBundleStatus("\nValidation complete!...\n");
562 
563  return true;
564  }
565 
572  bool BundleAdjust::initializeCHOLMODLibraryVariables() {
573  if ( m_rank <= 0 ) {
574  return false;
575  }
576 
577  m_cholmodTriplet = NULL;
578 
579  cholmod_start(&m_cholmodCommon);
580 
581  // set user-defined cholmod error handler
582  m_cholmodCommon.error_handler = cholmodErrorHandler;
583 
584  // testing not using metis
585  m_cholmodCommon.nmethods = 1;
586  m_cholmodCommon.method[0].ordering = CHOLMOD_AMD;
587 
588  return true;
589  }
590 
591 
602  bool BundleAdjust::initializeNormalEquationsMatrix() {
603 
604  int nBlockColumns = m_bundleObservations.size();
605 
606  if (m_bundleSettings->solveTargetBody())
607  nBlockColumns += 1;
608 
609  m_sparseNormals.setNumberOfColumns(nBlockColumns);
610 
611  m_sparseNormals.at(0)->setStartColumn(0);
612 
613  int nParameters = 0;
614  if (m_bundleSettings->solveTargetBody()) {
615  nParameters += m_bundleSettings->numberTargetBodyParameters();
616  m_sparseNormals.at(1)->setStartColumn(nParameters);
617 
618  int observation = 0;
619  for (int i = 2; i < nBlockColumns; i++) {
620  nParameters += m_bundleObservations.at(observation)->numberParameters();
621  m_sparseNormals.at(i)->setStartColumn(nParameters);
622  observation++;
623  }
624  }
625  else {
626  for (int i = 0; i < nBlockColumns; i++) {
627  m_sparseNormals.at(i)->setStartColumn(nParameters);
628  nParameters += m_bundleObservations.at(i)->numberParameters();
629  }
630  }
631 
632  return true;
633  }
634 
635 
644  bool BundleAdjust::freeCHOLMODLibraryVariables() {
645 
646  cholmod_free_triplet(&m_cholmodTriplet, &m_cholmodCommon);
647  cholmod_free_sparse(&m_cholmodNormal, &m_cholmodCommon);
648  cholmod_free_factor(&m_L, &m_cholmodCommon);
649 
650  cholmod_finish(&m_cholmodCommon);
651 
652  return true;
653  }
654 
655 
665  BundleSolutionInfo* BundleAdjust::solveCholeskyBR() {
666  solveCholesky();
667  return bundleSolveInformation();
668  }
669 
670 
675  void BundleAdjust::abortBundle() {
676  m_abort = true;
677  }
678 
679 
694  bool BundleAdjust::solveCholesky() {
695  emit(statusBarUpdate("Solving"));
696  try {
697 
698  // throw error if a frame camera is included AND
699  // if m_bundleSettings->solveInstrumentPositionOverHermiteSpline()
700  // is set to true (can only use for line scan or radar)
701  // if (m_bundleSettings->solveInstrumentPositionOverHermiteSpline() == true) {
702  // int numImages = images();
703  // for (int i = 0; i < numImages; i++) {
704  // if (m_controlNet->Camera(i)->GetCameraType() == 0) {
705  // QString msg = "At least one sensor is a frame camera. "
706  // "Spacecraft Option OVERHERMITE is not valid for frame cameras\n";
707  // throw IException(IException::User, msg, _FILEINFO_);
708  // }
709  // }
710  // }
711 
712  // Compute the apriori coordinates for each nonheld point
713  m_controlNet->ComputeApriori(); // original location
714 
715  // ken testing - if solving for target mean radius, set point radius to current mean radius
716  // if solving for triaxial radii, set point radius to local radius
717  if (m_bundleTargetBody && m_bundleTargetBody->solveMeanRadius()) {
718  int numControlPoints = m_bundleControlPoints.size();
719  for (int i = 0; i < numControlPoints; i++) {
720  BundleControlPointQsp point = m_bundleControlPoints.at(i);
721  SurfacePoint surfacepoint = point->adjustedSurfacePoint();
722 
723  surfacepoint.ResetLocalRadius(m_bundleTargetBody->meanRadius());
724 
725  point->setAdjustedSurfacePoint(surfacepoint);
726  }
727  }
728 
729  // Only use target body solution options when using Latitudinal coordinates
730  if (m_bundleTargetBody && m_bundleTargetBody->solveTriaxialRadii()
731  && m_bundleSettings->controlPointCoordTypeBundle() == SurfacePoint::Latitudinal) {
732  int numControlPoints = m_bundleControlPoints.size();
733  for (int i = 0; i < numControlPoints; i++) {
734  BundleControlPointQsp point = m_bundleControlPoints.at(i);
735  SurfacePoint surfacepoint = point->adjustedSurfacePoint();
736 
737  Distance localRadius = m_bundleTargetBody->localRadius(surfacepoint.GetLatitude(),
738  surfacepoint.GetLongitude());
739  surfacepoint.ResetLocalRadius(localRadius);
740 
741  point->setAdjustedSurfacePoint(surfacepoint);
742  }
743  }
744 
745  // Beginning of iterations
746  m_iteration = 1;
747  double vtpv = 0.0;
748  double previousSigma0 = 0.0;
749 
750  // start the clock
751  clock_t solveStartClock = clock();
752 
753  for (;;) {
754 
755  emit iterationUpdate(m_iteration);
756 
757  // testing
758  if (m_abort) {
759  m_bundleResults.setConverged(false);
760  emit statusUpdate("\n aborting...");
761  emit finished();
762  return false;
763  }
764  // testing
765 
766  emit statusUpdate( QString("starting iteration %1\n").arg(m_iteration) );
767 
768  clock_t iterationStartClock = clock();
769 
770  // zero normals (after iteration 0)
771  if (m_iteration != 1) {
772  m_sparseNormals.zeroBlocks();
773  }
774 
775  // form normal equations -- computePartials is called in here.
776  if (!formNormalEquations()) {
777  m_bundleResults.setConverged(false);
778  break;
779  }
780 
781  // testing
782  if (m_abort) {
783  m_bundleResults.setConverged(false);
784  emit statusUpdate("\n aborting...");
785  emit finished();
786  return false;
787  }
788  // testing
789 
790  // solve the system
791  if (!solveSystem()) {
792  outputBundleStatus("\nsolve failed!");
793  m_bundleResults.setConverged(false);
794  break;
795  }
796 
797  // testing
798  if (m_abort) {
799  m_bundleResults.setConverged(false);
800  emit statusUpdate("\n aborting...");
801  emit finished();
802  return false;
803  }
804  // testing
805 
806  // apply parameter corrections
807  applyParameterCorrections();
808 
809  // testing
810  if (m_abort) {
811  m_bundleResults.setConverged(false);
812  emit statusUpdate("\n aborting...");
813  emit finished();
814  return false;
815  }
816  // testing
817 
818  // compute residuals
819  vtpv = computeResiduals();
820 
821  // flag outliers
822  if ( m_bundleSettings->outlierRejection() ) {
823  computeRejectionLimit();
824  flagOutliers();
825  }
826 
827  // testing
828  if (m_abort) {
829  m_bundleResults.setConverged(false);
830  emit statusUpdate("\n aborting...");
831  emit finished();
832  return false;
833  }
834  // testing
835 
836  // variance of unit weight (also reference variance, variance factor, etc.)
837  m_bundleResults.computeSigma0(vtpv, m_bundleSettings->convergenceCriteria());
838 
839  // Set up formatting for status updates with doubles (e.g. Sigma0, Elapsed Time)
840  int fieldWidth = 20;
841  char format = 'f';
842  int precision = 10;
843 
844  emit statusUpdate(QString("Iteration: %1 \n")
845  .arg(m_iteration));
846  emit statusUpdate(QString("Sigma0: %1 \n")
847  .arg(m_bundleResults.sigma0(),
848  fieldWidth,
849  format,
850  precision));
851  emit statusUpdate(QString("Observations: %1 \n")
852  .arg(m_bundleResults.numberObservations()));
853  emit statusUpdate(QString("Constrained Parameters:%1 \n")
854  .arg(m_bundleResults.numberConstrainedPointParameters()));
855  emit statusUpdate(QString("Unknowns: %1 \n")
856  .arg(m_bundleResults.numberUnknownParameters()));
857  emit statusUpdate(QString("Degrees of Freedom: %1 \n")
858  .arg(m_bundleResults.degreesOfFreedom()));
859  emit iterationUpdate(m_iteration);
860 
861  // check for convergence
862  if (m_bundleSettings->convergenceCriteria() == BundleSettings::Sigma0) {
863  if (fabs(previousSigma0 - m_bundleResults.sigma0())
864  <= m_bundleSettings->convergenceCriteriaThreshold()) {
865  // convergence detected
866 
867  // if maximum likelihood tiers are being processed,
868  // check to see if there's another tier to go.
869  if (m_bundleResults.maximumLikelihoodModelIndex()
870  < m_bundleResults.numberMaximumLikelihoodModels() - 1
871  && m_bundleResults.maximumLikelihoodModelIndex()
872  < 2) {
873  // TODO is this second condition redundant???
874  // should BundleResults require num models <= 3, so num models - 1 <= 2
875  if (m_bundleResults.numberMaximumLikelihoodModels()
876  > m_bundleResults.maximumLikelihoodModelIndex() + 1) {
877 
878  // If there is another tier left we will increment the index.
879  m_bundleResults.incrementMaximumLikelihoodModelIndex();
880  }
881  }
882  else { // otherwise iterations are complete
883  m_bundleResults.setConverged(true);
884  emit statusUpdate("Bundle has converged\n");
885  emit statusBarUpdate("Converged");
886  break;
887  }
888  }
889  }
890  else {
891  // bundleSettings.convergenceCriteria() == BundleSettings::ParameterCorrections
892  int numConvergedParams = 0;
893  int numImgParams = m_imageSolution.size();
894  for (int ij = 0; ij < numImgParams; ij++) {
895  if (fabs(m_imageSolution(ij)) > m_bundleSettings->convergenceCriteriaThreshold()) {
896  break;
897  }
898  else
899  numConvergedParams++;
900  }
901 
902  if ( numConvergedParams == numImgParams ) {
903  m_bundleResults.setConverged(true);
904  emit statusUpdate("Bundle has converged\n");
905  emit statusBarUpdate("Converged");
906  break;
907  }
908  }
909 
910  m_bundleResults.printMaximumLikelihoodTierInformation();
911  clock_t iterationStopClock = clock();
912  double iterationTime = (iterationStopClock - iterationStartClock)
913  / (double)CLOCKS_PER_SEC;
914  emit statusUpdate( QString("End of Iteration %1 \n").arg(m_iteration) );
915  emit statusUpdate( QString("Elapsed Time: %1 \n").arg(iterationTime,
916  fieldWidth,
917  format,
918  precision) );
919 
920  // check for maximum iterations
921  if (m_iteration >= m_bundleSettings->convergenceCriteriaMaximumIterations()) {
922  emit(statusBarUpdate("Max Iterations Reached"));
923  break;
924  }
925 
926  // restart the dynamic calculation of the cumulative probility distribution of residuals
927  // (in unweighted pixels) --so it will be up to date for the next iteration
928  if (!m_bundleResults.converged()) {
929  m_bundleResults.initializeResidualsProbabilityDistribution(101);
930  }
931  // TODO: is this necessary ???
932  // probably all ready initialized to 101 nodes in bundle settings constructor...
933 
934  // if we're using CHOLMOD and still going, release cholmod_factor
935  // (if we don't, memory leaks will occur), otherwise we need it for error propagation
936  if (!m_bundleResults.converged() || !m_bundleSettings->errorPropagation()) {
937  cholmod_free_factor(&m_L, &m_cholmodCommon);
938  }
939 
940 
941  iterationSummary();
942 
943  m_iteration++;
944 
945  previousSigma0 = m_bundleResults.sigma0();
946  }
947 
948  if (m_bundleResults.converged() && m_bundleSettings->errorPropagation()) {
949  clock_t errorPropStartClock = clock();
950 
951  outputBundleStatus("\nStarting Error Propagation");
952 
953  errorPropagation();
954  emit statusUpdate("\n\nError Propagation Complete\n");
955  clock_t errorPropStopClock = clock();
956  m_bundleResults.setElapsedTimeErrorProp((errorPropStopClock - errorPropStartClock)
957  / (double)CLOCKS_PER_SEC);
958  }
959 
960  clock_t solveStopClock = clock();
961  m_bundleResults.setElapsedTime((solveStopClock - solveStartClock)
962  / (double)CLOCKS_PER_SEC);
963 
964  wrapUp();
965 
966  m_bundleResults.setIterations(m_iteration);
967  m_bundleResults.setObservations(m_bundleObservations);
968  m_bundleResults.setBundleControlPoints(m_bundleControlPoints);
969 
970  emit resultsReady(bundleSolveInformation());
971 
972  emit statusUpdate("\nBundle Complete\n");
973 
974  iterationSummary();
975  }
976  catch (IException &e) {
977  m_bundleResults.setConverged(false);
978  emit statusUpdate("\n aborting...");
979  emit statusBarUpdate("Failed to Converge");
980  emit finished();
981  QString msg = "Could not solve bundle adjust.";
982  throw IException(e, e.errorType(), msg, _FILEINFO_);
983  }
984 
985  emit finished();
986  return true;
987  }
988 
989 
998  BundleSolutionInfo *BundleAdjust::bundleSolveInformation() {
999  BundleSolutionInfo *bundleSolutionInfo = new BundleSolutionInfo(m_bundleSettings,
1000  FileName(m_cnetFileName),
1001  m_bundleResults,
1002  imageLists());
1003  bundleSolutionInfo->setRunTime("");
1004  return bundleSolutionInfo;
1005  }
1006 
1007 
1019  bool BundleAdjust::formNormalEquations() {
1020  emit(statusBarUpdate("Forming Normal Equations"));
1021  bool status = false;
1022 
1023  m_bundleResults.setNumberObservations(0);// ???
1024  m_bundleResults.resetNumberConstrainedPointParameters();//???
1025 
1026  // Initialize auxilary matrices and vectors.
1027  static LinearAlgebra::Matrix coeffTarget;
1028  static LinearAlgebra::Matrix coeffImage;
1029  static LinearAlgebra::Matrix coeffPoint3D(2, 3);
1030  static LinearAlgebra::Vector coeffRHS(2);
1031  static boost::numeric::ublas::symmetric_matrix<double, upper> N22(3);
1033  static LinearAlgebra::Vector n2(3);
1034  boost::numeric::ublas::compressed_vector<double> n1(m_rank);
1035 
1036  m_RHS.resize(m_rank);
1037 
1038  // if solving for target body parameters, set size of coeffTarget
1039  // (note this size will not change through the adjustment).
1040  if (m_bundleSettings->solveTargetBody()) {
1041  int numTargetBodyParameters = m_bundleSettings->numberTargetBodyParameters();
1042  // TODO make sure numTargetBodyParameters is greater than 0
1043  coeffTarget.resize(2,numTargetBodyParameters);
1044  }
1045 
1046  // clear N12, n1, and nj
1047  N12.clear();
1048  n1.clear();
1049  m_RHS.clear();
1050 
1051  // clear static matrices
1052  coeffPoint3D.clear();
1053  coeffRHS.clear();
1054  N22.clear();
1055  n2.clear();
1056 
1057  // loop over 3D points
1058  int numGood3DPoints = 0;
1059  int numRejected3DPoints = 0;
1060  int pointIndex = 0;
1061  int num3DPoints = m_bundleControlPoints.size();
1062 
1063  outputBundleStatus("\n\n");
1064 
1065  for (int i = 0; i < num3DPoints; i++) {
1066  emit(pointUpdate(i+1));
1067  BundleControlPointQsp point = m_bundleControlPoints.at(i);
1068 
1069  if (point->isRejected()) {
1070  numRejected3DPoints++;
1071 
1072  pointIndex++;
1073  continue;
1074  }
1075 
1076  if ( i != 0 ) {
1077  N22.clear();
1078  N12.wipe();
1079  n2.clear();
1080  }
1081 
1082  // loop over measures for this point
1083  int numMeasures = point->size();
1084  for (int j = 0; j < numMeasures; j++) {
1085  BundleMeasureQsp measure = point->at(j);
1086 
1087  // flagged as "JigsawFail" implies this measure has been rejected
1088  // TODO IsRejected is obsolete -- replace code or add to ControlMeasure
1089  if (measure->isRejected()) {
1090  continue;
1091  }
1092 
1093  status = computePartials(coeffTarget, coeffImage, coeffPoint3D, coeffRHS, *measure,
1094  *point);
1095 
1096  if (!status) {
1097  // TODO should status be set back to true? JAM
1098  // TODO this measure should be flagged as rejected.
1099  continue;
1100  }
1101 
1102  // update number of observations
1103  int numObs = m_bundleResults.numberObservations();
1104  m_bundleResults.setNumberObservations(numObs + 2);
1105 
1106  formMeasureNormals(N22, N12, n1, n2, coeffTarget, coeffImage, coeffPoint3D, coeffRHS,
1107  measure->observationIndex());
1108 
1109  } // end loop over this points measures
1110 
1111  formPointNormals(N22, N12, n2, m_RHS, point);
1112 
1113  pointIndex++;
1114 
1115  numGood3DPoints++;
1116 
1117  } // end loop over 3D points
1118 
1119  // finally, form the reduced normal equations
1120  formWeightedNormals(n1, m_RHS);
1121 
1122  // update number of unknown parameters
1123  m_bundleResults.setNumberUnknownParameters(m_rank + 3 * numGood3DPoints);
1124 
1125  return status;
1126 }
1127 
1128 
1148  bool BundleAdjust::formMeasureNormals(symmetric_matrix<double, upper>&N22,
1150  compressed_vector<double> &n1,
1151  vector<double> &n2,
1152  matrix<double> &coeffTarget,
1153  matrix<double> &coeffImage,
1154  matrix<double> &coeffPoint3D,
1155  vector<double> &coeffRHS,
1156  int observationIndex) {
1157 
1158  static symmetric_matrix<double, upper> N11;
1159  static matrix<double> N11TargetImage;
1160 
1161  int blockIndex = observationIndex;
1162 
1163  // if we are solving for target body parameters
1164  int numTargetPartials = coeffTarget.size2();
1165  if (m_bundleSettings->solveTargetBody()) {
1166  blockIndex++;
1167 
1168  static vector<double> n1Target(numTargetPartials);
1169  n1Target.resize(numTargetPartials);
1170  n1Target.clear();
1171 
1172  // form N11 (normals for target body)
1173  N11.resize(numTargetPartials);
1174  N11.clear();
1175 
1176  N11 = prod(trans(coeffTarget), coeffTarget);
1177 
1178  // insert submatrix at column, row
1179  m_sparseNormals.insertMatrixBlock(0, 0, numTargetPartials, numTargetPartials);
1180 
1181  (*(*m_sparseNormals[0])[0]) += N11;
1182 
1183  // form portion of N11 between target and image
1184  N11TargetImage.resize(numTargetPartials, coeffImage.size2());
1185  N11TargetImage.clear();
1186  N11TargetImage = prod(trans(coeffTarget),coeffImage);
1187 
1188  m_sparseNormals.insertMatrixBlock(blockIndex, 0,
1189  numTargetPartials, coeffImage.size2());
1190  (*(*m_sparseNormals[blockIndex])[0]) += N11TargetImage;
1191 
1192  // form N12 target portion
1193  static matrix<double> N12Target(numTargetPartials, 3);
1194  N12Target.clear();
1195 
1196  N12Target = prod(trans(coeffTarget), coeffPoint3D);
1197 
1198  // insert N12Target into N12
1199  N12.insertMatrixBlock(0, numTargetPartials, 3);
1200  *N12[0] += N12Target;
1201 
1202  // form n1Target
1203  n1Target = prod(trans(coeffTarget), coeffRHS);
1204 
1205  // insert n1Target into n1
1206  for (int i = 0; i < numTargetPartials; i++) {
1207  n1(i) += n1Target(i);
1208  }
1209  }
1210 
1211 
1212 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ below is ok (2015-06-03)
1213 // TODO - if solving for target (and/or self-cal) have to use not observationIndex below but
1214 // observationIndex plus 1 or 2
1215 
1216  int numImagePartials = coeffImage.size2();
1217 
1218  static LinearAlgebra::Vector n1Image(numImagePartials);
1219  n1Image.resize(numImagePartials);
1220  n1Image.clear();
1221 
1222  // form N11 (normals for photo)
1223  N11.resize(numImagePartials);
1224  N11.clear();
1225 
1226  N11 = prod(trans(coeffImage), coeffImage);
1227 
1228  int t = m_sparseNormals.at(blockIndex)->startColumn();
1229 
1230  // insert submatrix at column, row
1231  m_sparseNormals.insertMatrixBlock(blockIndex, blockIndex,
1232  numImagePartials, numImagePartials);
1233 
1234  (*(*m_sparseNormals[blockIndex])[blockIndex]) += N11;
1235 
1236  // form N12Image
1237  static matrix<double> N12Image(numImagePartials, 3);
1238  N12Image.resize(numImagePartials, 3);
1239  N12Image.clear();
1240 
1241  N12Image = prod(trans(coeffImage), coeffPoint3D);
1242 
1243  // insert N12Image into N12
1244  N12.insertMatrixBlock(blockIndex, numImagePartials, 3);
1245  *N12[blockIndex] += N12Image;
1246 
1247  // form n1
1248  n1Image = prod(trans(coeffImage), coeffRHS);
1249 
1250  // insert n1Image into n1
1251  // TODO - MUST ACCOUNT FOR TARGET BODY PARAMETERS
1252  // WHEN INSERTING INTO n1 HERE!!!!!
1253  for (int i = 0; i < numImagePartials; i++) {
1254  n1(i + t) += n1Image(i);
1255  }
1256 
1257  // form N22
1258  N22 += prod(trans(coeffPoint3D), coeffPoint3D);
1259 
1260  // form n2
1261  n2 += prod(trans(coeffPoint3D), coeffRHS);
1262 
1263  return true;
1264  }
1265 
1266 
1284  bool BundleAdjust::formPointNormals(symmetric_matrix<double, upper>&N22,
1286  vector<double> &n2,
1287  vector<double> &nj,
1288  BundleControlPointQsp &bundleControlPoint) {
1289 
1290  boost::numeric::ublas::bounded_vector<double, 3> &NIC = bundleControlPoint->nicVector();
1291  SparseBlockRowMatrix &Q = bundleControlPoint->cholmodQMatrix();
1292 
1293  NIC.clear();
1294  Q.zeroBlocks();
1295 
1296  // weighting of 3D point parameters
1297  // Make sure weights are in the units corresponding to the bundle coordinate type
1298  boost::numeric::ublas::bounded_vector<double, 3> &weights
1299  = bundleControlPoint->weights();
1300  boost::numeric::ublas::bounded_vector<double, 3> &corrections
1301  = bundleControlPoint->corrections();
1302 
1303  if (weights(0) > 0.0) {
1304  N22(0,0) += weights(0);
1305  n2(0) += (-weights(0) * corrections(0));
1306  m_bundleResults.incrementNumberConstrainedPointParameters(1);
1307  }
1308 
1309  if (weights(1) > 0.0) {
1310  N22(1,1) += weights(1);
1311  n2(1) += (-weights(1) * corrections(1));
1312  m_bundleResults.incrementNumberConstrainedPointParameters(1);
1313  }
1314 
1315  if (weights(2) > 0.0) {
1316  N22(2,2) += weights(2);
1317  n2(2) += (-weights(2) * corrections(2));
1318  m_bundleResults.incrementNumberConstrainedPointParameters(1);
1319  }
1320 
1321  // invert N22
1322  invert3x3(N22);
1323 
1324  // save upper triangular covariance matrix for error propagation
1325  SurfacePoint SurfacePoint = bundleControlPoint->adjustedSurfacePoint();
1326  SurfacePoint.SetMatrix(m_bundleSettings->controlPointCoordTypeBundle(), N22);
1327  bundleControlPoint->setAdjustedSurfacePoint(SurfacePoint);
1328 
1329  // form Q (this is N22{-1} * N12{T})
1330  productATransB(N22, N12, Q);
1331 
1332  // form product of N22(inverse) and n2; store in NIC
1333  NIC = prod(N22, n2);
1334 
1335  // accumulate -R directly into reduced normal equations
1336  productAB(N12, Q);
1337 
1338  // accumulate -nj
1339  accumProductAlphaAB(-1.0, Q, n2, nj);
1340 
1341  return true;
1342  }
1343 
1344 
1356  bool BundleAdjust::formWeightedNormals(compressed_vector<double> &n1,
1357  vector<double> &nj) {
1358 
1359  m_bundleResults.resetNumberConstrainedImageParameters();
1360 
1361  int n = 0;
1362 
1363  for (int i = 0; i < m_sparseNormals.size(); i++) {
1364  LinearAlgebra::Matrix *diagonalBlock = m_sparseNormals.getBlock(i, i);
1365  if ( !diagonalBlock )
1366  continue;
1367 
1368  if (m_bundleSettings->solveTargetBody() && i == 0) {
1369  m_bundleResults.resetNumberConstrainedTargetParameters();
1370 
1371  // get parameter weights for target body
1372  vector<double> weights = m_bundleTargetBody->parameterWeights();
1373  vector<double> corrections = m_bundleTargetBody->parameterCorrections();
1374 
1375  int blockSize = diagonalBlock->size1();
1376  for (int j = 0; j < blockSize; j++) {
1377  if (weights[j] > 0.0) {
1378  (*diagonalBlock)(j,j) += weights[j];
1379  nj[n] -= weights[j] * corrections(j);
1380  m_bundleResults.incrementNumberConstrainedTargetParameters(1);
1381  }
1382  n++;
1383  }
1384  }
1385  else {
1386  BundleObservationQsp observation;
1387 
1388  // get parameter weights for this observation
1389  if (m_bundleSettings->solveTargetBody()) {
1390  observation = m_bundleObservations.at(i-1);
1391  }
1392  else {
1393  observation = m_bundleObservations.at(i);
1394  }
1395 
1396  LinearAlgebra::Vector weights = observation->parameterWeights();
1397  LinearAlgebra::Vector corrections = observation->parameterCorrections();
1398 
1399  int blockSize = diagonalBlock->size1();
1400  for (int j = 0; j < blockSize; j++) {
1401  if (weights(j) > 0.0) {
1402  (*diagonalBlock)(j,j) += weights(j);
1403  nj[n] -= weights(j) * corrections(j);
1404  m_bundleResults.incrementNumberConstrainedImageParameters(1);
1405  }
1406  n++;
1407  }
1408  }
1409  }
1410 
1411  // add n1 to nj
1412  nj += n1;
1413 
1414  return true;
1415  }
1416 
1417 
1427  bool BundleAdjust::productATransB(symmetric_matrix <double,upper> &N22,
1429  SparseBlockRowMatrix &Q) {
1430 
1431  QMapIterator< int, LinearAlgebra::Matrix * > N12it(N12);
1432 
1433  while ( N12it.hasNext() ) {
1434  N12it.next();
1435 
1436  int rowIndex = N12it.key();
1437 
1438  // insert submatrix in Q at block "rowIndex"
1439  Q.insertMatrixBlock(rowIndex, 3, N12it.value()->size1());
1440 
1441  *(Q[rowIndex]) = prod(N22,trans(*(N12it.value())));
1442  }
1443 
1444  return true;
1445  }
1446 
1447 
1457  void BundleAdjust::productAB(SparseBlockColumnMatrix &N12,
1458  SparseBlockRowMatrix &Q) {
1459  // iterators for N12 and Q
1460  QMapIterator<int, LinearAlgebra::Matrix*> N12it(N12);
1461  QMapIterator<int, LinearAlgebra::Matrix*> Qit(Q);
1462 
1463  // now multiply blocks and subtract from m_sparseNormals
1464  while ( N12it.hasNext() ) {
1465  N12it.next();
1466 
1467  int rowIndex = N12it.key();
1468  LinearAlgebra::Matrix *N12block = N12it.value();
1469 
1470  while ( Qit.hasNext() ) {
1471  Qit.next();
1472 
1473  int columnIndex = Qit.key();
1474 
1475  if ( rowIndex > columnIndex ) {
1476  continue;
1477  }
1478 
1479  LinearAlgebra::Matrix *Qblock = Qit.value();
1480 
1481  // insert submatrix at column, row
1482  m_sparseNormals.insertMatrixBlock(columnIndex, rowIndex,
1483  N12block->size1(), Qblock->size2());
1484 
1485  (*(*m_sparseNormals[columnIndex])[rowIndex]) -= prod(*N12block,*Qblock);
1486  }
1487  Qit.toFront();
1488  }
1489  }
1490 
1491 
1502  void BundleAdjust::accumProductAlphaAB(double alpha,
1504  vector<double> &n2,
1505  vector<double> &nj) {
1506 
1507  if (alpha == 0.0) {
1508  return;
1509  }
1510 
1511  int numParams;
1512 
1513  QMapIterator<int, LinearAlgebra::Matrix*> Qit(Q);
1514 
1515  while ( Qit.hasNext() ) {
1516  Qit.next();
1517 
1518  int columnIndex = Qit.key();
1519  LinearAlgebra::Matrix *Qblock = Qit.value();
1520 
1521  LinearAlgebra::Vector blockProduct = prod(trans(*Qblock),n2);
1522 
1523  numParams = m_sparseNormals.at(columnIndex)->startColumn();
1524 
1525  for (unsigned i = 0; i < blockProduct.size(); i++) {
1526  nj(numParams+i) += alpha*blockProduct(i);
1527  }
1528  }
1529  }
1530 
1531 
1541  bool BundleAdjust::solveSystem() {
1542 
1543  // load cholmod triplet
1544  if ( !loadCholmodTriplet() ) {
1545  QString msg = "CHOLMOD: Failed to load Triplet matrix";
1546  throw IException(IException::Programmer, msg, _FILEINFO_);
1547  }
1548 
1549  // convert triplet to sparse matrix
1550  m_cholmodNormal = cholmod_triplet_to_sparse(m_cholmodTriplet,
1551  m_cholmodTriplet->nnz,
1552  &m_cholmodCommon);
1553 
1554  // analyze matrix
1555  // TODO should we analyze just 1st iteration?
1556  m_L = cholmod_analyze(m_cholmodNormal, &m_cholmodCommon);
1557 
1558  // create cholmod cholesky factor
1559  // CHOLMOD will choose LLT or LDLT decomposition based on the characteristics of the matrix.
1560  cholmod_factorize(m_cholmodNormal, m_L, &m_cholmodCommon);
1561 
1562  // check for "matrix not positive definite" error
1563  if (m_cholmodCommon.status == CHOLMOD_NOT_POSDEF) {
1564  QString msg = "Matrix NOT positive-definite: failure at column " + toString((int) m_L->minor);
1565 // throw IException(IException::User, msg, _FILEINFO_);
1566  error(msg);
1567  emit(finished());
1568  return false;
1569  }
1570 
1571  // cholmod solution and right-hand side vectors
1572  cholmod_dense *x, *b;
1573 
1574  // initialize right-hand side vector
1575  b = cholmod_zeros(m_cholmodNormal->nrow, 1, m_cholmodNormal->xtype, &m_cholmodCommon);
1576 
1577  // copy right-hand side vector into b
1578  double *px = (double*)b->x;
1579  for (int i = 0; i < m_rank; i++) {
1580  px[i] = m_RHS[i];
1581  }
1582 
1583  // cholmod solve
1584  x = cholmod_solve(CHOLMOD_A, m_L, b, &m_cholmodCommon);
1585 
1586  // copy solution vector x out into m_imageSolution
1587  double *sx = (double*)x->x;
1588  for (int i = 0; i < m_rank; i++) {
1589  m_imageSolution[i] = sx[i];
1590  }
1591 
1592  // free cholmod structures
1593  cholmod_free_sparse(&m_cholmodNormal, &m_cholmodCommon);
1594  cholmod_free_dense(&b, &m_cholmodCommon);
1595  cholmod_free_dense(&x, &m_cholmodCommon);
1596 
1597  return true;
1598  }
1599 
1600 
1612  bool BundleAdjust::loadCholmodTriplet() {
1613 
1614  if ( m_iteration == 1 ) {
1615  int numElements = m_sparseNormals.numberOfElements();
1616  m_cholmodTriplet = cholmod_allocate_triplet(m_rank, m_rank, numElements,
1617  -1, CHOLMOD_REAL, &m_cholmodCommon);
1618 
1619  if ( !m_cholmodTriplet ) {
1620  outputBundleStatus("\nTriplet allocation failure\n");
1621  return false;
1622  }
1623 
1624  m_cholmodTriplet->nnz = 0;
1625  }
1626 
1627  int *tripletColumns = (int*) m_cholmodTriplet->i;
1628  int *tripletRows = (int*) m_cholmodTriplet->j;
1629  double *tripletValues = (double*)m_cholmodTriplet->x;
1630 
1631  double entryValue;
1632 
1633  int numEntries = 0;
1634 
1635  int numBlockcolumns = m_sparseNormals.size();
1636  for (int columnIndex = 0; columnIndex < numBlockcolumns; columnIndex++) {
1637 
1638  SparseBlockColumnMatrix *normalsColumn = m_sparseNormals[columnIndex];
1639 
1640  if ( !normalsColumn ) {
1641  QString status = "\nSparseBlockColumnMatrix retrieval failure at column " +
1642  QString::number(columnIndex);
1643  outputBundleStatus(status);
1644  return false;
1645  }
1646 
1647  int numLeadingColumns = normalsColumn->startColumn();
1648 
1649  QMapIterator< int, LinearAlgebra::Matrix * > it(*normalsColumn);
1650 
1651  while ( it.hasNext() ) {
1652  it.next();
1653 
1654  int rowIndex = it.key();
1655 
1656  // note: as the normal equations matrix is symmetric, the # of leading rows for a block is
1657  // equal to the # of leading columns for a block column at the "rowIndex" position
1658  int numLeadingRows = m_sparseNormals.at(rowIndex)->startColumn();
1659 
1660  LinearAlgebra::Matrix *normalsBlock = it.value();
1661  if ( !normalsBlock ) {
1662  QString status = "\nmatrix block retrieval failure at column ";
1663  status.append(columnIndex);
1664  status.append(", row ");
1665  status.append(rowIndex);
1666  outputBundleStatus(status);
1667  status = "Total # of block columns: " + QString::number(numBlockcolumns);
1668  outputBundleStatus(status);
1669  status = "Total # of blocks: " + QString::number(m_sparseNormals.numberOfBlocks());
1670  outputBundleStatus(status);
1671  return false;
1672  }
1673 
1674  if ( columnIndex == rowIndex ) { // diagonal block (upper-triangular)
1675  for (unsigned ii = 0; ii < normalsBlock->size1(); ii++) {
1676  for (unsigned jj = ii; jj < normalsBlock->size2(); jj++) {
1677  entryValue = normalsBlock->at_element(ii,jj);
1678  int entryColumnIndex = jj + numLeadingColumns;
1679  int entryRowIndex = ii + numLeadingRows;
1680 
1681  if ( m_iteration == 1 ) {
1682  tripletColumns[numEntries] = entryColumnIndex;
1683  tripletRows[numEntries] = entryRowIndex;
1684  m_cholmodTriplet->nnz++;
1685  }
1686 
1687  tripletValues[numEntries] = entryValue;
1688 
1689  numEntries++;
1690  }
1691  }
1692  }
1693  else { // off-diagonal block (square)
1694  for (unsigned ii = 0; ii < normalsBlock->size1(); ii++) {
1695  for (unsigned jj = 0; jj < normalsBlock->size2(); jj++) {
1696  entryValue = normalsBlock->at_element(ii,jj);
1697  int entryColumnIndex = jj + numLeadingColumns;
1698  int entryRowIndex = ii + numLeadingRows;
1699 
1700  if ( m_iteration ==1 ) {
1701  tripletColumns[numEntries] = entryRowIndex;
1702  tripletRows[numEntries] = entryColumnIndex;
1703  m_cholmodTriplet->nnz++;
1704  }
1705 
1706  tripletValues[numEntries] = entryValue;
1707 
1708  numEntries++;
1709  }
1710  }
1711  }
1712  }
1713  }
1714 
1715  return true;
1716  }
1717 
1718 
1727  bool BundleAdjust::cholmodInverse() {
1728  int i, j;
1729 
1730  // allocate matrix inverse
1731  m_normalInverse.resize(m_rank);
1732 
1733  cholmod_dense *x; // solution vector
1734  cholmod_dense *b; // right-hand side (column vectors of identity)
1735 
1736  b = cholmod_zeros ( m_rank, 1, CHOLMOD_REAL, &m_cholmodCommon ) ;
1737  double *pb = (double*)b->x;
1738 
1739  double *px = NULL;
1740 
1741  for (i = 0; i < m_rank; i++) {
1742  if ( i > 0 ) {
1743  pb[i-1] = 0.0;
1744  }
1745  pb[i] = 1.0;
1746 
1747  x = cholmod_solve ( CHOLMOD_A, m_L, b, &m_cholmodCommon ) ;
1748  px = (double*)x->x;
1749 
1750  // store solution in corresponding column of inverse
1751  for (j = 0; j <= i; j++) {
1752  m_normalInverse(j, i) = px[j];
1753  }
1754 
1755  cholmod_free_dense(&x,&m_cholmodCommon);
1756  }
1757 
1758  cholmod_free_dense(&b,&m_cholmodCommon);
1759 
1760  return true;
1761  }
1762 
1763 
1776  bool BundleAdjust::invert3x3(symmetric_matrix<double, upper> &m) {
1777  double det;
1778  double den;
1779 
1780  boost::numeric::ublas::symmetric_matrix< double, upper > c = m;
1781 
1782  den = m(0, 0) * (m(1, 1) * m(2, 2) - m(1, 2) * m(2, 1))
1783  - m(0, 1) * (m(1, 0) * m(2, 2) - m(1, 2) * m(2, 0))
1784  + m(0, 2) * (m(1, 0) * m(2, 1) - m(1, 1) * m(2, 0));
1785 
1786  // check for divide by zero
1787  if (fabs(den) < 1.0e-100) {
1788  return false;
1789  }
1790 
1791  det = 1.0 / den;
1792 
1793  m(0, 0) = (c(1, 1) * c(2, 2) - c(1, 2) * c(2, 1)) * det;
1794  m(0, 1) = (c(0, 2) * c(2, 1) - c(0, 1) * c(2, 2)) * det;
1795  m(0, 2) = (c(0, 1) * c(1, 2) - c(0, 2) * c(1, 1)) * det;
1796  m(1, 1) = (c(0, 0) * c(2, 2) - c(0, 2) * c(2, 0)) * det;
1797  m(1, 2) = (c(0, 2) * c(1, 0) - c(0, 0) * c(1, 2)) * det;
1798  m(2, 2) = (c(0, 0) * c(1, 1) - c(0, 1) * c(1, 0)) * det;
1799 
1800  return true;
1801  }
1802 
1803 
1823  bool BundleAdjust::computePartials(matrix<double> &coeffTarget,
1824  matrix<double> &coeffImage,
1825  matrix<double> &coeffPoint3D,
1826  vector<double> &coeffRHS,
1827  BundleMeasure &measure,
1828  BundleControlPoint &point) {
1829 
1830  // These vectors are either body-fixed latitudinal (lat/lon/radius) or rectangular (x/y/z)
1831  // depending on the value of coordinate type in SurfacePoint
1832  std::vector<double> lookBWRTCoord1;
1833  std::vector<double> lookBWRTCoord2;
1834  std::vector<double> lookBWRTCoord3;
1835 
1836  Camera *measureCamera = NULL;
1837 
1838  double measuredX, computedX, measuredY, computedY;
1839  double deltaX, deltaY;
1840  double observationSigma;
1841  double observationWeight;
1842 
1843  measureCamera = measure.camera();
1844 
1845  const BundleObservationSolveSettingsQsp observationSolveSettings =
1846  measure.observationSolveSettings();
1847  BundleObservationQsp observation = measure.parentBundleObservation();
1848 
1849  int numImagePartials = observation->numberParameters();
1850 
1851  // we're saving the number of image partials in m_previousNumberImagePartials
1852  // to compare to the previous computePartials call to avoid unnecessary resizing of the
1853  // coeffImage matrix
1854  if (numImagePartials != m_previousNumberImagePartials) {
1855  coeffImage.resize(2,numImagePartials);
1856  m_previousNumberImagePartials = numImagePartials;
1857  }
1858 
1859  // clear partial derivative matrices and vectors
1860  if (m_bundleSettings->solveTargetBody()) {
1861  coeffTarget.clear();
1862  }
1863 
1864  coeffImage.clear();
1865  coeffPoint3D.clear();
1866  coeffRHS.clear();
1867 
1868  // no need to call SetImage for framing camera ( CameraType = 0 )
1869  if (measureCamera->GetCameraType() != 0) {
1870  // Set the Spice to the measured point. A framing camera exposes the entire image at one time.
1871  // It will have a single set of Spice for the entire image. Scanning cameras may populate a single
1872  // image with multiple exposures, each with a unique set of Spice. SetImage needs to be called
1873  // repeatedly for these images to point to the Spice for the current pixel.
1874  measureCamera->SetImage(measure.sample(), measure.line());
1875  }
1876 
1877  // REMOVE
1878  SurfacePoint surfacePoint = point.adjustedSurfacePoint();
1879  // REMOVE
1880 
1881  // Compute the look vector in instrument coordinates based on time of observation and apriori
1882  // lat/lon/radius. As of 05/15/2019, this call no longer does the back-of-planet test. An optional
1883  // bool argument was added CameraGroundMap::GetXY to turn off the test.
1884  if (!(measureCamera->GroundMap()->GetXY(point.adjustedSurfacePoint(),
1885  &computedX, &computedY, false))) {
1886  QString msg = "Unable to map apriori surface point for measure ";
1887  msg += measure.cubeSerialNumber() + " on point " + point.id() + " into focal plane";
1888  throw IException(IException::User, msg, _FILEINFO_);
1889  }
1890 
1891  // Retrieve the coordinate type (latitudinal or rectangular) and compute the partials for
1892  // the fixed point with respect to each coordinate in Body-Fixed
1893  SurfacePoint::CoordinateType type = m_bundleSettings->controlPointCoordTypeBundle();
1894  lookBWRTCoord1 = point.adjustedSurfacePoint().Partial(type, SurfacePoint::One);
1895  lookBWRTCoord2 = point.adjustedSurfacePoint().Partial(type, SurfacePoint::Two);
1896  lookBWRTCoord3 = point.adjustedSurfacePoint().Partial(type, SurfacePoint::Three);
1897 
1898  int index = 0;
1899  if (m_bundleSettings->solveTargetBody() && m_bundleSettings->solvePoleRA()) {
1900  measureCamera->GroundMap()->GetdXYdTOrientation(SpiceRotation::WRT_RightAscension, 0,
1901  &coeffTarget(0, index),
1902  &coeffTarget(1, index));
1903  index++;
1904  }
1905 
1906  if (m_bundleSettings->solveTargetBody() && m_bundleSettings->solvePoleRAVelocity()) {
1907  measureCamera->GroundMap()->GetdXYdTOrientation(SpiceRotation::WRT_RightAscension, 1,
1908  &coeffTarget(0, index),
1909  &coeffTarget(1, index));
1910  index++;
1911  }
1912 
1913  if (m_bundleSettings->solveTargetBody() && m_bundleSettings->solvePoleDec()) {
1914  measureCamera->GroundMap()->GetdXYdTOrientation(SpiceRotation::WRT_Declination, 0,
1915  &coeffTarget(0, index),
1916  &coeffTarget(1, index));
1917  index++;
1918  }
1919 
1920  if (m_bundleSettings->solveTargetBody() && m_bundleSettings->solvePoleDecVelocity()) {
1921  measureCamera->GroundMap()->GetdXYdTOrientation(SpiceRotation::WRT_Declination, 1,
1922  &coeffTarget(0, index),
1923  &coeffTarget(1, index));
1924  index++;
1925  }
1926 
1927  if (m_bundleSettings->solveTargetBody() && m_bundleSettings->solvePM()) {
1928  measureCamera->GroundMap()->GetdXYdTOrientation(SpiceRotation::WRT_Twist, 0,
1929  &coeffTarget(0, index),
1930  &coeffTarget(1, index));
1931  index++;
1932  }
1933 
1934  if (m_bundleSettings->solveTargetBody() && m_bundleSettings->solvePMVelocity()) {
1935  measureCamera->GroundMap()->GetdXYdTOrientation(SpiceRotation::WRT_Twist, 1,
1936  &coeffTarget(0, index),
1937  &coeffTarget(1, index));
1938  index++;
1939  }
1940 
1941  if (m_bundleSettings->solveTargetBody() && m_bundleTargetBody->solveMeanRadius()) {
1942  std::vector<double> lookBWRTMeanRadius =
1943  measureCamera->GroundMap()->MeanRadiusPartial(surfacePoint,
1944  m_bundleTargetBody->meanRadius());
1945 
1946  measureCamera->GroundMap()->GetdXYdPoint(lookBWRTMeanRadius, &coeffTarget(0, index),
1947  &coeffTarget(1, index));
1948  index++;
1949  }
1950 
1951  if (m_bundleSettings->solveTargetBody() && m_bundleTargetBody->solveTriaxialRadii()) {
1952 
1953  std::vector<double> lookBWRTRadiusA =
1954  measureCamera->GroundMap()->EllipsoidPartial(surfacePoint,
1955  CameraGroundMap::WRT_MajorAxis);
1956 
1957  measureCamera->GroundMap()->GetdXYdPoint(lookBWRTRadiusA, &coeffTarget(0, index),
1958  &coeffTarget(1, index));
1959  index++;
1960 
1961  std::vector<double> lookBWRTRadiusB =
1962  measureCamera->GroundMap()->EllipsoidPartial(surfacePoint,
1963  CameraGroundMap::WRT_MinorAxis);
1964 
1965  measureCamera->GroundMap()->GetdXYdPoint(lookBWRTRadiusB, &coeffTarget(0, index),
1966  &coeffTarget(1, index));
1967  index++;
1968 
1969  std::vector<double> lookBWRTRadiusC =
1970  measureCamera->GroundMap()->EllipsoidPartial(surfacePoint,
1971  CameraGroundMap::WRT_PolarAxis);
1972 
1973  measureCamera->GroundMap()->GetdXYdPoint(lookBWRTRadiusC, &coeffTarget(0, index),
1974  &coeffTarget(1, index));
1975  index++;
1976  }
1977 
1978  index = 0;
1979 
1980  if (observationSolveSettings->instrumentPositionSolveOption() !=
1981  BundleObservationSolveSettings::NoPositionFactors) {
1982 
1983  int numCamPositionCoefficients =
1984  observationSolveSettings->numberCameraPositionCoefficientsSolved();
1985 
1986  // Add the partial for the x coordinate of the position (differentiating
1987  // point(x,y,z) - spacecraftPosition(x,y,z) in J2000
1988  for (int cameraCoef = 0; cameraCoef < numCamPositionCoefficients; cameraCoef++) {
1989  measureCamera->GroundMap()->GetdXYdPosition(SpicePosition::WRT_X, cameraCoef,
1990  &coeffImage(0, index),
1991  &coeffImage(1, index));
1992  index++;
1993  }
1994 
1995  // Add the partial for the y coordinate of the position
1996  for (int cameraCoef = 0; cameraCoef < numCamPositionCoefficients; cameraCoef++) {
1997  measureCamera->GroundMap()->GetdXYdPosition(SpicePosition::WRT_Y, cameraCoef,
1998  &coeffImage(0, index),
1999  &coeffImage(1, index));
2000  index++;
2001  }
2002 
2003  // Add the partial for the z coordinate of the position
2004  for (int cameraCoef = 0; cameraCoef < numCamPositionCoefficients; cameraCoef++) {
2005  measureCamera->GroundMap()->GetdXYdPosition(SpicePosition::WRT_Z, cameraCoef,
2006  &coeffImage(0, index),
2007  &coeffImage(1, index));
2008  index++;
2009  }
2010 
2011  }
2012 
2013  if (observationSolveSettings->instrumentPointingSolveOption() !=
2014  BundleObservationSolveSettings::NoPointingFactors) {
2015 
2016  int numCamAngleCoefficients =
2017  observationSolveSettings->numberCameraAngleCoefficientsSolved();
2018 
2019  // Add the partials for ra
2020  for (int cameraCoef = 0; cameraCoef < numCamAngleCoefficients; cameraCoef++) {
2021  measureCamera->GroundMap()->GetdXYdOrientation(SpiceRotation::WRT_RightAscension,
2022  cameraCoef, &coeffImage(0, index),
2023  &coeffImage(1, index));
2024  index++;
2025  }
2026 
2027  // Add the partials for dec
2028  for (int cameraCoef = 0; cameraCoef < numCamAngleCoefficients; cameraCoef++) {
2029  measureCamera->GroundMap()->GetdXYdOrientation(SpiceRotation::WRT_Declination,
2030  cameraCoef, &coeffImage(0, index),
2031  &coeffImage(1, index));
2032  index++;
2033  }
2034 
2035  // Add the partial for twist if necessary
2036  if (observationSolveSettings->solveTwist()) {
2037  for (int cameraCoef = 0; cameraCoef < numCamAngleCoefficients; cameraCoef++) {
2038  measureCamera->GroundMap()->GetdXYdOrientation(SpiceRotation::WRT_Twist,
2039  cameraCoef, &coeffImage(0, index),
2040  &coeffImage(1, index));
2041  index++;
2042  }
2043  }
2044  }
2045 
2046  // Complete partials calculations for 3D point (latitudinal or rectangular)
2047  measureCamera->GroundMap()->GetdXYdPoint(lookBWRTCoord1,
2048  &coeffPoint3D(0, 0),
2049  &coeffPoint3D(1, 0));
2050  measureCamera->GroundMap()->GetdXYdPoint(lookBWRTCoord2,
2051  &coeffPoint3D(0, 1),
2052  &coeffPoint3D(1, 1));
2053  measureCamera->GroundMap()->GetdXYdPoint(lookBWRTCoord3,
2054  &coeffPoint3D(0, 2),
2055  &coeffPoint3D(1, 2));
2056 
2057  // right-hand side (measured - computed)
2058  measuredX = measure.focalPlaneMeasuredX();
2059  measuredY = measure.focalPlaneMeasuredY();
2060 
2061  deltaX = measuredX - computedX;
2062  deltaY = measuredY - computedY;
2063 
2064  coeffRHS(0) = deltaX;
2065  coeffRHS(1) = deltaY;
2066 
2067  // residual prob distribution is calculated even if there is no maximum likelihood estimation
2068  double obsValue = deltaX / measureCamera->PixelPitch();
2069  m_bundleResults.addResidualsProbabilityDistributionObservation(obsValue);
2070 
2071  obsValue = deltaY / measureCamera->PixelPitch();
2072  m_bundleResults.addResidualsProbabilityDistributionObservation(obsValue);
2073 
2074  observationSigma = 1.4 * measureCamera->PixelPitch();
2075  observationWeight = 1.0 / observationSigma;
2076 
2077  if (m_bundleResults.numberMaximumLikelihoodModels()
2078  > m_bundleResults.maximumLikelihoodModelIndex()) {
2079  // if maximum likelihood estimation is being used
2080  double residualR2ZScore
2081  = sqrt(deltaX * deltaX + deltaY * deltaY) / observationSigma / sqrt(2.0);
2082  //dynamically build the cumulative probability distribution of the R^2 residual Z Scores
2083  m_bundleResults.addProbabilityDistributionObservation(residualR2ZScore);
2084  int currentModelIndex = m_bundleResults.maximumLikelihoodModelIndex();
2085  observationWeight *= m_bundleResults.maximumLikelihoodModelWFunc(currentModelIndex)
2086  .sqrtWeightScaler(residualR2ZScore);
2087  }
2088 
2089  // multiply coefficients by observation weight
2090  coeffImage *= observationWeight;
2091  coeffPoint3D *= observationWeight;
2092  coeffRHS *= observationWeight;
2093 
2094  if (m_bundleSettings->solveTargetBody()) {
2095  coeffTarget *= observationWeight;
2096  }
2097 
2098  return true;
2099  }
2100 
2101 
2105  void BundleAdjust::applyParameterCorrections() {
2106  emit(statusBarUpdate("Updating Parameters"));
2107  int t = 0;
2108 
2109  // TODO - update target body parameters if in solution
2110  // note these come before BundleObservation parameters in normal equations matrix
2111  if (m_bundleSettings->solveTargetBody()) {
2112  int numTargetBodyParameters = m_bundleTargetBody->numberParameters();
2113 
2114  m_bundleTargetBody->applyParameterCorrections(subrange(m_imageSolution,0,
2115  numTargetBodyParameters));
2116 
2117  t += numTargetBodyParameters;
2118  }
2119 
2120  // Update spice for each BundleObservation
2121  int numObservations = m_bundleObservations.size();
2122  for (int i = 0; i < numObservations; i++) {
2123  BundleObservationQsp observation = m_bundleObservations.at(i);
2124 
2125  int numParameters = observation->numberParameters();
2126 
2127  observation->applyParameterCorrections(subrange(m_imageSolution,t,t+numParameters));
2128 
2129  if (m_bundleSettings->solveTargetBody()) {
2130  observation->updateBodyRotation();
2131  }
2132 
2133  t += numParameters;
2134  }
2135  // TODO - When BundleXYZ gets merged into dev, go with Ken's version of merging the updating of
2136  // of the adjusted surface point into BundleControlPoint.
2137 
2138  int pointIndex = 0;
2139  int numControlPoints = m_bundleControlPoints.size();
2140 
2141  for (int i = 0; i < numControlPoints; i++) {
2142  BundleControlPointQsp point = m_bundleControlPoints.at(i);
2143 
2144  if (point->isRejected()) {
2145  pointIndex++;
2146  continue;
2147  }
2148 
2149  point->applyParameterCorrections(m_imageSolution, m_sparseNormals,
2150  m_bundleTargetBody);
2151  pointIndex++;
2152 
2153  } // end loop over point corrections
2154  }
2155 
2156 
2168  double BundleAdjust::computeResiduals() {
2169  emit(statusBarUpdate("Computing Residuals"));
2170  double vtpv = 0.0;
2171  double vtpvControl = 0.0;
2172  double vtpvImage = 0.0;
2173  double weight;
2174  double v, vx, vy;
2175 
2176  // clear residual stats vectors
2177  m_xResiduals.Reset();
2178  m_yResiduals.Reset();
2179  m_xyResiduals.Reset();
2180 
2181  // vtpv for image coordinates
2182  int numObjectPoints = m_bundleControlPoints.size();
2183 
2184  for (int i = 0; i < numObjectPoints; i++) {
2185 
2186  BundleControlPointQsp point = m_bundleControlPoints.at(i);
2187 
2188  point->computeResiduals();
2189 
2190  int numMeasures = point->numberOfMeasures();
2191  for (int j = 0; j < numMeasures; j++) {
2192  const BundleMeasureQsp measure = point->at(j);
2193 
2194  weight = 1.4 * (measure->camera())->PixelPitch();
2195  weight = 1.0 / weight;
2196  weight *= weight;
2197 
2198  vx = measure->focalPlaneMeasuredX() - measure->focalPlaneComputedX();
2199  vy = measure->focalPlaneMeasuredY() - measure->focalPlaneComputedY();
2200 
2201  // if rejected, don't include in statistics
2202  if (measure->isRejected()) {
2203  continue;
2204  }
2205 
2206  m_xResiduals.AddData(vx);
2207  m_yResiduals.AddData(vy);
2208  m_xyResiduals.AddData(vx);
2209  m_xyResiduals.AddData(vy);
2210 
2211  vtpv += vx * vx * weight + vy * vy * weight;
2212  }
2213  }
2214 
2215  // add vtpv from constrained 3D points
2216  int pointIndex = 0; // *** TODO *** This does not appear to be used. Delete? DAC 07-14-2017
2217  for (int i = 0; i < numObjectPoints; i++) {
2218  BundleControlPointQsp bundleControlPoint = m_bundleControlPoints.at(i);
2219 
2220  // get weight and correction vector for this point
2221  boost::numeric::ublas::bounded_vector<double, 3> weights = bundleControlPoint->weights();
2222  boost::numeric::ublas::bounded_vector<double, 3> corrections =
2223  bundleControlPoint->corrections();
2224 
2225  if ( weights(0) > 0.0 ) {
2226  vtpvControl += corrections(0) * corrections(0) * weights(0);
2227  }
2228  if ( weights(1) > 0.0 ) {
2229  vtpvControl += corrections(1) * corrections(1) * weights(1);
2230  }
2231  if ( weights(2) > 0.0 ) {
2232  vtpvControl += corrections(2) * corrections(2) * weights(2);
2233  }
2234 
2235  pointIndex++;
2236  }
2237 
2238  // add vtpv from constrained image parameters
2239  for (int i = 0; i < m_bundleObservations.size(); i++) {
2240  BundleObservationQsp observation = m_bundleObservations.at(i);
2241 
2242  // get weight and correction vector for this observation
2243  const LinearAlgebra::Vector &weights = observation->parameterWeights();
2244  const LinearAlgebra::Vector &corrections = observation->parameterCorrections();
2245 
2246  for (int j = 0; j < (int)corrections.size(); j++) {
2247  if (weights[j] > 0.0) {
2248  v = corrections[j];
2249  vtpvImage += v * v * weights[j];
2250  }
2251  }
2252  }
2253 
2254  // TODO - add vtpv from constrained target body parameters
2255  double vtpvTargetBody = 0.0;
2256  if ( m_bundleTargetBody) {
2257  vtpvTargetBody = m_bundleTargetBody->vtpv();
2258  }
2259 
2260  vtpv = vtpv + vtpvControl + vtpvImage + vtpvTargetBody;
2261 
2262  // Compute rms for all image coordinate residuals
2263  // separately for x, y, then x and y together
2264  m_bundleResults.setRmsXYResiduals(m_xResiduals.Rms(), m_yResiduals.Rms(), m_xyResiduals.Rms());
2265 
2266  return vtpv;
2267  }
2268 
2269 
2276  bool BundleAdjust::wrapUp() {
2277  // compute residuals in pixels
2278 
2279  // vtpv for image coordinates
2280  for (int i = 0; i < m_bundleControlPoints.size(); i++) {
2281  BundleControlPointQsp point = m_bundleControlPoints.at(i);
2282  point->computeResiduals();
2283  }
2284 
2285  computeBundleStatistics();
2286 
2287  return true;
2288  }
2289 
2290 
2305  bool BundleAdjust::computeRejectionLimit() {
2306  double vx, vy;
2307 
2308  int numResiduals = m_bundleResults.numberObservations() / 2;
2309 
2310  std::vector<double> residuals;
2311 
2312  residuals.resize(numResiduals);
2313 
2314  // load magnitude (squared) of residual vector
2315  int residualIndex = 0;
2316  int numObjectPoints = m_bundleControlPoints.size();
2317  for (int i = 0; i < numObjectPoints; i++) {
2318 
2319  const BundleControlPointQsp point = m_bundleControlPoints.at(i);
2320 
2321  if ( point->isRejected() ) {
2322  continue;
2323  }
2324 
2325  int numMeasures = point->numberOfMeasures();
2326  for (int j = 0; j < numMeasures; j++) {
2327 
2328  const BundleMeasureQsp measure = point->at(j);
2329 
2330  if ( measure->isRejected() ) {
2331  continue;
2332  }
2333 
2334  vx = measure->sampleResidual();
2335  vy = measure->lineResidual();
2336 
2337  residuals[residualIndex] = sqrt(vx*vx + vy*vy);
2338 
2339  residualIndex++;
2340  }
2341  }
2342 
2343  // sort vectors
2344  std::sort(residuals.begin(), residuals.end());
2345 
2346  double median;
2347  double medianDev;
2348  double mad;
2349 
2350  int midpointIndex = numResiduals / 2;
2351 
2352  if ( numResiduals % 2 == 0 ) {
2353  median = (residuals[midpointIndex - 1] + residuals[midpointIndex]) / 2;
2354  }
2355  else {
2356  median = residuals[midpointIndex];
2357  }
2358 
2359  // compute M.A.D.
2360  for (int i = 0; i < numResiduals; i++) {
2361  residuals[i] = fabs(residuals[i] - median);
2362  }
2363 
2364  std::sort(residuals.begin(), residuals.end());
2365 
2366  if ( numResiduals % 2 == 0 ) {
2367  medianDev = (residuals[midpointIndex - 1] + residuals[midpointIndex]) / 2;
2368  }
2369  else {
2370  medianDev = residuals[midpointIndex];
2371  }
2372 
2373  QString status = "\nmedian deviation: ";
2374  status.append(QString("%1").arg(medianDev));
2375  status.append("\n");
2376  outputBundleStatus(status);
2377 
2378  mad = 1.4826 * medianDev;
2379 
2380  status = "\nmad: ";
2381  status.append(QString("%1").arg(mad));
2382  status.append("\n");
2383  outputBundleStatus(status);
2384 
2385  m_bundleResults.setRejectionLimit(median
2386  + m_bundleSettings->outlierRejectionMultiplier() * mad);
2387 
2388  status = "\nRejection Limit: ";
2389  status.append(QString("%1").arg(m_bundleResults.rejectionLimit()));
2390  status.append("\n");
2391  outputBundleStatus(status);
2392 
2393  return true;
2394  }
2395 
2396 
2404  bool BundleAdjust::flagOutliers() {
2405  double vx, vy;
2406  int numRejected;
2407  int totalNumRejected = 0;
2408 
2409  int maxResidualIndex;
2410  double maxResidual;
2411  double sumSquares;
2412  double usedRejectionLimit = m_bundleResults.rejectionLimit();
2413 
2414  // TODO What to do if usedRejectionLimit is too low?
2415 
2416  int numComingBack = 0;
2417 
2418  int numObjectPoints = m_bundleControlPoints.size();
2419 
2420  outputBundleStatus("\n");
2421  for (int i = 0; i < numObjectPoints; i++) {
2422  BundleControlPointQsp point = m_bundleControlPoints.at(i);
2423 
2424  point->zeroNumberOfRejectedMeasures();
2425 
2426  numRejected = 0;
2427  maxResidualIndex = -1;
2428  maxResidual = -1.0;
2429 
2430  int numMeasures = point->numberOfMeasures();
2431  for (int j = 0; j < numMeasures; j++) {
2432 
2433  BundleMeasureQsp measure = point->at(j);
2434 
2435  vx = measure->sampleResidual();
2436  vy = measure->lineResidual();
2437 
2438  sumSquares = sqrt(vx*vx + vy*vy);
2439 
2440  // measure is good
2441  if ( sumSquares <= usedRejectionLimit ) {
2442 
2443  // was it previously rejected?
2444  if ( measure->isRejected() ) {
2445  QString status = "Coming back in: ";
2446  status.append(QString("%1").arg(point->id().toLatin1().data()));
2447  status.append("\r");
2448  outputBundleStatus(status);
2449  numComingBack++;
2450  m_controlNet->DecrementNumberOfRejectedMeasuresInImage(measure->cubeSerialNumber());
2451  }
2452 
2453  measure->setRejected(false);
2454  continue;
2455  }
2456 
2457  // if it's still rejected, skip it
2458  if ( measure->isRejected() ) {
2459  numRejected++;
2460  totalNumRejected++;
2461  continue;
2462  }
2463 
2464  if ( sumSquares > maxResidual ) {
2465  maxResidual = sumSquares;
2466  maxResidualIndex = j;
2467  }
2468  }
2469 
2470  // no observations above the current rejection limit for this 3D point
2471  if ( maxResidual == -1.0 || maxResidual <= usedRejectionLimit ) {
2472  point->setNumberOfRejectedMeasures(numRejected);
2473  continue;
2474  }
2475 
2476  // this is another kluge - if we only have two observations
2477  // we won't reject (for now)
2478  if ((numMeasures - (numRejected + 1)) < 2) {
2479  point->setNumberOfRejectedMeasures(numRejected);
2480  continue;
2481  }
2482 
2483  // otherwise, we have at least one observation
2484  // for this point whose residual is above the
2485  // current rejection limit - we'll flag the
2486  // worst of these as rejected
2487  BundleMeasureQsp rejected = point->at(maxResidualIndex);
2488  rejected->setRejected(true);
2489  numRejected++;
2490  point->setNumberOfRejectedMeasures(numRejected);
2491  m_controlNet->IncrementNumberOfRejectedMeasuresInImage(rejected->cubeSerialNumber());
2492  totalNumRejected++;
2493 
2494  // do we still have sufficient remaining observations for this 3D point?
2495  if ( ( numMeasures-numRejected ) < 2 ) {
2496  point->setRejected(true);
2497  QString status = "Rejecting Entire Point: ";
2498  status.append(QString("%1").arg(point->id().toLatin1().data()));
2499  status.append("\r");
2500  outputBundleStatus(status);
2501  }
2502  else
2503  point->setRejected(false);
2504 
2505  }
2506 
2507  int numberRejectedObservations = 2*totalNumRejected;
2508 
2509  QString status = "\nRejected Observations:";
2510  status.append(QString("%1").arg(numberRejectedObservations));
2511  status.append(" (Rejection Limit:");
2512  status.append(QString("%1").arg(usedRejectionLimit));
2513  status.append(")\n");
2514  outputBundleStatus(status);
2515 
2516  m_bundleResults.setNumberRejectedObservations(numberRejectedObservations);
2517 
2518  status = "\nMeasures that came back: ";
2519  status.append(QString("%1").arg(numComingBack));
2520  status.append("\n");
2521  outputBundleStatus(status);
2522 
2523  return true;
2524  }
2525 
2526 
2534  QList<ImageList *> BundleAdjust::imageLists() {
2535 
2536  if (m_imageLists.count() > 0) {
2537  return m_imageLists;
2538  }
2539  else if (m_serialNumberList->size() > 0) {
2540  ImageList *imgList = new ImageList;
2541  try {
2542  for (int i = 0; i < m_serialNumberList->size(); i++) {
2543  Image *image = new Image(m_serialNumberList->fileName(i));
2544  imgList->append(image);
2545  image->closeCube();
2546  }
2547  m_imageLists.append(imgList);
2548  }
2549  catch (IException &e) {
2550  QString msg = "Invalid image in serial number list\n";
2551  throw IException(IException::Programmer, msg, _FILEINFO_);
2552  }
2553  }
2554  else {
2555  QString msg = "No images used in bundle adjust\n";
2556  throw IException(IException::Programmer, msg, _FILEINFO_);
2557  }
2558 
2559  return m_imageLists;
2560  }
2561 
2562 
2583  bool BundleAdjust::errorPropagation() {
2584  emit(statusBarUpdate("Error Propagation"));
2585  // free unneeded memory
2586  cholmod_free_triplet(&m_cholmodTriplet, &m_cholmodCommon);
2587  cholmod_free_sparse(&m_cholmodNormal, &m_cholmodCommon);
2588 
2589  LinearAlgebra::Matrix T(3, 3);
2590  // *** TODO ***
2591  // Can any of the control point specific code be moved to BundleControlPoint?
2592 
2593  double sigma0Squared = m_bundleResults.sigma0() * m_bundleResults.sigma0();
2594 
2595  int numObjectPoints = m_bundleControlPoints.size();
2596 
2597  std::string currentTime = iTime::CurrentLocalTime().toLatin1().data();
2598 
2599  QString status = " Time: ";
2600  status.append(currentTime.c_str());
2601  status.append("\n\n");
2602  outputBundleStatus(status);
2603 
2604  // create and initialize array of 3x3 matrices for all object points
2605  std::vector< symmetric_matrix<double> > pointCovariances(numObjectPoints,
2606  symmetric_matrix<double>(3));
2607  for (int d = 0; d < numObjectPoints; d++) {
2608  pointCovariances[d].clear();
2609  }
2610 
2611  cholmod_dense *x; // solution vector
2612  cholmod_dense *b; // right-hand side (column vectors of identity)
2613 
2614  b = cholmod_zeros ( m_rank, 1, CHOLMOD_REAL, &m_cholmodCommon );
2615  double *pb = (double*)b->x;
2616 
2617  double *px = NULL;
2618 
2619  SparseBlockColumnMatrix inverseMatrix;
2620 
2621  // Create unique file name
2622  FileName matrixFile(m_bundleSettings->outputFilePrefix() + "inverseMatrix.dat");
2623  //???FileName matrixFile = FileName::createTempFile(m_bundleSettings.outputFilePrefix()
2624  //??? + "inverseMatrix.dat");
2625  // Create file handle
2626  QFile matrixOutput(matrixFile.expanded());
2627 
2628  // Check to see if creating the inverse correlation matrix is turned on
2629  if (m_bundleSettings->createInverseMatrix()) {
2630  // Open file to write to
2631  matrixOutput.open(QIODevice::WriteOnly);
2632  }
2633  QDataStream outStream(&matrixOutput);
2634 
2635  int i, j, k;
2636  int columnIndex = 0;
2637  int numColumns = 0;
2638  int numBlockColumns = m_sparseNormals.size();
2639  for (i = 0; i < numBlockColumns; i++) {
2640 
2641  // columns in this column block
2642  SparseBlockColumnMatrix *normalsColumn = m_sparseNormals.at(i);
2643  if (i == 0) {
2644  numColumns = normalsColumn->numberOfColumns();
2645  int numRows = normalsColumn->numberOfRows();
2646  inverseMatrix.insertMatrixBlock(i, numRows, numColumns);
2647  inverseMatrix.zeroBlocks();
2648  }
2649  else {
2650  if (normalsColumn->numberOfColumns() == numColumns) {
2651  int numRows = normalsColumn->numberOfRows();
2652  inverseMatrix.insertMatrixBlock(i, numRows, numColumns);
2653  inverseMatrix.zeroBlocks();
2654  }
2655  else {
2656  numColumns = normalsColumn->numberOfColumns();
2657 
2658  // reset inverseMatrix
2659  inverseMatrix.wipe();
2660 
2661  // insert blocks
2662  for (j = 0; j < (i+1); j++) {
2663  SparseBlockColumnMatrix *normalsRow = m_sparseNormals.at(j);
2664  int numRows = normalsRow->numberOfRows();
2665 
2666  inverseMatrix.insertMatrixBlock(j, numRows, numColumns);
2667  }
2668  }
2669  }
2670 
2671  int localCol = 0;
2672 
2673  // solve for inverse for nCols
2674  for (j = 0; j < numColumns; j++) {
2675  if ( columnIndex > 0 ) {
2676  pb[columnIndex - 1] = 0.0;
2677  }
2678  pb[columnIndex] = 1.0;
2679 
2680  x = cholmod_solve ( CHOLMOD_A, m_L, b, &m_cholmodCommon );
2681  px = (double*)x->x;
2682  int rp = 0;
2683 
2684  // store solution in corresponding column of inverse
2685  for (k = 0; k < inverseMatrix.size(); k++) {
2686  LinearAlgebra::Matrix *matrix = inverseMatrix.value(k);
2687 
2688  int sz1 = matrix->size1();
2689 
2690  for (int ii = 0; ii < sz1; ii++) {
2691  (*matrix)(ii,localCol) = px[ii + rp];
2692  }
2693  rp += matrix->size1();
2694  }
2695 
2696  columnIndex++;
2697  localCol++;
2698 
2699  cholmod_free_dense(&x,&m_cholmodCommon);
2700  }
2701 
2702  // save adjusted target body sigmas if solving for target
2703  if (m_bundleSettings->solveTargetBody() && i == 0) {
2704  vector< double > &adjustedSigmas = m_bundleTargetBody->adjustedSigmas();
2705  matrix< double > *targetCovMatrix = inverseMatrix.value(i);
2706 
2707  for (int z = 0; z < numColumns; z++)
2708  adjustedSigmas[z] = sqrt((*targetCovMatrix)(z,z))*m_bundleResults.sigma0();
2709  }
2710  // save adjusted image sigmas
2711  else {
2712  BundleObservationQsp observation;
2713  if (m_bundleSettings->solveTargetBody()) {
2714  observation = m_bundleObservations.at(i-1);
2715  }
2716  else {
2717  observation = m_bundleObservations.at(i);
2718  }
2719  vector< double > &adjustedSigmas = observation->adjustedSigmas();
2720  matrix< double > *imageCovMatrix = inverseMatrix.value(i);
2721  for ( int z = 0; z < numColumns; z++) {
2722  adjustedSigmas[z] = sqrt((*imageCovMatrix)(z,z))*m_bundleResults.sigma0();
2723  }
2724  }
2725 
2726  // Output the inverse matrix if requested
2727  if (m_bundleSettings->createInverseMatrix()) {
2728  outStream << inverseMatrix;
2729  }
2730 
2731  // now loop over all object points to sum contributions into 3x3 point covariance matrix
2732  int pointIndex = 0;
2733  for (j = 0; j < numObjectPoints; j++) {
2734  emit(pointUpdate(j+1));
2735  BundleControlPointQsp point = m_bundleControlPoints.at(pointIndex);
2736  if ( point->isRejected() ) {
2737  continue;
2738  }
2739 
2740  // only update point every 100 points
2741  if (j%100 == 0) {
2742  QString status = "\rError Propagation: Inverse Block ";
2743  status.append(QString::number(i+1));
2744  status.append(" of ");
2745  status.append(QString::number(numBlockColumns));
2746  status.append("; Point ");
2747  status.append(QString::number(j+1));
2748  status.append(" of ");
2749  status.append(QString::number(numObjectPoints));
2750  outputBundleStatus(status);
2751  }
2752 
2753  // get corresponding Q matrix
2754  // NOTE: we are getting a reference to the Q matrix stored
2755  // in the BundleControlPoint for speed (without the & it is dirt slow)
2756  SparseBlockRowMatrix &Q = point->cholmodQMatrix();
2757 
2758  T.clear();
2759 
2760  // get corresponding point covariance matrix
2761  boost::numeric::ublas::symmetric_matrix<double> &covariance = pointCovariances[pointIndex];
2762 
2763  // get firstQBlock - index i is the key into Q for firstQBlock
2764  LinearAlgebra::Matrix *firstQBlock = Q.value(i);
2765  if (!firstQBlock) {
2766  pointIndex++;
2767  continue;
2768  }
2769 
2770  // iterate over Q
2771  // secondQBlock is current map value
2772  QMapIterator< int, LinearAlgebra::Matrix * > it(Q);
2773  while ( it.hasNext() ) {
2774  it.next();
2775 
2776  int nKey = it.key();
2777 
2778  if (it.key() > i) {
2779  break;
2780  }
2781 
2782  LinearAlgebra::Matrix *secondQBlock = it.value();
2783 
2784  if ( !secondQBlock ) {// should never be NULL
2785  continue;
2786  }
2787 
2788  LinearAlgebra::Matrix *inverseBlock = inverseMatrix.value(it.key());
2789 
2790  if ( !inverseBlock ) {// should never be NULL
2791  continue;
2792  }
2793 
2794  T = prod(*inverseBlock, trans(*firstQBlock));
2795  T = prod(*secondQBlock,T);
2796 
2797  if (nKey != i) {
2798  T += trans(T);
2799  }
2800 
2801  try {
2802  covariance += T;
2803  }
2804 
2805  catch (std::exception &e) {
2806  outputBundleStatus("\n\n");
2807  QString msg = "Input data and settings are not sufficiently stable "
2808  "for error propagation.";
2809  throw IException(IException::User, msg, _FILEINFO_);
2810  }
2811  }
2812  pointIndex++;
2813  }
2814  }
2815 
2816  if (m_bundleSettings->createInverseMatrix()) {
2817  // Close the file.
2818  matrixOutput.close();
2819  // Save the location of the "covariance" matrix
2820  m_bundleResults.setCorrMatCovFileName(matrixFile);
2821  }
2822 
2823  // can free sparse normals now
2824  m_sparseNormals.wipe();
2825 
2826  // free b (right-hand side vector
2827  cholmod_free_dense(&b,&m_cholmodCommon);
2828 
2829  outputBundleStatus("\n\n");
2830 
2831  currentTime = Isis::iTime::CurrentLocalTime().toLatin1().data();
2832 
2833  status = "\rFilling point covariance matrices: Time ";
2834  status.append(currentTime.c_str());
2835  outputBundleStatus(status);
2836  outputBundleStatus("\n\n");
2837 
2838  // now loop over points again and set final covariance stuff
2839  // *** TODO *** Can this loop go into BundleControlPoint
2840  int pointIndex = 0;
2841  for (j = 0; j < numObjectPoints; j++) {
2842 
2843  BundleControlPointQsp point = m_bundleControlPoints.at(pointIndex);
2844 
2845  if ( point->isRejected() ) {
2846  continue;
2847  }
2848 
2849  if (j%100 == 0) {
2850  status = "\rError Propagation: Filling point covariance matrices ";
2851  status.append(QString("%1").arg(j+1));
2852  status.append(" of ");
2853  status.append(QString("%1").arg(numObjectPoints));
2854  status.append("\r");
2855  outputBundleStatus(status);
2856  }
2857 
2858  // get corresponding point covariance matrix
2859  boost::numeric::ublas::symmetric_matrix<double> &covariance = pointCovariances[pointIndex];
2860 
2861  // Update and reset the matrix
2862  // Get the Limiting Error Propagation uncertainties: sigmas for coordinate 1, 2, and 3 in meters
2863  //
2864  SurfacePoint SurfacePoint = point->adjustedSurfacePoint();
2865 
2866  // Get the TEP by adding the corresponding members of pCovar and covariance
2867  boost::numeric::ublas::symmetric_matrix <double,boost::numeric::ublas::upper> pCovar;
2868 
2869  if (m_bundleSettings->controlPointCoordTypeBundle() == SurfacePoint::Latitudinal) {
2870  pCovar = SurfacePoint.GetSphericalMatrix(SurfacePoint::Kilometers);
2871  }
2872  else {
2873  // Assume Rectangular coordinates
2874  pCovar = SurfacePoint.GetRectangularMatrix(SurfacePoint::Kilometers);
2875  }
2876  pCovar += covariance;
2877  pCovar *= sigma0Squared;
2878 
2879  // debug lines
2880  // if (j < 3) {
2881  // std::cout << " Adjusted surface point ..." << std::endl;
2882  // std:: cout << " sigmaLat (radians) = " << sqrt(pCovar(0,0)) << std::endl;
2883  // std:: cout << " sigmaLon (radians) = " << sqrt(pCovar(1,1)) << std::endl;
2884  // std:: cout << " sigmaRad (km) = " << sqrt(pCovar(2,2)) << std::endl;
2885  // std::cout << " Adjusted matrix = " << std::endl;
2886  // std::cout << " " << pCovar(0,0) << " " << pCovar(0,1) << " "
2887  // << pCovar(0,2) << std::endl;
2888  // std::cout << " " << pCovar(1,0) << " " << pCovar(1,1) << " "
2889  // << pCovar(1,2) << std::endl;
2890  // std::cout << " " << pCovar(2,0) << " " << pCovar(2,1) << " "
2891  // << pCovar(2,2) << std::endl;
2892  // }
2893  // end debug
2894 
2895  // Distance units are km**2
2896  SurfacePoint.SetMatrix(m_bundleSettings->controlPointCoordTypeBundle(),pCovar);
2897  point->setAdjustedSurfacePoint(SurfacePoint);
2898  // // debug lines
2899  // if (j < 3) {
2900  // boost::numeric::ublas::symmetric_matrix <double,boost::numeric::ublas::upper> recCovar;
2901  // recCovar = SurfacePoint.GetRectangularMatrix(SurfacePoint::Meters);
2902  // std:: cout << " sigmaLat (meters) = " <<
2903  // point->adjustedSurfacePoint().GetSigmaDistance(SurfacePoint::Latitudinal,
2904  // SurfacePoint::One).meters() << std::endl;
2905  // std:: cout << " sigmaLon (meters) = " <<
2906  // point->adjustedSurfacePoint().GetSigmaDistance(SurfacePoint::Latitudinal,
2907  // SurfacePoint::Two).meters() << std::endl;
2908  // std:: cout << " sigmaRad (km) = " << sqrt(pCovar(2,2)) << std::endl;
2909  // std::cout << "Rectangular matrix with radius in meters" << std::endl;
2910  // std::cout << " " << recCovar(0,0) << " " << recCovar(0,1) << " "
2911  // << recCovar(0,2) << std::endl;
2912  // std::cout << " " << recCovar(1,0) << " " << recCovar(1,1) << " "
2913  // << recCovar(1,2) << std::endl;
2914  // std::cout << " " << recCovar(2,0) << " " << recCovar(2,1) << " "
2915  // << recCovar(2,2) << std::endl;
2916  // }
2917  // // end debug
2918 
2919  pointIndex++;
2920  }
2921 
2922  return true;
2923  }
2924 
2925 
2931  ControlNetQsp BundleAdjust::controlNet() {
2932  return m_controlNet;
2933  }
2934 
2935 
2941  SerialNumberList *BundleAdjust::serialNumberList() {
2942  return m_serialNumberList;
2943  }
2944 
2945 
2951  int BundleAdjust::numberOfImages() const {
2952  return m_serialNumberList->size();
2953  }
2954 
2955 
2963  // TODO: probably don't need this, can get from BundleObservation
2964  QString BundleAdjust::fileName(int i) {
2965  return m_serialNumberList->fileName(i);
2966  }
2967 
2968 
2974  double BundleAdjust::iteration() const {
2975  return m_iteration;
2976  }
2977 
2978 
2986  Table BundleAdjust::cMatrix(int i) {
2987  return m_controlNet->Camera(i)->instrumentRotation()->Cache("InstrumentPointing");
2988  }
2989 
2998  Table BundleAdjust::spVector(int i) {
2999  return m_controlNet->Camera(i)->instrumentPosition()->Cache("InstrumentPosition");
3000  }
3001 
3002 
3011  void BundleAdjust::iterationSummary() {
3012  QString iterationNumber;
3013 
3014  if ( m_bundleResults.converged() ) {
3015  iterationNumber = "Iteration" + toString(m_iteration) + ": Final";
3016  }
3017  else {
3018  iterationNumber = "Iteration" + toString(m_iteration);
3019  }
3020 
3021  PvlGroup summaryGroup(iterationNumber);
3022 
3023  summaryGroup += PvlKeyword("Sigma0",
3024  toString( m_bundleResults.sigma0() ) );
3025  summaryGroup += PvlKeyword("Observations",
3026  toString( m_bundleResults.numberObservations() ) );
3027  summaryGroup += PvlKeyword("Constrained_Point_Parameters",
3028  toString( m_bundleResults.numberConstrainedPointParameters() ) );
3029  summaryGroup += PvlKeyword("Constrained_Image_Parameters",
3030  toString( m_bundleResults.numberConstrainedImageParameters() ) );
3031  if (m_bundleSettings->bundleTargetBody()) {
3032  summaryGroup += PvlKeyword("Constrained_Target_Parameters",
3033  toString( m_bundleResults.numberConstrainedTargetParameters() ) );
3034  }
3035  summaryGroup += PvlKeyword("Unknown_Parameters",
3036  toString( m_bundleResults.numberUnknownParameters() ) );
3037  summaryGroup += PvlKeyword("Degrees_of_Freedom",
3038  toString( m_bundleResults.degreesOfFreedom() ) );
3039  summaryGroup += PvlKeyword( "Rejected_Measures",
3040  toString( m_bundleResults.numberRejectedObservations()/2) );
3041 
3042  if ( m_bundleResults.numberMaximumLikelihoodModels() >
3043  m_bundleResults.maximumLikelihoodModelIndex() ) {
3044  // if maximum likelihood estimation is being used
3045 
3046  summaryGroup += PvlKeyword("Maximum_Likelihood_Tier: ",
3047  toString( m_bundleResults.maximumLikelihoodModelIndex() ) );
3048  summaryGroup += PvlKeyword("Median_of_R^2_residuals: ",
3049  toString( m_bundleResults.maximumLikelihoodMedianR2Residuals() ) );
3050  }
3051 
3052  if ( m_bundleResults.converged() ) {
3053  summaryGroup += PvlKeyword("Converged", "TRUE");
3054  summaryGroup += PvlKeyword("TotalElapsedTime", toString( m_bundleResults.elapsedTime() ) );
3055 
3056  if (m_bundleSettings->errorPropagation()) {
3057  summaryGroup += PvlKeyword("ErrorPropagationElapsedTime",
3058  toString( m_bundleResults.elapsedTimeErrorProp() ) );
3059  }
3060  }
3061 
3062  std::ostringstream ostr;
3063  ostr << summaryGroup << std::endl;
3064  m_iterationSummary += QString::fromStdString( ostr.str() );
3065  if (m_printSummary) {
3066  Application::Log(summaryGroup);
3067  }
3068  }
3069 
3070 
3076  bool BundleAdjust::isConverged() {
3077  return m_bundleResults.converged();
3078  }
3079 
3080 
3086  bool BundleAdjust::isAborted() {
3087  return m_abort;
3088  }
3089 
3090 
3098  QString BundleAdjust::iterationSummaryGroup() const {
3099  return m_iterationSummary;
3100  }
3101 
3102 
3112  void BundleAdjust::outputBundleStatus(QString status) {
3113  if (QCoreApplication::applicationName() != "ipce") {
3114  printf("%s", status.toStdString().c_str());
3115  }
3116  }
3117 
3118 
3119 
3155  bool BundleAdjust::computeBundleStatistics() {
3156 
3157  // use qvectors so that we can set the size.
3158  // this will be useful later when adding data.
3159  // data may added out of index order
3160  int numberImages = m_serialNumberList->size();
3161  QVector<Statistics> rmsImageSampleResiduals(numberImages);
3162  QVector<Statistics> rmsImageLineResiduals(numberImages);
3163  QVector<Statistics> rmsImageResiduals(numberImages);
3164 
3165  int numObjectPoints = m_bundleControlPoints.size();
3166  for (int i = 0; i < numObjectPoints; i++) {
3167 
3168  const BundleControlPointQsp point = m_bundleControlPoints.at(i);
3169 
3170  if (point->isRejected()) {
3171  continue;
3172  }
3173 
3174  int numMeasures = point->numberOfMeasures();
3175  for (int j = 0; j < numMeasures; j++) {
3176 
3177  const BundleMeasureQsp measure = point->at(j);
3178 
3179  if (measure->isRejected()) {
3180  continue;
3181  }
3182 
3183  double sampleResidual = fabs(measure->sampleResidual());
3184  double lineResidual = fabs(measure->lineResidual());
3185 
3186  // Determine the index for this measure's serial number
3187  int imageIndex = m_serialNumberList->serialNumberIndex(measure->cubeSerialNumber());
3188 
3189  // add residual data to the statistics object at the appropriate serial number index
3190  rmsImageSampleResiduals[imageIndex].AddData(sampleResidual);
3191  rmsImageLineResiduals[imageIndex].AddData(lineResidual);
3192  rmsImageResiduals[imageIndex].AddData(lineResidual);
3193  rmsImageResiduals[imageIndex].AddData(sampleResidual);
3194  }
3195  }
3196 
3197 
3198  if (m_bundleSettings->errorPropagation()) {
3199 
3200  // initialize body-fixed coordinate boundaries
3201 
3202  // Latitude or X
3203  Distance minSigmaCoord1Dist;
3204  QString minSigmaCoord1PointId = "";
3205 
3206  Distance maxSigmaCoord1Dist;
3207  QString maxSigmaCoord1PointId = "";
3208 
3209  // Longitude or Y
3210  Distance minSigmaCoord2Dist;
3211  QString minSigmaCoord2PointId = "";
3212 
3213  Distance maxSigmaCoord2Dist;
3214  QString maxSigmaCoord2PointId = "";
3215 
3216  // Radius or Z
3217  Distance minSigmaCoord3Dist;
3218  QString minSigmaCoord3PointId = "";
3219 
3220  Distance maxSigmaCoord3Dist;
3221  QString maxSigmaCoord3PointId = "";
3222 
3223  // compute stats for point sigmas
3224  Statistics sigmaCoord1Stats;
3225  Statistics sigmaCoord2Stats;
3226  Statistics sigmaCoord3Stats;
3227 
3228  Distance sigmaCoord1Dist, sigmaCoord2Dist, sigmaCoord3Dist;
3229  SurfacePoint::CoordinateType type = m_bundleSettings->controlPointCoordTypeReports();
3230 
3231  int numPoints = m_bundleControlPoints.size();
3232  // initialize max and min values to those from first valid point
3233  for (int i = 0; i < numPoints; i++) {
3234 
3235  const BundleControlPointQsp point = m_bundleControlPoints.at(i);
3236 
3237  maxSigmaCoord1Dist = point->adjustedSurfacePoint().GetSigmaDistance(type,
3238  SurfacePoint::One);
3239  minSigmaCoord1Dist = maxSigmaCoord1Dist;
3240 
3241  maxSigmaCoord2Dist = point->adjustedSurfacePoint().GetSigmaDistance(type,
3242  SurfacePoint::Two);
3243  minSigmaCoord2Dist = maxSigmaCoord2Dist;
3244 
3245  maxSigmaCoord1PointId = point->id();
3246  maxSigmaCoord2PointId = maxSigmaCoord1PointId;
3247  minSigmaCoord1PointId = maxSigmaCoord1PointId;
3248  minSigmaCoord2PointId = maxSigmaCoord1PointId;
3249 
3250  // Get stats for coordinate 3 if used
3251  if (m_bundleSettings->solveRadius() || type == SurfacePoint::Rectangular) {
3252  maxSigmaCoord3Dist = point->adjustedSurfacePoint().GetSigmaDistance(type,
3253  SurfacePoint::Three);
3254  minSigmaCoord3Dist = maxSigmaCoord3Dist;
3255 
3256  maxSigmaCoord3PointId = maxSigmaCoord1PointId;
3257  minSigmaCoord3PointId = maxSigmaCoord1PointId;
3258  }
3259  break;
3260  }
3261 
3262  for (int i = 0; i < numPoints; i++) {
3263 
3264  const BundleControlPointQsp point = m_bundleControlPoints.at(i);
3265 
3266  sigmaCoord1Dist = point->adjustedSurfacePoint().GetSigmaDistance(type,
3267  SurfacePoint::One);
3268  sigmaCoord2Dist = point->adjustedSurfacePoint().GetSigmaDistance(type,
3269  SurfacePoint::Two);
3270  sigmaCoord3Dist = point->adjustedSurfacePoint().GetSigmaDistance(type,
3271  SurfacePoint::Three);
3272 
3273  sigmaCoord1Stats.AddData(sigmaCoord1Dist.meters());
3274  sigmaCoord2Stats.AddData(sigmaCoord2Dist.meters());
3275  sigmaCoord3Stats.AddData(sigmaCoord3Dist.meters());
3276 
3277  if (sigmaCoord1Dist > maxSigmaCoord1Dist) {
3278  maxSigmaCoord1Dist = sigmaCoord1Dist;
3279  maxSigmaCoord1PointId = point->id();
3280  }
3281  if (sigmaCoord2Dist > maxSigmaCoord2Dist) {
3282  maxSigmaCoord2Dist = sigmaCoord2Dist;
3283  maxSigmaCoord2PointId = point->id();
3284  }
3285  if (m_bundleSettings->solveRadius() || type == SurfacePoint::Rectangular) {
3286  if (sigmaCoord3Dist > maxSigmaCoord3Dist) {
3287  maxSigmaCoord3Dist = sigmaCoord3Dist;
3288  maxSigmaCoord3PointId = point->id();
3289  }
3290  }
3291  if (sigmaCoord1Dist < minSigmaCoord1Dist) {
3292  minSigmaCoord1Dist = sigmaCoord1Dist;
3293  minSigmaCoord1PointId = point->id();
3294  }
3295  if (sigmaCoord2Dist < minSigmaCoord2Dist) {
3296  minSigmaCoord2Dist = sigmaCoord2Dist;
3297  minSigmaCoord2PointId = point->id();
3298  }
3299  if (m_bundleSettings->solveRadius() || type == SurfacePoint::Rectangular) {
3300  if (sigmaCoord3Dist < minSigmaCoord3Dist) {
3301  minSigmaCoord3Dist = sigmaCoord3Dist;
3302  minSigmaCoord3PointId = point->id();
3303  }
3304  }
3305  }
3306 
3307  // update bundle results
3308  m_bundleResults.resizeSigmaStatisticsVectors(numberImages);
3309 
3310  m_bundleResults.setSigmaCoord1Range(minSigmaCoord1Dist, maxSigmaCoord1Dist,
3311  minSigmaCoord1PointId, maxSigmaCoord1PointId);
3312 
3313  m_bundleResults.setSigmaCoord2Range(minSigmaCoord2Dist, maxSigmaCoord2Dist,
3314  minSigmaCoord2PointId, maxSigmaCoord2PointId);
3315 
3316  m_bundleResults.setSigmaCoord3Range(minSigmaCoord3Dist, maxSigmaCoord3Dist,
3317  minSigmaCoord3PointId, maxSigmaCoord3PointId);
3318 
3319  m_bundleResults.setRmsFromSigmaStatistics(sigmaCoord1Stats.Rms(),
3320  sigmaCoord2Stats.Rms(),
3321  sigmaCoord3Stats.Rms());
3322  }
3323  m_bundleResults.setRmsImageResidualLists(rmsImageLineResiduals.toList(),
3324  rmsImageSampleResiduals.toList(),
3325  rmsImageResiduals.toList());
3326 
3327  return true;
3328  }
3329 
3330 }
This class defines a body-fixed surface point.
Definition: SurfacePoint.h:148
This represents an ISIS control net in a project-based GUI interface.
Definition: Control.h:79
Camera * camera() const
Accesses the associated camera for this bundle measure.
void setRunTime(QString runTime)
Sets the run time, and the name if a name is not already set.
double meters() const
Get the distance in meters.
Definition: Distance.cpp:97
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
virtual CameraType GetCameraType() const =0
Returns the type of camera that was created.
std::vector< double > EllipsoidPartial(SurfacePoint spoint, PartialType raxis)
Compute derivative of focal plane coordinate w/r to one of the ellipsoidal radii (a, b, or c)
Internalizes a list of images and allows for operations on the entire list.
Definition: ImageList.h:55
SparseBlockColumnMatrix.
static QString CurrentLocalTime()
Returns the current local time This time is taken directly from the system clock, so if the system cl...
Definition: iTime.cpp:503
File name manipulation and expansion.
Definition: FileName.h:116
QSharedPointer< ControlNet > ControlNetQsp
This typedef is for future implementation of target body.
Definition: ControlNet.h:496
double focalPlaneMeasuredY() const
Accesses the measured focal plane y value for this control measure //TODO verify? ...
QSharedPointer< BundleObservation > parentBundleObservation()
Accesses the parent BundleObservation for this bundle measure.
Container class for BundleAdjustment results.
Camera(Cube &cube)
Constructs the Camera object.
Definition: Camera.cpp:70
void add(const QString &filename, bool def2filename=false)
Adds a new filename / serial number pair to the SerialNumberList.
static void cholmodErrorHandler(int nStatus, const char *file, int nLineNo, const char *message)
Custom error handler for CHOLMOD.
virtual bool GetdXYdTOrientation(const SpiceRotation::PartialType varType, int coefIndex, double *cudx, double *cudy)
Compute derivative of focal plane coordinate w/r to target body using current state.
void zeroBlocks()
Sets all elements of all matrix blocks to zero.
double line() const
Accesses the current line measurement for this control measure.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
A container class for a ControlMeasure.
Definition: BundleMeasure.h:69
void ResetLocalRadius(const Distance &radius)
This method resets the local radius of a SurfacePoint.
boost::numeric::ublas::matrix< double > Matrix
Definition for an Isis::LinearAlgebra::Matrix of doubles.
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition: IString.cpp:226
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
Distance measurement, usually in meters.
Definition: Distance.h:47
Latitude GetLatitude() const
Return the body-fixed latitude for the surface point.
void wipe()
Deletes all pointer elements and removes them from the map.
bool SetImage(const double sample, const double line)
Sets the sample/line values of the image to get the lat/lon values.
Definition: Camera.cpp:170
This class is used to accumulate statistics on double arrays.
Definition: Statistics.h:107
This class hold image information that BundleAdjust needs to run correctly.Definition for a BundleIma...
Definition: BundleImage.h:51
boost::numeric::ublas::vector< double > Vector
Definition for an Isis::LinearAlgebra::Vector of doubles.
Program progress reporter.
Definition: Progress.h:58
a control network
Definition: ControlNet.h:271
int numberOfRows()
Returns total number of rows in map (this needs to be clarified and maybe rewritten).
std::vector< double > MeanRadiusPartial(SurfacePoint spoint, Distance meanRadius)
Compute derivative of focal plane coordinate w/r to mean of the ellipsoidal radii (a...
std::vector< double > Partial(CoordinateType type, CoordIndex index)
Compute partial derivative of conversion from body-fixed coordinates to the specified.
CameraGroundMap * GroundMap()
Returns a pointer to the CameraGroundMap object.
Definition: Camera.cpp:2868
virtual bool GetdXYdPosition(const SpicePosition::PartialType varType, int coefIndex, double *cudx, double *cudy)
Compute derivative w/r to position of focal plane coordinate from ground position using current Spice...
Contains multiple PvlContainers.
Definition: PvlGroup.h:57
double sample() const
Accesses the current sample measurement for this control measure.
bool insertMatrixBlock(int nRowBlock, int nRows, int nCols)
Inserts a "newed" LinearAlgebra::Matrix pointer of size (nRows, nCols) into the map with the block ro...
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
A single keyword-value pair.
Definition: PvlKeyword.h:98
This class holds information about a control point that BundleAdjust needs to run correctly...
virtual bool GetXY(const SurfacePoint &spoint, double *cudx, double *cudy, bool test=true)
Compute undistorted focal plane coordinate from ground position using current Spice from SetImage cal...
A single control point.
Definition: ControlPoint.h:369
This represents a cube in a project-based GUI interface.
Definition: Image.h:107
double PixelPitch() const
Returns the pixel pitch.
Definition: Camera.cpp:2754
void SetMatrix(CoordinateType type, const boost::numeric::ublas::symmetric_matrix< double, boost::numeric::ublas::upper > &covar)
Set the covariance matrix.
double Rms() const
Computes and returns the rms.
Definition: Statistics.cpp:378
double focalPlaneMeasuredX() const
Accesses the measured focal plane x value for this control measure //TODO verify? ...
void append(Image *const &value)
Appends an image to the image list.
Definition: ImageList.cpp:153
const QSharedPointer< BundleObservationSolveSettings > observationSolveSettings()
Accesses the parent observation&#39;s solve settings.
CoordinateType
Defines the coordinate typ, units, and coordinate index for some of the output methods.
Definition: SurfacePoint.h:155
int size() const
How many serial number / filename combos are in the list.
virtual bool GetdXYdOrientation(const SpiceRotation::PartialType varType, int coefIndex, double *cudx, double *cudy)
Compute derivative of focal plane coordinate w/r to instrument using current state from SetImage call...
int numberOfColumns()
Returns total number of columns in map (NOTE: NOT the number of matrix blocks).
QString cubeSerialNumber() const
Accesses the serial number of the cube containing this control measure.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
SurfacePoint adjustedSurfacePoint() const
Accesses the adjusted SurfacePoint associated with this BundleControlPoint.
Longitude GetLongitude() const
Return the body-fixed longitude for the surface point.
ErrorType errorType() const
Returns the source of the error for this exception.
Definition: IException.cpp:446
Class for storing Table blobs information.
Definition: Table.h:77
QString id() const
Accesses the Point ID associated with this BundleControlPoint.
Isis exception class.
Definition: IException.h:107
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
bool insertMatrixBlock(int nColumnBlock, int nRows, int nCols)
Inserts a "newed" LinearAlgebra::Matrix pointer of size (nRows, nCols) into the map with the block co...
int startColumn() const
Sets starting column for block in full matrix.
void AddData(const double *data, const unsigned int count)
Add an array of doubles to the accumulators and counters.
Definition: Statistics.cpp:154
Serial Number list generator.
SparseBlockRowMatrix.
QString fileName() const
Access the name of the control network file associated with this Control.
Definition: Control.cpp:264
void closeCube()
Cleans up the Cube pointer.
Definition: Image.cpp:307
QString fileName() const
Get the file name of the cube that this image represents.
Definition: Image.cpp:340
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
virtual bool GetdXYdPoint(std::vector< double > d_pB, double *dx, double *dy)
Compute derivative of focal plane coordinate w/r to ground point using current state.