点云半径滤波算法实现
原理
半径滤波 (Radius Outlier Removal) 是一种基于局部空间密度的点云去噪算法。该算法通过分析每个点在其邻域内点的密度,识别并移除密度过低的孤立点,这些点通常被认为是噪声或异常点。
算法核心思想:
对于点云中的每个点,以该点为中心,求指定半径范围内领域点的数量。如果领域点数量少于设定的阈值,则认为该点是“孤立点”并将其从点云中移除。
算法详细流程:
- 邻域定义:以点 \(p_i\) 为中心,定义半径为 \(r\) 的球形邻域 \(N(p_i, r)\)
- 邻居统计:统计邻域内的点数 \(n_i = |\{p_j : \|p_j - p_i\| < r\}|\)
- 孤立点判断:如果 \(n_i < k\)(\(k\) 为最小邻居数阈值),则判定 \(p_i\) 为孤立点
- 点云更新:移除所有被判定为孤立点的点,生成新的点云
数学表达:
对于点云 \(P = \{p_1, p_2, ..., p_N\}\),半径滤波定义为:
\[P_{\text{filtered}} = \{p_i \in P : |\{p_j \in P : \|p_j - p_i\| < r\}| \geq k\}
\]
其中:
- \(r\):搜索半径
- \(k\):最小邻居数阈值
- \(\|\cdot\|\):欧氏距离
关键参数:
- 搜索半径 \(r\):
- 物理意义:定义局部邻域的空间范围
- 设置原则:应大于点云的平均点间距,通常设置为点云密度的2-3倍。
- 影响效果:\(r\) 过小:可能将正常点误判为孤立点, \(r\) 过大:可能无法检测到真正的孤立点
- 最小邻域数 (k):
- 物理意义:定义"正常点"的最小密度要求
- 设置原则:基于期望的局部密度设置,通常设置为3-10,具体取决于应用场景。
- 影响效果:(k) 过小:去噪效果弱,可能保留噪声点, (k) 过大:去噪效果强,可能移除正常点
算法优势:
- 直观易懂:参数物理意义明确,易于理解和调整
- 效果明显:对孤立噪声点的去除效果显著
- 适应性好:适用于各种类型的点云数据
- 可控性强:通过调整半径和阈值可以精确控制过滤效果
- 保留特征:主要移除孤立点,保留密集区域的特征
算法局限性:
- 密度敏感性:对点云密度变化敏感,需要根据局部密度调整参数
- 边界效应:在物体边界处,正常点可能因邻居不足被误判
- 参数依赖性:效果严重依赖半径和阈值的选择,需要针对具体数据进行参数调优
- 计算开销:需要近邻搜索,计算量较大,对于大规模点云可能需要优化
实现算法
基本用法
pcl::RadiusOutlierRemoval<pcl::PointXYZ> ror;
ror.setInputCloud(cloud); // 设置输入点云
ror.setRadiusSearch(0.1); // 设置搜索半径
ror.setMinNeighborsInRadius(10); // 设置最小邻居数
ror.filter(*cloud_filtered); // 执行滤波
参数调整
// 更严格的去噪(移除更多点)
ror.setRadiusSearch(0.05); // 减小搜索半径
ror.setMinNeighborsInRadius(15); // 增加最小邻居数
// 更宽松的去噪(保留更多点)
ror.setRadiusSearch(0.2); // 增大搜索半径
ror.setMinNeighborsInRadius(5); // 减少最小邻居数
完整代码
#include <iostream>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/filters/radius_outlier_removal.h>
#include <pcl/visualization/pcl_visualizer.h>
int main() {
// 1. 加载点云
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
if (pcl::io::loadPCDFile<pcl::PointXYZ>("02.pcd", *cloud) == -1) {
std::cerr << "错误:无法加载02.pcd文件" << std::endl;
return -1;
}
std::cout << "加载点云成功,点数: " << cloud->points.size() << std::endl;
// 2. 执行半径滤波
pcl::PointCloud<pcl::PointXYZ>::Ptr filtered(new pcl::PointCloud<pcl::PointXYZ>);
pcl::RadiusOutlierRemoval<pcl::PointXYZ> ror;
ror.setInputCloud(cloud);
ror.setRadiusSearch(0.1); // 搜索半径0.1米
ror.setMinNeighborsInRadius(10); // 最小邻居数10
ror.filter(*filtered);
std::cout << "半径滤波后点数: " << filtered->points.size() << std::endl;
std::cout << "移除孤立点数量: " << cloud->size() - filtered->size() << std::endl;
std::cout << "参数: 搜索半径=0.1m, 最小邻域点数=10" << std::endl;
// 3. 可视化
pcl::visualization::PCLVisualizer viewer("filter");
// 原始点云(红色,半透明)
viewer.addPointCloud<pcl::PointXYZ>(cloud, "original");
viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1.0, 0.0, 0.0, "original");
viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "original");
viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, 0.3, "original");
// 滤波后点云(白色)
viewer.addPointCloud<pcl::PointXYZ>(filtered, "filtered");
viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1.0, 1.0, 1.0, "filtered");
viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "filtered");
// 4. 运行窗口
while (!viewer.wasStopped()) {
viewer.spinOnce(100);
}
// 5. 保存结果
pcl::io::savePCDFileASCII("filtered.pcd", *filtered);
std::cout << "结果已保存到: filtered.pcd" << std::endl;
return 0;
}
运行结果

白色点云就是过滤后得到的点云

过滤了16747个点,但可以根据实际需要减少最近邻的数量或者扩大搜索半径。
浙公网安备 33010602011771号