// ---------------------------------------------------------------------------- // - Open3D: www.open3d.org - // ---------------------------------------------------------------------------- // The MIT License (MIT) // // Copyright (c) 2018 www.open3d.org // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // ---------------------------------------------------------------------------- #include "Open3D/Registration/TransformationEstimation.h" #include #include "Open3D/Geometry/PointCloud.h" #include "Open3D/Utility/Eigen.h" namespace open3d { namespace registration { double TransformationEstimationPointToPoint::ComputeRMSE( const geometry::PointCloud &source, const geometry::PointCloud &target, const CorrespondenceSet &corres) const { if (corres.empty()) return 0.0; double err = 0.0; for (const auto &c : corres) { err += (source.points_[c[0]] - target.points_[c[1]]).squaredNorm(); } return std::sqrt(err / (double)corres.size()); } Eigen::Matrix4d TransformationEstimationPointToPoint::ComputeTransformation( const geometry::PointCloud &source, const geometry::PointCloud &target, const CorrespondenceSet &corres) const { if (corres.empty()) return Eigen::Matrix4d::Identity(); Eigen::MatrixXd source_mat(3, corres.size()); Eigen::MatrixXd target_mat(3, corres.size()); for (size_t i = 0; i < corres.size(); i++) { source_mat.block<3, 1>(0, i) = source.points_[corres[i][0]]; target_mat.block<3, 1>(0, i) = target.points_[corres[i][1]]; } return Eigen::umeyama(source_mat, target_mat, with_scaling_); } double TransformationEstimationPointToPlane::ComputeRMSE( const geometry::PointCloud &source, const geometry::PointCloud &target, const CorrespondenceSet &corres) const { if (corres.empty() || target.HasNormals() == false) return 0.0; double err = 0.0, r; for (const auto &c : corres) { r = (source.points_[c[0]] - target.points_[c[1]]) .dot(target.normals_[c[1]]); err += r * r; } return std::sqrt(err / (double)corres.size()); } Eigen::Matrix4d TransformationEstimationPointToPlane::ComputeTransformation( const geometry::PointCloud &source, const geometry::PointCloud &target, const CorrespondenceSet &corres) const { if (corres.empty() || target.HasNormals() == false) return Eigen::Matrix4d::Identity(); auto compute_jacobian_and_residual = [&](int i, Eigen::Vector6d &J_r, double &r) { const Eigen::Vector3d &vs = source.points_[corres[i][0]]; const Eigen::Vector3d &vt = target.points_[corres[i][1]]; const Eigen::Vector3d &nt = target.normals_[corres[i][1]]; r = (vs - vt).dot(nt); J_r.block<3, 1>(0, 0) = vs.cross(nt); J_r.block<3, 1>(3, 0) = nt; }; Eigen::Matrix6d JTJ; Eigen::Vector6d JTr; double r2; std::tie(JTJ, JTr, r2) = utility::ComputeJTJandJTr( compute_jacobian_and_residual, (int)corres.size()); bool is_success; Eigen::Matrix4d extrinsic; std::tie(is_success, extrinsic) = utility::SolveJacobianSystemAndObtainExtrinsicMatrix(JTJ, JTr); return is_success ? extrinsic : Eigen::Matrix4d::Identity(); } } // namespace registration } // namespace open3d