点云区域生长分割

该博客介绍了区域生长分割算法的详细步骤,包括计算点云曲率、选取种子点、邻域搜索和法线夹角及曲率阈值判断。通过PCL库实现了点云数据的处理,以曲率从小到大排序选取种子点,进行聚类,并过滤满足特定点数范围的类。该算法用于从点云数据中分割出特定特征的平面区域。
摘要由CSDN通过智能技术生成

原理介绍:
(1)首先计算出来各点的曲率值,将曲率值按照从小到大的顺序进行排序。
(2)设置一空的种子点序列和一个空的聚类数组。
(3)选取曲率最小的点放入上述种子点序列中。
(4)从序列中拿出来一个种子点搜索其邻域。
(5)搜索到邻域后,这些点先过法线夹角阈值,通过的保留到聚类数据,然后再从这些通过法线夹角阈值的点中,检查是否通过曲率阈值,通过的加入种子点序列。即比较邻域点的法线与当前种子点法线之间的夹角,如果小于阈值将该邻域点加入聚类数组。再从这些小于法线阈值的点中判断是否小于曲率阈值,如果小于则将该邻域点加入种子序列。
(6)结束后删除用过的种子点,用下一个种子点重复第(4)(5)步,直至种子点序列清空为止。
(7)从曲率排序的点中,即从小到大的排序,取聚类数组中没有的点作为种子点,重复第(2)(3)(4)(5)(6)步骤。
可以理解为,聚类出的同一个平面上的点,曲率小于某一阈值(设置的曲率阈值),才可以当作种子点,继续生长。
代码实现:

/**
 * @description:				区域生长分割算法
 * @param cloud					输入点云
 * @param clusters				分割结果
 * @param smoothness_threshold	平滑度阈值
 * @param curvature_threshold	曲率阈值
 * @param min_cluster_size		最小聚类点数	
 * @param max_cluster_size		最大聚类点数
 * @param nr_k					k邻近点数
 */
void regiongrow(pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud, std::vector<pcl::PointCloud<pcl::PointXYZ>::Ptr>& clusters,
	float smoothness_threshold, float curvature_threshold, int min_cluster_size, int max_cluster_size, int nr_k)
{
	pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);//创建Kd搜索树
	tree->setInputCloud(cloud);//输入点云

	//计算点云的法线和曲率
	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(nr_k);// 设置用于法向量估计的k近邻数目
	normal_estimator.compute(*normals);//计算并输出法向量

	pcl::PointCloud<pcl::PointXYZINormal>::Ptr cloud_normals(new pcl::PointCloud<pcl::PointXYZINormal>);
	pcl::concatenateFields(*cloud, *normals, *cloud_normals);
	//pcl::io::savePCDFile("cloud_normals.pcd", *cloud_normals);

	//将曲率值按照从小到大的顺序对点云中的点进行排序
	sort(cloud_normals->begin(), cloud_normals->end(),
		[](pcl::PointXYZINormal pt1, pcl::PointXYZINormal pt2) {return pt1.curvature < pt2.curvature; });
	//pcl::io::savePCDFile("cloud_normals_sorted.pcd", *cloud_normals);

	//设置一空的种子点序列和一空的聚类数组
	std::vector<int> seed_queue;
	std::vector<bool> cluster_queue(cloud_normals->points.size(), false); 
	std::vector<bool> cluster_queue_old(cloud_normals->points.size(), false);

	pcl::KdTreeFLANN<pcl::PointXYZINormal> kdtree;
	kdtree.setInputCloud(cloud_normals);
	std::vector<int> pointsIdx(nr_k);          //索引
	std::vector<float> pointsDistance(nr_k);   //距离

	int cluster_size = 0; //类的数目

	//从曲率排序的点中,即从小到大的排序,取聚类数组中没有的点作为种子点
	for (int i = 0; i < cloud_normals->size(); ++i)
	{
		if (cluster_queue[i]) continue;
			
		//选取曲率最小的点放入上述种子点序列中
		seed_queue.push_back(i);

		++cluster_size;

		//直至种子点序列清空为止
		while (!seed_queue.empty())
		{
			//从序列中拿出来一个种子点搜索其邻域
			int seed_index = seed_queue.back();
			seed_queue.pop_back();

			kdtree.nearestKSearch(cloud_normals->points[seed_index], nr_k, pointsIdx, pointsDistance);    //k近邻搜索

			//搜索到邻域后,这些点先过法线夹角阈值,通过的保留到聚类数据,从这些通过法线夹角阈值的点中,检查是否通过曲率阈值,通过的加入种子点序列。
			//即比较邻域点的法线与当前种子点法线之间的夹角,如果小于阈值将该邻域点加入聚类数组。
			//再从这些小于法线阈值的点中判断是否小于曲率阈值,如果小于则将该邻域点加入种子序列。
			Eigen::Vector3f v1(cloud_normals->points[seed_index].normal_x, cloud_normals->points[seed_index].normal_y, cloud_normals->points[seed_index].normal_z);
			for (int j = 0; j < pointsIdx.size(); ++j)
			{
				Eigen::Vector3f	v2(cloud_normals->points[pointsIdx[j]].normal_x, cloud_normals->points[pointsIdx[j]].normal_y, cloud_normals->points[pointsIdx[j]].normal_z);
				float angle = atan2(v1.cross(v2).norm(), v1.transpose() * v2) * 180 / M_PI;
				if (fabs(angle) <= smoothness_threshold && !cluster_queue[pointsIdx[j]])
				{
					cluster_queue[pointsIdx[j]] = true;
					if (cloud_normals->points[pointsIdx[j]].curvature <= curvature_threshold && find(seed_queue.begin(), seed_queue.end(), pointsIdx[j]) == seed_queue.end())
						seed_queue.push_back(pointsIdx[j]);
				}
			}
		}

		//计算当前类中包含点的索引
		std::vector<int> indexs;
		for (size_t i = 0; i < cluster_queue.size(); ++i)
		{
			if (cluster_queue[i] && !cluster_queue_old[i])
				indexs.push_back(i);
		}
		cluster_queue_old = cluster_queue;
	
		//过滤满足最大点数和最小点数的类
		if (indexs.size() >= min_cluster_size && indexs.size() <= max_cluster_size)
		{
			pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_flag(new pcl::PointCloud<pcl::PointXYZ>);
			pcl::copyPointCloud(*cloud_normals, indexs, *cloud_flag);
			clusters.push_back(cloud_flag);
			//std::cout << indexs.size() << std::endl;
			//pcl::io::savePCDFile("cloud_flag" + std::to_string(cluster_size) + ".pcd", *cloud_flag);
		}
	}
}

代码传送门:https://github.com/taifyang/PCL-algorithm

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

给算法爸爸上香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值