3. 点云预处理

任务目标

本案例实现点云统计滤波去噪:对输入的带噪声点云,基于每个点与其k近邻的平均距离分布,剔除距离统计特征显著偏离的离群点,输出干净点云并验证滤波效果。

环境依赖

需要 Open3D(≥0.15)与 NumPy。若需可视化,安装 matplotlib。无需ROS或其他重型依赖。

核心实现

import numpy as np
import open3d as o3d

def generate_noisy_pcd(n_points=5000, noise_ratio=0.05, seed=42):
    """生成带离群点的球面点云(含随机离群点)"""
    rng = np.random.default_rng(seed)
    # 球面主体点
    phi = rng.uniform(0, np.pi, n_points)
    theta = rng.uniform(0, 2 * np.pi, n_points)
    r = 1.0 + 0.02 * rng.standard_normal(n_points)  # 轻微径向噪声
    x = r * np.sin(phi) * np.cos(theta)
    y = r * np.sin(phi) * np.sin(theta)
    z = r * np.cos(phi)
    clean = np.stack([x, y, z], axis=1)

    # 随机离群点(远离球面)
    n_outliers = int(n_points * noise_ratio)
    outliers = rng.uniform(-3, 3, size=(n_outliers, 3))

    points = np.vstack([clean, outliers])
    pcd = o3d.geometry.PointCloud()
    pcd.points = o3d.utility.Vector3dVector(points)
    return pcd, n_outliers

def statistical_filter(pcd, nb_neighbors=20, std_ratio=2.0):
    """统计滤波:移除k近邻平均距离超出 mean + std_ratio*std 的点"""
    cl, ind = pcd.remove_statistical_outlier(
        nb_neighbors=nb_neighbors, std_ratio=std_ratio)
    return cl, ind

if __name__ == "__main__":
    # 1. 生成带噪点云
    pcd, true_outliers = generate_noisy_pcd()
    print(f"输入点数: {len(pcd.points)}, 真实离群点数: {true_outliers}")

    # 2. 执行统计滤波
    filtered_pcd, inlier_indices = statistical_filter(pcd)

    # 3. 验证:统计滤波应移除大部分离群点
    n_removed = len(pcd.points) - len(filtered_pcd.points)
    print(f"滤波后点数: {len(filtered_pcd.points)}, 移除点数: {n_removed}")

    # 4. 保存结果(可选)
    # o3d.io.write_point_cloud("filtered.pcd", filtered_pcd)

    # 5. 可视化对比(取消注释以查看)
    # pcd.paint_uniform_color([0.8, 0.2, 0.2])   # 红色=原始
    # filtered_pcd.paint_uniform_color([0.2, 0.8, 0.2])  # 绿色=滤波后
    # o3d.visualization.draw_geometries([pcd, filtered_pcd])

运行与验证

运行方式:

pip install open3d numpy
python statistical_filter_demo.py

通过条件:

  • 程序无报错执行完毕;
  • 输出中“移除点数”接近真实离群点数(5000×5%=250),且明显小于总点数;
  • 若取消可视化注释,可看到红色点云中远离球面的离群点被绿色滤波点云排除。

预期输出(基于代码逻辑可唯一推出):

输入点数: 5250, 真实离群点数: 250
滤波后点数: ~5000-5100, 移除点数: ~150-250

实际数值因随机种子固定(seed=42)而确定,但Open3D内部实现可能随版本微调,因此以“移除点数接近250且滤波后点云主体完整”为判定标准。

关键说明

  • remove_statistical_outlier 对每个点计算其到k近邻的平均距离,全局均值为μ、标准差为σ,距离超过 μ+std_ratio×σ 的点视为离群点。
  • 参数 nb_neighbors=20 控制邻域大小,std_ratio=2.0 为常用阈值;点云越密集,邻域数可适当增大。
  • 本案例使用合成球面点云以便定量验证,实际使用时替换为 o3d.io.read_point_cloud("your.pcd") 读取真实点云即可。