ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
GeneralizedICP.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 // @author Ignacio Vizzo [ivizzo@uni-bonn.de]
8 //
9 // Copyright (c) 2021 Ignacio Vizzo, Cyrill Stachniss, University of Bonn.
10 // ----------------------------------------------------------------------------
11 
13 
14 #include <Eigen.h>
15 #include <Logging.h>
16 #include <ecvKDTreeSearchParam.h>
17 #include <ecvPointCloud.h>
18 
19 #include <Eigen/Dense>
20 #include <unsupported/Eigen/MatrixFunctions>
21 
22 namespace cloudViewer {
23 namespace pipelines {
24 namespace registration {
25 
26 namespace {
27 
30 inline Eigen::Matrix3d GetRotationFromE1ToX(const Eigen::Vector3d &x) {
31  const Eigen::Vector3d e1{1, 0, 0};
32  const Eigen::Vector3d v = e1.cross(x);
33  const double c = e1.dot(x);
34  if (c < -0.99) {
35  // Then means that x and e1 are in the same direction
36  return Eigen::Matrix3d::Identity();
37  }
38 
39  const Eigen::Matrix3d sv = utility::SkewMatrix(v);
40  const double factor = 1 / (1 + c);
41  return Eigen::Matrix3d::Identity() + sv + (sv * sv) * factor;
42 }
43 
49 std::shared_ptr<ccPointCloud> InitializePointCloudForGeneralizedICP(
50  const ccPointCloud &pcd, double epsilon) {
51  auto output = std::make_shared<ccPointCloud>(pcd);
52  if (output->HasCovariances()) {
53  utility::LogDebug("GeneralizedICP: Using pre-computed covariances.");
54  return output;
55  }
56  if (output->hasNormals()) {
57  utility::LogDebug("GeneralizedICP: Computing covariances from normals");
58  } else {
59  // Compute covariances the same way is done in the original GICP paper.
60  utility::LogDebug("GeneralizedICP: Computing covariances from points.");
61  output->EstimateNormals(
63  }
64 
65  output->covariances_.resize(output->size());
66  const Eigen::Matrix3d C = Eigen::Vector3d(epsilon, 1, 1).asDiagonal();
67 #pragma omp parallel for schedule(static)
68  for (int i = 0; i < (int)output->size(); i++) {
69  const auto Rx = GetRotationFromE1ToX(output->getEigenNormal(i));
70  output->covariances_[i] = Rx * C * Rx.transpose();
71  }
72  return output;
73 }
74 } // namespace
75 
77  const ccPointCloud &source,
78  const ccPointCloud &target,
79  const CorrespondenceSet &corres) const {
80  if (corres.empty()) {
81  return 0.0;
82  }
83  double err = 0.0;
84  for (const auto &c : corres) {
85  const Eigen::Vector3d vs = source.getEigenPoint(c[0]);
86  const Eigen::Matrix3d &Cs = source.covariances_[c[0]];
87  const Eigen::Vector3d vt = target.getEigenPoint(c[1]);
88  const Eigen::Matrix3d &Ct = target.covariances_[c[1]];
89  const Eigen::Vector3d d = vs - vt;
90  const Eigen::Matrix3d M = Ct + Cs;
91  const Eigen::Matrix3d W = M.inverse().sqrt();
92  err += d.transpose() * W * d;
93  }
94  return std::sqrt(err / (double)corres.size());
95 }
96 
97 Eigen::Matrix4d
99  const ccPointCloud &source,
100  const ccPointCloud &target,
101  const CorrespondenceSet &corres) const {
102  if (corres.empty() || !target.HasCovariances() ||
103  !source.HasCovariances()) {
104  return Eigen::Matrix4d::Identity();
105  }
106 
107  auto compute_jacobian_and_residual =
108  [&](int i,
109  std::vector<Eigen::Vector6d, utility::Vector6d_allocator> &J_r,
110  std::vector<double> &r, std::vector<double> &w) {
111  const Eigen::Vector3d vs = source.getEigenPoint(corres[i][0]);
112  const Eigen::Matrix3d &Cs = source.covariances_[corres[i][0]];
113  const Eigen::Vector3d vt = target.getEigenPoint(corres[i][1]);
114  const Eigen::Matrix3d &Ct = target.covariances_[corres[i][1]];
115  const Eigen::Vector3d d = vs - vt;
116  const Eigen::Matrix3d M = Ct + Cs;
117  const Eigen::Matrix3d W = M.inverse().sqrt();
118 
119  Eigen::Matrix<double, 3, 6> J;
120  J.block<3, 3>(0, 0) = -utility::SkewMatrix(vs);
121  J.block<3, 3>(0, 3) = Eigen::Matrix3d::Identity();
122  J = W * J;
123 
124  constexpr int n_rows = 3;
125  J_r.resize(n_rows);
126  r.resize(n_rows);
127  w.resize(n_rows);
128  for (size_t i = 0; i < n_rows; ++i) {
129  r[i] = W.row(i).dot(d);
130  w[i] = kernel_->Weight(r[i]);
131  J_r[i] = J.row(i);
132  }
133  };
134 
135  Eigen::Matrix6d JTJ;
136  Eigen::Vector6d JTr;
137  double r2 = -1.0;
138  std::tie(JTJ, JTr, r2) =
139  utility::ComputeJTJandJTr<Eigen::Matrix6d, Eigen::Vector6d>(
140  compute_jacobian_and_residual, (int)corres.size());
141 
142  bool is_success = false;
143  Eigen::Matrix4d extrinsic;
144  std::tie(is_success, extrinsic) =
146 
147  return is_success ? extrinsic : Eigen::Matrix4d::Identity();
148 }
149 
151  const ccPointCloud &source,
152  const ccPointCloud &target,
154  const Eigen::Matrix4d &init /* = Eigen::Matrix4d::Identity()*/,
156  &estimation /* = TransformationEstimationForGeneralizedICP()*/,
158  &criteria /* = ICPConvergenceCriteria()*/) {
159  return RegistrationICP(
160  *InitializePointCloudForGeneralizedICP(source, estimation.epsilon_),
161  *InitializePointCloudForGeneralizedICP(target, estimation.epsilon_),
162  max_correspondence_distance, init, estimation, criteria);
163 }
164 
165 } // namespace registration
166 } // namespace pipelines
167 } // namespace cloudViewer
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool HasCovariances() const
Returns 'true' if the point cloud contains per-point covariance matrix.
std::vector< Eigen::Matrix3d > covariances_
Covariance Matrix for each point.
Eigen::Vector3d getEigenPoint(size_t index) const
KDTree search parameters for pure KNN search.
Class that defines the convergence criteria of ICP.
Definition: Registration.h:36
double ComputeRMSE(const ccPointCloud &source, const ccPointCloud &target, const CorrespondenceSet &corres) const override
std::shared_ptr< RobustKernel > kernel_
shared_ptr to an Abstract RobustKernel that could mutate at runtime.
Eigen::Matrix4d ComputeTransformation(const ccPointCloud &source, const ccPointCloud &target, const CorrespondenceSet &corres) const override
#define LogDebug(...)
Definition: Logging.h:90
Eigen::Matrix< double, 6, 1 > Vector6d
Definition: Eigen.h:107
Eigen::Matrix< double, 6, 6 > Matrix6d
Extending Eigen namespace by adding frequently used matrix type.
Definition: Eigen.h:106
RegistrationResult RegistrationGeneralizedICP(const ccPointCloud &source, const ccPointCloud &target, double max_correspondence_distance, const Eigen::Matrix4d &init, const TransformationEstimationForGeneralizedICP &estimation, const ICPConvergenceCriteria &criteria)
Function for Generalized ICP registration.
std::vector< Eigen::Vector2i > CorrespondenceSet
RegistrationResult RegistrationICP(const ccPointCloud &source, const ccPointCloud &target, double max_correspondence_distance, const Eigen::Matrix4d &init, const TransformationEstimation &estimation, const ICPConvergenceCriteria &criteria)
Functions for ICP registration.
static const double max_correspondence_distance
Eigen::Matrix3d SkewMatrix(const Eigen::Vector3d &vec)
Genretate a skew-symmetric matrix from a vector 3x1.
Definition: Eigen.cpp:414
std::tuple< bool, Eigen::Matrix4d > SolveJacobianSystemAndObtainExtrinsicMatrix(const Eigen::Matrix6d &JTJ, const Eigen::Vector6d &JTr)
Definition: Eigen.cpp:105
Generic file read and write utility for python interface.