目录
一、前言
1、全部代码
2、分块介绍
1.创建数据
2.下采样
3.滤波
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、图像展示
标签:分割,01,pcl,points,PCL,点云,cloud From: https://blog.51cto.com/u_12001271/5751250