Point Cloud Library (PCL) 1.15.1-dev
Loading...
Searching...
No Matches
kdtree_nanoflann.h
1/*
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Point Cloud Library (PCL) - www.pointclouds.org
5 * Copyright (c) 2025-, Open Perception Inc.
6 *
7 * All rights reserved
8 */
9
10#pragma once
11
12#include <pcl/pcl_config.h>
13#if PCL_HAS_NANOFLANN || defined(DOXYGEN_ONLY)
14
15#include <pcl/search/search.h>
16#include <pcl/point_representation.h>
17
18#include <nanoflann.hpp>
19
20namespace pcl {
21namespace search {
22namespace internal {
23/** Helper for KdTreeNanoflann, serves as a dataset adaptor.
24 */
25template <typename T = float>
27 PointCloudAdaptor(const T* data,
28 bool delete_data,
29 std::size_t dim,
30 std::size_t point_count)
31 : data_(data), delete_data_(delete_data), dim_(dim), point_count_(point_count)
32 {}
33
35 {
36 if (delete_data_)
37 delete[] data_;
38 }
39
40 inline void
41 reset_adaptor(const T* data,
42 bool delete_data,
43 std::size_t dim,
44 std::size_t point_count)
45 {
46 if (delete_data_)
47 delete[] data_;
48 data_ = data;
49 delete_data_ = delete_data;
50 dim_ = dim;
51 point_count_ = point_count;
52 }
53
54 inline std::size_t
56 {
57 return point_count_;
58 }
59
60 inline T
61 kdtree_get_pt(const std::size_t idx, const std::size_t dim) const
62 {
63 return data_[dim_ * idx + dim];
64 }
65
66 template <class BBOX>
67 bool
68 kdtree_get_bbox(BBOX& /* bb */) const
69 {
70 return false;
71 }
72
73private:
74 const T* data_;
75 bool delete_data_;
76 std::size_t dim_;
77 std::size_t point_count_;
78};
79
80/** Helper function for radiusSearch: must have a template specialization if the
81 * distance norm is L2
82 */
83template <typename Dist>
84float
85square_if_l2(float radius)
86{
87 return radius;
88};
89template <>
90inline float
91square_if_l2<nanoflann::L2_Adaptor<float,
93 float,
94 pcl::index_t>>(float radius)
95{
96 return radius * radius;
97};
98template <>
99inline float
101 nanoflann::L2_Simple_Adaptor<float,
103 float,
104 pcl::index_t>>(float radius)
105{
106 return radius * radius;
107};
108} // namespace internal
109
110// for convenience/brevity
112 nanoflann::L1_Adaptor<float,
114 float,
117 nanoflann::L2_Adaptor<float,
119 float,
122 nanoflann::L2_Simple_Adaptor<float,
124 float,
126#if NANOFLANN_VERSION < 0x020000
128 nanoflann::SO2_Adaptor<float,
130 float,
133 nanoflann::SO3_Adaptor<float,
135 float,
137#endif // NANOFLANN_VERSION < 0x020000
138// TODO maybe additional adaptor with simd?
139
140/** @brief @b pcl::search::KdTreeNanoflann is a faster and flexible alternative to
141 * pcl::search::KdTree. It is based on the nanoflann library by Jose Luis Blanco-Claraco
142 * ( https://github.com/jlblancoc/nanoflann ). Since nanoflann is treated as an optional
143 * dependency to PCL, you must test the preprocessor symbol `PCL_HAS_NANOFLANN` after
144 * including `pcl/search/kdtree_nanoflann.h`.
145 * @code
146 * // Example 1: using KdTreeNanoflann in NormalEstimation:
147 * pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
148 * auto tree = pcl::make_shared<pcl::search::KdTreeNanoflann<pcl::PointXYZ>>();
149 * ne.setSearchMethod (tree);
150 * // rest as usual
151 * // Example 2: using KdTreeNanoflannn in ICP:
152 * pcl::IterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> icp;
153 * icp.setSearchMethodSource(pcl::make_shared<pcl::search::KdTreeNanoflann<pcl::PointXYZ>>());
154 * icp.setSearchMethodTarget(pcl::make_shared<pcl::search::KdTreeNanoflann<pcl::PointXYZ>>());
155 * // rest as usual
156 * // Example 3: using a L1 distance norm:
157 * pcl::search::KdTreeNanoflann<PointXYZ, 3, pcl::search::L1_Adaptor> kdtree_nanoflann;
158 * kdtree_nanoflann.setInputCloud (my_cloud);
159 * // now ready to use kdtree_nanoflann.nearestKSearch(...) and
160 * // kdtree_nanoflann.radiusSearch(...);
161 * // Example 4: using KdTreeNanoflann with features instead of 3D points:
162 * pcl::search::KdTreeNanoflann<pcl::FPFHSignature33, 33> kdtree_nanoflann_feat;
163 * kdtree_nanoflann_feat.setInputCloud(my_features_cloud);
164 * // now ready for searching
165 * @endcode
166 *
167 * @tparam PointT Can be a point like pcl::PointXYZ or a feature like pcl::SHOT352
168 * @tparam Dim Dimension of the KdTree to build. If set to -1, it will infer the
169 * dimension at runtime from the given point representation, but with a performance
170 * penalty. Default is 3, appropriate for all xyz point types.
171 * @tparam Distance The distance to use. Possible values include pcl::L1_Adaptor
172 * (taxicab/manhattan distance), pcl::L2_Simple_Adaptor (euclidean distance, optimized
173 * for few dimensions), pcl::L2_Adaptor (euclidean distance, optimized for many
174 * dimensions), pcl::SO2_Adaptor, pcl::SO3_Adaptor. Default is pcl::L2_Simple_Adaptor
175 *
176 * @author Markus Vieth
177 * @ingroup search
178 */
179template <typename PointT,
180 std::int32_t Dim = 3,
181 typename Distance =
182 std::conditional_t<Dim >= 4, L2_Adaptor, L2_Simple_Adaptor>,
183 typename Tree = nanoflann::KDTreeSingleIndexAdaptor<
184 Distance,
186 Dim,
188class KdTreeNanoflann : public pcl::search::Search<PointT> {
189private:
190 /** The special thing here is that indices and distances are stored in _two_ vectors,
191 * not as a pair in _one_ vector.
192 */
193 template <typename _DistanceType = float, typename _IndexType = pcl::index_t>
194 class PCLRadiusResultSet {
195 public:
196 using DistanceType = _DistanceType;
197 using IndexType = _IndexType;
198
199 public:
200 const DistanceType radius;
201
202 std::vector<IndexType>& m_indices;
203 std::vector<DistanceType>& m_dists;
204 std::size_t max_results_;
205
206 explicit PCLRadiusResultSet(
207 DistanceType radius_,
208 std::vector<IndexType>& indices,
209 std::vector<DistanceType>& dists,
210 std::size_t max_results = std::numeric_limits<std::size_t>::max())
211 : radius(radius_), m_indices(indices), m_dists(dists), max_results_(max_results)
212 {
213 init();
214 }
215
216 void
217 init()
218 {
219 clear();
220 if (max_results_ != std::numeric_limits<std::size_t>::max()) {
221 m_indices.reserve(max_results_);
222 m_dists.reserve(max_results_);
223 }
224 }
225 void
226 clear()
227 {
228 m_indices.clear();
229 m_dists.clear();
230 }
231
232 size_t
233 size() const
234 {
235 return m_indices.size();
236 }
237 size_t
238 empty() const
239 {
240 return m_indices.empty();
241 }
242
243 bool
244 full() const
245 {
246 return true;
247 }
248
249 /**
250 * Called during search to add an element matching the criteria.
251 * @return true if the search should be continued, false if the results are
252 * sufficient
253 */
254 bool
255 addPoint(DistanceType dist, IndexType index)
256 {
257 if (dist < radius) {
258 m_indices.emplace_back(index);
259 m_dists.emplace_back(dist);
260 }
261 return (m_indices.size() < max_results_);
262 }
263
264 DistanceType
265 worstDist() const
266 {
267 return radius;
268 }
269
270 void
271 sort()
272 {
273 // this sort function does not do anything (yet). For now, PCLRadiusResultSet
274 // should only be used if the results do not have to be sorted. In the future, we
275 // might add sorting here if we find a really efficient way to sort both vectors
276 // simultaneously.
277 }
278 };
279
280public:
281 using PointCloud = typename Search<PointT>::PointCloud;
282 using PointCloudConstPtr = typename pcl::PointCloud<PointT>::ConstPtr;
283
284 using pcl::search::Search<PointT>::indices_;
285 using pcl::search::Search<PointT>::input_;
286 using pcl::search::Search<PointT>::getIndices;
287 using pcl::search::Search<PointT>::getInputCloud;
288 using pcl::search::Search<PointT>::nearestKSearch;
289 using pcl::search::Search<PointT>::radiusSearch;
290 using pcl::search::Search<PointT>::sorted_results_;
291 using pcl::search::Search<PointT>::name_;
292
293 using Ptr = shared_ptr<KdTreeNanoflann<PointT, Dim, Distance, Tree>>;
294 using ConstPtr = shared_ptr<const KdTreeNanoflann<PointT, Dim, Distance, Tree>>;
295
296 using KdTreePtr = shared_ptr<Tree>;
297 using KdTreeConstPtr = shared_ptr<const Tree>;
298 using PointRepresentationConstPtr = shared_ptr<const PointRepresentation<PointT>>;
299
300 /** @brief Constructor for KdTreeNanoflann.
301 *
302 * @param[in] sorted Set to true if the nearest neighbor search results
303 * need to be sorted in ascending order based on their distance to the
304 * query point (default is false, which is faster)
305 * @param[in] leaf_max_size Max number of points/samples in a leaf, can be tuned to
306 * achieve best performance. When using this for 1nn search (e.g. correspondence
307 * estimation in icp/registration), then leaf_max_size=10 is usually fastest.
308 * Otherwise, leaf_max_size=20 is a good choice.
309 * @param[in] n_thread_build Number of threads for concurrent tree build (default is
310 * 1, meaning no concurrent build)
311 */
312 KdTreeNanoflann(bool sorted,
313 std::size_t leaf_max_size,
314 unsigned int n_thread_build = 1)
315 : pcl::search::Search<PointT>("KdTreeNanoflann", sorted)
316 , leaf_max_size_(leaf_max_size)
317 , n_thread_build_(n_thread_build)
318 {}
319
320 /** @brief Constructor for KdTreeNanoflann.
321 *
322 * @param[in] sorted set to true if the nearest neighbor search results
323 * need to be sorted in ascending order based on their distance to the
324 * query point
325 *
326 */
327 KdTreeNanoflann(bool sorted = false) : KdTreeNanoflann(sorted, 15, 1) {}
328
329 /** @brief Destructor for KdTreeNanoflann. */
330 ~KdTreeNanoflann() override = default;
331
332 /** @brief Provide a pointer to the point representation to use to convert points into
333 * k-D vectors. If you want to use this function, it is recommended to do so _before_
334 * calling setInputCloud, to avoid setting up the kd-tree twice.
335 * @param[in] point_representation the const shared pointer to a PointRepresentation
336 */
337 bool
338 setPointRepresentation(const PointRepresentationConstPtr& point_representation) override
339 {
340 PCL_DEBUG("[KdTreeNanoflann::setPointRepresentation] "
341 "KdTreeNanoflann::setPointRepresentation called, "
342 "point_representation->getNumberOfDimensions()=%i, "
343 "point_representation->isTrivial()=%i\n",
344 point_representation->getNumberOfDimensions(),
345 point_representation->isTrivial());
346 if (Dim != -1 && Dim != point_representation->getNumberOfDimensions()) {
347 PCL_ERROR("[KdTreeNanoflann::setPointRepresentation] The given point "
348 "representation uses %i dimensions, but the nr of dimensions given as "
349 "template parameter to KdTreeNanoflann is %i.\n",
350 point_representation->getNumberOfDimensions(),
351 Dim);
352 return false;
353 }
354 point_representation_ = point_representation;
355 if (input_)
356 setUpTree();
357 return true;
358 }
359
360 /** @brief Get a pointer to the point representation used when converting points into
361 * k-D vectors. */
362 inline PointRepresentationConstPtr
363 getPointRepresentation() const
364 {
365 return point_representation_;
366 }
367
368 /** @brief Sets whether the results have to be sorted or not.
369 * @param[in] sorted_results set to true if the radius search results should be sorted
370 */
371 void
372 setSortedResults(bool sorted_results) override
373 {
374 sorted_results_ = sorted_results;
375 }
376
377 /** @brief Set the search epsilon precision (error bound) for nearest neighbors
378 * searches.
379 * @param[in] eps precision (error bound) for nearest neighbors searches
380 */
381 void
382 setEpsilon(float eps)
383 {
384 eps_ = eps;
385 }
386
387 /** @brief Get the search epsilon precision (error bound) for nearest neighbors
388 * searches. */
389 inline float
390 getEpsilon() const
391 {
392 return eps_;
393 }
394
395 /** @brief Influences the results of radiusSearch when max_nn is not set to zero.
396 * If the parameter max_nn of radiusSearch is set to a value greater than zero, it
397 * will return _at most_ that many points. If you set `use_rknn=true`, it will always
398 * return the points _closest_ to the query point. If you set `use_rknn=false`, it may
399 * return _any_ points within the radius, which can be faster.
400 */
401 void
402 setUseRKNN(bool use_rknn)
403 {
404 use_rknn_ = use_rknn;
405 }
406
407 /** @brief Provide a pointer to the input dataset, this will build the kd-tree.
408 * @param[in] cloud the const shared pointer to a PointCloud
409 * @param[in] indices the point indices subset that is to be used from \a cloud
410 */
411 bool
412 setInputCloud(const PointCloudConstPtr& cloud,
413 const IndicesConstPtr& indices = IndicesConstPtr()) override
414 {
415 input_ = cloud;
416 indices_ = indices;
417
418 return setUpTree();
419 }
420
421 /** @brief Get pointer to internal nanoflann tree. Use with caution.
422 */
423 KdTreePtr
424 getNanoflannTree()
425 {
426 return nanoflann_tree_;
427 }
428
429 /** @brief Search for the k-nearest neighbors for the given query point.
430 * @param[in] point the given query point
431 * @param[in] k the number of neighbors to search for
432 * @param[out] k_indices the resultant indices of the neighboring points (must be
433 * resized to \a k a priori!)
434 * @param[out] k_sqr_distances the resultant squared distances to the neighboring
435 * points (must be resized to \a k a priori!)
436 * @return number of neighbors found
437 */
438 int
439 nearestKSearch(const PointT& point,
440 int k,
441 Indices& k_indices,
442 std::vector<float>& k_sqr_distances) const override
443 {
444 assert(point_representation_->isValid(point) &&
445 "Invalid (NaN, Inf) point coordinates given to nearestKSearch!");
446 if (static_cast<std::size_t>(k) > adaptor_->kdtree_get_point_count())
447 k = adaptor_->kdtree_get_point_count();
448 k_indices.resize(k);
449 k_sqr_distances.resize(k);
450 float* query_point = nullptr;
451 if (!point_representation_->isTrivial()) {
452 query_point = new float[point_representation_->getNumberOfDimensions()];
453 point_representation_->vectorize(point, query_point);
454 }
455 // like nanoflann_tree_->knnSearch
456 nanoflann::KNNResultSet<float, pcl::index_t> resultSet(k);
457 resultSet.init(k_indices.data(), k_sqr_distances.data());
458 nanoflann_tree_->findNeighbors(
459 resultSet,
460 (query_point ? query_point : reinterpret_cast<const float*>(&point))
461#if NANOFLANN_VERSION < 0x150
462 ,
463 nanoflann::SearchParams()
464#endif // NANOFLANN_VERSION < 0x150
465 );
466 const auto search_result = resultSet.size();
467 delete[] query_point;
468 assert(search_result == k);
469
470 if (!identity_mapping_) {
471 for (auto& index : k_indices) {
472 index = index_mapping_[index];
473 }
474 }
475 return search_result;
476 }
477
478 /** @brief Search for all the nearest neighbors of the query point in a given radius.
479 * @param[in] point the given query point
480 * @param[in] radius the radius of the sphere bounding all of p_q's neighbors
481 * @param[out] k_indices the resultant indices of the neighboring points
482 * @param[out] k_sqr_distances the resultant squared distances to the neighboring
483 * points
484 * @param[in] max_nn if given, bounds the maximum returned neighbors to this value. If
485 * \a max_nn is set to 0 or to a number higher than the number of points in the input
486 * cloud, all neighbors in \a radius will be returned.
487 * @return number of neighbors found in radius
488 */
489 int
490 radiusSearch(const PointT& point,
491 double radius,
492 Indices& k_indices,
493 std::vector<float>& k_sqr_distances,
494 unsigned int max_nn = 0) const override
495 {
496 assert(point_representation_->isValid(point) &&
497 "Invalid (NaN, Inf) point coordinates given to radiusSearch!");
498 float* query_point = nullptr;
499 if (!point_representation_->isTrivial()) {
500 query_point = new float[point_representation_->getNumberOfDimensions()];
501 point_representation_->vectorize(point, query_point);
502 }
503 if (max_nn == 0) {
504 // return _all_ points within radius
505 // Calling this->sortResults is currently slower than sorting the vector of
506 // nanoflann::ResultItem However, if sorted results are not requested, using
507 // PCLRadiusResultSet with radiusSearchCustomCallback avoids copies and is faster
508 if (sorted_results_) {
509#if NANOFLANN_VERSION < 0x150
510 std::vector<std::pair<pcl::index_t, float>>
511 IndicesDists; // nanoflann::ResultItem was introduced in version 1.5.0
512#else
513 std::vector<nanoflann::ResultItem<pcl::index_t, float>> IndicesDists;
514#endif // NANOFLANN_VERSION < 0x150
515 // like nanoflann_tree_->radiusSearch
516 nanoflann::RadiusResultSet<float, pcl::index_t> resultSet(
517 pcl::search::internal::square_if_l2<Distance>(radius), IndicesDists);
518 nanoflann_tree_->findNeighbors(
519 resultSet,
520 (query_point ? query_point : reinterpret_cast<const float*>(&point)),
521#if NANOFLANN_VERSION < 0x150
522 {32, eps_, sorted_results_} // first parameter is ignored in older versions,
523 // and removed in newer versions
524#else
525 {eps_, sorted_results_}
526#endif // NANOFLANN_VERSION < 0x150
527 );
528#if NANOFLANN_VERSION < 0x160
529 // before, nanoflann did not sort in findNeighbours
530 if (sorted_results_)
531 std::sort(
532 IndicesDists.begin(), IndicesDists.end(), nanoflann::IndexDist_Sorter());
533#endif // NANOFLANN_VERSION < 0x160
534 const auto search_result = resultSet.size();
535 k_indices.resize(IndicesDists.size());
536 k_sqr_distances.resize(IndicesDists.size());
537 for (std::size_t i = 0; i < IndicesDists.size(); ++i) {
538 k_indices[i] = identity_mapping_ ? IndicesDists[i].first
539 : index_mapping_[IndicesDists[i].first];
540 k_sqr_distances[i] = IndicesDists[i].second;
541 }
542 delete[] query_point;
543 return search_result;
544 }
545 else {
546 PCLRadiusResultSet<float, pcl::index_t> resultSet(
547 pcl::search::internal::square_if_l2<Distance>(radius),
548 k_indices,
549 k_sqr_distances);
550 // like nanoflann_tree_->radiusSearchCustomCallback
551 nanoflann_tree_->findNeighbors(
552 resultSet,
553 (query_point ? query_point : reinterpret_cast<const float*>(&point)),
554#if NANOFLANN_VERSION < 0x150
555 {32, eps_, sorted_results_} // first parameter is ignored in older versions,
556 // and removed in newer versions
557#else
558 {eps_, sorted_results_}
559#endif // NANOFLANN_VERSION < 0x150
560 );
561 const auto search_result = resultSet.size();
562 if (!identity_mapping_) {
563 for (auto& index : k_indices) {
564 index = index_mapping_[index];
565 }
566 }
567 if (sorted_results_)
568 this->sortResults(k_indices, k_sqr_distances);
569 delete[] query_point;
570 return search_result;
571 }
572 }
573 else {
574 // return no more than max_nn points
575 if (use_rknn_) {
576 // return points closest to query point
577 if (static_cast<std::size_t>(max_nn) > adaptor_->kdtree_get_point_count())
578 max_nn = adaptor_->kdtree_get_point_count();
579 k_indices.resize(max_nn);
580 k_sqr_distances.resize(max_nn);
581#if NANOFLANN_VERSION < 0x151
582 // rknnSearch and RKNNResultSet were added in nanoflann 1.5.1, so do knn search
583 // and discard those neighbors that are outside of search radius
584 nanoflann::KNNResultSet<float, pcl::index_t> resultSet(max_nn);
585 resultSet.init(k_indices.data(), k_sqr_distances.data());
586 nanoflann_tree_->findNeighbors(
587 resultSet,
588 (query_point ? query_point : reinterpret_cast<const float*>(&point))
589#if NANOFLANN_VERSION < 0x150
590 ,
591 nanoflann::SearchParams()
592#endif // NANOFLANN_VERSION < 0x150
593 );
594 auto search_result = resultSet.size();
595 for (auto iter = k_sqr_distances.rbegin();
596 iter != k_sqr_distances.rend() &&
597 *iter > pcl::search::internal::square_if_l2<Distance>(radius);
598 ++iter) {
599 --search_result;
600 }
601 k_indices.resize(search_result);
602 k_sqr_distances.resize(search_result);
603#else
604 // like nanoflann_tree_->rknnSearch
605 nanoflann::RKNNResultSet<float, pcl::index_t> resultSet(
606 max_nn, pcl::search::internal::square_if_l2<Distance>(radius));
607 resultSet.init(k_indices.data(), k_sqr_distances.data());
608 nanoflann_tree_->findNeighbors(
609 resultSet,
610 (query_point ? query_point : reinterpret_cast<const float*>(&point)));
611 const auto search_result = resultSet.size();
612 k_indices.resize(search_result);
613 k_sqr_distances.resize(search_result);
614#endif // NANOFLANN_VERSION < 0x151
615 if (!identity_mapping_) {
616 for (auto& index : k_indices) {
617 index = index_mapping_[index];
618 }
619 }
620 delete[] query_point;
621 return search_result;
622 }
623 else {
624 // may return any points within the radius, can be faster
625 PCLRadiusResultSet<float, pcl::index_t> resultSet(
626 pcl::search::internal::square_if_l2<Distance>(radius),
627 k_indices,
628 k_sqr_distances,
629 max_nn);
630 // like nanoflann_tree_->radiusSearchCustomCallback
631 nanoflann_tree_->findNeighbors(
632 resultSet,
633 (query_point ? query_point : reinterpret_cast<const float*>(&point)),
634#if NANOFLANN_VERSION < 0x150
635 {32, eps_, sorted_results_} // first parameter is ignored in older versions,
636 // and removed in newer versions
637#else
638 {eps_, sorted_results_}
639#endif // NANOFLANN_VERSION < 0x150
640 );
641 const auto search_result = resultSet.size();
642 if (!identity_mapping_) {
643 for (auto& index : k_indices) {
644 index = index_mapping_[index];
645 }
646 }
647 if (sorted_results_)
648 this->sortResults(k_indices, k_sqr_distances);
649 delete[] query_point;
650 return search_result;
651 }
652 }
653 }
654
655private:
656 /** Set up index_mapping_, adaptor_, and nanoflann_tree_, based on
657 * point_representation_, input_, and indices_.
658 */
659 bool
660 setUpTree()
661 {
662 if (!point_representation_ || !input_ || input_->empty()) {
663 PCL_ERROR("[KdTreeNanoflann::setUpTree] point representation is null or input is "
664 "null or input is empty\n");
665 return false;
666 }
667
668 const std::size_t dim = point_representation_->getNumberOfDimensions();
669 if (Dim != -1 && Dim != static_cast<int>(dim)) {
670 PCL_ERROR("[KdTreeNanoflann::setUpTree] The given point "
671 "representation uses %i dimensions, but the nr of dimensions given as "
672 "template parameter to KdTreeNanoflann is %i.\n",
673 dim,
674 Dim);
675 return false;
676 }
677 index_mapping_.clear();
678 if (indices_ && !indices_->empty()) {
679 index_mapping_.reserve(indices_->size());
680 for (const auto& index : *indices_) {
681 if (point_representation_->isValid((*input_)[index])) {
682 index_mapping_.emplace_back(index);
683 }
684 }
685 }
686 else {
687 index_mapping_.reserve(input_->size());
688 for (std::size_t index = 0; index < input_->size(); ++index) {
689 if (point_representation_->isValid((*input_)[index])) {
690 index_mapping_.emplace_back(index);
691 }
692 }
693 }
694 identity_mapping_ = true;
695 for (pcl::index_t index = 0;
696 identity_mapping_ && static_cast<std::size_t>(index) < index_mapping_.size();
697 ++index) {
698 identity_mapping_ = identity_mapping_ && index_mapping_[index] == index;
699 }
700
701 PCL_DEBUG("[KdTreeNanoflann::setUpTree] identity_mapping_=%i, "
702 "point_representation_->getNumberOfDimensions()=%lu, "
703 "point_representation_->isTrivial ()=%i\n",
704 identity_mapping_ ? 1 : 0,
705 dim,
706 point_representation_->isTrivial() ? 1 : 0);
707 if (identity_mapping_ && point_representation_->isTrivial() &&
708 sizeof(PointT) % sizeof(float) == 0) {
709 // no need to allocate memory and copy data
710 PCL_DEBUG("[KdTreeNanoflann::setUpTree] Mapping cloud directly, without "
711 "allocating memory.\n");
712 adaptor_.reset(new internal::PointCloudAdaptor<float>(
713 reinterpret_cast<const float*>(&(*input_)[0]),
714 false,
715 sizeof(PointT) / sizeof(float),
716 index_mapping_.size()));
717 }
718 else {
719 float* data = new float[dim * index_mapping_.size()];
720 float* data_itr = data;
721 for (auto& index : index_mapping_) {
722 point_representation_->vectorize((*input_)[index], data_itr);
723 data_itr += dim;
724 }
725 adaptor_.reset(new internal::PointCloudAdaptor<float>(
726 data, true, dim, index_mapping_.size()));
727 }
728 nanoflann_tree_.reset(
729 new Tree(dim,
730 *adaptor_,
731#if NANOFLANN_VERSION >= 0x150
732 // concurrent tree building is possible since nanoflann 1.5.0
733 {leaf_max_size_,
734 nanoflann::KDTreeSingleIndexAdaptorFlags::None,
735 n_thread_build_}));
736#else
737 {leaf_max_size_}));
738 if (n_thread_build_ != 1)
739 PCL_WARN("[KdTreeNanoflann::setUpTree] concurrent tree building is only possible "
740 "with nanoflann version 1.5.0 and newer\n");
741#endif // NANOFLANN_VERSION >= 0x150
742 return true;
743 }
744
745 KdTreePtr nanoflann_tree_;
746
747 shared_ptr<internal::PointCloudAdaptor<float>> adaptor_;
748
749 PointRepresentationConstPtr point_representation_{
751
752 Indices index_mapping_;
753
754 bool identity_mapping_{false};
755
756 std::size_t leaf_max_size_{15};
757
758 unsigned int n_thread_build_{1};
759
760 float eps_{0.0f};
761
762 bool use_rknn_{true};
763};
764} // namespace search
765} // namespace pcl
766
767#else
768//#warning "KdTreeNanoflann is not available"
769#endif // PCL_HAS_NANOFLANN
DefaultPointRepresentation extends PointRepresentation to define default behavior for common point ty...
PointCloud represents the base class in PCL for storing collections of 3D points.
shared_ptr< const PointCloud< PointT > > ConstPtr
Generic search class.
Definition search.h:78
float square_if_l2(float radius)
Helper function for radiusSearch: must have a template specialization if the distance norm is L2.
nanoflann::L2_Simple_Adaptor< float, pcl::search::internal::PointCloudAdaptor< float >, float, pcl::index_t > L2_Simple_Adaptor
nanoflann::SO2_Adaptor< float, pcl::search::internal::PointCloudAdaptor< float >, float, pcl::index_t > SO2_Adaptor
nanoflann::SO3_Adaptor< float, pcl::search::internal::PointCloudAdaptor< float >, float, pcl::index_t > SO3_Adaptor
nanoflann::L1_Adaptor< float, pcl::search::internal::PointCloudAdaptor< float >, float, pcl::index_t > L1_Adaptor
nanoflann::L2_Adaptor< float, pcl::search::internal::PointCloudAdaptor< float >, float, pcl::index_t > L2_Adaptor
shared_ptr< const Indices > IndicesConstPtr
Definition pcl_base.h:59
detail::int_type_t< detail::index_type_size, detail::index_type_signed > index_t
Type used for an index in PCL.
Definition types.h:112
IndicesAllocator<> Indices
Type used for indices in PCL.
Definition types.h:133
A point structure representing Euclidean xyz coordinates, and the RGB color.
Helper for KdTreeNanoflann, serves as a dataset adaptor.
T kdtree_get_pt(const std::size_t idx, const std::size_t dim) const
void reset_adaptor(const T *data, bool delete_data, std::size_t dim, std::size_t point_count)
PointCloudAdaptor(const T *data, bool delete_data, std::size_t dim, std::size_t point_count)