简介
谈及点云体素化是一种点云比较普遍的处理方式,之前的一篇博文【pcl入门教程滤波系列】之VoxelGrid有讲解点云体素化与近似体素化的情况。主要涉及两个函数pcl::VoxelGrid
与pcl::ApproximateVoxelGrid
的体素化方式,其中第一个是通过划分体素栅格求取每个栅格里面的点云重心来作为这个栅格的点云代表,而第二个则是依据栅格的中心点来代表该栅格的点云。当然,各有各的用在不同的场景。不知道你是否想过,为什么没有求取栅格中最低点的方法?这样对于划分体素栅格后获取最低点,有助于对地面点分割求取比较重要的一步。很庆幸,pcl::GridMinimum
就是用来满足这个需求的。
![](https://img-blog.csdnimg.cn/20200903185158361.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NtYWxsX011bmljaA==,size_16,color_FFFFFF,t_70)
类pcl::GridMinimum
就是通过将三维点云在X-Y平面划分网格,然后寻找每个网格中最小的z点云代表该网格。如果网格没有点云,那么不会像pcl::ApproximateVoxelGrid
产生中心点代表该网格点云。
代码Demo
下面的代码就是通过pcl::GridMinimum
实现网格化求取最小高程点云的算法示例,比较简单就不多做解释啦。
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/grid_minimum.h>
#include <chrono>
int main(int argc, char** argv)
{
pcl::PointCloud<pcl::PointXYZI>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZI>);
pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZI>);
auto startTime = std::chrono::steady_clock::now();
// Fill in the cloud data
pcl::PCDReader reader;
// Replace the path below with the path where you saved your file
reader.read("apollo.pcd", *cloud);
// Create the filtering object
pcl::GridMinimum<pcl::PointXYZI> sor(0.25);
sor.setInputCloud(cloud);
sor.filter(*cloud_filtered);
std::cerr << "PointCloud before filtering: " << cloud->width * cloud->height
<< " data points ( " << pcl::getFieldsList(*cloud) << " )." << std::endl;
std::cerr << "PointCloud after filtering: " << cloud_filtered->width * cloud_filtered->height
<< " data points ( " << pcl::getFieldsList(*cloud_filtered) << " )." << std::endl;
auto endTime = std::chrono::steady_clock::now();
auto ellapsedTime = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
std::cerr << "Ellapse-Time: " << ellapsedTime.count() << " milliseconds." << std::endl;
pcl::PCDWriter writer;
writer.write("apollo_downsampled0.25.pcd", *cloud_filtered);
return 0;
}
grid_minimum.h
源码头文件分析
namespace pcl
{
/** \brief GridMinimum assembles a local 2D grid over a given PointCloud, and downsamples the data.
*
* The GridMinimum class creates a *2D grid* over the input point cloud
* data. Then, in each *cell* (i.e., 2D grid element), all the points
* present will be *downsampled* with the minimum z value. This grid minimum
* can be useful in a number of topographic processing tasks such as crudely
* estimating ground returns, especially under foliage.
*
* \author Bradley J Chambers
* \ingroup filters
*/
template <typename PointT>
class GridMinimum: public FilterIndices<PointT>
{
protected:
using Filter<PointT>::filter_name_;
using Filter<PointT>::getClassName;
using Filter<PointT>::input_;
using Filter<PointT>::indices_;
using PointCloud = typename FilterIndices<PointT>::PointCloud;
public:
/** \brief Empty constructor. */
GridMinimum (const float resolution)
{
setResolution (resolution);
filter_name_ = "GridMinimum";
}
/** \brief Destructor. */
~GridMinimum ()
{
}
/** \brief Set the grid resolution.
* \param[in] resolution the grid resolution
*/
inline void
setResolution (const float resolution)
{
resolution_ = resolution;
// Use multiplications instead of divisions
inverse_resolution_ = 1.0f / resolution_;
}
/** \brief Get the grid resolution. */
inline float
getResolution () { return (resolution_); }
protected:
/** \brief The resolution. */
float resolution_;
/** \brief Internal resolution stored as 1/resolution_ for efficiency reasons. */
float inverse_resolution_;
/** \brief Downsample a Point Cloud using a 2D grid approach
* \param[out] output the resultant point cloud message
*/
void
applyFilter (PointCloud &output) override;
/** \brief Filtered results are indexed by an indices array.
* \param[out] indices The resultant indices.
*/
void
applyFilter (std::vector<int> &indices) override
{
applyFilterIndices (indices);
}
/** \brief Filtered results are indexed by an indices array.
* \param[out] indices The resultant indices.
*/
void
applyFilterIndices (std::vector<int> &indices);
};
}
我们从上述源码头文件可以清晰的看出class GridMinimum
类的唯一设置参数是resolution_
,它可以通过构造函数显示进行初始化。当然,可以通过setResolution()
函数进行设置分辨率。这个是与网格的大小有关,分辨率越小网格划分越密集,分辨率设置越大,网格划分越稀疏。类class GridMinimum
继承FilterIndices<PointT>
因此只需要在构造函数初始化时候设置分辨率,就可以获取网格最小化后的点云结果。
实验结果
下面我们看下不同分辨率情况下的网格化GridMinimum
结果:
![](https://img-blog.csdnimg.cn/20200903183510701.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NtYWxsX011bmljaA==,size_16,color_FFFFFF,t_70)
![](https://img-blog.csdnimg.cn/20200903183741643.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NtYWxsX011bmljaA==,size_16,color_FFFFFF,t_70)
![](https://img-blog.csdnimg.cn/20200903183825398.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NtYWxsX011bmljaA==,size_16,color_FFFFFF,t_70)
![](https://img-blog.csdnimg.cn/20200903183910175.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NtYWxsX011bmljaA==,size_16,color_FFFFFF,t_70)
小结
从上面实验结果来看,分辨率设置越小点云网格划分越密集,这样输出的网格后点云也更多。例如:分辨率为0.5时候,仔细发现中心周围车辆会出现一些蓝色的点。分辨率过大的话,网格会过于稀疏,例如分辨率为2.0的可视化效果。当然,不同分辨率计算的时间也会有一定的差距,需要依据实际场景来进行设置。pcl::GridMinimum
能够有效的过滤高程信息间断的情况,例如车辆停在树下等,可以有效的找出最低点,为后续分割做准备。但是,如果树下没有车辆或者也没能够扫描出地面点,那么该网格的点可能就是扫描出树叶的最低点。这需要后续更进一步的处理。本篇博文主要介绍一下pcl::GridMinimum
的应用,如有错误,还请批评指正。
参考
https://pointclouds.org/documentation/classpcl_1_1_grid_minimum.html#a1ea51d3b624cbaf1dcba3af90d67c292