第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把点云转到相机坐标系下。

还有一个常见问题:点云中有空洞怎么办?比如物体表面反光导致某些区域没有点。我的做法是:

  1. 先用xyz_to_depth_map生成带空洞的深度图
  2. 再用fill_interlace或median_image填充空洞
  3. 如果空洞太大,考虑用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画的一张深度图生成与可视化流程图,帮你理清整个技术脉络:

深度图生成与可视化流程 深度图生成与可视化流程 点云输入 坐标系转换(相机坐标系) xyz_to_depth_map 投影 深度图(含空洞) 深度图(无空洞) 空洞填充(fill_interlace) 伪彩色映射 3D曲面显示 提示:空洞填充和伪彩色映射是深度图预处理的关键步骤,直接影响后续测量精度

可视化时还有几个细节要注意:

  • 深度范围:先统计深度图的直方图,找到有效深度范围,再做映射。不要用全局最大最小值,否则会被离群点带偏。
  • 颜色条:在图像旁边显示颜色条,标注深度值对应的颜色,方便观察者理解。
  • 交互式显示:用disp_object_model_3d可以旋转查看深度图的3D效果,比平面图直观得多。

总结一下本章核心:

  • 深度图是点云的二维投影,两者通过相机内参相互转换
  • 深度图常用uint16或float32存储,PNG和TIFF是主流格式
  • xyz_to_depth_map是Halcon生成深度图的核心算子,注意坐标系和插值方式
  • 伪彩色映射和3D曲面显示是深度图可视化的两种主要方式