PCL学习笔记(二十六)-- 基于区域生长的分割

一、简介

    区域生长分割算法的输出是一个聚类集合,每个聚类集合被认为是同一光滑表面的一部分。该算法思想:首先依据点的曲率值对点进行排序,之所以排序,是因为区域生长算法是从曲率最小的点开始生长的,这个点就是初始种子点,初始种子点所在的区域即为最平滑的区域,一般场景中平面区域较大,这样从最平滑的区域开始生长可减少分割区域的总数,提高效率。

    算法的流程:设置一空的种子点序列和空的聚类数组,选好初始种子点后,将其加入到种子点序列中,并搜索邻域点。对每一个邻域点,比较邻域点的法线与当前种子点的法线之间的夹角,小于平滑阈值的邻域点加入到当前区域。然后检查每一个邻域点的曲率值,小于曲率阈值的邻域点加入到种子点序列中。在进行种子点邻域判断后,删除当前种子点,利用新加入的种子点继续生长,重负进行以上生长过程,知道种子点序列被清空。一个区域生长完成,将其加入聚类数组。最后,利用曲率值从小到大排序,顺序选择输入点集的点作为种子点加入到种子点序列中,重复以上生长步骤,这样就通过区域生长实现了点云的分割。

二、代码分析

    1)利用pcl::NormalEstimation类,计算法线:

	pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normal_estimator;//创建法线估计对象
	normal_estimator.setSearchMethod (tree);//设置搜索方法
	normal_estimator.setInputCloud (cloud);//设置法线估计对象输入点集
	normal_estimator.setKSearch (KN_normal);// 设置用于法向量估计的k近邻数目
	normal_estimator.compute (*normals);//计算并输出法向量

    2)对类pcl::RegionGrowing进行实例化,该模板类具有两个参数:PointT是所用点云的类型,NormalT是所用法线的类型:

	pcl::RegionGrowing<pcl::PointXYZ, pcl::Normal> reg;//创建区域生长分割对象

    3)设置最大和最小聚类大小,这样分割后点的数量小于最小聚类值(或大于最大聚类值)的都将被舍弃:

	reg.setMinClusterSize (50);//设置一个聚类需要的最小点数
	reg.setMaxClusterSize (1000000);//设置一个聚类需要的最大点数

    4)该方法需要用K近邻搜索遍历近邻,一下几行提供搜索方法和设置近邻大小阈值,并输入点云,点云索引和点云对应的法向量:

	reg.setSearchMethod (tree);//设置搜索方法
	reg.setNumberOfNeighbours (30);//设置搜索的临近点数目
	reg.setInputCloud (cloud);//设置输入点云
	if(Bool_Cuting)reg.setIndices (indices);//通过输入参数设置,确定是否输入点云索引
	reg.setInputNormals (normals);//设置输入点云的法向量

    5)setSmoothnessThreshold成员函数用于设置平滑阈值,也就是两点法线偏差的允许范围。如果两点法线间的偏差小于算法设置的平滑阈值,那么这两点被认为是同一个聚类;setCurvatureThreshold成员函数用于设置曲率阈值,即两点曲率偏差的允许范围,如果两点的法向量偏差较小,那么将继续检测它们曲率之间的差异,如果这个值小于曲率阈值,那么算法会采用新增加的点迭代进行区域生长:

	reg.setSmoothnessThreshold (SmoothnessThreshold / 180.0 * M_PI);//设置平滑阈值
	reg.setCurvatureThreshold (CurvatureThreshold);//设置曲率阈值

    6)启动分割算法,返回聚类向量:

	std::vector <pcl::PointIndices> clusters;
	reg.extract (clusters);//获取聚类的结果,分割结果保存在点云索引的向量中。
	end = time(0); 
	diff[2] = difftime (end, start)-diff[0]-diff[1]; 
	PCL_INFO ("\Region growing takes(seconds): %d\n", diff[2]); 

	std::cout << "Number of clusters is equal to " << clusters.size () << std::endl;//输出聚类的数量
	std::cout << "First cluster has " << clusters[0].indices.size () << " points." << endl;//输出第一个聚类的数量

    7)整体代码如下:

