ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
projection_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/projection"
33 #include "util/testing.h"
34 
35 #include <Eigen/Core>
36 
37 #include "base/camera_models.h"
38 #include "base/pose.h"
39 #include "base/projection.h"
40 #include "util/math.h"
41 
42 using namespace colmap;
43 
44 BOOST_AUTO_TEST_CASE(TestComposeProjectionMatrix) {
45  const Eigen::Matrix3d R = EulerAnglesToRotationMatrix(0, 1, 2);
46  const Eigen::Vector4d qvec = RotationMatrixToQuaternion(R);
47  const Eigen::Vector3d tvec = Eigen::Vector3d::Random();
48 
49  const auto proj_matrix1 = ComposeProjectionMatrix(qvec, tvec);
50  const auto proj_matrix2 = ComposeProjectionMatrix(R, tvec);
51 
52  BOOST_CHECK((proj_matrix1 - proj_matrix2).norm() < 1e-6);
53  BOOST_CHECK((proj_matrix1.leftCols<3>() - R).norm() < 1e-6);
54  BOOST_CHECK_CLOSE((proj_matrix1.rightCols<1>() - tvec).norm(), 0, 1e-6);
55  BOOST_CHECK_CLOSE((proj_matrix2.leftCols<3>() - R).norm(), 0, 1e-6);
56  BOOST_CHECK_CLOSE((proj_matrix2.rightCols<1>() - tvec).norm(), 0, 1e-6);
57 }
58 
59 BOOST_AUTO_TEST_CASE(TestInvertProjectionMatrix) {
60  const Eigen::Matrix3d R = EulerAnglesToRotationMatrix(0, 1, 2);
61  const Eigen::Vector3d tvec = Eigen::Vector3d::Random();
62 
63  const auto proj_matrix = ComposeProjectionMatrix(R, tvec);
64  const auto inv_proj_matrix = InvertProjectionMatrix(proj_matrix);
65  const auto inv_inv_proj_matrix = InvertProjectionMatrix(inv_proj_matrix);
66 
67  BOOST_CHECK((proj_matrix - inv_inv_proj_matrix).norm() < 1e-6);
68 }
69 
70 BOOST_AUTO_TEST_CASE(TestComputeClosestRotationMatrix) {
71  const Eigen::Matrix3d A = Eigen::Matrix3d::Identity();
72  BOOST_CHECK_LT((ComputeClosestRotationMatrix(A) - A).norm(), 1e-6);
73  BOOST_CHECK_LT((ComputeClosestRotationMatrix(2 * A) - A).norm(), 1e-6);
74 }
75 
76 BOOST_AUTO_TEST_CASE(TestDecomposeProjectionMatrix) {
77  for (int i = 1; i < 100; ++i) {
78  Eigen::Matrix3d ref_K = i * Eigen::Matrix3d::Identity();
79  ref_K(0, 2) = i;
80  ref_K(1, 2) = 2 * i;
81  const Eigen::Matrix3d ref_R = EulerAnglesToRotationMatrix(i, 2 * i, 3 * i);
82  const Eigen::Vector3d ref_T = Eigen::Vector3d::Random();
83  const Eigen::Matrix3x4d ref_P =
84  ref_K * ComposeProjectionMatrix(ref_R, ref_T);
85  Eigen::Matrix3d K;
86  Eigen::Matrix3d R;
87  Eigen::Vector3d T;
88  DecomposeProjectionMatrix(ref_P, &K, &R, &T);
89  BOOST_CHECK(ref_K.isApprox(K, 1e-6));
90  BOOST_CHECK(ref_R.isApprox(R, 1e-6));
91  BOOST_CHECK(ref_T.isApprox(T, 1e-6));
92  }
93 }
94 
95 BOOST_AUTO_TEST_CASE(TestCalculateSquaredReprojectionError) {
96  const Eigen::Vector4d qvec = ComposeIdentityQuaternion();
97  const Eigen::Vector3d tvec = Eigen::Vector3d::Zero();
98 
99  const auto proj_matrix = ComposeProjectionMatrix(qvec, tvec);
100 
101  const Eigen::Vector3d point3D = Eigen::Vector3d::Random().cwiseAbs();
102  const Eigen::Vector3d point2D_h = proj_matrix * point3D.homogeneous();
103  const Eigen::Vector2d point2D = point2D_h.hnormalized();
104 
105  Camera camera;
107 
108  const double error1 =
109  CalculateSquaredReprojectionError(point2D, point3D, qvec, tvec, camera);
110  BOOST_CHECK_EQUAL(error1, 0);
111 
112  const double error2 =
113  CalculateSquaredReprojectionError(point2D, point3D, proj_matrix, camera);
114  BOOST_CHECK_GE(error2, 0);
115  BOOST_CHECK_LT(error2, 1e-6);
116 
117  const double error3 = CalculateSquaredReprojectionError(
118  point2D.array() + 1, point3D, qvec, tvec, camera);
119  BOOST_CHECK_CLOSE(error3, 2, 1e-6);
120 
121  const double error4 = CalculateSquaredReprojectionError(
122  point2D.array() + 1, point3D, proj_matrix, camera);
123  BOOST_CHECK_CLOSE(error4, 2, 1e-6);
124 }
125 
126 BOOST_AUTO_TEST_CASE(TestCalculateAngularError) {
127  const Eigen::Vector4d qvec = ComposeIdentityQuaternion();
128  const Eigen::Vector3d tvec = Eigen::Vector3d(0, 0, 0);
129 
130  const auto proj_matrix = ComposeProjectionMatrix(qvec, tvec);
131  Camera camera;
133  camera.Params() = {1, 0, 0};
134 
135  const double error1 = CalculateAngularError(
136  Eigen::Vector2d(0, 0), Eigen::Vector3d(0, 0, 1), proj_matrix, camera);
137  BOOST_CHECK_CLOSE(error1, 0, 1e-6);
138 
139  const double error2 = CalculateAngularError(
140  Eigen::Vector2d(0, 0), Eigen::Vector3d(0, 1, 1), proj_matrix, camera);
141  BOOST_CHECK_CLOSE(error2, M_PI / 4, 1e-6);
142 
143  const double error3 = CalculateAngularError(
144  Eigen::Vector2d(0, 0), Eigen::Vector3d(0, 5, 5), proj_matrix, camera);
145  BOOST_CHECK_CLOSE(error3, M_PI / 4, 1e-6);
146 
147  const double error4 = CalculateAngularError(
148  Eigen::Vector2d(1, 0), Eigen::Vector3d(0, 0, 1), proj_matrix, camera);
149  BOOST_CHECK_CLOSE(error4, M_PI / 4, 1e-6);
150 
151  const double error5 = CalculateAngularError(
152  Eigen::Vector2d(2, 0), Eigen::Vector3d(0, 0, 1), proj_matrix, camera);
153  BOOST_CHECK_CLOSE(error5, 1.10714872, 1e-6);
154 
155  const double error6 = CalculateAngularError(
156  Eigen::Vector2d(2, 0), Eigen::Vector3d(1, 0, 1), proj_matrix, camera);
157  BOOST_CHECK_CLOSE(error6, 1.10714872 - M_PI / 4, 1e-6);
158 
159  const double error7 = CalculateAngularError(
160  Eigen::Vector2d(2, 0), Eigen::Vector3d(5, 0, 5), proj_matrix, camera);
161  BOOST_CHECK_CLOSE(error7, 1.10714872 - M_PI / 4, 1e-6);
162 
163  const double error8 = CalculateAngularError(
164  Eigen::Vector2d(1, 0), Eigen::Vector3d(-1, 0, 1), proj_matrix, camera);
165  BOOST_CHECK_CLOSE(error8, M_PI / 2, 1e-6);
166 
167  const double error9 = CalculateAngularError(
168  Eigen::Vector2d(1, 0), Eigen::Vector3d(-1, 0, 0), proj_matrix, camera);
169  BOOST_CHECK_CLOSE(error9, M_PI * 3 / 4, 1e-6);
170 
171  const double error10 = CalculateAngularError(
172  Eigen::Vector2d(1, 0), Eigen::Vector3d(-1, 0, -1), proj_matrix, camera);
173  BOOST_CHECK_CLOSE(error10, M_PI, 1e-6);
174 
175  const double error11 = CalculateAngularError(
176  Eigen::Vector2d(1, 0), Eigen::Vector3d(0, 0, -1), proj_matrix, camera);
177  BOOST_CHECK_CLOSE(error11, M_PI * 3 / 4, 1e-6);
178 }
179 
180 BOOST_AUTO_TEST_CASE(TestCalculateDepth) {
181  const Eigen::Vector4d qvec(1, 0, 0, 0);
182  const Eigen::Vector3d tvec(0, 0, 0);
183  const auto proj_matrix = ComposeProjectionMatrix(qvec, tvec);
184 
185  // In the image plane
186  const double depth1 = CalculateDepth(proj_matrix, Eigen::Vector3d(0, 0, 0));
187  BOOST_CHECK_CLOSE(depth1, 0, 1e-10);
188  const double depth2 = CalculateDepth(proj_matrix, Eigen::Vector3d(0, 2, 0));
189  BOOST_CHECK_CLOSE(depth2, 0, 1e-10);
190 
191  // Infront of camera
192  const double depth3 = CalculateDepth(proj_matrix, Eigen::Vector3d(0, 0, 1));
193  BOOST_CHECK_CLOSE(depth3, 1, 1e-10);
194 
195  // Behind camera
196  const double depth4 = CalculateDepth(proj_matrix, Eigen::Vector3d(0, 0, -1));
197  BOOST_CHECK_CLOSE(depth4, -1, 1e-10);
198 }
199 
200 BOOST_AUTO_TEST_CASE(TestHasPointPositiveDepth) {
201  const Eigen::Vector4d qvec(1, 0, 0, 0);
202  const Eigen::Vector3d tvec(0, 0, 0);
203  const auto proj_matrix = ComposeProjectionMatrix(qvec, tvec);
204 
205  // In the image plane
206  const bool check1 =
207  HasPointPositiveDepth(proj_matrix, Eigen::Vector3d(0, 0, 0));
208  BOOST_CHECK(!check1);
209  const bool check2 =
210  HasPointPositiveDepth(proj_matrix, Eigen::Vector3d(0, 2, 0));
211  BOOST_CHECK(!check2);
212 
213  // Infront of camera
214  const bool check3 =
215  HasPointPositiveDepth(proj_matrix, Eigen::Vector3d(0, 0, 1));
216  BOOST_CHECK(check3);
217 
218  // Behind camera
219  const bool check4 =
220  HasPointPositiveDepth(proj_matrix, Eigen::Vector3d(0, 0, -1));
221  BOOST_CHECK(!check4);
222 }
constexpr double M_PI
Pi.
Definition: CVConst.h:19
void InitializeWithId(const int model_id, const double focal_length, const size_t width, const size_t height)
Definition: camera.cc:203
const std::vector< double > & Params() const
Definition: camera.h:176
void SetModelId(const int model_id)
Definition: camera.cc:51
const double * e
Matrix< double, 3, 4 > Matrix3x4d
Definition: types.h:39
double CalculateSquaredReprojectionError(const Eigen::Vector2d &point2D, const Eigen::Vector3d &point3D, const Eigen::Vector4d &qvec, const Eigen::Vector3d &tvec, const Camera &camera)
Definition: projection.cc:111
bool HasPointPositiveDepth(const Eigen::Matrix3x4d &proj_matrix, const Eigen::Vector3d &point3D)
Definition: projection.cc:191
Eigen::Matrix3d EulerAnglesToRotationMatrix(const double rx, const double ry, const double rz)
Definition: pose.cc:59
Eigen::Vector4d ComposeIdentityQuaternion()
Definition: pose.h:209
Eigen::Matrix3x4d InvertProjectionMatrix(const Eigen::Matrix3x4d &proj_matrix)
Definition: projection.cc:55
double CalculateDepth(const Eigen::Matrix3x4d &proj_matrix, const Eigen::Vector3d &point3D)
Definition: projection.cc:185
Eigen::Vector4d RotationMatrixToQuaternion(const Eigen::Matrix3d &rot_mat)
Definition: pose.cc:70
bool DecomposeProjectionMatrix(const Eigen::Matrix3x4d &P, Eigen::Matrix3d *K, Eigen::Matrix3d *R, Eigen::Vector3d *T)
Definition: projection.cc:72
Eigen::Matrix3d ComputeClosestRotationMatrix(const Eigen::Matrix3d &matrix)
Definition: projection.cc:62
Eigen::Matrix3x4d ComposeProjectionMatrix(const Eigen::Vector4d &qvec, const Eigen::Vector3d &tvec)
Definition: projection.cc:39
double CalculateAngularError(const Eigen::Vector2d &point2D, const Eigen::Vector3d &point3D, const Eigen::Vector4d &qvec, const Eigen::Vector3d &tvec, const Camera &camera)
Definition: projection.cc:151
BOOST_AUTO_TEST_CASE(TestComposeProjectionMatrix)