PCL 【点云分割】

一、平面模型分割

1、分割代码

//平面分割代码
#include <pcl/ModelCoefficients.h>//模型系数相关头文件
#include <pcl/sample_consensus/method_types.h>//模型定义头文件
#include <pcl/sample_consensus/model_types.h>//随机参数估计方法头文件
#include <pcl/segmentation/sac_segmentation.h>//基于采样一致性分割的类的头文件
pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);//创建分割时所需要的模型系数对象coefficients以及存储内点的点索引集合对象
pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
//创建分割对象
  pcl::SACSegmentation<pcl::PointXYZ> seg;
 //可选设置,设置模型系数需要优化
  seg.setOptimizeCoefficients (true);
//必须设置,设置分割的模型类型、所用的随机参数估计方法、距离阈值、输入点云
  seg.setModelType (pcl::SACMODEL_PLANE);
  seg.setMethodType (pcl::SAC_RANSAC);
  seg.setDistanceThreshold (0.01);//距离阈值,包括0.01
  seg.setInputCloud (cloud.makeShared ());
  seg.segment (*inliers, *coefficients);//分割,存储分割结果到点集合inliers及存储平面模型的系数coefficients

2、完整代码

#include <iostream>
#include <pcl/ModelCoefficients.h>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/sample_consensus/method_types.h>//模型定义头文件
#include <pcl/sample_consensus/model_types.h>//随机参数估计方法头文件
#include <pcl/segmentation/sac_segmentation.h>//基于采样一致性分割的类的头文件

#include <pcl/visualization/pcl_visualizer.h>//可视化头文件
int
main (int argc, char** argv)
{
  pcl::PointCloud<pcl::PointXYZ> cloud;
//填充点云数据
cloud.width  = 15;
  cloud.height = 1;
  cloud.points.resize (cloud.width * cloud.height);
//生成数据
for (size_t i = 0; i < cloud.points.size (); ++i)
  {
    cloud.points[i].x = 1024 * rand () / (RAND_MAX + 1.0f);
    cloud.points[i].y = 1024 * rand () / (RAND_MAX + 1.0f);
    cloud.points[i].z = 1.0;
  }
//设置几个局外点,使其偏离z=1的平面
cloud.points[0].z = 2.0;
cloud.points[3].z = -2.0;
cloud.points[6].z = 4.0;
cloud.points[7].z = 1.01;
cloud.points[8].z = 1.005;
  std::cerr << "Point cloud data: " << cloud.points.size () <<" points" << std::endl;
for (size_t i = 0; i < cloud.points.size (); ++i)
    std::cerr << "    " << cloud.points[i].x << " " 
<< cloud.points[i].y << " " 
<< cloud.points[i].z << std::endl;
pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);//创建分割时所需要的模型系数对象coefficients以及存储内点的点索引集合对象
pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
//创建分割对象
  pcl::SACSegmentation<pcl::PointXYZ> seg;
 //可选设置,设置模型系数需要优化
  seg.setOptimizeCoefficients (true);
//必须设置,设置分割的模型类型、所用的随机参数估计方法、距离阈值、输入点云
  seg.setModelType (pcl::SACMODEL_PLANE);
  seg.setMethodType (pcl::SAC_RANSAC);
  seg.setDistanceThreshold (0.01);//距离阈值,包括0.01
  seg.setInputCloud (cloud.makeShared ());
  seg.segment (*inliers, *coefficients);//分割,存储分割结果到点集合inliers及存储平面模型的系数coefficients
if (inliers->indices.size () == 0)
  {
    PCL_ERROR ("Could not estimate a planar model for the given dataset.");
return (-1);
  }
//输出平面模型的系数 a,b,c,d
  std::cerr << "Model coefficients: " << coefficients->values[0] << " " 
<<coefficients->values[1] << " "
<<coefficients->values[2] << " " 
<<coefficients->values[3] <<std::endl;
  std::cerr << "Model inliers: " << inliers->indices.size () << std::endl;
  //输出点集合的坐标
for (size_t i = 0; i < inliers->indices.size (); ++i)
    std::cerr << inliers->indices[i] << "    " <<cloud.points[inliers->indices[i]].x << " "
