4. 半径滤波(Radius Outlier Removal):原理详解、半径与邻点数的关系、PCL实现、与统计滤波的对比

各位同学好,今天我们来聊聊点云滤波里一个非常实用的方法——半径滤波。英文全称叫 Radius Outlier Removal,简称 ROR。

说实话,我在做SLAM和三维重建的初期,经常被点云里的“飞点”搞得头疼。就是那种明明扫描的是墙面,结果莫名其妙飘在空中的几个点。后来我意识到,这些孤立的离群点,用半径滤波来处理,效果出奇的好。

4.1 原理详解:一句话说清楚

半径滤波的原理,说白了就是“看朋友圈”。

对于点云中的每一个点,我们以它为中心,画一个半径为 r 的球。然后数一数这个球里面有多少个邻居点。如果邻居点的数量少于我们设定的阈值 k,那这个点就被判定为离群点,直接干掉。

你想想看,一个正常的点,周围应该有不少小伙伴陪着它。而那些孤零零的飞点,周围空空荡荡,自然就被识别出来了。

核心思想: 每个点必须在其半径 r 的邻域内,至少拥有 k 个邻居点,否则视为离群点。

嗯,这里要注意一点:半径滤波是逐个点进行判断的,不是对整个点云做全局统计。这一点和后面要讲的统计滤波有本质区别。

4.2 半径与邻点数的关系:怎么调参?

我个人习惯把这两个参数看成一对“搭档”。半径 r 和邻点数阈值 k 是相互制约的,调参的时候不能只看一个。

我给大家画个图,方便理解:

半径与邻点数的关系示意图 半径 r 大 小 邻点数阈值 k 高 低 稀疏点云 密集点云 小半径 低阈值 大半径 高阈值

从图上可以看出来:

  • 半径 r 越大,球内包含的邻居点就越多,所以阈值 k 可以设得高一些。
  • 半径 r 越小,球内邻居点就少,阈值 k 就得设低一些,否则会把正常点也误删。
  • 点云密度不均匀时,这个矛盾会更突出。我在项目中遇到过,用激光雷达扫描室内环境,近处的墙面点很密,远处的墙角点很稀疏。这时候一个固定的半径和阈值,很难同时照顾到远近。

我的调参经验: 先根据点云的平均密度估算一个半径。比如点云平均点间距是 0.05m,那半径可以取 0.1m ~ 0.2m。然后阈值 k 从 3 开始试,慢慢往上加,直到离群点被清干净,同时正常点没有被过度删除。

4.3 PCL实现:代码说话

好了,理论讲完了,咱们直接上代码。PCL 里实现半径滤波非常简单,核心就几行。

#include <pcl/point_types.h>
#include <pcl/filters/radius_outlier_removal.h>

// 1. 创建点云对象
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);

// 2. 加载点云(假设你已经有了)
// cloud = ... 

// 3. 创建半径滤波对象
pcl::RadiusOutlierRemoval<pcl::PointXYZ> ror;

// 4. 设置参数
ror.setInputCloud(cloud);
ror.setRadiusSearch(0.1);      // 半径:0.1米
ror.setMinNeighborsInRadius(5); // 最少邻居点数:5个

// 5. 执行滤波
ror.filter(*cloud_filtered);

// 6. 查看结果
std::cout << "滤波前点数: " << cloud->size() << std::endl;
std::cout << "滤波后点数: " << cloud_filtered->size() << std::endl;

代码就这么简单。但我要提醒你,setRadiusSearch 和 setMinNeighborsInRadius 这两个参数,一定要根据你的点云实际情况来调。

我曾经在一个项目里,点云的单位是毫米,结果我忘了换算,半径设成了 0.1,结果把整个点云都删光了。嗯,这种低级错误,犯过一次就记住了。

4.4 与统计滤波的对比:到底选哪个?

很多同学会问:半径滤波和统计滤波(Statistical Outlier Removal, SOR)到底有什么区别?我该用哪个?

好,我直接给你对比一下:

对比项 半径滤波 (ROR) 统计滤波 (SOR)
判断依据 每个点邻域内的绝对数量是否达标 每个点到邻居的平均距离是否偏离全局分布
核心参数 半径 r、邻点数阈值 k 邻居数 k、标准差倍数 std_mul
适用场景 孤立飞点、稀疏离群点 分布均匀的噪声点、高斯噪声
对密度变化的敏感度 高(密度不均时容易误删或漏删) 低(基于统计分布,适应性更强)
计算速度 较快(只需一次邻域搜索) 稍慢(需要计算距离分布)
我的推荐 点云密度均匀、离群点明显孤立时 点云密度变化大、噪声分布复杂时

说白了,半径滤波是个“老实人”,它只看局部,不看全局。统计滤波则更“聪明”一些,它会先看看整个点云的距离分布是什么样的,再决定哪些点是异常的。

但“聪明”也有代价。统计滤波假设点云的距离分布是高斯分布,如果你的点云不是这样,那它反而会出错。

避坑指南: 我曾经在一个户外大场景的 LiDAR 点云上用了统计滤波,结果把远处的稀疏点全删了,因为这些点到邻居的距离确实很大,但它们是真实的地面点,不是噪声。后来换成半径滤波,配合一个稍大的半径,才保住了这些点。

所以我的建议是:

  • 如果你知道你的离群点就是那种“孤零零飘着”的,用半径滤波,简单粗暴有效。
  • 如果你的噪声是那种“均匀散布”的,或者点云密度变化很大,用统计滤波更稳妥。
  • 实在拿不准?两个都试一下,对比结果再选。反正代码就几行,不费事。

好了,半径滤波的内容就讲到这里。记住它的核心:看朋友圈,朋友太少就拉黑。下节课我们继续聊点云滤波的其他方法。

本章小结:

  • 半径滤波通过检查每个点邻域内的邻居数量来判断是否为离群点
  • 半径 r 和阈值 k 需要配合调整,点云密度是关键因素
  • PCL 实现简单,核心就两个参数设置
  • 与统计滤波相比,半径滤波更适用于孤立飞点,统计滤波更适用于分布均匀的噪声
可可精品资料库