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

目录

一、前言

二、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官网,很多技术都在这里能找到(不敢说全部哈)适合英语比较差的小伙伴入门学习。

最近刚刚入门,很多东西也不是很了解,如果大家有什么问题,都可以跟我沟通交流。除了通过博客交流外,欢迎你加入我的QQ群,一起交流有关于机器学习、深度学习、计算机视觉有关内容。目前我并未确定具体的研究方向,所以现在处于广泛涉猎阶段,希望我们能够一起沟通。下图是我的群二维码:

接下来让我们一起走进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、图像展示

 

  • 4
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
PCL(Point Cloud Library)是一个用于处理点云数据的开源库,其中包含了许多用于点云分析和处理的算法。PCL可以基于法向量对点云进行分割。 点云是由大量的点组成的三维数据集。在进行点云分割时,我们希望将点云分成一些具有相似特征的子集,以便进一步进行各种分析和处理。 法向量是指点云中每个点周围表面的法线方向。通过计算每个点的法向量,我们可以获取点云中的结构信息,如平面、曲线等。在基于法向量分割点云时,我们通过分析点云中每个点的法向量来判断其是否属于同一个表面。 基于法向量的点云分割算法通常包括以下步骤: 1. 首先,通过某个算法(如最近邻算法)计算每个点的法向量。这些法向量可以表示点云中每个点周围表面的方向。 2. 接下来,我们选择一个点作为种子点,并按照一定的条件将其加入到一个分割的子集中。 3. 然后,我们检查周围的点,判断它们的法向量与种子点的法向量是否一致。如果一致,我们将这些点也加入到分割的子集中。 4. 重复步骤3,直到没有点满足条件为止。 5. 最后,我们切换到下一个未分割的点,然后重复步骤2-4,直到所有点都被分割完毕。 基于法向量的点云分割可以帮助我们识别出点云中的不同表面,例如建筑物的墙面、地面、屋顶等。这对于进一步的点云处理和分析非常有帮助,如物体识别、建模、匹配等应用。PCL提供了丰富的函数和算法,可以方便地实现基于法向量的点云分割

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值