PCL的filter模块中实现了很多点云滤波算法,以下简单介绍:
(1)直通滤波器 (2)条件滤波器 (3)统计滤波器 (4)体素栅格滤波器 (5)半径滤波器 的基本原理,并给出使用各种滤波器的完整片段。
(1)直通滤波器
最简单的一种滤波器,它的作用是过滤掉在指定维度方向上取值不在给定值域内的点。直通滤波器的实现原理如下:首先,指定一个维度以及该维度下的值域,其次,遍历点云中的每个点,判断该点在指定维度上的取值是否在值域内,删除取值不在值域内的点,最后,遍历结束,留下的点即构成滤波后的点云。直通滤波器简单高效,适用于消除背景等操作。
(2)条件滤波器
与直通滤波器类似,它的作用是过滤掉点云中不满足给定条件的点,其实现原理也与直通滤波器类似,只是判断方式改变了。相比于直通滤波器,条件滤波器更加灵活,功能也更丰富,其优势主要体现在条件的定义上。我们可以依据点云的不同特征定义多种条件,例如,定义条件为点在x,y和z维度下的取值同时满足某个值域,则可以在指定3D空间内对点云进行裁剪。
(3)统计滤波器
主要作用是去除稀疏离群噪点。在采集点云的过程中,由于测量噪声的影响,会引入部分离群噪点,它们在点云空间中分布稀疏。在估算点云局部特征(例如计算采样点处的法向量和曲率变化率)时,这些噪点可能导致错误的计算结果,从而使点云配准等后期处理失败。统计滤波器的主要思想是假设点云中所有的点与其最近的k个邻居点的平均距离满足高斯分布,那么,根据均值和方差可确定一个距离阈值,当某个点与其最近k个点的平均距离大于这个阈值时,判定该点为离群点并去除。统计滤波器的实现原理如下:首先,遍历点云,计算每个点与其最近的k个邻居点之间的平均距离;其次,计算所有平均距离的均值μ与标准差σ,则距离阈值dmax可表示为dmax=μ+α×σ,α是一个常数,可称为比例系数,它取决于邻居点的数目;最后,再次遍历点云,剔除与k个邻居点的平均距离大于dmax的点。
(4)体素栅格滤波器
主要作用是对点云进行降采样,可以在保证点云原有几何结构基本不变的前提下减少点的数量,多用于密集型点云的预处理中,以加快后续配准、重建等操作的执行速度。体素栅格滤波器的主要思想是在点云所在三维空间创建一个体素栅格,对于栅格内的每一个体素,以体素内所有点的重心近似代替体素中的点,最终,所有体素中的重心点组成滤波后的点云。通过体素的大小可以控制降采样的程度,体素越大,则滤波后的点云越稀疏。体素栅格滤波器的执行速度较慢,但是它能最大限度地保留点云原始的几何信息。
(5)半径滤波器
半径滤波器与统计滤波器相比更加简单粗暴。以某点为中心画一个圆计算落在该圆中点的数量,当数量大于给定值时,则保留该点,数量小于给定值则剔除该点。此算法运行速度快,依序迭代留下的点一定是最密集的,但是圆的半径和圆内点的数目都需要人工指定。
#include<iostream>
#include<pcl/point_types.h>
#include<pcl/filters/passthrough.h> //直通滤波器头文件
#include<pcl/filters/voxel_grid.h> //体素滤波器头文件
#include<pcl/filters/statistical_outlier_removal.h> //统计滤波器头文件
#include <pcl/filters/conditional_removal.h> //条件滤波器头文件
#include <pcl/filters/radius_outlier_removal.h> //半径滤波器头文件
int main(int argc, char** argv)
{
///****************************************************
/*创建点云数据集。*/
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->width = 500;
cloud->height = 1;
cloud->points.resize(cloud->width*cloud->height);
std::cout << "创建原始点云数据" << std::endl;
for (size_t i = 0; i < cloud->points.size(); ++i)
{
cloud->points[i].x = 1024 * rand() / (RAND_MAX + 1.0f);
cloud->points[i].y = 1024 * rand() / (RAND_MAX + 1.0f);
cloud->points[i].z = 1024 * rand() / (RAND_MAX + 1.0f);
}
for (size_t i = 0; i < cloud->points.size(); i++)
{
std::cerr << " " << cloud->points[i].x << " "
<< cloud->points[i].y << " "
<< cloud->points[i].z << std::endl;
}
std::cout << "原始点云数据点数:" << cloud->points.size()<< std::endl << std::endl;
///****************************************************
///****************************************************
/*方法一:直通滤波器对点云进行处理。*/
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_after_PassThrough(new pcl::PointCloud<pcl::PointXYZ>);//
pcl::PassThrough<pcl::PointXYZ> passthrough;
passthrough.setInputCloud(cloud);//输入点云
passthrough.setFilterFieldName("z");//对z轴进行操作
passthrough.setFilterLimits(0.0, 400.0);//设置直通滤波器操作范围
//passthrough.setFilterLimitsNegative(true);//true表示保留范围内,false表示保留范围外
passthrough.filter(*cloud_after_PassThrough);//执行滤波,过滤结果保存在 cloud_after_PassThrough
std::cout << "直通滤波后点云数据点数:" << cloud_after_PassThrough->points.size() << std::endl;
///****************************************************
///****************************************************
/*方法二:体素滤波器实现下采样*/
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_after_voxelgrid(new pcl::PointCloud<pcl::PointXYZ>);//
pcl::VoxelGrid<pcl::PointXYZ> voxelgrid;
voxelgrid.setInputCloud(cloud);//输入点云数据
voxelgrid.setLeafSize(10.0f, 10.0f, 10.0f);//AABB长宽高
voxelgrid.filter(*cloud_after_voxelgrid);
std::cout << "体素化网格方法后点云数据点数:" << cloud_after_voxelgrid->points.size() << std::endl;
///****************************************************
///****************************************************
/*方法三:统计滤波器滤波*/
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_after_StatisticalRemoval(new pcl::PointCloud<pcl::PointXYZ>);//
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> Statistical;
Statistical.setInputCloud(cloud);
Statistical.setMeanK(20);//取平均值的临近点数
Statistical.setStddevMulThresh(5);//临近点数数目少于多少时会被舍弃
Statistical.filter(*cloud_after_StatisticalRemoval);
std::cout << "统计分析滤波后点云数据点数:" << cloud_after_StatisticalRemoval->points.size() << std::endl;
///****************************************************
///****************************************************
/*方法四:条件滤波器*/
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_after_Condition(new pcl::PointCloud<pcl::PointXYZ>);
pcl::ConditionAnd<pcl::PointXYZ>::Ptr range_condition(new pcl::ConditionAnd<pcl::PointXYZ>());
range_condition->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new
pcl::FieldComparison<pcl::PointXYZ>("z", pcl::ComparisonOps::GT, 0.0))); //GT表示大于等于
range_condition->addComparison(pcl::FieldComparison<pcl::PointXYZ>::ConstPtr(new
pcl::FieldComparison<pcl::PointXYZ>("z", pcl::ComparisonOps::LT, 0.8))); //LT表示小于等于
pcl::ConditionalRemoval<pcl::PointXYZ> condition;
condition.setCondition(range_condition);
condition.setInputCloud(cloud); //输入点云
condition.setKeepOrganized(true);
condition.filter(*cloud_after_Condition);
std::cout << "条件滤波后点云数据点数:" << cloud_after_Condition->points.size() << std::endl;
///****************************************************
///****************************************************
/*方法五:半径滤波器*/
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_after_Radius(new pcl::PointCloud<pcl::PointXYZ>);
pcl::RadiusOutlierRemoval<pcl::PointXYZ> radiusoutlier; //创建滤波器
radiusoutlier.setInputCloud(cloud); //设置输入点云
radiusoutlier.setRadiusSearch(100); //设置半径为100的范围内找临近点
radiusoutlier.setMinNeighborsInRadius(2); //设置查询点的邻域点集数小于2的删除
radiusoutlier.filter(*cloud_after_Radius);
std::cout << "半径滤波后点云数据点数:" << cloud_after_Radius->points.size() << std::endl;
int a;
std::cin >> a;
return (0);
}