Isis 3 Programmer Reference
BundleAdjust.cpp
1
7/* SPDX-License-Identifier: CC0-1.0 */
8
9#include "BundleAdjust.h"
10
11// std lib
12#include <iomanip>
13#include <iostream>
14#include <sstream>
15
16// qt lib
17#include <QCoreApplication>
18#include <QDebug>
19#include <QFile>
20#include <QMutex>
21
22// boost lib
23#include <boost/lexical_cast.hpp>
24#include <boost/numeric/ublas/io.hpp>
25#include <boost/numeric/ublas/matrix_proxy.hpp>
26#include <boost/numeric/ublas/matrix_sparse.hpp>
27#include <boost/numeric/ublas/vector_proxy.hpp>
28
29// Isis lib
30#include "Application.h"
31#include "BundleLidarControlPoint.h"
32#include "BundleMeasure.h"
33#include "BundleObservation.h"
34#include "IsisBundleObservation.h"
35#include "BundleObservationSolveSettings.h"
36#include "BundleResults.h"
37#include "BundleSettings.h"
38#include "BundleSolutionInfo.h"
39#include "BundleTargetBody.h"
40#include "Camera.h"
41#include "CameraDetectorMap.h"
42#include "CameraDistortionMap.h"
43#include "CameraFocalPlaneMap.h"
44#include "CameraGroundMap.h"
45#include "CSMCamera.h"
46#include "Control.h"
47#include "ControlPoint.h"
48#include "CorrelationMatrix.h"
49#include "Distance.h"
50#include "ImageList.h"
51#include "iTime.h"
52#include "Latitude.h"
53#include "LidarControlPoint.h"
54#include "Longitude.h"
55#include "MaximumLikelihoodWFunctions.h"
56#include "SpecialPixel.h"
57#include "StatCumProbDistDynCalc.h"
58#include "SurfacePoint.h"
59#include "Target.h"
60
61using namespace boost::numeric::ublas;
62
63namespace Isis {
64
65
75 static void cholmodErrorHandler(int nStatus,
76 const char* file,
77 int nLineNo,
78 const char* message) {
79 QString errlog;
80
81 errlog = "SPARSE: ";
82 errlog += message;
83
84 PvlGroup gp(errlog);
85
86 gp += PvlKeyword("File",file);
87 gp += PvlKeyword("Line_Number", toString(nLineNo));
88 gp += PvlKeyword("Status", toString(nStatus));
89
90// Application::Log(gp);
91
92 errlog += ". (See print.prt for details)";
93
94// throw IException(IException::Unknown, errlog, _FILEINFO_);
95 }
96
97
108 const QString &cnetFile,
109 const QString &cubeList,
110 bool printSummary) {
111 m_abort = false;
112 Progress progress;
113 // initialize constructor dependent settings...
114 // m_printSummary, m_cleanUp, m_cnetFileName, m_controlNet,
115 // m_serialNumberList, m_bundleSettings
116 m_printSummary = printSummary;
117 m_cleanUp = true;
118 m_cnetFileName = cnetFile;
119 try {
120 m_controlNet = ControlNetQsp( new ControlNet(cnetFile, &progress,
121 bundleSettings->controlPointCoordTypeReports()) );
122 }
123 catch (IException &e) {
124 throw;
125 }
127 m_serialNumberList = new SerialNumberList(cubeList);
128 m_bundleSettings = bundleSettings;
129 m_bundleTargetBody = bundleSettings->bundleTargetBody();
130
131 init(&progress);
132 }
133
134
146 const QString &cnetFile,
147 const QString &cubeList,
148 const QString &lidarDataFile,
149 bool printSummary) {
150 m_abort = false;
151 Progress progress;
152 // initialize constructor dependent settings...
153 // m_printSummary, m_cleanUp, m_cnetFileName, m_controlNet,
154 // m_serialNumberList, m_bundleSettings
155 m_printSummary = printSummary;
156 m_cleanUp = true;
157 m_cnetFileName = cnetFile;
158 m_lidarFileName = lidarDataFile;
159 try {
160 m_controlNet = ControlNetQsp( new ControlNet(cnetFile, &progress) );
161 }
162 catch (IException &e) {
163 throw;
164 }
165
166 // read lidar point data file
167 try {
169 m_lidarDataSet->read(lidarDataFile);
170 }
171 catch (IException &e) {
172 throw;
173 }
174
177 m_serialNumberList = new SerialNumberList(cubeList);
178 m_bundleSettings = bundleSettings;
179 m_bundleTargetBody = bundleSettings->bundleTargetBody();
180
181 init(&progress);
182 }
183
184
196 QString &cnetFile,
197 SerialNumberList &snlist,
198 bool printSummary) {
199 // initialize constructor dependent settings...
200 // m_printSummary, m_cleanUp, m_cnetFileName, m_controlNet,
201 // m_serialNumberList, m_bundleSettings
202 m_abort = false;
203 Progress progress;
204 m_printSummary = printSummary;
205 m_cleanUp = false;
206 m_cnetFileName = cnetFile;
207 try {
208 m_controlNet = ControlNetQsp( new ControlNet(cnetFile, &progress) );
209 }
210 catch (IException &e) {
211 throw;
212 }
214 m_serialNumberList = &snlist;
215 m_bundleSettings = bundleSettings;
216 m_bundleTargetBody = bundleSettings->bundleTargetBody();
217
218 init();
219 }
220
221
233 Control &cnet,
234 SerialNumberList &snlist,
235 bool printSummary) {
236 // initialize constructor dependent settings...
237 // m_printSummary, m_cleanUp, m_cnetFileName, m_controlNet,
238 // m_serialNumberList, m_bundleSettings
239 m_abort = false;
240 Progress progress;
241 m_printSummary = printSummary;
242 m_cleanUp = false;
243 m_cnetFileName = cnet.fileName();
244 try {
245 m_controlNet = ControlNetQsp( new ControlNet(cnet.fileName(), &progress) );
246 }
247 catch (IException &e) {
248 throw;
249 }
251 m_serialNumberList = &snlist;
252 m_bundleSettings = bundleSettings;
253 m_bundleTargetBody = bundleSettings->bundleTargetBody();
254
255 init();
256 }
257
258
270 ControlNet &cnet,
271 SerialNumberList &snlist,
272 bool printSummary) {
273 // initialize constructor dependent settings...
274 // m_printSummary, m_cleanUp, m_cnetFileName, m_controlNet,
275 // m_serialNumberList, m_bundleSettings
276 m_abort = false;
277 m_printSummary = printSummary;
278 m_cleanUp = false;
279 m_cnetFileName = "";
280 try {
282 }
283 catch (IException &e) {
284 throw;
285 }
287 m_serialNumberList = &snlist;
288 m_bundleSettings = bundleSettings;
289 m_bundleTargetBody = bundleSettings->bundleTargetBody();
290
291 init();
292 }
293
294
304 ControlNetQsp cnet,
305 const QString &cubeList,
306 bool printSummary) {
307 m_abort = false;
308 m_printSummary = printSummary;
309 m_cleanUp = false;
310 m_cnetFileName = "";
311 try {
312 m_controlNet = cnet;
313 }
314 catch (IException &e) {
315 throw;
316 }
318 m_serialNumberList = new SerialNumberList(cubeList);
319 m_bundleSettings = bundleSettings;
320 m_bundleTargetBody = bundleSettings->bundleTargetBody();
321
322 init();
323 }
324
325
336 Control &control,
337 QList<ImageList *> imgLists,
338 bool printSummary) {
339 m_bundleSettings = bundleSettings;
340
341 m_abort = false;
342 try {
343 m_controlNet = ControlNetQsp( new ControlNet(control.fileName()) );
344 }
345 catch (IException &e) {
346 throw;
347 }
349
350 m_imageLists = imgLists;
351
352 // this is too slow and we need to get rid of the serial number list anyway
353 // should be unnecessary as Image class has serial number
354 // could hang on to image list until creating BundleObservations?
356
357 foreach (ImageList *imgList, imgLists) {
358 foreach (Image *image, *imgList) {
360// m_serialNumberList->add(image->serialNumber(), image->fileName());
361 }
362 }
363
364 m_bundleTargetBody = bundleSettings->bundleTargetBody();
365
366 m_printSummary = printSummary;
367
368 m_cleanUp = false;
369 m_cnetFileName = control.fileName();
370
371 init();
372 }
373
374
384 // If we have ownership
385 if (m_cleanUp) {
386 delete m_serialNumberList;
387 }
388
390
391 }
392
393
425 void BundleAdjust::init(Progress *progress) {
426 emit(statusUpdate("Initialization"));
428
429 // initialize
430 //
431 // JWB
432 // - some of these not originally initialized.. better values???
433 m_iteration = 0;
434 m_rank = 0;
435 m_iterationSummary = "";
436
437 // Get the cameras set up for all images
438 // NOTE - THIS IS NOT THE SAME AS "setImage" as called in BundleAdjust::computePartials
439 // this call only does initializations; sets measure's camera pointer, etc
440 // RENAME????????????
441 m_controlNet->SetImages(*m_serialNumberList, progress);
442
443 if (m_lidarDataSet) {
444 // TODO: (KLE) document why we're (at the moment) required to use an existing control net to
445 // SetImages for the lidar data set. In my opinion this is a major drawback to this
446 // implementation, and a really good argument for a control net design that allows
447 // multiple point sources in the same net (e.g. photogrammetric, lidar, and other?
448 // types).
449 m_lidarDataSet->SetImages(*(m_controlNet.data()), progress);
450 }
451
452 // clear JigsawRejected flags
453 m_controlNet->ClearJigsawRejected();
454
455 // initialize held variables
456 int numImages = m_serialNumberList->size();
457
458 // matrix stuff
459 m_normalInverse.clear();
460 m_RHS.clear();
461 m_imageSolution.clear();
462
463 // we don't want to call initializeCHOLMODLibraryVariables() here since mRank=0
464 // m_cholmodCommon, m_sparseNormals are not initialized
465 m_L = NULL;
466 m_cholmodNormal = NULL;
467 m_cholmodTriplet = NULL;
468
469 // set up BundleObservations and assign solve settings for each from BundleSettings class
470 for (int i = 0; i < numImages; i++) {
471 Camera *camera = m_controlNet->Camera(i);
472 QString observationNumber = m_serialNumberList->observationNumber(i);
473 QString instrumentId = m_serialNumberList->spacecraftInstrumentId(i);
474 QString serialNumber = m_serialNumberList->serialNumber(i);
476
477 // If any camera is initialized via CSMInit, but no csm solve options are specified, fail early.
478 if (camera->GetCameraType() == Camera::Csm && m_bundleSettings->observationSolveSettings(observationNumber).csmSolveOption() == 0){
479 QString msg = fileName + " camera was initialized using CSMInit, so jigsaw must use CSM parameters." +
480 " Please refer to documentation for more information." + "\n";
481 throw IException(IException::User, msg, _FILEINFO_);
482 }
483
484 // create a new BundleImage and add to new (or existing if observation mode is on)
485 // BundleObservation
486 BundleImageQsp image = BundleImageQsp(new BundleImage(camera, serialNumber, fileName));
487
488 if (!image) {
489 QString msg = "In BundleAdjust::init(): image " + fileName + "is null." + "\n";
490 throw IException(IException::Programmer, msg, _FILEINFO_);
491 }
492
493 BundleObservationQsp observation =
494 m_bundleObservations.addNew(image, observationNumber, instrumentId, m_bundleSettings);
495
496 if (!observation) {
497 QString msg = "In BundleAdjust::init(): observation "
498 + observationNumber + "is null." + "\n";
499 throw IException(IException::Programmer, msg, _FILEINFO_);
500 }
501 }
502
503 // set up vector of BundleControlPoints
504 int numControlPoints = m_controlNet->GetNumPoints();
505 for (int i = 0; i < numControlPoints; i++) {
506 ControlPoint *point = m_controlNet->GetPoint(i);
507 if (point->IsIgnored()) {
508 continue;
509 }
510
511 BundleControlPointQsp bundleControlPoint(new BundleControlPoint
512 (m_bundleSettings, point));
513 m_bundleControlPoints.append(bundleControlPoint);
514
515 // set parent observation for each BundleMeasure
516 int numMeasures = bundleControlPoint->size();
517 for (int j = 0; j < numMeasures; j++) {
518 BundleMeasureQsp measure = bundleControlPoint->at(j);
519 QString cubeSerialNumber = measure->cubeSerialNumber();
520
521 BundleObservationQsp observation =
522 m_bundleObservations.observationByCubeSerialNumber(cubeSerialNumber);
523 BundleImageQsp image = observation->imageByCubeSerialNumber(cubeSerialNumber);
524
525 measure->setParentObservation(observation);
526 measure->setParentImage(image);
527 measure->setSigma(1.4);
528 }
529
530 point->ComputeApriori();
531 }
532
533 // set up vector of BundleLidarControlPoints
534 int numLidarPoints = 0;
535 if (m_lidarDataSet) {
536 numLidarPoints = m_lidarDataSet->points().size();
537 }
538 for (int i = 0; i < numLidarPoints; i++) {
539 LidarControlPointQsp lidarPoint = m_lidarDataSet->points().at(i);
540 if (lidarPoint->IsIgnored()) {
541 continue;
542 }
543
545 lidarPoint));
546 m_bundleLidarControlPoints.append(bundleLidarPoint);
547
548 // set parent observation for each BundleMeasure
549 int numMeasures = bundleLidarPoint->size();
550 for (int j = 0; j < numMeasures; j++) {
551 BundleMeasureQsp measure = bundleLidarPoint->at(j);
552 QString cubeSerialNumber = measure->cubeSerialNumber();
553
554 BundleObservationQsp observation =
555 m_bundleObservations.observationByCubeSerialNumber(cubeSerialNumber);
556 BundleImageQsp image = observation->imageByCubeSerialNumber(cubeSerialNumber);
557
558 measure->setParentObservation(observation);
559 measure->setParentImage(image);
560 measure->setSigma(30.0);
561 }
562
563 // WHY ARE WE CALLING COMPUTE APRIORI FOR LIDAR POINTS?
564 // ANSWER: Because the ::computeApriori method is also setting the focal plane measures, see
565 // line 916 in ControlPoint.Constrained_Point_Parameters
566 // This really stinks, maybe we should be setting the focal plane measures here, as part of
567 // the BundleAdjust::init method? Or better yet as part of the BundleControlPoint constructor?
568 // Currently have a kluge in the ControlPoint::setApriori method to not update the coordinates
569 // of lidar points.
570 // Also, maybe we could address Brents constant complaint about points where we can't get a
571 // lat or lon due to bad SPICE causing the bundle to fail.
572 lidarPoint->ComputeApriori();
573
574 // initialize range constraints
575 bundleLidarPoint->initializeRangeConstraints();
576 }
577
578 //===========================================================================================//
579 //==== Use the bundle settings to initialize more member variables and set up solutions =====//
580 //===========================================================================================//
581
582 // TODO: Need to have some validation code to make sure everything is
583 // on the up-and-up with the control network. Add checks for multiple
584 // networks, images without any points, and points on images removed from
585 // the control net (when we start adding software to remove points with high
586 // residuals) and ?. For "deltack" a single measure on a point is allowed
587 // so skip the test.
588 if (m_bundleSettings->validateNetwork()) {
590 }
591 m_bundleResults.maximumLikelihoodSetUp(m_bundleSettings->maximumLikelihoodEstimatorModels());
592
593 //===========================================================================================//
594 //=============== End Bundle Settings =======================================================//
595 //===========================================================================================//
596
597 //===========================================================================================//
598 //======================== initialize matrices and more parameters ==========================//
599 //===========================================================================================//
600
601 // size of reduced normals matrix
602
603 // TODO
604 // this should be determined from BundleSettings
605 // m_rank will be the sum of observation, target, and self-cal parameters
606 // TODO
607 m_rank = m_bundleObservations.numberParameters();
608
609 if (m_bundleSettings->solveTargetBody()) {
610 m_rank += m_bundleSettings->numberTargetBodyParameters();
611
612 if (m_bundleTargetBody->solveMeanRadius() || m_bundleTargetBody->solveTriaxialRadii()) {
613 outputBundleStatus("Warning: Solving for the target body radii (triaxial or mean) "
614 "is NOT possible and likely increases error in the solve.\n");
615 }
616
617 if (m_bundleTargetBody->solveMeanRadius()){
618 // Check if MeanRadiusValue is abnormal compared to observation
619 bool isMeanRadiusValid = true;
620 double localRadius, aprioriRadius;
621
622 // Arbitrary control point containing an observed localRadius
624 SurfacePoint surfacepoint = point->adjustedSurfacePoint();
625
626 localRadius = surfacepoint.GetLocalRadius().meters();
627 aprioriRadius = m_bundleTargetBody->meanRadius().meters();
628
629 // Ensure aprioriRadius is within some threshold
630 // Considered potentially inaccurate if it's off by atleast a factor of two
631 if (aprioriRadius >= 2 * localRadius || aprioriRadius <= localRadius / 2) {
632 isMeanRadiusValid = false;
633 }
634
635 // Warn user for abnormal MeanRadiusValue
636 if (!isMeanRadiusValid) {
637 outputBundleStatus("Warning: User-entered MeanRadiusValue appears to be inaccurate. "
638 "This can cause a bundle failure.\n");
639 }
640 }
641 }
642
643 int num3DPoints = m_bundleControlPoints.size() + m_bundleLidarControlPoints.size();
644
646
647 m_imageSolution.resize(m_rank);
648
649 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
650 // initializations for cholmod
652
653 // initialize normal equations matrix
655 }
656
657
678
679 outputBundleStatus("\nValidating network...");
680
681 int imagesWithInsufficientMeasures = 0;
682 QString msg = "Images with one or less measures:\n";
683 int numObservations = m_bundleObservations.size();
684 for (int i = 0; i < numObservations; i++) {
685 int numImages = m_bundleObservations.at(i)->size();
686 for (int j = 0; j < numImages; j++) {
687 BundleImageQsp bundleImage = m_bundleObservations.at(i)->at(j);
688 int numMeasures = m_controlNet->
689 GetNumberOfValidMeasuresInImage(bundleImage->serialNumber());
690
691 if (numMeasures > 1) {
692 continue;
693 }
694
695 imagesWithInsufficientMeasures++;
696 msg += bundleImage->fileName() + ": " + toString(numMeasures) + "\n";
697 }
698 }
699
700 if ( imagesWithInsufficientMeasures > 0 ) {
701 throw IException(IException::User, msg, _FILEINFO_);
702 }
703
704 outputBundleStatus("\nValidation complete!...\n");
705
706 return true;
707 }
708
709
717 if ( m_rank <= 0 ) {
718 return false;
719 }
720
721 m_cholmodTriplet = NULL;
722
723 cholmod_l_start(&m_cholmodCommon);
724
725 // set user-defined cholmod error handler
726 m_cholmodCommon.error_handler = cholmodErrorHandler;
727
728 // testing not using metis
729 m_cholmodCommon.nmethods = 1;
730 m_cholmodCommon.method[0].ordering = CHOLMOD_AMD;
731
732 return true;
733 }
734
735
745
746 cholmod_l_free_triplet(&m_cholmodTriplet, &m_cholmodCommon);
747 cholmod_l_free_sparse(&m_cholmodNormal, &m_cholmodCommon);
748 cholmod_l_free_factor(&m_L, &m_cholmodCommon);
749
750 cholmod_l_finish(&m_cholmodCommon);
751
752 return true;
753 }
754
755
767
768 int nBlockColumns = m_bundleObservations.size();
769
770 if (m_bundleSettings->solveTargetBody())
771 nBlockColumns += 1;
772
773 m_sparseNormals.setNumberOfColumns(nBlockColumns);
774
775 m_sparseNormals.at(0)->setStartColumn(0);
776
777 int nParameters = 0;
778 if (m_bundleSettings->solveTargetBody()) {
779 nParameters += m_bundleSettings->numberTargetBodyParameters();
780 m_sparseNormals.at(1)->setStartColumn(nParameters);
781
782 int observation = 0;
783 for (int i = 2; i < nBlockColumns; i++) {
784 nParameters += m_bundleObservations.at(observation)->numberParameters();
785 m_sparseNormals.at(i)->setStartColumn(nParameters);
786 observation++;
787 }
788 }
789 else {
790 for (int i = 0; i < nBlockColumns; i++) {
791 m_sparseNormals.at(i)->setStartColumn(nParameters);
792 nParameters += m_bundleObservations.at(i)->numberParameters();
793 }
794 }
795
796 return true;
797 }
798
799
813
814
820 m_abort = true;
821 }
822
823
839 emit(statusBarUpdate("Solving"));
840 try {
841
842 // throw error if a frame camera is included AND
843 // if m_bundleSettings->solveInstrumentPositionOverHermiteSpline()
844 // is set to true (can only use for line scan or radar)
845 // if (m_bundleSettings->solveInstrumentPositionOverHermiteSpline() == true) {
846 // int numImages = images();
847 // for (int i = 0; i < numImages; i++) {
848 // if (m_controlNet->Camera(i)->GetCameraType() == 0) {
849 // QString msg = "At least one sensor is a frame camera. "
850 // "Spacecraft Option OVERHERMITE is not valid for frame cameras\n";
851 // throw IException(IException::User, msg, _FILEINFO_);
852 // }
853 // }
854 // }
855
856 // ken testing - if solving for target mean radius, set point radius to current mean radius
857 // if solving for triaxial radii, set point radius to local radius
858 if (m_bundleTargetBody && m_bundleTargetBody->solveMeanRadius()) {
859 int numControlPoints = m_bundleControlPoints.size();
860 for (int i = 0; i < numControlPoints; i++) {
862 SurfacePoint surfacepoint = point->adjustedSurfacePoint();
863
864 surfacepoint.ResetLocalRadius(m_bundleTargetBody->meanRadius());
865
866 point->setAdjustedSurfacePoint(surfacepoint);
867 }
868 }
869
870 // Only use target body solution options when using Latitudinal coordinates
871 if (m_bundleTargetBody && m_bundleTargetBody->solveTriaxialRadii()
872 && m_bundleSettings->controlPointCoordTypeBundle() == SurfacePoint::Latitudinal) {
873 int numControlPoints = m_bundleControlPoints.size();
874 for (int i = 0; i < numControlPoints; i++) {
876 SurfacePoint surfacepoint = point->adjustedSurfacePoint();
877
878 Distance localRadius = m_bundleTargetBody->localRadius(surfacepoint.GetLatitude(),
879 surfacepoint.GetLongitude());
880 surfacepoint.ResetLocalRadius(localRadius);
881
882 point->setAdjustedSurfacePoint(surfacepoint);
883 }
884 }
885
886 // Beginning of iterations
887 m_iteration = 1;
888 double vtpv = 0.0;
889 double previousSigma0 = 0.0;
890
891 // start the clock
892 clock_t solveStartClock = clock();
893
894 for (;;) {
895
896 emit iterationUpdate(m_iteration);
897
898 // testing
899 if (m_abort) {
901 emit statusUpdate("\n aborting...");
902 emit finished();
903 return false;
904 }
905 // testing
906
907 emit statusUpdate( QString("\nstarting iteration %1\n").arg(m_iteration) );
908
909 clock_t iterationStartClock = clock();
910
911 // zero normals (after iteration 0)
912 if (m_iteration != 1) {
914 }
915
916 // form normal equations -- computePartials is called in here.
917 if (!formNormalEquations()) {
919 break;
920 }
921
922 // testing
923 if (m_abort) {
925 emit statusUpdate("\n aborting...");
926 emit finished();
927 return false;
928 }
929 // testing
930
931 // solve the system
932 if (!solveSystem()) {
933 outputBundleStatus("\nsolve failed!");
935 break;
936 }
937
938 // testing
939 if (m_abort) {
941 emit statusUpdate("\n aborting...");
942 emit finished();
943 return false;
944 }
945 // testing
946
947 // apply parameter corrections
949
950 // testing
951 if (m_abort) {
953 emit statusUpdate("\n aborting...");
954 emit finished();
955 return false;
956 }
957 // testing
958
959 // compute residuals
960 emit(statusBarUpdate("Computing Residuals"));
962
963 // compute vtpv (weighted sum of squares of residuals)
964 vtpv = computeVtpv();
965
966 // flag outliers
967 if ( m_bundleSettings->outlierRejection() ) {
969 flagOutliers();
970 }
971
972 // testing
973 if (m_abort) {
975 emit statusUpdate("\n aborting...");
976 emit finished();
977 return false;
978 }
979 // testing
980
981 // Sigma0 (or "sigma nought") is the standard deviation of an observation of unit weight.
982 // Sigma0^2 is the variance of an observation of unit weight (also reference variance or
983 // variance factor).
984 m_bundleResults.computeSigma0(vtpv, m_bundleSettings->convergenceCriteria());
985
986 // Set up formatting for status updates with doubles (e.g. Sigma0, Elapsed Time)
987 int fieldWidth = 20;
988 char format = 'f';
989 int precision = 10;
990
991 emit statusUpdate(QString("Iteration: %1 \n")
992 .arg(m_iteration));
993 emit statusUpdate(QString("Sigma0: %1 \n")
994 .arg(m_bundleResults.sigma0(),
995 fieldWidth,
996 format,
997 precision));
998 emit statusUpdate(QString("Observations: %1 \n")
1000 emit statusUpdate(QString("Constrained Parameters:%1 \n")
1002 emit statusUpdate(QString("Unknowns: %1 \n")
1004 emit statusUpdate(QString("Degrees of Freedom: %1 \n")
1006 emit iterationUpdate(m_iteration);
1007
1008 // check for convergence
1009 if (m_bundleSettings->convergenceCriteria() == BundleSettings::Sigma0) {
1010 if (fabs(previousSigma0 - m_bundleResults.sigma0())
1011 <= m_bundleSettings->convergenceCriteriaThreshold()) {
1012 // convergence detected
1013
1014 // if maximum likelihood tiers are being processed,
1015 // check to see if there's another tier to go.
1019 < 2) {
1020 // TODO is this second condition redundant???
1021 // should BundleResults require num models <= 3, so num models - 1 <= 2
1024
1025 // If there is another tier left we will increment the index.
1027 }
1028 }
1029 else { // otherwise iterations are complete
1031 emit statusUpdate("Bundle has converged\n");
1032 emit statusBarUpdate("Converged");
1033
1034 clock_t iterationStopClock = clock();
1035 m_iterationTime = (iterationStopClock - iterationStartClock)
1036 / (double)CLOCKS_PER_SEC;
1037 break;
1038 }
1039 }
1040 }
1041 else {
1042 // bundleSettings.convergenceCriteria() == BundleSettings::ParameterCorrections
1043 int numConvergedParams = 0;
1044 int numImgParams = m_imageSolution.size();
1045 for (int ij = 0; ij < numImgParams; ij++) {
1046 if (fabs(m_imageSolution(ij)) > m_bundleSettings->convergenceCriteriaThreshold()) {
1047 break;
1048 }
1049 else
1050 numConvergedParams++;
1051 }
1052
1053 if ( numConvergedParams == numImgParams ) {
1055 emit statusUpdate("Bundle has converged\n");
1056 emit statusBarUpdate("Converged");
1057 break;
1058 }
1059 }
1060
1062 clock_t iterationStopClock = clock();
1063 m_iterationTime = (iterationStopClock - iterationStartClock)
1064 / (double)CLOCKS_PER_SEC;
1065 emit statusUpdate( QString("End of Iteration %1 \n").arg(m_iteration) );
1066 emit statusUpdate( QString("Elapsed Time: %1 \n").arg(m_iterationTime,
1067 fieldWidth,
1068 format,
1069 precision) );
1070
1071 // check for maximum iterations
1072 if (m_iteration >= m_bundleSettings->convergenceCriteriaMaximumIterations()) {
1073 emit(statusBarUpdate("Max Iterations Reached"));
1074 break;
1075 }
1076
1077 // restart the dynamic calculation of the cumulative probility distribution of residuals
1078 // (in unweighted pixels) --so it will be up to date for the next iteration
1079 if (!m_bundleResults.converged()) {
1081 }
1082
1083 // if we're using CHOLMOD and still going, release cholmod_factor
1084 // (if we don't, memory leaks will occur), otherwise we need it for error propagation
1085 if (!m_bundleResults.converged() || !m_bundleSettings->errorPropagation()) {
1086 cholmod_l_free_factor(&m_L, &m_cholmodCommon);
1087 }
1088
1090
1091 m_iteration++;
1092
1093 previousSigma0 = m_bundleResults.sigma0();
1094 } // end of bundle iteration loop
1095
1096 if (m_bundleResults.converged() && m_bundleSettings->errorPropagation()) {
1097 clock_t errorPropStartClock = clock();
1098
1099 outputBundleStatus("\nStarting Error Propagation");
1100
1102 emit statusUpdate("\n\nError Propagation Complete\n");
1103 clock_t errorPropStopClock = clock();
1104 m_bundleResults.setElapsedTimeErrorProp((errorPropStopClock - errorPropStartClock)
1105 / (double)CLOCKS_PER_SEC);
1106 }
1107
1108 clock_t solveStopClock = clock();
1109 m_bundleResults.setElapsedTime((solveStopClock - solveStartClock)
1110 / (double)CLOCKS_PER_SEC);
1111
1113
1115 m_bundleResults.setObservations(m_bundleObservations);
1117
1118 if (!m_bundleLidarControlPoints.isEmpty()) {
1120 }
1121
1122 emit resultsReady(bundleSolveInformation());
1123
1124 emit statusUpdate("\nBundle Complete\n");
1125
1127 }
1128 catch (IException &e) {
1130 emit statusUpdate("\n aborting...");
1131 emit statusBarUpdate("Failed to Converge");
1132 emit finished();
1133 QString msg = "Could not solve bundle adjust.";
1134 throw IException(e, e.errorType(), msg, _FILEINFO_);
1135 }
1136
1137 emit finished();
1138 return true;
1139 }
1140
1141
1146
1147 // residuals for photogrammetric measures
1148 emit(statusBarUpdate("Computing Measure Residuals"));
1149 for (int i = 0; i < m_bundleControlPoints.size(); i++) {
1150 m_bundleControlPoints.at(i)->computeResiduals();
1151 }
1152
1153 // residuals for lidar measures
1154 if (!m_bundleLidarControlPoints.isEmpty()) {
1155 emit(statusBarUpdate("Computing Lidar Measure Residuals"));
1156 for (int i = 0; i < m_bundleLidarControlPoints.size(); i++) {
1157 m_bundleLidarControlPoints.at(i)->computeResiduals();
1158 }
1159 }
1160 }
1161
1162
1172 BundleSolutionInfo *bundleSolutionInfo = new BundleSolutionInfo(m_bundleSettings,
1176 imageLists());
1177 bundleSolutionInfo->setRunTime("");
1178 return bundleSolutionInfo;
1179 }
1180
1181
1194 emit(statusBarUpdate("Forming Normal Equations"));
1195 bool status = false;
1196
1197 // Initialize auxilary matrices and vectors.
1198 static LinearAlgebra::Matrix coeffTarget;
1199 static LinearAlgebra::Matrix coeffImage;
1200 static LinearAlgebra::Matrix coeffPoint3D(2, 3);
1201 static LinearAlgebra::Vector coeffRHS(2);
1204 static LinearAlgebra::Vector n2(3);
1206
1207 m_RHS.resize(m_rank);
1208
1209 // if solving for target body parameters, set size of coeffTarget
1210 // (note this size will not change through the adjustment).
1211 if (m_bundleSettings->solveTargetBody()) {
1212 int numTargetBodyParameters = m_bundleSettings->numberTargetBodyParameters();
1213 // TODO make sure numTargetBodyParameters is greater than 0
1214 coeffTarget.resize(2,numTargetBodyParameters);
1215 }
1216
1217 // clear N12, n1, and nj
1218 N12.clear();
1219 n1.clear();
1220 m_RHS.clear();
1221
1222 // clear static matrices
1223 coeffPoint3D.clear();
1224 coeffRHS.clear();
1225 N22.clear();
1226 n2.clear();
1227
1228 // loop over 3D points
1229 int numObservations = 0;
1230 int numGood3DPoints = 0;
1231 int numRejected3DPoints = 0;
1232 int numConstrainedCoordinates = 0;
1233 int num3DPoints = m_bundleControlPoints.size();
1234
1235 outputBundleStatus("\n\n");
1236
1237 for (int i = 0; i < num3DPoints; i++) {
1238 emit(pointUpdate(i+1));
1240
1241 if (point->isRejected()) {
1242 numRejected3DPoints++;
1243 continue;
1244 }
1245
1246 if ( i != 0 ) {
1247 N22.clear();
1248 N12.wipe();
1249 n2.clear();
1250 }
1251
1252 // loop over measures for this point
1253 int numMeasures = point->size();
1254 for (int j = 0; j < numMeasures; j++) {
1255 BundleMeasureQsp measure = point->at(j);
1256
1257 // flagged as "JigsawFail" implies this measure has been rejected
1258 // TODO IsRejected is obsolete -- replace code or add to ControlMeasure
1259 if (measure->isRejected()) {
1260 continue;
1261 }
1262
1263 status = computePartials(coeffTarget, coeffImage, coeffPoint3D, coeffRHS, *measure,
1264 *point);
1265
1266 if (!status) {
1267 // TODO should status be set back to true? JAM
1268 // TODO this measure should be flagged as rejected.
1269 continue;
1270 }
1271
1272 // increment number of observations
1273 numObservations += 2;
1274
1275 formMeasureNormals(N22, N12, n1, n2, coeffTarget, coeffImage, coeffPoint3D, coeffRHS,
1276 measure->observationIndex());
1277
1278 } // end loop over this points measures
1279
1280 numConstrainedCoordinates += formPointNormals(N22, N12, n2, m_RHS, point);
1281
1282 numGood3DPoints++;
1283 } // end loop over 3D points
1284
1285 m_bundleResults.setNumberConstrainedPointParameters(numConstrainedCoordinates);
1287
1288 int numRejectedLidarPoints = 0.0;
1289 int numGoodLidarPoints = 0.0;
1290 numObservations = 0;
1291 numConstrainedCoordinates = 0;
1292
1293 // loop over lidar points
1294 int numLidarPoints = m_bundleLidarControlPoints.size();
1296
1297
1298 for (int i = 0; i < numLidarPoints; i++) {
1299 emit(pointUpdate(i+1));
1301
1302 if (point->isRejected()) {
1303 numRejectedLidarPoints++;
1304 continue;
1305 }
1306
1307 N22.clear();
1308 N12.wipe();
1309 n2.clear();
1310
1311 // loop over measures for this point
1312 int numMeasures = point->size();
1313 for (int j = 0; j < numMeasures; j++) {
1314 BundleMeasureQsp measure = point->at(j);
1315
1316 if (measure->isRejected()) {
1317 continue;
1318 }
1319
1320 status = computePartials(coeffTarget, coeffImage, coeffPoint3D, coeffRHS, *measure,
1321 *point);
1322
1323 if (!status) {
1324 // TODO this measure should be flagged as rejected.
1325 continue;
1326 }
1327
1328 // increment number of lidar image "measurement" observations
1329 numObservations += 2;
1330
1331 formMeasureNormals(N22, N12, n1, n2, coeffTarget, coeffImage, coeffPoint3D, coeffRHS,
1332 measure->observationIndex());
1333
1334 } // end loop over this points measures
1335
1336 m_numLidarConstraints += point->applyLidarRangeConstraints(m_sparseNormals, N22, N12, n1, n2);
1337
1338 numConstrainedCoordinates += formLidarPointNormals(N22, N12, n2, m_RHS, point);
1339
1340 numGoodLidarPoints++;
1341 } // end loop over lidar 3D points
1342
1346
1347 // form the reduced normal equations
1349
1350 // update number of unknown parameters
1351 m_bundleResults.setNumberUnknownParameters(m_rank + 3 * numGood3DPoints);
1352
1353 return status;
1354}
1355
1356
1380 LinearAlgebra::Matrix &coeffTarget,
1381 LinearAlgebra::Matrix &coeffImage,
1382 LinearAlgebra::Matrix &coeffPoint3D,
1383 LinearAlgebra::Vector &coeffRHS,
1384 int observationIndex) {
1385
1386 int blockIndex = observationIndex;
1387
1388 // if we are solving for target body parameters
1389 if (m_bundleSettings->solveTargetBody()) {
1390 int numTargetPartials = coeffTarget.size2();
1391 blockIndex++;
1392
1393 // insert submatrix at column, row
1394 m_sparseNormals.insertMatrixBlock(0, 0, numTargetPartials, numTargetPartials);
1395
1396 // contribution to N11 matrix for target body
1397 (*(*m_sparseNormals[0])[0]) += prod(trans(coeffTarget), coeffTarget);
1398
1399 m_sparseNormals.insertMatrixBlock(blockIndex, 0,
1400 numTargetPartials, coeffImage.size2());
1401 (*(*m_sparseNormals[blockIndex])[0]) += prod(trans(coeffTarget),coeffImage);
1402
1403 // insert N12 target into N12
1404 N12.insertMatrixBlock(0, numTargetPartials, 3);
1405 *N12[0] += prod(trans(coeffTarget), coeffPoint3D);
1406
1407 // contribution to n1 vector
1408 vector_range<LinearAlgebra::VectorCompressed> n1_range(n1, range(0, numTargetPartials));
1409
1410 n1_range += prod(trans(coeffTarget), coeffRHS);
1411 }
1412
1413
1414 int numImagePartials = coeffImage.size2();
1415
1416 // insert submatrix at column, row
1417 m_sparseNormals.insertMatrixBlock(blockIndex, blockIndex,
1418 numImagePartials, numImagePartials);
1419
1420 (*(*m_sparseNormals[blockIndex])[blockIndex]) += prod(trans(coeffImage), coeffImage);
1421
1422 // insert N12Image into N12
1423 N12.insertMatrixBlock(blockIndex, numImagePartials, 3);
1424 *N12[blockIndex] += prod(trans(coeffImage), coeffPoint3D);
1425
1426 // insert n1Image into n1
1427 vector_range<LinearAlgebra::VectorCompressed> vr(
1428 n1,
1429 range(
1430 m_sparseNormals.at(blockIndex)->startColumn(),
1431 m_sparseNormals.at(blockIndex)->startColumn() + numImagePartials));
1432
1433 vr += prod(trans(coeffImage), coeffRHS);
1434
1435 // form N22 matrix
1436 N22 += prod(trans(coeffPoint3D), coeffPoint3D);
1437
1438 // form n2 vector
1439 n2 += prod(trans(coeffPoint3D), coeffRHS);
1440
1441 return true;
1442 }
1443
1444
1462 int BundleAdjust::formPointNormals(symmetric_matrix<double, upper>&N22,
1464 vector<double> &n2,
1465 vector<double> &nj,
1466 BundleControlPointQsp &bundleControlPoint) {
1467
1468 boost::numeric::ublas::bounded_vector<double, 3> &NIC = bundleControlPoint->nicVector();
1469 SparseBlockRowMatrix &Q = bundleControlPoint->cholmodQMatrix();
1470
1471 NIC.clear();
1472 Q.zeroBlocks();
1473
1474 int numConstrainedCoordinates = 0;
1475
1476 // weighting of 3D point parameters
1477 // Make sure weights are in the units corresponding to the bundle coordinate type
1478 boost::numeric::ublas::bounded_vector<double, 3> &weights
1479 = bundleControlPoint->weights();
1480 boost::numeric::ublas::bounded_vector<double, 3> &corrections
1481 = bundleControlPoint->corrections();
1482
1483 if (weights(0) > 0.0) {
1484 N22(0,0) += weights(0);
1485 n2(0) += (-weights(0) * corrections(0));
1486 numConstrainedCoordinates++;
1487 }
1488
1489 if (weights(1) > 0.0) {
1490 N22(1,1) += weights(1);
1491 n2(1) += (-weights(1) * corrections(1));
1492 numConstrainedCoordinates++;
1493 }
1494
1495 if (weights(2) > 0.0) {
1496 N22(2,2) += weights(2);
1497 n2(2) += (-weights(2) * corrections(2));
1498 numConstrainedCoordinates++;
1499 }
1500
1501 // invert N22
1502 invert3x3(N22);
1503
1504 // save upper triangular covariance matrix for error propagation
1505 SurfacePoint SurfacePoint = bundleControlPoint->adjustedSurfacePoint();
1506 SurfacePoint.SetMatrix(m_bundleSettings->controlPointCoordTypeBundle(), N22);
1507 bundleControlPoint->setAdjustedSurfacePoint(SurfacePoint);
1508
1509 // form Q (this is N22{-1} * N12{T})
1510 productATransB(N22, N12, Q);
1511
1512 // form product of N22(inverse) and n2; store in NIC
1513 NIC = prod(N22, n2);
1514
1515 // accumulate -R directly into reduced normal equations
1516 productAB(N12, Q);
1517
1518 // accumulate -nj
1519 accumProductAlphaAB(-1.0, Q, n2, nj);
1520
1521 return numConstrainedCoordinates;
1522 }
1523
1524
1542 int BundleAdjust::formLidarPointNormals(symmetric_matrix<double, upper>&N22,
1544 vector<double> &n2,
1545 vector<double> &nj,
1546 BundleLidarControlPointQsp &bundleLidarControlPoint) {
1547
1548 boost::numeric::ublas::bounded_vector<double, 3> &NIC = bundleLidarControlPoint->nicVector();
1549 SparseBlockRowMatrix &Q = bundleLidarControlPoint->cholmodQMatrix();
1550
1551 NIC.clear();
1552 Q.zeroBlocks();
1553
1554 int numConstrainedCoordinates = 0;
1555
1556 // weighting of 3D point parameters
1557 // Make sure weights are in the units corresponding to the bundle coordinate type
1558 boost::numeric::ublas::bounded_vector<double, 3> &weights
1559 = bundleLidarControlPoint->weights();
1560 boost::numeric::ublas::bounded_vector<double, 3> &corrections
1561 = bundleLidarControlPoint->corrections();
1562
1563 if (weights(0) > 0.0) {
1564 N22(0,0) += weights(0);
1565 n2(0) += (-weights(0) * corrections(0));
1566 numConstrainedCoordinates++;
1567 }
1568
1569 if (weights(1) > 0.0) {
1570 N22(1,1) += weights(1);
1571 n2(1) += (-weights(1) * corrections(1));
1572 numConstrainedCoordinates++;
1573 }
1574
1575 if (weights(2) > 0.0) {
1576 N22(2,2) += weights(2);
1577 n2(2) += (-weights(2) * corrections(2));
1578 numConstrainedCoordinates++;
1579 }
1580
1581 // invert N22
1582 invert3x3(N22);
1583
1584 // save upper triangular covariance matrix for error propagation
1585 SurfacePoint SurfacePoint = bundleLidarControlPoint->adjustedSurfacePoint();
1586 SurfacePoint.SetMatrix(m_bundleSettings->controlPointCoordTypeBundle(), N22);
1587 bundleLidarControlPoint->setAdjustedSurfacePoint(SurfacePoint);
1588
1589 // form Q (this is N22{-1} * N12{T})
1590 productATransB(N22, N12, Q);
1591
1592 // form product of N22(inverse) and n2; store in NIC
1593 NIC = prod(N22, n2);
1594
1595 // accumulate -R directly into reduced normal equations
1596 productAB(N12, Q);
1597
1598 // accumulate -nj
1599 accumProductAlphaAB(-1.0, Q, n2, nj);
1600
1601 return numConstrainedCoordinates;
1602 }
1603
1604
1623 bool BundleAdjust::formWeightedNormals(compressed_vector<double> &n1,
1624 vector<double> &nj) {
1625
1627
1628 int n = 0;
1629
1630 for (int i = 0; i < m_sparseNormals.size(); i++) {
1631 LinearAlgebra::Matrix *diagonalBlock = m_sparseNormals.getBlock(i, i);
1632 if ( !diagonalBlock )
1633 continue;
1634
1635 if (m_bundleSettings->solveTargetBody() && i == 0) {
1637
1638 // get parameter weights for target body
1639 vector<double> weights = m_bundleTargetBody->parameterWeights();
1640 vector<double> corrections = m_bundleTargetBody->parameterCorrections();
1641
1642 int blockSize = diagonalBlock->size1();
1643 for (int j = 0; j < blockSize; j++) {
1644 if (weights[j] > 0.0) {
1645 (*diagonalBlock)(j,j) += weights[j];
1646 nj[n] -= weights[j] * corrections(j);
1648 }
1649 n++;
1650 }
1651 }
1652 else {
1653 BundleObservationQsp observation;
1654 if (m_bundleSettings->solveTargetBody()) {
1655 // If we are solving for taget body the observation index is off by 1
1656 observation = m_bundleObservations.at(i-1);
1657 }
1658 else {
1659 observation = m_bundleObservations.at(i);
1660 }
1661
1662 // get parameter weights for this observation
1663 LinearAlgebra::Vector weights = observation->parameterWeights();
1664 LinearAlgebra::Vector corrections = observation->parameterCorrections();
1665
1666 int blockSize = diagonalBlock->size1();
1667 for (int j = 0; j < blockSize; j++) {
1668 if (weights(j) > 0.0) {
1669 (*diagonalBlock)(j,j) += weights(j);
1670 nj[n] -= weights(j) * corrections(j);
1672 }
1673 n++;
1674 }
1675 }
1676 }
1677
1678 // add n1 to nj
1679 nj += n1;
1680
1681 return true;
1682 }
1683
1684
1694 bool BundleAdjust::productATransB(symmetric_matrix <double,upper> &N22,
1697
1698 QMapIterator< int, LinearAlgebra::Matrix * > N12it(N12);
1699
1700 while ( N12it.hasNext() ) {
1701 N12it.next();
1702
1703 int rowIndex = N12it.key();
1704
1705 // insert submatrix in Q at block "rowIndex"
1706 Q.insertMatrixBlock(rowIndex, 3, N12it.value()->size1());
1707
1708 *(Q[rowIndex]) = prod(N22,trans(*(N12it.value())));
1709 }
1710
1711 return true;
1712 }
1713
1714
1726 // iterators for N12 and Q
1727 QMapIterator<int, LinearAlgebra::Matrix*> N12it(N12);
1728 QMapIterator<int, LinearAlgebra::Matrix*> Qit(Q);
1729
1730 // now multiply blocks and subtract from m_sparseNormals
1731 while ( N12it.hasNext() ) {
1732 N12it.next();
1733
1734 int rowIndex = N12it.key();
1735 LinearAlgebra::Matrix *N12block = N12it.value();
1736
1737 while ( Qit.hasNext() ) {
1738 Qit.next();
1739
1740 int columnIndex = Qit.key();
1741
1742 if ( rowIndex > columnIndex ) {
1743 continue;
1744 }
1745
1746 LinearAlgebra::Matrix *Qblock = Qit.value();
1747
1748 // insert submatrix at column, row
1749 m_sparseNormals.insertMatrixBlock(columnIndex, rowIndex,
1750 N12block->size1(), Qblock->size2());
1751
1752 (*(*m_sparseNormals[columnIndex])[rowIndex]) -= prod(*N12block,*Qblock);
1753 }
1754 Qit.toFront();
1755 }
1756 }
1757
1758
1771 vector<double> &n2,
1772 vector<double> &nj) {
1773
1774 if (alpha == 0.0) {
1775 return;
1776 }
1777
1778 int numParams;
1779
1780 QMapIterator<int, LinearAlgebra::Matrix*> Qit(Q);
1781
1782 while ( Qit.hasNext() ) {
1783 Qit.next();
1784
1785 int columnIndex = Qit.key();
1786 LinearAlgebra::Matrix *Qblock = Qit.value();
1787
1788 LinearAlgebra::Vector blockProduct = prod(trans(*Qblock),n2);
1789
1790 numParams = m_sparseNormals.at(columnIndex)->startColumn();
1791
1792 for (unsigned i = 0; i < blockProduct.size(); i++) {
1793 nj(numParams+i) += alpha*blockProduct(i);
1794 }
1795 }
1796 }
1797
1798
1809
1810 // load cholmod triplet
1811 if ( !loadCholmodTriplet() ) {
1812 QString msg = "CHOLMOD: Failed to load Triplet matrix";
1813 throw IException(IException::Programmer, msg, _FILEINFO_);
1814 }
1815
1816 // convert triplet to sparse matrix
1817 m_cholmodNormal = cholmod_l_triplet_to_sparse(m_cholmodTriplet,
1818 m_cholmodTriplet->nnz,
1819 &m_cholmodCommon);
1820
1821 // analyze matrix
1822 // TODO should we analyze just 1st iteration?
1823 m_L = cholmod_l_analyze(m_cholmodNormal, &m_cholmodCommon);
1824
1825 // create cholmod cholesky factor
1826 // CHOLMOD will choose LLT or LDLT decomposition based on the characteristics of the matrix.
1827 cholmod_l_factorize(m_cholmodNormal, m_L, &m_cholmodCommon);
1828
1829 // check for "matrix not positive definite" error
1830 if (m_cholmodCommon.status == CHOLMOD_NOT_POSDEF) {
1831 QString msg = "Matrix NOT positive-definite: failure at column " + toString((int) m_L->minor);
1832// throw IException(IException::User, msg, _FILEINFO_);
1833 error(msg);
1834 emit(finished());
1835 return false;
1836 }
1837
1838 // cholmod solution and right-hand side vectors
1839 cholmod_dense *x, *b;
1840
1841 // initialize right-hand side vector
1842 b = cholmod_l_zeros(m_cholmodNormal->nrow, 1, m_cholmodNormal->xtype, &m_cholmodCommon);
1843
1844 // copy right-hand side vector into b
1845 double *px = (double*)b->x;
1846 for (int i = 0; i < m_rank; i++) {
1847 px[i] = m_RHS[i];
1848 }
1849
1850 // cholmod solve
1851 x = cholmod_l_solve(CHOLMOD_A, m_L, b, &m_cholmodCommon);
1852
1853 // copy solution vector x out into m_imageSolution
1854 double *sx = (double*)x->x;
1855 for (int i = 0; i < m_rank; i++) {
1856 m_imageSolution[i] = sx[i];
1857 }
1858
1859 // free cholmod structures
1860 cholmod_l_free_sparse(&m_cholmodNormal, &m_cholmodCommon);
1861 cholmod_l_free_dense(&b, &m_cholmodCommon);
1862 cholmod_l_free_dense(&x, &m_cholmodCommon);
1863
1864 return true;
1865 }
1866
1867
1880
1881 if ( m_iteration == 1 ) {
1882 int numElements = m_sparseNormals.numberOfElements();
1883 m_cholmodTriplet = cholmod_l_allocate_triplet(m_rank, m_rank, numElements,
1884 -1, CHOLMOD_REAL, &m_cholmodCommon);
1885
1886 if ( !m_cholmodTriplet ) {
1887 outputBundleStatus("\nTriplet allocation failure\n");
1888 return false;
1889 }
1890 m_cholmodTriplet->nnz = 0;
1891 }
1892
1893 long *tripletColumns = (long*) m_cholmodTriplet->i;
1894 long *tripletRows = (long*) m_cholmodTriplet->j;
1895 double *tripletValues = (double*)m_cholmodTriplet->x;
1896
1897 double entryValue;
1898
1899 long numEntries = 0;
1900
1901 long numBlockcolumns = m_sparseNormals.size();
1902 for (int columnIndex = 0; columnIndex < numBlockcolumns; columnIndex++) {
1903
1904 SparseBlockColumnMatrix *normalsColumn = m_sparseNormals[columnIndex];
1905
1906 if ( !normalsColumn ) {
1907 QString status = "\nSparseBlockColumnMatrix retrieval failure at column " +
1908 QString::number(columnIndex);
1909 outputBundleStatus(status);
1910 return false;
1911 }
1912
1913 int numLeadingColumns = normalsColumn->startColumn();
1914
1915 QMapIterator< int, LinearAlgebra::Matrix * > it(*normalsColumn);
1916
1917 while ( it.hasNext() ) {
1918 it.next();
1919
1920 int rowIndex = it.key();
1921
1922 // note: as the normal equations matrix is symmetric, the # of leading rows for a block is
1923 // equal to the # of leading columns for a block column at the "rowIndex" position
1924 int numLeadingRows = m_sparseNormals.at(rowIndex)->startColumn();
1925
1926 LinearAlgebra::Matrix *normalsBlock = it.value();
1927 if ( !normalsBlock ) {
1928 QString status = "\nmatrix block retrieval failure at column ";
1929 status.append(columnIndex);
1930 status.append(", row ");
1931 status.append(rowIndex);
1932 outputBundleStatus(status);
1933 status = "Total # of block columns: " + QString::number(numBlockcolumns);
1934 outputBundleStatus(status);
1935 status = "Total # of blocks: " + QString::number(m_sparseNormals.numberOfBlocks());
1936 outputBundleStatus(status);
1937 return false;
1938 }
1939
1940 if ( columnIndex == rowIndex ) { // diagonal block (upper-triangular)
1941 for (unsigned ii = 0; ii < normalsBlock->size1(); ii++) {
1942 for (unsigned jj = ii; jj < normalsBlock->size2(); jj++) {
1943 entryValue = normalsBlock->at_element(ii,jj);
1944 int entryColumnIndex = jj + numLeadingColumns;
1945 int entryRowIndex = ii + numLeadingRows;
1946
1947 if ( m_iteration == 1 ) {
1948 tripletColumns[numEntries] = entryColumnIndex;
1949 tripletRows[numEntries] = entryRowIndex;
1950 m_cholmodTriplet->nnz++;
1951 }
1952
1953 tripletValues[numEntries] = entryValue;
1954
1955 numEntries++;
1956 }
1957 }
1958 }
1959 else { // off-diagonal block (square)
1960 for (unsigned ii = 0; ii < normalsBlock->size1(); ii++) {
1961 for (unsigned jj = 0; jj < normalsBlock->size2(); jj++) {
1962 entryValue = normalsBlock->at_element(ii,jj);
1963 int entryColumnIndex = jj + numLeadingColumns;
1964 int entryRowIndex = ii + numLeadingRows;
1965
1966 if ( m_iteration ==1 ) {
1967 tripletColumns[numEntries] = entryRowIndex;
1968 tripletRows[numEntries] = entryColumnIndex;
1969 m_cholmodTriplet->nnz++;
1970 }
1971
1972 tripletValues[numEntries] = entryValue;
1973
1974 numEntries++;
1975 }
1976 }
1977 }
1978 }
1979 }
1980
1981 return true;
1982 }
1983
1984
1998 double det;
1999 double den;
2000
2001 boost::numeric::ublas::symmetric_matrix< double, upper > c = m;
2002
2003 den = m(0, 0) * (m(1, 1) * m(2, 2) - m(1, 2) * m(2, 1))
2004 - m(0, 1) * (m(1, 0) * m(2, 2) - m(1, 2) * m(2, 0))
2005 + m(0, 2) * (m(1, 0) * m(2, 1) - m(1, 1) * m(2, 0));
2006
2007 // check for divide by zero
2008 if (fabs(den) < 1.0e-100) {
2009 return false;
2010 }
2011
2012 det = 1.0 / den;
2013
2014 m(0, 0) = (c(1, 1) * c(2, 2) - c(1, 2) * c(2, 1)) * det;
2015 m(0, 1) = (c(0, 2) * c(2, 1) - c(0, 1) * c(2, 2)) * det;
2016 m(0, 2) = (c(0, 1) * c(1, 2) - c(0, 2) * c(1, 1)) * det;
2017 m(1, 1) = (c(0, 0) * c(2, 2) - c(0, 2) * c(2, 0)) * det;
2018 m(1, 2) = (c(0, 2) * c(1, 0) - c(0, 0) * c(1, 2)) * det;
2019 m(2, 2) = (c(0, 0) * c(1, 1) - c(0, 1) * c(1, 0)) * det;
2020
2021 return true;
2022 }
2023
2024
2044 bool BundleAdjust::computePartials(matrix<double> &coeffTarget,
2045 matrix<double> &coeffImage,
2046 matrix<double> &coeffPoint3D,
2047 vector<double> &coeffRHS,
2048 BundleMeasure &measure,
2049 BundleControlPoint &point) {
2050
2051 Camera *measureCamera = measure.camera();
2052 BundleObservationQsp observation = measure.parentBundleObservation();
2053
2054 int numImagePartials = observation->numberParameters();
2055
2056 // we're saving the number of image partials in m_previousNumberImagePartials
2057 // to compare to the previous computePartials call to avoid unnecessary resizing of the
2058 // coeffImage matrix
2059 if (numImagePartials != m_previousNumberImagePartials) {
2060 coeffImage.resize(2,numImagePartials);
2061 m_previousNumberImagePartials = numImagePartials;
2062 }
2063
2064 // No need to call SetImage for framing camera
2065 if (measureCamera->GetCameraType() != Camera::Framing) {
2066 // Set the Spice to the measured point. A framing camera exposes the entire image at one time.
2067 // It will have a single set of Spice for the entire image. Scanning cameras may populate a single
2068 // image with multiple exposures, each with a unique set of Spice. SetImage needs to be called
2069 // repeatedly for these images to point to the Spice for the current pixel.
2070 measureCamera->SetImage(measure.sample(), measure.line());
2071 }
2072
2073 // CSM Cameras do not have a ground map
2074 if (measureCamera->GetCameraType() != Camera::Csm) {
2075 // Compute the look vector in instrument coordinates based on time of observation and apriori
2076 // lat/lon/radius. As of 05/15/2019, this call no longer does the back-of-planet test. An optional
2077 // bool argument was added CameraGroundMap::GetXY to turn off the test.
2078 double computedX, computedY;
2079 if (!(measureCamera->GroundMap()->GetXY(point.adjustedSurfacePoint(),
2080 &computedX, &computedY, false))) {
2081 QString msg = "Unable to map apriori surface point for measure ";
2082 msg += measure.cubeSerialNumber() + " on point " + point.id() + " into focal plane";
2083 throw IException(IException::User, msg, _FILEINFO_);
2084 }
2085 }
2086
2087 if (m_bundleSettings->solveTargetBody()) {
2088 observation->computeTargetPartials(coeffTarget, measure, m_bundleSettings, m_bundleTargetBody);
2089 }
2090
2091 observation->computeImagePartials(coeffImage, measure);
2092
2093 // Complete partials calculations for 3D point (latitudinal or rectangular)
2094 // Retrieve the coordinate type (latitudinal or rectangular) and compute the partials for
2095 // the fixed point with respect to each coordinate in Body-Fixed
2096 SurfacePoint::CoordinateType coordType = m_bundleSettings->controlPointCoordTypeBundle();
2097 observation->computePoint3DPartials(coeffPoint3D, measure, coordType);
2098
2099 // right-hand side (measured - computed)
2100 observation->computeRHSPartials(coeffRHS, measure);
2101
2102 double deltaX = coeffRHS(0);
2103 double deltaY = coeffRHS(1);
2104
2105 m_bundleResults.addResidualsProbabilityDistributionObservation(observation->computeObservationValue(measure, deltaX));
2106 m_bundleResults.addResidualsProbabilityDistributionObservation(observation->computeObservationValue(measure, deltaY));
2107
2110 // If maximum likelihood estimation is being used
2111 double residualR2ZScore = sqrt(deltaX * deltaX + deltaY * deltaY) / sqrt(2.0);
2112
2113 // Dynamically build the cumulative probability distribution of the R^2 residual Z Scores
2115
2116 int currentModelIndex = m_bundleResults.maximumLikelihoodModelIndex();
2117 double observationWeight = m_bundleResults.maximumLikelihoodModelWFunc(currentModelIndex)
2118 .sqrtWeightScaler(residualR2ZScore);
2119 coeffImage *= observationWeight;
2120 coeffPoint3D *= observationWeight;
2121 coeffRHS *= observationWeight;
2122
2123 if (m_bundleSettings->solveTargetBody()) {
2124 coeffTarget *= observationWeight;
2125 }
2126 }
2127
2128 return true;
2129 }
2130
2131
2136 emit(statusBarUpdate("Updating Parameters"));
2137 int t = 0;
2138
2139 // TODO - update target body parameters if in solution
2140 // note these come before BundleObservation parameters in normal equations matrix
2141 if (m_bundleSettings->solveTargetBody()) {
2142 int numTargetBodyParameters = m_bundleTargetBody->numberParameters();
2143
2144 m_bundleTargetBody->applyParameterCorrections(subrange(m_imageSolution,0,
2145 numTargetBodyParameters));
2146
2147 t += numTargetBodyParameters;
2148 }
2149
2150 // Update spice for each BundleObservation
2151 int numObservations = m_bundleObservations.size();
2152 for (int i = 0; i < numObservations; i++) {
2153 BundleObservationQsp observation = m_bundleObservations.at(i);
2154
2155 int numParameters = observation->numberParameters();
2156
2157 observation->applyParameterCorrections(subrange(m_imageSolution,t,t+numParameters));
2158
2159 if (m_bundleSettings->solveTargetBody()) {
2160 // TODO: needs to be updated for ISIS vs. CSM CSM has no updateBodyRotation]
2161 // TODO: this is no good.
2162 QSharedPointer<IsisBundleObservation> isisObservation = qSharedPointerDynamicCast<IsisBundleObservation>(observation);
2163 isisObservation->updateBodyRotation();
2164 }
2165
2166 t += numParameters;
2167 }
2168
2169 // Apply corrections for photogrammetric control points
2170 for (int i = 0; i < m_bundleControlPoints.size(); i++) {
2172
2173 if (point->isRejected()) {
2174 continue;
2175 }
2176
2177 point->applyParameterCorrections(m_imageSolution, m_sparseNormals,
2178 m_bundleTargetBody);
2179
2180 }
2181
2182 // Apply corrections for lidar points
2183 for (int i = 0; i < m_bundleLidarControlPoints.size(); i++) {
2185
2186 if (point->isRejected()) {
2187 continue;
2188 }
2189
2190 point->applyParameterCorrections(m_imageSolution, m_sparseNormals,
2191 m_bundleTargetBody);
2192
2193 }
2194 }
2195
2196
2204 emit(statusBarUpdate("Computing Residuals"));
2205 double vtpv = 0.0;
2206
2207 // x, y, and xy residual stats vectors
2208 Statistics xResiduals;
2209 Statistics yResiduals;
2210 Statistics xyResiduals;
2211
2212 // vtpv for photo measures
2213 for (int i = 0; i < m_bundleControlPoints.size(); i++) {
2214 vtpv += m_bundleControlPoints.at(i)->vtpvMeasures();
2215 vtpv += m_bundleControlPoints.at(i)->vtpv();
2216 }
2217
2218 // vtpv for lidar measures
2219 for (int i = 0; i < m_bundleLidarControlPoints.size(); i++) {
2220 vtpv += m_bundleLidarControlPoints.at(i)->vtpvMeasures();
2221 vtpv += m_bundleLidarControlPoints.at(i)->vtpv();
2223 }
2224
2225 // vtpv for image parameters
2226 for (int i = 0; i < m_bundleObservations.size(); i++) {
2227 vtpv += m_bundleObservations.at(i)->vtpv();
2228 }
2229
2230 // vtpv for target body parameters
2231 if ( m_bundleTargetBody) {
2232 vtpv += m_bundleTargetBody->vtpv();
2233 }
2234
2235
2236 // Compute rms for all image coordinate residuals
2237 // separately for x, y, then x and y together
2238 m_bundleResults.setRmsXYResiduals(xResiduals.Rms(), yResiduals.Rms(), xyResiduals.Rms());
2239
2240 return vtpv;
2241 }
2242
2243
2259 double vx, vy;
2260
2261 int numResiduals = m_bundleResults.numberObservations() / 2;
2262
2263 std::vector<double> residuals;
2264
2265 residuals.resize(numResiduals);
2266
2267 // load magnitude (squared) of residual vector
2268 int residualIndex = 0;
2269 int numObjectPoints = m_bundleControlPoints.size();
2270 for (int i = 0; i < numObjectPoints; i++) {
2271
2272 const BundleControlPointQsp point = m_bundleControlPoints.at(i);
2273
2274 if ( point->isRejected() ) {
2275 continue;
2276 }
2277
2278 int numMeasures = point->numberOfMeasures();
2279 for (int j = 0; j < numMeasures; j++) {
2280
2281 const BundleMeasureQsp measure = point->at(j);
2282
2283 if ( measure->isRejected() ) {
2284 continue;
2285 }
2286
2287 vx = measure->sampleResidual();
2288 vy = measure->lineResidual();
2289
2290 residuals[residualIndex] = sqrt(vx*vx + vy*vy);
2291
2292 residualIndex++;
2293 }
2294 }
2295
2296 // sort vectors
2297 std::sort(residuals.begin(), residuals.end());
2298
2299 double median;
2300 double medianDev;
2301 double mad;
2302
2303 int midpointIndex = numResiduals / 2;
2304
2305 if ( numResiduals % 2 == 0 ) {
2306 median = (residuals[midpointIndex - 1] + residuals[midpointIndex]) / 2;
2307 }
2308 else {
2309 median = residuals[midpointIndex];
2310 }
2311
2312 // compute M.A.D.
2313 for (int i = 0; i < numResiduals; i++) {
2314 residuals[i] = fabs(residuals[i] - median);
2315 }
2316
2317 std::sort(residuals.begin(), residuals.end());
2318
2319 if ( numResiduals % 2 == 0 ) {
2320 medianDev = (residuals[midpointIndex - 1] + residuals[midpointIndex]) / 2;
2321 }
2322 else {
2323 medianDev = residuals[midpointIndex];
2324 }
2325
2326 QString status = "\nmedian deviation: ";
2327 status.append(QString("%1").arg(medianDev));
2328 status.append("\n");
2329 outputBundleStatus(status);
2330
2331 mad = 1.4826 * medianDev;
2332
2333 status = "\nmad: ";
2334 status.append(QString("%1").arg(mad));
2335 status.append("\n");
2336 outputBundleStatus(status);
2337
2339 + m_bundleSettings->outlierRejectionMultiplier() * mad);
2340
2341 status = "\nRejection Limit: ";
2342 status.append(QString("%1").arg(m_bundleResults.rejectionLimit()));
2343 status.append("\n");
2344 outputBundleStatus(status);
2345
2346 return true;
2347 }
2348
2349
2358 double vx, vy;
2359 int numRejected;
2360 int totalNumRejected = 0;
2361
2362 int maxResidualIndex;
2363 double maxResidual;
2364 double sumSquares;
2365 double usedRejectionLimit = m_bundleResults.rejectionLimit();
2366
2367 // TODO What to do if usedRejectionLimit is too low?
2368
2369 int numComingBack = 0;
2370
2371 int numObjectPoints = m_bundleControlPoints.size();
2372
2373 outputBundleStatus("\n");
2374 for (int i = 0; i < numObjectPoints; i++) {
2376
2377 point->zeroNumberOfRejectedMeasures();
2378
2379 numRejected = 0;
2380 maxResidualIndex = -1;
2381 maxResidual = -1.0;
2382
2383 int numMeasures = point->numberOfMeasures();
2384 for (int j = 0; j < numMeasures; j++) {
2385
2386 BundleMeasureQsp measure = point->at(j);
2387
2388 vx = measure->sampleResidual();
2389 vy = measure->lineResidual();
2390
2391 sumSquares = sqrt(vx*vx + vy*vy);
2392
2393 // measure is good
2394 if ( sumSquares <= usedRejectionLimit ) {
2395
2396 // was it previously rejected?
2397 if ( measure->isRejected() ) {
2398 QString status = "Coming back in: ";
2399 status.append(QString("%1").arg(point->id().toLatin1().data()));
2400 status.append("\r");
2401 outputBundleStatus(status);
2402 numComingBack++;
2403 m_controlNet->DecrementNumberOfRejectedMeasuresInImage(measure->cubeSerialNumber());
2404 }
2405
2406 measure->setRejected(false);
2407 continue;
2408 }
2409
2410 // if it's still rejected, skip it
2411 if ( measure->isRejected() ) {
2412 numRejected++;
2413 totalNumRejected++;
2414 continue;
2415 }
2416
2417 if ( sumSquares > maxResidual ) {
2418 maxResidual = sumSquares;
2419 maxResidualIndex = j;
2420 }
2421 }
2422
2423 // no observations above the current rejection limit for this 3D point
2424 if ( maxResidual == -1.0 || maxResidual <= usedRejectionLimit ) {
2425 point->setNumberOfRejectedMeasures(numRejected);
2426 continue;
2427 }
2428
2429 // this is another kluge - if we only have two observations
2430 // we won't reject (for now)
2431 if ((numMeasures - (numRejected + 1)) < 2) {
2432 point->setNumberOfRejectedMeasures(numRejected);
2433 continue;
2434 }
2435
2436 // otherwise, we have at least one observation
2437 // for this point whose residual is above the
2438 // current rejection limit - we'll flag the
2439 // worst of these as rejected
2440 BundleMeasureQsp rejected = point->at(maxResidualIndex);
2441 rejected->setRejected(true);
2442 numRejected++;
2443 point->setNumberOfRejectedMeasures(numRejected);
2444 m_controlNet->IncrementNumberOfRejectedMeasuresInImage(rejected->cubeSerialNumber());
2445 totalNumRejected++;
2446
2447 // do we still have sufficient remaining observations for this 3D point?
2448 if ( ( numMeasures-numRejected ) < 2 ) {
2449 point->setRejected(true);
2450 QString status = "Rejecting Entire Point: ";
2451 status.append(QString("%1").arg(point->id().toLatin1().data()));
2452 status.append("\r");
2453 outputBundleStatus(status);
2454 }
2455 else
2456 point->setRejected(false);
2457
2458 }
2459
2460 int numberRejectedObservations = 2*totalNumRejected;
2461
2462 QString status = "\nRejected Observations:";
2463 status.append(QString("%1").arg(numberRejectedObservations));
2464 status.append(" (Rejection Limit:");
2465 status.append(QString("%1").arg(usedRejectionLimit));
2466 status.append(")\n");
2467 outputBundleStatus(status);
2468
2469 m_bundleResults.setNumberRejectedObservations(numberRejectedObservations);
2470
2471 status = "\nMeasures that came back: ";
2472 status.append(QString("%1").arg(numComingBack));
2473 status.append("\n");
2474 outputBundleStatus(status);
2475
2476 return true;
2477 }
2478
2479
2487 QList<ImageList *> BundleAdjust::imageLists() {
2488
2489 if (m_imageLists.count() > 0) {
2490 return m_imageLists;
2491 }
2492 else if (m_serialNumberList->size() > 0) {
2493 ImageList *imgList = new ImageList;
2494 try {
2495 for (int i = 0; i < m_serialNumberList->size(); i++) {
2496 Image *image = new Image(m_serialNumberList->fileName(i));
2497 imgList->append(image);
2498 image->closeCube();
2499 }
2500 m_imageLists.append(imgList);
2501 }
2502 catch (IException &e) {
2503 QString msg = "Invalid image in serial number list\n";
2504 throw IException(IException::Programmer, msg, _FILEINFO_);
2505 }
2506 }
2507 else {
2508 QString msg = "No images used in bundle adjust\n";
2509 throw IException(IException::Programmer, msg, _FILEINFO_);
2510 }
2511
2512 return m_imageLists;
2513 }
2514
2515
2537 emit(statusBarUpdate("Error Propagation"));
2538 // free unneeded memory
2539 cholmod_l_free_triplet(&m_cholmodTriplet, &m_cholmodCommon);
2540 cholmod_l_free_sparse(&m_cholmodNormal, &m_cholmodCommon);
2541
2542 LinearAlgebra::Matrix T(3, 3);
2543 // *** TODO ***
2544 // Can any of the control point specific code be moved to BundleControlPoint?
2545
2546 double sigma0Squared = m_bundleResults.sigma0() * m_bundleResults.sigma0();
2547
2548 int numObjectPoints = m_bundleControlPoints.size();
2549
2550 std::string currentTime = iTime::CurrentLocalTime().toLatin1().data();
2551
2552 QString status = " Time: ";
2553 status.append(currentTime.c_str());
2554 status.append("\n\n");
2555 outputBundleStatus(status);
2556
2557 // create and initialize array of 3x3 matrices for all object points
2558 std::vector< symmetric_matrix<double> > pointCovariances(numObjectPoints,
2559 symmetric_matrix<double>(3));
2560 for (int d = 0; d < numObjectPoints; d++) {
2561 pointCovariances[d].clear();
2562 }
2563
2564 cholmod_dense *x; // solution vector
2565 cholmod_dense *b; // right-hand side (column vectors of identity)
2566
2567 b = cholmod_l_zeros ( m_rank, 1, CHOLMOD_REAL, &m_cholmodCommon );
2568 double *pb = (double*)b->x;
2569
2570 double *px = NULL;
2571
2572 SparseBlockColumnMatrix inverseMatrix;
2573
2574 // Create unique file name
2575 FileName matrixFile(m_bundleSettings->outputFilePrefix() + "inverseMatrix.dat");
2576 //???FileName matrixFile = FileName::createTempFile(m_bundleSettings.outputFilePrefix()
2577 //??? + "inverseMatrix.dat");
2578 // Create file handle
2579 QFile matrixOutput(matrixFile.expanded());
2580
2581 // Check to see if creating the inverse correlation matrix is turned on
2582 if (m_bundleSettings->createInverseMatrix()) {
2583 // Open file to write to
2584 matrixOutput.open(QIODevice::WriteOnly);
2585 }
2586 QDataStream outStream(&matrixOutput);
2587
2588 int i, j, k;
2589 int columnIndex = 0;
2590 int numColumns = 0;
2591 int numBlockColumns = m_sparseNormals.size();
2592 for (i = 0; i < numBlockColumns; i++) {
2593
2594 // columns in this column block
2595 SparseBlockColumnMatrix *normalsColumn = m_sparseNormals.at(i);
2596 if (i == 0) {
2597 numColumns = normalsColumn->numberOfColumns();
2598 int numRows = normalsColumn->numberOfRows();
2599 inverseMatrix.insertMatrixBlock(i, numRows, numColumns);
2600 inverseMatrix.zeroBlocks();
2601 }
2602 else {
2603 if (normalsColumn->numberOfColumns() == numColumns) {
2604 int numRows = normalsColumn->numberOfRows();
2605 inverseMatrix.insertMatrixBlock(i, numRows, numColumns);
2606 inverseMatrix.zeroBlocks();
2607 }
2608 else {
2609 numColumns = normalsColumn->numberOfColumns();
2610
2611 // reset inverseMatrix
2612 inverseMatrix.wipe();
2613
2614 // insert blocks
2615 for (j = 0; j < (i+1); j++) {
2616 SparseBlockColumnMatrix *normalsRow = m_sparseNormals.at(j);
2617 int numRows = normalsRow->numberOfRows();
2618
2619 inverseMatrix.insertMatrixBlock(j, numRows, numColumns);
2620 }
2621 }
2622 }
2623
2624 int localCol = 0;
2625
2626 // solve for inverse for nCols
2627 for (j = 0; j < numColumns; j++) {
2628 if ( columnIndex > 0 ) {
2629 pb[columnIndex - 1] = 0.0;
2630 }
2631 pb[columnIndex] = 1.0;
2632
2633 x = cholmod_l_solve ( CHOLMOD_A, m_L, b, &m_cholmodCommon );
2634 px = (double*)x->x;
2635 int rp = 0;
2636
2637 // store solution in corresponding column of inverse
2638 for (k = 0; k < inverseMatrix.size(); k++) {
2639 LinearAlgebra::Matrix *matrix = inverseMatrix.value(k);
2640
2641 int sz1 = matrix->size1();
2642
2643 for (int ii = 0; ii < sz1; ii++) {
2644 (*matrix)(ii,localCol) = px[ii + rp];
2645 }
2646 rp += matrix->size1();
2647 }
2648
2649 columnIndex++;
2650 localCol++;
2651
2652 cholmod_l_free_dense(&x,&m_cholmodCommon);
2653 }
2654
2655 // save adjusted target body sigmas if solving for target
2656 if (m_bundleSettings->solveTargetBody() && i == 0) {
2657 vector< double > &adjustedSigmas = m_bundleTargetBody->adjustedSigmas();
2658 matrix< double > *targetCovMatrix = inverseMatrix.value(i);
2659
2660 for (int z = 0; z < numColumns; z++)
2661 adjustedSigmas[z] = sqrt((*targetCovMatrix)(z,z))*m_bundleResults.sigma0();
2662 }
2663 // save adjusted image sigmas
2664 else {
2665 BundleObservationQsp observation;
2666 if (m_bundleSettings->solveTargetBody()) {
2667 observation = m_bundleObservations.at(i-1);
2668 }
2669 else {
2670 observation = m_bundleObservations.at(i);
2671 }
2672 vector< double > &adjustedSigmas = observation->adjustedSigmas();
2673 matrix< double > *imageCovMatrix = inverseMatrix.value(i);
2674 for ( int z = 0; z < numColumns; z++) {
2675 adjustedSigmas[z] = sqrt((*imageCovMatrix)(z,z))*m_bundleResults.sigma0();
2676 }
2677 }
2678
2679 // Output the inverse matrix if requested
2680 if (m_bundleSettings->createInverseMatrix()) {
2681 outStream << inverseMatrix;
2682 }
2683
2684 // now loop over all object points to sum contributions into 3x3 point covariance matrix
2685 int pointIndex = 0;
2686 for (j = 0; j < numObjectPoints; j++) {
2687 emit(pointUpdate(j+1));
2688 BundleControlPointQsp point = m_bundleControlPoints.at(pointIndex);
2689 if ( point->isRejected() ) {
2690 continue;
2691 }
2692
2693 // only update point every 100 points
2694 if (j%100 == 0) {
2695 QString status = "\rError Propagation: Inverse Block ";
2696 status.append(QString::number(i+1));
2697 status.append(" of ");
2698 status.append(QString::number(numBlockColumns));
2699 status.append("; Point ");
2700 status.append(QString::number(j+1));
2701 status.append(" of ");
2702 status.append(QString::number(numObjectPoints));
2703 outputBundleStatus(status);
2704 }
2705
2706 // get corresponding Q matrix
2707 // NOTE: we are getting a reference to the Q matrix stored
2708 // in the BundleControlPoint for speed (without the & it is dirt slow)
2709 SparseBlockRowMatrix &Q = point->cholmodQMatrix();
2710
2711 T.clear();
2712
2713 // get corresponding point covariance matrix
2714 boost::numeric::ublas::symmetric_matrix<double> &covariance = pointCovariances[pointIndex];
2715
2716 // get firstQBlock - index i is the key into Q for firstQBlock
2717 LinearAlgebra::Matrix *firstQBlock = Q.value(i);
2718 if (!firstQBlock) {
2719 pointIndex++;
2720 continue;
2721 }
2722
2723 // iterate over Q
2724 // secondQBlock is current map value
2725 QMapIterator< int, LinearAlgebra::Matrix * > it(Q);
2726 while ( it.hasNext() ) {
2727 it.next();
2728
2729 int nKey = it.key();
2730
2731 if (it.key() > i) {
2732 break;
2733 }
2734
2735 LinearAlgebra::Matrix *secondQBlock = it.value();
2736
2737 if ( !secondQBlock ) {// should never be NULL
2738 continue;
2739 }
2740
2741 LinearAlgebra::Matrix *inverseBlock = inverseMatrix.value(it.key());
2742
2743 if ( !inverseBlock ) {// should never be NULL
2744 continue;
2745 }
2746
2747 T = prod(*inverseBlock, trans(*firstQBlock));
2748 T = prod(*secondQBlock,T);
2749
2750 if (nKey != i) {
2751 T += trans(T);
2752 }
2753
2754 try {
2755 covariance += T;
2756 }
2757
2758 catch (std::exception &e) {
2759 outputBundleStatus("\n\n");
2760 QString msg = "Input data and settings are not sufficiently stable "
2761 "for error propagation.";
2762 throw IException(IException::User, msg, _FILEINFO_);
2763 }
2764 }
2765 pointIndex++;
2766 }
2767 }
2768
2769 if (m_bundleSettings->createInverseMatrix()) {
2770 // Close the file.
2771 matrixOutput.close();
2772 // Save the location of the "covariance" matrix
2774 }
2775
2776 // can free sparse normals now
2778
2779 // free b (right-hand side vector)
2780 cholmod_l_free_dense(&b,&m_cholmodCommon);
2781
2782 outputBundleStatus("\n\n");
2783
2784 currentTime = Isis::iTime::CurrentLocalTime().toLatin1().data();
2785
2786 status = "\rFilling point covariance matrices: Time ";
2787 status.append(currentTime.c_str());
2788 outputBundleStatus(status);
2789 outputBundleStatus("\n\n");
2790
2791 // now loop over points again and set final covariance stuff
2792 // *** TODO *** Can this loop go into BundleControlPoint
2793 int pointIndex = 0;
2794 for (j = 0; j < numObjectPoints; j++) {
2795
2796 BundleControlPointQsp point = m_bundleControlPoints.at(pointIndex);
2797
2798 if ( point->isRejected() ) {
2799 continue;
2800 }
2801
2802 if (j%100 == 0) {
2803 status = "\rError Propagation: Filling point covariance matrices ";
2804 status.append(QString("%1").arg(j+1));
2805 status.append(" of ");
2806 status.append(QString("%1").arg(numObjectPoints));
2807 status.append("\r");
2808 outputBundleStatus(status);
2809 }
2810
2811 // get corresponding point covariance matrix
2812 boost::numeric::ublas::symmetric_matrix<double> &covariance = pointCovariances[pointIndex];
2813
2814 // Update and reset the matrix
2815 // Get the Limiting Error Propagation uncertainties: sigmas for coordinate 1, 2, and 3 in meters
2816 //
2817 SurfacePoint SurfacePoint = point->adjustedSurfacePoint();
2818
2819 // Get the TEP by adding the corresponding members of pCovar and covariance
2820 boost::numeric::ublas::symmetric_matrix <double,boost::numeric::ublas::upper> pCovar;
2821
2822 if (m_bundleSettings->controlPointCoordTypeBundle() == SurfacePoint::Latitudinal) {
2823 pCovar = SurfacePoint.GetSphericalMatrix(SurfacePoint::Kilometers);
2824 }
2825 else {
2826 // Assume Rectangular coordinates
2827 pCovar = SurfacePoint.GetRectangularMatrix(SurfacePoint::Kilometers);
2828 }
2829 pCovar += covariance;
2830 pCovar *= sigma0Squared;
2831
2832 // debug lines
2833 // if (j < 3) {
2834 // std::cout << " Adjusted surface point ..." << std::endl;
2835 // std:: cout << " sigmaLat (radians) = " << sqrt(pCovar(0,0)) << std::endl;
2836 // std:: cout << " sigmaLon (radians) = " << sqrt(pCovar(1,1)) << std::endl;
2837 // std:: cout << " sigmaRad (km) = " << sqrt(pCovar(2,2)) << std::endl;
2838 // std::cout << " Adjusted matrix = " << std::endl;
2839 // std::cout << " " << pCovar(0,0) << " " << pCovar(0,1) << " "
2840 // << pCovar(0,2) << std::endl;
2841 // std::cout << " " << pCovar(1,0) << " " << pCovar(1,1) << " "
2842 // << pCovar(1,2) << std::endl;
2843 // std::cout << " " << pCovar(2,0) << " " << pCovar(2,1) << " "
2844 // << pCovar(2,2) << std::endl;
2845 // }
2846 // end debug
2847
2848 // Distance units are km**2
2849 SurfacePoint.SetMatrix(m_bundleSettings->controlPointCoordTypeBundle(),pCovar);
2850 point->setAdjustedSurfacePoint(SurfacePoint);
2851 // // debug lines
2852 // if (j < 3) {
2853 // boost::numeric::ublas::symmetric_matrix <double,boost::numeric::ublas::upper> recCovar;
2854 // recCovar = SurfacePoint.GetRectangularMatrix(SurfacePoint::Meters);
2855 // std:: cout << " sigmaLat (meters) = " <<
2856 // point->adjustedSurfacePoint().GetSigmaDistance(SurfacePoint::Latitudinal,
2857 // SurfacePoint::One).meters() << std::endl;
2858 // std:: cout << " sigmaLon (meters) = " <<
2859 // point->adjustedSurfacePoint().GetSigmaDistance(SurfacePoint::Latitudinal,
2860 // SurfacePoint::Two).meters() << std::endl;
2861 // std:: cout << " sigmaRad (km) = " << sqrt(pCovar(2,2)) << std::endl;
2862 // std::cout << "Rectangular matrix with radius in meters" << std::endl;
2863 // std::cout << " " << recCovar(0,0) << " " << recCovar(0,1) << " "
2864 // << recCovar(0,2) << std::endl;
2865 // std::cout << " " << recCovar(1,0) << " " << recCovar(1,1) << " "
2866 // << recCovar(1,2) << std::endl;
2867 // std::cout << " " << recCovar(2,0) << " " << recCovar(2,1) << " "
2868 // << recCovar(2,2) << std::endl;
2869 // }
2870 // // end debug
2871
2872 pointIndex++;
2873 }
2874
2875 return true;
2876 }
2877
2878
2887
2888
2897
2898
2907
2908
2915 return m_serialNumberList->size();
2916 }
2917
2918
2928 QString BundleAdjust::fileName(int i) {
2929 return m_serialNumberList->fileName(i);
2930 }
2931
2932
2939 return m_iteration;
2940 }
2941
2942
2954 return m_controlNet->Camera(i)->instrumentRotation()->Cache("InstrumentPointing");
2955 }
2956
2957
2969 return m_controlNet->Camera(i)->instrumentPosition()->Cache("InstrumentPosition");
2970 }
2971
2972
2982 Camera *imageCam = m_controlNet->Camera(i);
2983 if (imageCam->GetCameraType() != Camera::Csm) {
2984 QString msg = "Cannot get model state for image [" + toString(i) +
2985 "] because it is not a CSM camera model.";
2986 throw IException(IException::Programmer, msg, _FILEINFO_);
2987 }
2988
2989 CSMCamera *csmCamera = dynamic_cast<CSMCamera*>(imageCam);
2990 return csmCamera->getModelState();
2991 }
2992
2993
3003 QString iterationNumber;
3004
3005 if ( m_bundleResults.converged() ) {
3006 iterationNumber = "Iteration" + toString(m_iteration) + ": Final";
3007 }
3008 else {
3009 iterationNumber = "Iteration" + toString(m_iteration);
3010 }
3011
3012 PvlGroup summaryGroup(iterationNumber);
3013
3014 summaryGroup += PvlKeyword("Sigma0",
3016 summaryGroup += PvlKeyword("Observations",
3018 summaryGroup += PvlKeyword("Constrained_Point_Parameters",
3020 summaryGroup += PvlKeyword("Constrained_Image_Parameters",
3022 if (m_bundleSettings->bundleTargetBody()) {
3023 summaryGroup += PvlKeyword("Constrained_Target_Parameters",
3025 }
3026 summaryGroup += PvlKeyword("Unknown_Parameters",
3028 summaryGroup += PvlKeyword("Degrees_of_Freedom",
3030 summaryGroup += PvlKeyword( "Rejected_Measures",
3032
3035 // if maximum likelihood estimation is being used
3036
3037 summaryGroup += PvlKeyword("Maximum_Likelihood_Tier: ",
3039 summaryGroup += PvlKeyword("Median_of_R^2_residuals: ",
3041 }
3042
3043 if ( m_bundleResults.converged() ) {
3044 summaryGroup += PvlKeyword("Converged", "TRUE");
3045 summaryGroup += PvlKeyword("TotalElapsedTime", toString( m_bundleResults.elapsedTime() ) );
3046
3047 if (m_bundleSettings->errorPropagation()) {
3048 summaryGroup += PvlKeyword("ErrorPropagationElapsedTime",
3050 }
3051 }
3052 else {
3053 summaryGroup += PvlKeyword("Elapsed_Time",
3055 }
3056
3057 std::ostringstream ostr;
3058 ostr << summaryGroup << std::endl;
3059 m_iterationSummary += QString::fromStdString( ostr.str() );
3060
3061 if (m_printSummary && iApp != NULL) {
3062 Application::Log(summaryGroup);
3063 }
3064 else {
3065 std::cout << summaryGroup << std::endl;
3066 }
3067 }
3068
3069
3078
3079
3086 return m_abort;
3087 }
3088
3089
3098 return m_iterationSummary;
3099 }
3100
3101
3112 if (iApp == NULL) { // in a function call
3113 printf("%s", status.toStdString().c_str());
3114 }
3115 else if (QCoreApplication::applicationName() != "ipce") {
3116 printf("%s", status.toStdString().c_str());
3117 }
3118 }
3119
3120
3121
3158
3159 // use qvectors so that we can set the size.
3160 // this will be useful later when adding data.
3161 // data may added out of index order
3162 int numberImages = m_serialNumberList->size();
3163 QVector<Statistics> rmsImageSampleResiduals(numberImages);
3164 QVector<Statistics> rmsImageLineResiduals(numberImages);
3165 QVector<Statistics> rmsImageResiduals(numberImages);
3166
3167 int numObjectPoints = m_bundleControlPoints.size();
3168 for (int i = 0; i < numObjectPoints; i++) {
3169
3170 const BundleControlPointQsp point = m_bundleControlPoints.at(i);
3171
3172 if (point->isRejected()) {
3173 continue;
3174 }
3175
3176 int numMeasures = point->numberOfMeasures();
3177 for (int j = 0; j < numMeasures; j++) {
3178
3179 const BundleMeasureQsp measure = point->at(j);
3180
3181 if (measure->isRejected()) {
3182 continue;
3183 }
3184
3185 double sampleResidual = fabs(measure->sampleResidual());
3186 double lineResidual = fabs(measure->lineResidual());
3187
3188 // Determine the index for this measure's serial number
3189 int imageIndex = m_serialNumberList->serialNumberIndex(measure->cubeSerialNumber());
3190
3191 // add residual data to the statistics object at the appropriate serial number index
3192 rmsImageSampleResiduals[imageIndex].AddData(sampleResidual);
3193 rmsImageLineResiduals[imageIndex].AddData(lineResidual);
3194 rmsImageResiduals[imageIndex].AddData(lineResidual);
3195 rmsImageResiduals[imageIndex].AddData(sampleResidual);
3196 }
3197 }
3198
3199 QVector<Statistics> rmsLidarImageSampleResiduals(numberImages);
3200 QVector<Statistics> rmsLidarImageLineResiduals(numberImages);
3201 QVector<Statistics> rmsLidarImageResiduals(numberImages);
3202
3203
3204 int numLidarPoints = m_bundleLidarControlPoints.size();
3205 for (int i = 0; i < numLidarPoints; i++) {
3206
3208
3209 if (point->isRejected()) {
3210 continue;
3211 }
3212
3213 int numMeasures = point->numberOfMeasures();
3214 for (int j = 0; j < numMeasures; j++) {
3215
3216 const BundleMeasureQsp measure = point->at(j);
3217
3218 if (measure->isRejected()) {
3219 continue;
3220 }
3221
3222 double sampleResidual = fabs(measure->sampleResidual());
3223 double lineResidual = fabs(measure->lineResidual());
3224
3225 // Determine the index for this measure's serial number
3226 int imageIndex = m_serialNumberList->serialNumberIndex(measure->cubeSerialNumber());
3227
3228 // add residual data to the statistics object at the appropriate serial number index
3229 rmsLidarImageSampleResiduals[imageIndex].AddData(sampleResidual);
3230 rmsLidarImageLineResiduals[imageIndex].AddData(lineResidual);
3231 rmsLidarImageResiduals[imageIndex].AddData(lineResidual);
3232 rmsLidarImageResiduals[imageIndex].AddData(sampleResidual);
3233 }
3234 }
3235
3236 if (m_bundleSettings->errorPropagation()) {
3237
3238 // initialize body-fixed coordinate boundaries
3239
3240 // Latitude or X
3241 Distance minSigmaCoord1Dist;
3242 QString minSigmaCoord1PointId = "";
3243
3244 Distance maxSigmaCoord1Dist;
3245 QString maxSigmaCoord1PointId = "";
3246
3247 // Longitude or Y
3248 Distance minSigmaCoord2Dist;
3249 QString minSigmaCoord2PointId = "";
3250
3251 Distance maxSigmaCoord2Dist;
3252 QString maxSigmaCoord2PointId = "";
3253
3254 // Radius or Z
3255 Distance minSigmaCoord3Dist;
3256 QString minSigmaCoord3PointId = "";
3257
3258 Distance maxSigmaCoord3Dist;
3259 QString maxSigmaCoord3PointId = "";
3260
3261 // compute stats for point sigmas
3262 Statistics sigmaCoord1Stats;
3263 Statistics sigmaCoord2Stats;
3264 Statistics sigmaCoord3Stats;
3265
3266 Distance sigmaCoord1Dist, sigmaCoord2Dist, sigmaCoord3Dist;
3267 SurfacePoint::CoordinateType type = m_bundleSettings->controlPointCoordTypeReports();
3268
3269 int numPoints = m_bundleControlPoints.size();
3270 // initialize max and min values to those from first valid point
3271 for (int i = 0; i < numPoints; i++) {
3272
3273 const BundleControlPointQsp point = m_bundleControlPoints.at(i);
3274
3275 maxSigmaCoord1Dist = point->adjustedSurfacePoint().GetSigmaDistance(type,
3276 SurfacePoint::One);
3277 minSigmaCoord1Dist = maxSigmaCoord1Dist;
3278
3279 maxSigmaCoord2Dist = point->adjustedSurfacePoint().GetSigmaDistance(type,
3280 SurfacePoint::Two);
3281 minSigmaCoord2Dist = maxSigmaCoord2Dist;
3282
3283 maxSigmaCoord1PointId = point->id();
3284 maxSigmaCoord2PointId = maxSigmaCoord1PointId;
3285 minSigmaCoord1PointId = maxSigmaCoord1PointId;
3286 minSigmaCoord2PointId = maxSigmaCoord1PointId;
3287
3288 // Get stats for coordinate 3 if used
3289 if (m_bundleSettings->solveRadius() || type == SurfacePoint::Rectangular) {
3290 maxSigmaCoord3Dist = point->adjustedSurfacePoint().GetSigmaDistance(type,
3291 SurfacePoint::Three);
3292 minSigmaCoord3Dist = maxSigmaCoord3Dist;
3293
3294 maxSigmaCoord3PointId = maxSigmaCoord1PointId;
3295 minSigmaCoord3PointId = maxSigmaCoord1PointId;
3296 }
3297 break;
3298 }
3299
3300 for (int i = 0; i < numPoints; i++) {
3301
3302 const BundleControlPointQsp point = m_bundleControlPoints.at(i);
3303
3304 sigmaCoord1Dist = point->adjustedSurfacePoint().GetSigmaDistance(type,
3305 SurfacePoint::One);
3306 sigmaCoord2Dist = point->adjustedSurfacePoint().GetSigmaDistance(type,
3307 SurfacePoint::Two);
3308 sigmaCoord3Dist = point->adjustedSurfacePoint().GetSigmaDistance(type,
3309 SurfacePoint::Three);
3310
3311 sigmaCoord1Stats.AddData(sigmaCoord1Dist.meters());
3312 sigmaCoord2Stats.AddData(sigmaCoord2Dist.meters());
3313 sigmaCoord3Stats.AddData(sigmaCoord3Dist.meters());
3314
3315 if (sigmaCoord1Dist > maxSigmaCoord1Dist) {
3316 maxSigmaCoord1Dist = sigmaCoord1Dist;
3317 maxSigmaCoord1PointId = point->id();
3318 }
3319 if (sigmaCoord2Dist > maxSigmaCoord2Dist) {
3320 maxSigmaCoord2Dist = sigmaCoord2Dist;
3321 maxSigmaCoord2PointId = point->id();
3322 }
3323 if (m_bundleSettings->solveRadius() || type == SurfacePoint::Rectangular) {
3324 if (sigmaCoord3Dist > maxSigmaCoord3Dist) {
3325 maxSigmaCoord3Dist = sigmaCoord3Dist;
3326 maxSigmaCoord3PointId = point->id();
3327 }
3328 }
3329 if (sigmaCoord1Dist < minSigmaCoord1Dist) {
3330 minSigmaCoord1Dist = sigmaCoord1Dist;
3331 minSigmaCoord1PointId = point->id();
3332 }
3333 if (sigmaCoord2Dist < minSigmaCoord2Dist) {
3334 minSigmaCoord2Dist = sigmaCoord2Dist;
3335 minSigmaCoord2PointId = point->id();
3336 }
3337 if (m_bundleSettings->solveRadius() || type == SurfacePoint::Rectangular) {
3338 if (sigmaCoord3Dist < minSigmaCoord3Dist) {
3339 minSigmaCoord3Dist = sigmaCoord3Dist;
3340 minSigmaCoord3PointId = point->id();
3341 }
3342 }
3343 }
3344
3345 // update bundle results
3347
3348 m_bundleResults.setSigmaCoord1Range(minSigmaCoord1Dist, maxSigmaCoord1Dist,
3349 minSigmaCoord1PointId, maxSigmaCoord1PointId);
3350
3351 m_bundleResults.setSigmaCoord2Range(minSigmaCoord2Dist, maxSigmaCoord2Dist,
3352 minSigmaCoord2PointId, maxSigmaCoord2PointId);
3353
3354 m_bundleResults.setSigmaCoord3Range(minSigmaCoord3Dist, maxSigmaCoord3Dist,
3355 minSigmaCoord3PointId, maxSigmaCoord3PointId);
3356
3357 m_bundleResults.setRmsFromSigmaStatistics(sigmaCoord1Stats.Rms(),
3358 sigmaCoord2Stats.Rms(),
3359 sigmaCoord3Stats.Rms());
3360 }
3361 m_bundleResults.setRmsImageResidualLists(rmsImageLineResiduals.toList(),
3362 rmsImageSampleResiduals.toList(),
3363 rmsImageResiduals.toList());
3364
3365 m_bundleResults.setRmsLidarImageResidualLists(rmsLidarImageLineResiduals.toList(),
3366 rmsLidarImageSampleResiduals.toList(),
3367 rmsLidarImageResiduals.toList());
3368 return true;
3369 }
3370
3371}
static void Log(PvlGroup &results)
Writes Pvl results to sessionlog and printfile.
QString modelState(int index)
Return the updated model state for the ith cube in the cube list given to the constructor.
bool m_cleanUp
!< If the iteration summaries should be output to the log file.
int m_rank
!< If the serial number lists need to be deleted by the destructor.
SparseBlockMatrix m_sparseNormals
!< The right hand side of the normal equations.
int numberOfImages() const
Returns the number of images.
void productAB(SparseBlockColumnMatrix &A, SparseBlockRowMatrix &B)
Perform the matrix multiplication C = N12 x Q.
bool solveCholesky()
Compute the least squares bundle adjustment solution using Cholesky decomposition.
bool computeBundleStatistics()
Compute Bundle statistics and store them in m_bundleResults.
double m_iterationTime
Time for last iteration.
int formLidarPointNormals(LinearAlgebra::MatrixUpperTriangular &N22, SparseBlockColumnMatrix &N12, LinearAlgebra::Vector &n2, LinearAlgebra::Vector &nj, BundleLidarControlPointQsp &point)
Compute the Q matrix and NIC vector for a control point.
bool formNormalEquations()
Form the least-squares normal equations matrix via cholmod.
SerialNumberList * serialNumberList()
Returns a pointer to the serial number list.
void init(Progress *progress=0)
Initialize all solution parameters.
bool computePartials(LinearAlgebra::Matrix &coeffTarget, LinearAlgebra::Matrix &coeffImage, LinearAlgebra::Matrix &coeffPoint3D, LinearAlgebra::Vector &coeffRHS, BundleMeasure &measure, BundleControlPoint &point)
Compute partial derivatives and weighted residuals for a measure.
bool isConverged()
Returns if the BundleAdjust converged.
QString fileName(int index)
Return the ith filename in the cube list file given to constructor.
void abortBundle()
Flag to abort when bundle is threaded.
QString m_lidarFileName
Input lidar point filename.
int m_previousNumberImagePartials
!< The image parameter solution vector.
double iteration() const
Returns what iteration the BundleAdjust is currently on.
void outputBundleStatus(QString status)
Slot for deltack and jigsaw to output the bundle status.
bool flagOutliers()
Flags outlier measures and control points.
QVector< BundleControlPointQsp > m_bundleControlPoints
Vector of control points.
LidarDataQsp lidarData()
Returns a pointer to the output lidar data file.
bool isAborted()
Returns if the BundleAdjust has been aborted.
BundleSettingsQsp m_bundleSettings
Contains the solve settings.
bool invert3x3(LinearAlgebra::MatrixUpperTriangular &m)
Dedicated quick inverse of 3x3 matrix.
int m_numLidarConstraints
TODO: temp.
Table cMatrix(int index)
Return the updated instrument pointing table for the ith cube in the cube list given to the construct...
double computeVtpv()
Computes vtpv, the weighted sum of squares of residuals.
QList< ImageList * > imageLists()
This method returns the image list used in the bundle adjust.
void applyParameterCorrections()
Apply parameter corrections for current iteration.
bool initializeCHOLMODLibraryVariables()
Initializations for CHOLMOD sparse matrix package.
boost::numeric::ublas::symmetric_matrix< double, boost::numeric::ublas::upper, boost::numeric::ublas::column_major > m_normalInverse
!< The lists of images used in the bundle.
LinearAlgebra::Vector m_imageSolution
!< The lower triangular L matrix from Cholesky decomposition.
cholmod_triplet * m_cholmodTriplet
!< The sparse block normal equations matrix.
ControlNetQsp m_controlNet
Output control net.
bool validateNetwork()
control network validation - on the very real chance that the net has not been checked before running...
SerialNumberList * m_serialNumberList
!< Vector of observations.
bool loadCholmodTriplet()
Load sparse normal equations matrix into CHOLMOD triplet.
LidarDataQsp m_lidarDataSet
Output lidar data.
cholmod_factor * m_L
!< The CHOLMOD sparse normal equations matrix used by cholmod_factorize to solve the system.
bool solveSystem()
Compute the solution to the normal equations using the CHOLMOD library.
BundleAdjust(BundleSettingsQsp bundleSettings, const QString &cnetFile, const QString &cubeList, bool printSummary=true)
Construct a BundleAdjust object from the given settings, control network file, and cube list.
void accumProductAlphaAB(double alpha, SparseBlockRowMatrix &A, LinearAlgebra::Vector &B, LinearAlgebra::Vector &C)
Performs the matrix multiplication nj = nj + alpha (Q x n2).
bool initializeNormalEquationsMatrix()
Initialize Normal Equations matrix (m_sparseNormals).
QString iterationSummaryGroup() const
Returns the iteration summary string.
LinearAlgebra::Vector m_RHS
!< Contains object parameters, statistics, and workspace used by the CHOLMOD library.
bool errorPropagation()
Error propagation for solution.
BundleResults m_bundleResults
Stores the results of the bundle adjust.
QString m_cnetFileName
The control net filename.
bool formMeasureNormals(LinearAlgebra::MatrixUpperTriangular &N22, SparseBlockColumnMatrix &N12, LinearAlgebra::VectorCompressed &n1, LinearAlgebra::Vector &n2, LinearAlgebra::Matrix &coeffTarget, LinearAlgebra::Matrix &coeffImage, LinearAlgebra::Matrix &coeffPoint3D, LinearAlgebra::Vector &coeffRHS, int observationIndex)
Form the auxilary normal equation matrices for a measure.
BundleSolutionInfo * bundleSolveInformation()
Create a BundleSolutionInfo containing the settings and results from the bundle adjustment.
int formPointNormals(LinearAlgebra::MatrixUpperTriangular &N22, SparseBlockColumnMatrix &N12, LinearAlgebra::Vector &n2, LinearAlgebra::Vector &nj, BundleControlPointQsp &point)
Compute the Q matrix and NIC vector for a control point.
bool m_abort
!< Contains information about the target body.
BundleLidarPointVector m_bundleLidarControlPoints
Vector of lidar points.
void computeResiduals()
Compute image measure residuals.
bool m_printSummary
!< Summary of the most recently completed iteration.
bool computeRejectionLimit()
Compute rejection limit.
bool productATransB(LinearAlgebra::MatrixUpperTriangular &N22, SparseBlockColumnMatrix &N12, SparseBlockRowMatrix &Q)
Perform the matrix multiplication Q = N22 x N12(transpose)
void iterationSummary()
Creates an iteration summary and an iteration group for the solution summary.
int m_iteration
The current iteration.
bool freeCHOLMODLibraryVariables()
Free CHOLMOD library variables.
bool formWeightedNormals(LinearAlgebra::VectorCompressed &n1, LinearAlgebra::Vector &nj)
Apply weighting for spacecraft position, velocity, acceleration and camera angles,...
BundleSolutionInfo * solveCholeskyBR()
Compute the least squares bundle adjustment solution using Cholesky decomposition.
ControlNetQsp controlNet()
Returns a pointer to the output control network.
cholmod_sparse * m_cholmodNormal
!< The CHOLMOD triplet representation of the sparse normal equations matrix.
~BundleAdjust()
Destroys BundleAdjust object, deallocates pointers (if we have ownership), and frees variables from c...
Table spVector(int index)
Return the updated instrument position table for the ith cube in the cube list given to the construct...
This class holds information about a control point that BundleAdjust needs to run correctly.
SurfacePoint adjustedSurfacePoint() const
Accesses the adjusted SurfacePoint associated with this BundleControlPoint.
QString id() const
Accesses the Point ID associated with this BundleControlPoint.
This class hold image information that BundleAdjust needs to run correctly.Definition for a BundleIma...
Definition BundleImage.h:36
This class holds information about a lidar control point that BundleAdjust requires.
double vtpvRangeContribution()
Compute vtpv of lidar range constraints.
A container class for a ControlMeasure.
BundleObservationQsp addNew(BundleImageQsp image, QString observationNumber, QString instrumentId, BundleSettingsQsp bundleSettings)
Adds a new BundleObservation to this vector or fetches an existing BundleObservation if this vector a...
int numberParameters()
Returns the sum of the position parameters and pointing parameters for the contained BundleObservatio...
BundleObservationQsp observationByCubeSerialNumber(QString cubeSerialNumber)
Accesses a BundleObservation associated with the passed serial number.
void addProbabilityDistributionObservation(double obsValue)
Adds an observation to the cumulative probability distribution of |R^2 residuals|.
void incrementNumberConstrainedImageParameters(int incrementAmount)
Increase the number of constrained image parameters.
double elapsedTimeErrorProp() const
Returns the elapsed time for error propagation.
int numberRejectedObservations() const
Returns the number of observation that were rejected.
double rejectionLimit() const
Returns the rejection limit.
int numberObservations() const
Returns the number of observations.
void setElapsedTime(double time)
Sets the elapsed time for the bundle adjustment.
MaximumLikelihoodWFunctions maximumLikelihoodModelWFunc(int modelIndex) const
Returns the maximum likelihood model at the given index.
void setBundleControlPoints(QVector< BundleControlPointQsp > controlPoints)
Sets the bundle control point vector.
void setNumberConstrainedPointParameters(int numberParameters)
Set number of contrained point parameters.
void setBundleLidarPoints(QVector< BundleLidarControlPointQsp > lidarPoints)
Sets the bundle lidar point vector.
double sigma0() const
Returns the Sigma0 of the bundle adjustment.
void setIterations(int iterations)
Sets the number of iterations taken by the BundleAdjust.
bool converged() const
Returns whether or not the bundle adjustment converged.
void setSigmaCoord1Range(Distance minCoord1Dist, Distance maxCoord1Dist, QString minCoord1PointId, QString maxCoord1PointId)
Sets the min and max sigma distances and point ids for coordinate 1.
void incrementMaximumLikelihoodModelIndex()
Increases the value that indicates which stage the maximum likelihood adjustment is currently on.
void setOutputControlNet(ControlNetQsp outNet)
Sets the output ControlNet.
void setSigmaCoord2Range(Distance minCoord2Dist, Distance maxCoord2Dist, QString minCoord2PointId, QString maxCoord2PointId)
Sets the min and max sigma distances and point ids for coordinate 2.
int numberConstrainedPointParameters() const
Returns the number of constrained point parameters.
void resetNumberConstrainedImageParameters()
Resets the number of constrained image parameters to 0.
void setNumberRejectedObservations(int numberObservations)
Sets the number of rejected observations.
void maximumLikelihoodSetUp(QList< QPair< MaximumLikelihoodWFunctions::Model, double > > modelsWithQuantiles)
This method steps up the maximum likelihood estimation solution.
int numberUnknownParameters() const
Returns the number of unknown parameters.
void addResidualsProbabilityDistributionObservation(double obsValue)
Adds an observation to the cumulative probability distribution of residuals used for reporting.
int numberMaximumLikelihoodModels() const
Returns how many maximum likelihood models were used in the bundle adjustment.
void setNumberLidarRangeConstraints(int numberLidarRangeConstraints)
Sets the total number of lidar range constraints.
int maximumLikelihoodModelIndex() const
Returns which step the bundle adjustment is on.
void setRejectionLimit(double rejectionLimit)
Sets the rejection limit.
void incrementNumberConstrainedTargetParameters(int incrementAmount)
Increases the number of constrained target parameters.
void setOutputLidarData(LidarDataQsp outLidarData)
Sets the output LidarData object.
void setNumberImageObservations(int numberObservations)
Sets the number of photogrammetric image observations.
void setRmsImageResidualLists(QList< Statistics > rmsImageLineResiduals, QList< Statistics > rmsImageSampleResiduals, QList< Statistics > rmsImageResiduals)
Sets the root mean square image residual Statistics lists.
double maximumLikelihoodMedianR2Residuals() const
Returns the median of the |R^2 residuals|.
void setNumberLidarImageObservations(int numberLidarObservations)
Sets the number of lidar observations.
int numberConstrainedTargetParameters() const
Return the number of constrained target parameters.
double elapsedTime() const
Returns the elapsed time for the bundle adjustment.
void setNumberUnknownParameters(int numberParameters)
Sets the total number of parameters to solve for.
void setSigmaCoord3Range(Distance minCoord3Dist, Distance maxCoord3Dist, QString minCoord3PointId, QString maxCoord3PointId)
Sets the min and max sigma distances and point ids for coordinate 3.
void setNumberConstrainedLidarPointParameters(int numberParameters)
Set number of contrained point parameters.
void setObservations(BundleObservationVector observations)
Sets the vector of BundleObservations.
void initializeResidualsProbabilityDistribution(unsigned int nodes=20)
Initializes or resets the cumulative probability distribution of residuals used for reporting.
void setRmsFromSigmaStatistics(double rmsFromSigmaCoord1Stats, double rmsFromSigmaCoord2Stats, double rmsFromSigmaCoord3Stats)
Sets the root mean square values of the adjusted sigmas for all three coordinates.
void setRmsXYResiduals(double rx, double ry, double rxy)
Sets the root mean square of the x and y residuals.
void printMaximumLikelihoodTierInformation()
Prints out information about which tier the solution is in and the status of the residuals.
void resizeSigmaStatisticsVectors(int numberImages)
Resizes all image sigma vectors.
void setCorrMatCovFileName(FileName name)
Set the covariance file name for the matrix used to calculate the correlation matrix.
void setRmsLidarImageResidualLists(QList< Statistics > rmsLidarImageLineResiduals, QList< Statistics > rmsLidarImageSampleResiduals, QList< Statistics > rmsLidarImageResiduals)
Sets the root mean square lidar image residual Statistics lists.
void computeSigma0(double dvtpv, BundleSettings::ConvergenceCriteria criteria)
Computes the sigma0 and stores it internally.
int numberConstrainedImageParameters() const
Returns the number of constrained image parameters.
void setElapsedTimeErrorProp(double time)
Sets the elapsed time for error propegation.
void setConverged(bool converged)
Sets if the bundle adjustment converged.
void resetNumberConstrainedTargetParameters()
Resets the number of constrained target parameters to 0.
int degreesOfFreedom() const
Returns the degrees of freedom.
@ Sigma0
The value of sigma0 will be used to determine that the bundle adjustment has converged.
Container class for BundleAdjustment results.
void setRunTime(QString runTime)
Sets the run time, and the name if a name is not already set.
QString getModelState() const
Get the CSM Model state string to re-create the CSM Model.
@ Framing
Framing Camera.
Definition Camera.h:359
@ Csm
Community Sensor Model Camera.
Definition Camera.h:365
virtual CameraType GetCameraType() const =0
Returns the type of camera that was created.
This represents an ISIS control net in a project-based GUI interface.
Definition Control.h:65
QString fileName() const
Access the name of the control network file associated with this Control.
Definition Control.cpp:252
a control network
Definition ControlNet.h:258
A single control point.
Status ComputeApriori()
Computes a priori lat/lon/radius point coordinates by determining the average lat/lon/radius of all m...
Distance measurement, usually in meters.
Definition Distance.h:34
File name manipulation and expansion.
Definition FileName.h:100
Isis exception class.
Definition IException.h:91
@ User
A type of error that could only have occurred due to a mistake on the user's part (e....
Definition IException.h:126
@ Programmer
This error is for when a programmer made an API call that was illegal.
Definition IException.h:146
This represents a cube in a project-based GUI interface.
Definition Image.h:105
void closeCube()
Cleans up the Cube pointer.
Definition Image.cpp:282
QString fileName() const
Get the file name of the cube that this image represents.
Definition Image.cpp:315
Internalizes a list of images and allows for operations on the entire list.
Definition ImageList.h:53
LidarData class.
Definition LidarData.h:49
boost::numeric::ublas::compressed_vector< double > VectorCompressed
Definition for an Isis::LinearAlgebra::VectorCompressed of doubles.
boost::numeric::ublas::vector< double > Vector
Definition for an Isis::LinearAlgebra::Vector of doubles.
boost::numeric::ublas::matrix< double > Matrix
Definition for an Isis::LinearAlgebra::Matrix of doubles.
boost::numeric::ublas::symmetric_matrix< double, boost::numeric::ublas::upper > MatrixUpperTriangular
Definition for an Isis::LinearAlgebra::MatrixUpperTriangular of doubles with an upper configuration.
double sqrtWeightScaler(double residualZScore)
This provides the scaler to the sqrt of the weight, which is very useful for building normal equation...
Program progress reporter.
Definition Progress.h:42
Contains multiple PvlContainers.
Definition PvlGroup.h:41
A single keyword-value pair.
Definition PvlKeyword.h:87
Serial Number list generator.
QString observationNumber(int index)
Return a observation number given an index.
QString serialNumber(const QString &filename)
Return a serial number given a filename.
void add(const QString &filename, bool def2filename=false)
Adds a new filename / serial number pair to the SerialNumberList.
QString spacecraftInstrumentId(int index)
Return the spacecraftname/instrumentid at the given index.
int size() const
How many serial number / filename combos are in the list.
int serialNumberIndex(const QString &sn)
Return a list index given a serial number.
QString fileName(const QString &sn)
Return a filename given a serial number.
SparseBlockColumnMatrix.
void zeroBlocks()
Sets all elements of all matrix blocks to zero.
int numberOfBlocks()
Returns total number of blocks in matrix.
void wipe()
Deletes all pointer elements and removes them from the list.
bool insertMatrixBlock(int nColumnBlock, int nRowBlock, int nRows, int nCols)
Inserts a "newed" boost LinearAlgebra::Matrix pointer of size (nRows, nCols) into the matrix at nColu...
LinearAlgebra::Matrix * getBlock(int column, int row)
Returns pointer to boost matrix at position (column, row).
bool setNumberOfColumns(int n)
Initializes number of columns (SparseBlockColumnMatrix).
int numberOfElements()
Returns number of matrix elements in matrix.
void zeroBlocks()
Sets all elements of all matrix blocks to zero.
This class is used to accumulate statistics on double arrays.
Definition Statistics.h:93
This class defines a body-fixed surface point.
CoordinateType
Defines the coordinate typ, units, and coordinate index for some of the output methods.
@ Latitudinal
Planetocentric latitudinal (lat/lon/rad) coordinates.
@ Rectangular
Body-fixed rectangular x/y/z coordinates.
void SetMatrix(CoordinateType type, const boost::numeric::ublas::symmetric_matrix< double, boost::numeric::ublas::upper > &covar)
Set the covariance matrix.
Class for storing Table blobs information.
Definition Table.h:61
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:520
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
QSharedPointer< LidarData > LidarDataQsp
Definition for a shared pointer to a LidarData object.
Definition LidarData.h:100
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition IString.cpp:211
static void cholmodErrorHandler(int nStatus, const char *file, int nLineNo, const char *message)
Custom error handler for CHOLMOD.
QSharedPointer< ControlNet > ControlNetQsp
Typedef for QSharedPointer to control network. This typedef is for future implementation of target bo...
Definition ControlNet.h:485