1. 坐标系基础:世界坐标系、相机坐标系、图像坐标系、像素坐标系的定义与转换关系

做异构相机融合,第一件事就是把坐标系理清楚。我见过不少项目,算法模型选得挺好,最后融合结果一塌糊涂,查来查去——坐标系搞反了。说白了,空间坐标映射就是回答一个问题:现实世界中的一个点,到底落在像素的哪个位置?

这中间要经过四次坐标变换,四个坐标系。咱们一个一个来拆。

1.1 四个坐标系,一张图说清楚

先看整体结构。我习惯把这四个坐标系想象成一条流水线:

四个坐标系的转换流水线 世界坐标系 (Xw, Yw, Zw) 真实世界的位置 刚体变换 相机坐标系 (Xc, Yc, Zc) 以相机光心为原点 透视投影 图像坐标系 (x, y) 物理单位(mm) 离散化 像素 (u, v) 像素单位 关键理解: 1. 世界→相机:旋转 + 平移(外参),把物体从世界搬到相机眼前 2. 相机→图像:小孔成像(内参),把三维点投影到成像平面 3. 图像→像素:缩放 + 平移(内参),把物理坐标变成像素坐标 4. 整条链路就是:世界点 → 相机点 → 像点 → 像素点
我的习惯:做异构相机融合时,我总会在代码里先打印出四个坐标系的转换矩阵,看一眼数值范围。如果像素坐标出现负数或者超过图像宽高,那一定是某个环节的坐标系方向搞反了。

1.2 世界坐标系 → 相机坐标系(刚体变换)

世界坐标系是我们定义的一个参考系。你可以把原点放在任何地方——比如房间的角落、机器人底盘的几何中心。相机坐标系则以相机光心为原点,Z轴指向相机前方。

从世界坐标到相机坐标,说白了就是一次旋转加平移。数学上写成:

|Xc|   | r11  r12  r13 | |Xw|   |tx|
|Yc| = | r21  r22  r23 | |Yw| + |ty|
|Zc|   | r31  r32  r33 | |Zw|   |tz|

用齐次坐标写更简洁:

|Xc|   | R   t | |Xw|
|Yc| = |       | |Yw|
|Zc|   | 0   1 | |Zw|
|1 |            |1 |

这里的R和t,就是我们常说的相机外参。每个相机都有自己的外参,异构相机融合的核心工作之一,就是把不同相机的外参统一到同一个世界坐标系下。

我曾经踩过的坑:有一次做双目融合,左右相机的外参标定结果看起来没问题,但融合后的点云总是错位。查了两天,最后发现——我把旋转矩阵的转置和逆搞混了。记住:R是正交矩阵,R的逆等于R的转置,但前提是你得保证R确实是正交的。标定出来的R如果不是严格正交,千万别偷懒直接用转置当逆。

1.3 相机坐标系 → 图像坐标系(透视投影)

这一步是小孔成像模型。相机坐标系下的点(Xc, Yc, Zc),投影到成像平面上:

x = f * Xc / Zc
y = f * Yc / Zc

其中f是焦距。注意,这里得到的(x, y)是物理单位(毫米),原点在成像平面的中心(主点)。

写成矩阵形式:

|x|   |f  0  0  0| |Xc|
|y| = |0  f  0  0| |Yc|
|1|   |0  0  1  0| |Zc|
                     |1 |

为什么Zc不能为0? 你想想看,如果物体正好在相机光心所在的平面上,Zc=0,那投影公式就除以零了。实际中这种情况不会发生——物体不可能穿过相机光心。但做数值计算时要注意,Zc太接近0也会导致数值不稳定。

1.4 图像坐标系 → 像素坐标系(离散化)

图像坐标系的单位是毫米,像素坐标系的单位是像素。这一步就是把物理尺寸映射到像素网格上:

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

其中dx、dy是每个像素的物理尺寸(毫米/像素),(u0, v0)是主点在像素坐标系下的位置。

矩阵形式:

|u|   |1/dx  0    u0| |x|
|v| = |0     1/dy v0| |y|
|1|   |0     0    1 | |1|

1.5 完整的内参矩阵

把相机→图像和图像→像素两步合并,就得到了我们熟悉的相机内参矩阵K:

    |fx  0   u0|
K = |0   fy  v0|
    |0   0   1 |

其中fx = f/dx,fy = f/dy。注意,fx和fy不一定相等——如果像素不是正方形,这两个值就会有差异。我在做工业相机融合时就遇到过这种情况,有些相机的像素是矩形的,fx和fy能差出好几个百分点。

参数 含义 单位 典型值范围
fx x方向等效焦距 像素 500 ~ 3000
fy y方向等效焦距 像素 500 ~ 3000
u0 主点x坐标 像素 图像宽度的一半附近
v0 主点y坐标 像素 图像高度的一半附近

1.6 完整的投影方程

把外参和内参串起来,一个世界坐标点(Xw, Yw, Zw)到像素坐标(u, v)的完整映射就是:

|u|   |fx  0   u0| |r11 r12 r13 tx| |Xw|
|v| = |0   fy  v0| |r21 r22 r23 ty| |Yw|
|1|   |0   0   1 | |r31 r32 r33 tz| |Zw|
                     |0   0   0   1 | |1 |

简写为:

p = K * [R|t] * Pw

这就是整个坐标映射的核心公式。异构相机融合的所有工作,本质上都是在求解和优化这个公式里的参数。

我个人的调试习惯:拿到一个新相机,我会先拍一张棋盘格,然后用OpenCV的calibrateCamera标定出内参。标定完之后,我会手动算一个角点的重投影误差——把世界坐标通过公式投影到像素坐标,再跟实际检测到的像素坐标对比。如果误差超过0.5个像素,我会重新标定。这个习惯帮我避免了好几次因为标定板不平整导致的融合失败。

1.7 异构相机融合中的坐标系对齐

做异构相机融合时,不同相机的内参、外参都不一样。比如一个可见光相机和一个热成像相机,它们的fx、fy、u0、v0完全不同,分辨率也可能不一样。

对齐的思路其实不复杂:

  1. 统一世界坐标系:选一个参考相机,把它的坐标系作为世界坐标系
  2. 标定其他相机的外参:求出其他相机相对于参考相机的旋转和平移
  3. 统一像素分辨率:把所有相机的图像缩放到同一个分辨率下
  4. 建立像素映射表:对每个像素位置,计算它在其他相机中的对应位置

嗯,这里要注意:第4步的像素映射表,不是简单的线性关系。因为不同相机的畸变参数不同,映射关系是非线性的。我一般会预先算好一张查找表(LUT),运行时直接查表,省掉实时计算的开销。

一句话总结:世界坐标系是"真实位置",相机坐标系是"相机看到的位置",图像坐标系是"物理像平面上的位置",像素坐标系是"数字图像上的位置"。四个坐标系串起来,就是一条从三维世界到二维像素的完整链路。