gtsam  4.0.0
gtsam
triangulation.h
Go to the documentation of this file.
1 /* ----------------------------------------------------------------------------
2 
3  * GTSAM Copyright 2010, Georgia Tech Research Corporation,
4  * Atlanta, Georgia 30332-0415
5  * All Rights Reserved
6  * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
7 
8  * See LICENSE for the license information
9 
10  * -------------------------------------------------------------------------- */
11 
19 #pragma once
20 
22 #include <gtsam/geometry/Pose2.h>
23 #include <gtsam/slam/TriangulationFactor.h>
24 #include <gtsam/slam/PriorFactor.h>
26 #include <gtsam/inference/Symbol.h>
27 
28 namespace gtsam {
29 
31 class TriangulationUnderconstrainedException: public std::runtime_error {
32 public:
34  std::runtime_error("Triangulation Underconstrained Exception.") {
35  }
36 };
37 
39 class TriangulationCheiralityException: public std::runtime_error {
40 public:
42  std::runtime_error(
43  "Triangulation Cheirality Exception: The resulting landmark is behind one or more cameras.") {
44  }
45 };
46 
54 GTSAM_EXPORT Vector4 triangulateHomogeneousDLT(
55  const std::vector<Matrix34>& projection_matrices,
56  const std::vector<Point2>& measurements, double rank_tol = 1e-9);
57 
65 GTSAM_EXPORT Point3 triangulateDLT(
66  const std::vector<Matrix34>& projection_matrices,
67  const std::vector<Point2>& measurements, double rank_tol = 1e-9);
68 
78 template<class CALIBRATION>
79 std::pair<NonlinearFactorGraph, Values> triangulationGraph(
80  const std::vector<Pose3>& poses, boost::shared_ptr<CALIBRATION> sharedCal,
81  const std::vector<Point2>& measurements, Key landmarkKey,
82  const Point3& initialEstimate) {
83  Values values;
84  values.insert(landmarkKey, initialEstimate); // Initial landmark value
87  static SharedNoiseModel prior_model(noiseModel::Isotropic::Sigma(6, 1e-6));
88  for (size_t i = 0; i < measurements.size(); i++) {
89  const Pose3& pose_i = poses[i];
90  typedef PinholePose<CALIBRATION> Camera;
91  Camera camera_i(pose_i, sharedCal);
93  (camera_i, measurements[i], unit2, landmarkKey));
94  }
95  return std::make_pair(graph, values);
96 }
97 
107 template<class CAMERA>
108 std::pair<NonlinearFactorGraph, Values> triangulationGraph(
109  const std::vector<CAMERA>& cameras,
110  const std::vector<typename CAMERA::Measurement>& measurements, Key landmarkKey,
111  const Point3& initialEstimate) {
112  Values values;
113  values.insert(landmarkKey, initialEstimate); // Initial landmark value
114  NonlinearFactorGraph graph;
117  for (size_t i = 0; i < measurements.size(); i++) {
118  const CAMERA& camera_i = cameras[i];
120  (camera_i, measurements[i], unit, landmarkKey));
121  }
122  return std::make_pair(graph, values);
123 }
124 
126 template<class CALIBRATION>
127 std::pair<NonlinearFactorGraph, Values> triangulationGraph(
128  const std::vector<PinholeCamera<CALIBRATION> >& cameras,
129  const std::vector<Point2>& measurements, Key landmarkKey,
130  const Point3& initialEstimate) {
131  return triangulationGraph<PinholeCamera<CALIBRATION> > //
132  (cameras, measurements, landmarkKey, initialEstimate);
133 }
134 
142 GTSAM_EXPORT Point3 optimize(const NonlinearFactorGraph& graph,
143  const Values& values, Key landmarkKey);
144 
153 template<class CALIBRATION>
154 Point3 triangulateNonlinear(const std::vector<Pose3>& poses,
155  boost::shared_ptr<CALIBRATION> sharedCal,
156  const std::vector<Point2>& measurements, const Point3& initialEstimate) {
157 
158  // Create a factor graph and initial values
159  Values values;
160  NonlinearFactorGraph graph;
161  boost::tie(graph, values) = triangulationGraph<CALIBRATION> //
162  (poses, sharedCal, measurements, Symbol('p', 0), initialEstimate);
163 
164  return optimize(graph, values, Symbol('p', 0));
165 }
166 
174 template<class CAMERA>
176  const std::vector<CAMERA>& cameras,
177  const std::vector<typename CAMERA::Measurement>& measurements, const Point3& initialEstimate) {
178 
179  // Create a factor graph and initial values
180  Values values;
181  NonlinearFactorGraph graph;
182  boost::tie(graph, values) = triangulationGraph<CAMERA> //
183  (cameras, measurements, Symbol('p', 0), initialEstimate);
184 
185  return optimize(graph, values, Symbol('p', 0));
186 }
187 
189 template<class CALIBRATION>
191  const std::vector<PinholeCamera<CALIBRATION> >& cameras,
192  const std::vector<Point2>& measurements, const Point3& initialEstimate) {
193  return triangulateNonlinear<PinholeCamera<CALIBRATION> > //
194  (cameras, measurements, initialEstimate);
195 }
196 
204 template<class CALIBRATION>
206  CameraProjectionMatrix(const CALIBRATION& calibration) :
207  K_(calibration.K()) {
208  }
209  Matrix34 operator()(const Pose3& pose) const {
210  return K_ * (pose.inverse().matrix()).block<3, 4>(0, 0);
211  }
212 private:
213  const Matrix3 K_;
214 };
215 
228 template<class CALIBRATION>
229 Point3 triangulatePoint3(const std::vector<Pose3>& poses,
230  boost::shared_ptr<CALIBRATION> sharedCal,
231  const std::vector<Point2>& measurements, double rank_tol = 1e-9,
232  bool optimize = false) {
233 
234  assert(poses.size() == measurements.size());
235  if (poses.size() < 2)
237 
238  // construct projection matrices from poses & calibration
239  std::vector<Matrix34> projection_matrices;
240  CameraProjectionMatrix<CALIBRATION> createP(*sharedCal); // partially apply
241  for(const Pose3& pose: poses)
242  projection_matrices.push_back(createP(pose));
243 
244  // Triangulate linearly
245  Point3 point = triangulateDLT(projection_matrices, measurements, rank_tol);
246 
247  // Then refine using non-linear optimization
248  if (optimize)
249  point = triangulateNonlinear<CALIBRATION> //
250  (poses, sharedCal, measurements, point);
251 
252 #ifdef GTSAM_THROW_CHEIRALITY_EXCEPTION
253  // verify that the triangulated point lies in front of all cameras
254  for(const Pose3& pose: poses) {
255  const Point3& p_local = pose.transform_to(point);
256  if (p_local.z() <= 0)
258  }
259 #endif
260 
261  return point;
262 }
263 
276 template<class CAMERA>
278  const std::vector<CAMERA>& cameras,
279  const std::vector<Point2>& measurements, double rank_tol = 1e-9,
280  bool optimize = false) {
281 
282  size_t m = cameras.size();
283  assert(measurements.size() == m);
284 
285  if (m < 2)
287 
288  // construct projection matrices from poses & calibration
289  std::vector<Matrix34> projection_matrices;
290  for(const CAMERA& camera: cameras)
291  projection_matrices.push_back(
293  camera.pose()));
294  Point3 point = triangulateDLT(projection_matrices, measurements, rank_tol);
295 
296  // The n refine using non-linear optimization
297  if (optimize)
298  point = triangulateNonlinear<CAMERA>(cameras, measurements, point);
299 
300 #ifdef GTSAM_THROW_CHEIRALITY_EXCEPTION
301  // verify that the triangulated point lies in front of all cameras
302  for(const CAMERA& camera: cameras) {
303  const Point3& p_local = camera.pose().transform_to(point);
304  if (p_local.z() <= 0)
306  }
307 #endif
308 
309  return point;
310 }
311 
313 template<class CALIBRATION>
315  const std::vector<PinholeCamera<CALIBRATION> >& cameras,
316  const std::vector<Point2>& measurements, double rank_tol = 1e-9,
317  bool optimize = false) {
318  return triangulatePoint3<PinholeCamera<CALIBRATION> > //
319  (cameras, measurements, rank_tol, optimize);
320 }
321 
323 
324  double rankTolerance;
325  bool enableEPI;
326 
332 
339 
348  TriangulationParameters(const double _rankTolerance = 1.0,
349  const bool _enableEPI = false, double _landmarkDistanceThreshold = -1,
350  double _dynamicOutlierRejectionThreshold = -1) :
351  rankTolerance(_rankTolerance), enableEPI(_enableEPI), //
352  landmarkDistanceThreshold(_landmarkDistanceThreshold), //
353  dynamicOutlierRejectionThreshold(_dynamicOutlierRejectionThreshold) {
354  }
355 
356  // stream to output
357  friend std::ostream &operator<<(std::ostream &os,
358  const TriangulationParameters& p) {
359  os << "rankTolerance = " << p.rankTolerance << std::endl;
360  os << "enableEPI = " << p.enableEPI << std::endl;
361  os << "landmarkDistanceThreshold = " << p.landmarkDistanceThreshold
362  << std::endl;
363  os << "dynamicOutlierRejectionThreshold = "
364  << p.dynamicOutlierRejectionThreshold << std::endl;
365  return os;
366  }
367 
368 private:
369 
371  friend class boost::serialization::access;
372  template<class ARCHIVE>
373  void serialize(ARCHIVE & ar, const unsigned int version) {
374  ar & BOOST_SERIALIZATION_NVP(rankTolerance);
375  ar & BOOST_SERIALIZATION_NVP(enableEPI);
376  ar & BOOST_SERIALIZATION_NVP(landmarkDistanceThreshold);
377  ar & BOOST_SERIALIZATION_NVP(dynamicOutlierRejectionThreshold);
378  }
379 };
380 
384 class TriangulationResult: public boost::optional<Point3> {
385  enum Status {
386  VALID, DEGENERATE, BEHIND_CAMERA
387  };
388  Status status_;
389  TriangulationResult(Status s) :
390  status_(s) {
391  }
392 public:
393 
398 
403  status_(VALID) {
404  reset(p);
405  }
406  static TriangulationResult Degenerate() {
407  return TriangulationResult(DEGENERATE);
408  }
409  static TriangulationResult BehindCamera() {
410  return TriangulationResult(BEHIND_CAMERA);
411  }
412  bool degenerate() const {
413  return status_ == DEGENERATE;
414  }
415  bool behindCamera() const {
416  return status_ == BEHIND_CAMERA;
417  }
418  // stream to output
419  friend std::ostream &operator<<(std::ostream &os,
420  const TriangulationResult& result) {
421  if (result)
422  os << "point = " << *result << std::endl;
423  else
424  os << "no point, status = " << result.status_ << std::endl;
425  return os;
426  }
427 
428 private:
429 
431  friend class boost::serialization::access;
432  template<class ARCHIVE>
433  void serialize(ARCHIVE & ar, const unsigned int version) {
434  ar & BOOST_SERIALIZATION_NVP(status_);
435  }
436 };
437 
439 template<class CAMERA>
440 TriangulationResult triangulateSafe(const std::vector<CAMERA>& cameras,
441  const std::vector<Point2>& measured,
442  const TriangulationParameters& params) {
443 
444  size_t m = cameras.size();
445 
446  // if we have a single pose the corresponding factor is uninformative
447  if (m < 2)
448  return TriangulationResult::Degenerate();
449  else
450  // We triangulate the 3D position of the landmark
451  try {
452  Point3 point = triangulatePoint3<CAMERA>(cameras, measured,
453  params.rankTolerance, params.enableEPI);
454 
455  // Check landmark distance and re-projection errors to avoid outliers
456  size_t i = 0;
457  double totalReprojError = 0.0;
458  for(const CAMERA& camera: cameras) {
459  const Pose3& pose = camera.pose();
460  if (params.landmarkDistanceThreshold > 0
461  && distance3(pose.translation(), point)
462  > params.landmarkDistanceThreshold)
463  return TriangulationResult::Degenerate();
464 #ifdef GTSAM_THROW_CHEIRALITY_EXCEPTION
465  // verify that the triangulated point lies in front of all cameras
466  // Only needed if this was not yet handled by exception
467  const Point3& p_local = pose.transform_to(point);
468  if (p_local.z() <= 0)
469  return TriangulationResult::BehindCamera();
470 #endif
471  // Check reprojection error
472  if (params.dynamicOutlierRejectionThreshold > 0) {
473  const Point2& zi = measured.at(i);
474  Point2 reprojectionError(camera.project(point) - zi);
475  totalReprojError += reprojectionError.norm();
476  }
477  i += 1;
478  }
479  // Flag as degenerate if average reprojection error is too large
480  if (params.dynamicOutlierRejectionThreshold > 0
481  && totalReprojError / m > params.dynamicOutlierRejectionThreshold)
482  return TriangulationResult::Degenerate();
483 
484  // all good!
485  return TriangulationResult(point);
487  // This exception is thrown if
488  // 1) There is a single pose for triangulation - this should not happen because we checked the number of poses before
489  // 2) The rank of the matrix used for triangulation is < 3: rotation-only, parallel cameras (or motion towards the landmark)
490  return TriangulationResult::Degenerate();
492  // point is behind one of the cameras: can be the case of close-to-parallel cameras or may depend on outliers
493  return TriangulationResult::BehindCamera();
494  }
495 }
496 
497 } // \namespace gtsam
498 
Exception thrown by triangulateDLT when landmark is behind one or more of the cameras.
Definition: triangulation.h:39
Point3 triangulateDLT(const std::vector< Matrix34 > &projection_matrices, const std::vector< Point2 > &measurements, double rank_tol)
DLT triangulation: See Hartley and Zisserman, 2nd Ed., page 312.
Definition: triangulation.cpp:56
double dynamicOutlierRejectionThreshold
If this is nonnegative the we will check if the average reprojection error is smaller than this thres...
Definition: triangulation.h:338
TriangulationResult is an optional point, along with the reasons why it is invalid.
Definition: triangulation.h:384
void insert(Key j, const Value &val)
Add a variable with the given j, throws KeyAlreadyExists<J> if j is already present.
Definition: Values.cpp:133
std::pair< NonlinearFactorGraph, Values > triangulationGraph(const std::vector< Pose3 > &poses, boost::shared_ptr< CALIBRATION > sharedCal, const std::vector< Point2 > &measurements, Key landmarkKey, const Point3 &initialEstimate)
Create a factor graph with projection factors from poses and one calibration.
Definition: triangulation.h:79
Point3 transform_to(const Point3 &p, OptionalJacobian< 3, 6 > Dpose=boost::none, OptionalJacobian< 3, 3 > Dpoint=boost::none) const
takes point in world coordinates and transforms it to Pose coordinates
Definition: Pose3.cpp:319
2D Pose
double rankTolerance
threshold to decide whether triangulation is result.degenerate
Definition: triangulation.h:324
Character and index key used in VectorValues, GaussianFactorGraph, GaussianFactor, etc.
Definition: Symbol.h:34
Point3 triangulateNonlinear(const std::vector< Pose3 > &poses, boost::shared_ptr< CALIBRATION > sharedCal, const std::vector< Point2 > &measurements, const Point3 &initialEstimate)
Given an initial estimate , refine a point using measurements in several cameras. ...
Definition: triangulation.h:154
TriangulationResult(const Point3 &p)
Constructor.
Definition: triangulation.h:402
noiseModel::Base::shared_ptr SharedNoiseModel
Note, deliberately not in noiseModel namespace.
Definition: NoiseModel.h:1072
Vector4 triangulateHomogeneousDLT(const std::vector< Matrix34 > &projection_matrices, const std::vector< Point2 > &measurements, double rank_tol)
DLT triangulation: See Hartley and Zisserman, 2nd Ed., page 312.
Definition: triangulation.cpp:26
Pose3 inverse() const
inverse transformation with derivatives
Definition: Pose3.cpp:47
Definition: PinholePose.h:225
static shared_ptr Sigma(size_t dim, double sigma, bool smart=true)
An isotropic noise model created by specifying a standard devation sigma.
Definition: NoiseModel.cpp:561
Definition: Pose3.h:37
Definition: Point3.h:45
A non-templated config holding any types of Manifold-group elements.
Definition: Values.h:70
bool enableEPI
if set to true, will refine triangulation using LM
Definition: triangulation.h:325
Definition: triangulation.h:322
Point3 triangulatePoint3(const std::vector< Pose3 > &poses, boost::shared_ptr< CALIBRATION > sharedCal, const std::vector< Point2 > &measurements, double rank_tol=1e-9, bool optimize=false)
Function to triangulate 3D landmark point from an arbitrary number of poses (at least 2) using the DL...
Definition: triangulation.h:229
TriangulationResult()
Default constructor, only for serialization.
Definition: triangulation.h:397
double landmarkDistanceThreshold
if the landmark is triangulated at distance larger than this, result is flagged as degenerate...
Definition: triangulation.h:331
TriangulationResult triangulateSafe(const std::vector< CAMERA > &cameras, const std::vector< Point2 > &measured, const TriangulationParameters &params)
triangulateSafe: extensive checking of the outcome
Definition: triangulation.h:440
Definition: Point2.h:40
double distance3(const Point3 &p1, const Point3 &q, OptionalJacobian< 1, 3 > H1, OptionalJacobian< 1, 3 > H2)
distance between two points
Definition: Point3.cpp:83
Base class for all pinhole cameras.
TriangulationParameters(const double _rankTolerance=1.0, const bool _enableEPI=false, double _landmarkDistanceThreshold=-1, double _dynamicOutlierRejectionThreshold=-1)
Constructor.
Definition: triangulation.h:348
A manifold defines a space in which there is a notion of a linear tangent space that can be centered ...
Definition: concepts.h:30
Definition: PinholeCamera.h:33
Point3 optimize(const NonlinearFactorGraph &graph, const Values &values, Key landmarkKey)
Optimize for triangulation.
Definition: triangulation.cpp:73
double z() const
get z
Definition: Point3.h:114
Exception thrown by triangulateDLT when SVD returns rank < 3.
Definition: triangulation.h:31
A non-linear factor graph is a graph of non-Gaussian, i.e.
Definition: NonlinearFactorGraph.h:77
boost::enable_if< boost::is_base_of< FactorType, DERIVEDFACTOR > >::type push_back(boost::shared_ptr< DERIVEDFACTOR > factor)
Add a factor directly using a shared_ptr.
Definition: FactorGraph.h:155
Create a 3*4 camera projection matrix from calibration and pose.
Definition: triangulation.h:205
Factor Graph Constsiting of non-linear factors.
static shared_ptr Create(size_t dim)
Create a unit covariance noise model.
Definition: NoiseModel.h:601
Definition: TriangulationFactor.h:31
Matrix4 matrix() const
convert to 4*4 matrix
Definition: Pose3.cpp:280
const Point3 & translation(OptionalJacobian< 3, 6 > H=boost::none) const
get translation
Definition: Pose3.cpp:265
double norm(OptionalJacobian< 1, 2 > H=boost::none) const
norm of point, with derivative
Definition: Point2.cpp:65
std::uint64_t Key
Integer nonlinear key type.
Definition: types.h:57
Global functions in a separate testing namespace.
Definition: chartTesting.h:28