坐标系与变换:世界坐标系、相机坐标系、图像坐标系、像素坐标系及其转换关系

说到摄像头标定,绕不开的就是这四个坐标系。我刚开始接触这个领域时,也被这些坐标系绕得头晕。说白了,它们就是描述「一个三维空间点,最终怎么变成屏幕上那个像素」的数学工具。

你想想看,我们拍一张照片,本质上是把三维世界里的物体,映射到二维图像上。这个映射过程,就是通过一系列坐标系变换完成的。嗯,咱们一个一个来拆解。

1. 世界坐标系 → 相机坐标系

世界坐标系是我们定义的一个「绝对参考系」。你可以把它想象成整个场景的「大地图」。比如我在做自动驾驶项目时,会把车辆启动位置设为世界坐标系原点,X轴指向车头,Y轴指向左侧,Z轴朝天。

相机坐标系呢?它以相机光心为原点,Z轴指向镜头前方,X轴向右,Y轴向下(这个方向跟图像坐标系有关,后面会讲)。

从世界坐标系到相机坐标系的变换,说白了就是一个旋转加平移。用数学公式表示就是:

X_cam = R * X_world + T

其中 R 是 3x3 旋转矩阵,T 是 3x1 平移向量。这两个参数合起来,就是我们常说的「相机外参」。每个相机的位置和朝向不同,外参就不同。

重要概念:外参描述的是「相机在世界中的位置和朝向」。标定棋盘格时,我们通常把棋盘格平面设为 Z=0 的世界坐标系,这样能简化计算。

2. 相机坐标系 → 图像坐标系

这一步是「从三维到二维」的关键。相机坐标系下的点 (X_cam, Y_cam, Z_cam),通过小孔成像模型,投影到成像平面上。

我记得第一次推导这个公式时,总觉得哪里不对劲。后来画了个小孔成像的示意图才恍然大悟——其实就是相似三角形:

x = f * X_cam / Z_cam
y = f * Y_cam / Z_cam

这里 (x, y) 是图像坐标系下的坐标,单位是毫米。f 是焦距,单位也是毫米。注意,图像坐标系的原点通常在图像中心(主点),X轴向右,Y轴向下。

个人经验:实际镜头不一定是理想的小孔模型,会有畸变。我曾在项目中遇到过因为忽略畸变,导致测距误差达到 10% 的情况。所以标定时一定要做畸变校正。

3. 图像坐标系 → 像素坐标系

图像坐标系是连续的,单位是毫米。但我们的图像是由离散像素组成的。这一步就是把连续坐标变成离散的像素坐标。

转换公式很简单:

u = x / dx + u0
v = y / dy + v0

其中 dx、dy 是每个像素在 X、Y 方向上的物理尺寸(毫米/像素),(u0, v0) 是主点在像素坐标系下的坐标。这些参数就是「相机内参」的一部分。

你想想看,如果传感器是正方形的,dx = dy。但很多摄像头为了降低成本,像素不是正方形,所以 dx 和 dy 可能不同。嗯,这个细节在标定时要留意。

4. 完整变换链:从世界到像素

把上面三步串起来,就得到了完整的投影方程:

s * [u, v, 1]^T = K * [R | T] * [X_world, Y_world, Z_world, 1]^T

其中:

  • K 是内参矩阵(3x3),包含 fx, fy, u0, v0
  • [R | T] 是外参矩阵(3x4)
  • s 是尺度因子(因为齐次坐标的关系)

这个公式,说白了就是整个标定过程的核心。我们标定棋盘格,本质上就是在解这个方程里的内参和外参。

避坑指南:我曾经在标定一个广角摄像头时,发现重投影误差总是很大。后来检查发现,是因为我用的棋盘格角点检测算法对畸变图像不鲁棒。建议使用亚像素级别的角点检测,或者用圆形标定板代替棋盘格。

5. 坐标系变换的矩阵表示

为了方便计算,我们通常用齐次坐标来表示这些变换。下面这个表格总结了各个变换的矩阵形式:

变换 矩阵形式 参数
世界→相机 [R | T] (3x4) 旋转矩阵R,平移向量T
相机→图像 透视投影 (3x3) 焦距f
图像→像素 仿射变换 (3x3) dx, dy, u0, v0
世界→像素 K * [R | T] (3x4) 内参K,外参R,T

6. 实际应用中的注意事项

搞清楚了这些坐标系变换,你在做标定时就能心里有数了。我个人习惯在标定前先画个坐标系变换的流程图,把每个步骤的输入输出写清楚。这样调试时能快速定位问题。

举个例子,如果你发现标定出来的内参中,fx 和 fy 差异很大,先别急着怀疑算法。检查一下你的标定板是不是贴歪了?或者摄像头传感器本身就不是正方形的?

另外,我建议你在做目标检测时,也把坐标系变换考虑进去。比如检测到某个物体的像素坐标后,想计算它在世界坐标系下的位置,就需要用到逆投影变换。这个逆变换不是唯一的(因为深度信息丢失了),需要额外假设(比如物体在地面上)。

小技巧:在 OpenCV 中,用 cv2.projectPoints() 可以方便地把世界坐标点投影到像素坐标。反过来,用 cv2.solvePnP() 可以求解外参。这两个函数我几乎每个项目都会用到。

好了,坐标系变换这块就讲到这里。记住一句话:标定的本质,就是求解从世界坐标系到像素坐标系的映射参数。搞懂了这四个坐标系的关系,你就掌握了摄像头标定的核心。