small_gicp
incremental_voxelmap.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 <vector>
6 #include <unordered_map>
7 
8 #include <Eigen/Core>
9 #include <Eigen/Geometry>
10 
17 
18 namespace small_gicp {
19 
21 struct VoxelInfo {
22 public:
26  VoxelInfo(const Eigen::Vector3i& coord, size_t lru) : lru(lru), coord(coord) {}
27 
28 public:
29  size_t lru;
30  Eigen::Vector3i coord;
31 };
32 
37 template <typename VoxelContents>
39 public:
40  using Ptr = std::shared_ptr<IncrementalVoxelMap>;
41  using ConstPtr = std::shared_ptr<const IncrementalVoxelMap>;
42 
45  explicit IncrementalVoxelMap(double leaf_size) : inv_leaf_size(1.0 / leaf_size), lru_horizon(100), lru_clear_cycle(10), lru_counter(0) { set_search_offsets(1); }
46 
48  size_t size() const { return flat_voxels.size(); }
49 
53  template <typename PointCloud>
54  void insert(const PointCloud& points, const Eigen::Isometry3d& T = Eigen::Isometry3d::Identity()) {
55  // Insert points to the voxelmap
56  for (size_t i = 0; i < traits::size(points); i++) {
57  const Eigen::Vector4d pt = T * traits::point(points, i);
58  const Eigen::Vector3i coord = fast_floor(pt * inv_leaf_size).template head<3>();
59 
60  auto found = voxels.find(coord);
61  if (found == voxels.end()) {
62  auto voxel = std::make_shared<std::pair<VoxelInfo, VoxelContents>>(VoxelInfo(coord, lru_counter), VoxelContents());
63 
64  found = voxels.emplace_hint(found, coord, flat_voxels.size());
65  flat_voxels.emplace_back(voxel);
66  }
67 
68  auto& [info, voxel] = *flat_voxels[found->second];
69  info.lru = lru_counter;
70  voxel.add(voxel_setting, pt, points, i, T);
71  }
72 
73  if ((++lru_counter) % lru_clear_cycle == 0) {
74  // Remove least recently used voxels
75  auto remove_counter = std::remove_if(flat_voxels.begin(), flat_voxels.end(), [&](const std::shared_ptr<std::pair<VoxelInfo, VoxelContents>>& voxel) {
76  return voxel->first.lru + lru_horizon < lru_counter;
77  });
78  flat_voxels.erase(remove_counter, flat_voxels.end());
79 
80  // Rehash
81  voxels.clear();
82  for (size_t i = 0; i < flat_voxels.size(); i++) {
83  voxels[flat_voxels[i]->first.coord] = i;
84  }
85  }
86 
87  // Finalize voxel means and covs
88  for (auto& voxel : flat_voxels) {
89  voxel->second.finalize();
90  }
91  }
92 
98  size_t nearest_neighbor_search(const Eigen::Vector4d& pt, size_t* index, double* sq_dist) const {
99  const Eigen::Vector3i center = fast_floor(pt * inv_leaf_size).template head<3>();
100  size_t voxel_index = 0;
101  const auto index_transform = [&](size_t i) { return calc_index(voxel_index, i); };
102  KnnResult<1, decltype(index_transform)> result(index, sq_dist, -1, index_transform);
103 
104  for (const auto& offset : search_offsets) {
105  const Eigen::Vector3i coord = center + offset;
106  const auto found = voxels.find(coord);
107  if (found == voxels.end()) {
108  continue;
109  }
110 
111  voxel_index = found->second;
112  const auto& voxel = flat_voxels[voxel_index]->second;
113 
115  }
116 
117  return result.num_found();
118  }
119 
126  size_t knn_search(const Eigen::Vector4d& pt, size_t k, size_t* k_indices, double* k_sq_dists) const {
127  const Eigen::Vector3i center = fast_floor(pt * inv_leaf_size).template head<3>();
128 
129  size_t voxel_index = 0;
130  const auto index_transform = [&](size_t i) { return calc_index(voxel_index, i); };
131  KnnResult<-1, decltype(index_transform)> result(k_indices, k_sq_dists, k, index_transform);
132 
133  for (const auto& offset : search_offsets) {
134  const Eigen::Vector3i coord = center + offset;
135  const auto found = voxels.find(coord);
136  if (found == voxels.end()) {
137  continue;
138  }
139 
140  voxel_index = found->second;
141  const auto& voxel = flat_voxels[voxel_index]->second;
142 
144  }
145 
146  return result.num_found();
147  }
148 
150  inline size_t calc_index(const size_t voxel_id, const size_t point_id) const { return (voxel_id << point_id_bits) | point_id; }
151  inline size_t voxel_id(const size_t i) const { return i >> point_id_bits; }
152  inline size_t point_id(const size_t i) const { return i & ((1ull << point_id_bits) - 1); }
153 
156  void set_search_offsets(int num_offsets) {
157  switch (num_offsets) {
158  default:
159  std::cerr << "warning: unsupported search_offsets=" << num_offsets << " (supported values: 1, 7, 27)" << std::endl;
160  std::cerr << " : using default search_offsets=1" << std::endl;
161  [[fallthrough]];
162  case 1:
163  search_offsets = {Eigen::Vector3i(0, 0, 0)};
164  break;
165  case 7:
166  search_offsets = {
167  Eigen::Vector3i(0, 0, 0),
168  Eigen::Vector3i(1, 0, 0),
169  Eigen::Vector3i(0, 1, 0),
170  Eigen::Vector3i(0, 0, 1),
171  Eigen::Vector3i(-1, 0, 0),
172  Eigen::Vector3i(0, -1, 0),
173  Eigen::Vector3i(0, 0, -1)};
174  break;
175  case 27:
176  for (int i = -1; i <= 1; i++) {
177  for (int j = -1; j <= 1; j++) {
178  for (int k = -1; k <= 1; k++) {
179  search_offsets.emplace_back(i, j, k);
180  }
181  }
182  }
183  break;
184  }
185  }
186 
187 public:
188  static_assert(sizeof(size_t) == 8, "size_t must be 64-bit");
189  static constexpr int point_id_bits = 32;
190  static constexpr int voxel_id_bits = 64 - point_id_bits;
191  const double inv_leaf_size;
192 
193  size_t lru_horizon;
195  size_t lru_counter;
196 
197  std::vector<Eigen::Vector3i> search_offsets;
198 
199  typename VoxelContents::Setting voxel_setting;
200  std::vector<std::shared_ptr<std::pair<VoxelInfo, VoxelContents>>> flat_voxels;
201  std::unordered_map<Eigen::Vector3i, size_t, XORVector3iHash> voxels;
202 };
203 
204 namespace traits {
205 
206 template <typename VoxelContents>
207 struct Traits<IncrementalVoxelMap<VoxelContents>> {
208  static size_t size(const IncrementalVoxelMap<VoxelContents>& voxelmap) { return voxelmap.size(); }
209 
210  static Eigen::Vector4d point(const IncrementalVoxelMap<VoxelContents>& voxelmap, size_t i) {
211  const size_t voxel_id = voxelmap.voxel_id(i);
212  const size_t point_id = voxelmap.point_id(i);
213  const auto& voxel = voxelmap.flat_voxels[voxel_id]->second;
214  return traits::point(voxel, point_id);
215  }
216  static Eigen::Vector4d normal(const IncrementalVoxelMap<VoxelContents>& voxelmap, size_t i) {
217  const size_t voxel_id = voxelmap.voxel_id(i);
218  const size_t point_id = voxelmap.point_id(i);
219  const auto& voxel = voxelmap.flat_voxels[voxel_id]->second;
220  return traits::normal(voxel, point_id);
221  }
222  static Eigen::Matrix4d cov(const IncrementalVoxelMap<VoxelContents>& voxelmap, size_t i) {
223  const size_t voxel_id = voxelmap.voxel_id(i);
224  const size_t point_id = voxelmap.point_id(i);
225  const auto& voxel = voxelmap.flat_voxels[voxel_id]->second;
226  return traits::cov(voxel, point_id);
227  }
228 
229  static size_t nearest_neighbor_search(const IncrementalVoxelMap<VoxelContents>& voxelmap, const Eigen::Vector4d& pt, size_t* k_index, double* k_sq_dist) {
230  return voxelmap.nearest_neighbor_search(pt, k_index, k_sq_dist);
231  }
232 
233  static size_t knn_search(const IncrementalVoxelMap<VoxelContents>& voxelmap, const Eigen::Vector4d& pt, int k, size_t* k_index, double* k_sq_dist) {
234  return voxelmap.knn_search(pt, k, k_index, k_sq_dist);
235  }
236 };
237 
238 template <typename VoxelContents>
239 std::vector<size_t> point_indices(const IncrementalVoxelMap<VoxelContents>& voxelmap) {
240  std::vector<size_t> indices;
241  indices.reserve(voxelmap.size() * 5);
242 
243  for (size_t voxel_id = 0; voxel_id < voxelmap.flat_voxels.size(); voxel_id++) {
244  const auto& voxel = voxelmap.flat_voxels[voxel_id];
245  for (size_t point_id = 0; point_id < traits::size(voxel->second); point_id++) {
246  indices.emplace_back(voxelmap.calc_index(voxel_id, point_id));
247  }
248  }
249 
250  return indices;
251 }
252 
253 template <typename VoxelContents>
254 std::vector<Eigen::Vector4d> voxel_points(const IncrementalVoxelMap<VoxelContents>& voxelmap) {
255  std::vector<Eigen::Vector4d> points;
256  points.reserve(voxelmap.size() * 5);
257 
258  for (const auto& voxel : voxelmap.flat_voxels) {
259  for (size_t i = 0; i < traits::size(voxel->second); i++) {
260  points.push_back(traits::point(voxel->second, i));
261  }
262  }
263  return points;
264 }
265 
266 template <typename VoxelContents>
267 std::vector<Eigen::Vector4d> voxel_normals(const IncrementalVoxelMap<VoxelContents>& voxelmap) {
268  std::vector<Eigen::Vector4d> normals;
269  normals.reserve(voxelmap.size() * 5);
270 
271  for (const auto& voxel : voxelmap.flat_voxels) {
272  for (size_t i = 0; i < traits::size(voxel->second); i++) {
273  normals.push_back(traits::normal(voxel->second, i));
274  }
275  }
276  return normals;
277 }
278 
279 template <typename VoxelContents>
280 std::vector<Eigen::Matrix4d> voxel_covs(const IncrementalVoxelMap<VoxelContents>& voxelmap) {
281  std::vector<Eigen::Matrix4d> covs;
282  covs.reserve(voxelmap.size() * 5);
283 
284  for (const auto& voxel : voxelmap.flat_voxels) {
285  for (size_t i = 0; i < traits::size(voxel->second); i++) {
286  covs.push_back(traits::cov(voxel->second, i));
287  }
288  }
289  return covs;
290 }
291 
292 } // namespace traits
293 
294 } // namespace small_gicp
std::vector< Eigen::Matrix4d > voxel_covs(const IncrementalVoxelMap< VoxelContents > &voxelmap)
Definition: incremental_voxelmap.hpp:280
std::vector< Eigen::Vector4d > voxel_points(const IncrementalVoxelMap< VoxelContents > &voxelmap)
Definition: incremental_voxelmap.hpp:254
size_t size(const T &points)
Get the number of points.
Definition: traits.hpp:16
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
std::vector< Eigen::Vector4d > voxel_normals(const IncrementalVoxelMap< VoxelContents > &voxelmap)
Definition: incremental_voxelmap.hpp:267
std::vector< size_t > point_indices(const IncrementalVoxelMap< VoxelContents > &voxelmap)
Definition: incremental_voxelmap.hpp:239
size_t knn_search(const T &tree, const Eigen::Vector4d &point, size_t k, size_t *k_indices, double *k_sq_dists)
Find k-nearest neighbors.
Definition: traits.hpp:23
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
Definition: flat_container.hpp:12
Eigen::Array4i fast_floor(const Eigen::Array4d &pt)
Fast floor (https://stackoverflow.com/questions/824118/why-is-floor-so-slow).
Definition: fast_floor.hpp:12
Incremental voxelmap. This class supports incremental point cloud insertion and LRU-based voxel delet...
Definition: incremental_voxelmap.hpp:38
std::vector< std::shared_ptr< std::pair< VoxelInfo, VoxelContents > > > flat_voxels
Voxel contents.
Definition: incremental_voxelmap.hpp:200
static constexpr int voxel_id_bits
Use the remaining bits for voxel id.
Definition: incremental_voxelmap.hpp:190
void insert(const PointCloud &points, const Eigen::Isometry3d &T=Eigen::Isometry3d::Identity())
Insert points to the voxelmap.
Definition: incremental_voxelmap.hpp:54
size_t lru_clear_cycle
LRU clear cycle. Voxel deletion is performed every lru_clear_cycle steps.
Definition: incremental_voxelmap.hpp:194
size_t point_id(const size_t i) const
Extract the voxel ID from a global index.
Definition: incremental_voxelmap.hpp:152
size_t voxel_id(const size_t i) const
Extract the point ID from a global index.
Definition: incremental_voxelmap.hpp:151
size_t nearest_neighbor_search(const Eigen::Vector4d &pt, size_t *index, double *sq_dist) const
Find the nearest neighbor.
Definition: incremental_voxelmap.hpp:98
void set_search_offsets(int num_offsets)
Set the pattern of the search offsets. (Must be 1, 7, or 27)
Definition: incremental_voxelmap.hpp:156
std::shared_ptr< const IncrementalVoxelMap > ConstPtr
Definition: incremental_voxelmap.hpp:41
size_t calc_index(const size_t voxel_id, const size_t point_id) const
Calculate the global point index from the voxel index and the point index.
Definition: incremental_voxelmap.hpp:150
size_t size() const
Number of points in the voxelmap.
Definition: incremental_voxelmap.hpp:48
VoxelContents::Setting voxel_setting
Voxel setting.
Definition: incremental_voxelmap.hpp:199
size_t lru_counter
LRU counter. Incremented every step.
Definition: incremental_voxelmap.hpp:195
std::shared_ptr< IncrementalVoxelMap > Ptr
Definition: incremental_voxelmap.hpp:40
size_t knn_search(const Eigen::Vector4d &pt, size_t k, size_t *k_indices, double *k_sq_dists) const
Find k nearest neighbors.
Definition: incremental_voxelmap.hpp:126
IncrementalVoxelMap(double leaf_size)
Constructor.
Definition: incremental_voxelmap.hpp:45
size_t lru_horizon
LRU horizon size. Voxels that have not been accessed for lru_horizon steps are deleted.
Definition: incremental_voxelmap.hpp:193
static constexpr int point_id_bits
Use the first 32 bits for point id.
Definition: incremental_voxelmap.hpp:189
std::unordered_map< Eigen::Vector3i, size_t, XORVector3iHash > voxels
Voxel index map.
Definition: incremental_voxelmap.hpp:201
const double inv_leaf_size
Inverse of the voxel size.
Definition: incremental_voxelmap.hpp:191
std::vector< Eigen::Vector3i > search_offsets
Voxel search offsets.
Definition: incremental_voxelmap.hpp:197
K-nearest neighbor search result container.
Definition: knn_result.hpp:33
size_t num_found() const
Number of found neighbors.
Definition: knn_result.hpp:73
Point cloud.
Definition: point_cloud.hpp:15
Voxel meta information.
Definition: incremental_voxelmap.hpp:21
size_t lru
Last used time.
Definition: incremental_voxelmap.hpp:29
Eigen::Vector3i coord
Voxel coordinate.
Definition: incremental_voxelmap.hpp:30
VoxelInfo(const Eigen::Vector3i &coord, size_t lru)
Constructor.
Definition: incremental_voxelmap.hpp:26
static Eigen::Vector4d point(const IncrementalVoxelMap< VoxelContents > &voxelmap, size_t i)
Definition: incremental_voxelmap.hpp:210
static Eigen::Vector4d normal(const IncrementalVoxelMap< VoxelContents > &voxelmap, size_t i)
Definition: incremental_voxelmap.hpp:216
static Eigen::Matrix4d cov(const IncrementalVoxelMap< VoxelContents > &voxelmap, size_t i)
Definition: incremental_voxelmap.hpp:222
static size_t size(const IncrementalVoxelMap< VoxelContents > &voxelmap)
Definition: incremental_voxelmap.hpp:208
static size_t knn_search(const IncrementalVoxelMap< VoxelContents > &voxelmap, const Eigen::Vector4d &pt, int k, size_t *k_index, double *k_sq_dist)
Definition: incremental_voxelmap.hpp:233
static size_t nearest_neighbor_search(const IncrementalVoxelMap< VoxelContents > &voxelmap, const Eigen::Vector4d &pt, size_t *k_index, double *k_sq_dist)
Definition: incremental_voxelmap.hpp:229
Definition: traits.hpp:13