#include <iostream>
#include <vector>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/search/search.h>
#include <pcl/search/kdtree.h>
#include <pcl/features/normal_3d.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/filters/passthrough.h>
#include <pcl/segmentation/region_growing.h>
#include <pcl/console/print.h>
#include <pcl/console/parse.h>
#include <pcl/console/time.h>
#include <Windows.h>
#include <stdio.h>
#include <psapi.h>

void PrintMemoryInfo()																					//输出内存中的报错信息
{
	HANDLE hProcess;
	PROCESS_MEMORY_COUNTERS pmc;

	hProcess = GetCurrentProcess();
	printf("\nProcess ID: %u\n", hProcess);

	if (NULL == hProcess)
		return;
	
	if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
	{
		printf("\t PageFaultCount: 0x%08X\n", pmc.PageFaultCount);
		printf("\t PeakWorkingSetSize: 0x%08X\n", pmc.PeakWorkingSetSize);
		printf("\t WorkingSetSize: 0x%08X\n", pmc.WorkingSetSize);
		printf("\t QuotaPeakPagedPoolUsage: 0x%08X\n", pmc.QuotaPeakPagedPoolUsage);
		printf("\t QuotaPagedPoolUsage: 0x%08X\n", pmc.QuotaPagedPoolUsage);
		printf("\t QuotaPeakNonPagedPoolUsage: 0x%08X\n", pmc.QuotaPeakNonPagedPoolUsage);
		printf("\t QuotaNonPagedPoolUsge: 0x%08X\n", pmc.QuotaNonPagedPoolUsage);
		printf("\t PagefileUsge: 0x%08X\n", pmc.PagefileUsage);
		printf("\t peakPagefileUsage: 0x%08X\n", pmc.PeakPagefileUsage);
	}

	CloseHandle(hProcess);
}

