47#include <gtsam_points/config.hpp>
48#include <gtsam_points/ann/knn_result.hpp>
49#include <gtsam_points/types/frame_traits.hpp>
51#ifdef GTSAM_POINTS_USE_TBB
52#include <tbb/parallel_invoke.h>
55namespace gtsam_points {
76 template <
typename Po
intCloud,
typename IndexConstIterator>
79 const size_t N = std::distance(first, last);
80 Eigen::Vector4d sum_pt = Eigen::Vector4d::Zero();
81 Eigen::Vector4d sum_sq = Eigen::Vector4d::Zero();
83 const size_t step = N < setting.max_scan_count ? 1 : N / setting.max_scan_count;
84 const size_t num_steps = N / step;
85 for (
int i = 0; i < num_steps; i++) {
86 const auto itr = first + step * i;
87 const Eigen::Vector4d pt = frame::point(points, *itr);
89 sum_sq += pt.cwiseProduct(pt);
92 const Eigen::Vector4d mean = sum_pt / sum_pt.w();
93 const Eigen::Vector4d var = (sum_sq - mean.cwiseProduct(sum_pt));
95 return AxisAlignedProjection{var[0] > var[1] ? (var[0] > var[2] ? 0 : 2) : (var[1] > var[2] ? 1 : 2)};
102using NodeIndexType = std::uint32_t;
103static constexpr NodeIndexType INVALID_NODE = std::numeric_limits<NodeIndexType>::max();
106template <
typename Projection>
119 NodeIndexType
left = INVALID_NODE;
129 template <
typename KdTree,
typename Po
intCloud>
131 kdtree.indices.resize(frame::size(points));
132 std::iota(kdtree.indices.begin(), kdtree.indices.end(), 0);
134 size_t node_count = 0;
135 kdtree.nodes.resize(frame::size(points));
136 kdtree.root =
create_node(kdtree, node_count, points, kdtree.indices.begin(), kdtree.indices.begin(), kdtree.indices.end());
137 kdtree.nodes.resize(node_count);
145 template <
typename Po
intCloud,
typename KdTree,
typename IndexConstIterator>
150 IndexConstIterator global_first,
151 IndexConstIterator first,
152 IndexConstIterator last)
const {
153 const size_t N = std::distance(first, last);
154 const NodeIndexType node_index = node_count++;
155 auto& node = kdtree.nodes[node_index];
160 node.node_type.lr.first = std::distance(global_first, first);
161 node.node_type.lr.last = std::distance(global_first, last);
167 using Projection =
typename KdTree::Projection;
169 const auto median_itr = first + N / 2;
170 std::nth_element(first, median_itr, last, [&](
size_t i,
size_t j) {
return proj(frame::point(points, i)) < proj(frame::point(points, j)); });
173 node.node_type.sub.proj = proj;
174 node.node_type.sub.thresh = proj(frame::point(points, *median_itr));
177 node.left =
create_node(kdtree, node_count, points, global_first, first, median_itr);
178 node.right =
create_node(kdtree, node_count, points, global_first, median_itr, last);
196 template <
typename KdTree,
typename Po
intCloud>
198 kdtree.indices.resize(frame::size(points));
199 std::iota(kdtree.indices.begin(), kdtree.indices.end(), 0);
201 std::atomic_uint64_t node_count = 0;
202 kdtree.nodes.resize(frame::size(points));
205#pragma omp parallel num_threads(num_threads)
207#pragma omp single nowait
209 kdtree.root =
create_node(kdtree, node_count, points, kdtree.indices.begin(), kdtree.indices.begin(), kdtree.indices.end());
213 kdtree.root =
create_node(kdtree, node_count, points, kdtree.indices.begin(), kdtree.indices.begin(), kdtree.indices.end());
216 kdtree.nodes.resize(node_count);
224 template <
typename Po
intCloud,
typename KdTree,
typename IndexConstIterator>
227 std::atomic_uint64_t& node_count,
229 IndexConstIterator global_first,
230 IndexConstIterator first,
231 IndexConstIterator last)
const {
232 const size_t N = std::distance(first, last);
233 const NodeIndexType node_index = node_count++;
234 auto& node = kdtree.nodes[node_index];
239 node.node_type.lr.first = std::distance(global_first, first);
240 node.node_type.lr.last = std::distance(global_first, last);
246 using Projection =
typename KdTree::Projection;
248 const auto median_itr = first + N / 2;
249 std::nth_element(first, median_itr, last, [&](
size_t i,
size_t j) {
return proj(frame::point(points, i)) < proj(frame::point(points, j)); });
252 node.node_type.sub.proj = proj;
253 node.node_type.sub.thresh = proj(frame::point(points, *median_itr));
257#pragma omp task default(shared) if (N > 512)
258 node.left =
create_node(kdtree, node_count, points, global_first, first, median_itr);
259#pragma omp task default(shared) if (N > 512)
260 node.right =
create_node(kdtree, node_count, points, global_first, median_itr, last);
263 node.left =
create_node(kdtree, node_count, points, global_first, first, median_itr);
264 node.right =
create_node(kdtree, node_count, points, global_first, median_itr, last);
276#ifdef GTSAM_POINTS_USE_TBB
278struct KdTreeBuilderTBB {
281 template <
typename KdTree,
typename Po
intCloud>
283 kdtree.indices.resize(frame::size(points));
284 std::iota(kdtree.indices.begin(), kdtree.indices.end(), 0);
286 std::atomic_uint64_t node_count = 0;
287 kdtree.nodes.resize(frame::size(points));
288 kdtree.root = create_node(kdtree, node_count, points, kdtree.indices.begin(), kdtree.indices.begin(), kdtree.indices.end());
289 kdtree.nodes.resize(node_count);
297 template <
typename Po
intCloud,
typename KdTree,
typename IndexConstIterator>
298 NodeIndexType create_node(
300 std::atomic_uint64_t& node_count,
301 const PointCloud& points,
302 IndexConstIterator global_first,
303 IndexConstIterator first,
304 IndexConstIterator last)
const {
305 const size_t N = std::distance(first, last);
306 const NodeIndexType node_index = node_count++;
307 auto& node = kdtree.nodes[node_index];
310 if (N <= max_leaf_size) {
312 node.node_type.lr.first = std::distance(global_first, first);
313 node.node_type.lr.last = std::distance(global_first, last);
319 using Projection =
typename KdTree::Projection;
320 const auto proj = Projection::find_axis(points, first, last, projection_setting);
321 const auto median_itr = first + N / 2;
322 std::nth_element(first, median_itr, last, [&](
size_t i,
size_t j) {
return proj(frame::point(points, i)) < proj(frame::point(points, j)); });
325 node.node_type.sub.proj = proj;
326 node.node_type.sub.thresh = proj(frame::point(points, *median_itr));
330 tbb::parallel_invoke(
331 [&] { node.left = create_node(kdtree, node_count, points, global_first, first, median_itr); },
332 [&] { node.right = create_node(kdtree, node_count, points, global_first, median_itr, last); });
334 node.left = create_node(kdtree, node_count, points, global_first, first, median_itr);
335 node.right = create_node(kdtree, node_count, points, global_first, median_itr, last);
342 int max_leaf_size = 20;
343 ProjectionSetting projection_setting;
350template <
typename Po
intCloud,
typename Projection_ = AxisAlignedProjection>
353 using Projection = Projection_;
354 using Node = KdTreeNode<Projection>;
359 template <
typename Builder = KdTreeBuilder>
361 if (frame::size(
points) == 0) {
362 std::cerr <<
"warning: Empty point cloud" << std::endl;
366 builder.build_tree(*
this,
points);
377 return knn_search<1>(query_, k_indices, k_sq_dists, setting);
388 const Eigen::Vector4d query = (Eigen::Vector4d() << query_, 1.0).finished();
391 return result.num_found();
402 const Eigen::Vector4d query = (Eigen::Vector4d() << query_, 1.0).finished();
403 KnnResult<N> result(k_indices, k_sq_dists, -1,
identity_transform(), setting.max_sq_dist);
405 return result.num_found();
416 const Eigen::Vector3d& query_,
419 std::vector<double>& sq_dists,
421 const Eigen::Vector4d query = (Eigen::Vector4d() << query_, 1.0).finished();
426 indices.resize(result.num_found());
427 std::transform(result.neighbors.begin(), result.neighbors.end(),
indices.begin(), [](
const auto& p) { return p.first; });
428 sq_dists.resize(result.num_found());
429 std::transform(result.neighbors.begin(), result.neighbors.end(), sq_dists.begin(), [](
const auto& p) { return p.second; });
431 return result.num_found();
436 template <
typename Result>
437 bool knn_search(
const Eigen::Vector4d& query, NodeIndexType node_index, Result& result,
const KnnSetting& setting)
const {
438 const auto& node =
nodes[node_index];
441 if (node.left == INVALID_NODE) {
443 for (
size_t i = node.node_type.lr.first; i < node.node_type.lr.last; i++) {
444 const double sq_dist = (frame::point(
points,
indices[i]) - query).squaredNorm();
445 result.push(
indices[i], sq_dist);
450 const double val = node.node_type.sub.proj(query);
451 const double diff = val - node.node_type.sub.thresh;
452 const double cut_sq_dist = diff * diff;
454 NodeIndexType best_child;
455 NodeIndexType other_child;
458 best_child = node.left;
459 other_child = node.right;
461 best_child = node.right;
462 other_child = node.left;
466 if (!
knn_search(query, best_child, result, setting)) {
471 if (result.worst_distance() > cut_sq_dist) {
472 return knn_search(query, other_child, result, setting);
479 template <
typename Result>
480 bool radius_search(
const Eigen::Vector4d& query, NodeIndexType node_index, Result& result,
double sq_radius,
const KnnSetting& setting)
const {
481 const auto& node =
nodes[node_index];
484 if (node.left == INVALID_NODE) {
486 for (
size_t i = node.node_type.lr.first; i < node.node_type.lr.last; i++) {
487 if (setting.fulfilled(result)) {
491 const double sq_dist = (frame::point(
points,
indices[i]) - query).squaredNorm();
492 if (sq_dist < sq_radius) {
493 result.push(
indices[i], sq_dist);
496 return !setting.fulfilled(result);
499 const double val = node.node_type.sub.proj(query);
500 const double diff = val - node.node_type.sub.thresh;
501 const double cut_sq_dist = diff * diff;
503 NodeIndexType best_child;
504 NodeIndexType other_child;
507 best_child = node.left;
508 other_child = node.right;
510 best_child = node.right;
511 other_child = node.left;
515 if (!
radius_search(query, best_child, result, sq_radius, setting)) {
520 if (sq_radius > cut_sq_dist) {
521 return radius_search(query, other_child, result, sq_radius, setting);
Conventional axis-aligned projection (i.e., selecting any of XYZ axes with the largest variance).
Definition small_kdtree.hpp:63
static AxisAlignedProjection find_axis(const PointCloud &points, IndexConstIterator first, IndexConstIterator last, const ProjectionSetting &setting)
Find the axis with the largest variance.
Definition small_kdtree.hpp:78
double operator()(const Eigen::Vector4d &pt) const
Project the point to the selected axis.
Definition small_kdtree.hpp:68
int axis
Axis index (0: X, 1: Y, 2: Z)
Definition small_kdtree.hpp:99
Kd-tree builder with OpenMP.
Definition small_kdtree.hpp:189
int num_threads
Number of threads.
Definition small_kdtree.hpp:271
void build_tree(KdTree &kdtree, const PointCloud &points) const
Build KdTree.
Definition small_kdtree.hpp:197
NodeIndexType create_node(KdTree &kdtree, std::atomic_uint64_t &node_count, const PointCloud &points, IndexConstIterator global_first, IndexConstIterator first, IndexConstIterator last) const
Create a Kd-tree node from the given point indices.
Definition small_kdtree.hpp:225
ProjectionSetting projection_setting
Projection setting.
Definition small_kdtree.hpp:273
KdTreeBuilderOMP(int num_threads=4)
Constructor.
Definition small_kdtree.hpp:193
int max_leaf_size
Maximum number of points in a leaf node.
Definition small_kdtree.hpp:272
Single thread Kd-tree builder.
Definition small_kdtree.hpp:124
int max_leaf_size
Maximum number of points in a leaf node.
Definition small_kdtree.hpp:184
NodeIndexType create_node(KdTree &kdtree, size_t &node_count, const PointCloud &points, IndexConstIterator global_first, IndexConstIterator first, IndexConstIterator last) const
Create a Kd-tree node from the given point indices.
Definition small_kdtree.hpp:146
ProjectionSetting projection_setting
Projection setting.
Definition small_kdtree.hpp:185
void build_tree(KdTree &kdtree, const PointCloud &points) const
Build KdTree.
Definition small_kdtree.hpp:130
KdTree node.
Definition small_kdtree.hpp:107
NodeIndexType right
Right child node index.
Definition small_kdtree.hpp:120
double thresh
Threshold value.
Definition small_kdtree.hpp:115
Projection proj
Projection axis.
Definition small_kdtree.hpp:114
NodeIndexType left
Left child node index.
Definition small_kdtree.hpp:119
struct gtsam_points::KdTreeNode::@0::Leaf lr
Leaf node.
struct gtsam_points::KdTreeNode::@0::NonLeaf sub
Non-leaf node.
NodeIndexType first
First point index in the leaf node.
Definition small_kdtree.hpp:110
NodeIndexType last
Last point index in the leaf node.
Definition small_kdtree.hpp:111
KdTree-based nearest neighbor search.
Definition kdtree.hpp:21
K-nearest neighbor search result container.
Definition knn_result.hpp:36
K-nearest neighbor search setting.
Definition knn_result.hpp:11
bool fulfilled(const Result &result) const
Check if the result satisfies the early termination condition.
Definition knn_result.hpp:15
Standard point cloud class that holds only pointers to point attributes.
Definition point_cloud.hpp:19
Parameters to control the projection axis search.
Definition small_kdtree.hpp:58
int max_scan_count
Maximum number of points to use for the axis search.
Definition small_kdtree.hpp:59
Radius neighbor search result container.
Definition knn_result.hpp:121
"Unsafe" KdTree.
Definition small_kdtree.hpp:351
NodeIndexType root
Root node index (should be zero)
Definition small_kdtree.hpp:531
size_t radius_search(const Eigen::Vector3d &query_, double radius, std::vector< size_t > &indices, std::vector< double > &sq_dists, const KnnSetting &setting=KnnSetting()) const
Find neighbors in a search radius.
Definition small_kdtree.hpp:415
size_t knn_search(const Eigen::Vector3d &query_, int k, size_t *k_indices, double *k_sq_dists, const KnnSetting &setting=KnnSetting()) const
Find k-nearest neighbors. This method uses dynamic memory allocation.
Definition small_kdtree.hpp:387
UnsafeKdTree(const PointCloud &points, const Builder &builder=KdTreeBuilder())
Constructor.
Definition small_kdtree.hpp:360
std::vector< size_t > indices
Point indices refered by nodes.
Definition small_kdtree.hpp:529
size_t nearest_neighbor_search(const Eigen::Vector3d &query_, size_t *k_indices, double *k_sq_dists, const KnnSetting &setting=KnnSetting()) const
Find the nearest neighbor.
Definition small_kdtree.hpp:375
std::vector< Node > nodes
Kd-tree nodes.
Definition small_kdtree.hpp:532
const PointCloud & points
Input points.
Definition small_kdtree.hpp:528
size_t knn_search(const Eigen::Vector3d &query_, size_t *k_indices, double *k_sq_dists, const KnnSetting &setting=KnnSetting()) const
Find k-nearest neighbors. This method uses fixed and static memory allocation. Might be faster for sm...
Definition small_kdtree.hpp:401