点云半径滤波算法实现

原理

半径滤波 (Radius Outlier Removal) 是一种基于局部空间密度的点云去噪算法。该算法通过分析每个点在其邻域内点的密度,识别并移除密度过低的孤立点,这些点通常被认为是噪声或异常点。

算法核心思想:
对于点云中的每个点,以该点为中心,求指定半径范围内领域点的数量。如果领域点数量少于设定的阈值,则认为该点是“孤立点”并将其从点云中移除。

算法详细流程:

  1. 邻域定义:以点 \(p_i\) 为中心,定义半径为 \(r\) 的球形邻域 \(N(p_i, r)\)
  2. 邻居统计:统计邻域内的点数 \(n_i = |\{p_j : \|p_j - p_i\| < r\}|\)
  3. 孤立点判断:如果 \(n_i < k\)\(k\) 为最小邻居数阈值),则判定 \(p_i\) 为孤立点
  4. 点云更新:移除所有被判定为孤立点的点,生成新的点云

数学表达:
对于点云 \(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\|\):欧氏距离

关键参数

  1. 搜索半径 \(r\)
    • 物理意义:定义局部邻域的空间范围
    • 设置原则:应大于点云的平均点间距,通常设置为点云密度的2-3倍。
    • 影响效果\(r\) 过小:可能将正常点误判为孤立点, \(r\) 过大:可能无法检测到真正的孤立点
  2. 最小邻域数 (k)
    • 物理意义:定义"正常点"的最小密度要求
    • 设置原则:基于期望的局部密度设置,通常设置为3-10,具体取决于应用场景。
    • 影响效果:(k) 过小:去噪效果弱,可能保留噪声点, (k) 过大:去噪效果强,可能移除正常点

算法优势

  1. 直观易懂:参数物理意义明确,易于理解和调整
  2. 效果明显:对孤立噪声点的去除效果显著
  3. 适应性好:适用于各种类型的点云数据
  4. 可控性强:通过调整半径和阈值可以精确控制过滤效果
  5. 保留特征:主要移除孤立点,保留密集区域的特征

算法局限性

  1. 密度敏感性:对点云密度变化敏感,需要根据局部密度调整参数
  2. 边界效应:在物体边界处,正常点可能因邻居不足被误判
  3. 参数依赖性:效果严重依赖半径和阈值的选择,需要针对具体数据进行参数调优
  4. 计算开销:需要近邻搜索,计算量较大,对于大规模点云可能需要优化

实现算法

基本用法

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个点,但可以根据实际需要减少最近邻的数量或者扩大搜索半径。

posted @ 2026-03-14 21:21  图图的点云库  阅读(13)  评论(0)    收藏  举报