PCL 点云表面凸包重建(Surface_ConvexHull)

PCL专栏目录及须知-优快云博客

注意:凸包在工作中非常常用!!!二维、三维均是。

1.原理

计算点云凸包。凸包计算

(1)三维凸包

1)任选点云中四个点,形成一个初始凸包(四体)[四面体点集为D]

2)遍历所有点云点,取每个点云点P,射线法计算是否在凸多面体内部:

<1>若点P在凸包,跳过。

<2>若点P在凸包将点P加入点集D中,重新计算一个多面体[凸包],并删除点集D中,不属于凸包顶点的点云点,得到新的点集D。

3)遍历所有点云点,得到凸包。

如下图:

假设点12345[点集D]...组成当前凸包多面体。

若在三维空间中,射线法算得点P在凸包的外部。那么就将点P加入到点12345....组成的凸包点集D中,重新计算得到新的凸包。并将点P加入到点集D中。

遍历算得最终的凸包。

(2)二维凸包

1)计算点云的投影面(实际操作中往往都是点云面积最大的那个平面)。

2)将所有点云点投影到二维平面上

3)计算投影后的二维点云点的二维凸包(计算原理同三维凸包类似,只是多面体变成了多边形)。

4)得到凸包结果。

如下图:

我们假设你要计算下图这个兔子的二维凸包,投影面是屏幕正朝着你的这个面。你想象在这你把所有点云点拍平到这个面上(点云投影二维化【三维离散点变成一幅画】),然后你计算这个二维平面点云的凸包。

2.使用场景

(1)三维凸包使用场景:用于表面重建,计算物体大致轮廓时。

(2)二维凸包使用场景:

1)可用于计算某个平面的边缘,计算完毕之后,再进行多次直线拟合,拟合出平面。

2)计算某个物体的二维轮廓[往往需要先手动把点云点的z值设置为相同],然后通过该二维轮廓做一些相应操作。(不要不修改点云z值,直接计算二维凸包,它投影的面有一定随机性,实际工作中都是要做一下修改z值的前处理

比如

工作中,规划变电站巡检路线。变电站的这些变电桩在巡检的时候,有很多固定的拍摄点。

取这些点,把这些点的z值设置为某个固定高度,然后计算二维凸包

无人机拍摄路径沿着该凸包飞行。

3.注意事项

凸包挺好用,比较重要,很多场景可能可以用上。

4.关键函数

(1)设置是计算三维凸包还是二维凸包。

setDimension(3)

(2)设置是否计算凸包的表面积和体积(true是计算,false是不计算)。

setComputeAreaVolume(true)

(3)计算时:凸包的结果点云保存在参数1中;凸包的多边形点保存在参数2中。

reconstruct(*cloud_out_3d, polygons_3d)

5.代码

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/surface/convex_hull.h>
#include <boost/thread/thread.hpp>
#include <pcl/visualization/pcl_visualizer.h>

int main()
{
    /****************计算点云凸包********************/
    // 原始点云
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in_3d(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in_2d(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::io::loadPCDFile("D:/code/csdn/data/rabbit.pcd", *cloud_in_3d);   // 加载原始点云数据
	pcl::io::loadPCDFile("D:/code/csdn/data/JZDM.pcd", *cloud_in_2d);   // 加载原始点云数据
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_out_3d(new pcl::PointCloud<pcl::PointXYZ>);   // 结果点云
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_out_2d(new pcl::PointCloud<pcl::PointXYZ>);   // 结果点云

	// 计算凸包(3d)
	pcl::ConvexHull<pcl::PointXYZ> surface_3d;						// 3d凸包
	surface_3d.setInputCloud(cloud_in_3d);
	surface_3d.setDimension(3);										// 凸包设置凸包是2d的还是3d的
	surface_3d.setComputeAreaVolume(true);							// true:计算凸包的总面积和体积;false:不计算
	std::vector<pcl::Vertices> polygons_3d;							// 保存凸包顶点
	surface_3d.reconstruct(*cloud_out_3d, polygons_3d);				// 计算凸包
	float surface_area_3d = surface_3d.getTotalArea();				// 凸包总面积
	float surface_volume_3d = surface_3d.getTotalVolume();			// 凸包总体积

	// 计算凸包(2d)
	pcl::ConvexHull<pcl::PointXYZ> surface_2d;						// 2d凸包
	surface_2d.setInputCloud(cloud_in_2d);
	surface_2d.setDimension(2);										// 凸包设置凸包是2d的还是3d的
	surface_2d.setComputeAreaVolume(true);							// true:计算凸包的总面积和体积;false:不计算
	std::vector<pcl::Vertices> polygons_2d;							// 保存凸包顶点
	surface_2d.reconstruct(*cloud_out_2d, polygons_2d);				// 计算凸包
	float surface_area_2d = surface_2d.getTotalArea();				// 凸包总面积
	float surface_volume_2d = surface_2d.getTotalVolume();			// 凸包总体积

	/****************展示********************/
	// 3d
	// 模型
	boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer_3d(new pcl::visualization::PCLVisualizer("surface_3d"));
	viewer_3d->setBackgroundColor(0, 0, 0);
	viewer_3d->addPointCloud<pcl::PointXYZ>(cloud_in_3d, "cloud_3d");
	viewer_3d->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "cloud_3d");
	// 3d凸包
	viewer_3d->addPolygonMesh<pcl::PointXYZ>(cloud_out_3d, polygons_3d, "polygons_3d");
	viewer_3d->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 0, 0, "polygons_3d");
	viewer_3d->setRepresentationToWireframeForAllActors();

	// 2d
	// 模型
	boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer_2d(new pcl::visualization::PCLVisualizer("surface_2d"));
	viewer_2d->setBackgroundColor(0, 0, 0);
	viewer_2d->addPointCloud<pcl::PointXYZ>(cloud_in_2d, "cloud_2d");
	viewer_2d->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "cloud_2d");
	// 2d凸包
	viewer_2d->addPolygonMesh<pcl::PointXYZ>(cloud_out_2d, polygons_2d, "polygons_2d");
	viewer_2d->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 0, 0, "polygons_2d");
	viewer_2d->setRepresentationToWireframeForAllActors();

	while (!viewer_2d->wasStopped() || !viewer_3d->wasStopped())
	{

		viewer_2d->spinOnce(1000);
		viewer_3d->spinOnce(1000);
		boost::this_thread::sleep(boost::posix_time::microseconds(1000));

	}

	return 0;
}

