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 知识体系图
下面这张图,把本章的核心逻辑串起来了。你可以看到从三维点到二维像素的完整流程,以及每个环节对应的数学模型。
这张图把整个流程讲得很清楚:三维点先经过针孔投影得到理想坐标,再经过畸变模型得到畸变坐标,最后通过内参矩阵K映射到像素坐标。每一步都有对应的数学模型和参数。
好了,这一章的内容就到这里。内参模型是相机标定的基础,理解透了,后面的标定实操才能得心应手。