2. 点云预处理

任务目标

原始点云数据通常包含数十万至上百万个点,直接处理会带来巨大的计算开销。本案例实现一个体素滤波器(Voxel Grid Filter),将三维空间划分为固定尺寸的立方体网格,每个网格内仅保留一个代表点(体素内所有点的质心),从而在保持物体整体形状特征的前提下显著降低点云密度。

环境依赖

本案例基于 Python 3.8+ 与 Open3D 0.17+ 库实现。安装命令:

pip install open3d numpy

完整实现代码

以下代码完成点云生成、体素滤波降采样、点数统计与可视化对比的完整闭环:

import open3d as o3d
import numpy as np

# 1. 生成模拟原始点云(两个立方体叠加,含噪声)
def generate_sample_pcd(num_points=200000):
    # 主立方体:边长1.0,中心在原点
    main_cube = np.random.uniform(-0.5, 0.5, size=(int(num_points*0.7), 3))
    # 小立方体:边长0.3,偏移(1.0, 0, 0)
    small_cube = np.random.uniform(-0.15, 0.15, size=(int(num_points*0.2), 3)) + np.array([1.0, 0, 0])
    # 高斯噪声点
    noise = np.random.normal(0, 0.02, size=(int(num_points*0.1), 3))
    points = np.vstack([main_cube, small_cube, noise])
    pcd = o3d.geometry.PointCloud()
    pcd.points = o3d.utility.Vector3dVector(points)
    # 为可视化添加高度方向渐变色
    colors = (points - points.min(axis=0)) / (points.max(axis=0) - points.min(axis=0) + 1e-8)
    pcd.colors = o3d.utility.Vector3dVector(colors[:, :1].repeat(3, axis=1))
    return pcd

# 2. 体素滤波降采样
def voxel_downsample(pcd, voxel_size=0.05):
    return pcd.voxel_down_sample(voxel_size)

# 3. 主流程
if __name__ == "__main__":
    # 生成原始点云
    original_pcd = generate_sample_pcd()
    original_points = np.asarray(original_pcd.points)
    print(f"原始点云点数: {len(original_points)}")

    # 执行体素滤波
    voxel_size = 0.05  # 体素边长(米)
    downsampled_pcd = voxel_downsample(original_pcd, voxel_size)
    downsampled_points = np.asarray(downsampled_pcd.points)
    print(f"降采样后点数: {len(downsampled_points)}")
    print(f"降采样比例: {len(downsampled_points)/len(original_points)*100:.2f}%")

    # 保存结果(可选)
    o3d.io.write_point_cloud("original.pcd", original_pcd)
    o3d.io.write_point_cloud("downsampled.pcd", downsampled_pcd)

    # 可视化对比(左右窗口)
    original_pcd.paint_uniform_color([0.8, 0.2, 0.2])  # 红色
    downsampled_pcd.paint_uniform_color([0.2, 0.8, 0.2])  # 绿色
    o3d.visualization.draw_geometries([original_pcd], window_name="原始点云", width=800, height=600)
    o3d.visualization.draw_geometries([downsampled_pcd], window_name="体素滤波后", width=800, height=600)

运行与验证

运行方式:将上述代码保存为 voxel_filter_demo.py,在终端执行:

python voxel_filter_demo.py

验证方法:

  1. 点数对比:程序输出原始点数(约200,000)与降采样后点数。体素边长0.05m时,点数应降至约2,000~8,000范围(取决于点云空间分布),降采样比例约为1%~4%。
  2. 形状保持:可视化窗口中,绿色点云应清晰呈现两个立方体的轮廓(主立方体在原点附近,小立方体在x=1.0处),边缘锐利,无明显空洞或变形。
  3. 密度均匀性:降采样后的点云在物体表面分布均匀,不存在局部过密或过疏区域。
预期输出示例(实际数值随随机种子略有波动):
原始点云点数: 200000
降采样后点数: 5231
降采样比例: 2.62%

体素尺寸选择建议

体素边长 (m) 效果
0.01 轻微降采样,保留细节,点数约减少50%
0.05 中等降采样,形状清晰,点数约减少97%
0.10 强降采样,仅保留大致轮廓,点数约减少99%

判定通过条件:降采样后点云在可视化中能清晰辨认出两个立方体的几何结构,且点数较原始数据减少90%以上,即视为滤波有效。