第4章 深度图基础:从点云到深度图的完整映射
深度图这东西,说白了就是一张二维图像,每个像素的值代表物体到相机的距离。我刚开始接触3D视觉时,总觉得点云才是王道,深度图不过是降维打击。直到有一次做在线检测项目,点云数据量太大导致处理速度跟不上产线节拍,换成深度图后效率直接翻了三倍。嗯,从那以后我再也不敢小看深度图了。
4.1 深度图与点云的映射关系
点云是三维空间中的离散点集合,每个点有(X,Y,Z)坐标。深度图则是二维矩阵,每个像素存储一个深度值。它们之间是什么关系?其实就是一个投影过程。
核心映射公式:
给定相机内参(fx, fy, cx, cy),点云中的点(X,Y,Z)映射到深度图像素坐标(u,v)为:
u = fx * X / Z + cx
v = fy * Y / Z + cy
深度值 = Z
反过来,深度图像素(u,v)反投影到点云:
Z = depth(u,v)
X = (u - cx) * Z / fx
Y = (v - cy) * Z / fy
我在做结构光三维重建时遇到过一个问题:深度图的分辨率决定了点云的密度。你想想看,一张640×480的深度图,最多只能生成30万个点。而激光扫描仪动辄上千万个点。所以深度图适合做快速测量,但不适合做精细建模。
| 特性 | 点云 | 深度图 |
|---|---|---|
| 数据结构 | 无序点集 | 有序矩阵 |
| 存储效率 | 低(每个点3个float) | 高(每个像素1个值) |
| 邻域关系 | 需建立KD-Tree | 天然存在(4邻域/8邻域) |
| 处理速度 | 慢 | 快(可直接用图像算法) |
4.2 深度图的数据类型与存储
深度图不是普通的灰度图。普通灰度图每个像素是0-255的整数,而深度图需要存储真实的物理距离。我个人习惯用以下几种数据类型:
- uint16(16位无符号整数):最常用,范围0-65535,单位通常为毫米或0.1毫米。Kinect、RealSense等消费级传感器都用这个格式。
- float32(32位浮点数):精度高,适合工业相机。但文件体积大,是uint16的两倍。
- uint8(8位无符号整数):仅用于可视化,精度太低,不能用于测量。
我的经验:在Halcon中,深度图通常用real类型(即float32)存储。但如果你用read_image读取uint16的深度图,记得用convert_image_type转成real,否则后续测量会出错。我曾经在这个坑里爬了整整一个下午。
存储格式方面,常见的深度图文件格式有:
| 格式 | 说明 | 适用场景 |
|---|---|---|
| PNG | 无损压缩,支持16位 | 存储uint16深度图 |
| TIFF | 支持多通道、浮点数 | 存储float32深度图 |
| PLY | 点云格式,可包含深度图 | 同时保存点云和深度图 |
| HALCON格式 | .hobj、.tif | Halcon原生格式 |
4.3 从点云生成深度图:xyz_to_depth_map
Halcon提供了xyz_to_depth_map算子,专门用于将点云投影成深度图。这个算子的核心参数是相机内参和投影方式。
* 读取点云
read_object_model_3d(PointCloud, 'ply', [], [], &)
* 设置相机内参
CameraParam := [0.012, 0, 0.000006, 0.000006, 640, 480, 320, 240]
* 生成深度图
xyz_to_depth_map(PointCloud, CameraParam, 'bilinear', DepthMap, Width, Height)
参数说明:
- CameraParam:相机内参,格式为[focus, kappa, pixel_width, pixel_height, width, height, cx, cy]
- 插值方式:'nearest_neighbor'(最近邻,速度快但锯齿明显)、'bilinear'(双线性,平滑但略模糊)
- Width/Height:输出深度图的分辨率
避坑指南:我曾经在生成深度图时,发现结果全是黑色。排查了半天,原来是点云的坐标系和相机坐标系不一致。点云的Z轴朝上,而深度图需要Z轴朝向相机。记得先做rigid_trans_object_model_3d把点云转到相机坐标系下。
还有一个常见问题:点云中有空洞怎么办?比如物体表面反光导致某些区域没有点。我的做法是:
- 先用
xyz_to_depth_map生成带空洞的深度图 - 再用
fill_interlace或median_image填充空洞 - 如果空洞太大,考虑用
interpolate_image做插值
4.4 深度图的可视化
深度图直接看是灰蒙蒙的一片,根本看不出深度变化。我们需要做可视化映射,把深度值映射到彩色或伪彩色图像上。
Halcon中常用的可视化方法:
* 方法1:线性映射到0-255
scale_image(DepthMap, DepthScaled, 255 / max_depth, 0)
* 方法2:伪彩色映射(彩虹色)
rainbow(DepthScaled, ColorDepth)
* 方法3:3D曲面显示
disp_object_model_3d(WindowHandle, DepthMap, [], [])
我个人最喜欢用伪彩色映射。为什么?因为人眼对颜色的敏感度远高于灰度。红色代表近,蓝色代表远,中间渐变过渡,一眼就能看出物体的立体结构。
可视化技巧:在做深度图可视化时,记得做截断映射。比如你的测量范围是200-800mm,就把200mm映射到红色,800mm映射到蓝色,超出范围的设为黑色或白色。这样能突出有效区域,避免背景噪声干扰。
下面是我用SVG画的一张深度图生成与可视化流程图,帮你理清整个技术脉络:
可视化时还有几个细节要注意:
- 深度范围:先统计深度图的直方图,找到有效深度范围,再做映射。不要用全局最大最小值,否则会被离群点带偏。
- 颜色条:在图像旁边显示颜色条,标注深度值对应的颜色,方便观察者理解。
- 交互式显示:用
disp_object_model_3d可以旋转查看深度图的3D效果,比平面图直观得多。
总结一下本章核心:
- 深度图是点云的二维投影,两者通过相机内参相互转换
- 深度图常用uint16或float32存储,PNG和TIFF是主流格式
- xyz_to_depth_map是Halcon生成深度图的核心算子,注意坐标系和插值方式
- 伪彩色映射和3D曲面显示是深度图可视化的两种主要方式