点云技术相关产学研社区

 找回密码
 立即注册加入PCL中国点云技术相关产学研社区

扫一扫,访问微社区

查看: 12033|回复: 13

在PCL中如何实现圆柱体模型分割

[复制链接]
发表于 2013-3-1 09:45:21 | 显示全部楼层 |阅读模式
本小节举例说明了如何采用随机采样一致性估计从带有噪声的点云中提取一个圆柱体模型,整个程序处理流程如下:
1、过滤掉远于1.5米的数据点;
2、估计每个点的表面法线;      
3、分割出平面模型(在我们的演示数据集中表示桌面)并保存到磁盘中;
4、分割圆出柱体模型(在我们的演示数据集中表示圆杯)并保存到磁盘中。
注意:由于数据中噪音的出现,圆柱体模型并不十分严格。
代码
首先,在PCL(Point Cloud Learning)中国协助发行的书[1]提供光盘的第14章例2文件夹中,打开名为cylinder_segmentation.cpp的代码文件,在同一文件夹下有点云文件table_scene_mug_stereo_textured_plane.pcd。
解释说明
下面来详细解释打开源代码的关键语句。
  pcl::PCDReader reader;                                 //pcd文件读取对象
  pcl::PassThrough<PointT> pass;                         //直通滤波对象
  pcl::NormalEstimation<PointT, pcl::Normal> ne;        //法线估计对象
  pcl::SACSegmentationFromNormals<PointT, pcl::Normal> seg; //分割对象
  pcl::PCDWriter writer;                                  //pcd文件写入对象
  pcl::ExtractIndices<PointT> extract;                   //点提取对象
  pcl::ExtractIndices<pcl::Normal> extract_normals;     //点提取对象
  pcl::search::KdTree<PointT>::Ptr tree (new pcl::search::KdTree<PointT> ());

上面代码定义在程序中用到的对象。
  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);

上面代码定义在程序中用到的数据对象,点类型、法线类型的点云对象,以及模型系数等,以及存储平面上内点的点索引集合对象。
  reader.read ("table_scene_mug_stereo_textured.pcd", *cloud);
  std::cerr <<"PointCloud has: "<< cloud->points.size () <<" data points."<< std::endl;

上面代码完成对点云数据从文件的读取。
  pass.setInputCloud (cloud);
  pass.setFilterFieldName ("z");
  pass.setFilterLimits (0, 1.5);
  pass.filter (*cloud_filtered);
  std::cerr <<"PointCloud after filtering has: "<< cloud_filtered->points.size () <<" data points."<< std::endl;

上面代码进行直通滤波,将z轴不在(0,1.5)范围内的点过滤掉,将剩余点存储在cloud_filtered对象中后续使用。
  ne.setSearchMethod (tree);
  ne.setInputCloud (cloud_filtered);
  ne.setKSearch (50);
  ne.compute (*cloud_normals);

上面代码对过滤后的点云进行法线估计,为后续进行基于法线的分割准备数据。
//设置分割所用的模型类型、方法、相关参数
seg.setOptimizeCoefficients (true);
  seg.setModelType (pcl::SACMODEL_NORMAL_PLANE);
  seg.setNormalDistanceWeight (0.1);
  seg.setMethodType (pcl::SAC_RANSAC);
  seg.setMaxIterations (100);
  seg.setDistanceThreshold (0.03);
  seg.setInputCloud (cloud_filtered);
  seg.setInputNormals (cloud_normals);
// 根据上面的输入参数执行分割获取平面模型系数和处在平面上的内点
  seg.segment (*inliers_plane, *coefficients_plane);
  std::cerr <<"Plane coefficients: "<<*coefficients_plane << std::endl;
// 从点云中抽取分割的处在平面上的点集
  extract.setInputCloud (cloud_filtered);
  extract.setIndices (inliers_plane);
  extract.setNegative (false);
  pcl::PointCloud<PointT>::Ptr cloud_plane (new pcl::PointCloud<PointT> ());
  extract.filter (*cloud_plane);
//存储分割得到的平面上的点到点云文件
  std::cerr <<"PointCloud representing the planar component: "<< cloud_plane->points.size () <<" data points."<< std::endl;
  writer.write ("table_scene_mug_stereo_textured_plane.pcd", *cloud_plane, false);

