ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
essential_matrix.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 
33 
34 #include <complex>
35 
36 #include <Eigen/Geometry>
37 #include <Eigen/LU>
38 #include <Eigen/SVD>
39 
40 #include "base/polynomial.h"
41 #include "estimators/utils.h"
42 #include "util/logging.h"
43 #include "util/math.h"
44 
45 namespace colmap {
46 
47 std::vector<EssentialMatrixFivePointEstimator::M_t>
48 EssentialMatrixFivePointEstimator::Estimate(const std::vector<X_t>& points1,
49  const std::vector<Y_t>& points2) {
50  CHECK_EQ(points1.size(), points2.size());
51 
52  // Step 1: Extraction of the nullspace x, y, z, w.
53 
54  Eigen::Matrix<double, Eigen::Dynamic, 9> Q(points1.size(), 9);
55  for (size_t i = 0; i < points1.size(); ++i) {
56  const double x1_0 = points1[i](0);
57  const double x1_1 = points1[i](1);
58  const double x2_0 = points2[i](0);
59  const double x2_1 = points2[i](1);
60  Q(i, 0) = x1_0 * x2_0;
61  Q(i, 1) = x1_1 * x2_0;
62  Q(i, 2) = x2_0;
63  Q(i, 3) = x1_0 * x2_1;
64  Q(i, 4) = x1_1 * x2_1;
65  Q(i, 5) = x2_1;
66  Q(i, 6) = x1_0;
67  Q(i, 7) = x1_1;
68  Q(i, 8) = 1;
69  }
70 
71  // Extract the 4 Eigen vectors corresponding to the smallest singular values.
72  const Eigen::JacobiSVD<Eigen::Matrix<double, Eigen::Dynamic, 9>> svd(
73  Q, Eigen::ComputeFullV);
74  const Eigen::Matrix<double, 9, 4> E = svd.matrixV().block<9, 4>(0, 5);
75 
76  // Step 3: Gauss-Jordan elimination with partial pivoting on A.
77 
78  Eigen::Matrix<double, 10, 20> A;
80  Eigen::Matrix<double, 10, 10> AA =
81  A.block<10, 10>(0, 0).partialPivLu().solve(A.block<10, 10>(0, 10));
82 
83  // Step 4: Expansion of the determinant polynomial of the 3x3 polynomial
84  // matrix B to obtain the tenth degree polynomial.
85 
86  Eigen::Matrix<double, 13, 3> B;
87  for (size_t i = 0; i < 3; ++i) {
88  B(0, i) = 0;
89  B(4, i) = 0;
90  B(8, i) = 0;
91  B.block<3, 1>(1, i) = AA.block<1, 3>(i * 2 + 4, 0);
92  B.block<3, 1>(5, i) = AA.block<1, 3>(i * 2 + 4, 3);
93  B.block<4, 1>(9, i) = AA.block<1, 4>(i * 2 + 4, 6);
94  B.block<3, 1>(0, i) -= AA.block<1, 3>(i * 2 + 5, 0);
95  B.block<3, 1>(4, i) -= AA.block<1, 3>(i * 2 + 5, 3);
96  B.block<4, 1>(8, i) -= AA.block<1, 4>(i * 2 + 5, 6);
97  }
98 
99  // Step 5: Extraction of roots from the degree 10 polynomial.
100  Eigen::Matrix<double, 11, 1> coeffs;
102 
103  Eigen::VectorXd roots_real;
104  Eigen::VectorXd roots_imag;
105  if (!FindPolynomialRootsCompanionMatrix(coeffs, &roots_real, &roots_imag)) {
106  return {};
107  }
108 
109  std::vector<M_t> models;
110  models.reserve(roots_real.size());
111 
112  for (Eigen::VectorXd::Index i = 0; i < roots_imag.size(); ++i) {
113  const double kMaxRootImag = 1e-10;
114  if (std::abs(roots_imag(i)) > kMaxRootImag) {
115  continue;
116  }
117 
118  const double z1 = roots_real(i);
119  const double z2 = z1 * z1;
120  const double z3 = z2 * z1;
121  const double z4 = z3 * z1;
122 
123  Eigen::Matrix3d Bz;
124  for (size_t j = 0; j < 3; ++j) {
125  Bz(j, 0) = B(0, j) * z3 + B(1, j) * z2 + B(2, j) * z1 + B(3, j);
126  Bz(j, 1) = B(4, j) * z3 + B(5, j) * z2 + B(6, j) * z1 + B(7, j);
127  Bz(j, 2) = B(8, j) * z4 + B(9, j) * z3 + B(10, j) * z2 + B(11, j) * z1 +
128  B(12, j);
129  }
130 
131  const Eigen::JacobiSVD<Eigen::Matrix3d> svd(Bz, Eigen::ComputeFullV);
132  const Eigen::Vector3d X = svd.matrixV().block<3, 1>(0, 2);
133 
134  const double kMaxX3 = 1e-10;
135  if (std::abs(X(2)) < kMaxX3) {
136  continue;
137  }
138 
139  Eigen::MatrixXd essential_vec = E.col(0) * (X(0) / X(2)) +
140  E.col(1) * (X(1) / X(2)) + E.col(2) * z1 +
141  E.col(3);
142  essential_vec /= essential_vec.norm();
143 
144  const Eigen::Matrix3d essential_matrix =
145  Eigen::Map<Eigen::Matrix<double, 3, 3, Eigen::RowMajor>>(
146  essential_vec.data());
147  models.push_back(essential_matrix);
148  }
149 
150  return models;
151 }
152 
154  const std::vector<X_t>& points1, const std::vector<Y_t>& points2,
155  const M_t& E, std::vector<double>* residuals) {
156  ComputeSquaredSampsonError(points1, points2, E, residuals);
157 }
158 
159 std::vector<EssentialMatrixEightPointEstimator::M_t>
160 EssentialMatrixEightPointEstimator::Estimate(const std::vector<X_t>& points1,
161  const std::vector<Y_t>& points2) {
162  CHECK_EQ(points1.size(), points2.size());
163 
164  // Center and normalize image points for better numerical stability.
165  std::vector<X_t> normed_points1;
166  std::vector<Y_t> normed_points2;
167  Eigen::Matrix3d points1_norm_matrix;
168  Eigen::Matrix3d points2_norm_matrix;
169  CenterAndNormalizeImagePoints(points1, &normed_points1, &points1_norm_matrix);
170  CenterAndNormalizeImagePoints(points2, &normed_points2, &points2_norm_matrix);
171 
172  // Setup homogeneous linear equation as x2' * F * x1 = 0.
173  Eigen::Matrix<double, Eigen::Dynamic, 9> cmatrix(points1.size(), 9);
174  for (size_t i = 0; i < points1.size(); ++i) {
175  cmatrix.block<1, 3>(i, 0) = normed_points1[i].homogeneous();
176  cmatrix.block<1, 3>(i, 0) *= normed_points2[i].x();
177  cmatrix.block<1, 3>(i, 3) = normed_points1[i].homogeneous();
178  cmatrix.block<1, 3>(i, 3) *= normed_points2[i].y();
179  cmatrix.block<1, 3>(i, 6) = normed_points1[i].homogeneous();
180  }
181 
182  // Solve for the nullspace of the constraint matrix.
183  Eigen::JacobiSVD<Eigen::Matrix<double, Eigen::Dynamic, 9>> cmatrix_svd(
184  cmatrix, Eigen::ComputeFullV);
185  const Eigen::VectorXd ematrix_nullspace = cmatrix_svd.matrixV().col(8);
186  const Eigen::Map<const Eigen::Matrix3d> ematrix_t(ematrix_nullspace.data());
187 
188  // De-normalize to image points.
189  const Eigen::Matrix3d E_raw = points2_norm_matrix.transpose() *
190  ematrix_t.transpose() * points1_norm_matrix;
191 
192  // Enforcing the internal constraint that two singular values must be equal
193  // and one must be zero.
194  Eigen::JacobiSVD<Eigen::Matrix3d> E_raw_svd(
195  E_raw, Eigen::ComputeFullU | Eigen::ComputeFullV);
196  Eigen::Vector3d singular_values = E_raw_svd.singularValues();
197  singular_values(0) = (singular_values(0) + singular_values(1)) / 2.0;
198  singular_values(1) = singular_values(0);
199  singular_values(2) = 0.0;
200  const Eigen::Matrix3d E = E_raw_svd.matrixU() * singular_values.asDiagonal() *
201  E_raw_svd.matrixV().transpose();
202 
203  const std::vector<M_t> models = {E};
204  return models;
205 }
206 
208  const std::vector<X_t>& points1, const std::vector<Y_t>& points2,
209  const M_t& E, std::vector<double>* residuals) {
210  ComputeSquaredSampsonError(points1, points2, E, residuals);
211 }
212 
213 } // namespace colmap
void * X
Definition: SmallVector.cpp:45
static void Residuals(const std::vector< X_t > &points1, const std::vector< Y_t > &points2, const M_t &E, std::vector< double > *residuals)
static std::vector< M_t > Estimate(const std::vector< X_t > &points1, const std::vector< Y_t > &points2)
static void Residuals(const std::vector< X_t > &points1, const std::vector< Y_t > &points2, const M_t &E, std::vector< double > *residuals)
static std::vector< M_t > Estimate(const std::vector< X_t > &points1, const std::vector< Y_t > &points2)
coeffs(0)
const double * e
normal_z y
normal_z x
bool FindPolynomialRootsCompanionMatrix(const Eigen::VectorXd &coeffs_all, Eigen::VectorXd *real, Eigen::VectorXd *imag)
Definition: polynomial.cc:208
void CenterAndNormalizeImagePoints(const std::vector< Eigen::Vector2d > &points, std::vector< Eigen::Vector2d > *normed_points, Eigen::Matrix3d *matrix)
Definition: utils.cc:38
void ComputeSquaredSampsonError(const std::vector< Eigen::Vector2d > &points1, const std::vector< Eigen::Vector2d > &points2, const Eigen::Matrix3d &E, std::vector< double > *residuals)
Definition: utils.cc:87
Eigen::MatrixXd::Index Index
Definition: knncpp.h:26