ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
essential_matrix_test.cc
Go to the documentation of this file.
1 // Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 //
14 // * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of
15 // its contributors may be used to endorse or promote products derived
16 // from this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 // POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)
31 
32 #define TEST_NAME "base/essential_matrix"
33 #include "util/testing.h"
34 
35 #include <Eigen/Geometry>
36 
37 #include "base/essential_matrix.h"
38 #include "base/pose.h"
39 #include "base/projection.h"
40 
41 using namespace colmap;
42 
43 BOOST_AUTO_TEST_CASE(TestDecomposeEssentialMatrix) {
44  const Eigen::Matrix3d R = EulerAnglesToRotationMatrix(0, 1, 1);
45  const Eigen::Vector3d t = Eigen::Vector3d(0.5, 1, 1).normalized();
46  const Eigen::Matrix3d E = EssentialMatrixFromPose(R, t);
47 
48  Eigen::Matrix3d R1;
49  Eigen::Matrix3d R2;
50  Eigen::Vector3d tt;
51  DecomposeEssentialMatrix(E, &R1, &R2, &tt);
52 
53  BOOST_CHECK((R1 - R).norm() < 1e-10 || (R2 - R).norm() < 1e-10);
54  BOOST_CHECK((tt - t).norm() < 1e-10 || (tt + t).norm() < 1e-10);
55 }
56 
57 BOOST_AUTO_TEST_CASE(TestEssentialMatrixFromPose) {
58  BOOST_CHECK_EQUAL(
60  Eigen::Vector3d(0, 0, 1)),
61  (Eigen::MatrixXd(3, 3) << 0, -1, 0, 1, 0, 0, 0, 0, 0).finished());
62  BOOST_CHECK_EQUAL(
64  Eigen::Vector3d(0, 0, 2)),
65  (Eigen::MatrixXd(3, 3) << 0, -1, 0, 1, 0, 0, 0, 0, 0).finished());
66 }
67 
68 BOOST_AUTO_TEST_CASE(TestEssentialMatrixFromPoses) {
69  const Eigen::Matrix3d R1 = EulerAnglesToRotationMatrix(0, 0, 0);
70  const Eigen::Matrix3d R2 = EulerAnglesToRotationMatrix(0, 1, 2);
71  const Eigen::Vector3d t1(0, 0, 0);
72  const Eigen::Vector3d t2 = Eigen::Vector3d(0.5, 1, 1).normalized();
73 
74  const Eigen::Matrix3d E1 = EssentialMatrixFromPose(R2, t2);
75  const Eigen::Matrix3d E2 = EssentialMatrixFromAbsolutePoses(
77 
78  BOOST_CHECK_CLOSE((E1 - E2).norm(), 0, 1e-6);
79 }
80 
81 BOOST_AUTO_TEST_CASE(TestPoseFromEssentialMatrix) {
82  const Eigen::Matrix3d R = EulerAnglesToRotationMatrix(0, 0, 0);
83  const Eigen::Vector3d t = Eigen::Vector3d(1, 0, 0).normalized();
84  const Eigen::Matrix3d E = EssentialMatrixFromPose(R, t);
85 
86  const Eigen::Matrix3x4d proj_matrix1 = Eigen::Matrix3x4d::Identity();
87  const Eigen::Matrix3x4d proj_matrix2 = ComposeProjectionMatrix(R, t);
88 
89  std::vector<Eigen::Vector3d> points3D(4);
90  points3D[0] = Eigen::Vector3d(0, 0, 1);
91  points3D[1] = Eigen::Vector3d(0, 0.1, 1);
92  points3D[2] = Eigen::Vector3d(0.1, 0, 1);
93  points3D[3] = Eigen::Vector3d(0.1, 0.1, 1);
94 
95  std::vector<Eigen::Vector2d> points1(4);
96  std::vector<Eigen::Vector2d> points2(4);
97  for (size_t i = 0; i < points3D.size(); ++i) {
98  const Eigen::Vector3d point1 = proj_matrix1 * points3D[i].homogeneous();
99  points1[i] = point1.hnormalized();
100  const Eigen::Vector3d point2 = proj_matrix2 * points3D[i].homogeneous();
101  points2[i] = point2.hnormalized();
102  }
103 
104  points3D.clear();
105 
106  Eigen::Matrix3d RR;
107  Eigen::Vector3d tt;
108  PoseFromEssentialMatrix(E, points1, points2, &RR, &tt, &points3D);
109 
110  BOOST_CHECK_EQUAL(points3D.size(), 4);
111 
112  BOOST_CHECK(RR.isApprox(R));
113  BOOST_CHECK(tt.isApprox(t));
114 }
115 
116 BOOST_AUTO_TEST_CASE(TestFindOptimalImageObservations) {
117  const Eigen::Matrix3d R = EulerAnglesToRotationMatrix(0, 0, 0);
118  const Eigen::Vector3d t = Eigen::Vector3d(1, 0, 0).normalized();
119  const Eigen::Matrix3d E = EssentialMatrixFromPose(R, t);
120 
121  const Eigen::Matrix3x4d proj_matrix1 = Eigen::Matrix3x4d::Identity();
122  const Eigen::Matrix3x4d proj_matrix2 = ComposeProjectionMatrix(R, t);
123 
124  std::vector<Eigen::Vector3d> points3D(4);
125  points3D[0] = Eigen::Vector3d(0, 0, 1);
126  points3D[1] = Eigen::Vector3d(0, 0.1, 1);
127  points3D[2] = Eigen::Vector3d(0.1, 0, 1);
128  points3D[3] = Eigen::Vector3d(0.1, 0.1, 1);
129 
130  // Test if perfect projection is equivalent to optimal image observations.
131  for (size_t i = 0; i < points3D.size(); ++i) {
132  const Eigen::Vector3d point1_homogeneous =
133  proj_matrix1 * points3D[i].homogeneous();
134  const Eigen::Vector2d point1 = point1_homogeneous.hnormalized();
135  const Eigen::Vector3d point2_homogeneous =
136  proj_matrix2 * points3D[i].homogeneous();
137  const Eigen::Vector2d point2 = point2_homogeneous.hnormalized();
138  Eigen::Vector2d optimal_point1;
139  Eigen::Vector2d optimal_point2;
140  FindOptimalImageObservations(E, point1, point2, &optimal_point1,
141  &optimal_point2);
142  BOOST_CHECK(point1.isApprox(optimal_point1));
143  BOOST_CHECK(point2.isApprox(optimal_point2));
144  }
145 }
146 
147 BOOST_AUTO_TEST_CASE(TestEpipoleFromEssentialMatrix) {
148  const Eigen::Matrix3d R = EulerAnglesToRotationMatrix(0, 0, 0);
149  const Eigen::Vector3d t = Eigen::Vector3d(0, 0, -1).normalized();
150  const Eigen::Matrix3d E = EssentialMatrixFromPose(R, t);
151 
152  const Eigen::Vector3d left_epipole = EpipoleFromEssentialMatrix(E, true);
153  const Eigen::Vector3d right_epipole = EpipoleFromEssentialMatrix(E, false);
154  BOOST_CHECK(left_epipole.isApprox(Eigen::Vector3d(0, 0, 1)));
155  BOOST_CHECK(right_epipole.isApprox(Eigen::Vector3d(0, 0, 1)));
156 }
157 
158 BOOST_AUTO_TEST_CASE(TestInvertEssentialMatrix) {
159  for (size_t i = 1; i < 10; ++i) {
160  const Eigen::Matrix3d R = EulerAnglesToRotationMatrix(0, 0.1, 0);
161  const Eigen::Vector3d t = Eigen::Vector3d(0, 0, i).normalized();
162  const Eigen::Matrix3d E = EssentialMatrixFromPose(R, t);
163  const Eigen::Matrix3d inv_inv_E =
165  BOOST_CHECK(E.isApprox(inv_inv_E));
166  }
167 }
168 
169 BOOST_AUTO_TEST_CASE(TestRefineEssentialMatrix) {
170  const Eigen::Matrix3d R = EulerAnglesToRotationMatrix(0, 0, 0);
171  const Eigen::Vector3d t = Eigen::Vector3d(1, 0, 0).normalized();
172  const Eigen::Matrix3d E = EssentialMatrixFromPose(R, t);
173 
174  const Eigen::Matrix3x4d proj_matrix1 = Eigen::Matrix3x4d::Identity();
175  const Eigen::Matrix3x4d proj_matrix2 = ComposeProjectionMatrix(R, t);
176 
177  std::vector<Eigen::Vector3d> points3D(150);
178  for (size_t i = 0; i < points3D.size() / 3; ++i) {
179  points3D[3 * i + 0] = Eigen::Vector3d(i * 0.01, 0, 1);
180  points3D[3 * i + 1] = Eigen::Vector3d(0, i * 0.01, 1);
181  points3D[3 * i + 2] = Eigen::Vector3d(i * 0.01, i * 0.01, 1);
182  }
183 
184  std::vector<Eigen::Vector2d> points1(points3D.size());
185  std::vector<Eigen::Vector2d> points2(points3D.size());
186  for (size_t i = 0; i < points3D.size(); ++i) {
187  const Eigen::Vector3d point1 = proj_matrix1 * points3D[i].homogeneous();
188  points1[i] = point1.hnormalized();
189  const Eigen::Vector3d point2 = proj_matrix2 * points3D[i].homogeneous();
190  points2[i] = point2.hnormalized();
191  }
192 
193  const Eigen::Matrix3d R_pertubated = EulerAnglesToRotationMatrix(0, 0, 0);
194  const Eigen::Vector3d t_pertubated =
195  Eigen::Vector3d(1.02, 0.02, 0.02).normalized();
196  const Eigen::Matrix3d E_pertubated =
197  EssentialMatrixFromPose(R_pertubated, t_pertubated);
198 
199  Eigen::Matrix3d E_refined = E_pertubated;
200  ceres::Solver::Options options;
201  RefineEssentialMatrix(options, points1, points2,
202  std::vector<char>(points1.size(), true), &E_refined);
203 
204  BOOST_CHECK_LE((E - E_refined).norm(), (E - E_pertubated).norm());
205 }
BOOST_AUTO_TEST_CASE(TestDecomposeEssentialMatrix)
const double * e
Matrix< double, 3, 4 > Matrix3x4d
Definition: types.h:39
Eigen::Matrix3d InvertEssentialMatrix(const Eigen::Matrix3d &E)
Eigen::Matrix3d EulerAnglesToRotationMatrix(const double rx, const double ry, const double rz)
Definition: pose.cc:59
Eigen::Matrix3d EssentialMatrixFromAbsolutePoses(const Eigen::Matrix3x4d &proj_matrix1, const Eigen::Matrix3x4d &proj_matrix2)
void FindOptimalImageObservations(const Eigen::Matrix3d &E, const Eigen::Vector2d &point1, const Eigen::Vector2d &point2, Eigen::Vector2d *optimal_point1, Eigen::Vector2d *optimal_point2)
Eigen::Matrix3d EssentialMatrixFromPose(const Eigen::Matrix3d &R, const Eigen::Vector3d &t)
void DecomposeEssentialMatrix(const Eigen::Matrix3d &E, Eigen::Matrix3d *R1, Eigen::Matrix3d *R2, Eigen::Vector3d *t)
Eigen::Vector3d EpipoleFromEssentialMatrix(const Eigen::Matrix3d &E, const bool left_image)
void PoseFromEssentialMatrix(const Eigen::Matrix3d &E, const std::vector< Eigen::Vector2d > &points1, const std::vector< Eigen::Vector2d > &points2, Eigen::Matrix3d *R, Eigen::Vector3d *t, std::vector< Eigen::Vector3d > *points3D)
bool RefineEssentialMatrix(const ceres::Solver::Options &options, const std::vector< Eigen::Vector2d > &points1, const std::vector< Eigen::Vector2d > &points2, const std::vector< char > &inlier_mask, Eigen::Matrix3d *E)
Eigen::Matrix3x4d ComposeProjectionMatrix(const Eigen::Vector4d &qvec, const Eigen::Vector3d &tvec)
Definition: projection.cc:39