上面代码将过滤后的点云进行分割,并从点云中提取出处在平面上的点,存储到点云文件。
//为圆柱体分割创建分割对象,并设置所有参数seg.setOptimizeCoefficients (true);         //设置对估计的模型系数需要进行优化seg.setModelType (pcl::SACMODEL_CYLINDER); //设置分割模型为圆柱型seg.setMethodType (pcl::SAC_RANSAC);       //设置采用RANSAC作为算法的参数估计方法seg.setNormalDistanceWeight (0.1);         //设置表面法线权重系数seg.setMaxIterations (10000);              //设置迭代的最大次数10000seg.setDistanceThreshold (0.05);           //设置内点到模型的距离允许最大值seg.setRadiusLimits (0, 0.1);              //设置估计出的圆柱模型的半径范围
如上,我们使用RANSAC随机采样一致性鲁棒估计来获取圆柱体模型系数,并且限制了每个局内点到模型的距离阀值DistanceThreshold,即内点与模型的距离不能大于5厘米,设置表面法线的影响权重NormalDistanceWeight为0.1,并且限制了圆柱体模型的半径RadiusLimits要小于10厘米,估计出圆柱模型系数以及对应的内点后,存储内点到点云文件。
回复

使用道具 举报

发表于 2013-6-14 15:56:32 | 显示全部楼层
那是相当的详细啊!:lol
回复 支持 反对

使用道具 举报

发表于 2013-6-18 17:31:22 | 显示全部楼层
seg.setNormalDistanceWeight (0.1); // 設置表面法線權重係數
請問是什麼意思?
回复 支持 反对

使用道具 举报

发表于 2013-6-19 09:47:57 | 显示全部楼层
C.M-Chiu 发表于 2013-6-18 17:31
seg.setNormalDistanceWeight (0.1); // 設置表面法線權重係數
請問是什麼意思?

这个问题蛮有水平的,我看了下源码才找到了,随机采样一致性算法中有一个判断内点过程,其中需要计算每个点到可能模型之间的距离,这个距离在带有normal的计算中由两部分组成,一个是法线上的差异,另一部分是欧氏距离上的差异,double distance = fabs (normal_distance_weight_ * d_normal + (1 - normal_distance_weight_) * d_euclid); 就这句了,但在不同估计模型中这个公式中的normal_distance_weight_是有差异的。
到这里,大概我的理解就是表面法线权重系数:是在这个公式中,两个组成之间的相对比重了。和为1.0。

哥们,你的繁体字看起来蛮别扭的。你不会看简体感觉别扭吧。哈哈。
回复 支持 反对

使用道具 举报

发表于 2013-6-20 12:51:13 | 显示全部楼层
本帖最后由 C.M-Chiu 于 2013-6-20 12:53 编辑

谢谢楼上帮忙,哈哈,我的系统是繁体的
为了方便大家交流我还是丢谷歌转一下简体好啦!
(虽然语句的用法可能还是别妞〜)
我这边想问的都是PCL如何实践RANSAC方法的问题〜
小弟我稍稍看过RANSAC文献
其实我还是不太确定為何PCL的过程中他要这样做
而不是直接设一个距离阀值,跟一个角度阀值共兩個條件去約制
请问PCL有提供演算法过程的参考文献吗?
(我只找到这个.http://www.pointclouds.org/assets/rss2011/04_segmentation.pdf)

另外,想请问​​PCL实践的RANSAC算法中,停止迭代运算的条件的因素是什么?
何时会找不到圓柱? ("Can't find the cylindrical component")
PS:我也在研究源码中,感谢交流
回复 支持 反对

使用道具 举报

发表于 2013-6-27 21:11:44 | 显示全部楼层
C.M-Chiu 发表于 2013-6-20 12:51
谢谢楼上帮忙,哈哈,我的系统是繁体的
为了方便大家交流我还是丢谷歌转一下简体好啦!
(虽然语句的用法可 ...

chiu兄,抱歉才回复,我平时都是看到qq群里发链接才会看的。
这些代码好像没有参考文献,她内部怎么写的就怎么算的。停止迭代的条件好像有迭代次数吧,我找找源码把链接过会发给你吧
回复 支持 反对

使用道具 举报

发表于 2013-6-27 21:20:13 | 显示全部楼层
http://docs.pointclouds.org/trunk/ransac_8hpp_source.html
应该具体到那个模型的时候,也有相应的一起其他条件吧。如果有什么发链接到群里,我一般都会看学习下的,谢谢支持!
回复 支持 反对

使用道具 举报

发表于 2014-11-9 17:20:14 | 显示全部楼层
如何实现多圆柱体的分割,比如椅子有四条腿,但这个只分割出一个圆柱,
回复 支持 反对

使用道具 举报

发表于 2015-1-30 22:17:04 | 显示全部楼层
求教一下:用PointXYZ都很正常,可一用PointT就不识别是什么情况啊?
回复 支持 反对

使用道具 举报

发表于 2015-5-5 14:30:02 | 显示全部楼层
求出圆柱模型系数,有什么用啊?
回复 支持 反对

使用道具 举报

本版积分规则

QQ|小黑屋|点云技术相关产学研社区 ( 陕ICP备13001629号 )

GMT+8, 2024-4-29 11:08 , Processed in 2.044352 second(s), 16 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表