PCL库学习(二)(滤波算法详解)
简介:上一章对pcl进行了整体概述,这一节将对pcl滤波算法进行详解。pcl源代码提供了多种点云处理方式,本节先对源码进行解析,后续将会自己编写一些针对性的滤波算法。
在获取点云数据时 ,由于设备精度,操作者经验环境因素带来的影响,以及电磁波的衍射特性,被测物体表面性质变化和数据拼接配准操作过程的影响,点云数据中将不可避免的出现一些噪声。在点云处理流程中滤波处理作为预处理的第一步,对后续的影响比较大,只有在滤波预处理中将噪声点 ,离群点,孔洞,数据压缩等按照后续处理定制,才能够更好的进行配准,特征提取,曲面重建,可视化等后续应用处理,PCL中点云滤波模块提供了很多灵活实用的滤波处理算法,例如:双边滤波,高斯滤波,条件滤波,直通滤波,基于随机采样一致性滤波RANSAC等。滤波模块是作为 PCL的一个重要处理模块,其在应用中可以非常方便与其他点云处理流程协同使用。
1. 低通滤波器(NOISE filter)
局部范围内拟合平面,设置适当的阈值,把远离平面的点当做离群点删除,适合于处理大块平整点云。但是,特别是如果使用太高的内核半径(或太低的错误阈值),它将“吃掉”角落。为了保存角点或锐边,可以尝试以较小的半径和相对较高的错误阈值重复运行该算法。
Points/Radius:选择在每个点周围提取给定数量的邻居(适用于密度恒定的云)或指定球半径(球应该足够大,通常至少可以捕获6个点)
Max error:输入最大误差(点到拟合平面的距离),以确定点是否被删除。误差可以是相对的Relative(作为拟合平面上邻域重投影误差的一个因子)或绝对的Absolute。
Remove isolated points:去除孤立点。如果使用的是②位置的半径搜索,球邻域内少于3个点,则认为该点为孤立点,剔除。
**总结:**低通滤波器包含了KNN算法,通过球状空间囊括最近领域的点云数据对离群点云进行了滤波,可以减少毛刺,但对于大面积点云没有较好的滤除效果。
2. 高斯滤波
高斯滤波
Spatial sigma:滤波器空间部分的正态分布方差
Scalar sigma:滤波器标量部分的正态分布方差
高斯滤波代码如下:
///
template<typename PointInT, typename PointOutT> bool
pcl::filters::GaussianKernel<PointInT, PointOutT>::initCompute ()
{
if (sigma_ == 0)
{
PCL_ERROR ("Sigma is not set or equal to 0!\n", sigma_);
return (false);
}
sigma_sqr_ = sigma_ * sigma_;
if (sigma_coefficient_)
{
if ((*sigma_coefficient_) > 6 || (*sigma_coefficient_) < 3)
{
PCL_ERROR ("Sigma coefficient (%f) out of [3..6]!\n", (*sigma_coefficient_));
return (false);
}
else
threshold_ = (*sigma_coefficient_) * (*sigma_coefficient_) * sigma_sqr_;
}
return (true);
}
///
template<typename PointInT, typename PointOutT> PointOutT
pcl::filters::GaussianKernel<PointInT, PointOutT>::operator() (const Indices& indices,
const std::vector<float>& distances)
{
using namespace pcl::common;
PointOutT result;
float total_weight = 0;
std::vector<float>::const_iterator dist_it = distances.begin ();
for (Indices::const_iterator idx_it = indices.begin ();
idx_it != indices.end ();
++idx_it, ++dist_it)
{
if (*dist_it <= threshold_ && isFinite ((*input_) [*idx_it]))
{
float weight = std::exp (-0.5f * (*dist_it) / sigma_sqr_);
result += weight * (*input_) [*idx_it];
total_weight += weight;
}
}
if (total_weight != 0)
result /= total_weight;
else
makeInfinite (result);
return (result);
}
3. 统计滤波
在点云处理中,异常点(Outliers)是指与周围点相比显著不同的点,稀疏离群点去除,基于计算输入数据中的点与邻域距离的分布。对于每个点,计算它与所有邻居之间的平均距离。假设结果分布是具有平均数和标准差的高斯分布,所有平均距离在由全局距离平均数和标准差定义的区间之外的点都可以被视为离群点,并从数据集中修剪掉。
统计异常点移除的工作原理如下:
1.对于点云中的每个点,统计其邻近点的距离分布情况,计算其邻近点的距离均值和标准差。
2.将距离均值的K倍标准差之外的点标记为异常点。
3.将所有被标记为异常点的点移除,得到经过滤波处理后的点云数据。
在PCL中,统计异常点移除的主要参数如下:
setInputCloud:设置输入点云数据。
setMeanK:设置用于计算邻近点的数量K。
setStddevMulThresh:设置标准差倍数阈值,超出该阈值的点将被视为异常点。
实现代码如下
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/statistical_outlier_removal.h>
int main(int argc, char** argv)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
// 从文件读取点云
pcl::PCDReader reader;
reader.read<pcl::PointXYZ>("G:/vsdata/PCLlearn/PCDdata/table_scene_lms400.pcd", *cloud);
std::cerr << "Cloud before filtering: " << std::endl;
std::cerr << *cloud << std::endl;
// 创建过滤器,每个点分析计算时考虑的最近邻居个数为50个;
// 设置标准差阈值为1,这意味着所有距离查询点的平均距离的标准偏差均大于1个标准偏差的所有点都将被标记为离群值并删除。
// 计算输出并将其存储在cloud_filtered中
// 创建滤波器
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);
// 设置平均距离估计的最近邻的数量K
sor.setMeanK(50);
// 设置标准差阈值系数
sor.setStddevMulThresh(1.0);
// 执行过滤
sor.filter(*cloud_filtered);
std::cerr << "Cloud after filtering: " << std::endl;
std::cerr << *cloud_filtered << std::endl;
// 将留下来的点保存到后缀为_inliers.pcd的文件
pcl::PCDWriter writer;
writer.write<pcl::PointXYZ>("G:/vsdata/PCLlearn/PCDdata/table_scene_lms400_inliers.pcd", *cloud_filtered, false);
// 使用个相同的过滤器,但是对输出结果取反,则得到那些被过滤掉的点,保存到_outliers.pcd文件
sor.setNegative(true);
sor.filter(*cloud_filtered);
writer.write<pcl::PointXYZ>("G:/vsdata/PCLlearn/PCDdata/table_scene_lms400_outliers.pcd", *cloud_filtered, false);
return (0);
}