4、点云降采样:体素降采样、随机降采样、均匀降采样
大家好,我是你们的老朋友。今天我们来聊聊点云处理里一个绕不开的话题——降采样。
你想想看,一个激光雷达扫一圈,动辄几十万甚至上百万个点。这么多点,直接拿去跑算法,电脑风扇能转得飞起。我刚开始做SLAM那会儿,就吃过这个亏。有一次在园区跑数据,笔记本直接卡死,重启后一看,点云文件1.2个G。嗯,从那以后,我养成了一个习惯:拿到点云,先降采样。
降采样说白了,就是用更少的点,保留尽可能多的几何结构信息。今天咱们聊三种最常用的方法:体素降采样、随机降采样、均匀降采样。
4.1 体素降采样(Voxel Downsampling)
这是我最常用的方法,没有之一。体素降采样的思路很直观:把三维空间切成一个个小立方体(体素),每个体素里只保留一个点。这个点通常是体素内所有点的重心,或者中心点。
为什么我偏爱它?因为体素降采样能很好地保持点云的几何形状。你想想看,如果点云密度不均匀,随机降采样会把稀疏的地方采得更稀疏,但体素降采样不会。每个体素一视同仁,该留几个点就留几个点。
核心参数:体素大小(voxel_size)
体素大小直接决定了降采样后的点云密度。体素越大,点越少;体素越小,点越多。我一般从0.01米开始试,根据场景调整。
在Open3D里,实现起来就几行代码:
import open3d as o3d
# 读取点云
pcd = o3d.io.read_point_cloud("scene.pcd")
# 体素降采样,体素大小0.05米
downsampled = pcd.voxel_down_sample(voxel_size=0.05)
# 可视化对比
o3d.visualization.draw_geometries([pcd], window_name="原始点云")
o3d.visualization.draw_geometries([downsampled], window_name="体素降采样后")
我在项目中遇到过一个问题:体素大小设得太小,降采样效果不明显;设得太大,细节全丢了。后来我总结了一个经验——体素大小取点云平均间距的2-3倍,效果比较稳妥。
小技巧:如果你用的是PCL库,体素降采样函数叫pcl::VoxelGrid。Open3D里叫voxel_down_sample。名字不同,原理一样。
4.2 随机降采样(Random Downsampling)
随机降采样,顾名思义,就是从原始点云里随机抽取一部分点。这个方法简单粗暴,但有个致命缺点——它不考虑点的空间分布。
举个例子,如果点云里某个区域点特别密,随机降采样后这个区域还是密;如果某个区域点特别稀疏,随机降采样后可能就没了。我早期做地面分割时用过随机降采样,结果地面点被采得七零八落,分割效果一塌糊涂。
不过,随机降采样也不是一无是处。它速度快,适合对实时性要求高的场景。比如在SLAM前端,如果点云量太大,可以先随机降采样到一定数量,再去做特征匹配。
import numpy as np
import open3d as o3d
pcd = o3d.io.read_point_cloud("scene.pcd")
points = np.asarray(pcd.points)
# 随机降采样,保留30%的点
sample_ratio = 0.3
indices = np.random.choice(len(points), size=int(len(points) * sample_ratio), replace=False)
sampled_points = points[indices]
# 构建新的点云
sampled_pcd = o3d.geometry.PointCloud()
sampled_pcd.points = o3d.utility.Vector3dVector(sampled_points)
注意:随机降采样时,replace=False表示不重复采样。如果点云量特别大,可以设replace=True,但这样会有重复点,一般不建议。
4.3 均匀降采样(Uniform Downsampling)
均匀降采样,也叫最远点采样(Farthest Point Sampling, FPS)。它的思路是:每次选一个离已选点集最远的点加入,直到达到目标点数。
这样做的好处是,采样后的点能均匀分布在三维空间里,不会出现局部过密或过疏的情况。我在做点云配准(ICP)时,经常先用均匀降采样,因为均匀分布的点能提供更稳定的匹配约束。
不过,均匀降采样有个缺点——慢。它的时间复杂度是O(n²),点云量一大,跑起来很吃力。我一般只在点云量小于10万时用均匀降采样。
import open3d as o3d
pcd = o3d.io.read_point_cloud("scene.pcd")
# 均匀降采样,采样到5000个点
uniform_sampled = pcd.uniform_down_sample(every_k_points=10)
# 注意:uniform_down_sample是每隔k个点取一个,不是最远点采样
# 真正的FPS在Open3D里需要自己实现,或者用PCL的pcl::RandomSample
补充说明:Open3D的uniform_down_sample其实是均匀间隔采样,不是严格意义上的最远点采样。如果你需要真正的FPS,建议用PCL或者自己写一个。
4.4 降采样对点云密度的影响
聊完三种方法,咱们来看看降采样到底怎么影响点云密度。我画了一张图,帮你直观理解:
从这张图你能看到:
- 体素降采样:点云密度变得均匀,每个体素内只有一个点。适合做特征提取、配准等需要均匀分布的场景。
- 随机降采样:密度分布和原始点云一样,密的地方还是密,疏的地方还是疏。适合快速预览、实时处理。
- 均匀降采样:点与点之间的间距尽量相等,密度最均匀,但计算代价高。
我个人的建议是:
- 做SLAM前端:用体素降采样,体素大小0.05-0.1米,平衡速度和精度。
- 做点云分类/分割:用体素降采样,体素大小根据物体尺寸调整。比如汽车用0.2米,行人用0.05米。
- 做可视化预览:用随机降采样,保留5%-10%的点就够了。
- 做高精度配准:用均匀降采样(FPS),采样到5000-10000个点。
避坑指南:我曾经在做一个室内重建项目时,用了随机降采样,结果墙角处的点被采没了,导致重建出来的墙角是圆的。后来换成体素降采样,问题就解决了。所以,如果你的场景里有明显的几何特征(墙角、边缘、平面),尽量用体素降采样。
好了,降采样这块就聊到这儿。三种方法各有千秋,选哪个取决于你的应用场景。记住一句话:降采样不是越少越好,而是够用就好。