4、点云数据生成(二):从深度图生成点云,理解相机内参与点云坐标映射关系
上一章我们聊了怎么用代码生成模拟点云。这一章,咱们来点真格的——从深度图生成点云。
深度图是什么?说白了,就是一张灰度图。每个像素的值,代表相机到物体的距离。你拿个Kinect或者RealSense拍一下,得到的就是这玩意儿。但光有深度图还不够,你得知道每个像素对应到三维空间哪个点。这就引出了今天的主角——相机内参。
核心一句话:深度图 + 相机内参 = 点云。缺一不可。
4.1 相机内参到底是什么?
我刚开始接触这个的时候,也被一堆参数搞晕过。其实没那么复杂。相机内参就是描述相机内部光学特性的矩阵。它告诉我们:三维空间里的一个点,是怎么投影到二维图像上的。
标准的内参矩阵长这样:
K = [fx, 0, cx]
[ 0, fy, cy]
[ 0, 0, 1]
这里:
- fx, fy:焦距,单位是像素。说白了就是镜头到成像平面的距离,用像素个数来衡量。
- cx, cy:主点坐标。一般是图像中心,但不一定完全在正中间。我遇到过一台相机,主点偏了十几个像素,一开始没注意,生成的点云整个歪了。
你可能会问:为什么焦距要用像素表示?嗯,这是为了方便计算。实际物理焦距除以像素尺寸,就得到fx和fy。你不需要自己算,相机标定的时候这些值就出来了。
4.2 从像素到三维点——坐标映射公式
好,现在我们有深度图了,也知道内参了。怎么把像素坐标(u, v)和深度值d,变成三维点(x, y, z)?
公式其实就三行:
z = d
x = (u - cx) * z / fx
y = (v - cy) * z / fy
等等,这里有个坑。深度图里的d,单位是什么?毫米?米?还是别的?
注意:不同深度传感器的深度单位可能不同。Kinect一般是毫米,有些ToF相机是米。做映射前一定要确认单位,否则点云会缩放到奇怪的比例。
我个人习惯在代码里先统一成米。这样后续处理方便,可视化也直观。
4.3 代码实现——从深度图到点云
光说不练假把式。咱们直接上代码。这个函数我写过不下十次了,每次项目不同,但核心逻辑都一样。
import numpy as np
def depth_to_pointcloud(depth_image, K, scale=1000.0):
"""
从深度图生成点云
参数:
depth_image: (H, W) 深度图,单位毫米
K: 相机内参矩阵 [fx, 0, cx; 0, fy, cy; 0, 0, 1]
scale: 深度缩放因子,默认1000(毫米转米)
返回:
points: (N, 3) 点云数组
"""
fx, fy = K[0, 0], K[1, 1]
cx, cy = K[0, 2], K[1, 2]
h, w = depth_image.shape
# 生成像素坐标网格
u, v = np.meshgrid(np.arange(w), np.arange(h))
# 深度值转米
z = depth_image.astype(np.float32) / scale
# 计算三维坐标
x = (u - cx) * z / fx
y = (v - cy) * z / fy
# 堆叠成点云
points = np.stack([x, y, z], axis=-1)
# 去掉深度为0的无效点
valid = z > 0
return points[valid]
这段代码看着简单,但我曾经在这上面栽过跟头。你猜怎么着?我忘了处理深度值为0的像素。那些点投影到无穷远,点云里出现了一堆乱七八糟的噪点。嗯,从那以后,我每次都会加一个有效点过滤。
4.4 核心逻辑流程图
为了让你更直观地理解整个过程,我画了一张图:
4.5 避坑指南——我踩过的那些坑
做深度图转点云,看起来简单,但坑不少。我一个个说:
- 坑一:深度单位不统一。我曾经从两个不同传感器拿数据,一个用毫米,一个用厘米。合并点云的时候,形状完全对不上。排查了半天才发现是单位问题。
- 坑二:主点偏移没考虑。有些相机的主点不在图像正中心。如果你直接用(w/2, h/2)代替cx, cy,点云会整体偏移。我建议每次都用标定得到的真实值。
- 坑三:深度图有空洞。有些像素没有深度值(比如黑色物体吸光了)。这些点直接扔掉会丢失信息。我一般用插值或者邻域填充来处理。
我的小技巧:生成点云后,先可视化看看形状对不对。如果物体看起来被拉伸了,八成是fx/fy比例不对。如果整体偏移,检查cx/cy。如果尺度不对,检查深度单位。
4.6 进阶——反向映射:从点云到深度图
你可能会问:反过来行不行?当然可以。把三维点投影回图像平面,就得到了深度图。这在点云配准、重投影误差计算中很常用。
公式就是正向映射的逆过程:
u = fx * x / z + cx
v = fy * y / z + cy
d = z
不过要注意,反向映射得到的u, v不一定是整数。你需要做插值。我一般用最近邻插值,速度快,效果也还行。
4.7 总结一下
从深度图生成点云,说白了就是三件事:拿到内参、套用公式、过滤无效点。但细节决定成败。单位、主点、空洞处理,每一个都可能让你的点云面目全非。
我个人建议,刚开始做的时候,先用一个已知形状的物体(比如一个平面或者球体)做测试。生成点云后看看形状对不对。这样能快速发现问题。
好了,这一章就到这里。代码和公式都在上面了,拿去用吧。
公众号:蓝海资料掘金营,微信deep3321