/* ---------------------------------------------------------------------------- * 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 EssentialViewGraphExample.cpp * @brief View-graph calibration with essential matrices. * @author Frank Dellaert * @date October 2024 */ #include #include #include #include #include #include #include #include #include #include #include #include // Contains EssentialTransferFactor #include #include "SFMdata.h" // For createPoints() and posesOnCircle() using namespace std; using namespace gtsam; using namespace symbol_shorthand; // For K(symbol) // Main function int main(int argc, char* argv[]) { // Define the camera calibration parameters Cal3f cal(50.0, 50.0, 50.0); // Create the set of 8 ground-truth landmarks vector points = createPoints(); // Create the set of 4 ground-truth poses vector poses = posesOnCircle(4, 30); // Calculate ground truth essential matrices, 1 and 2 poses apart auto E1 = EssentialMatrix::FromPose3(poses[0].between(poses[1])); auto E2 = EssentialMatrix::FromPose3(poses[0].between(poses[2])); // Simulate measurements from each camera pose std::array, 4> p; for (size_t i = 0; i < 4; ++i) { PinholeCamera camera(poses[i], cal); for (size_t j = 0; j < 8; ++j) { p[i][j] = camera.project(points[j]); } } // Create the factor graph NonlinearFactorGraph graph; using Factor = EssentialTransferFactor; 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 // Vectors to collect tuples for each factor std::vector> 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]); } // Add transfer factors between views a, b, and c. graph.emplace_shared(EdgeKey(a, c), EdgeKey(b, c), tuples1); graph.emplace_shared(EdgeKey(a, b), EdgeKey(b, c), tuples2); graph.emplace_shared(EdgeKey(a, c), EdgeKey(a, b), tuples3); } // Formatter for printing keys auto formatter = [](Key key) { if (Symbol(key).chr() == 'k') { return (string)Symbol(key); } else { EdgeKey edge(key); return (std::string)edge; } }; graph.print("Factor Graph:\n", formatter); // Create a delta vector to perturb the ground truth (small perturbation) Vector5 delta; delta << 1, 1, 1, 1, 1; delta *= 1e-2; // Create the initial estimate for essential matrices 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 initialEstimate.insert(EdgeKey(a, b), E1.retract(delta)); initialEstimate.insert(EdgeKey(a, c), E2.retract(delta)); } // Insert initial calibrations (using K symbol) for (size_t i = 0; i < 4; ++i) { initialEstimate.insert(K(i), cal); } initialEstimate.print("Initial Estimates:\n", formatter); graph.printErrors(initialEstimate, "Initial Errors:\n", formatter); // 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(); cout << "Initial error = " << graph.error(initialEstimate) << endl; cout << "Final error = " << graph.error(result) << endl; result.print("Final Results:\n", formatter); E1.print("Ground Truth E1:\n"); E2.print("Ground Truth E2:\n"); return 0; }