首页 > 其他分享 >【PCL模块解析 07 之 点云分割】01 平面模型分割

【PCL模块解析 07 之 点云分割】01 平面模型分割

时间:2022-10-12 18:03:29浏览次数:92  
标签:分割 01 pcl points PCL 点云 cloud


目录

​一、前言​

​二、PCL简介​

​1、PCL简介​

​2、PCL分割​

​三、平面模型分割​

​1、全部代码​

​2、分块介绍​

​1.创建数据​

​2.下采样​

​3.滤波​

​4.创建分割对象​

​5.分割并获取平面聚类​

​6.将聚类写入到数据集​

​7.点云文件可视化​

​3、图像展示​


一、前言

最近在学习点云库,在做笔记记录时,希望能跟更多的人一起分享一些学习心得,但是由于是初学,无法像其他内容一样去写一整套学习笔记,只能跟着自己做的一些实战的东西,来逐步完善相关的技术,所以第一篇PCL点云库的内容从分割开始。

本文中所有代码可以去我的GitHub主页下载(​​点此进入代码下载地址​​)。

学习点云库,主要用到两个网站:

1.PCL官网教程:​​http://pointclouds.org/documentation/​

这是原网站的文档页,有相关源码的教程以及相关代码,适合英语大佬学习PCL、想通过技术来提升自己英语水平的同学(比如我)。

2.PCL中国:​​http://www.pclcn.org/​

接下来让我们一起走进PCL的世界。

二、PCL简介

1、PCL简介

简单介绍一下PCL:

PCL(Point Cloud Library)是在吸收了前人点云相关研究基础上建立起来的大型跨平台开源C++编程库,它实现了大量点云相关的通用算法和高效数据结构,涉及到点云获取、滤波、分割、配准、检索、特征提取、识别、追踪、曲面重建、可视化等。支持多种操作系统平台,可在Windows、Linux、Android、Mac OS X、部分嵌入式实时系统上运行。

如果说OpenCV是2D信息获取与处理的结晶,那么PCL就在3D信息获取与处理上具有同等地位。如果大家对OpenCV有兴趣,可以看我的相关博客:《​​OpenCV系列​​》。

2、PCL分割

没有找到相关的定义,通过对几种分割方式的学习,我们也能对分割有大概的理解:通过一定的技术,将点云图像属于不同层次,不同部分,不同类型的东西区分开来,并把自己需要的部分的信息存储或展示出来

PCL分割主要有以下几种:

(1)平面模型分割;

(2)圆柱模型分割;

(3)欧几里得簇提取;

(4)区域增长细分;

(5)基于颜色的区域增长分割;

(6)基于最小分割的分割;

(7)条件欧几里得聚类;

(8)基于法线的分割差异;

(9)点云聚类形成超级体素集群 - 理论引物(Clustering of Pointclouds into Supervoxels - Theoretical primer);

(10)利用渐进形态滤波器返回分割识别地面;

(11)使用模型离群值方法移除过滤点云;

这篇博客主要给大家分享的是平面模型分割。

三、平面模型分割

1、全部代码

这里代码是原网站的代码,原网站采用的点云文件是自己写的,我做项目时的点云文件是自己生成的。下面是原网站的代码。

#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>

int main (int argc, char** argv)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);

// Fill in the cloud data
cloud->width = 15;
cloud->height = 1;
cloud->points.resize (cloud->width * cloud->height);

// Generate the data
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;
}

// Set a few outliers
cloud->points[0].z = 2.0;
cloud->points[3].z = -2.0;
cloud->points[6].z = 4.0;

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);
pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
// Create the segmentation object
pcl::SACSegmentation<pcl::PointXYZ> seg;
// Optional
seg.setOptimizeCoefficients (true);
// Mandatory
seg.setModelType (pcl::SACMODEL_PLANE);
seg.setMethodType (pcl::SAC_RANSAC);
seg.setDistanceThreshold (0.01);

seg.setInputCloud (cloud);
seg.segment (*inliers, *coefficients);

if (inliers->indices.size () == 0)
{
PCL_ERROR ("Could not estimate a planar model for the given dataset.");
return (-1);
}

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;

