small_gicp
normal_estimation.hpp
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright 2024 Kenji Koide
2 // SPDX-License-Identifier: MIT
3 #pragma once
4 
5 #include <Eigen/Eigen>
7 
8 namespace small_gicp {
9 
12 template <typename PointCloud>
13 struct NormalSetter {
15  static void set_invalid(PointCloud& cloud, size_t i) { traits::set_normal(cloud, i, Eigen::Vector4d::Zero()); }
16 
18  static void set(PointCloud& cloud, size_t i, const Eigen::Matrix3d& eigenvectors) {
19  const Eigen::Vector4d normal = (Eigen::Vector4d() << eigenvectors.col(0).normalized(), 0.0).finished();
20  if (traits::point(cloud, i).dot(normal) > 0) {
21  traits::set_normal(cloud, i, -normal);
22  } else {
23  traits::set_normal(cloud, i, normal);
24  }
25  }
26 };
27 
30 template <typename PointCloud>
33  static void set_invalid(PointCloud& cloud, size_t i) {
34  Eigen::Matrix4d cov = Eigen::Matrix4d::Identity();
35  cov(3, 3) = 0.0;
36  traits::set_cov(cloud, i, cov);
37  }
38 
40  static void set(PointCloud& cloud, size_t i, const Eigen::Matrix3d& eigenvectors) {
41  const Eigen::Vector3d values(1e-3, 1.0, 1.0);
42  Eigen::Matrix4d cov = Eigen::Matrix4d::Zero();
43  cov.block<3, 3>(0, 0) = eigenvectors * values.asDiagonal() * eigenvectors.transpose();
44  traits::set_cov(cloud, i, cov);
45  }
46 };
47 
50 template <typename PointCloud>
53  static void set_invalid(PointCloud& cloud, size_t i) {
56  }
57 
59  static void set(PointCloud& cloud, size_t i, const Eigen::Matrix3d& eigenvectors) {
60  NormalSetter<PointCloud>::set(cloud, i, eigenvectors);
61  CovarianceSetter<PointCloud>::set(cloud, i, eigenvectors);
62  }
63 };
64 
65 template <typename Setter, typename PointCloud, typename Tree>
66 void estimate_local_features(PointCloud& cloud, Tree& kdtree, int num_neighbors, size_t point_index) {
67  std::vector<size_t> k_indices(num_neighbors);
68  std::vector<double> k_sq_dists(num_neighbors);
69  const size_t n = kdtree.knn_search(traits::point(cloud, point_index), num_neighbors, k_indices.data(), k_sq_dists.data());
70 
71  if (n < 5) {
72  // Insufficient number of neighbors
73  Setter::set_invalid(cloud, point_index);
74  return;
75  }
76 
77  Eigen::Vector4d sum_points = Eigen::Vector4d::Zero();
78  Eigen::Matrix4d sum_cross = Eigen::Matrix4d::Zero();
79  for (size_t i = 0; i < n; i++) {
80  const auto& pt = traits::point(cloud, k_indices[i]);
81  sum_points += pt;
82  sum_cross += pt * pt.transpose();
83  }
84 
85  const Eigen::Vector4d mean = sum_points / n;
86  const Eigen::Matrix4d cov = (sum_cross - mean * sum_points.transpose()) / n;
87 
88  Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eig;
89  eig.computeDirect(cov.block<3, 3>(0, 0));
90 
91  Setter::set(cloud, point_index, eig.eigenvectors());
92 }
93 
94 template <typename Setter, typename PointCloud, typename KdTree>
95 void estimate_local_features(PointCloud& cloud, const KdTree& kdtree, int num_neighbors) {
96  traits::resize(cloud, traits::size(cloud));
97  for (size_t i = 0; i < traits::size(cloud); i++) {
98  estimate_local_features<Setter>(cloud, kdtree, num_neighbors, i);
99  }
100 }
101 
102 template <typename Setter, typename PointCloud>
103 void estimate_local_features(PointCloud& cloud, int num_neighbors) {
104  traits::resize(cloud, traits::size(cloud));
105 
106  UnsafeKdTree<PointCloud> kdtree(cloud);
107  for (size_t i = 0; i < traits::size(cloud); i++) {
108  estimate_local_features<Setter>(cloud, kdtree, num_neighbors, i);
109  }
110 }
111 
117 template <typename PointCloud>
118 void estimate_normals(PointCloud& cloud, int num_neighbors = 20) {
119  estimate_local_features<NormalSetter<PointCloud>>(cloud, num_neighbors);
120 }
121 
128 template <typename PointCloud, typename KdTree>
129 void estimate_normals(PointCloud& cloud, KdTree& kdtree, int num_neighbors = 20) {
130  estimate_local_features<NormalSetter<PointCloud>>(cloud, kdtree, num_neighbors);
131 }
132 
138 template <typename PointCloud>
139 void estimate_covariances(PointCloud& cloud, int num_neighbors = 20) {
140  estimate_local_features<CovarianceSetter<PointCloud>>(cloud, num_neighbors);
141 }
142 
149 template <typename PointCloud, typename KdTree>
150 void estimate_covariances(PointCloud& cloud, KdTree& kdtree, int num_neighbors = 20) {
151  estimate_local_features<CovarianceSetter<PointCloud>>(cloud, kdtree, num_neighbors);
152 }
153 
159 template <typename PointCloud>
160 void estimate_normals_covariances(PointCloud& cloud, int num_neighbors = 20) {
161  estimate_local_features<NormalCovarianceSetter<PointCloud>>(cloud, num_neighbors);
162 }
163 
170 template <typename PointCloud, typename KdTree>
171 void estimate_normals_covariances(PointCloud& cloud, KdTree& kdtree, int num_neighbors = 20) {
172  estimate_local_features<NormalCovarianceSetter<PointCloud>>(cloud, kdtree, num_neighbors);
173 }
174 
175 } // namespace small_gicp
size_t size(const T &points)
Get the number of points.
Definition: traits.hpp:16
void set_cov(T &points, size_t i, const Eigen::Matrix4d &cov)
Set i-th covariance. Only the top-left 3x3 matrix should be filled.
Definition: traits.hpp:76
void set_normal(T &points, size_t i, const Eigen::Vector4d &pt)
Set i-th normal. (nx, nz, nz, 0)
Definition: traits.hpp:70
auto point(const T &points, size_t i)
Get i-th point. 4D vector is used to take advantage of SIMD intrinsics. The last element must be fill...
Definition: traits.hpp:40
auto cov(const T &points, size_t i)
Get i-th covariance. Only the top-left 3x3 matrix is filled, and the bottom row and the right col mus...
Definition: traits.hpp:52
auto normal(const T &points, size_t i)
Get i-th normal. 4D vector is used to take advantage of SIMD intrinsics. The last element must be fil...
Definition: traits.hpp:46
void resize(T &points, size_t n)
Resize the point cloud (this function should resize all attributes)
Definition: traits.hpp:58
Definition: flat_container.hpp:12
void estimate_normals(PointCloud &cloud, int num_neighbors=20)
Estimate point normals.
Definition: normal_estimation.hpp:118
void estimate_normals_covariances(PointCloud &cloud, int num_neighbors=20)
Estimate point normals and covariances.
Definition: normal_estimation.hpp:160
void estimate_covariances(PointCloud &cloud, int num_neighbors=20)
Estimate point covariances.
Definition: normal_estimation.hpp:139
void estimate_local_features(PointCloud &cloud, Tree &kdtree, int num_neighbors, size_t point_index)
Definition: normal_estimation.hpp:66
Computes point covariances from eigenvectors and sets them to the point cloud.
Definition: normal_estimation.hpp:31
static void set(PointCloud &cloud, size_t i, const Eigen::Matrix3d &eigenvectors)
Compute and set the covariance to the point cloud.
Definition: normal_estimation.hpp:40
static void set_invalid(PointCloud &cloud, size_t i)
Handle invalid case (too few points).
Definition: normal_estimation.hpp:33
"Safe" KdTree that holds the ownership of the input points.
Definition: kdtree.hpp:245
Computes point normals and covariances from eigenvectors and sets them to the point cloud.
Definition: normal_estimation.hpp:51
static void set(PointCloud &cloud, size_t i, const Eigen::Matrix3d &eigenvectors)
Compute and set the normal and covariance to the point cloud.
Definition: normal_estimation.hpp:59
static void set_invalid(PointCloud &cloud, size_t i)
Handle invalid case (too few points).
Definition: normal_estimation.hpp:53
Computes point normals from eigenvectors and sets them to the point cloud.
Definition: normal_estimation.hpp:13
static void set_invalid(PointCloud &cloud, size_t i)
Handle invalid case (too few points).
Definition: normal_estimation.hpp:15
static void set(PointCloud &cloud, size_t i, const Eigen::Matrix3d &eigenvectors)
Compute and set the normal to the point cloud.
Definition: normal_estimation.hpp:18
Point cloud.
Definition: point_cloud.hpp:15
"Unsafe" KdTree.
Definition: kdtree.hpp:137