6.结果展示

(1)三维凸包的结果(测试数据为z值不同的兔子)

(2)二维凸包的结果(测试数据为z值相同的一个平面点云):

好的,以下是参考代码: ```cpp #include <iostream> #include <pcl/io/pcd_io.h> #include <pcl/point_types.h> #include <pcl/surface/mls.h> #include <pcl/visualization/pcl_visualizer.h> #include <pcl/surface/convex_hull.h> #include <pcl/common/transforms.h> int main() { // 读取点云文件 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); pcl::io::loadPCDFile("D:\\wodedm\\py_pcd\\volex_grid\\copy_of_fragment_vxsj.pcd", *cloud); // 流形最小二乘法进行表面拟合 pcl::PointCloud<pcl::PointXYZ>::Ptr mls_points(new pcl::PointCloud<pcl::PointXYZ>); pcl::MovingLeastSquares<pcl::PointXYZ, pcl::PointXYZ> mls; mls.setInputCloud(cloud); mls.setSearchRadius(0.05); mls.setPolynomialFit(true); mls.setPolynomialOrder(2); mls.setUpsamplingMethod(pcl::MovingLeastSquares<pcl::PointXYZ, pcl::PointXYZ>::SAMPLE_LOCAL_PLANE); mls.setUpsamplingRadius(0.005); mls.setUpsamplingStepSize(0.003); mls.process(*mls_points); // 进行凸包计算 pcl::PointCloud<pcl::PointXYZ>::Ptr hull(new pcl::PointCloud<pcl::PointXYZ>); pcl::ConvexHull<pcl::PointXYZ> convex_hull; convex_hull.setInputCloud(mls_points); convex_hull.reconstruct(*hull); // 可视化结果 pcl::visualization::PCLVisualizer viewer("Surface boundary"); viewer.setBackgroundColor(0.0, 0.0, 0.0); viewer.addPointCloud<pcl::PointXYZ>(cloud, "original"); viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "original"); viewer.addPointCloud<pcl::PointXYZ>(mls_points, "fitting"); viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "fitting"); viewer.addPointCloud<pcl::PointXYZ>(hull, "hull"); viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "hull"); viewer.spin(); // 保存边界点到文件 pcl::io::savePCDFileASCII("surface_boundary.pcd", *hull); return 0; } ``` 注意,此代码使用了流形最小二乘法进行表面拟合,并进行了凸包计算,最终可视化了原始点云、拟合点云以及表面边界,并将表面边界点保存到了新文件 "surface_boundary.pcd"。具体参数可以根据实际情况进行调整。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值