return (0);
}

重点是给大家讲解,在这里就不写我自己的代码了,有需要的同学可以上我的Github上去下载。

2、分块介绍

分块介绍不仅仅会介绍网站上公布的代码,还会讲解一些我自己的代码,下面部分与上面的全部代码可能没有关系。只是我在学习的过程中会用到。

1.创建数据

原网站中采用的是自己创建的随机数据。控制好z变量的值,并随机生成一些数据点。并设置三个据外点测试效果。

//填充点云数据,设置点云宽度为15,高度为1,即为无序点云
cloud.width = 15;
cloud.height = 1;
cloud.points.resize (cloud.width * cloud.height);
//生成数据,采用随机数填充点云的x,y坐标,但都处在z为1的平面上
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值,使其偏离z为1的平面
cloud.points[0].z = 2.0;
cloud.points[3].z = -2.0;
cloud.points[6].z = 4.0;
//在标准输出上打印出点云中各点的坐标值,方便分割后的参考
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;

2.下采样

下采样是会经常遇到的方式,目的是为了减少数据点,提高运算速度。

// 下采样,体素叶子大小为0.01
pcl::VoxelGrid<pcl::PointXYZ> vg; //体素栅格下采样对象
vg.setInputCloud(cloud); // 原始点云
vg.setLeafSize(0.005f, 0.005f, 0.005f); // 设置采样体素大小
vg.filter(*cloud_vg_filtered); //保存
std::cout << "PointCloud after filtering has: " << cloud_vg_filtered->points.size() << " data points." << std::endl;

3.滤波

滤波的目的是为了降噪,将不符合要求的数据点排除出去。

不过,这个NaNs是个啥?我个人理解是错误数据代称,你们的理解呢?

// Build a passthrough filter to remove spurious NaNs 建立一个直通过滤器以消除错误的NaNs
pcl::PassThrough<pcl::PointXYZ> pass;
pass.setInputCloud(cloud_vg_filtered);
pass.setFilterFieldName("z");
pass.setFilterLimits(0, 1.5);
pass.filter(*cloud_NaNs_filtered);//预处理后的全景

4.创建分割对象

这一步的工作是为平面模型创建分割对象并设置所有参数。这里涉及到的参数远比原网站中的多。

pcl::SACSegmentationFromNormals<pcl::PointXYZ, pcl::Normal> seg;  //创建分割对象(使用曲面法线进行平面分割的类)
// Create the segmentation object for the planar model and set all the parameters
seg.setOptimizeCoefficients(true);
seg.setModelType(pcl::SACMODEL_NORMAL_PLANE); //设置模型类型
seg.setNormalDistanceWeight(0.1);
seg.setMethodType(pcl::SAC_RANSAC); //设置随机采样一致性方法类型
seg.setMaxIterations(100); //设置最大迭代次数
seg.setDistanceThreshold(0.05); //设定距离阀值,距离阀值决定了点被认为是局内点是必须满足的条件,表示点到估计模型的距离最大值
seg.setInputCloud(cloud_NaNs_filtered);
seg.setInputNormals(cloud_normals);

5.分割并获取平面聚类

将图像平面分割出来并获取图像平面聚类。

//进行欧式分割,获得目标点云集合--------------------------------------------------------------------------------
// Creating the KdTree object for the search method of the extraction
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree2(new pcl::search::KdTree<pcl::PointXYZ>);

tree2->setInputCloud(cloud_objects); //创建点云索引向量,用于存储实际的点云信息

std::vector<pcl::PointIndices> cluster_indices;
pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;
ec.setClusterTolerance(0.8); //设置近邻搜索的搜索半径为2cm
ec.setMinClusterSize(10);//设置一个聚类需要的最少点数目为100
ec.setMaxClusterSize(2500);//设置一个聚类需要的最大点数目为20000
ec.setSearchMethod(tree2);//设置点云的搜索机制
ec.setInputCloud(cloud_objects);
ec.extract(cluster_indices);//从点云中提取聚类,并将点云索引保存在cluster_indices中

6.将聚类写入到数据集

