4. 点云预处理(一)

点云拿到手,很少能直接用的。这是我做了这么多年视觉项目最深的体会。传感器扫出来的原始数据,要么范围太大,要么点太多,要么全是飞点噪声。说白了,你得先「收拾」一下,才能让后续的测量或识别算法正常工作。

这一章,咱们就聊三种最常用的预处理手段:直通滤波(裁剪范围)、体素滤波(降采样)、统计滤波(去噪点)。它们就像三把筛子,一把比一把细。

核心思路:原始点云 → 直通滤波(切出ROI) → 体素滤波(减少点数) → 统计滤波(剔除离群点) → 干净点云。这个流程我几乎每个项目都会用,顺序也基本固定。

点云预处理三阶段流程图 点云预处理三阶段流程 原始点云 直通滤波 体素滤波 统计滤波 各阶段作用 • 直通滤波:沿坐标轴裁剪,快速去除无关区域 • 体素滤波:用体素网格降采样,控制点云密度 • 统计滤波:基于邻域统计,剔除离群噪声点

4.1 直通滤波:先切一刀

直通滤波,名字听着玄乎,其实就是沿着某个坐标轴(X、Y 或 Z)设定一个范围,把范围之外的点全扔掉。比如你只关心工作台上一个 200mm×150mm 的区域,那就把 X 和 Y 方向一裁,剩下的点云就干净多了。

我个人习惯在 Halcon 里用 reduce_domain 配合 threshold 来做,但更直接的方式是用 select_points_object_model_3d 这个算子。它支持按坐标范围筛选,非常灵活。

* 读取点云
read_object_model_3d('pointcloud.ply', 'm', [], [], ObjectModel3D)

* 直通滤波:保留 X 在 [-50, 50] mm,Y 在 [-30, 30] mm 内的点
select_points_object_model_3d(ObjectModel3D, 'point_coord_x', -50, 50, ObjectModel3D_XY)
select_points_object_model_3d(ObjectModel3D_XY, 'point_coord_y', -30, 30, ObjectModel3D_XY)

* 也可以一步到位,用 'box' 方式裁剪
* 创建一个包围盒:中心 (0,0,0),尺寸 (100, 60, 200)
select_points_object_model_3d(ObjectModel3D, 'box', [0, 0, 0, 100, 60, 200, 0, 0, 0], ObjectModel3D_Box)

我的经验:直通滤波的「box」方式,最后一个参数是旋转角度(弧度)。如果工件有倾斜,记得调整这个角度,否则会切歪。我曾经在一个 PCB 检测项目里忘了设角度,结果切出来的点云全是斜的,排查了半天才发现。

4.2 体素滤波:给点云「减肥」

高分辨率 3D 相机扫出来的点云,动不动就几百万个点。你想想看,这么多点全拿去处理,算法跑起来得有多慢。体素滤波就是干这个的——它把空间划分成一个个小立方体(体素),每个体素内只保留一个代表点(通常是重心或中心)。

说白了,就是用精度换速度。体素边长越大,点越少,但细节丢失也越多。这个参数需要根据你的测量精度来调。

体素边长 (mm) 输出点数(约) 适用场景
0.5 80% 原始点数 高精度测量,细节保留
1.0 30% 原始点数 通用场景,平衡速度与精度
2.0 10% 原始点数 快速预览,粗定位
* 体素滤波:设置体素边长为 1.0 mm
* 使用 'voxel_size' 参数控制降采样程度
sample_object_model_3d(ObjectModel3D_XY, 'fast', 1.0, ObjectModel3D_Voxel)

* 另一种方式:指定目标点数(不常用,但有时很方便)
* sample_object_model_3d(ObjectModel3D_XY, 'random', 50000, ObjectModel3D_Random)

注意:体素滤波的 'fast' 模式用的是体素中心,'random' 模式是随机采样。我个人推荐用 'fast',因为它能保证点云分布均匀,不会出现局部过密或过疏的情况。但如果你需要保留边缘细节,可以试试 'random' 配合较大的目标点数。

4.3 统计滤波:把「飞点」揪出来

这一步是我觉得最解气的。3D 传感器经常会在边缘或反光区域产生一些「飞点」——就是那些明显偏离真实表面的孤立点。统计滤波的原理很简单:对每个点,计算它到 k 个最近邻的平均距离。如果这个距离超过全局均值的一定标准差,就判定为离群点,直接剔除。

嗯,这里有两个关键参数:k(邻域点数)和 std_dev(标准差倍数)。k 越大,判断越平滑;std_dev 越小,剔除越严格。

* 统计滤波:设置邻域点数 k=10,标准差倍数 std_dev=1.5
* 注意:Halcon 中这个算子叫 'outlier_filter',参数是 'k' 和 'sigma_scale'
filter_object_model_3d(ObjectModel3D_Voxel, 'outlier_filter', 10, 1.5, ObjectModel3D_Filtered)

* 查看滤波前后的点数差异
get_object_model_3d_params(ObjectModel3D_Voxel, 'num_points', NumPointsBefore)
get_object_model_3d_params(ObjectModel3D_Filtered, 'num_points', NumPointsAfter)
disp_message(WindowHandle, '滤波前:' + NumPointsBefore + ' 点,滤波后:' + NumPointsAfter + ' 点', 'window', 12, 12, 'black', 'true')

避坑指南:我曾经在一个汽车钣金件检测项目里,把 std_dev 设成了 0.8,结果把工件边缘的正常点也剔掉了,导致测量结果偏小。后来我改成 1.5,效果就好了。所以我的建议是:先设一个宽松的值(比如 2.0),看看效果,再逐步收紧。别一上来就下狠手。

4.4 三种滤波的对比与选择

这三种滤波各有各的用处,我整理了一张表,方便你对照着选。

滤波类型 核心作用 关键参数 典型应用
直通滤波 裁剪空间范围 坐标轴、上下限 去除背景、聚焦ROI
体素滤波 降采样、控制密度 体素边长 加速后续处理
统计滤波 剔除离群噪声 k、std_dev 去除飞点、边缘毛刺

实际项目中,我通常按「直通 → 体素 → 统计」的顺序串起来用。但也不是的——如果你的点云本身就很干净,可以跳过统计滤波;如果点数不多,体素滤波也可以省掉。灵活一点,别死板。

一个小技巧:在 Halcon 里,你可以用 visualize_object_model_3d 实时查看每一步滤波后的效果。我习惯把原始点云和滤波后的点云用不同颜色叠加显示,这样一眼就能看出哪些点被去掉了,非常直观。