using namespace pcl::console;
int main(int argc, char** argv)
{
	if (argc < 2)
	{
		std::cerr << ".exe xx.pcd -kn 50 -bc 0 -fc 10.0 -nc 0 -st 30 -ct 0.05" << std::endl;			//输出参考信息
		return (0);
	}
	time_t start, end, diff[5], option;																	//用于保存运行的相应时间等
	start = time(0);																					//记录当前的时间
	int KN_normal = 50;
	bool Bool_cutting = false;
	float far_cutting = 10, near_cutting = 0, SmoothnessThreshold = 30.0, CurvatureThreshold = 0.05;
	parse_argument(argc, argv, "-kn", KN_normal);														//设置k近邻点的个数
	parse_argument(argc, argv, "-bc", Bool_cutting);													//设置是否进行直通滤波的标志位
	parse_argument(argc, argv, "-fc", far_cutting);														//设置直通滤波的上限
	parse_argument(argc, argv, "-nc", near_cutting);													//设置直通滤波的下限
	parse_argument(argc, argv, "-st", SmoothnessThreshold);												//设置平滑阈值
	parse_argument(argc, argv, "-ct", CurvatureThreshold);												//设置曲率阈值
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);						//设置输入点云的对象,用于加载点云数据
	if (pcl::io::loadPCDFile<pcl::PointXYZ>(argv[1], *cloud) == -1)
	{
		std::cout << "Cloud reading failed." << std::endl;
		return (-1);
	}
	end = time(0);																						//记录加载完点云时的时间
	diff[0] = difftime(end, start);																		//记载加载所用的时间
	PCL_INFO("\Loading pcd file takes(seconds):%d\n", diff[0]);

	pcl::search::Search<pcl::PointXYZ>::Ptr tree = pcl::shared_ptr< pcl::search::Search<pcl::PointXYZ> > (new pcl::search::KdTree<pcl::PointXYZ>);
	pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);						//智能指针声明kd tree与法线
	pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normal_estimator;									//实例化一个法线估计对象
	normal_estimator.setSearchMethod(tree);																//设置搜索方式
	normal_estimator.setInputCloud(cloud);																//设置输入点云
	normal_estimator.setKSearch(KN_normal);																//设置k近邻点的个数
	normal_estimator.compute(*normals);																	//执行法线估计
	end = time(0);
	diff[1] = difftime(end, start) - diff[0];															//记录法线估计所用的时间
	PCL_INFO("\Estimationg normal takes : %d seconds\n", diff[1]);
	
	pcl::IndicesPtr indices(new std::vector<int>);														//声明一个索引指针
	if (Bool_cutting)																					//判断是否需要进行直通滤波
	{
		pcl::PassThrough<pcl::PointXYZ> pass;															//设置直通滤波器对象
		pass.setInputCloud(cloud);																		//设置输入点云
		pass.setFilterFieldName("z");																	//指定进行过滤的字段
		pass.setFilterLimits(near_cutting, far_cutting);												//设置进行过滤的范围
		pass.filter(*indices);																			//执行滤波,并保存滤波结果
	}

	pcl::RegionGrowing<pcl::PointXYZ, pcl::Normal>reg;													//创建区域生长的分割对象
	reg.setMinClusterSize(50);																			//设置聚类所需要的最小点数
	reg.setMaxClusterSize(1000000);																		//设置一个聚类的最大点数
	reg.setSearchMethod(tree);																			//设置搜索方式
	reg.setNumberOfNeighbours(30);																		//设置近邻点的个数
	reg.setInputCloud(cloud);																			//设置输入点云
	if (Bool_cutting)reg.setIndices(indices);															//通过输入参数设置,确定是否输入点云的索引
	reg.setInputNormals(normals);																		//输入点云的法向量
	reg.setSmoothnessThreshold(SmoothnessThreshold / 180.0 * M_PI);										//设置平滑阈值
	reg.setCurvatureThreshold(CurvatureThreshold);														//设置曲率阈值

	std::vector<pcl::PointIndices> clusters;															//用动态数组保存聚类的结果
	reg.extract(clusters);																				
	end = time(0);																						//计算聚类所需的时间
	diff[2] = difftime(end, start) - diff[0] - diff[1];
	PCL_INFO("\Region growing takes %d seconds\n", diff[2]);

	std::cout << "Number of clusters is equal to " << clusters.size() << std::endl;
	std::cout << "First cluster has " << clusters[0].indices.size() << " points." << std::endl;
	std::cout << "These are the indices of the points of the initial" << std::endl << "cloud that belong to the first cluster:" << std::endl;
	PrintMemoryInfo();
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr colored_cloud = reg.getColoredCloud();
	pcl::visualization::CloudViewer viewer("区域生长分割方法");
	viewer.showCloud(colored_cloud);
	while (!viewer.wasStopped())
	{

	}
	return (0);
}

三、编译结果

    分割后的图像存在部分红色点,说明这部分点的数量小于或大于用户设定的阈值。基于区域生长的分割算法,对输入参数有一啦,通过修改相关输入参数,可以获得更好的分割效果:

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
参数化的连通区域生长PCL)是一种常用的点云分割方法,通过从种子点开始连续生长来识别和分割点云中的区域PCL首先选择一个种子点作为起始点,并将其标记为当前生长区域的一部分。然后,它会检查该种子点的邻域内的相邻点,并根据一些预定义的规则来判断它们是否属于同一个区域。这些规则可能包括点之间的距离、法向量的相似性以及表面法线之间的差异等。 如果一个相邻点被判定为属于当前生长区域,那么它将被添加到该区域中,并被标记为已被访问。然后,PCL会继续检查这个新加入区域的所有点的邻域,通过遍历这个过程,不断扩展区域的范围。 当没有更多的相邻点可以被添加到区域中时,生长过程停止。该区域中的点将被认为是一个单独的分割,并且可以用不同的颜色或标签进行标记。 PCL的基于区域生长的点云分割方法的优点是可以有效地处理不规则形状和复杂的点云。通过设置适当的生长参数,可以实现对所需分割的精确控制。 然而,PCL的基于区域生长的点云分割方法也存在一些限制。在处理非常密集的点云时,生长过程可能会变得非常耗时。此外,当存在重叠的物体或存在不规则形状的区域时,该方法可能无法正确地分割点云。 总而言之,PCL的基于区域生长的点云分割方法是一种流行且有效的分割技术,可以用于处理各种点云数据,并为进一步的分析和处理提供有价值的信息。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值