/*为了从点云索引向量中分割出每个聚类,必须迭代访问点云索引,每次创建一个新的点云数据集,并且将所有当前聚类的点写入到点云数据集中。*/
//迭代访问点云索引cluster_indices,直到分割出所有聚类
int j = 0;
for (std::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 (std::vector<int>::const_iterator pit = it->indices.begin(); pit != it->indices.end(); ++pit)
{
cloud_cluster->points.push_back(cloud_objects->points[*pit]); //*
}
cloud_cluster->width = cloud_cluster->points.size();
cloud_cluster->height = 1;
cloud_cluster->is_dense = true;
std::cout << "PointCloud representing the Cluster: " << cloud_cluster->points.size() << " data points." << std::endl;
std::stringstream ss;
ss << "D:/cloud_cluster_jiaoshui" << j << ".pcd";
writer.write<pcl::PointXYZ>(ss.str(), *cloud_cluster, false); //*
j++;
}

7.点云文件可视化

将点云文件以图像展示出来。

//显示提取的结果
//读取文件
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer;
viewer = simpleVis(cloud_objects);
while (!viewer->wasStopped())
{
viewer->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}

3、图像展示

【PCL模块解析 07 之 点云分割】01 平面模型分割_PCL

 

标签:分割,01,pcl,points,PCL,点云,cloud
From: https://blog.51cto.com/u_12001271/5751250

相关文章

  • 【C++从入门到熟练练习题】000 VS2015新建程序及输出Hello World
    一、前言大学很多计算机相关专业的基础课都会有C++,C++是比较经典的编程语言,编程语言一定不是看出来的,而是敲代码敲出来的。所以会给大家不定期分享一些C++练习题。在下一次......
  • java基础语法[01]
    一、注释注释不会被执行,是给写代码的人看的。是非常重要非常好的习惯。单行注释://多行注释:/**/文档注释:javadoc,/***/有兴趣可以搜:有趣的代码注释。......
  • 【图像分割应用】医学图像分割(一)——脑区域分割
     从本周开始,新专栏《图像分割应用》就跟大家见面了。本专栏主要介绍图像分割在各个领域的应用、难点、技术要求等常见问题。本文是专栏《图像分割应用》的第1篇文章,首先来......
  • 【图像分割应用】医学图像分割小总结
    这是专栏《图像分割应用》的第4篇文章,本专栏主要介绍图像分割在各个领域的应用、难点、技术要求等常见问题。本专栏的第一个板块医学图像分割中从具体应用出发,介绍了脑分割......
  • 面向对象(上)01
    面向对象(上)01Java面向对象学习的三条主线:(第4-6章)1.Java类和类的成员:属性,方法,构造器;代码块,内部类.1.面向对象的三大特征:封装性,继承性,多态......
  • LcdToos如何实现PX01自动调Flicker及VCOM烧录
    准备工作:LcdTools+PX01点亮需调Flicker的屏;F118Flicker探头,用于自动Flicker校准测量,F118连接PX01上电后,探头屏会提示零点校准,此时需盖住探头窗口再按探头“MODE”按......
  • [2core]基于httpclient实现负载均衡
    一、准备由于没有采用asp.netcore框架提供的HttpClientFactory和依赖注入方式使用HttpClient,所以从asp.net迁移到asp.netcore变化并不大,而且HttpClient的使用也基本没有......
  • 01数据库作用
    问题引出概念解决之道数据库简单原理图cmd连接数据库确保MYSQL服务在运行中(netstartmysql)mysql三层结构图数据在数据库中存储的方式(表)SQL语句分类......
  • letcode刷题记录-day01-两数之和
    题目:两数之和描述给定一个整数数组nums 和一个整数目标值target,请你在该数组中找出和为目标值target 的那 两个 整数,并返回它们的数组下标。你可以假设每种输......
  • WebAssembly01--web 编译FFmpeg(WebAssembly版)库
    下载ffmpeg版本注意这里是承接dockerUbuntuwasm环境配置浏览器debugc/c++gitclonehttps://git.ffmpeg.org/ffmpeg.gitffmpegcdffmpeggitbranch-rgitc......