<<cloud.points[inliers->indices[i]].y << " "
<<cloud.points[inliers->indices[i]].z << std::endl;

//可视化
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3d viewer"));
//左边窗口显示输入的点云(待拟合)
int v1(0);
viewer->createViewPort(0,0,0.5,1,v1);
viewer->setBackgroundColor(0,0,0,v1);
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> color_in(cloud.makeShared(),255,0,0);
viewer->addPointCloud<pcl::PointXYZ>(cloud.makeShared(),color_in,"cloud_in",v1);
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,9,"cloud_in",v1);

//右边的窗口显示拟合之后的点云
int v2(0);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_out(new pcl::PointCloud<pcl::PointXYZ>);//存放最后拟合好的平面
cloud_out->width = inliers->indices.size();
cloud_out->height = 1;
cloud_out->is_dense = false;
cloud_out->resize(cloud_out->height * cloud_out->width);
for(size_t i=0;i<inliers->indices.size();i++)
{
    cloud_out->points[i].x = cloud.points[inliers->indices[i]].x;
    cloud_out->points[i].y = cloud.points[inliers->indices[i]].y;
    cloud_out->points[i].z = cloud.points[inliers->indices[i]].z;
}
std::cout<<"convert succeed!"<<std::endl;
viewer->createViewPort(0.5,0,1,1,v2);
viewer->setBackgroundColor(255,255,255,v2);
viewer->addPointCloud<pcl::PointXYZ>(cloud_out,"cloud_out",v2);
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR,0,255,0,"cloud_out",v2);
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,9,"cloud_out",v2);

while (!viewer->wasStopped())
{
    viewer->spinOnce();
}
return (0);
}

3、可视化 

 

 

二、圆柱体模型分割提取

1、完整代码(不太懂,和之前的从点云中提取一个子集流程基本相同)

#include <pcl/ModelCoefficients.h>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/extract_indices.h>
#include <pcl/filters/passthrough.h>
#include <pcl/features/normal_3d.h>
#include <pcl/sample_consensus/method_types.h>
#include <pcl/sample_consensus/model_types.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/visualization/pcl_visualizer.h>//可视化相关头文件

typedef pcl::PointXYZ PointT;

int
main (int argc, char** argv)
{
  // All the objects needed
  pcl::PCDReader reader;//点云读入对象
  pcl::PassThrough<PointT> pass;//直通滤波器对象
  pcl::NormalEstimation<PointT, pcl::Normal> ne;//法线估计对象
  pcl::SACSegmentationFromNormals<PointT, pcl::Normal> seg; //分割对象
  pcl::PCDWriter writer;//写对象
  pcl::ExtractIndices<PointT> extract;//点提取对象
  pcl::ExtractIndices<pcl::Normal> extract_normals;//法线提取对象
  pcl::search::KdTree<PointT>::Ptr tree (new pcl::search::KdTree<PointT> ());//搜索方式对象

  // Datasets
  pcl::PointCloud<PointT>::Ptr cloud (new pcl::PointCloud<PointT>);
  pcl::PointCloud<PointT>::Ptr cloud_filtered (new pcl::PointCloud<PointT>);
  pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);
  pcl::PointCloud<PointT>::Ptr cloud_filtered2 (new pcl::PointCloud<PointT>);
  pcl::PointCloud<pcl::Normal>::Ptr cloud_normals2 (new pcl::PointCloud<pcl::Normal>);
  pcl::ModelCoefficients::Ptr coefficients_plane (new pcl::ModelCoefficients), coefficients_cylinder (new pcl::ModelCoefficients);
  pcl::PointIndices::Ptr inliers_plane (new pcl::PointIndices), inliers_cylinder (new pcl::PointIndices);//一个指向索引的指针

  // Read in the cloud data
  reader.read ("table_scene_mug_stereo_textured.pcd", *cloud);
  std::cerr << "PointCloud has: " << cloud->points.size () << " data points." << std::endl;

  // Build a passthrough filter to remove spurious NaNs
  //对cloud的z fields进行滤波,剔除0~1.5之外的点
  pass.setInputCloud (cloud);//设置输入点云
  pass.setFilterFieldName ("z");//设置滤波的field
  pass.setFilterLimits (0, 1.5);//滤波范围
  pass.filter (*cloud_filtered);//滤波结果存放
  std::cerr << "PointCloud after filtering has: " << cloud_filtered->points.size () << " data points." << std::endl;

  // Estimate point normals
  ne.setSearchMethod (tree);//搜索方式
  ne.setInputCloud (cloud_filtered);//输入点云为直通滤波器的结果
  ne.setKSearch (50);//选择最近邻的50个点进行法线估计
  ne.compute (*cloud_normals);//法线的计算结果存放至cloud_normals

  // Create the segmentation object for the planar model and set all the parameters
  seg.setOpti
