文章目录
- 前言
- 一、应用案例
- 二、源码分析
- 1.ConvexHull类
- 2.reconstruct函数
- 3.performReconstruction 函数
- 4.calculateInputDimension 函数
- 总结
前言
本文分析一下pcl里凸包的源码。什么是凸包以及怎么求解,可以了解一下概念。
一、应用案例
#include <pcl/surface/convex_hull.h>
pcl::ConvexHull<pcl::PointXYZ> convex_hull;
convex_hull.setInputCloud(cloud_in);//输入点云
//convex_hull.setDimension(3);//设置维数,当然不设置,内部算法也会自己计算
//pcl::PointCloud<pcl::PointXYZ>::Ptr hull_out(new pcl::PointCloud<pcl::PointXYZ>);
convex_hull.reconstruct(*cloud_out);//输出凸包(点)
convex_hull.getHullPointIndices(Indices);//输出凸包(点)在输入点云中的id
二、源码分析
1.ConvexHull类
template<typename PointInT>
class ConvexHull : public MeshConstruction<PointInT>
{
...
//构造函数中dimension_(数据的维数) 默认是0
//x_axis_ , y_axis_ , z_axis_ 坐标轴三个参数也在这里确定
/** \brief Empty constructor. */
ConvexHull () : compute_area_ (false), total_area_ (0), total_volume_ (0), dimension_ (0),
projection_angle_thresh_ (cos (0.174532925) ), qhull_flags ("qhull "),
x_axis_ (1.0, 0.0, 0.0), y_axis_ (0.0, 1.0, 0.0), z_axis_ (0.0, 0.0, 1.0)
{
};
...
//输出凸包接口
/** \brief Compute a convex hull for all points given.
* \param[out] points the resultant points lying on the convex hull.
*/
void
reconstruct (PointCloud &points);
...
//设置维数接口(这里如果不设置具体的维数,则会维数默认值是构造函数里的值0,并且算法里会自行计算真实的维数)
void
setDimension (int dimension)
{
if ((dimension == 2) || (dimension == 3))
dimension_ = dimension;
else
PCL_ERROR ("[pcl::%s::setDimension] Invalid input dimension specified!\n", getClassName ().c_str ());
}
...
//获取凸包点在输入点云中的id
void
getHullPointIndices (pcl::PointIndices &hull_point_indices) const;
...
}
2.reconstruct函数
template <typename PointInT> void
pcl::ConvexHull<PointInT>::reconstruct (PointCloud &points)
{
points.header = input_->header;
//初始化,这里initCompute 实际上是PCLBase这个基类的初始化函数,前文有讲过,这里不再赘述
if (!initCompute () || input_->points.empty () || indices_->empty ())
{
points.points.clear ();
return;
}
// Perform the actual surface reconstruction
//这里就是真正的凸包计算接口
std::vector<pcl::Vertices> polygons;
performReconstruction (points, polygons, false);
points.width = static_cast<uint32_t> (points.points.size ());
points.height = 1;
points.is_dense = true;
deinitCompute ();
}
3.performReconstruction 函数
对于Qhull,有兴趣可以了解学习一下:
3.1 什么是Qhull
3.2 怎么下载编译Qhull
3.2 Qhull官网地址
template <typename PointInT> void
pcl::ConvexHull<PointInT>::performReconstruction (PointCloud &hull, std::vector<pcl::Vertices> &polygons,
bool fill_polygon_data)
{
//如上面所说,如果没有在维度设置接口中设置维度,则默认维度是0,也即会执行calculateInputDimension
if (dimension_ == 0)
calculateInputDimension ();
//后面这两函数就是具体的调用哦个Ahull库来实现凸包的计算
//具体怎么实现的,后续有空再研究吧,毕竟又涉及到另一个开源库,need more time~~
if (dimension_ == 2)
performReconstruction2D (hull, polygons, fill_polygon_data);
else if (dimension_ == 3)
performReconstruction3D (hull, polygons, fill_polygon_data);
else
PCL_ERROR ("[pcl::%s::performReconstruction] Error: invalid input dimension requested: %d\n",getClassName ().c_str (),dimension_);
}
4.calculateInputDimension 函数
template <typename PointInT> void
pcl::ConvexHull<PointInT>::calculateInputDimension ()
{
PCL_DEBUG ("[pcl::%s::calculateInputDimension] WARNING: Input dimension not specified. Automatically determining input dimension.\n", getClassName ().c_str ());
//计算输入点云质心
Eigen::Vector4d xyz_centroid;
compute3DCentroid (*input_, *indices_, xyz_centroid);
EIGEN_ALIGN16 Eigen::Matrix3d covariance_matrix;
//计算每个点的协方差矩阵
//这里原理就是:
//1.将原始点云坐标减去中心点坐标
//2.再对这些数据构造协方差矩阵,协方差矩阵具体构造原理看前文harris3d源码分析,就不再详细分析
//3.有一点要说明,这个部分构造协方差矩阵的开源代码,比harris3d里协方差矩阵开源代码要清晰很多
computeCovarianceMatrixNormalized (*input_, *indices_, xyz_centroid, covariance_matrix);
//求协方差矩阵的特征值
//主法向量是协方差矩阵的最小特征值对应的特征向量
EIGEN_ALIGN16 Eigen::Vector3d eigen_values;
pcl::eigen33 (covariance_matrix, eigen_values);
//对于二维的数据,主法向量就是0或者近似于0
if (std::abs (eigen_values[0]) < std::numeric_limits<double>::epsilon () || std::abs (eigen_values[0] / eigen_values[2]) < 1.0e-3)
dimension_ = 2;
else
dimension_ = 3;
}
总结
分析了下pcl里求解凸包的整体流程,有一些收获~~