简单平面点云的内外侧轮廓提取

本文介绍了一种基于经纬线扫描法和alphashapes算法的点云轮廓提取方法,并提供了具体的C++实现代码。通过这两种方法可以有效地从原始点云中提取内外侧轮廓。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原点云如图所示:
在这里插入图片描述
外侧轮廓提取用的方法是经纬线扫描法,全轮廓提取的方法是alpha shapes算法(点云边界提取方法总结),从全轮廓中剔除外侧轮廓得到内侧轮廓。

alpha shapes算法轮廓提取结果:
在这里插入图片描述

经纬线扫描法轮廓提取结果:

在这里插入图片描述
内侧轮廓:
在这里插入图片描述

#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/surface/concave_hull.h>


void BoundaryExtraction1(const pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud, pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud_boundary, int resolution)
{
	pcl::PointXYZ px_min = *std::min_element(cloud->begin(), cloud->end(), [](pcl::PointXYZ pt1, pcl::PointXYZ pt2) {return pt1.x < pt2.x; });
	pcl::PointXYZ px_max = *std::max_element(cloud->begin(), cloud->end(), [](pcl::PointXYZ pt1, pcl::PointXYZ pt2) {return pt1.x < pt2.x; });

	float delta_x = (px_max.x - px_min.x) / resolution;
	float min_y = INT_MAX, max_y = -INT_MAX;
	std::vector<int> indexs_x(2 * resolution);
	std::vector<std::pair<float, float>> minmax_x(resolution, { INT_MAX,-INT_MAX });
	for (size_t i = 0; i < cloud->size(); ++i)
	{
		int id = (cloud->points[i].x - px_min.x) / delta_x;
		if (cloud->points[i].y < minmax_x[id].first)
		{
			minmax_x[id].first = cloud->points[i].y;
			indexs_x[id] = i;
		}
		else if (cloud->points[i].y > minmax_x[id].second)
		{
			minmax_x[id].second = cloud->points[i].y;
			indexs_x[id + resolution] = i;
		}
	}

	pcl::PointXYZ py_min = *std::min_element(cloud->begin(), cloud->end(), [](pcl::PointXYZ pt1, pcl::PointXYZ pt2) {return pt1.y < pt2.y; });
	pcl::PointXYZ py_max = *std::max_element(cloud->begin(), cloud->end(), [](pcl::PointXYZ pt1, pcl::PointXYZ pt2) {return pt1.y < pt2.y; });

	float delta_y = (py_max.y - py_min.y) / resolution;
	float min_x = INT_MAX, max_x = -INT_MAX;
	std::vector<int> indexs_y(2 * resolution);
	std::vector<std::pair<float, float>> minmax_y(resolution, { INT_MAX,-INT_MAX });
	for (size_t i = 0; i < cloud->size(); ++i)
	{
		int id = (cloud->points[i].y - py_min.y) / delta_y;
		if (cloud->points[i].x < minmax_y[id].first)
		{
			minmax_y[id].first = cloud->points[i].x;
			indexs_y[id] = i;
		}
		else if (cloud->points[i].x > minmax_y[id].second)
		{
			minmax_y[id].second = cloud->points[i].x;
			indexs_y[id + resolution] = i;
		}
	}

	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_xboundary(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::copyPointCloud(*cloud, indexs_x, *cloud_xboundary);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_yboundary(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::copyPointCloud(*cloud, indexs_y, *cloud_yboundary);
	*cloud_boundary = *cloud_xboundary + *cloud_yboundary;
}


void BoundaryExtraction2(const pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud, pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud_boundary, double alpha)
{
	pcl::ConcaveHull<pcl::PointXYZ> chull;
	chull.setInputCloud(cloud); 
	chull.setAlpha(alpha); 
	chull.reconstruct(*cloud_boundary);
}


int main(int argc, char* argv[])
{
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_boundary1(new  pcl::PointCloud<pcl::PointXYZ>);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_boundary2(new  pcl::PointCloud<pcl::PointXYZ>);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_boundary3(new  pcl::PointCloud<pcl::PointXYZ>);
	pcl::io::loadPCDFile("test.pcd", *cloud);

	BoundaryExtraction1(cloud, cloud_boundary1, 200); //经纬线扫描法
	pcl::io::savePCDFile("boundary1.pcd", *cloud_boundary1);

	BoundaryExtraction2(cloud, cloud_boundary2, 3); //alpha shapes算法
	pcl::io::savePCDFile("boundary2.pcd", *cloud_boundary2);

	//求cloud_boundary2-cloud_boundary1
	pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree(new pcl::search::KdTree<pcl::PointXYZ>);
	kdtree->setInputCloud(cloud_boundary1);

	std::vector<int> pointIdxRadiusSearch;
	std::vector<float> pointRadiusSquaredDistance;
	std::vector<int> indices;
	float radius = 3;

	for (size_t i = 0; i < cloud_boundary2->size(); i++)
	{
		if (kdtree->radiusSearch(cloud_boundary2->points[i], radius, pointIdxRadiusSearch, pointRadiusSquaredDistance) > 0)
		{
			indices.push_back(i);
		}
	}

	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud3(new pcl::PointCloud<pcl::PointXYZ>);
	for (size_t i = 0; i < cloud_boundary2->size(); i++)
	{
		if (find(indices.begin(), indices.end(), i) == indices.end())
			cloud_boundary3->push_back(cloud_boundary2->points[i]);
	}
	
	pcl::io::savePCDFile("boundary3.pcd", *cloud_boundary3);

	return EXIT_SUCCESS;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

给算法爸爸上香

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

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

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

打赏作者

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

抵扣说明:

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

余额充值