4、数学基础(三):相机几何——针孔相机模型、内参矩阵、外参矩阵、畸变模型
好,咱们继续往下走。前面聊了坐标系和刚体变换,那些是三维世界的“骨架”。今天要聊的,是连接三维世界和二维图像的“桥梁”——相机几何。
说白了,相机几何就是回答一个问题:三维空间里的一个点,是怎么跑到照片上那个像素位置的? 这个问题看似简单,但背后藏着针孔模型、内外参矩阵、畸变模型这一整套逻辑。我当年刚入行时,觉得这东西太理论,结果第一次做标定就吃了大亏——拍出来的棋盘格图像,角点提取总是歪的。嗯,从那以后,我再也不敢小看相机几何了。
核心一句话:相机几何就是描述“世界坐标 → 相机坐标 → 图像坐标 → 像素坐标”的完整映射链条。
4.1 针孔相机模型——最朴素的成像原理
先说说针孔相机模型。你想想看,一个暗箱,前面戳个小孔,光线穿过小孔在后面的胶片上成像。这就是最原始的相机。
数学上怎么描述?很简单,相似三角形。假设相机坐标系的原点在光心,Z轴指向物体方向。那么空间点 (X, Y, Z) 在成像平面上的投影 (x, y) 满足:
x = f * X / Z
y = f * Y / Z
这里的 f 是焦距(光心到成像平面的距离)。注意,这个模型假设成像平面在光心前方(虚拟成像平面),这样图像就是正立的,方便计算。
我的经验:实际相机不是针孔,而是透镜组。但针孔模型是基础,所有标定算法都基于它。我在做双目立体匹配时,第一步就是把图像校正到针孔模型下,否则视差计算全是错的。
4.2 内参矩阵——从物理世界到像素
针孔模型给出的是物理坐标(毫米),但照片是像素。怎么转换?这就需要内参矩阵 K。
内参矩阵 K 是一个 3x3 的上三角矩阵:
K = [ fx 0 cx
0 fy cy
0 0 1 ]
其中:
- fx, fy:分别是 x 和 y 方向的焦距(像素单位)。通常 fx = f / dx,fy = f / dy,dx、dy 是每个像素的物理尺寸。
- cx, cy:主点坐标(像素单位),即光轴与成像平面的交点。理想情况下在图像中心,但实际有偏移。
所以,从相机坐标系到像素坐标系的完整映射是:
[ u ] [ fx 0 cx ] [ X/Z ]
[ v ] = [ 0 fy cy ] [ Y/Z ]
[ 1 ] [ 0 0 1 ] [ 1 ]
说白了,内参矩阵就是描述“相机内部”的几何属性。每个相机都有自己的内参,而且会随着焦距、光圈变化而变化。
避坑指南:我曾经在标定一个广角相机时,发现 fx 和 fy 差异很大。一开始以为是标定板不平,后来才发现是传感器像素不是正方形。所以,不要假设 fx = fy,除非你用的是高端工业相机。
4.3 外参矩阵——相机放在哪里?
内参管的是“相机内部”,外参管的是“相机在哪儿、朝哪儿看”。
外参矩阵 [R|t] 是一个 3x4 的矩阵,由旋转矩阵 R(3x3)和平移向量 t(3x1)组成。它把世界坐标系下的点变换到相机坐标系下:
P_cam = R * P_world + t
或者写成齐次形式:
[ X_cam ] [ R t ] [ X_world ]
[ Y_cam ] = [ ] [ Y_world ]
[ Z_cam ] [ 0 1 ] [ Z_world ]
[ 1 ] [ 1 ]
外参矩阵有 6 个自由度:3 个旋转角度(绕 X、Y、Z 轴)和 3 个平移量。旋转矩阵 R 是正交矩阵,满足 R^T R = I,det(R) = 1。
我的习惯:在实际项目中,我通常用四元数表示旋转,而不是欧拉角。为什么?因为欧拉角有万向锁问题,而四元数插值更平滑。做 SLAM 或运动恢复结构(SfM)时,这个细节能省很多调试时间。
4.4 畸变模型——镜头不是完美的
现实中的镜头不是完美的针孔。透镜的曲率、装配误差都会导致图像变形。这就是畸变。
畸变主要分两类:
- 径向畸变:光线通过透镜边缘时弯曲更厉害,导致图像边缘向内凹(桶形畸变)或向外凸(枕形畸变)。数学模型用泰勒展开:
x_distorted = x * (1 + k1*r^2 + k2*r^4 + k3*r^6)
y_distorted = y * (1 + k1*r^2 + k2*r^4 + k3*r^6)
其中 r^2 = x^2 + y^2,k1、k2、k3 是径向畸变系数。
- 切向畸变:透镜和成像平面不平行,导致图像倾斜。数学模型:
x_distorted = x + [2*p1*x*y + p2*(r^2 + 2*x^2)]
y_distorted = y + [p1*(r^2 + 2*y^2) + 2*p2*x*y]
其中 p1、p2 是切向畸变系数。
完整的畸变模型通常用 5 个参数表示:(k1, k2, p1, p2, k3)。
注意:畸变校正必须在投影到像素坐标之前完成。也就是说,先校正畸变,再用内参矩阵投影。顺序搞反了,图像会变得很奇怪。我曾经在调试一个 AR 项目时,把畸变校正放在投影之后,结果虚拟物体怎么都对不齐真实场景——折腾了两天才发现是顺序问题。
4.5 完整映射流程
把上面所有内容串起来,从世界坐标到像素坐标的完整流程是:
| 步骤 | 描述 | 公式 |
|---|---|---|
| 1 | 世界坐标 → 相机坐标 | P_cam = R * P_world + t |
| 2 | 相机坐标 → 归一化平面 | x = X_cam / Z_cam, y = Y_cam / Z_cam |
| 3 | 畸变校正 | x' = f_distort(x, y), y' = f_distort(x, y) |
| 4 | 归一化平面 → 像素坐标 | u = fx * x' + cx, v = fy * y' + cy |
核心公式(齐次形式):
s * [u, v, 1]^T = K * [R|t] * [X, Y, Z, 1]^T
其中 s 是尺度因子(等于 Z_cam)。这个公式是三维重建的基石,所有标定、三角化、PnP 算法都基于它。
4.6 实际项目中的注意事项
- 标定板要足够大:覆盖整个视场,否则边缘畸变参数不准。我习惯用 10x7 的棋盘格,每个格子 30mm。
- 多角度拍摄:至少 15-20 张不同角度的图像,包括倾斜、旋转、靠近、远离。
- 检查重投影误差:标定完成后,计算重投影误差(通常小于 0.5 像素算合格)。如果误差大,检查标定板角点是否提取准确。
- 固定焦距:标定过程中不要变焦,否则内参会变。变焦镜头需要重新标定。
我的经验:如果你用的是手机摄像头,注意它可能有自动对焦和光学防抖。这些功能会改变内参。我建议用固定焦距的工业相机做三维重建,或者用 OpenCV 的 calibrateCamera() 时传入 CALIB_FIX_PRINCIPAL_POINT 等标志来约束参数。
好了,相机几何的核心内容就这些。内参、外参、畸变,这三者构成了从三维到二维的完整映射。理解了这个映射,你就能看懂标定、三角化、PnP 这些后续算法的原理了。