4、相机内参模型:针孔相机模型、畸变模型,内参矩阵K

做视觉SLAM也好,做三维重建也罢,咱们第一步要搞清楚的,就是相机到底是怎么看到这个世界的。说白了,相机就是一个把三维空间投影到二维图像上的装置。这个投影过程,我们用一个数学模型来描述,这就是相机内参模型。

我个人习惯把内参模型拆成两部分来看:理想投影模型和畸变模型。理想模型就是针孔相机模型,畸变模型则负责修正镜头带来的各种变形。两者结合,才是我们实际使用的相机模型。

4.1 针孔相机模型:最朴素的投影方式

针孔相机模型,名字很形象。想象一个密闭的盒子,前面戳一个小孔,光线穿过小孔,在后面的感光平面上成像。这就是最原始的相机原理。

数学上怎么描述?我们设世界坐标系中有一个点 \( P(X, Y, Z) \),它在相机坐标系下的坐标是 \( (X_c, Y_c, Z_c) \)。这个点通过小孔投影到成像平面上,得到像素坐标 \( (u, v) \)。

投影公式其实很简单,就是相似三角形:

u = f_x * (X_c / Z_c) + c_x
v = f_y * (Y_c / Z_c) + c_y

这里 \( f_x, f_y \) 是焦距在像素坐标系下的表示,单位是像素。\( c_x, c_y \) 是光心在像素坐标系下的偏移,也就是主点坐标。

嗯,这里要注意:\( f_x \) 和 \( f_y \) 通常不相等。为什么?因为像素不是正方形的,传感器上的像素单元可能是矩形。我在项目中遇到过一台工业相机,它的 \( f_x \) 和 \( f_y \) 差了将近5%,如果不分别处理,重建出来的物体就是扁的。

把上面两个公式写成矩阵形式,就是大名鼎鼎的内参矩阵 K:

K = | f_x   0   c_x |
    |  0   f_y  c_y |
    |  0    0    1  |

这个矩阵,就是连接三维点和二维像素的桥梁。你想想看,有了K矩阵,我们就能把相机坐标系下的三维点,映射到图像平面上。反过来,给定一个像素点,我们也能知道它对应的一条三维射线方向。

核心要点:内参矩阵K只与相机本身有关,与相机在世界中的位置无关。所以它叫“内参”。一旦标定好,只要不换镜头、不调焦距,K就是固定的。

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

理想很丰满,现实很骨感。针孔模型假设镜头是完美的,但实际镜头有各种缺陷。最典型的就是畸变。

畸变主要分两类:径向畸变和切向畸变。

4.2.1 径向畸变:镜头曲率惹的祸

径向畸变,说白了就是光线经过透镜时,边缘的弯曲程度和中心不一样。结果就是图像边缘的直线变弯了。

径向畸变有两种:

  • 桶形畸变:图像边缘向外凸出,像木桶一样。广角镜头常见。
  • 枕形畸变:图像边缘向内凹陷。长焦镜头常见。

数学上怎么修正?我们用泰勒级数展开来近似。设归一化平面上的理想坐标为 \( (x, y) \),畸变后的坐标为 \( (x_d, y_d) \),那么:

x_d = x * (1 + k1 * r^2 + k2 * r^4 + k3 * r^6)
y_d = y * (1 + k1 * r^2 + k2 * r^4 + k3 * r^6)

其中 \( r^2 = x^2 + y^2 \),\( k1, k2, k3 \) 就是径向畸变系数。

我曾经在标定一个鱼眼镜头时,发现只用k1和k2根本不够,边缘的畸变还是很大。后来加了k3,效果才明显改善。所以对于大广角镜头,建议至少用三个径向畸变参数。

我的经验:对于普通手机镜头或监控摄像头,k1和k2通常就够了。但对于运动相机、车载环视摄像头这种大广角,一定要用k3,甚至需要更复杂的畸变模型(比如等距投影模型)。