<think>首先,用户的问题是:“我想使用PCL进行点云分割提取各个组件 请问PCL 点云分割 提取组件”。这涉及到点云分割,目的是提取点云中的不同组件。 我需要参考提供的引用内容,这些引用是关于点云分割的,特别是PCL库中的方法。引用包括: - 引用[1]: PCL点云分割库包含多种算法,用于将点云分割为不同簇,适合处理由多个隔离区域空间组成的点云。提到平面模型分割和圆柱模型分割的结果。 - 引用[2]: PCL提供10来种点云分割的方法,点云识别的深度学习需要好的点云分割器。 - 引用[3]: 点云分割是物体识别与分类的一个步骤,最终目的是物体识别和分类。 - 引用[4]: 描述基于区域生长分割算法流程,包括设置聚类区域、种子点序列、法线夹角和曲率阈值。 - 引用[5]: 提到基于欧氏距离、平滑度、RGB颜色的分割方法,用户可自定义约束条件,但存在缺陷如无初始种子、过分割或欠分割。 用户的需求是使用PCL进行点云分割提取各个组件。所以,我应该提供一个清晰的步骤,解释如何使用PCL来实现这一点。 根据系统级指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如[^1]。 - 回答结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 结构上,我应该: 1. 介绍点云分割的基本概念。 2. 解释PCL中的点云分割方法。 3. 提供具体步骤或代码示例如何使用PCL提取组件。 4. 基于引用,讨论相关方法。 5. 在末尾生成相关问题。 从引用中,我可以看出PCL有几种分割方法,如基于区域生长(引用[4])、基于欧氏距离(引用[5])等。 关键方法: - 基于区域生长分割:使用种子点、法线夹角和曲率阈值来聚类点。 - 其他方法如平面模型分割、圆柱模型分割(引用[1])。 在回答中,我应该逐步引导用户: 1. **理解点云分割**:点云分割是将点云分成多个部分的过程,每个部分代表一个对象或组件。 2. **选择PCL方法**:PCL提供多种算法,如区域生长分割、欧氏聚类分割等。 3. **具体实现**:提供一个代码示例或伪代码,展示如何使用这些方法。 4. **注意事项**:讨论可能的缺陷,如过分割或欠分割(引用[5])。 5. **生成相关问题**:在末尾添加相关问题。 由于用户提到“提取各个组件”,这类似于聚类分割点云中的不同对象。 一个常见的方法是欧氏聚类分割(Euclidean Cluster Extraction),这在PCL中很流行。 从引用[4]和[5],区域生长和条件聚类是选项。 让我草拟回答结构: - 开头:解释点云分割的目的。 - 步骤1:安装和准备PCL环境。 - 步骤2:加载点云数据。 - 步骤3:选择分割方法(例如,欧氏聚类分割区域生长)。 - 步骤4:设置参数(如距离阈值、种子点等)。 - 步骤5:执行分割提取组件。 - 步骤6:后处理或可视化。 - 结尾:总结和生成相关问题。 使用代码示例:引用[4]提到部分代码,我可以提供一个简单的PCL代码片段。 在LaTeX中,代码块应该用```...```格式,如示例中所示。 数学表达式:如果涉及,如距离计算,使用$...$。 例如,欧氏距离:$d = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2 + (z_2 - z_1)^2}$。 在引用中,没有具体的数学公式,但如果有,我会加入。 引用标识:在回答中,当引用特定内容时添加[^n]。 例如,当提到区域生长算法时,引用[4]。 生成相关问题:类似于示例,3-5个问题。 例如: 1. 如何优化点云分割的参数? 2. PCL中其他分割方法有哪些? 3. 如何处理点云分割中的噪声? 现在,写回答。</think>### PCL点云分割提取组件指南 点云分割点云处理中的关键步骤,旨在将原始点云数据分割成多个独立的组件或簇,每个组件代表一个对象或区域。这对于后续的物体识别、分类和场景理解至关重要。PCL(Point Cloud Library)是一个强大的开源库,提供多种点云分割算法。根据您的需求,我将逐步指导您如何使用PCL提取点云中的各个组件。整个过程包括数据准备、选择分割方法、设置参数、执行分割提取结果。在回答中,我会引用相关资源以确保可靠性。 #### 步骤1: 理解点云分割基础 点云分割的目标是将点云分解为多个簇,每个簇对应一个物理对象(如桌子、椅子或墙壁)。PCL中的分割算法通常基于点云的空间特征(如欧氏距离、法线方向或曲率)来划分点云。例如: - **欧氏聚类分割**:基于点之间的欧氏距离,将邻近点归为一簇。距离阈值$d_{\text{th}}$是关键参数,表示两个点被视为同一簇的最大距离。 - **区域生长分割**:使用种子点扩散方式,基于法线夹角和曲率阈值来聚类点。法线夹角$\theta$和曲率$c$需预先设定[^4]。 - **其他方法**:PCL还支持模型拟合(如平面或圆柱分割)和条件聚类,用户可自定义约束(如RGB颜色或平滑度)[^1][^5]。 点云分割是物体识别的前置步骤,但本身并非最终目标;它旨在为后续处理提供干净的组件[^3]。在开始前,确保已安装PCL库(推荐使用Ubuntu系统或Windows的预编译版本)。 #### 步骤2: 准备点云数据 首先,加载点云数据。PCL支持多种格式(如`.pcd`或`.ply`)。以下是一个简单的代码示例,读取点云并进行预处理(如降噪和法线估计),因为分割算法通常需要法线信息。 ```cpp #include <pcl/point_types.h> #include <pcl/io/pcd_io.h> #include <pcl/filters/voxel_grid.h> #include <pcl/features/normal_3d.h> int main() { // 加载点云 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); pcl::io::loadPCDFile<pcl::PointXYZ>("input_cloud.pcd", *cloud); // 降噪(可选):使用体素网格滤波简化点云 pcl::PointCloud<pcl::PointXYZ>::Ptr filtered_cloud(new pcl::PointCloud<pcl::PointXYZ>); pcl::VoxelGrid<pcl::PointXYZ> voxel; voxel.setInputCloud(cloud); voxel.setLeafSize(0.01f, 0.01f, 0.01f); // 设置体素大小,单位:米 voxel.filter(*filtered_cloud); // 计算法线(许多分割算法需要法线) pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>); pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne; ne.setInputCloud(filtered_cloud); pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>()); ne.setSearchMethod(tree); ne.setRadiusSearch(0.03); // 搜索半径,单位:米 ne.compute(*normals); } ``` 此代码读取点云、降噪(减少计算量),并计算法线。法线估计是区域生长分割的关键步骤[^4]。 #### 步骤3: 选择并实现分割算法 PCL提供多种分割方法,我推荐两种常用方法提取组件:**欧氏聚类分割**(简单高效)和**区域生长分割**(适合复杂形状)。您可以根据点云特性选择。 - **方法A: 欧氏聚类分割(推荐用于简单场景)** 这种方法基于欧氏距离聚类点,速度快,适合提取空间隔离的物体(如室内家具)。距离阈值$d_{\text{th}}$是核心参数:如果点间距小于$d_{\text{th}}$,则归为同一簇。 ```cpp #include <pcl/segmentation/extract_clusters.h> // 使用步骤2的filtered_cloud pcl::search::KdTree<pcl::PointXYZ>::Ptr cluster_tree(new pcl::search::KdTree<pcl::PointXYZ>); cluster_tree->setInputCloud(filtered_cloud); // 创建欧氏聚类对象 std::vector<pcl::PointIndices> cluster_indices; pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec; ec.setClusterTolerance(0.02); // 距离阈值d_th,单位:米(例如2cm) ec.setMinClusterSize(100); // 最小簇点数(过滤噪声) ec.setMaxClusterSize(25000); // 最大簇点数(避免过大簇) ec.setSearchMethod(cluster_tree); ec.setInputCloud(filtered_cloud); ec.extract(cluster_indices); // 提取簇索引 // 提取并保存各组件 int cluster_id = 0; for (const auto& indices : cluster_indices) { pcl::PointCloud<pcl::PointXYZ>::Ptr cluster_cloud(new pcl::PointCloud<pcl::PointXYZ>); for (const auto& idx : indices.indices) { cluster_cloud->points.push_back(filtered_cloud->points[idx]); } cluster_cloud->width = cluster_cloud->points.size(); cluster_cloud->height = 1; std::string filename = "cluster_" + std::to_string(cluster_id++) + ".pcd"; pcl::io::savePCDFile(filename, *cluster_cloud); } ``` 此方法高效,但可能受噪声影响;调整`setClusterTolerance()`和簇大小参数可优化结果[^5]。 - **方法B: 区域生长分割(适合复杂形状)** 这种方法从种子点开始,基于法线相似性和曲率扩散聚类点。适合提取具有平滑表面的物体(如圆柱或曲面)。关键参数包括法线夹角阈值$\theta_{\text{th}}$和曲率阈值$c_{\text{th}}$[^4]。 ```cpp #include <pcl/segmentation/region_growing.h> // 使用步骤2的filtered_cloud和normals pcl::RegionGrowing<pcl::PointXYZ, pcl::Normal> reg; reg.setMinClusterSize(50); // 最小簇点数 reg.setMaxClusterSize(10000); // 最大簇点数 reg.setSearchMethod(tree); // 使用KdTree搜索 reg.setNumberOfNeighbours(30); // 搜索近邻点数 reg.setInputCloud(filtered_cloud); reg.setInputNormals(normals); reg.setSmoothnessThreshold(3.0 / 180.0 * M_PI); // 法线夹角阈值θ_th(弧度),例如3度 reg.setCurvatureThreshold(0.05); // 曲率阈值c_th // 执行分割 std::vector<pcl::PointIndices> clusters; reg.extract(clusters); // 提取组件(类似欧氏聚类代码) for (size_t i = 0; i < clusters.size(); ++i) { pcl::PointCloud<pcl::PointXYZ>::Ptr seg_cloud(new pcl::PointCloud<pcl::PointXYZ>); pcl::copyPointCloud(*filtered_cloud, clusters[i], *seg_cloud); pcl::io::savePCDFile("segment_" + std::to_string(i) + ".pcd", *seg_cloud); } ``` 区域生长法能处理复杂几何,但需手动设置种子点或依赖自动初始化;过分割(太多小簇)或欠分割(太少大簇)是常见问题[^4][^5]。 #### 步骤4: 参数调优和后处理 - **参数调优**:分割效果高度依赖参数。例如: - 欧氏聚类的$d_{\text{th}}$应略大于点云平均间距(通常0.01-0.1米)。 - 区域生长的$\theta_{\text{th}}$(角度)和$c_{\text{th}}$(曲率)需根据物体表面光滑度调整。建议先用PCL的`pcl_viewer`可视化点云实验。 - **后处理**:分割后,您可能需过滤噪声(如移除小簇)或合并过分割簇。PCL提供工具如`pcl::ConditionalEuclideanClustering`支持自定义约束[^5]。 - **可视化**:使用PCL的`pcl::visualization`模块查看分割结果。例如,不同簇可着色显示。 #### 注意事项和最佳实践 - **性能考虑**:点云分割计算量大,建议在预处理中降采样(如步骤2的体素滤波)。对于大型点云,GPU加速方法可能更优。 - **缺陷与优化**:欧氏聚类对噪声敏感;区域生长需精确法线估计。引用[5]指出,条件聚类可减少过分割,但需用户定义约束函数[^5]。深度学习分割(如PointNet++)是替代方案,但PCL经典方法更易上手[^2]。 - **真实可靠性**:PCL分割算法已在工业中广泛应用,例如提取室内场景中的物体或机械部件。分割结果应作为物体识别的输入,而非最终输出[^3]。 通过以上步骤,您可高效提取点云组件。分割后,每个组件可独立处理,用于识别或分类[^1]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tech沉思录

点赞加投币,感谢您的资瓷~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值