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值相同的一个平面点云):