3、内参矩阵与外参矩阵:内参矩阵K的构成(fx, fy, cx, cy)、外参矩阵(旋转R与平移t)、投影方程

好,咱们接着往下聊。前面我们把相机模型和坐标系的关系理清了,现在该动真格的了——把数学公式请出来。

说白了,相机标定要解决的核心问题就一个:世界坐标系里的一个点,是怎么跑到图像像素坐标系里去的? 这个映射过程,全靠内参矩阵和外参矩阵来搭桥。

3.1 内参矩阵K:相机的“内部基因”

内参矩阵,我习惯叫它K矩阵。它描述的是相机本身的固有属性,跟相机放哪儿、朝哪拍没关系。你换个场景拍,K矩阵不变。

K矩阵长这样:

K = | fx  0  cx |
    |  0  fy  cy |
    |  0   0   1 |

这里面四个参数,每个都有物理意义:

  • fx, fy:焦距参数。单位是像素。fx = F / dx,F是物理焦距(毫米),dx是每个像素的物理宽度(毫米/像素)。说白了,fx告诉你物体在图像上横向能放多大。
  • cx, cy:主点坐标。就是光轴穿过图像平面的那个点,单位也是像素。理想情况下它在图像正中心,但实际总有偏差。

我个人的经验: 很多初学者以为cx、cy就是图像宽高的一半。我刚开始也这么想,结果标定出来的结果总差那么一点。后来才发现,镜头装配时主点偏移个几十个像素太正常了。千万别偷懒,老老实实标定。

内参矩阵的作用,是把相机坐标系下的三维点映射到图像物理坐标系,再转到像素坐标系。公式如下:

| u |   | fx  0  cx |   | Xc |
| v | = |  0  fy  cy | * | Yc |
| 1 |   |  0   0   1 |   | Zc |

这里 (Xc, Yc, Zc) 是相机坐标系下的坐标,(u, v) 是像素坐标。注意,这个公式里Zc还没归一化,实际用的时候要除以Zc。

小技巧: 如果你拿到一个标定结果,fx和fy相差很大(比如超过5%),那大概率是标定板没放平,或者图像有畸变没处理好。我曾经遇到过fx=1200、fy=800的情况,查了半天发现是标定图片里有一张模糊的混进去了。

3.2 外参矩阵:相机在“世界”中的位置

外参矩阵描述的是世界坐标系相机坐标系之间的变换关系。说白了,就是相机在世界里怎么摆的——朝哪看、站多远。

外参矩阵由两部分组成:

  • 旋转矩阵R:3x3的正交矩阵,描述相机的朝向。说白了就是相机绕三个轴转了多少。
  • 平移向量t:3x1的向量,描述相机在世界坐标系中的位置。

外参矩阵通常写成4x4的齐次形式:

[R | t] = | r11  r12  r13  t1 |
          | r21  r22  r23  t2 |
          | r31  r32  r33  t3 |
          |  0    0    0    1 |

它的作用是把世界坐标系下的点,变换到相机坐标系下:

| Xc |   | r11  r12  r13  t1 |   | Xw |
| Yc | = | r21  r22  r23  t2 | * | Yw |
| Zc |   | r31  r32  r33  t3 |   | Zw |
|  1 |   |  0    0    0    1 |   |  1 |

注意: 旋转矩阵R必须是正交矩阵,也就是说R的逆等于R的转置。如果你标定出来的R不满足这个性质(比如行列式不是1),那说明标定过程有问题。我曾经见过一个案例,标定出来的R行列式是0.97,结果重建出来的三维点全歪了。

3.3 投影方程:从世界到像素的“完整路线”

好了,现在把内参和外参串起来,就是完整的投影方程:

| u |   | fx  0  cx  0 |   | R   t |   | Xw |
| v | = |  0  fy  cy  0 | * | 0   1 | * | Yw |
| 1 |   |  0   0   1  0 |   |       |   | Zw |
                                                |  1 |

简化一下:

s * [u, v, 1]^T = K * [R | t] * [Xw, Yw, Zw, 1]^T

这里s是个尺度因子,就是前面说的Zc。为什么要有s?因为从三维到二维,深度信息丢失了,我们只能知道方向,不知道具体多远。

你想想看,这个公式其实就干了三件事:

  1. 外参变换:把世界坐标点转到相机坐标系下
  2. 内参映射:把相机坐标点投影到图像平面上
  3. 尺度归一化:除以深度,得到最终的像素坐标

避坑指南: 我曾经在项目里犯过一个低级错误——把外参矩阵的平移向量t理解反了。记住,[R | t] 是把世界坐标转到相机坐标,不是反过来。如果你要算相机在世界里的位置,得用 -R^T * t。这个坑我踩过,后来每次写代码都先画个坐标系确认一下。

3.4 一个完整的例子

假设我们有一个标定好的相机,内参如下:

fx = 800, fy = 800, cx = 640, cy = 480

外参:相机在世界坐标系原点,朝Z轴正方向看(即R为单位矩阵,t为零向量)。

现在世界坐标系下有一个点 P = (100, 200, 500) 毫米。它在图像上的像素坐标是多少?

计算过程:

1. 外参变换:因为R=I, t=0,所以相机坐标 = (100, 200, 500)
2. 内参映射:
   u' = 800 * 100 / 500 + 640 = 160 + 640 = 800
   v' = 800 * 200 / 500 + 480 = 320 + 480 = 800
3. 所以像素坐标是 (800, 800)

嗯,这个点正好落在图像中心偏右下的位置,符合直觉。

个人习惯: 我每次标定完,都会拿几个已知坐标的点手动算一遍投影,验证标定结果对不对。虽然麻烦,但能避免很多后期调试的麻烦。你想想看,如果标定错了,后面做三维重建、SLAM全白搭。

好了,内参矩阵和外参矩阵就讲到这里。下一节我们聊聊畸变参数——镜头不是完美的,总有点歪歪扭扭的地方需要校正。