2、相机几何基础:针孔相机模型、内参矩阵、外参矩阵、畸变模型

各位同学,咱们今天聊聊相机几何。这是三维视觉的基石,说白了就是搞明白「相机是怎么看到世界的」。

我刚开始做SLAM那会儿,总觉得相机几何就是一堆矩阵乘法,没啥意思。直到有一次在室外跑数据集,发现地图点全飘了,折腾了两天才发现是畸变参数没标对……嗯,从那以后我再也不敢小看这部分了。

2.1 针孔相机模型:最朴素的成像原理

先说说针孔相机模型。你想想看,一个暗箱,前面戳个小孔,光线穿过小孔在后面的胶片上成像——这就是最原始的相机。

数学上怎么描述?很简单,就是相似三角形。假设世界中有个点 P = [X, Y, Z]ᵀ,它通过小孔投影到成像平面上,得到像素坐标 [u, v]ᵀ。关系是这样的:

u = f * X / Z + c_x
v = f * Y / Z + c_y

这里的 f 是焦距,c_x、c_y 是光心偏移。说白了,就是把三维点「压」到二维平面上。

核心理解:针孔模型本质是一个透视投影,近大远小。Z 值越小,投影后物体越大;Z 值越大,物体越小。这也是单目相机无法直接恢复绝对尺度的根本原因。

2.2 内参矩阵:相机的「身份证」

内参矩阵,我习惯叫它 K 矩阵。它描述了相机内部的几何属性,包括焦距、光心位置,还有像素的歪斜程度。

数学形式长这样:

K = [f_x,  s,  c_x;
     0,   f_y, c_y;
     0,    0,   1 ]

各参数含义:

  • f_x, f_y:x 和 y 方向的焦距(单位是像素)。通常 f_x ≈ f_y,但也不绝对。
  • c_x, c_y:光心在像素坐标系中的位置,一般在图像中心附近。
  • s:歪斜参数,现代相机几乎为 0,但老式相机或工业相机可能会有。

我在项目中遇到过一件事:用手机拍了一组照片做三维重建,结果重建出来的模型总是扭曲的。查了半天,发现是内参矩阵里的 c_x、c_y 没更新——手机裁剪了图像,但参数还是原来的。嗯,这里要注意:图像裁剪或缩放后,内参必须重新计算

个人经验:标定内参时,我建议至少拍 20 张不同角度的棋盘格照片。太少的话,标定结果不稳定,尤其是边缘区域的畸变参数容易跑偏。

2.3 外参矩阵:相机在哪儿?在看哪儿?

外参矩阵描述的是相机在世界坐标系中的位置和朝向。它由旋转矩阵 R 和平移向量 t 组成。

数学形式:

[R | t]  是一个 3×4 的矩阵

它的作用是把世界坐标系下的点,变换到相机坐标系下。说白了就是:世界点 → 相机点

举个例子:

P_cam = R * P_world + t

这里 R 是 3×3 的旋转矩阵,t 是 3×1 的平移向量。R 和 t 合起来有 6 个自由度(3 个旋转角 + 3 个平移量),所以也叫 6DoF 位姿。

我曾经犯过一个低级错误:把 R 和 t 的顺序搞反了。在代码里写成了 P_cam = t + R * P_world,结果重建出来的点云全跑到相机后面去了……调试了一下午才发现。所以我的建议是:写代码时统一用齐次坐标,避免顺序混淆

避坑指南:外参矩阵的坐标系定义一定要统一。有的库用「相机→世界」,有的用「世界→相机」。搞混了,整个系统都会崩。我习惯用「世界→相机」的约定,OpenCV 和大部分 SLAM 系统也是这么用的。

2.4 畸变模型:镜头不是完美的

现实中的镜头不是完美的针孔。光线经过透镜时会发生弯曲,导致图像变形。这就是畸变。

畸变主要分两类:

  • 径向畸变:光线在透镜边缘弯曲更厉害,导致图像边缘的直线变弯。分为桶形畸变和枕形畸变。
  • 切向畸变:透镜和成像平面不平行,导致图像倾斜。

数学上,畸变模型通常用多项式来描述:

x_distorted = x * (1 + k1*r² + k2*r⁴ + k3*r⁶) + 2*p1*x*y + p2*(r² + 2*x²)
y_distorted = y * (1 + k1*r² + k2*r⁴ + k3*r⁶) + p1*(r² + 2*y²) + 2*p2*x*y

其中:

  • k1, k2, k3 是径向畸变参数
  • p1, p2 是切向畸变参数
  • r² = x² + y² 是点到光心的距离

为什么会这样?因为透镜的曲率不是完美的球面,尤其是广角镜头,边缘畸变特别明显。我做过一个项目,用鱼眼镜头做室内重建,如果不做畸变校正,墙壁的直线在图像里全是弯的,重建出来的平面也是曲面。

关键点:畸变校正必须在投影之前做。流程是:原始图像 → 畸变校正 → 投影到像素坐标。顺序错了,结果全错。

2.5 知识体系总览

下面我用一张图来总结本章的核心逻辑。你看,从世界坐标到像素坐标,中间经历了三次变换:

世界坐标系 P_world = [X, Y, Z] 外参矩阵 [R|t] 相机坐标系 P_cam = [Xc, Yc, Zc] 畸变模型 像素坐标系 p = [u, v] 内参矩阵 K 外参变换 投影 + 内参 完整流程:世界点 → 外参 → 相机点 → 畸变校正 → 内参投影 → 像素点 畸变发生在相机坐标系到像素坐标系之间

你看,整个流程其实就三步:

  1. 外参变换:用 [R|t] 把世界点转到相机坐标系
  2. 畸变校正:对相机坐标下的点做畸变补偿
  3. 内参投影:用 K 矩阵把校正后的点投影到像素平面

这三步缺一不可。我见过不少初学者,拿着相机直接拍,不做畸变校正就开始做特征匹配,结果匹配出来的点全是歪的。嗯,这里要记住:畸变校正是预处理的第一步

实用建议:在实际项目中,我习惯用 OpenCV 的 cv::calibrateCamera() 做标定,它会同时输出内参和畸变参数。标定完成后,用 cv::undistort() 对图像做校正。这样后续的特征提取、匹配都会稳定很多。

好了,相机几何的基础就讲到这里。这些内容虽然看起来简单,但它们是整个三维视觉的根基。后面的章节里,我们会反复用到这些概念——特征匹配、对极几何、三角化、PnP……每一个都离不开今天讲的这些东西。


专注资料整理