热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

PCL教程点云分割之圆柱体分割

原文链接:Cylindermodelsegmentation—PointCloudLibrary0.0documentation目录处理流程圆柱分割程序代码实验

原文链接:Cylinder model segmentation — Point Cloud Library 0.0 documentation


目录

处理流程

圆柱分割

程序代码

 实验结果

 打印结果

 原始点云

 过滤杂点

 平面分割​

 将平面去除​

 分割圆柱​

CMakeLists.txt


 本教程演示了如何运行圆柱形模型的Sample Consensus分割。为了使示例更实用一些,将以下操作应用于输入数据集(按顺序):

  • 距离1.5米以上的数据点被过滤
  • 每一点的表面法线被估计
  • 一个平面模型被分割,显示,并保存到本地
  • 一个圆柱形模型(在本数据集中是一个杯子)被分割,显示,并保存到本地

本次使用的数据集:table_scene_mug_stereo_textured.pcd

  •  一个杯子
  • 一个平面
  • 离得很远的背景杂点

由于数据中存在噪声,圆柱模型并不完美。

处理流程
  1. 滤波去噪:pass_through_filter();
  2. 法线估计 normals_estimate();
  3. 把平面分割出来 plane_seg();
  4. 利用分割结果(获取到的下标):使用平面点云的下标将平面抽取出来,并保存:get_plane();
  5. 移除平面及其法线,将结果保存在cloud_filtered2,cloud_normals2:remove_plane();
  6. 将圆柱分割出来,得到系数因子和下标 cylinder_seg();
  7. 将圆柱抽取并保存 get_cylinder();

圆柱分割

平面分割可以参考博文:PCL教程-点云分割之平面模型分割 

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);

  • Using a model of type: SACMODEL_NORMAL_PLANE
  • Setting normal distance weight to 0.100000
  • Using a method of type: SAC_RANSAC with a model threshold of 0.030000
  • Setting the maximum number of iterations to 100

下面重点介绍圆柱分割:

// Create the segmentation object for cylinder segmentation and set all the parametersseg.setOptimizeCoefficients (true);seg.setModelType (pcl::SACMODEL_CYLINDER);seg.setMethodType (pcl::SAC_RANSAC);seg.setNormalDistanceWeight (0.1);seg.setMaxIterations (10000);seg.setDistanceThreshold (0.05);seg.setRadiusLimits (0, 0.1);

  •  这里同样使用了RANSAC鲁棒估计来获取圆柱的系数因子。
  • 还设置了距离阈值为0.05m(5cm):小于这个阈值的点将标记为圆柱内部点。
  • 此外还设置了表面法线的距离权重为0.1
  • 限制了圆柱模型的半径为0~0.1m(10cm)
  • 最大的迭代次数为10000


