PCL区域生长的代码实现

---------原理介绍:

(1)首先计算出来各点的曲率值,将曲率值按照从小到大的顺序进行排序。

(2)设置一空的种子点序列和一个空的聚类数组。

(3)选取曲率最小的点放入上述种子点序列中。

(4)从序列中拿出来一个种子点搜索其邻域。

(5)搜索到邻域后,这些点先过法线夹角阈值,通过的保留到聚类数据,然后再从这些通过法线夹角阈值的点中,检查是否通过曲率阈值,通过的加入种子点序列。即比较邻域点的法线与当前种子点法线之间的夹角,如果小于阈值将该邻域点加入聚类数组。再从这些小于法线阈值的点中判断是否小于曲率阈值,如果小于则将该邻域点加入种子序列。

(6)结束后删除用过的种子点,用下一个种子点重复第(4)(5)步,直至种子点序列清空为止。

(7)从曲率排序的点中,即从小到大的排序,取聚类数组中没有的点作为种子点,重复第(2)(3)(4)(5)(6)步骤。

可以理解为,聚类出的同一个平面上的点,曲率小于某一阈值(设置的曲率阈值),才可以当作种子点,继续生长。

曲率阈值和法相量夹角阈值都是需要提前设置的。首先将曲率从小到大排序,选取最小的加入种子序列,这时候种子序列里只有一个点,然后拿出种子序列中的一个点(这时候也就是这个刚放进去的唯一的点),和设置了指定范围的临域中的点比较法相量夹角,如果小于法相量夹角阈值,则这个临域的点和种子点是同一个平面,另外,如果这个点的曲率小于曲率阈值,则把这个点当作种子点,放入种子序列。反复上面的过程,直到种子序列为空,此时长出了一个平面。

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

int main(int argc, char** argv)
{
	
	//点云的类型
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
	//打开点云
	if (pcl::io::loadPCDFile <pcl::PointXYZ>("d://PCD//11.pcd", *cloud) == -1)//改成想要输入的点云名称...*cloud就是把输入的点云记录到变量指针cloud中。
	{
		std::cout << "Cloud reading failed." << std::endl;
		return (-1);
	}
	cout << cloud->size() << endl;
	//设置搜索的方式或者说是结构
	pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
	//求法线
	pcl::PointCloud <pcl::Normal>::Ptr normals(new pcl::PointCloud <pcl::Normal>);
	pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normal_estimator;
	normal_estimator.setSearchMethod(tree);
	normal_estimator.setInputCloud(cloud);
	normal_estimator.setKSearch(50);
	normal_estimator.compute(*normals);
	
	//聚类对象<点,法线>
	pcl::RegionGrowing<pcl::PointXYZ, pcl::Normal> reg;//1首先还是先建立了一个区域增长的对象reg
	reg.setMinClusterSize(50);  //最小的聚类的点数  //2然后设置平面包含的最少点数(这个参数非常重要,小于这个参数的平面会被忽略不计)

	reg.setMaxClusterSize(1000000);  //最大的   //3然后设置最大的点数,原理同上,但是一般我们希望的是无穷大,所以可以设大一点,当然如果你有特殊要求可以按自己需求来
	reg.setSearchMethod(tree);    //搜索方式
	reg.setNumberOfNeighbours(30);    //设置搜索的邻域点的个数  //4然后设置参考的邻域点数,也就是看看周边的多少个点来决定这是一个平面(这个参数至关重要,决定了你的容错率,如果设置的很大,那么从全局角度看某一个点稍微有点歪也可以接受,如果设置的很小则通常检测到的平面都会很小)
	reg.setInputCloud(cloud);         //输入点  //5然后输入要检测的点云cloud
	//reg.setIndices (indices);
	reg.setInputNormals(normals);     //输入的法线  //6然后输入点云的法线
	reg.setSmoothnessThreshold(20 / 180.0 * M_PI);  //设置平滑度  //7然后设置判断的阈值,大概也就是两个法线在多大的夹角内还可以当做是共面的
	reg.setCurvatureThreshold(2.0);     //设置曲率的阈值   //8最后也是一个弯曲的阈值,这个决定了比当前考察的点和平均的法线角度,决定是否还有继续探索下去的必要。(也就是假设每个点都是平稳弯曲的,那么normal的夹角都很小,但是时间长了偏移的就大了,这个参数就是限制这个用的)
	std::vector <pcl::PointIndices> clusters;//9
	reg.extract(clusters);

	
	//9然后就可以把结果输出到一个簇里面,这个簇会自动把每个平面分成一个vector,可以打印下来看看
	std::cout << "Number of clusters is equal to " << clusters.size() << std::endl;
	std::cout << "First cluster has " << clusters[0].indices.size() << " points." << endl;
	std::cout << "These are the indices of the points of the initial" <<
		std::endl << "cloud that belong to the first cluster:" << std::endl;

	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_1(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_2(new pcl::PointCloud<pcl::PointXYZ>);
	for (int i = 0; i < clusters[0].indices.size(); i++)
	{
		cloud_1->push_back(cloud->points[clusters[0].indices[i]]);
	}
	cout << cloud_1->size() << endl;
	for (int j = 1; j < clusters.size(); j++)
	{
		for (int k = 0; k < clusters[j].indices.size(); k++)
		{
			cloud_2->push_back(cloud->points[clusters[j].indices[k]]);
		}
	}
	pcl::PCDWriter writer;
	writer.write("d://tree1.pcd", *cloud_2, false);//将点保存到PCD文件中去
	cout << cloud_2->size() << endl;
	//可视化聚类的结果   //10也可以把检测到的每个平面涂上不同的颜色
	pcl::PointCloud <pcl::PointXYZRGB>::Ptr colored_cloud = reg.getColoredCloud();
	pcl::visualization::CloudViewer viewer("Cluster viewer");
	viewer.showCloud(cloud_2);
	//viewer.runOnVisualizationThreadOnce(viewerOneOff);
	system("pause");
	return (0);
}

参考:
https://blog.csdn.net/m0_37957160/article/details/105856478

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值