From 144bd6837ada0bec84b2fe62cec23d3f68d8397e Mon Sep 17 00:00:00 2001 From: Markus Vieth Date: Sun, 23 Aug 2026 10:58:49 +0200 Subject: [PATCH 1/2] Move further towards KdTreeNanoflann --- apps/src/openni_tracking.cpp | 1 + apps/src/pcd_select_object_plane.cpp | 1 + apps/src/ppf_object_recognition.cpp | 1 + .../conditional_euclidean_clustering.cpp | 2 +- .../correspondence_grouping.cpp | 2 +- .../cylinder_segmentation.cpp | 2 +- .../pairwise_incremental_registration.cpp | 2 +- .../pcl_visualizer/pcl_visualizer_demo.cpp | 2 +- .../sources/rops_feature/rops_feature.cpp | 2 +- .../template_alignment/template_alignment.cpp | 2 +- .../example_difference_of_normals.cpp | 1 + .../example_extract_clusters_normals.cpp | 6 +- .../include/pcl/registration/gicp6d.h | 4 +- registration/src/gicp6d.cpp | 23 +- .../pcl/segmentation/extract_clusters.h | 250 +++++++++++++++++- test/features/CMakeLists.txt | 1 + test/filters/CMakeLists.txt | 1 + test/keypoints/CMakeLists.txt | 1 + test/recognition/CMakeLists.txt | 1 + test/registration/CMakeLists.txt | 4 +- .../test_correspondence_estimation.cpp | 1 + test/registration/test_ndt.cpp | 1 + test/registration/test_registration.cpp | 1 + test/registration/test_registration_api.cpp | 1 + test/registration/test_sac_ia.cpp | 1 + test/search/CMakeLists.txt | 1 + test/surface/CMakeLists.txt | 1 + test/visualization/CMakeLists.txt | 1 + tools/sac_segmentation_plane.cpp | 1 + 29 files changed, 291 insertions(+), 27 deletions(-) diff --git a/apps/src/openni_tracking.cpp b/apps/src/openni_tracking.cpp index 3c0b6f8379c..c61913bade4 100644 --- a/apps/src/openni_tracking.cpp +++ b/apps/src/openni_tracking.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include diff --git a/apps/src/pcd_select_object_plane.cpp b/apps/src/pcd_select_object_plane.cpp index 4d9b8586ee6..42e9701988a 100644 --- a/apps/src/pcd_select_object_plane.cpp +++ b/apps/src/pcd_select_object_plane.cpp @@ -48,6 +48,7 @@ #include #include #include // for pointToPlaneDistance +#include #include #include #include diff --git a/apps/src/ppf_object_recognition.cpp b/apps/src/ppf_object_recognition.cpp index 4be5cb54f36..11492b193b9 100644 --- a/apps/src/ppf_object_recognition.cpp +++ b/apps/src/ppf_object_recognition.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include diff --git a/doc/tutorials/content/sources/conditional_euclidean_clustering/conditional_euclidean_clustering.cpp b/doc/tutorials/content/sources/conditional_euclidean_clustering/conditional_euclidean_clustering.cpp index 158c38a0e83..79d98712dd4 100644 --- a/doc/tutorials/content/sources/conditional_euclidean_clustering/conditional_euclidean_clustering.cpp +++ b/doc/tutorials/content/sources/conditional_euclidean_clustering/conditional_euclidean_clustering.cpp @@ -1,7 +1,7 @@ #include #include #include - +#include #include #include #include diff --git a/doc/tutorials/content/sources/correspondence_grouping/correspondence_grouping.cpp b/doc/tutorials/content/sources/correspondence_grouping/correspondence_grouping.cpp index 81cd0fac088..59d3448efd4 100644 --- a/doc/tutorials/content/sources/correspondence_grouping/correspondence_grouping.cpp +++ b/doc/tutorials/content/sources/correspondence_grouping/correspondence_grouping.cpp @@ -12,7 +12,7 @@ #include #include #include - +#include typedef pcl::PointXYZRGBA PointType; typedef pcl::Normal NormalType; typedef pcl::ReferenceFrame RFType; diff --git a/doc/tutorials/content/sources/cylinder_segmentation/cylinder_segmentation.cpp b/doc/tutorials/content/sources/cylinder_segmentation/cylinder_segmentation.cpp index 326ebc3a5e5..aa4cd0af022 100644 --- a/doc/tutorials/content/sources/cylinder_segmentation/cylinder_segmentation.cpp +++ b/doc/tutorials/content/sources/cylinder_segmentation/cylinder_segmentation.cpp @@ -7,7 +7,7 @@ #include #include #include - +#include typedef pcl::PointXYZ PointT; int diff --git a/doc/tutorials/content/sources/pairwise_incremental_registration/pairwise_incremental_registration.cpp b/doc/tutorials/content/sources/pairwise_incremental_registration/pairwise_incremental_registration.cpp index 834ea301225..11d00018735 100644 --- a/doc/tutorials/content/sources/pairwise_incremental_registration/pairwise_incremental_registration.cpp +++ b/doc/tutorials/content/sources/pairwise_incremental_registration/pairwise_incremental_registration.cpp @@ -53,7 +53,7 @@ #include #include #include - +#include #include using pcl::visualization::PointCloudColorHandlerGenericField; diff --git a/doc/tutorials/content/sources/pcl_visualizer/pcl_visualizer_demo.cpp b/doc/tutorials/content/sources/pcl_visualizer/pcl_visualizer_demo.cpp index 6e388ef74fa..f37149e52e9 100644 --- a/doc/tutorials/content/sources/pcl_visualizer/pcl_visualizer_demo.cpp +++ b/doc/tutorials/content/sources/pcl_visualizer/pcl_visualizer_demo.cpp @@ -8,7 +8,7 @@ #include #include #include - +#include using namespace std::chrono_literals; // -------------- diff --git a/doc/tutorials/content/sources/rops_feature/rops_feature.cpp b/doc/tutorials/content/sources/rops_feature/rops_feature.cpp index 913da4284bc..2aa302563b1 100644 --- a/doc/tutorials/content/sources/rops_feature/rops_feature.cpp +++ b/doc/tutorials/content/sources/rops_feature/rops_feature.cpp @@ -1,6 +1,6 @@ #include #include - +#include int main (int argc, char** argv) { if (argc != 4) diff --git a/doc/tutorials/content/sources/template_alignment/template_alignment.cpp b/doc/tutorials/content/sources/template_alignment/template_alignment.cpp index bafeff588b8..ebc45e99da3 100644 --- a/doc/tutorials/content/sources/template_alignment/template_alignment.cpp +++ b/doc/tutorials/content/sources/template_alignment/template_alignment.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/examples/features/example_difference_of_normals.cpp b/examples/features/example_difference_of_normals.cpp index ecb3d168046..dc2e8b8f302 100644 --- a/examples/features/example_difference_of_normals.cpp +++ b/examples/features/example_difference_of_normals.cpp @@ -14,6 +14,7 @@ #include #include #include // for OrganizedNeighbor +#include #include #include diff --git a/examples/segmentation/example_extract_clusters_normals.cpp b/examples/segmentation/example_extract_clusters_normals.cpp index 5e334ffc817..1bfec01a5f9 100644 --- a/examples/segmentation/example_extract_clusters_normals.cpp +++ b/examples/segmentation/example_extract_clusters_normals.cpp @@ -42,6 +42,7 @@ #include #include +#include #include #define PCL_NO_PRECOMPILE // ConditionalEuclideanClustering is not always instantiated with PointNormal #include @@ -74,9 +75,8 @@ main (int argc, char **argv) ne.compute (*cloud_normals); std::cout << "Estimated the normals" << std::endl; - // Creating the kdtree object for the search method of the extraction - pcl::KdTree::Ptr tree_ec (new pcl::KdTreeFLANN ()); - tree_ec->setInputCloud (cloud_ptr); + // Creating the search method of the extraction + pcl::search::Search::Ptr tree_ec(pcl::search::autoSelectMethod(cloud_ptr, false)); // Extracting Euclidean clusters using cloud and its normals std::vector cluster_indices; diff --git a/registration/include/pcl/registration/gicp6d.h b/registration/include/pcl/registration/gicp6d.h index 1a102ae0dba..317910754b7 100644 --- a/registration/include/pcl/registration/gicp6d.h +++ b/registration/include/pcl/registration/gicp6d.h @@ -39,7 +39,7 @@ #pragma once #include -#include +#include #include #include // for PCL_EXPORTS #include @@ -118,7 +118,7 @@ class PCL_EXPORTS GeneralizedIterativeClosestPoint6D pcl::PointCloud::Ptr target_lab_; /** \brief 6d-tree to search in model cloud. */ - pcl::search::KdTree::Ptr target_tree_lab_; + pcl::search::Search::Ptr target_tree_lab_; /** \brief The color weight. */ float lab_weight_; diff --git a/registration/src/gicp6d.cpp b/registration/src/gicp6d.cpp index 45c1fe425c3..be07c31e2c9 100644 --- a/registration/src/gicp6d.cpp +++ b/registration/src/gicp6d.cpp @@ -38,8 +38,12 @@ #include #include -#include // for pcl::make_shared +#include // for pcl::make_shared +#include #include // for PointXYZRGBtoXYZLAB +#if PCL_HAS_FLANN +#include +#endif namespace pcl { @@ -84,17 +88,16 @@ GeneralizedIterativeClosestPoint6D::setInputTarget( #if PCL_HAS_NANOFLANN // 6 search dimensions for MyPointRepresentation; max. 10 points per leaf is a good // choice for one-nearest-neighbor search - target_tree_lab_.reset( - new pcl::search::KdTreeNanoflann(false, 10)); - target_tree_lab_->setPointRepresentation( - pcl::make_shared(point_rep_)); - target_tree_lab_->setInputCloud(target_lab_); + auto tree = new pcl::search::KdTreeNanoflann(false, 10); + tree->setPointRepresentation(pcl::make_shared(point_rep_)); + tree->setInputCloud(target_lab_); + target_tree_lab_.reset(tree); #else #if PCL_HAS_FLANN - target_tree_lab_.reset(new pcl::search::KdTree(false)); - target_tree_lab_->setPointRepresentation( - pcl::make_shared(point_rep_)); - target_tree_lab_->setInputCloud(target_lab_); + auto tree = new pcl::search::KdTree(false); + tree->setPointRepresentation(pcl::make_shared(point_rep_)); + tree->setInputCloud(target_lab_); + target_tree_lab_.reset(tree); #else #error "GeneralizedIterativeClosestPoint6D needs nanoflann or FLANN!" #endif diff --git a/segmentation/include/pcl/segmentation/extract_clusters.h b/segmentation/include/pcl/segmentation/extract_clusters.h index 2bfb4196c7f..b107420fe8d 100644 --- a/segmentation/include/pcl/segmentation/extract_clusters.h +++ b/segmentation/include/pcl/segmentation/extract_clusters.h @@ -43,7 +43,9 @@ #include #include // for Search -#include // for KdTree +#if PCL_HAS_FLANN +#include +#endif namespace pcl { @@ -82,6 +84,7 @@ namespace pcl const typename search::Search::Ptr &tree, float tolerance, std::vector &clusters, unsigned int min_pts_per_cluster = 1, unsigned int max_pts_per_cluster = (std::numeric_limits::max) ()); +#if PCL_HAS_FLANN ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** \brief Decompose a region of space into clusters based on the euclidean distance between points, and the normal * angular deviation between points. Each point added to the cluster is origin to another radius search. Each point @@ -99,7 +102,9 @@ namespace pcl * \param max_pts_per_cluster maximum number of points that a cluster may contain (default: max int) * \ingroup segmentation */ - template void + template + PCL_DEPRECATED(1, 19, "use a search method inheriting from pcl::search::Search instead") + void extractEuclideanClusters ( const PointCloud &cloud, const PointCloud &normals, float tolerance, const typename KdTree::Ptr &tree, @@ -215,7 +220,9 @@ namespace pcl * \ingroup segmentation */ template - void extractEuclideanClusters ( + PCL_DEPRECATED(1, 19, "use a search method inheriting from pcl::search::Search instead") + void + extractEuclideanClusters ( const PointCloud &cloud, const PointCloud &normals, const Indices &indices, const typename KdTree::Ptr &tree, float tolerance, std::vector &clusters, double eps_angle, @@ -315,6 +322,243 @@ namespace pcl } } } +#endif // PCL_HAS_FLANN + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** \brief Decompose a region of space into clusters based on the euclidean distance between points, and the normal + * angular deviation between points. Each point added to the cluster is origin to another radius search. Each point + * within radius range will be compared to the origin in respect to normal angle and euclidean distance. If both + * are under their respective threshold the point will be added to the cluster. Generally speaking the cluster + * algorithm will not stop on smooth surfaces but on surfaces with sharp edges. + * \param cloud the point cloud message + * \param normals the point cloud message containing normal information + * \param tree the spatial locator (e.g., kd-tree) used for nearest neighbors searching + * \note the tree has to be created as a spatial locator on \a cloud + * \param tolerance the spatial cluster tolerance as a measure in the L2 Euclidean space + * \param clusters the resultant clusters containing point indices (as a vector of PointIndices) + * \param eps_angle the maximum allowed difference between normals in radians for cluster/region growing + * \param min_pts_per_cluster minimum number of points that a cluster may contain (default: 1) + * \param max_pts_per_cluster maximum number of points that a cluster may contain (default: max int) + * \ingroup segmentation + */ + template + void + extractEuclideanClusters ( + const PointCloud &cloud, const PointCloud &normals, + float tolerance, const typename search::Search::Ptr &tree, + std::vector &clusters, double eps_angle, + unsigned int min_pts_per_cluster = 1, + unsigned int max_pts_per_cluster = (std::numeric_limits::max) ()) + { + if (tree->getInputCloud ()->size () != cloud.size ()) + { + PCL_ERROR("[pcl::extractEuclideanClusters] Tree built for a different point " + "cloud dataset (%zu) than the input cloud (%zu)!\n", + static_cast(tree->getInputCloud()->size()), + static_cast(cloud.size())); + return; + } + if (cloud.size () != normals.size ()) + { + PCL_ERROR("[pcl::extractEuclideanClusters] Number of points in the input point " + "cloud (%zu) different than normals (%zu)!\n", + static_cast(cloud.size()), + static_cast(normals.size())); + return; + } + // If tree gives sorted results, we can skip the first one because it is the query point itself + const std::size_t nn_start_idx = tree->getSortedResults () ? 1 : 0; + const double cos_eps_angle = std::cos (eps_angle); // compute this once instead of acos many times (faster) + + // Create a bool vector of processed point indices, and initialize it to false + std::vector processed (cloud.size (), false); + + Indices nn_indices; + std::vector nn_distances; + // Process all points in the indices vector + for (std::size_t i = 0; i < cloud.size (); ++i) + { + if (processed[i]) + continue; + + Indices seed_queue; + int sq_idx = 0; + seed_queue.push_back (static_cast (i)); + + processed[i] = true; + + while (sq_idx < static_cast (seed_queue.size ())) + { + // Search for sq_idx + if (!tree->radiusSearch (seed_queue[sq_idx], tolerance, nn_indices, nn_distances)) + { + sq_idx++; + continue; + } + + for (std::size_t j = nn_start_idx; j < nn_indices.size (); ++j) + { + if (processed[nn_indices[j]]) // Has this point been processed before ? + continue; + + //processed[nn_indices[j]] = true; + // [-1;1] + double dot_p = normals[seed_queue[sq_idx]].normal[0] * normals[nn_indices[j]].normal[0] + + normals[seed_queue[sq_idx]].normal[1] * normals[nn_indices[j]].normal[1] + + normals[seed_queue[sq_idx]].normal[2] * normals[nn_indices[j]].normal[2]; + if ( std::abs (dot_p) > cos_eps_angle ) + { + processed[nn_indices[j]] = true; + seed_queue.push_back (nn_indices[j]); + } + } + + sq_idx++; + } + + // If this queue is satisfactory, add to the clusters + if (seed_queue.size () >= min_pts_per_cluster && seed_queue.size () <= max_pts_per_cluster) + { + pcl::PointIndices r; + r.indices.resize (seed_queue.size ()); + for (std::size_t j = 0; j < seed_queue.size (); ++j) + r.indices[j] = seed_queue[j]; + + // After clustering, indices are out of order, so sort them + std::sort (r.indices.begin (), r.indices.end ()); + + r.header = cloud.header; + clusters.push_back (r); // We could avoid a copy by working directly in the vector + } + else + { + PCL_DEBUG("[pcl::extractEuclideanClusters] This cluster has %zu points, which is not between %u and %u points, so it is not a final cluster\n", + seed_queue.size (), min_pts_per_cluster, max_pts_per_cluster); + } + } + } + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** \brief Decompose a region of space into clusters based on the euclidean distance between points, and the normal + * angular deviation between points. Each point added to the cluster is origin to another radius search. Each point + * within radius range will be compared to the origin in respect to normal angle and euclidean distance. If both + * are under their respective threshold the point will be added to the cluster. Generally speaking the cluster + * algorithm will not stop on smooth surfaces but on surfaces with sharp edges. + * \param cloud the point cloud message + * \param normals the point cloud message containing normal information + * \param indices a list of point indices to use from \a cloud + * \param tree the spatial locator (e.g., kd-tree) used for nearest neighbors searching + * \note the tree has to be created as a spatial locator on \a cloud + * \param tolerance the spatial cluster tolerance as a measure in the L2 Euclidean space + * \param clusters the resultant clusters containing point indices (as PointIndices) + * \param eps_angle the maximum allowed difference between normals in radians for cluster/region growing + * \param min_pts_per_cluster minimum number of points that a cluster may contain (default: 1) + * \param max_pts_per_cluster maximum number of points that a cluster may contain (default: max int) + * \ingroup segmentation + */ + template + void + extractEuclideanClusters ( + const PointCloud &cloud, const PointCloud &normals, + const Indices &indices, const typename search::Search::Ptr &tree, + float tolerance, std::vector &clusters, double eps_angle, + unsigned int min_pts_per_cluster = 1, + unsigned int max_pts_per_cluster = (std::numeric_limits::max) ()) + { + // \note If the tree was created over , we guarantee a 1-1 mapping between what the tree returns + //and indices[i] + if (tree->getInputCloud()->size() != cloud.size()) { + PCL_ERROR("[pcl::extractEuclideanClusters] Tree built for a different point " + "cloud dataset (%zu) than the input cloud (%zu)!\n", + static_cast(tree->getInputCloud()->size()), + static_cast(cloud.size())); + return; + } + if (tree->getIndices()->size() != indices.size()) { + PCL_ERROR("[pcl::extractEuclideanClusters] Tree built for a different set of " + "indices (%zu) than the input set (%zu)!\n", + static_cast(tree->getIndices()->size()), + indices.size()); + return; + } + if (cloud.size() != normals.size()) { + PCL_ERROR("[pcl::extractEuclideanClusters] Number of points in the input point " + "cloud (%zu) different than normals (%zu)!\n", + static_cast(cloud.size()), + static_cast(normals.size())); + return; + } + // If tree gives sorted results, we can skip the first one because it is the query point itself + const std::size_t nn_start_idx = tree->getSortedResults () ? 1 : 0; + const double cos_eps_angle = std::cos (eps_angle); // compute this once instead of acos many times (faster) + // Create a bool vector of processed point indices, and initialize it to false + std::vector processed (cloud.size (), false); + + Indices nn_indices; + std::vector nn_distances; + // Process all points in the indices vector + for (const auto& point_idx : indices) + { + if (processed[point_idx]) + continue; + + Indices seed_queue; + int sq_idx = 0; + seed_queue.push_back (point_idx); + + processed[point_idx] = true; + + while (sq_idx < static_cast (seed_queue.size ())) + { + // Search for sq_idx + if (!tree->radiusSearch (cloud[seed_queue[sq_idx]], tolerance, nn_indices, nn_distances)) + { + sq_idx++; + continue; + } + + for (std::size_t j = nn_start_idx; j < nn_indices.size (); ++j) + { + if (processed[nn_indices[j]]) // Has this point been processed before ? + continue; + + //processed[nn_indices[j]] = true; + // [-1;1] + double dot_p = normals[seed_queue[sq_idx]].normal[0] * normals[nn_indices[j]].normal[0] + + normals[seed_queue[sq_idx]].normal[1] * normals[nn_indices[j]].normal[1] + + normals[seed_queue[sq_idx]].normal[2] * normals[nn_indices[j]].normal[2]; + if ( std::abs (dot_p) > cos_eps_angle ) + { + processed[nn_indices[j]] = true; + seed_queue.push_back (nn_indices[j]); + } + } + + sq_idx++; + } + + // If this queue is satisfactory, add to the clusters + if (seed_queue.size () >= min_pts_per_cluster && seed_queue.size () <= max_pts_per_cluster) + { + pcl::PointIndices r; + r.indices.resize (seed_queue.size ()); + for (std::size_t j = 0; j < seed_queue.size (); ++j) + r.indices[j] = seed_queue[j]; + + // After clustering, indices are out of order, so sort them + std::sort (r.indices.begin (), r.indices.end ()); + + r.header = cloud.header; + clusters.push_back (r); + } + else + { + PCL_DEBUG("[pcl::extractEuclideanClusters] This cluster has %zu points, which is not between %u and %u points, so it is not a final cluster\n", + seed_queue.size (), min_pts_per_cluster, max_pts_per_cluster); + } + } + } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/test/features/CMakeLists.txt b/test/features/CMakeLists.txt index d75ed0acd47..9362ba5908d 100644 --- a/test/features/CMakeLists.txt +++ b/test/features/CMakeLists.txt @@ -1,6 +1,7 @@ set(SUBSYS_NAME tests_features) set(SUBSYS_DESC "Point cloud library features module unit tests") PCL_SET_TEST_DEPENDENCIES(SUBSYS_DEPS features) +list(APPEND SUBSYS_DEPS kdtree) set(OPT_DEPS io keypoints) # module does not depend on these PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) diff --git a/test/filters/CMakeLists.txt b/test/filters/CMakeLists.txt index 80fb22c75d7..12f74e0d8f6 100644 --- a/test/filters/CMakeLists.txt +++ b/test/filters/CMakeLists.txt @@ -1,6 +1,7 @@ set(SUBSYS_NAME tests_filters) set(SUBSYS_DESC "Point cloud library filters module unit tests") PCL_SET_TEST_DEPENDENCIES(SUBSYS_DEPS filters) +list(APPEND SUBSYS_DEPS kdtree) set(OPT_DEPS io features segmentation) PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) diff --git a/test/keypoints/CMakeLists.txt b/test/keypoints/CMakeLists.txt index 173bf4473d4..07f49495464 100644 --- a/test/keypoints/CMakeLists.txt +++ b/test/keypoints/CMakeLists.txt @@ -1,6 +1,7 @@ set(SUBSYS_NAME tests_keypoints) set(SUBSYS_DESC "Point cloud library keypoints module unit tests") PCL_SET_TEST_DEPENDENCIES(SUBSYS_DEPS keypoints) +list(APPEND SUBSYS_DEPS kdtree) set(OPT_DEPS io) # module does not depend on these PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) diff --git a/test/recognition/CMakeLists.txt b/test/recognition/CMakeLists.txt index 0377cfda7f2..c991973c926 100644 --- a/test/recognition/CMakeLists.txt +++ b/test/recognition/CMakeLists.txt @@ -1,6 +1,7 @@ set(SUBSYS_NAME tests_recognition) set(SUBSYS_DESC "Point cloud library recognition module unit tests") PCL_SET_TEST_DEPENDENCIES(SUBSYS_DEPS recognition) +list(APPEND SUBSYS_DEPS kdtree) set(OPT_DEPS keypoints) # module does not depend on these PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) diff --git a/test/registration/CMakeLists.txt b/test/registration/CMakeLists.txt index f58ee30be17..7c67e30f528 100644 --- a/test/registration/CMakeLists.txt +++ b/test/registration/CMakeLists.txt @@ -1,8 +1,8 @@ set(SUBSYS_NAME tests_registration) set(SUBSYS_DESC "Point cloud library registration module unit tests") -PCL_SET_TEST_DEPENDENCIES(SUBSYS_DEPS io registration) +PCL_SET_TEST_DEPENDENCIES(SUBSYS_DEPS registration) +list(APPEND SUBSYS_DEPS io kdtree) set(OPT_DEPS io) # module does not depend on these - PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) PCL_SUBSYS_DEPEND(build NAME ${SUBSYS_NAME} DEPS ${SUBSYS_DEPS} OPT_DEPS ${OPT_DEPS}) diff --git a/test/registration/test_correspondence_estimation.cpp b/test/registration/test_correspondence_estimation.cpp index 3e026707841..ce8025de0f1 100644 --- a/test/registration/test_correspondence_estimation.cpp +++ b/test/registration/test_correspondence_estimation.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include diff --git a/test/registration/test_ndt.cpp b/test/registration/test_ndt.cpp index 05294c85390..02e188c03c1 100644 --- a/test/registration/test_ndt.cpp +++ b/test/registration/test_ndt.cpp @@ -43,6 +43,7 @@ #include #include #include +#include using namespace pcl; using namespace pcl::io; diff --git a/test/registration/test_registration.cpp b/test/registration/test_registration.cpp index 9bdc277bb39..d5c140ade15 100644 --- a/test/registration/test_registration.cpp +++ b/test/registration/test_registration.cpp @@ -58,6 +58,7 @@ #include #include #include +#include // We need Histogram<2> to function, so we'll explicitly add kdtree_flann.hpp here #include //(pcl::Histogram<2>) diff --git a/test/registration/test_registration_api.cpp b/test/registration/test_registration_api.cpp index d50344a6d9e..7395afafa7f 100644 --- a/test/registration/test_registration_api.cpp +++ b/test/registration/test_registration_api.cpp @@ -64,6 +64,7 @@ #include #include #include +#include #include "test_registration_api_data.h" diff --git a/test/registration/test_sac_ia.cpp b/test/registration/test_sac_ia.cpp index c03658b56db..44805639d33 100644 --- a/test/registration/test_sac_ia.cpp +++ b/test/registration/test_sac_ia.cpp @@ -46,6 +46,7 @@ #include #include #include +#include using namespace pcl; using namespace pcl::io; diff --git a/test/search/CMakeLists.txt b/test/search/CMakeLists.txt index 3729072b075..87cdc860f4d 100644 --- a/test/search/CMakeLists.txt +++ b/test/search/CMakeLists.txt @@ -1,6 +1,7 @@ set(SUBSYS_NAME tests_search) set(SUBSYS_DESC "Point cloud library search module unit tests") PCL_SET_TEST_DEPENDENCIES(SUBSYS_DEPS search) +list(APPEND SUBSYS_DEPS kdtree) set(OPT_DEPS io) PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) diff --git a/test/surface/CMakeLists.txt b/test/surface/CMakeLists.txt index 94574096ffb..f9db7ddbd2f 100644 --- a/test/surface/CMakeLists.txt +++ b/test/surface/CMakeLists.txt @@ -1,6 +1,7 @@ set(SUBSYS_NAME tests_surface) set(SUBSYS_DESC "Point cloud library surface module unit tests") PCL_SET_TEST_DEPENDENCIES(SUBSYS_DEPS surface) +list(APPEND SUBSYS_DEPS kdtree) set(OPT_DEPS io features sample_consensus filters) # module does not depend on these PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) diff --git a/test/visualization/CMakeLists.txt b/test/visualization/CMakeLists.txt index ae3a0e40159..fe8eb59e3d4 100644 --- a/test/visualization/CMakeLists.txt +++ b/test/visualization/CMakeLists.txt @@ -1,6 +1,7 @@ set(SUBSYS_NAME tests_visualization) set(SUBSYS_DESC "Point cloud library visualization module unit tests") PCL_SET_TEST_DEPENDENCIES(SUBSYS_DEPS visualization) +list(APPEND SUBSYS_DEPS kdtree) set(OPT_DEPS features) # module does not depend on these PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON) diff --git a/tools/sac_segmentation_plane.cpp b/tools/sac_segmentation_plane.cpp index f443c6b5125..5a70ea02aa4 100644 --- a/tools/sac_segmentation_plane.cpp +++ b/tools/sac_segmentation_plane.cpp @@ -36,6 +36,7 @@ */ #include +#include #include #include #include From 4494de3d36cac60bd7b3dc10f75ab80f8da5abac Mon Sep 17 00:00:00 2001 From: Markus Vieth Date: Sun, 23 Aug 2026 14:37:04 +0200 Subject: [PATCH 2/2] Changes per review --- registration/src/gicp6d.cpp | 9 +++++---- .../include/pcl/segmentation/extract_clusters.h | 11 ++++++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/registration/src/gicp6d.cpp b/registration/src/gicp6d.cpp index be07c31e2c9..9c09cc2137d 100644 --- a/registration/src/gicp6d.cpp +++ b/registration/src/gicp6d.cpp @@ -88,16 +88,17 @@ GeneralizedIterativeClosestPoint6D::setInputTarget( #if PCL_HAS_NANOFLANN // 6 search dimensions for MyPointRepresentation; max. 10 points per leaf is a good // choice for one-nearest-neighbor search - auto tree = new pcl::search::KdTreeNanoflann(false, 10); + auto tree = + pcl::make_shared>(false, 10); tree->setPointRepresentation(pcl::make_shared(point_rep_)); tree->setInputCloud(target_lab_); - target_tree_lab_.reset(tree); + target_tree_lab_ = tree; #else #if PCL_HAS_FLANN - auto tree = new pcl::search::KdTree(false); + auto tree = pcl::make_shared>(false); tree->setPointRepresentation(pcl::make_shared(point_rep_)); tree->setInputCloud(target_lab_); - target_tree_lab_.reset(tree); + target_tree_lab_ = tree; #else #error "GeneralizedIterativeClosestPoint6D needs nanoflann or FLANN!" #endif diff --git a/segmentation/include/pcl/segmentation/extract_clusters.h b/segmentation/include/pcl/segmentation/extract_clusters.h index b107420fe8d..70127a3b785 100644 --- a/segmentation/include/pcl/segmentation/extract_clusters.h +++ b/segmentation/include/pcl/segmentation/extract_clusters.h @@ -390,7 +390,7 @@ namespace pcl while (sq_idx < static_cast (seed_queue.size ())) { // Search for sq_idx - if (!tree->radiusSearch (seed_queue[sq_idx], tolerance, nn_indices, nn_distances)) + if (!tree->radiusSearch (cloud[seed_queue[sq_idx]], tolerance, nn_indices, nn_distances)) { sq_idx++; continue; @@ -475,10 +475,15 @@ namespace pcl static_cast(cloud.size())); return; } - if (tree->getIndices()->size() != indices.size()) { + const auto tree_indices = tree->getIndices(); + if (!tree_indices) { + PCL_ERROR("[pcl::extractEuclideanClusters] Tree was built without indices, but an indices set was provided!\n"); + return; + } + if (tree_indices->size() != indices.size()) { PCL_ERROR("[pcl::extractEuclideanClusters] Tree built for a different set of " "indices (%zu) than the input set (%zu)!\n", - static_cast(tree->getIndices()->size()), + static_cast(tree_indices->size()), indices.size()); return; }