程序代码

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#includetypedef pcl::PointXYZ PointT;
// All the objects needed
pcl::PCDReader reader;
pcl::PassThrough pass;
pcl::NormalEstimation ne;
pcl::SACSegmentationFromNormals seg;
pcl::PCDWriter writer;
pcl::ExtractIndices extract;
pcl::ExtractIndices extract_normals;
pcl::search::KdTree::Ptr tree(new pcl::search::KdTree());// Datasets
pcl::PointCloud::Ptr cloud(new pcl::PointCloud);
pcl::PointCloud::Ptr cloud_filtered(new pcl::PointCloud);
pcl::PointCloud::Ptr cloud_normals(new pcl::PointCloud);
pcl::PointCloud::Ptr cloud_filtered2(new pcl::PointCloud);
pcl::PointCloud::Ptr cloud_normals2(new pcl::PointCloud);
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);pcl::PointCloud::Ptr cloud_plane(new pcl::PointCloud());
pcl::PointCloud::Ptr cloud_cylinder(new pcl::PointCloud());
//PCLVisualizer
pcl::visualization::PCLVisualizer viewer("Cylinder Segmentation");int step_count;void pass_through_filter()
{// Build a passthrough filter to remove spurious NaNspass.setInputCloud(cloud);pass.setFilterFieldName("z");pass.setFilterLimits(0, 1.5);pass.filter(*cloud_filtered);std::cerr <<"PointCloud after filtering has: " <points.size() <<" data points." <{// Estimate point normalsne.setSearchMethod(tree);ne.setInputCloud(cloud_filtered);ne.setKSearch(50);ne.compute(*cloud_normals);
}void plane_seg()
{// Create the segmentation object for the planar model and set all the parametersseg.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);// Obtain the plane inliers and coefficientsseg.segment(*inliers_plane, *coefficients_plane);std::cerr <<"Plane coefficients: " <<*coefficients_plane <}void get_plane()
{// Extract the planar inliers from the input cloudextract.setInputCloud(cloud_filtered);extract.setIndices(inliers_plane);extract.setNegative(false);// Write the planar inliers to diskextract.filter(*cloud_plane);std::cerr <<"PointCloud representing the planar component: " <points.size() <<" data points." <}void remove_plane()
{// Remove the planar inliers, extract the restextract.setNegative(true);extract.filter(*cloud_filtered2);extract_normals.setNegative(true);extract_normals.setInputCloud(cloud_normals);extract_normals.setIndices(inliers_plane);extract_normals.filter(*cloud_normals2);
}void cylinder_seg()
{// Create the segmentation object for cylinder segmentation and set all the parametersseg.setOptimizeCoefficients(true);seg.setModelType(pcl::SACMODEL_CYLINDER);seg.setMethodType(pcl::SAC_RANSAC);seg.setNormalDistanceWeight(0.1);seg.setMaxIterations(10000);seg.setDistanceThreshold(0.05);seg.setRadiusLimits(0, 0.1);seg.setInputCloud(cloud_filtered2);seg.setInputNormals(cloud_normals2);// Obtain the cylinder inliers and coefficientsseg.segment(*inliers_cylinder, *coefficients_cylinder);std::cerr <<"Cylinder coefficients: " <<*coefficients_cylinder <}
void get_cylinder()
{extract.setInputCloud(cloud_filtered2);extract.setIndices(inliers_cylinder);extract.setNegative(false);extract.filter(*cloud_cylinder);if (cloud_cylinder->points.empty())std::cerr <<"Can&#39;t find the cylindrical component." <points.size() <<" data points." <}void keyboard_event_occurred(const pcl::visualization::KeyboardEvent& event,void * nothing)
{if (event.getKeySym() &#61;&#61; "space" && event.keyDown()){&#43;&#43;step_count;}
}
int
main(int argc, char** argv)
{step_count &#61; 1;std::string str &#61; "STEP";//初始化PCLVisualizer//viewer.addCoordinateSystem(1);//viewer.setBackgroundColor(0, 0, 255);viewer.addText(str&#43; std::to_string(step_count), 10, 10,16, 200,200,100,"text");viewer.registerKeyboardCallback(&keyboard_event_occurred, (void*)NULL);// Read in the cloud datareader.read("table_scene_mug_stereo_textured.pcd", *cloud);std::cerr <<"PointCloud has: " <points.size() <<" data points." < color_cloud(cloud, 255, 0, 0);viewer.addPointCloud(cloud,"cloud");viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1,"cloud");//滤波去噪pass_through_filter();// 法线估计normals_estimate();// 把平面分割出来plane_seg();// 利用平面点云的下标将平面抽取出来&#xff0c;并保存get_plane();// 移除平面及其法线&#xff0c;将结果保存在cloud_filtered2&#xff0c;cloud_normals2remove_plane();// 将圆柱分割出来&#xff0c;得到系数因子和下标cylinder_seg();// 将圆柱抽取并保存get_cylinder();while (!viewer.wasStopped()){viewer.spinOnce();if (step_count <8){viewer.updateText(str &#43; std::to_string(step_count), 10, 10, 16, 50, 100, 200, "text");switch (step_count){case 2://滤波去噪 viewer.updatePointCloud(cloud_filtered, "cloud"); break;case 3://法线估计plane_seg();break;case 4://利用平面点云的下标将平面抽取出来&#xff0c;并保存viewer.updatePointCloud(cloud_plane, "cloud"); break;case 5://移除平面及其法线&#xff0c;将结果保存在cloud_filtered2&#xff0c;cloud_normals2viewer.updatePointCloud(cloud_filtered2, "cloud"); break;case 6:// 将圆柱分割出来&#xff0c;得到系数因子和下标cylinder_seg(); break;case 7:将圆柱抽取并保存viewer.updatePointCloud(cloud_cylinder, "cloud"); break;default:break;}}else {break;}}return (0);
}

 实验结果

 打印结果


PointCloud has: 307200 data points.
PointCloud after filtering has: 139897 data points.
Plane coefficients: header:
seq: 0 stamp: 0 frame_id:
values[]
  values[0]:   0.0161902
  values[1]:   -0.837667
  values[2]:   -0.545941
  values[3]:   0.528862

PointCloud representing the planar component: 116300 data points.
Cylinder coefficients: header:
seq: 0 stamp: 0 frame_id:
values[]
  values[0]:   0.0543319
  values[1]:   0.100139
  values[2]:   0.787577
  values[3]:   -0.0135876
  values[4]:   0.834831
  values[5]:   0.550338
  values[6]:   0.0387446

PointCloud representing the cylindrical component: 11462 data points.


 原始点云

 过滤杂点

 

 平面分割


 将平面去除

 

 分割圆柱


CMakeLists.txt

cmake_minimum_required(VERSION 2.8 FATAL_ERROR)project(cylinder_segmentation)find_package(PCL 1.2 REQUIRED)include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})add_executable (cylinder_segmentation cylinder_segmentation.cpp)
target_link_libraries (cylinder_segmentation ${PCL_LIBRARIES})


推荐阅读
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • springmvc学习笔记(十):控制器业务方法中通过注解实现封装Javabean接收表单提交的数据
    本文介绍了在springmvc学习笔记系列的第十篇中,控制器的业务方法中如何通过注解实现封装Javabean来接收表单提交的数据。同时还讨论了当有多个注册表单且字段完全相同时,如何将其交给同一个控制器处理。 ... [详细]
author-avatar
2012我的语言
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有