// Copyright (c) 2022, ETH Zurich and UNC Chapel Hill. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de) #include "base/essential_matrix.h" #include #include "base/pose.h" #include "estimators/pose.h" namespace colmap { void DecomposeEssentialMatrix(const Eigen::Matrix3d& E, Eigen::Matrix3d* R1, Eigen::Matrix3d* R2, Eigen::Vector3d* t) { Eigen::JacobiSVD svd( E, Eigen::ComputeFullU | Eigen::ComputeFullV); Eigen::Matrix3d U = svd.matrixU(); Eigen::Matrix3d V = svd.matrixV().transpose(); if (U.determinant() < 0) { U *= -1; } if (V.determinant() < 0) { V *= -1; } Eigen::Matrix3d W; W << 0, 1, 0, -1, 0, 0, 0, 0, 1; *R1 = U * W * V; *R2 = U * W.transpose() * V; *t = U.col(2).normalized(); } void PoseFromEssentialMatrix(const Eigen::Matrix3d& E, const std::vector& points1, const std::vector& points2, Eigen::Matrix3d* R, Eigen::Vector3d* t, std::vector* points3D) { CHECK_EQ(points1.size(), points2.size()); Eigen::Matrix3d R1; Eigen::Matrix3d R2; DecomposeEssentialMatrix(E, &R1, &R2, t); // Generate all possible projection matrix combinations. const std::array R_cmbs{{R1, R2, R1, R2}}; const std::array t_cmbs{{*t, *t, -*t, -*t}}; points3D->clear(); for (size_t i = 0; i < R_cmbs.size(); ++i) { std::vector points3D_cmb; CheckCheirality(R_cmbs[i], t_cmbs[i], points1, points2, &points3D_cmb); if (points3D_cmb.size() >= points3D->size()) { *R = R_cmbs[i]; *t = t_cmbs[i]; *points3D = points3D_cmb; } } } Eigen::Matrix3d EssentialMatrixFromPose(const Eigen::Matrix3d& R, const Eigen::Vector3d& t) { return CrossProductMatrix(t.normalized()) * R; } Eigen::Matrix3d EssentialMatrixFromAbsolutePoses( const Eigen::Matrix3x4d& proj_matrix1, const Eigen::Matrix3x4d& proj_matrix2) { const Eigen::Matrix3d R1 = proj_matrix1.leftCols<3>(); const Eigen::Matrix3d R2 = proj_matrix2.leftCols<3>(); const Eigen::Vector3d t1 = proj_matrix1.rightCols<1>(); const Eigen::Vector3d t2 = proj_matrix2.rightCols<1>(); // Relative transformation between to cameras. const Eigen::Matrix3d R = R2 * R1.transpose(); const Eigen::Vector3d t = t2 - R * t1; return EssentialMatrixFromPose(R, t); } void FindOptimalImageObservations(const Eigen::Matrix3d& E, const Eigen::Vector2d& point1, const Eigen::Vector2d& point2, Eigen::Vector2d* optimal_point1, Eigen::Vector2d* optimal_point2) { const Eigen::Vector3d& point1h = point1.homogeneous(); const Eigen::Vector3d& point2h = point2.homogeneous(); Eigen::Matrix S; S << 1, 0, 0, 0, 1, 0; // Epipolar lines. Eigen::Vector2d n1 = S * E * point2h; Eigen::Vector2d n2 = S * E.transpose() * point1h; const Eigen::Matrix2d E_tilde = E.block<2, 2>(0, 0); const double a = n1.transpose() * E_tilde * n2; const double b = (n1.squaredNorm() + n2.squaredNorm()) / 2.0; const double c = point1h.transpose() * E * point2h; const double d = sqrt(b * b - a * c); double lambda = c / (b + d); n1 -= E_tilde * lambda * n1; n2 -= E_tilde.transpose() * lambda * n2; lambda *= (2.0 * d) / (n1.squaredNorm() + n2.squaredNorm()); *optimal_point1 = (point1h - S.transpose() * lambda * n1).hnormalized(); *optimal_point2 = (point2h - S.transpose() * lambda * n2).hnormalized(); } Eigen::Vector3d EpipoleFromEssentialMatrix(const Eigen::Matrix3d& E, const bool left_image) { Eigen::Vector3d e; if (left_image) { Eigen::JacobiSVD svd(E, Eigen::ComputeFullV); e = svd.matrixV().block<3, 1>(0, 2); } else { Eigen::JacobiSVD svd(E.transpose(), Eigen::ComputeFullV); e = svd.matrixV().block<3, 1>(0, 2); } return e; } Eigen::Matrix3d InvertEssentialMatrix(const Eigen::Matrix3d& E) { return E.transpose(); } bool RefineEssentialMatrix(const ceres::Solver::Options& options, const std::vector& points1, const std::vector& points2, const std::vector& inlier_mask, Eigen::Matrix3d* E) { CHECK_EQ(points1.size(), points2.size()); CHECK_EQ(points1.size(), inlier_mask.size()); // Extract inlier points for decomposing the essential matrix into // rotation and translation components. size_t num_inliers = 0; for (const auto inlier : inlier_mask) { if (inlier) { num_inliers += 1; } } std::vector inlier_points1(num_inliers); std::vector inlier_points2(num_inliers); size_t j = 0; for (size_t i = 0; i < inlier_mask.size(); ++i) { if (inlier_mask[i]) { inlier_points1[j] = points1[i]; inlier_points2[j] = points2[i]; j += 1; } } // Extract relative pose from essential matrix. Eigen::Matrix3d R; Eigen::Vector3d tvec; std::vector points3D; PoseFromEssentialMatrix(*E, inlier_points1, inlier_points2, &R, &tvec, &points3D); Eigen::Vector4d qvec = RotationMatrixToQuaternion(R); if (points3D.size() == 0) { return false; } // Refine essential matrix, use all points so that refinement is able to // consider points as inliers that were originally outliers. const bool refinement_success = RefineRelativePose(options, inlier_points1, inlier_points2, &qvec, &tvec); if (!refinement_success) { return false; } // Compose refined essential matrix. const Eigen::Matrix3d rot_mat = QuaternionToRotationMatrix(qvec); *E = EssentialMatrixFromPose(rot_mat, tvec); return true; } } // namespace colmap