3. 点云降采样
任务目标
本案例实现基于体素网格(Voxel Grid)的点云降采样算法。输入高密度点云后,通过将空间划分为固定尺寸的立方体网格,每个网格内仅保留一个代表点(重心或中心),从而在保持物体整体轮廓与关键几何特征的前提下,显著减少点云数量,降低下游处理(如配准、分割、曲面重建)的计算负载。
核心原理与实现
体素网格滤波的核心参数为体素边长 leaf_size。算法流程:
- 计算点云整体包围盒,按
leaf_size划分三维网格; - 为每个点计算其所属体素的索引
(hx, hy, hz); - 将同一体素内的点求重心(或取第一个点),作为该体素的输出点。
重心法对噪声更鲁棒,本实现采用重心法。代码不依赖任何第三方点云库,仅使用 Python 标准库与 NumPy,便于直接运行验证。
完整代码
import numpy as np
def voxel_grid_filter(points, leaf_size):
"""
体素网格降采样
:param points: (N, 3) 输入点云
:param leaf_size: 体素边长
:return: (M, 3) 降采样后点云,M <= N
"""
if points.shape[0] == 0:
return points.copy()
if leaf_size <= 0:
raise ValueError("leaf_size 必须为正数")
# 计算体素索引
voxel_indices = np.floor(points / leaf_size).astype(np.int64)
# 将三维索引编码为一维,便于分组
# 使用线性编码:idx = hx * max_yz + hy * max_z + hz
# 为避免溢出,使用字典分组(更安全)
from collections import defaultdict
groups = defaultdict(list)
for i, idx in enumerate(voxel_indices):
groups[(idx[0], idx[1], idx[2])].append(i)
# 对每个体素计算重心
filtered_points = []
for indices in groups.values():
cluster = points[indices]
centroid = np.mean(cluster, axis=0)
filtered_points.append(centroid)
return np.array(filtered_points)
if __name__ == "__main__":
# 生成测试数据:两个平面 + 随机噪声,共 5000 点
np.random.seed(42)
# 平面1:z=1 的 10x10 区域
x1 = np.random.uniform(0, 10, 2500)
y1 = np.random.uniform(0, 10, 2500)
z1 = np.ones(2500) * 1.0
# 平面2:z=5 的 10x10 区域
x2 = np.random.uniform(0, 10, 2500)
y2 = np.random.uniform(0, 10, 2500)
z2 = np.ones(2500) * 5.0
cloud = np.vstack([
np.column_stack([x1, y1, z1]),
np.column_stack([x2, y2, z2])
])
print(f"输入点云数量: {cloud.shape[0]}")
# 执行降采样
leaf = 0.5 # 体素边长 0.5m
downsampled = voxel_grid_filter(cloud, leaf)
print(f"降采样后点云数量: {downsampled.shape[0]}")
# 验证:检查两个平面的 z 值是否仍被保留
z_vals = np.unique(np.round(downsampled[:, 2], 6))
print(f"保留的 z 值层级: {z_vals}")
assert np.allclose(z_vals, [1.0, 5.0]), "关键特征丢失!"
print("验证通过:两个平面的关键高度特征均被保留。")
运行与验证
环境依赖:仅需 Python 3.6+ 与 NumPy(pip install numpy)。
运行方式:将代码保存为 voxel_filter.py,在终端执行:
python voxel_filter.py
预期输出:
输入点云数量: 5000
降采样后点云数量: 约 800~900(具体值取决于随机分布)
保留的 z 值层级: [1. 5.]
验证通过:两个平面的关键高度特征均被保留。
判定标准:
- 程序无异常退出;
- 输出点云数量显著小于输入(本例应小于 1500);
- 断言通过,说明 z=1 与 z=5 两个关键特征层在降采样后依然存在,证明滤波未破坏核心几何结构。
参数影响说明
leaf_size 决定降采样强度:值越大,点云越稀疏,细节丢失越多;值越小,保留细节越多但降采样效果有限。实际使用时需根据点云密度与后续算法需求调整,通常取点云平均点间距的 2~5 倍作为初始值。