4.2.2 切向畸变:镜头和传感器没对齐

切向畸变,也叫离心畸变。它是因为镜头的光学中心和传感器的中心没有严格对齐,或者镜头本身有倾斜。

切向畸变的修正公式:

x_d = x + (2 * p1 * x * y + p2 * (r^2 + 2 * x^2))
y_d = y + (p1 * (r^2 + 2 * y^2) + 2 * p2 * x * y)

这里 \( p1, p2 \) 就是切向畸变系数。

说实话,切向畸变在高质量的工业镜头中通常很小,甚至可以忽略。但我在做手机摄像头标定时,发现切向畸变还挺明显的。因为手机模组太小,组装精度有限,镜头和传感器很容易有微小的偏移。

避坑指南:我曾经在标定一个双目相机时,只标定了径向畸变,忽略了切向畸变。结果立体匹配时,左右图像的极线总是不对齐,找了半天原因。后来加上切向畸变标定,极线才完美对齐。所以,做双目或多目系统时,切向畸变一定要标。

4.3 完整的畸变模型

把径向畸变和切向畸变合在一起,就是完整的畸变模型:

// 输入:归一化平面上的理想坐标 (x, y)
// 输出:畸变后的坐标 (x_d, y_d)

r^2 = x^2 + y^2

// 径向畸变
x_radial = x * (1 + k1 * r^2 + k2 * r^4 + k3 * r^6)
y_radial = y * (1 + k1 * r^2 + k2 * r^4 + k3 * r^6)

// 切向畸变
x_tangential = 2 * p1 * x * y + p2 * (r^2 + 2 * x^2)
y_tangential = p1 * (r^2 + 2 * y^2) + 2 * p2 * x * y

// 最终畸变坐标
x_d = x_radial + x_tangential
y_d = y_radial + y_tangential

这个模型,就是OpenCV里用的畸变模型。你调用 cv::calibrateCamera() 时,它默认就是用的这个模型。

4.4 从畸变坐标到像素坐标

有了畸变后的归一化坐标 \( (x_d, y_d) \),再通过内参矩阵K,就能得到最终的像素坐标:

u = f_x * x_d + c_x
v = f_y * y_d + c_y

反过来,从像素坐标到三维射线,需要先做内参逆变换,再做畸变校正。这个过程通常需要迭代求解,因为畸变模型是非线性的。

一句话总结:相机内参模型 = 针孔投影 + 畸变修正。针孔投影用内参矩阵K描述,畸变修正用(k1,k2,k3,p1,p2)五个参数描述。两者缺一不可。

4.5 知识体系图

下面这张图,把本章的核心逻辑串起来了。你可以看到从三维点到二维像素的完整流程,以及每个环节对应的数学模型。

相机内参模型知识体系 三维点 P(X, Y, Z) 世界坐标系 → 相机坐标系 针孔相机模型 u = f_x * (X/Z) + c_x, v = f_y * (Y/Z) + c_y 理想归一化坐标 (x, y) 畸变模型 径向畸变: k1, k2, k3 | 切向畸变: p1, p2 x_d = x * (1 + k1*r² + k2*r⁴ + k3*r⁶) + 切向项 畸变后归一化坐标 (x_d, y_d) 内参矩阵 K u = f_x * x_d + c_x, v = f_y * y_d + c_y 像素坐标 (u, v) 关键参数 f_x, f_y: 焦距 c_x, c_y: 主点 k1, k2, k3: 径向畸变 p1, p2: 切向畸变 注意: 畸变参数通常 只有5个 (k1,k2,p1,p2,k3) 顺序不能乱

这张图把整个流程讲得很清楚:三维点先经过针孔投影得到理想坐标,再经过畸变模型得到畸变坐标,最后通过内参矩阵K映射到像素坐标。每一步都有对应的数学模型和参数。

好了,这一章的内容就到这里。内参模型是相机标定的基础,理解透了,后面的标定实操才能得心应手。