1欧几里得聚类提取
欧几里得聚类提取
在本教程中,我们将学习如何使用 class 提取 Euclidean 簇。为了不使 教程中,它的某些元素,例如平面分割算法, 这里就不解释了。请查看 平面模型分割教程 以了解更多信息。pcl::EuclideanClusterExtraction
理论入门
代码
首先,下载数据集 table_scene_lms400.pcd 并将其保存到磁盘的某个位置。
然后,创建一个文件,比如说,在你的收藏夹中 编辑器,并将以下内容放入其中:cluster_extraction.cpp
1#include <pcl/ModelCoefficients.h>
2#include <pcl/point_types.h>
3#include <pcl/io/pcd_io.h>
4#include <pcl/filters/extract_indices.h>
5#include <pcl/filters/voxel_grid.h>
6#include <pcl/features/normal_3d.h>
7#include <pcl/search/kdtree.h>
8#include <pcl/sample_consensus/method_types.h>
9#include <pcl/sample_consensus/model_types.h>
10#include <pcl/segmentation/sac_segmentation.h>
11#include <pcl/segmentation/extract_clusters.h>
12#include <iomanip> // for setw, setfill
13
14int
15main ()
16{
17 // Read in the cloud data
18 pcl::PCDReader reader;
19 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>), cloud_f (new pcl::PointCloud<pcl::PointXYZ>);
20 reader.read ("table_scene_lms400.pcd", *cloud);
21 std::cout << "PointCloud before filtering has: " << cloud->size () << " data points." << std::endl; //*
22
23 // Create the filtering object: downsample the dataset using a leaf size of 1cm
24 pcl::VoxelGrid<pcl::PointXYZ> vg;
25 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>);
26 vg.setInputCloud (cloud);
27 vg.setLeafSize (0.01f, 0.01f, 0.01f);
28 vg.filter (*cloud_filtered);
29 std::cout << "PointCloud after filtering has: " << cloud_filtered->size () << " data points." << std::endl; //*
30
31 // Create the segmentation object for the planar model and set all the parameters
32 pcl::SACSegmentation<pcl::PointXYZ> seg;
33 pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
34 pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);
35 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_plane (new pcl::PointCloud<pcl::PointXYZ> ());
36 pcl::PCDWriter writer;
37 seg.setOptimizeCoefficients (true);
38 seg.setModelType (pcl::SACMODEL_PLANE);
39 seg.setMethodType (pcl::SAC_RANSAC);
40 seg.setMaxIterations (100);
41 seg.setDistanceThreshold (0.02);
42
43 int nr_points = (int) cloud_filtered->size ();
44 while (cloud_filtered->size () > 0.3 * nr_points)
45 {
46 // Segment the largest planar component from the remaining cloud
47 seg.setInputCloud (cloud_filtered);
48 seg.segment (*inliers, *coefficients);
49 if (inliers->indices.size () == 0)
50 {
51 std::cout << "Could not estimate a planar model for the given dataset." << std::endl;
52 break;
53 }
54
55 // Extract the planar inliers from the input cloud
56 pcl::ExtractIndices<pcl::PointXYZ> extract;
57 extract.setInputCloud (cloud_filtered);
58 extract.setIndices (inliers);
59 extract.setNegative (false);
60
61 // Get the points associated with the planar surface
62 extract.filter (*cloud_plane);
63 std::cout << "PointCloud representing the planar component: " << cloud_plane->size () << " data points." << std::endl;
64
65 // Remove the planar inliers, extract the rest
66 extract.setNegative (true);
67 extract.filter (*cloud_f);
68 *cloud_filtered = *cloud_f;
69 }
70
71 // Creating the KdTree object for the search method of the extraction
72 pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ>);
73 tree->setInputCloud (cloud_filtered);
74
75 std::vector<pcl::PointIndices> cluster_indices;
76 pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;
77 ec.setClusterTolerance (0.02); // 2cm
78 ec.setMinClusterSize (100);
79 ec.setMaxClusterSize (25000);
80 ec.setSearchMethod (tree);
81 ec.setInputCloud (cloud_filtered);
82 ec.extract (cluster_indices);
83
84 int j = 0;
85 for (const auto& cluster : cluster_indices)
86 {
87 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_cluster (new pcl::PointCloud<pcl::PointXYZ>);
88 for (const auto& idx : cluster.indices) {
89 cloud_cluster->push_back((*cloud_filtered)[idx]);
90 } //*
91 cloud_cluster->width = cloud_cluster->size ();
92 cloud_cluster->height = 1;
93 cloud_cluster->is_dense = true;
94
95 std::cout << "PointCloud representing the Cluster: " << cloud_cluster->size () << " data points." << std::endl;
96 std::stringstream ss;
97 ss << std::setw(4) << std::setfill('0') << j;
98 writer.write<pcl::PointXYZ> ("cloud_cluster_" + ss.str () + ".pcd", *cloud_cluster, false); //*
99 j++;
100 }
101
102 return (0);
103}
解释
现在,让我们逐个分解代码,跳过显而易见的。
// Read in the cloud data
pcl::PCDReader reader;
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>), cloud_f (new pcl::PointCloud<pcl::PointXYZ>);
reader.read ("table_scene_lms400.pcd", *cloud);
std::cout << "PointCloud before filtering has: " << cloud->size () << " data points." << std::endl;
.
.
.
while (cloud_filtered->size () > 0.3 * nr_points)
{
.
.
.
// Remove the plane inliers, extract the rest
extract.setNegative (true);
extract.filter (*cloud_f);
cloud_filtered = cloud_f;
}
上面的代码已经在其他教程中进行了描述,因此您可以阅读 那里的解释(特别是 Plane model segmentation 和 Extracting indices from a PointCloud)。
// Creating the KdTree object for the search method of the extraction
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ>);
tree->setInputCloud (cloud_filtered);
在那里,我们为提取的搜索方法创建了一个 KdTree 对象 算法。
std::vector<pcl::PointIndices> cluster_indices;
在这里,我们将创建一个 PointIndices 的向量,其中包含 vector<int> 中的实际索引信息。检测到的每个 cluster 保存在此处 - 请注意 cluster_indices 是一个 vector 包含每个检测到的集群的一个 PointIndices 实例。所以 cluster_indices[0] 包含了点云中第一个集群的所有索引。
pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;
ec.setClusterTolerance (0.02); // 2cm
ec.setMinClusterSize (100);
ec.setMaxClusterSize (25000);
ec.setSearchMethod (tree);
ec.setInputCloud (cloud_filtered);
ec.extract (cluster_indices);
在这里,我们将创建一个具有点类型的 EuclideanClusterExtraction 对象 PointXYZ 的 URL,因为我们的点云是 PointXYZ 类型。我们还将 参数和变量。小心设置正确的 setClusterTolerance() 的值。如果采用非常小的值,则可以 碰巧一个实际的对象可以看作是多个集群。另一方面 如果将该值设置得太高,则可能会发生多个对象被视为一个群集的情况。因此,我们的建议是测试和试用 哪个值适合您的数据集。
我们规定找到的集群必须至少具有 setMinClusterSize() 点和最大 setMaxClusterSize() 点。
现在我们从点云中提取集群并将索引保存在 cluster_indices 中。为了将每个集群从 vector<PointIndices 中分离出来>我们必须遍历 cluster_indices,为 每个条目,并将当前集群的所有点写入 PointCloud。
int j = 0;
for (const auto& cluster : cluster_indices)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_cluster (new pcl::PointCloud<pcl::PointXYZ>);
for (const auto& idx : cluster.indices) {
cloud_cluster->push_back((*cloud_filtered)[idx]);
} //*
cloud_cluster->width = cloud_cluster->size ();
cloud_cluster->height = 1;
cloud_cluster->is_dense = true;
编译和运行程序
将以下行添加到您的CMakeLists.txt
1cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
2
3project(cluster_extraction)
4
5find_package(PCL 1.2 REQUIRED)
6
7include_directories(${PCL_INCLUDE_DIRS})
8link_directories(${PCL_LIBRARY_DIRS})
9add_definitions(${PCL_DEFINITIONS})
10
11add_executable (cluster_extraction cluster_extraction.cpp)
12target_link_libraries (cluster_extraction ${PCL_LIBRARIES})
创建可执行文件后,您可以运行它。只需:
$ ./cluster_extraction
您将看到类似于以下内容的内容:
PointCloud before filtering has: 460400 data points.
PointCloud after filtering has: 41049 data points.
[SACSegmentation::initSACModel] Using a model of type: SACMODEL_PLANE
[SACSegmentation::initSAC] Using a method of type: SAC_RANSAC with a model threshold of 0.020000
[SACSegmentation::initSAC] Setting the maximum number of iterations to 100
PointCloud representing the planar component: 20522 data points.
[SACSegmentation::initSACModel] Using a model of type: SACMODEL_PLANE
[SACSegmentation::initSAC] Using a method of type: SAC_RANSAC with a model threshold of 0.020000
[SACSegmentation::initSAC] Setting the maximum number of iterations to 100
PointCloud representing the planar component: 12429 data points.
PointCloud representing the Cluster: 4883 data points.
PointCloud representing the Cluster: 1386 data points.
PointCloud representing the Cluster: 320 data points.
PointCloud representing the Cluster: 290 data points.
PointCloud representing the Cluster: 120 data points.
您还可以查看输出 cloud_cluster_0000.pcd、cloud_cluster_0001.pcd、 cloud_cluster_0002.pcd、cloud_cluster_0003.pcd 和 cloud_cluster_0004.pcd:
$ ./pcl_viewer cloud_cluster_0000.pcd cloud_cluster_0001.pcd cloud_cluster_0002.pcd cloud_cluster_0003.pcd cloud_cluster_0004.pcd
现在,您可以在一个查看器中看到不同的集群。您应该会看到 类似于以下内容: