原文链接:Removing outliers using a StatisticalOutlierRemoval filter
本小节将学习如何使用统计分析技术,从一个点云数据集中移除测量噪声点(也就是离群点)。
背景知识
激光扫描通常会产生密度不均匀的点云数据集。另外,测量中的误差会产生稀疏的离群点,使效果更糟。估计局部点云特征(例如采样点处法向量或曲率变化率)的运算很复杂,这会导致错误的数值,反过来有可能导致点云的配准等后期处理失败。
以下方法可以解决其中部分问题:
对每个点的邻域进行一个统计分析,并修剪掉那些不符合一定标准的点。我们的稀疏离群点移除方法基于在输人数据中对点到临近点的距离分布的计算。对每个点,我们计算它到它的所有临近点的平均距离。假设得到的结果是一个高斯分布,其形状由均值和标准差决定,平均距离在标准范围(由全局距离平均值和方差定义)之外的点,可被定义为离群点并可从数据集中去除掉。
下图展示了稀疏离群点分析和去除的效果:原始数据集显示在左边,结果数据集显示在右边。图中显示了滤波前后点邻域的平均k近邻距离。
程序代码
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/statistical_outlier_removal.h>
#include<pcl/visualization/pcl_visualizer.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>("table_scene_lms400.pcd", *cloud);
std::cerr << "Cloud before filtering: " << std::endl;
std::cerr << *cloud << std::endl;
// 创建滤波器对象
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);//设置待滤波的点云
sor.setMeanK(50);//设置在进行统计时考虑查询点邻居点数
sor.setStddevMulThresh(1.0);//设置判断是否为离群点的阈值
sor.filter(*cloud_filtered);//将滤波结果保存在cloud_filtered中
std::cerr << "Cloud after filtering: " << std::endl;
std::cerr << *cloud_filtered << std::endl;
pcl::PCDWriter writer;
writer.write<pcl::PointXYZ>("table_scene_lms400_inliers.pcd", *cloud_filtered, false);
sor.setNegative(true);
pcl::visualization::PCLVisualizer viewer("statistic removal");
int v1(1);
int v2(2);
//创建视窗
viewer.createViewPort(0, 0, 0.5, 1, v1);
viewer.createViewPort(0.5, 0, 1, 1, v2);
viewer.addPointCloud(cloud, "cloud", v1);
viewer.addPointCloud(cloud_filtered, "cloud_filtered", v2);
sor.filter(*cloud_filtered);
writer.write<pcl::PointXYZ>("table_scene_lms400_outliers.pcd", *cloud_filtered, false);
while (!viewer.wasStopped())
{
viewer.spinOnce();
}
return (0);
}
首先创建了一个pcl : : StatisticalOutlierRemoval滤波器,将对每个点分析的临近点个数设为50,并将标准差倍数设为1,这意味着如果一个点的距离超出平均距离一个标准差以上,则该点被标记为离群点,并将被移除。计算后的输出结果储存在cloud_ filtered中。
// 创建滤波器对象
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);//设置待滤波的点云
sor.setMeanK(50);//设置在进行统计时考虑查询点邻居点数
sor.setStddevMulThresh(1.0);//设置判断是否为离群点的阈值
sor.filter(*cloud_filtered);//将滤波结果保存在cloud_filtered中
实验结果
左图为原始点云,右图为去除离群点之后的点云,可以看到一些噪点被去除干净:
原始点云:
经统计学滤波后的点云:
被剔除的离群点:
打印结果:
Cloud before filtering:
points[]: 460400
width: 460400
height: 1
is_dense: 1
sensor origin (xyz): [0, 0, 0] / orientation (xyzw): [0, 0, 0, 1]Cloud after filtering:
points[]: 451410
width: 451410
height: 1
is_dense: 1
sensor origin (xyz): [0, 0, 0] / orientation (xyzw): [0, 0, 0, 1]