基本思想
对空间某点P,通过KD-Tree近邻搜索算法查找k个距离p最近的点,这些点中距离小于设定阈值的便聚类到集合a中。如果Q中元素的数目不在增加,整个聚类过程便结束;否则须在集合Q中选取p点以外的点,重复上述过程,直到Q中元素的数目不在增加为止。
流程图
完整代码
#include <iostream>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/filters/extract_indices.h> // 根据索引提取点云
#include <pcl/filters/voxel_grid.h> // 体素滤波
#include <pcl/kdtree/kdtree.h> // kd树
#include <pcl/sample_consensus/method_types.h> // 采样方法
#include <pcl/sample_consensus/model_types.h> // 采样模型
#include <pcl/ModelCoefficients.h> // 模型系数
#include <pcl/segmentation/sac_segmentation.h> // 随机采样分割
#include <pcl/segmentation/extract_clusters.h> // 欧式聚类分割
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
using namespace std;
int
main(int argc, char** argv)
{
//--------------------------读取桌面场景点云---------------------------------
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPCDFile<pcl::PointXYZ>("table_scene_lms400.pcd", *cloud);
cout << "读取点云: " << cloud->points.size() << " 个." << endl;
//---------------------------体素滤波下采样----------------------------------
pcl::VoxelGrid<pcl::PointXYZ> vg;
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
vg.setInputCloud(cloud);
vg.setLeafSize(0.01f, 0.01f, 0.01f);
vg.filter(*cloud_filtered);
cout << "体素滤波后还有: " << cloud_filtered->points.size() << " 个." << endl;
//--------------------创建平面模型分割的对象并设置参数-----------------------
pcl::SACSegmentation<pcl::PointXYZ> seg;
pcl::PointIndices::Ptr inliers(new pcl::PointIndices);
pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_plane(new pcl::PointCloud<pcl::PointXYZ>);
seg.setOptimizeCoefficients(true);
seg.setModelType(pcl::SACMODEL_PLANE); // 分割模型,平面模型
seg.setMethodType(pcl::SAC_RANSAC); // 参数估计方法,随机采样一致性
seg.setMaxIterations(100); // 最大的迭代的次数
seg.setDistanceThreshold(0.02); // 设置符合模型的内点阈值
// -------------模型分割,直到剩余点云数量在30%以上,确保模型点云较好----------
int i = 0, nr_points = (int)cloud_filtered->points.size();// 下采样前点云数量
while (cloud_filtered->points.size() > 0.3 * nr_points)
{
seg.setInputCloud(cloud_filtered);
seg.segment(*inliers, *coefficients);// 分割
if (inliers->indices.size() == 0)
{
cout << "Could not estimate a planar model for the given dataset." << endl;
break;
}
//---------------------------根据索引提取点云-------------------------------
pcl::ExtractIndices<pcl::PointXYZ> extract;
extract.setInputCloud(cloud_filtered);
extract.setIndices(inliers); // 提取符合平面模型的内点
extract.setNegative(false);
//--------------------------平面模型内点------------------------------------
extract.filter(*cloud_plane);
cout << "平面模型: " << cloud_plane->points.size() << "个点." << endl;
//-------------------移去平面局内点,提取剩余点云---------------------------
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_f(new pcl::PointCloud<pcl::PointXYZ>);
extract.setNegative(true);
extract.filter(*cloud_f);
*cloud_filtered = *cloud_f; // 剩余点云
}
// --------------桌子平面上的点云团, 使用欧式聚类的算法对点云聚类分割----------
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
tree->setInputCloud(cloud_filtered); // 桌子平面上其他的点云
vector<pcl::PointIndices> cluster_indices; // 点云团索引
pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;// 欧式聚类对象
ec.setClusterTolerance(0.02); // 设置近邻搜索的搜索半径为2cm
ec.setMinClusterSize(100); // 设置一个聚类需要的最少的点数目为100
ec.setMaxClusterSize(25000); // 设置一个聚类需要的最大点数目为25000
ec.setSearchMethod(tree); // 设置点云的搜索机制
ec.setInputCloud(cloud_filtered);
ec.extract(cluster_indices); // 从点云中提取聚类,并将点云索引保存在cluster_indices中
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_cluster_all(new pcl::PointCloud<pcl::PointXYZ>);
//------------迭代访问点云索引cluster_indices,直到分割处所有聚类---------------
int j = 0;
for (vector<pcl::PointIndices>::const_iterator it = cluster_indices.begin(); it != cluster_indices.end(); ++it)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_cluster(new pcl::PointCloud<pcl::PointXYZ>);
//创建新的点云数据集cloud_cluster,将所有当前聚类写入到点云数据集中
for (vector<int>::const_iterator pit = it->indices.begin(); pit != it->indices.end(); ++pit)
cloud_cluster->points.push_back(cloud_filtered->points[*pit]); //获取每一个点云团的点
cloud_cluster->width = cloud_cluster->points.size();
cloud_cluster->height = 1;
cloud_cluster->is_dense = true;
cout << "PointCloud representing the Cluster: " << cloud_cluster->points.size() << " data points." << endl;
stringstream ss;
ss << "cloud_cluster_" << j << ".pcd";
pcl::PCDWriter writer;
writer.write<pcl::PointXYZ>(ss.str(), *cloud_cluster, false);
j++;
*cloud_cluster_all += *cloud_cluster;
}
pcl::io::savePCDFileASCII("cloud_cluster_all.pcd", *cloud_cluster_all);
//------------------------点云显示------------------------------------
pcl::visualization::PCLVisualizer viewer("3D Viewer");
viewer.setBackgroundColor(0, 0, 0);
//viewer.addCoordinateSystem (1.0);
viewer.initCameraParameters();
//--------------------平面上的点云 红色------------------------------
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> cloud_plane_handler(cloud_plane, 255, 0, 0);
viewer.addPointCloud(cloud_plane, cloud_plane_handler, "plan point");
viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "plan point");
//--------------------平面外的点云 绿色------------------------------
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> cloud_cluster_handler(cloud_cluster_all, 0, 255, 0);
viewer.addPointCloud(cloud_cluster_all, cloud_cluster_handler, "cloud_cluster point");
viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud_cluster point");
while (!viewer.wasStopped()) {
viewer.spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
return (0);
}
结果展示
获取pcd文件
链接:https://pan.baidu.com/s/1ZcaI4tbeSKHmMDvIg0isYA
提取码:Bing
复制这段内容后打开百度网盘手机App,操作更方便哦