Revert formatting for triangulation.h

release/4.3a0
Travis Driver 2023-06-06 09:48:05 -07:00
parent f6f91ce231
commit 8d134fd120
1 changed files with 161 additions and 142 deletions

View File

@ -20,38 +20,39 @@
#pragma once #pragma once
#include "gtsam/geometry/Point3.h"
#include <gtsam/geometry/Cal3Bundler.h> #include <gtsam/geometry/Cal3Bundler.h>
#include <gtsam/geometry/Cal3DS2.h>
#include <gtsam/geometry/Cal3Fisheye.h> #include <gtsam/geometry/Cal3Fisheye.h>
#include <gtsam/geometry/Cal3Unified.h> #include <gtsam/geometry/Cal3Unified.h>
#include <gtsam/geometry/Cal3_S2.h> #include <gtsam/geometry/Cal3_S2.h>
#include <gtsam/geometry/Cal3DS2.h>
#include <gtsam/geometry/CameraSet.h> #include <gtsam/geometry/CameraSet.h>
#include <gtsam/geometry/PinholeCamera.h> #include <gtsam/geometry/PinholeCamera.h>
#include <gtsam/geometry/Pose2.h>
#include <gtsam/geometry/SphericalCamera.h> #include <gtsam/geometry/SphericalCamera.h>
#include <gtsam/geometry/Pose2.h>
#include <gtsam/inference/Symbol.h> #include <gtsam/inference/Symbol.h>
#include <gtsam/nonlinear/NonlinearFactorGraph.h> #include <gtsam/nonlinear/NonlinearFactorGraph.h>
#include <gtsam/slam/TriangulationFactor.h> #include <gtsam/slam/TriangulationFactor.h>
#include "gtsam/geometry/Point3.h"
#include <optional> #include <optional>
namespace gtsam { namespace gtsam {
/// Exception thrown by triangulateDLT when SVD returns rank < 3 /// Exception thrown by triangulateDLT when SVD returns rank < 3
class GTSAM_EXPORT TriangulationUnderconstrainedException : public std::runtime_error { class GTSAM_EXPORT TriangulationUnderconstrainedException: public std::runtime_error {
public: public:
TriangulationUnderconstrainedException() TriangulationUnderconstrainedException() :
: std::runtime_error("Triangulation Underconstrained Exception.") {} std::runtime_error("Triangulation Underconstrained Exception.") {
}
}; };
/// Exception thrown by triangulateDLT when landmark is behind one or more of the cameras /// Exception thrown by triangulateDLT when landmark is behind one or more of the cameras
class GTSAM_EXPORT TriangulationCheiralityException : public std::runtime_error { class GTSAM_EXPORT TriangulationCheiralityException: public std::runtime_error {
public: public:
TriangulationCheiralityException() TriangulationCheiralityException() :
: std::runtime_error( std::runtime_error(
"Triangulation Cheirality Exception: The resulting landmark is behind one or more " "Triangulation Cheirality Exception: The resulting landmark is behind one or more cameras.") {
"cameras.") {} }
}; };
/** /**
@ -63,13 +64,11 @@ class GTSAM_EXPORT TriangulationCheiralityException : public std::runtime_error
*/ */
GTSAM_EXPORT Vector4 triangulateHomogeneousDLT( GTSAM_EXPORT Vector4 triangulateHomogeneousDLT(
const std::vector<Matrix34, Eigen::aligned_allocator<Matrix34>>& projection_matrices, const std::vector<Matrix34, Eigen::aligned_allocator<Matrix34>>& projection_matrices,
const Point2Vector& measurements, const Point2Vector& measurements, double rank_tol = 1e-9);
double rank_tol = 1e-9);
/** /**
* Same math as Hartley and Zisserman, 2nd Ed., page 312, but with unit-norm bearing vectors * Same math as Hartley and Zisserman, 2nd Ed., page 312, but with unit-norm bearing vectors
* (contrarily to pinhole projection, the z entry is not assumed to be 1 as in Hartley and * (contrarily to pinhole projection, the z entry is not assumed to be 1 as in Hartley and Zisserman)
* Zisserman)
* @param projection_matrices Projection matrices (K*P^-1) * @param projection_matrices Projection matrices (K*P^-1)
* @param measurements Unit3 bearing measurements * @param measurements Unit3 bearing measurements
* @param rank_tol SVD rank tolerance * @param rank_tol SVD rank tolerance
@ -77,8 +76,7 @@ GTSAM_EXPORT Vector4 triangulateHomogeneousDLT(
*/ */
GTSAM_EXPORT Vector4 triangulateHomogeneousDLT( GTSAM_EXPORT Vector4 triangulateHomogeneousDLT(
const std::vector<Matrix34, Eigen::aligned_allocator<Matrix34>>& projection_matrices, const std::vector<Matrix34, Eigen::aligned_allocator<Matrix34>>& projection_matrices,
const std::vector<Unit3>& measurements, const std::vector<Unit3>& measurements, double rank_tol = 1e-9);
double rank_tol = 1e-9);
/** /**
* DLT triangulation: See Hartley and Zisserman, 2nd Ed., page 312 * DLT triangulation: See Hartley and Zisserman, 2nd Ed., page 312
@ -87,18 +85,18 @@ GTSAM_EXPORT Vector4 triangulateHomogeneousDLT(
* @param rank_tol SVD rank tolerance * @param rank_tol SVD rank tolerance
* @return Triangulated Point3 * @return Triangulated Point3
*/ */
GTSAM_EXPORT Point3 GTSAM_EXPORT Point3 triangulateDLT(
triangulateDLT(const std::vector<Matrix34, Eigen::aligned_allocator<Matrix34>>& projection_matrices, const std::vector<Matrix34, Eigen::aligned_allocator<Matrix34>>& projection_matrices,
const Point2Vector& measurements, const Point2Vector& measurements,
double rank_tol = 1e-9); double rank_tol = 1e-9);
/** /**
* overload of previous function to work with Unit3 (projected to canonical camera) * overload of previous function to work with Unit3 (projected to canonical camera)
*/ */
GTSAM_EXPORT Point3 GTSAM_EXPORT Point3 triangulateDLT(
triangulateDLT(const std::vector<Matrix34, Eigen::aligned_allocator<Matrix34>>& projection_matrices, const std::vector<Matrix34, Eigen::aligned_allocator<Matrix34>>& projection_matrices,
const std::vector<Unit3>& measurements, const std::vector<Unit3>& measurements,
double rank_tol = 1e-9); double rank_tol = 1e-9);
/** /**
* @brief Triangulation using the LOST (Linear Optimal Sine Triangulation) * @brief Triangulation using the LOST (Linear Optimal Sine Triangulation)
@ -124,22 +122,20 @@ GTSAM_EXPORT Point3 triangulateLOST(const std::vector<Pose3>& poses,
* @param initialEstimate * @param initialEstimate
* @return graph and initial values * @return graph and initial values
*/ */
template <class CALIBRATION> template<class CALIBRATION>
std::pair<NonlinearFactorGraph, Values> triangulationGraph( std::pair<NonlinearFactorGraph, Values> triangulationGraph(
const std::vector<Pose3>& poses, const std::vector<Pose3>& poses, std::shared_ptr<CALIBRATION> sharedCal,
std::shared_ptr<CALIBRATION> sharedCal, const Point2Vector& measurements, Key landmarkKey,
const Point2Vector& measurements,
Key landmarkKey,
const Point3& initialEstimate, const Point3& initialEstimate,
const SharedNoiseModel& model = noiseModel::Unit::Create(2)) { const SharedNoiseModel& model = noiseModel::Unit::Create(2)) {
Values values; Values values;
values.insert(landmarkKey, initialEstimate); // Initial landmark value values.insert(landmarkKey, initialEstimate); // Initial landmark value
NonlinearFactorGraph graph; NonlinearFactorGraph graph;
for (size_t i = 0; i < measurements.size(); i++) { for (size_t i = 0; i < measurements.size(); i++) {
const Pose3& pose_i = poses[i]; const Pose3& pose_i = poses[i];
typedef PinholePose<CALIBRATION> Camera; typedef PinholePose<CALIBRATION> Camera;
Camera camera_i(pose_i, sharedCal); Camera camera_i(pose_i, sharedCal);
graph.emplace_shared<TriangulationFactor<Camera>> // graph.emplace_shared<TriangulationFactor<Camera> > //
(camera_i, measurements[i], model, landmarkKey); (camera_i, measurements[i], model, landmarkKey);
} }
return {graph, values}; return {graph, values};
@ -154,22 +150,21 @@ std::pair<NonlinearFactorGraph, Values> triangulationGraph(
* @param initialEstimate * @param initialEstimate
* @return graph and initial values * @return graph and initial values
*/ */
template <class CAMERA> template<class CAMERA>
std::pair<NonlinearFactorGraph, Values> triangulationGraph( std::pair<NonlinearFactorGraph, Values> triangulationGraph(
const CameraSet<CAMERA>& cameras, const CameraSet<CAMERA>& cameras,
const typename CAMERA::MeasurementVector& measurements, const typename CAMERA::MeasurementVector& measurements, Key landmarkKey,
Key landmarkKey,
const Point3& initialEstimate, const Point3& initialEstimate,
const SharedNoiseModel& model = nullptr) { const SharedNoiseModel& model = nullptr) {
Values values; Values values;
values.insert(landmarkKey, initialEstimate); // Initial landmark value values.insert(landmarkKey, initialEstimate); // Initial landmark value
NonlinearFactorGraph graph; NonlinearFactorGraph graph;
static SharedNoiseModel unit( static SharedNoiseModel unit(noiseModel::Unit::Create(
noiseModel::Unit::Create(traits<typename CAMERA::Measurement>::dimension)); traits<typename CAMERA::Measurement>::dimension));
for (size_t i = 0; i < measurements.size(); i++) { for (size_t i = 0; i < measurements.size(); i++) {
const CAMERA& camera_i = cameras[i]; const CAMERA& camera_i = cameras[i];
graph.emplace_shared<TriangulationFactor<CAMERA>> // graph.emplace_shared<TriangulationFactor<CAMERA> > //
(camera_i, measurements[i], model ? model : unit, landmarkKey); (camera_i, measurements[i], model? model : unit, landmarkKey);
} }
return {graph, values}; return {graph, values};
} }
@ -182,8 +177,7 @@ std::pair<NonlinearFactorGraph, Values> triangulationGraph(
* @return refined Point3 * @return refined Point3
*/ */
GTSAM_EXPORT Point3 optimize(const NonlinearFactorGraph& graph, GTSAM_EXPORT Point3 optimize(const NonlinearFactorGraph& graph,
const Values& values, const Values& values, Key landmarkKey);
Key landmarkKey);
/** /**
* Given an initial estimate , refine a point using measurements in several cameras * Given an initial estimate , refine a point using measurements in several cameras
@ -193,14 +187,14 @@ GTSAM_EXPORT Point3 optimize(const NonlinearFactorGraph& graph,
* @param initialEstimate * @param initialEstimate
* @return refined Point3 * @return refined Point3
*/ */
template <class CALIBRATION> template<class CALIBRATION>
Point3 triangulateNonlinear(const std::vector<Pose3>& poses, Point3 triangulateNonlinear(const std::vector<Pose3>& poses,
std::shared_ptr<CALIBRATION> sharedCal, std::shared_ptr<CALIBRATION> sharedCal,
const Point2Vector& measurements, const Point2Vector& measurements, const Point3& initialEstimate,
const Point3& initialEstimate, const SharedNoiseModel& model = nullptr) {
const SharedNoiseModel& model = nullptr) {
// Create a factor graph and initial values // Create a factor graph and initial values
const auto [graph, values] = triangulationGraph<CALIBRATION> // const auto [graph, values] = triangulationGraph<CALIBRATION> //
(poses, sharedCal, measurements, Symbol('p', 0), initialEstimate, model); (poses, sharedCal, measurements, Symbol('p', 0), initialEstimate, model);
return optimize(graph, values, Symbol('p', 0)); return optimize(graph, values, Symbol('p', 0));
@ -213,32 +207,33 @@ Point3 triangulateNonlinear(const std::vector<Pose3>& poses,
* @param initialEstimate * @param initialEstimate
* @return refined Point3 * @return refined Point3
*/ */
template <class CAMERA> template<class CAMERA>
Point3 triangulateNonlinear(const CameraSet<CAMERA>& cameras, Point3 triangulateNonlinear(
const typename CAMERA::MeasurementVector& measurements, const CameraSet<CAMERA>& cameras,
const Point3& initialEstimate, const typename CAMERA::MeasurementVector& measurements, const Point3& initialEstimate,
const SharedNoiseModel& model = nullptr) { const SharedNoiseModel& model = nullptr) {
// Create a factor graph and initial values // Create a factor graph and initial values
const auto [graph, values] = triangulationGraph<CAMERA> // const auto [graph, values] = triangulationGraph<CAMERA> //
(cameras, measurements, Symbol('p', 0), initialEstimate, model); (cameras, measurements, Symbol('p', 0), initialEstimate, model);
return optimize(graph, values, Symbol('p', 0)); return optimize(graph, values, Symbol('p', 0));
} }
template <class CAMERA> template<class CAMERA>
std::vector<Matrix34, Eigen::aligned_allocator<Matrix34>> projectionMatricesFromCameras( std::vector<Matrix34, Eigen::aligned_allocator<Matrix34>>
const CameraSet<CAMERA>& cameras) { projectionMatricesFromCameras(const CameraSet<CAMERA> &cameras) {
std::vector<Matrix34, Eigen::aligned_allocator<Matrix34>> projection_matrices; std::vector<Matrix34, Eigen::aligned_allocator<Matrix34>> projection_matrices;
for (const CAMERA& camera : cameras) { for (const CAMERA &camera: cameras) {
projection_matrices.push_back(camera.cameraProjectionMatrix()); projection_matrices.push_back(camera.cameraProjectionMatrix());
} }
return projection_matrices; return projection_matrices;
} }
// overload, assuming pinholePose // overload, assuming pinholePose
template <class CALIBRATION> template<class CALIBRATION>
std::vector<Matrix34, Eigen::aligned_allocator<Matrix34>> projectionMatricesFromPoses( std::vector<Matrix34, Eigen::aligned_allocator<Matrix34>> projectionMatricesFromPoses(
const std::vector<Pose3>& poses, std::shared_ptr<CALIBRATION> sharedCal) { const std::vector<Pose3> &poses, std::shared_ptr<CALIBRATION> sharedCal) {
std::vector<Matrix34, Eigen::aligned_allocator<Matrix34>> projection_matrices; std::vector<Matrix34, Eigen::aligned_allocator<Matrix34>> projection_matrices;
for (size_t i = 0; i < poses.size(); i++) { for (size_t i = 0; i < poses.size(); i++) {
PinholePose<CALIBRATION> camera(poses.at(i), sharedCal); PinholePose<CALIBRATION> camera(poses.at(i), sharedCal);
@ -263,9 +258,9 @@ Cal3_S2 createPinholeCalibration(const CALIBRATION& cal) {
/** Internal undistortMeasurement to be used by undistortMeasurement and /** Internal undistortMeasurement to be used by undistortMeasurement and
* undistortMeasurements */ * undistortMeasurements */
template <class CALIBRATION, class MEASUREMENT> template <class CALIBRATION, class MEASUREMENT>
MEASUREMENT undistortMeasurementInternal(const CALIBRATION& cal, MEASUREMENT undistortMeasurementInternal(
const MEASUREMENT& measurement, const CALIBRATION& cal, const MEASUREMENT& measurement,
std::optional<Cal3_S2> pinholeCal = {}) { std::optional<Cal3_S2> pinholeCal = {}) {
if (!pinholeCal) { if (!pinholeCal) {
pinholeCal = createPinholeCalibration(cal); pinholeCal = createPinholeCalibration(cal);
} }
@ -284,13 +279,13 @@ MEASUREMENT undistortMeasurementInternal(const CALIBRATION& cal,
* @return measurements with the effect of the distortion of sharedCal removed. * @return measurements with the effect of the distortion of sharedCal removed.
*/ */
template <class CALIBRATION> template <class CALIBRATION>
Point2Vector undistortMeasurements(const CALIBRATION& cal, const Point2Vector& measurements) { Point2Vector undistortMeasurements(const CALIBRATION& cal,
const Point2Vector& measurements) {
Cal3_S2 pinholeCalibration = createPinholeCalibration(cal); Cal3_S2 pinholeCalibration = createPinholeCalibration(cal);
Point2Vector undistortedMeasurements; Point2Vector undistortedMeasurements;
// Calibrate with cal and uncalibrate with pinhole version of cal so that // Calibrate with cal and uncalibrate with pinhole version of cal so that
// measurements are undistorted. // measurements are undistorted.
std::transform(measurements.begin(), std::transform(measurements.begin(), measurements.end(),
measurements.end(),
std::back_inserter(undistortedMeasurements), std::back_inserter(undistortedMeasurements),
[&cal, &pinholeCalibration](const Point2& measurement) { [&cal, &pinholeCalibration](const Point2& measurement) {
return undistortMeasurementInternal<CALIBRATION>( return undistortMeasurementInternal<CALIBRATION>(
@ -301,7 +296,8 @@ Point2Vector undistortMeasurements(const CALIBRATION& cal, const Point2Vector& m
/** Specialization for Cal3_S2 as it doesn't need to be undistorted. */ /** Specialization for Cal3_S2 as it doesn't need to be undistorted. */
template <> template <>
inline Point2Vector undistortMeasurements(const Cal3_S2& cal, const Point2Vector& measurements) { inline Point2Vector undistortMeasurements(const Cal3_S2& cal,
const Point2Vector& measurements) {
return measurements; return measurements;
} }
@ -318,15 +314,17 @@ inline Point2Vector undistortMeasurements(const Cal3_S2& cal, const Point2Vector
*/ */
template <class CAMERA> template <class CAMERA>
typename CAMERA::MeasurementVector undistortMeasurements( typename CAMERA::MeasurementVector undistortMeasurements(
const CameraSet<CAMERA>& cameras, const typename CAMERA::MeasurementVector& measurements) { const CameraSet<CAMERA>& cameras,
const typename CAMERA::MeasurementVector& measurements) {
const size_t nrMeasurements = measurements.size(); const size_t nrMeasurements = measurements.size();
assert(nrMeasurements == cameras.size()); assert(nrMeasurements == cameras.size());
typename CAMERA::MeasurementVector undistortedMeasurements(nrMeasurements); typename CAMERA::MeasurementVector undistortedMeasurements(nrMeasurements);
for (size_t ii = 0; ii < nrMeasurements; ++ii) { for (size_t ii = 0; ii < nrMeasurements; ++ii) {
// Calibrate with cal and uncalibrate with pinhole version of cal so that // Calibrate with cal and uncalibrate with pinhole version of cal so that
// measurements are undistorted. // measurements are undistorted.
undistortedMeasurements[ii] = undistortMeasurementInternal<typename CAMERA::CalibrationType>( undistortedMeasurements[ii] =
cameras[ii].calibration(), measurements[ii]); undistortMeasurementInternal<typename CAMERA::CalibrationType>(
cameras[ii].calibration(), measurements[ii]);
} }
return undistortedMeasurements; return undistortedMeasurements;
} }
@ -356,13 +354,12 @@ inline SphericalCamera::MeasurementVector undistortMeasurements(
* @return homogeneous measurements in image plane * @return homogeneous measurements in image plane
*/ */
template <class CALIBRATION> template <class CALIBRATION>
inline Point3Vector calibrateMeasurementsShared(const CALIBRATION& cal, inline Point3Vector calibrateMeasurementsShared(
const Point2Vector& measurements) { const CALIBRATION& cal, const Point2Vector& measurements) {
Point3Vector calibratedMeasurements; Point3Vector calibratedMeasurements;
// Calibrate with cal and uncalibrate with pinhole version of cal so that // Calibrate with cal and uncalibrate with pinhole version of cal so that
// measurements are undistorted. // measurements are undistorted.
std::transform(measurements.begin(), std::transform(measurements.begin(), measurements.end(),
measurements.end(),
std::back_inserter(calibratedMeasurements), std::back_inserter(calibratedMeasurements),
[&cal](const Point2& measurement) { [&cal](const Point2& measurement) {
Point3 p; Point3 p;
@ -381,21 +378,25 @@ inline Point3Vector calibrateMeasurementsShared(const CALIBRATION& cal,
* @return homogeneous measurements in image plane * @return homogeneous measurements in image plane
*/ */
template <class CAMERA> template <class CAMERA>
inline Point3Vector calibrateMeasurements(const CameraSet<CAMERA>& cameras, inline Point3Vector calibrateMeasurements(
const typename CAMERA::MeasurementVector& measurements) { const CameraSet<CAMERA>& cameras,
const typename CAMERA::MeasurementVector& measurements) {
const size_t nrMeasurements = measurements.size(); const size_t nrMeasurements = measurements.size();
assert(nrMeasurements == cameras.size()); assert(nrMeasurements == cameras.size());
Point3Vector calibratedMeasurements(nrMeasurements); Point3Vector calibratedMeasurements(nrMeasurements);
for (size_t ii = 0; ii < nrMeasurements; ++ii) { for (size_t ii = 0; ii < nrMeasurements; ++ii) {
calibratedMeasurements[ii] << cameras[ii].calibration().calibrate(measurements[ii]), 1.0; calibratedMeasurements[ii]
<< cameras[ii].calibration().calibrate(measurements[ii]),
1.0;
} }
return calibratedMeasurements; return calibratedMeasurements;
} }
/** Specialize for SphericalCamera to do nothing. */ /** Specialize for SphericalCamera to do nothing. */
template <class CAMERA = SphericalCamera> template <class CAMERA = SphericalCamera>
inline Point3Vector calibrateMeasurements(const CameraSet<SphericalCamera>& cameras, inline Point3Vector calibrateMeasurements(
const SphericalCamera::MeasurementVector& measurements) { const CameraSet<SphericalCamera>& cameras,
const SphericalCamera::MeasurementVector& measurements) {
Point3Vector calibratedMeasurements(measurements.size()); Point3Vector calibratedMeasurements(measurements.size());
for (size_t ii = 0; ii < measurements.size(); ++ii) { for (size_t ii = 0; ii < measurements.size(); ++ii) {
calibratedMeasurements[ii] << measurements[ii].point3(); calibratedMeasurements[ii] << measurements[ii].point3();
@ -420,8 +421,7 @@ template <class CALIBRATION>
Point3 triangulatePoint3(const std::vector<Pose3>& poses, Point3 triangulatePoint3(const std::vector<Pose3>& poses,
std::shared_ptr<CALIBRATION> sharedCal, std::shared_ptr<CALIBRATION> sharedCal,
const Point2Vector& measurements, const Point2Vector& measurements,
double rank_tol = 1e-9, double rank_tol = 1e-9, bool optimize = false,
bool optimize = false,
const SharedNoiseModel& model = nullptr, const SharedNoiseModel& model = nullptr,
const bool useLOST = false) { const bool useLOST = false) {
assert(poses.size() == measurements.size()); assert(poses.size() == measurements.size());
@ -433,21 +433,25 @@ Point3 triangulatePoint3(const std::vector<Pose3>& poses,
// Reduce input noise model to an isotropic noise model using the mean of // Reduce input noise model to an isotropic noise model using the mean of
// the diagonal. // the diagonal.
const double measurementSigma = model ? model->sigmas().mean() : 1e-4; const double measurementSigma = model ? model->sigmas().mean() : 1e-4;
SharedIsotropic measurementNoise = noiseModel::Isotropic::Sigma(2, measurementSigma); SharedIsotropic measurementNoise =
noiseModel::Isotropic::Sigma(2, measurementSigma);
// calibrate the measurements to obtain homogenous coordinates in image // calibrate the measurements to obtain homogenous coordinates in image
// plane. // plane.
auto calibratedMeasurements = auto calibratedMeasurements =
calibrateMeasurementsShared<CALIBRATION>(*sharedCal, measurements); calibrateMeasurementsShared<CALIBRATION>(*sharedCal, measurements);
point = triangulateLOST(poses, calibratedMeasurements, measurementNoise, rank_tol); point = triangulateLOST(poses, calibratedMeasurements, measurementNoise,
rank_tol);
} else { } else {
// construct projection matrices from poses & calibration // construct projection matrices from poses & calibration
auto projection_matrices = projectionMatricesFromPoses(poses, sharedCal); auto projection_matrices = projectionMatricesFromPoses(poses, sharedCal);
// Undistort the measurements, leaving only the pinhole elements in effect. // Undistort the measurements, leaving only the pinhole elements in effect.
auto undistortedMeasurements = undistortMeasurements<CALIBRATION>(*sharedCal, measurements); auto undistortedMeasurements =
undistortMeasurements<CALIBRATION>(*sharedCal, measurements);
point = triangulateDLT(projection_matrices, undistortedMeasurements, rank_tol); point =
triangulateDLT(projection_matrices, undistortedMeasurements, rank_tol);
} }
// Then refine using non-linear optimization // Then refine using non-linear optimization
@ -483,8 +487,7 @@ Point3 triangulatePoint3(const std::vector<Pose3>& poses,
template <class CAMERA> template <class CAMERA>
Point3 triangulatePoint3(const CameraSet<CAMERA>& cameras, Point3 triangulatePoint3(const CameraSet<CAMERA>& cameras,
const typename CAMERA::MeasurementVector& measurements, const typename CAMERA::MeasurementVector& measurements,
double rank_tol = 1e-9, double rank_tol = 1e-9, bool optimize = false,
bool optimize = false,
const SharedNoiseModel& model = nullptr, const SharedNoiseModel& model = nullptr,
const bool useLOST = false) { const bool useLOST = false) {
size_t m = cameras.size(); size_t m = cameras.size();
@ -498,7 +501,8 @@ Point3 triangulatePoint3(const CameraSet<CAMERA>& cameras,
// Reduce input noise model to an isotropic noise model using the mean of // Reduce input noise model to an isotropic noise model using the mean of
// the diagonal. // the diagonal.
const double measurementSigma = model ? model->sigmas().mean() : 1e-4; const double measurementSigma = model ? model->sigmas().mean() : 1e-4;
SharedIsotropic measurementNoise = noiseModel::Isotropic::Sigma(2, measurementSigma); SharedIsotropic measurementNoise =
noiseModel::Isotropic::Sigma(2, measurementSigma);
// construct poses from cameras. // construct poses from cameras.
std::vector<Pose3> poses; std::vector<Pose3> poses;
@ -507,17 +511,21 @@ Point3 triangulatePoint3(const CameraSet<CAMERA>& cameras,
// calibrate the measurements to obtain homogenous coordinates in image // calibrate the measurements to obtain homogenous coordinates in image
// plane. // plane.
auto calibratedMeasurements = calibrateMeasurements<CAMERA>(cameras, measurements); auto calibratedMeasurements =
calibrateMeasurements<CAMERA>(cameras, measurements);
point = triangulateLOST(poses, calibratedMeasurements, measurementNoise, rank_tol); point = triangulateLOST(poses, calibratedMeasurements, measurementNoise,
rank_tol);
} else { } else {
// construct projection matrices from poses & calibration // construct projection matrices from poses & calibration
auto projection_matrices = projectionMatricesFromCameras(cameras); auto projection_matrices = projectionMatricesFromCameras(cameras);
// Undistort the measurements, leaving only the pinhole elements in effect. // Undistort the measurements, leaving only the pinhole elements in effect.
auto undistortedMeasurements = undistortMeasurements<CAMERA>(cameras, measurements); auto undistortedMeasurements =
undistortMeasurements<CAMERA>(cameras, measurements);
point = triangulateDLT(projection_matrices, undistortedMeasurements, rank_tol); point =
triangulateDLT(projection_matrices, undistortedMeasurements, rank_tol);
} }
// Then refine using non-linear optimization // Then refine using non-linear optimization
@ -540,8 +548,7 @@ Point3 triangulatePoint3(const CameraSet<CAMERA>& cameras,
template <class CALIBRATION> template <class CALIBRATION>
Point3 triangulatePoint3(const CameraSet<PinholeCamera<CALIBRATION>>& cameras, Point3 triangulatePoint3(const CameraSet<PinholeCamera<CALIBRATION>>& cameras,
const Point2Vector& measurements, const Point2Vector& measurements,
double rank_tol = 1e-9, double rank_tol = 1e-9, bool optimize = false,
bool optimize = false,
const SharedNoiseModel& model = nullptr, const SharedNoiseModel& model = nullptr,
const bool useLOST = false) { const bool useLOST = false) {
return triangulatePoint3<PinholeCamera<CALIBRATION>> // return triangulatePoint3<PinholeCamera<CALIBRATION>> //
@ -549,16 +556,16 @@ Point3 triangulatePoint3(const CameraSet<PinholeCamera<CALIBRATION>>& cameras,
} }
struct GTSAM_EXPORT TriangulationParameters { struct GTSAM_EXPORT TriangulationParameters {
double rankTolerance; ///< threshold to decide whether triangulation is result.degenerate
///< (the rank is the number of singular values of the triangulation matrix which are larger than double rankTolerance; ///< threshold to decide whether triangulation is result.degenerate
///< rankTolerance) ///< (the rank is the number of singular values of the triangulation matrix which are larger than rankTolerance)
bool enableEPI; ///< if set to true, will refine triangulation using LM bool enableEPI; ///< if set to true, will refine triangulation using LM
/** /**
* if the landmark is triangulated at distance larger than this, * if the landmark is triangulated at distance larger than this,
* result is flagged as degenerate. * result is flagged as degenerate.
*/ */
double landmarkDistanceThreshold; // double landmarkDistanceThreshold; //
/** /**
* If this is nonnegative the we will check if the average reprojection error * If this is nonnegative the we will check if the average reprojection error
@ -567,7 +574,7 @@ struct GTSAM_EXPORT TriangulationParameters {
*/ */
double dynamicOutlierRejectionThreshold; double dynamicOutlierRejectionThreshold;
SharedNoiseModel noiseModel; ///< used in the nonlinear triangulation SharedNoiseModel noiseModel; ///< used in the nonlinear triangulation
/** /**
* Constructor * Constructor
@ -579,36 +586,39 @@ struct GTSAM_EXPORT TriangulationParameters {
* *
*/ */
TriangulationParameters(const double _rankTolerance = 1.0, TriangulationParameters(const double _rankTolerance = 1.0,
const bool _enableEPI = false, const bool _enableEPI = false, double _landmarkDistanceThreshold = -1,
double _landmarkDistanceThreshold = -1, double _dynamicOutlierRejectionThreshold = -1,
double _dynamicOutlierRejectionThreshold = -1, const SharedNoiseModel& _noiseModel = nullptr) :
const SharedNoiseModel& _noiseModel = nullptr) rankTolerance(_rankTolerance), enableEPI(_enableEPI), //
: rankTolerance(_rankTolerance), landmarkDistanceThreshold(_landmarkDistanceThreshold), //
enableEPI(_enableEPI), // dynamicOutlierRejectionThreshold(_dynamicOutlierRejectionThreshold),
landmarkDistanceThreshold(_landmarkDistanceThreshold), // noiseModel(_noiseModel){
dynamicOutlierRejectionThreshold(_dynamicOutlierRejectionThreshold), }
noiseModel(_noiseModel) {}
// stream to output // stream to output
friend std::ostream& operator<<(std::ostream& os, const TriangulationParameters& p) { friend std::ostream &operator<<(std::ostream &os,
const TriangulationParameters& p) {
os << "rankTolerance = " << p.rankTolerance << std::endl; os << "rankTolerance = " << p.rankTolerance << std::endl;
os << "enableEPI = " << p.enableEPI << std::endl; os << "enableEPI = " << p.enableEPI << std::endl;
os << "landmarkDistanceThreshold = " << p.landmarkDistanceThreshold << std::endl; os << "landmarkDistanceThreshold = " << p.landmarkDistanceThreshold
os << "dynamicOutlierRejectionThreshold = " << p.dynamicOutlierRejectionThreshold << std::endl; << std::endl;
os << "dynamicOutlierRejectionThreshold = "
<< p.dynamicOutlierRejectionThreshold << std::endl;
os << "noise model" << std::endl; os << "noise model" << std::endl;
return os; return os;
} }
private: private:
#ifdef GTSAM_ENABLE_BOOST_SERIALIZATION /// #ifdef GTSAM_ENABLE_BOOST_SERIALIZATION ///
/// Serialization function /// Serialization function
friend class boost::serialization::access; friend class boost::serialization::access;
template <class ARCHIVE> template<class ARCHIVE>
void serialize(ARCHIVE& ar, const unsigned int version) { void serialize(ARCHIVE & ar, const unsigned int version) {
ar& BOOST_SERIALIZATION_NVP(rankTolerance); ar & BOOST_SERIALIZATION_NVP(rankTolerance);
ar& BOOST_SERIALIZATION_NVP(enableEPI); ar & BOOST_SERIALIZATION_NVP(enableEPI);
ar& BOOST_SERIALIZATION_NVP(landmarkDistanceThreshold); ar & BOOST_SERIALIZATION_NVP(landmarkDistanceThreshold);
ar& BOOST_SERIALIZATION_NVP(dynamicOutlierRejectionThreshold); ar & BOOST_SERIALIZATION_NVP(dynamicOutlierRejectionThreshold);
} }
#endif #endif
}; };
@ -635,10 +645,16 @@ class TriangulationResult : public std::optional<Point3> {
* Constructor * Constructor
*/ */
TriangulationResult(const Point3& p) : status(VALID) { emplace(p); } TriangulationResult(const Point3& p) : status(VALID) { emplace(p); }
static TriangulationResult Degenerate() { return TriangulationResult(DEGENERATE); } static TriangulationResult Degenerate() {
return TriangulationResult(DEGENERATE);
}
static TriangulationResult Outlier() { return TriangulationResult(OUTLIER); } static TriangulationResult Outlier() { return TriangulationResult(OUTLIER); }
static TriangulationResult FarPoint() { return TriangulationResult(FAR_POINT); } static TriangulationResult FarPoint() {
static TriangulationResult BehindCamera() { return TriangulationResult(BEHIND_CAMERA); } return TriangulationResult(FAR_POINT);
}
static TriangulationResult BehindCamera() {
return TriangulationResult(BEHIND_CAMERA);
}
bool valid() const { return status == VALID; } bool valid() const { return status == VALID; }
bool degenerate() const { return status == DEGENERATE; } bool degenerate() const { return status == DEGENERATE; }
bool outlier() const { return status == OUTLIER; } bool outlier() const { return status == OUTLIER; }
@ -649,7 +665,8 @@ class TriangulationResult : public std::optional<Point3> {
return value(); return value();
} }
// stream to output // stream to output
friend std::ostream& operator<<(std::ostream& os, const TriangulationResult& result) { friend std::ostream& operator<<(std::ostream& os,
const TriangulationResult& result) {
if (result) if (result)
os << "point = " << *result << std::endl; os << "point = " << *result << std::endl;
else else
@ -669,10 +686,11 @@ class TriangulationResult : public std::optional<Point3> {
}; };
/// triangulateSafe: extensive checking of the outcome /// triangulateSafe: extensive checking of the outcome
template <class CAMERA> template<class CAMERA>
TriangulationResult triangulateSafe(const CameraSet<CAMERA>& cameras, TriangulationResult triangulateSafe(const CameraSet<CAMERA>& cameras,
const typename CAMERA::MeasurementVector& measured, const typename CAMERA::MeasurementVector& measured,
const TriangulationParameters& params) { const TriangulationParameters& params) {
size_t m = cameras.size(); size_t m = cameras.size();
// if we have a single pose the corresponding factor is uninformative // if we have a single pose the corresponding factor is uninformative
@ -681,22 +699,25 @@ TriangulationResult triangulateSafe(const CameraSet<CAMERA>& cameras,
else else
// We triangulate the 3D position of the landmark // We triangulate the 3D position of the landmark
try { try {
Point3 point = triangulatePoint3<CAMERA>( Point3 point =
cameras, measured, params.rankTolerance, params.enableEPI, params.noiseModel); triangulatePoint3<CAMERA>(cameras, measured, params.rankTolerance,
params.enableEPI, params.noiseModel);
// Check landmark distance and re-projection errors to avoid outliers // Check landmark distance and re-projection errors to avoid outliers
size_t i = 0; size_t i = 0;
double maxReprojError = 0.0; double maxReprojError = 0.0;
for (const CAMERA& camera : cameras) { for(const CAMERA& camera: cameras) {
const Pose3& pose = camera.pose(); const Pose3& pose = camera.pose();
if (params.landmarkDistanceThreshold > 0 && if (params.landmarkDistanceThreshold > 0
distance3(pose.translation(), point) > params.landmarkDistanceThreshold) && distance3(pose.translation(), point)
> params.landmarkDistanceThreshold)
return TriangulationResult::FarPoint(); return TriangulationResult::FarPoint();
#ifdef GTSAM_THROW_CHEIRALITY_EXCEPTION #ifdef GTSAM_THROW_CHEIRALITY_EXCEPTION
// verify that the triangulated point lies in front of all cameras // verify that the triangulated point lies in front of all cameras
// Only needed if this was not yet handled by exception // Only needed if this was not yet handled by exception
const Point3& p_local = pose.transformTo(point); const Point3& p_local = pose.transformTo(point);
if (p_local.z() <= 0) return TriangulationResult::BehindCamera(); if (p_local.z() <= 0)
return TriangulationResult::BehindCamera();
#endif #endif
// Check reprojection error // Check reprojection error
if (params.dynamicOutlierRejectionThreshold > 0) { if (params.dynamicOutlierRejectionThreshold > 0) {
@ -707,21 +728,19 @@ TriangulationResult triangulateSafe(const CameraSet<CAMERA>& cameras,
i += 1; i += 1;
} }
// Flag as degenerate if average reprojection error is too large // Flag as degenerate if average reprojection error is too large
if (params.dynamicOutlierRejectionThreshold > 0 && if (params.dynamicOutlierRejectionThreshold > 0
maxReprojError > params.dynamicOutlierRejectionThreshold) && maxReprojError > params.dynamicOutlierRejectionThreshold)
return TriangulationResult::Outlier(); return TriangulationResult::Outlier();
// all good! // all good!
return TriangulationResult(point); return TriangulationResult(point);
} catch (TriangulationUnderconstrainedException&) { } catch (TriangulationUnderconstrainedException&) {
// This exception is thrown if // This exception is thrown if
// 1) There is a single pose for triangulation - this should not happen because we checked the // 1) There is a single pose for triangulation - this should not happen because we checked the number of poses before
// number of poses before 2) The rank of the matrix used for triangulation is < 3: // 2) The rank of the matrix used for triangulation is < 3: rotation-only, parallel cameras (or motion towards the landmark)
// rotation-only, parallel cameras (or motion towards the landmark)
return TriangulationResult::Degenerate(); return TriangulationResult::Degenerate();
} catch (TriangulationCheiralityException&) { } catch (TriangulationCheiralityException&) {
// point is behind one of the cameras: can be the case of close-to-parallel cameras or may // point is behind one of the cameras: can be the case of close-to-parallel cameras or may depend on outliers
// depend on outliers
return TriangulationResult::BehindCamera(); return TriangulationResult::BehindCamera();
} }
} }
@ -733,4 +752,4 @@ using CameraSetCal3DS2 = CameraSet<PinholeCamera<Cal3DS2>>;
using CameraSetCal3Fisheye = CameraSet<PinholeCamera<Cal3Fisheye>>; using CameraSetCal3Fisheye = CameraSet<PinholeCamera<Cal3Fisheye>>;
using CameraSetCal3Unified = CameraSet<PinholeCamera<Cal3Unified>>; using CameraSetCal3Unified = CameraSet<PinholeCamera<Cal3Unified>>;
using CameraSetSpherical = CameraSet<SphericalCamera>; using CameraSetSpherical = CameraSet<SphericalCamera>;
} // namespace gtsam } // \namespace gtsam