small_gicp
downsampling.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 <memory>
6 #include <random>
7 #include <iostream>
8 #include <unordered_map>
12 
13 namespace small_gicp {
14 
22 template <typename InputPointCloud, typename OutputPointCloud = InputPointCloud>
23 std::shared_ptr<OutputPointCloud> voxelgrid_sampling(const InputPointCloud& points, double leaf_size) {
24  if (traits::size(points) == 0) {
25  return std::make_shared<OutputPointCloud>();
26  }
27 
28  const double inv_leaf_size = 1.0 / leaf_size;
29 
30  constexpr std::uint64_t invalid_coord = std::numeric_limits<std::uint64_t>::max();
31  constexpr int coord_bit_size = 21; // Bits to represent each voxel coordinate (pack 21x3=63bits in 64bit int)
32  constexpr size_t coord_bit_mask = (1 << 21) - 1; // Bit mask
33  constexpr int coord_offset = 1 << (coord_bit_size - 1); // Coordinate offset to make values positive
34 
35  std::vector<std::pair<std::uint64_t, size_t>> coord_pt(traits::size(points));
36  for (size_t i = 0; i < traits::size(points); i++) {
37  const Eigen::Array4i coord = fast_floor(traits::point(points, i) * inv_leaf_size) + coord_offset;
38  if ((coord < 0).any() || (coord > coord_bit_mask).any()) {
39  std::cerr << "warning: voxel coord is out of range!!" << std::endl;
40  coord_pt[i] = {invalid_coord, i};
41  continue;
42  }
43 
44  // Compute voxel coord bits (0|1bit, z|21bit, y|21bit, x|21bit)
45  const std::uint64_t bits = //
46  (static_cast<std::uint64_t>(coord[0] & coord_bit_mask) << (coord_bit_size * 0)) | //
47  (static_cast<std::uint64_t>(coord[1] & coord_bit_mask) << (coord_bit_size * 1)) | //
48  (static_cast<std::uint64_t>(coord[2] & coord_bit_mask) << (coord_bit_size * 2));
49  coord_pt[i] = {bits, i};
50  }
51 
52  // Sort by voxel coord
53  const auto compare = [](const auto& lhs, const auto& rhs) { return lhs.first < rhs.first; };
54  std::sort(coord_pt.begin(), coord_pt.end(), compare);
55 
56  auto downsampled = std::make_shared<OutputPointCloud>();
57  traits::resize(*downsampled, traits::size(points));
58 
59  size_t num_points = 0;
60  Eigen::Vector4d sum_pt = traits::point(points, coord_pt.front().second);
61  for (size_t i = 1; i < traits::size(points); i++) {
62  if (coord_pt[i].first == invalid_coord) {
63  continue;
64  }
65 
66  if (coord_pt[i - 1].first != coord_pt[i].first) {
67  traits::set_point(*downsampled, num_points++, sum_pt / sum_pt.w());
68  sum_pt.setZero();
69  }
70 
71  sum_pt += traits::point(points, coord_pt[i].second);
72  }
73 
74  traits::set_point(*downsampled, num_points++, sum_pt / sum_pt.w());
75  traits::resize(*downsampled, num_points);
76 
77  return downsampled;
78 }
79 
84 template <typename InputPointCloud, typename OutputPointCloud = InputPointCloud, typename RNG = std::mt19937>
85 std::shared_ptr<OutputPointCloud> random_sampling(const InputPointCloud& points, size_t num_samples, RNG& rng) {
86  if (traits::size(points) == 0) {
87  std::cerr << "warning: empty input points!!" << std::endl;
88  return std::make_shared<OutputPointCloud>();
89  }
90 
91  std::vector<size_t> indices(traits::size(points));
92  std::iota(indices.begin(), indices.end(), 0);
93 
94  if (num_samples >= indices.size()) {
95  std::cerr << "warning: num_samples >= points.size()!! (" << num_samples << " vs " << traits::size(points) << ")" << std::endl;
96  num_samples = indices.size();
97  }
98 
99  std::vector<size_t> samples(num_samples);
100  std::sample(indices.begin(), indices.end(), samples.begin(), num_samples, rng);
101 
102  auto downsampled = std::make_shared<OutputPointCloud>();
103  traits::resize(*downsampled, num_samples);
104 
105  for (size_t i = 0; i < num_samples; i++) {
106  traits::set_point(*downsampled, i, traits::point(points, samples[i]));
107  }
108 
109  return downsampled;
110 }
111 
112 } // namespace small_gicp
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
void set_point(T &points, size_t i, const Eigen::Vector4d &pt)
Set i-th point. (x, y, z, 1)
Definition: traits.hpp:64
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
std::shared_ptr< OutputPointCloud > voxelgrid_sampling(const InputPointCloud &points, double leaf_size)
Voxelgrid downsampling. This function computes exact average of points in each voxel,...
Definition: downsampling.hpp:23
std::shared_ptr< OutputPointCloud > random_sampling(const InputPointCloud &points, size_t num_samples, RNG &rng)
Random downsampling.
Definition: downsampling.hpp:85
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