opensfm/src/geometry/relative_pose.h (152 lines of code) (raw):
#pragma once
#include <ceres/rotation.h>
#include <ceres/tiny_solver.h>
#include <ceres/tiny_solver_autodiff_function.h>
#include <Eigen/Eigen>
#include <Eigen/SVD>
#include "triangulation.h"
template <class IT>
Eigen::Matrix<double, 3, 4> RelativePoseFromEssential(
const Eigen::Matrix3d& essential, IT begin, IT end) {
Eigen::JacobiSVD<Eigen::Matrix3d> SVD(
essential, Eigen::ComputeFullV | Eigen::ComputeFullU);
Eigen::Matrix3d U = SVD.matrixU();
Eigen::Matrix3d Vt = SVD.matrixV().transpose();
// Last column of U is undetermined since d = (a a 0).
if (U.determinant() < 0) {
U.col(2) *= -1;
}
// Last row of Vt is undetermined since d = (a a 0).
if (Vt.determinant() < 0) {
Vt.row(2) *= -1;
}
Eigen::Matrix3d W;
W << 0, -1, 0, 1, 0, 0, 0, 0, 1;
Eigen::Matrix<double, 2, 3> bearings;
Eigen::Matrix<double, 2, 3> centers;
centers.row(0) = Eigen::Vector3d::Zero();
auto best_decomposition = std::make_pair(0, Eigen::Matrix<double, 3, 4>());
for (int i = 0; i < 2; ++i) {
Eigen::Vector3d translation;
if (i == 0) {
translation = U.col(2);
} else {
translation = -U.col(2);
}
translation.normalize();
for (int j = 0; j < 2; ++j) {
Eigen::Matrix3d rotation;
if (j == 0) {
rotation = U * W * Vt;
} else {
rotation = U * W.transpose() * Vt;
}
// Since rotation=Rcamera and translation=Tcamera parametrization
centers.row(1) = -rotation.transpose() * translation;
double score = 0;
for (IT it = begin; it != end; ++it) {
bearings.row(0) = it->first;
bearings.row(1) = rotation.transpose() * it->second;
const auto point =
geometry::TriangulateTwoBearingsMidpointSolve(centers, bearings);
if (!point.first) {
continue;
}
const Eigen::Vector3d projected_x = point.second.normalized();
const Eigen::Vector3d projected_y =
(rotation * point.second + translation).normalized();
score +=
((projected_x.dot(it->first) + projected_y.dot(it->second)) * 0.5);
}
if (score > best_decomposition.first) {
Eigen::Matrix<double, 3, 4> RT;
RT.block<3, 3>(0, 0) = rotation;
RT.block<3, 1>(0, 3) = translation;
best_decomposition = std::make_pair(score, RT);
}
}
}
// Rcamera and Tcamera parametrization
return best_decomposition.second;
}
template <class IT>
struct RelativePoseCost {
RelativePoseCost(IT begin, IT end) : begin_(begin), end_(end) {
std::srand(42);
const int count = end_ - begin_;
for (int i = 0; i < MAX_ERRORS; ++i) {
const int index = (float(std::rand()) / RAND_MAX) * count;
picked_errors_.push_back(begin_ + index);
}
}
template <typename T>
bool operator()(const T* const parameters, T* residuals) const {
Eigen::Map<const Eigen::Matrix<T, 3, 1>> rotation(parameters);
Eigen::Map<const Eigen::Matrix<T, 3, 1>> translation(parameters + 3);
Eigen::Matrix<T, 3, 1> rotation_transpose = -rotation;
Eigen::Matrix<T, 2, 3> bearings;
Eigen::Matrix<T, 2, 3> centers;
centers.row(0) = Eigen::Matrix<T, 3, 1>::Zero();
centers.row(1) = translation;
Eigen::Matrix<T, 3, 1> some_tmp = Eigen::Matrix<T, 3, 1>::Zero();
residuals[0] = T(0.);
for (int i = 0; i < MAX_ERRORS; ++i) {
IT it = picked_errors_[i];
const Eigen::Matrix<T, 3, 1> x = it->first.template cast<T>();
const Eigen::Matrix<T, 3, 1> y = it->second.template cast<T>();
// Bearing : x stay at identity, y is brought to the world with R(t)
bearings.row(0) = x;
ceres::AngleAxisRotatePoint(rotation_transpose.data(), y.data(),
some_tmp.data());
bearings.row(1) = some_tmp;
// Triangulate
const auto point =
geometry::TriangulateTwoBearingsMidpointSolve(centers, bearings);
if (!point.first) {
residuals[i] = T(1.0);
continue;
}
// Point in x stays at identity, y is brought in second camera with
// R*(y-t)
const Eigen::Matrix<T, 3, 1> projected_x = point.second.normalized();
const Eigen::Matrix<T, 3, 1> y_centered = point.second - translation;
ceres::AngleAxisRotatePoint(rotation.data(), y_centered.data(),
some_tmp.data());
const Eigen::Matrix<T, 3, 1> projected_y = some_tmp.normalized();
residuals[i] = 1.0 - ((projected_x.dot(x) + projected_y.dot(y)) * T(0.5));
}
residuals[MAX_ERRORS] = 1.0 - translation.norm();
return true;
}
IT begin_;
IT end_;
static const int MAX_ERRORS = 100;
std::vector<IT> picked_errors_;
};
template <class IT>
Eigen::Matrix<double, 3, 4> RelativePoseRefinement(
const Eigen::Matrix<double, 3, 4>& relative_pose, IT begin, IT end,
int iterations) {
Eigen::Matrix<double, 6, 1> parameters;
parameters.setZero();
// Use Rcamera and Tworld parametrization
ceres::RotationMatrixToAngleAxis(relative_pose.block<3, 3>(0, 0).data(),
parameters.data());
parameters.segment<3>(3) =
-relative_pose.block<3, 3>(0, 0).transpose() * relative_pose.col(3);
using RelativePoseFunction = ceres::TinySolverAutoDiffFunction<
RelativePoseCost<IT>, RelativePoseCost<IT>::MAX_ERRORS + 1, 6>;
RelativePoseCost<IT> cost(begin, end);
RelativePoseFunction f(cost);
ceres::TinySolver<RelativePoseFunction> solver;
solver.options.max_num_iterations = iterations;
solver.Solve(f, ¶meters);
// Back with Tcamera parametrization
Eigen::Matrix<double, 3, 4> relative_pose_result;
ceres::AngleAxisToRotationMatrix(
parameters.data(), relative_pose_result.block<3, 3>(0, 0).data());
relative_pose_result.col(3) =
-relative_pose_result.block<3, 3>(0, 0) * parameters.tail<3>(3);
return relative_pose_result;
}
namespace geometry {
Eigen::Matrix<double, 3, 4> RelativePoseFromEssential(
const Eigen::Matrix3d& essential, const Eigen::Matrix<double, -1, 3>& x1,
const Eigen::Matrix<double, -1, 3>& x2);
Eigen::Matrix<double, 3, 4> RelativePoseRefinement(
const Eigen::Matrix<double, 3, 4>& relative_pose,
const Eigen::Matrix<double, -1, 3>& x1,
const Eigen::Matrix<double, -1, 3>& x2, int iterations);
Eigen::Matrix3d RelativeRotationNPoints(const Eigen::Matrix<double, -1, 3>& x1,
const Eigen::Matrix<double, -1, 3>& x2);
} // namespace geometry