| 
									
										
										
										
											2024-10-25 07:40:53 +08:00
										 |  |  | /* ----------------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  * GTSAM Copyright 2010, Georgia Tech Research Corporation, | 
					
						
							|  |  |  |  * Atlanta, Georgia 30332-0415 | 
					
						
							|  |  |  |  * All Rights Reserved | 
					
						
							|  |  |  |  * Authors: Frank Dellaert, et al. (see THANKS for the full author list) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  * See LICENSE for the license information | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  * -------------------------------------------------------------------------- */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * @file    ViewGraphExample.cpp | 
					
						
							|  |  |  |  * @brief   View-graph calibration on a simulated dataset, a la Sweeney 2015 | 
					
						
							|  |  |  |  * @author  Frank Dellaert | 
					
						
							| 
									
										
										
										
											2024-10-25 10:17:23 +08:00
										 |  |  |  * @date    October 2024 | 
					
						
							| 
									
										
										
										
											2024-10-25 07:40:53 +08:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <gtsam/geometry/Cal3_S2.h>
 | 
					
						
							|  |  |  | #include <gtsam/geometry/PinholeCamera.h>
 | 
					
						
							|  |  |  | #include <gtsam/geometry/Point2.h>
 | 
					
						
							|  |  |  | #include <gtsam/geometry/Point3.h>
 | 
					
						
							|  |  |  | #include <gtsam/geometry/Pose3.h>
 | 
					
						
							|  |  |  | #include <gtsam/inference/EdgeKey.h>
 | 
					
						
							|  |  |  | #include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>
 | 
					
						
							|  |  |  | #include <gtsam/nonlinear/NonlinearFactorGraph.h>
 | 
					
						
							|  |  |  | #include <gtsam/nonlinear/Values.h>
 | 
					
						
							|  |  |  | #include <gtsam/sfm/TransferFactor.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <vector>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "SFMdata.h"
 | 
					
						
							|  |  |  | #include "gtsam/inference/Key.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | using namespace std; | 
					
						
							|  |  |  | using namespace gtsam; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | int main(int argc, char* argv[]) { | 
					
						
							|  |  |  |   // Define the camera calibration parameters
 | 
					
						
							| 
									
										
										
										
											2024-10-29 02:59:29 +08:00
										 |  |  |   Cal3_S2 cal(50.0, 50.0, 0.0, 50.0, 50.0); | 
					
						
							| 
									
										
										
										
											2024-10-25 07:40:53 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Create the set of 8 ground-truth landmarks
 | 
					
						
							|  |  |  |   vector<Point3> points = createPoints(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Create the set of 4 ground-truth poses
 | 
					
						
							|  |  |  |   vector<Pose3> poses = posesOnCircle(4, 30); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-25 08:04:24 +08:00
										 |  |  |   // Calculate ground truth fundamental matrices, 1 and 2 poses apart
 | 
					
						
							| 
									
										
										
										
											2024-10-29 02:59:29 +08:00
										 |  |  |   auto F1 = FundamentalMatrix(cal.K(), poses[0].between(poses[1]), cal.K()); | 
					
						
							|  |  |  |   auto F2 = FundamentalMatrix(cal.K(), poses[0].between(poses[2]), cal.K()); | 
					
						
							| 
									
										
										
										
											2024-10-25 08:04:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-25 07:40:53 +08:00
										 |  |  |   // Simulate measurements from each camera pose
 | 
					
						
							|  |  |  |   std::array<std::array<Point2, 8>, 4> p; | 
					
						
							|  |  |  |   for (size_t i = 0; i < 4; ++i) { | 
					
						
							| 
									
										
										
										
											2024-10-29 02:59:29 +08:00
										 |  |  |     PinholeCamera<Cal3_S2> camera(poses[i], cal); | 
					
						
							| 
									
										
										
										
											2024-10-25 07:40:53 +08:00
										 |  |  |     for (size_t j = 0; j < 8; ++j) { | 
					
						
							|  |  |  |       p[i][j] = camera.project(points[j]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-25 08:04:24 +08:00
										 |  |  |   // This section of the code is inspired by the work of Sweeney et al.
 | 
					
						
							|  |  |  |   // [link](sites.cs.ucsb.edu/~holl/pubs/Sweeney-2015-ICCV.pdf) on view-graph
 | 
					
						
							|  |  |  |   // calibration. The graph is made up of transfer factors that enforce the
 | 
					
						
							|  |  |  |   // epipolar constraint between corresponding points across three views, as
 | 
					
						
							|  |  |  |   // described in the paper. Rather than adding one ternary error term per point
 | 
					
						
							|  |  |  |   // in a triplet, we add three binary factors for sparsity during optimization.
 | 
					
						
							|  |  |  |   // In this version, we only include triplets between 3 successive cameras.
 | 
					
						
							| 
									
										
										
										
											2024-10-25 07:40:53 +08:00
										 |  |  |   NonlinearFactorGraph graph; | 
					
						
							| 
									
										
										
										
											2024-10-25 08:36:53 +08:00
										 |  |  |   using Factor = TransferFactor<FundamentalMatrix>; | 
					
						
							| 
									
										
										
										
											2024-10-25 10:17:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-25 07:40:53 +08:00
										 |  |  |   for (size_t a = 0; a < 4; ++a) { | 
					
						
							|  |  |  |     size_t b = (a + 1) % 4;  // Next camera
 | 
					
						
							|  |  |  |     size_t c = (a + 2) % 4;  // Camera after next
 | 
					
						
							| 
									
										
										
										
											2024-10-25 10:17:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Vectors to collect tuples for each factor
 | 
					
						
							|  |  |  |     std::vector<std::tuple<Point2, Point2, Point2>> tuples1, tuples2, tuples3; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Collect data for the three factors
 | 
					
						
							|  |  |  |     for (size_t j = 0; j < 8; ++j) { | 
					
						
							|  |  |  |       tuples1.emplace_back(p[a][j], p[b][j], p[c][j]); | 
					
						
							|  |  |  |       tuples2.emplace_back(p[a][j], p[c][j], p[b][j]); | 
					
						
							|  |  |  |       tuples3.emplace_back(p[c][j], p[b][j], p[a][j]); | 
					
						
							| 
									
										
										
										
											2024-10-25 07:40:53 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-10-25 10:17:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Add transfer factors between views a, b, and c. Note that the EdgeKeys
 | 
					
						
							|  |  |  |     // are crucial in performing the transfer in the right direction. We use
 | 
					
						
							|  |  |  |     // exactly 8 unique EdgeKeys, corresponding to 8 unknown fundamental
 | 
					
						
							|  |  |  |     // matrices we will optimize for.
 | 
					
						
							|  |  |  |     graph.emplace_shared<Factor>(EdgeKey(a, c), EdgeKey(b, c), tuples1); | 
					
						
							|  |  |  |     graph.emplace_shared<Factor>(EdgeKey(a, b), EdgeKey(b, c), tuples2); | 
					
						
							|  |  |  |     graph.emplace_shared<Factor>(EdgeKey(a, c), EdgeKey(a, b), tuples3); | 
					
						
							| 
									
										
										
										
											2024-10-25 07:40:53 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   auto formatter = [](Key key) { | 
					
						
							|  |  |  |     EdgeKey edge(key); | 
					
						
							|  |  |  |     return (std::string)edge; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   graph.print("Factor Graph:\n", formatter); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-25 08:04:24 +08:00
										 |  |  |   // Create a delta vector to perturb the ground truth
 | 
					
						
							|  |  |  |   // We can't really go far before convergence becomes problematic :-(
 | 
					
						
							|  |  |  |   Vector7 delta; | 
					
						
							|  |  |  |   delta << 1, 2, 3, 4, 5, 6, 7; | 
					
						
							| 
									
										
										
										
											2024-10-25 10:17:23 +08:00
										 |  |  |   delta *= 1e-5; | 
					
						
							| 
									
										
										
										
											2024-10-25 08:04:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-25 07:40:53 +08:00
										 |  |  |   // Create the data structure to hold the initial estimate to the solution
 | 
					
						
							|  |  |  |   Values initialEstimate; | 
					
						
							|  |  |  |   for (size_t a = 0; a < 4; ++a) { | 
					
						
							|  |  |  |     size_t b = (a + 1) % 4;  // Next camera
 | 
					
						
							|  |  |  |     size_t c = (a + 2) % 4;  // Camera after next
 | 
					
						
							| 
									
										
										
										
											2024-10-25 08:04:24 +08:00
										 |  |  |     initialEstimate.insert(EdgeKey(a, b), F1.retract(delta)); | 
					
						
							|  |  |  |     initialEstimate.insert(EdgeKey(a, c), F2.retract(delta)); | 
					
						
							| 
									
										
										
										
											2024-10-25 07:40:53 +08:00
										 |  |  |   } | 
					
						
							|  |  |  |   initialEstimate.print("Initial Estimates:\n", formatter); | 
					
						
							| 
									
										
										
										
											2024-10-27 14:53:20 +08:00
										 |  |  |   graph.printErrors(initialEstimate, "errors: ", formatter); | 
					
						
							| 
									
										
										
										
											2024-10-25 07:40:53 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /* Optimize the graph and print results */ | 
					
						
							|  |  |  |   LevenbergMarquardtParams params; | 
					
						
							|  |  |  |   params.setlambdaInitial(1000.0);  // Initialize lambda to a high value
 | 
					
						
							|  |  |  |   params.setVerbosityLM("SUMMARY"); | 
					
						
							|  |  |  |   Values result = | 
					
						
							|  |  |  |       LevenbergMarquardtOptimizer(graph, initialEstimate, params).optimize(); | 
					
						
							| 
									
										
										
										
											2024-10-25 08:04:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-25 07:40:53 +08:00
										 |  |  |   cout << "initial error = " << graph.error(initialEstimate) << endl; | 
					
						
							|  |  |  |   cout << "final error = " << graph.error(result) << endl; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-25 08:04:24 +08:00
										 |  |  |   result.print("Final results:\n", formatter); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   cout << "Ground Truth F1:\n" << F1.matrix() << endl; | 
					
						
							|  |  |  |   cout << "Ground Truth F2:\n" << F2.matrix() << endl; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-25 07:40:53 +08:00
										 |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | /* ************************************************************************* */ |