#include <gtsam/geometry/Point3.h>
#include <gtsam/geometry/Cal3Bundler.h>
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
#include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>
#include <gtsam/nonlinear/Values.h>
#include <gtsam/sfm/SfmData.h>
#include <gtsam/slam/GeneralSFMFactor.h>
#include <gtsam/inference/Symbol.h>
#include "../../common/include/ba_benchmark_utils.h"
#include "../include/gtsam_ba.h"
benchmark_utils::BenchmarkResult BenchmarkGTSAM(const std::string& dataset_path) {
using namespace benchmark_utils;
BenchmarkResult result;
result.dataset = "problem-1723-156502-pre";
result.solver = "GTSAM";
result.language = "C++";
SfmData sfm_data;
try {
sfm_data = SfmData::FromBalFile(dataset_path);
} catch (const std::exception& e) {
std::cerr << "Failed to load BAL file: " << e.what() << std::endl;
result.status = "LOAD_FAILED";
return result;
}
result.num_cameras = sfm_data.numberCameras();
result.num_points = sfm_data.numberTracks();
size_t total_obs = 0;
for (size_t j = 0; j < sfm_data.numberTracks(); ++j) {
total_obs += sfm_data.tracks[j].numberMeasurements();
}
result.num_observations = static_cast<int>(total_obs);
NonlinearFactorGraph graph;
Values initial;
for (size_t i = 0; i < sfm_data.numberCameras(); ++i) {
initial.insert(C(i), sfm_data.cameras[i]);
}
auto noise = noiseModel::Robust::Create(
noiseModel::mEstimator::Huber::Create(1.0),
noiseModel::Isotropic::Sigma(2, 1.0));
using SfmFactor = GeneralSFMFactor<SfmCamera, Point3>;
for (size_t j = 0; j < sfm_data.numberTracks(); ++j) {
const SfmTrack& track = sfm_data.tracks[j];
initial.insert(P(j), track.point3());
for (size_t k = 0; k < track.numberMeasurements(); ++k) {
const auto& measurement = track.measurements[k];
size_t camera_idx = measurement.first;
Point2 uv = measurement.second;
graph.emplace_shared<SfmFactor>(uv, noise, C(camera_idx), P(j));
}
}
auto prior_noise = noiseModel::Diagonal::Sigmas(
(Vector(9) << 1e-6, 1e-6, 1e-6, 1e-6, 1e-6, 1e-6, 1e-6, 1e-6, 1e-6).finished());
graph.addPrior(C(0), sfm_data.cameras[0], prior_noise);
auto point_prior_noise = noiseModel::Isotropic::Sigma(3, 1e-6);
graph.addPrior(P(0), sfm_data.tracks[0].point3(), point_prior_noise);
LevenbergMarquardtParams params;
params.setVerbosityLM("SILENT");
params.setMaxIterations(100);
params.setAbsoluteErrorTol(1e-6);
params.setRelativeErrorTol(1e-6);
params.setlambdaInitial(1.0);
params.setlambdaFactor(2.0); params.setlambdaUpperBound(1e10);
params.setlambdaLowerBound(1e-10);
params.setDiagonalDamping(true);
double initial_error = graph.error(initial);
result.initial_mse = initial_error / result.num_observations;
result.initial_rmse = std::sqrt(result.initial_mse);
Timer timer;
try {
LevenbergMarquardtOptimizer optimizer(graph, initial, params);
Values optimized = optimizer.optimize();
result.time_ms = timer.elapsed_ms();
result.iterations = optimizer.iterations();
double final_error = graph.error(optimized);
result.final_mse = final_error / result.num_observations;
result.final_rmse = std::sqrt(result.final_mse);
result.status = "CONVERGED";
} catch (const std::exception& e) {
std::cerr << "Optimization failed: " << e.what() << std::endl;
result.time_ms = timer.elapsed_ms();
result.status = "FAILED";
result.final_mse = result.initial_mse;
result.final_rmse = result.initial_rmse;
}
return result;
}
int main(int argc, char** argv) {
std::string dataset_path = "../../../data/bundle_adjustment/ladybug/problem-1723-156502-pre.txt";
if (argc > 1) {
dataset_path = argv[1];
}
std::vector<benchmark_utils::BenchmarkResult> results;
results.push_back(BenchmarkGTSAM(dataset_path));
std::string csv_path = "gtsam_ba_benchmark_results.csv";
if (!benchmark_utils::WriteResultsToCSV(csv_path, results)) {
std::cerr << "Failed to write CSV results" << std::endl;
return 1;
}
return 0;
}