138 lines
4.4 KiB
C++
138 lines
4.4 KiB
C++
/* ----------------------------------------------------------------------------
|
|
|
|
* 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 <gtsam/geometry/Cal3f.h>
|
|
#include <gtsam/geometry/EssentialMatrix.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/inference/Symbol.h>
|
|
#include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>
|
|
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
|
|
#include <gtsam/nonlinear/Values.h>
|
|
#include <gtsam/sfm/TransferFactor.h> // Contains EssentialTransferFactorK
|
|
|
|
#include <vector>
|
|
|
|
#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<Point3> points = createPoints();
|
|
|
|
// Create the set of 4 ground-truth poses
|
|
vector<Pose3> 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<std::array<Point2, 8>, 4> p;
|
|
for (size_t i = 0; i < 4; ++i) {
|
|
PinholeCamera<Cal3f> 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 = EssentialTransferFactorK<Cal3f>;
|
|
|
|
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<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]);
|
|
}
|
|
|
|
// Add transfer factors between views a, b, and c.
|
|
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);
|
|
}
|
|
|
|
// 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;
|
|
} |