testTriangulation.cpp
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 
28 
29 
30 #include <boost/assign.hpp>
31 #include <boost/assign/std/vector.hpp>
32 
33 using namespace std;
34 using namespace gtsam;
35 using namespace boost::assign;
36 
37 // Some common constants
38 
39 static const boost::shared_ptr<Cal3_S2> sharedCal = //
40  boost::make_shared<Cal3_S2>(1500, 1200, 0, 640, 480);
41 
42 // Looking along X-axis, 1 meter above ground plane (x-y)
43 static const Rot3 upright = Rot3::Ypr(-M_PI / 2, 0., -M_PI / 2);
44 static const Pose3 pose1 = Pose3(upright, gtsam::Point3(0, 0, 1));
46 
47 // create second camera 1 meter to the right of first camera
48 static const Pose3 pose2 = pose1 * Pose3(Rot3(), Point3(1, 0, 0));
50 
51 // landmark ~5 meters infront of camera
52 static const Point3 landmark(5, 0.5, 1.2);
53 
54 // 1. Project two landmarks into two cameras and triangulate
57 
58 //******************************************************************************
59 // Simple test with a well-behaved two camera situation
60 TEST( triangulation, twoPoses) {
61 
62  vector<Pose3> poses;
63  Point2Vector measurements;
64 
65  poses += pose1, pose2;
66  measurements += z1, z2;
67 
68  double rank_tol = 1e-9;
69 
70  // 1. Test simple DLT, perfect in no noise situation
71  bool optimize = false;
72  boost::optional<Point3> actual1 = //
73  triangulatePoint3(poses, sharedCal, measurements, rank_tol, optimize);
74  EXPECT(assert_equal(landmark, *actual1, 1e-7));
75 
76  // 2. test with optimization on, same answer
77  optimize = true;
78  boost::optional<Point3> actual2 = //
79  triangulatePoint3(poses, sharedCal, measurements, rank_tol, optimize);
80  EXPECT(assert_equal(landmark, *actual2, 1e-7));
81 
82  // 3. Add some noise and try again: result should be ~ (4.995, 0.499167, 1.19814)
83  measurements.at(0) += Point2(0.1, 0.5);
84  measurements.at(1) += Point2(-0.2, 0.3);
85  optimize = false;
86  boost::optional<Point3> actual3 = //
87  triangulatePoint3(poses, sharedCal, measurements, rank_tol, optimize);
88  EXPECT(assert_equal(Point3(4.995, 0.499167, 1.19814), *actual3, 1e-4));
89 
90  // 4. Now with optimization on
91  optimize = true;
92  boost::optional<Point3> actual4 = //
93  triangulatePoint3(poses, sharedCal, measurements, rank_tol, optimize);
94  EXPECT(assert_equal(Point3(4.995, 0.499167, 1.19814), *actual4, 1e-4));
95 }
96 
97 //******************************************************************************
98 // Similar, but now with Bundler calibration
99 TEST( triangulation, twoPosesBundler) {
100 
101  boost::shared_ptr<Cal3Bundler> bundlerCal = //
102  boost::make_shared<Cal3Bundler>(1500, 0, 0, 640, 480);
103  PinholeCamera<Cal3Bundler> camera1(pose1, *bundlerCal);
104  PinholeCamera<Cal3Bundler> camera2(pose2, *bundlerCal);
105 
106  // 1. Project two landmarks into two cameras and triangulate
107  Point2 z1 = camera1.project(landmark);
108  Point2 z2 = camera2.project(landmark);
109 
110  vector<Pose3> poses;
111  Point2Vector measurements;
112 
113  poses += pose1, pose2;
114  measurements += z1, z2;
115 
116  bool optimize = true;
117  double rank_tol = 1e-9;
118 
119  boost::optional<Point3> actual = //
120  triangulatePoint3(poses, bundlerCal, measurements, rank_tol, optimize);
121  EXPECT(assert_equal(landmark, *actual, 1e-7));
122 
123  // Add some noise and try again
124  measurements.at(0) += Point2(0.1, 0.5);
125  measurements.at(1) += Point2(-0.2, 0.3);
126 
127  boost::optional<Point3> actual2 = //
128  triangulatePoint3(poses, bundlerCal, measurements, rank_tol, optimize);
129  EXPECT(assert_equal(Point3(4.995, 0.499167, 1.19847), *actual2, 1e-4));
130 }
131 
132 //******************************************************************************
133 TEST( triangulation, fourPoses) {
134  vector<Pose3> poses;
135  Point2Vector measurements;
136 
137  poses += pose1, pose2;
138  measurements += z1, z2;
139 
140  boost::optional<Point3> actual = triangulatePoint3(poses, sharedCal,
141  measurements);
142  EXPECT(assert_equal(landmark, *actual, 1e-2));
143 
144  // 2. Add some noise and try again: result should be ~ (4.995, 0.499167, 1.19814)
145  measurements.at(0) += Point2(0.1, 0.5);
146  measurements.at(1) += Point2(-0.2, 0.3);
147 
148  boost::optional<Point3> actual2 = //
149  triangulatePoint3(poses, sharedCal, measurements);
150  EXPECT(assert_equal(landmark, *actual2, 1e-2));
151 
152  // 3. Add a slightly rotated third camera above, again with measurement noise
153  Pose3 pose3 = pose1 * Pose3(Rot3::Ypr(0.1, 0.2, 0.1), Point3(0.1, -2, -.1));
155  Point2 z3 = camera3.project(landmark);
156 
157  poses += pose3;
158  measurements += z3 + Point2(0.1, -0.1);
159 
160  boost::optional<Point3> triangulated_3cameras = //
161  triangulatePoint3(poses, sharedCal, measurements);
162  EXPECT(assert_equal(landmark, *triangulated_3cameras, 1e-2));
163 
164  // Again with nonlinear optimization
165  boost::optional<Point3> triangulated_3cameras_opt = triangulatePoint3(poses,
166  sharedCal, measurements, 1e-9, true);
167  EXPECT(assert_equal(landmark, *triangulated_3cameras_opt, 1e-2));
168 
169  // 4. Test failure: Add a 4th camera facing the wrong way
170  Pose3 pose4 = Pose3(Rot3::Ypr(M_PI / 2, 0., -M_PI / 2), Point3(0, 0, 1));
171  PinholeCamera<Cal3_S2> camera4(pose4, *sharedCal);
172 
173 #ifdef GTSAM_THROW_CHEIRALITY_EXCEPTION
175 
176  poses += pose4;
177  measurements += Point2(400, 400);
178 
179  CHECK_EXCEPTION(triangulatePoint3(poses, sharedCal, measurements),
181 #endif
182 }
183 
184 //******************************************************************************
185 TEST( triangulation, fourPoses_distinct_Ks) {
186  Cal3_S2 K1(1500, 1200, 0, 640, 480);
187  // create first camera. Looking along X-axis, 1 meter above ground plane (x-y)
188  PinholeCamera<Cal3_S2> camera1(pose1, K1);
189 
190  // create second camera 1 meter to the right of first camera
191  Cal3_S2 K2(1600, 1300, 0, 650, 440);
192  PinholeCamera<Cal3_S2> camera2(pose2, K2);
193 
194  // 1. Project two landmarks into two cameras and triangulate
195  Point2 z1 = camera1.project(landmark);
196  Point2 z2 = camera2.project(landmark);
197 
199  Point2Vector measurements;
200 
201  cameras += camera1, camera2;
202  measurements += z1, z2;
203 
204  boost::optional<Point3> actual = //
205  triangulatePoint3(cameras, measurements);
206  EXPECT(assert_equal(landmark, *actual, 1e-2));
207 
208  // 2. Add some noise and try again: result should be ~ (4.995, 0.499167, 1.19814)
209  measurements.at(0) += Point2(0.1, 0.5);
210  measurements.at(1) += Point2(-0.2, 0.3);
211 
212  boost::optional<Point3> actual2 = //
213  triangulatePoint3(cameras, measurements);
214  EXPECT(assert_equal(landmark, *actual2, 1e-2));
215 
216  // 3. Add a slightly rotated third camera above, again with measurement noise
217  Pose3 pose3 = pose1 * Pose3(Rot3::Ypr(0.1, 0.2, 0.1), Point3(0.1, -2, -.1));
218  Cal3_S2 K3(700, 500, 0, 640, 480);
219  PinholeCamera<Cal3_S2> camera3(pose3, K3);
220  Point2 z3 = camera3.project(landmark);
221 
222  cameras += camera3;
223  measurements += z3 + Point2(0.1, -0.1);
224 
225  boost::optional<Point3> triangulated_3cameras = //
226  triangulatePoint3(cameras, measurements);
227  EXPECT(assert_equal(landmark, *triangulated_3cameras, 1e-2));
228 
229  // Again with nonlinear optimization
230  boost::optional<Point3> triangulated_3cameras_opt = triangulatePoint3(cameras,
231  measurements, 1e-9, true);
232  EXPECT(assert_equal(landmark, *triangulated_3cameras_opt, 1e-2));
233 
234  // 4. Test failure: Add a 4th camera facing the wrong way
235  Pose3 pose4 = Pose3(Rot3::Ypr(M_PI / 2, 0., -M_PI / 2), Point3(0, 0, 1));
236  Cal3_S2 K4(700, 500, 0, 640, 480);
237  PinholeCamera<Cal3_S2> camera4(pose4, K4);
238 
239 #ifdef GTSAM_THROW_CHEIRALITY_EXCEPTION
241 
242  cameras += camera4;
243  measurements += Point2(400, 400);
244  CHECK_EXCEPTION(triangulatePoint3(cameras, measurements),
246 #endif
247 }
248 
249 //******************************************************************************
250 TEST( triangulation, outliersAndFarLandmarks) {
251  Cal3_S2 K1(1500, 1200, 0, 640, 480);
252  // create first camera. Looking along X-axis, 1 meter above ground plane (x-y)
253  PinholeCamera<Cal3_S2> camera1(pose1, K1);
254 
255  // create second camera 1 meter to the right of first camera
256  Cal3_S2 K2(1600, 1300, 0, 650, 440);
257  PinholeCamera<Cal3_S2> camera2(pose2, K2);
258 
259  // 1. Project two landmarks into two cameras and triangulate
260  Point2 z1 = camera1.project(landmark);
261  Point2 z2 = camera2.project(landmark);
262 
264  Point2Vector measurements;
265 
266  cameras += camera1, camera2;
267  measurements += z1, z2;
268 
269  double landmarkDistanceThreshold = 10; // landmark is closer than that
270  TriangulationParameters params(1.0, false, landmarkDistanceThreshold); // all default except landmarkDistanceThreshold
271  TriangulationResult actual = triangulateSafe(cameras,measurements,params);
272  EXPECT(assert_equal(landmark, *actual, 1e-2));
273  EXPECT(actual.valid());
274 
275  landmarkDistanceThreshold = 4; // landmark is farther than that
276  TriangulationParameters params2(1.0, false, landmarkDistanceThreshold); // all default except landmarkDistanceThreshold
277  actual = triangulateSafe(cameras,measurements,params2);
278  EXPECT(actual.farPoint());
279 
280  // 3. Add a slightly rotated third camera above with a wrong measurement (OUTLIER)
281  Pose3 pose3 = pose1 * Pose3(Rot3::Ypr(0.1, 0.2, 0.1), Point3(0.1, -2, -.1));
282  Cal3_S2 K3(700, 500, 0, 640, 480);
283  PinholeCamera<Cal3_S2> camera3(pose3, K3);
284  Point2 z3 = camera3.project(landmark);
285 
286  cameras += camera3;
287  measurements += z3 + Point2(10, -10);
288 
289  landmarkDistanceThreshold = 10; // landmark is closer than that
290  double outlierThreshold = 100; // loose, the outlier is going to pass
291  TriangulationParameters params3(1.0, false, landmarkDistanceThreshold,outlierThreshold);
292  actual = triangulateSafe(cameras,measurements,params3);
293  EXPECT(actual.valid());
294 
295  // now set stricter threshold for outlier rejection
296  outlierThreshold = 5; // tighter, the outlier is not going to pass
297  TriangulationParameters params4(1.0, false, landmarkDistanceThreshold,outlierThreshold);
298  actual = triangulateSafe(cameras,measurements,params4);
299  EXPECT(actual.outlier());
300 }
301 
302 //******************************************************************************
303 TEST( triangulation, twoIdenticalPoses) {
304  // create first camera. Looking along X-axis, 1 meter above ground plane (x-y)
306 
307  // 1. Project two landmarks into two cameras and triangulate
308  Point2 z1 = camera1.project(landmark);
309 
310  vector<Pose3> poses;
311  Point2Vector measurements;
312 
313  poses += pose1, pose1;
314  measurements += z1, z1;
315 
316  CHECK_EXCEPTION(triangulatePoint3(poses, sharedCal, measurements),
318 }
319 
320 //******************************************************************************
321 TEST( triangulation, onePose) {
322  // we expect this test to fail with a TriangulationUnderconstrainedException
323  // because there's only one camera observation
324 
325  vector<Pose3> poses;
326  Point2Vector measurements;
327 
328  poses += Pose3();
329  measurements += Point2(0,0);
330 
331  CHECK_EXCEPTION(triangulatePoint3(poses, sharedCal, measurements),
333 }
334 
335 //******************************************************************************
336 TEST( triangulation, StereotriangulateNonlinear ) {
337 
338  auto stereoK = boost::make_shared<Cal3_S2Stereo>(1733.75, 1733.75, 0, 689.645, 508.835, 0.0699612);
339 
340  // two camera poses m1, m2
341  Matrix4 m1, m2;
342  m1 << 0.796888717, 0.603404026, -0.0295271487, 46.6673779,
343  0.592783835, -0.77156583, 0.230856632, 66.2186159,
344  0.116517574, -0.201470143, -0.9725393, -4.28382528,
345  0, 0, 0, 1;
346 
347  m2 << -0.955959025, -0.29288915, -0.0189328569, 45.7169799,
348  -0.29277519, 0.947083213, 0.131587097, 65.843136,
349  -0.0206094928, 0.131334858, -0.991123524, -4.3525033,
350  0, 0, 0, 1;
351 
352  typedef CameraSet<StereoCamera> Cameras;
353  Cameras cameras;
354  cameras.push_back(StereoCamera(Pose3(m1), stereoK));
355  cameras.push_back(StereoCamera(Pose3(m2), stereoK));
356 
357  StereoPoint2Vector measurements;
358  measurements += StereoPoint2(226.936, 175.212, 424.469);
359  measurements += StereoPoint2(339.571, 285.547, 669.973);
360 
361  Point3 initial = Point3(46.0536958, 66.4621179, -6.56285929); // error: 96.5715555191
362 
363  Point3 actual = triangulateNonlinear(cameras, measurements, initial);
364 
365  Point3 expected(46.0484569, 66.4710686, -6.55046613); // error: 0.763510644187
366 
367  EXPECT(assert_equal(expected, actual, 1e-4));
368 
369 
370  // regular stereo factor comparison - expect very similar result as above
371  {
373 
374  Values values;
375  values.insert(Symbol('x', 1), Pose3(m1));
376  values.insert(Symbol('x', 2), Pose3(m2));
377  values.insert(Symbol('l', 1), initial);
378 
380  static SharedNoiseModel unit(noiseModel::Unit::Create(3));
381  graph.emplace_shared<StereoFactor>(measurements[0], unit, Symbol('x',1), Symbol('l',1), stereoK);
382  graph.emplace_shared<StereoFactor>(measurements[1], unit, Symbol('x',2), Symbol('l',1), stereoK);
383 
384  const SharedDiagonal posePrior = noiseModel::Isotropic::Sigma(6, 1e-9);
385  graph.addPrior(Symbol('x',1), Pose3(m1), posePrior);
386  graph.addPrior(Symbol('x',2), Pose3(m2), posePrior);
387 
388  LevenbergMarquardtOptimizer optimizer(graph, values);
389  Values result = optimizer.optimize();
390 
391  EXPECT(assert_equal(expected, result.at<Point3>(Symbol('l',1)), 1e-4));
392  }
393 
394  // use Triangulation Factor directly - expect same result as above
395  {
396  Values values;
397  values.insert(Symbol('l', 1), initial);
398 
400  static SharedNoiseModel unit(noiseModel::Unit::Create(3));
401 
402  graph.emplace_shared<TriangulationFactor<StereoCamera> >(cameras[0], measurements[0], unit, Symbol('l',1));
403  graph.emplace_shared<TriangulationFactor<StereoCamera> >(cameras[1], measurements[1], unit, Symbol('l',1));
404 
405  LevenbergMarquardtOptimizer optimizer(graph, values);
406  Values result = optimizer.optimize();
407 
408  EXPECT(assert_equal(expected, result.at<Point3>(Symbol('l',1)), 1e-4));
409  }
410 
411  // use ExpressionFactor - expect same result as above
412  {
413  Values values;
414  values.insert(Symbol('l', 1), initial);
415 
417  static SharedNoiseModel unit(noiseModel::Unit::Create(3));
418 
419  Expression<Point3> point_(Symbol('l',1));
420  Expression<StereoCamera> camera0_(cameras[0]);
421  Expression<StereoCamera> camera1_(cameras[1]);
422  Expression<StereoPoint2> project0_(camera0_, &StereoCamera::project2, point_);
423  Expression<StereoPoint2> project1_(camera1_, &StereoCamera::project2, point_);
424 
425  graph.addExpressionFactor(unit, measurements[0], project0_);
426  graph.addExpressionFactor(unit, measurements[1], project1_);
427 
428  LevenbergMarquardtOptimizer optimizer(graph, values);
429  Values result = optimizer.optimize();
430 
431  EXPECT(assert_equal(expected, result.at<Point3>(Symbol('l',1)), 1e-4));
432  }
433 }
434 
435 //******************************************************************************
436 int main() {
437  TestResult tr;
438  return TestRegistry::runAllTests(tr);
439 }
440 //******************************************************************************
static Point2 project2(const CalibratedCamera &camera, const Point3 &point)
virtual const Values & optimize()
static int runAllTests(TestResult &result)
void insert(Key j, const Value &val)
Definition: Values.cpp:140
Matrix expected
Definition: testMatrix.cpp:974
Base class to create smart factors on poses or cameras.
MatrixType m2(n_dims)
PinholeCamera< Cal3_S2 > camera1(pose1,*sharedCal)
Vector2 Point2
Definition: Point2.h:27
#define M_PI
Definition: main.h:78
static const Pose3 pose1
leaf::MyValues values
static const Rot3 upright
int main()
Point3 triangulateNonlinear(const std::vector< Pose3 > &poses, boost::shared_ptr< CALIBRATION > sharedCal, const Point2Vector &measurements, const Point3 &initialEstimate)
Definition: Half.h:150
static const Point3 landmark(5, 0.5, 1.2)
#define CHECK_EXCEPTION(condition, exception_name)
Definition: Test.h:119
NonlinearFactorGraph graph
A set of cameras, all with their own calibration.
Definition: CameraSet.h:35
Point2 z2
static const boost::shared_ptr< Cal3_S2 > sharedCal
std::vector< StereoPoint2 > StereoPoint2Vector
Definition: StereoPoint2.h:162
static const CalibratedCamera camera3(pose1)
void addPrior(Key key, const T &prior, const SharedNoiseModel &model=nullptr)
PinholeCamera< Cal3_S2 > camera2(pose2,*sharedCal)
static const Cal3Bundler K2(625, 1e-3, 1e-3)
Base class for all pinhole cameras.
IsDerived< DERIVEDFACTOR > emplace_shared(Args &&...args)
Emplace a shared pointer to factor of given type.
Definition: FactorGraph.h:172
TEST(triangulation, twoPoses)
static const Unit3 z3
Values result
Point3 triangulatePoint3(const std::vector< Pose3 > &poses, boost::shared_ptr< CALIBRATION > sharedCal, const Point2Vector &measurements, double rank_tol=1e-9, bool optimize=false)
const ValueType at(Key j) const
Definition: Values-inl.h:342
Matrix3d m1
Definition: IOFormat.cpp:2
void addExpressionFactor(const SharedNoiseModel &R, const T &z, const Expression< T > &h)
std::vector< Point2, Eigen::aligned_allocator< Point2 > > Point2Vector
Definition: Point2.h:42
#define EXPECT(condition)
Definition: Test.h:151
static Pose3 pose3(rt3, pt3)
Functions for triangulation.
A nonlinear optimizer that uses the Levenberg-Marquardt trust-region scheme.
Array< double, 1, 3 > e(1./3., 0.5, 2.)
static Cal3_S2::shared_ptr K1(new Cal3_S2(fov, w, h))
Exception thrown by triangulateDLT when SVD returns rank < 3.
Definition: triangulation.h:33
static SmartStereoProjectionParams params
TriangulationResult triangulateSafe(const CameraSet< CAMERA > &cameras, const typename CAMERA::MeasurementVector &measured, const TriangulationParameters &params)
triangulateSafe: extensive checking of the outcome
Exception thrown by triangulateDLT when landmark is behind one or more of the cameras.
Definition: triangulation.h:41
noiseModel::Diagonal::shared_ptr SharedDiagonal
Definition: NoiseModel.h:736
traits
Definition: chartTesting.h:28
DirectSum< Z2, Z2 > K4
Definition: testCyclic.cpp:90
bool assert_equal(const Matrix &expected, const Matrix &actual, double tol)
Definition: Matrix.cpp:42
Point2 z1
A Stereo Camera based on two Simple Cameras.
int optimize(const SfmData &db, const NonlinearFactorGraph &graph, const Values &initial, bool separateCalibration=false)
Definition: timeSFMBAL.h:63
Point2 project(const Point3 &pw, OptionalJacobian< 2, 6 > Dpose=boost::none, OptionalJacobian< 2, 3 > Dpoint=boost::none, OptionalJacobian< 2, DimK > Dcal=boost::none) const
project a 3D point from world coordinates into the image
Definition: PinholePose.h:118
static const Pose3 pose2
Vector3 Point3
Definition: Point3.h:35
Calibration used by Bundler.
noiseModel::Base::shared_ptr SharedNoiseModel
Definition: NoiseModel.h:734


gtsam
Author(s):
autogenerated on Sat May 8 2021 02:50:11