4、统计滤波:统计离群点移除原理、均值与标准差计算、PCL实现、参数设置技巧
大家好,我是你们的老朋友。今天我们来聊聊点云滤波里一个非常实用的方法——统计滤波。说白了,它就是用来干掉那些“不靠谱”的离群点的。
你想想看,激光雷达扫描的时候,难免会碰到一些“捣乱”的点。比如空气中飘着的灰尘、传感器自身的噪声,或者物体边缘反射不稳定产生的点。这些点数量少,位置又很突兀,留着它们,后续做配准、分割、特征提取,都会出问题。统计滤波,就是专门收拾这些“坏点”的。
统计离群点移除原理
这个原理其实很直观。我打个比方:你站在一群人中间,如果大部分人都挤在一起,只有一个人离得远远的,那这个人就是离群点。统计滤波也是这个逻辑。
它基于一个假设:在点云中,每个点周围都应该有足够多的邻居点。如果一个点周围太“冷清”,邻居点很少,那它大概率就是个离群点。
具体怎么做呢?它会计算每个点到它所有邻居点的平均距离。然后,对整个点云的所有平均距离,算一个全局的均值(μ)和标准差(σ)。最后,设定一个阈值。如果一个点的平均距离超出了 μ + k * σ(k 是一个倍数),就把它判定为离群点,移除掉。
嗯,这里要注意:这个“邻居”是怎么定义的? 通常有两种方式:一种是找距离该点最近的 K 个点(K近邻);另一种是以该点为中心,画一个半径为 R 的球,球内的点就算邻居。PCL 里默认用的是 K 近邻。
核心思想一句话总结: 统计每个点与其邻居的距离分布,把那些“不合群”的点剔除。
均值与标准差计算
这部分是数学基础,但别怕,很简单。我拆开来讲。
假设我们有一个点云,里面有 N 个点。对于第 i 个点,我们计算它到 K 个最近邻居的平均距离,记作 d_i。那么,整个点云就有 N 个这样的平均距离值:d_1, d_2, ..., d_N。
均值(μ)的计算:
就是把所有 d_i 加起来,再除以 N。
μ = (d_1 + d_2 + ... + d_N) / N
标准差(σ)的计算:
标准差衡量的是这些平均距离的“离散程度”。公式是:
σ = sqrt( ( (d_1 - μ)^2 + (d_2 - μ)^2 + ... + (d_N - μ)^2 ) / N )
说白了,就是每个点的平均距离和全局平均距离的差的平方,加起来再平均,最后开个根号。
我在项目中遇到过一个问题:如果点云里离群点特别多,或者场景非常稀疏,这个均值和标准差的计算会被“带偏”。比如,一个空旷的走廊,点本来就少,平均距离自然就大。这时候再用默认参数,可能会把正常的墙点也给删了。所以,理解你的数据场景,比死记公式更重要。
PCL实现
在 PCL 里,实现统计滤波非常简单。核心就是 pcl::StatisticalOutlierRemoval 这个类。我直接上代码,大家一看就明白。
#include <pcl/point_types.h>
#include <pcl/filters/statistical_outlier_removal.h>
// 假设 cloud 是输入点云,类型是 pcl::PointCloud<pcl::PointXYZ>::Ptr
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
// 创建滤波器对象
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(cloud); // 设置输入点云
sor.setMeanK(50); // 设置K近邻搜索的邻居点数
sor.setStddevMulThresh(1.0); // 设置标准差倍数阈值
sor.filter(*cloud_filtered); // 执行滤波,结果保存到 cloud_filtered
// 如果你想保留被移除的点(离群点),可以这样做:
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_outliers(new pcl::PointCloud<pcl::PointXYZ>);
sor.setNegative(true); // 设置为取反,即保留离群点
sor.filter(*cloud_outliers);
你看,核心就三个步骤:设置输入、调两个参数、执行滤波。代码量很少,但参数调起来,学问可不少。
小提示: 如果你只想快速看看效果,可以先试试 setMeanK(50) 和 setStddevMulThresh(1.0)。这两个值在很多场景下是个不错的起点。
参数设置技巧
这部分是我最想跟大家分享的。参数设不好,滤波效果天差地别。我曾经就因为这个,在一个项目里折腾了两天。
统计滤波有两个关键参数:MeanK 和 StddevMulThresh。
| 参数 | 含义 | 设置技巧 |
|---|---|---|
| MeanK | 计算平均距离时,考虑的邻居点个数 | 点云密度大,可以设大一点(如50-100);密度小,设小一点(如10-30)。我一般先设50,再根据效果微调。 |
| StddevMulThresh | 标准差倍数阈值,控制滤波的严格程度 | 值越小,滤波越严格,移除的点越多。通常设为1.0到2.0之间。如果场景噪声大,可以设小点(如0.8);如果场景干净,可以设大点(如1.5)。 |
避坑指南:
- MeanK 太小,容易误删。 比如你设成5,一个点周围只有5个邻居,稍微有点抖动,平均距离就偏大,容易被当成离群点。我曾经在室内场景里设了10,结果把墙角的一些点全删了,导致后续建图出现空洞。
- StddevMulThresh 太大,容易漏删。 设成3.0以上,基本上就没什么滤波效果了,离群点全留着。
- 先粗调,后细调。 我的习惯是:先用
MeanK=50, StddevMulThresh=1.0跑一遍,看看效果。如果发现正常点被误删,就增大StddevMulThresh或者减小MeanK。如果发现离群点没删干净,就减小StddevMulThresh或者增大MeanK。
特别注意: 统计滤波对稀疏点云效果不太好。如果你的点云每平方米只有几十个点,那每个点的邻居都很少,平均距离都很大,滤波会把所有点都当成离群点。这种情况下,我建议先做体素滤波降采样,或者改用半径滤波。
最后,我画了一张图,帮大家把统计滤波的整个流程串起来。你看一眼,应该就全明白了。
好了,关于统计滤波,核心内容就这些。记住它的原理、两个关键参数,以及我分享的那些实战技巧。下次你在项目里遇到离群点,就知道该怎么收拾它们了。
可可精品资料库