2. 摄像头成像原理:针孔相机模型、透镜畸变与内外参矩阵
好,咱们正式开始聊摄像头成像。说实话,这是整个多摄像头标定里最基础、也最绕不开的一块。你如果不理解相机是怎么“看”世界的,后面那些标定算法、外参优化,基本就是空中楼阁。
我个人习惯,讲成像原理一定从针孔模型开始。为什么?因为它简单、直观,而且所有复杂模型都是在这个基础上打补丁。
2.1 针孔相机模型:最朴素的“小孔成像”
你想想看,最早的相机其实就是个暗箱。前面戳个小孔,光线穿过小孔,在后面的感光面上形成一个倒立的像。这就是针孔模型的核心思想。
数学上怎么描述?很简单。假设三维世界里有一个点 P,坐标为 (X, Y, Z)。它通过小孔投影到成像平面上,得到像素点 p,坐标为 (x, y)。它们的关系是:
x = f * (X / Z)
y = f * (Y / Z)
这里的 f 就是焦距——小孔到成像平面的距离。注意,这个模型假设光线是直线传播的,没有畸变,没有模糊。理想情况。
关键点:针孔模型是线性模型。它把三维世界到二维图像的映射,简化成了一个透视投影。你后面学到的所有标定算法,本质上都是在求解这个映射关系里的未知参数。
我在项目中遇到过一件事。有一次用鱼眼相机做车道线检测,直接套针孔模型去算,结果边缘的车道线全歪了。嗯,这就是忽略了畸变。所以记住:针孔模型是基础,但现实世界不完美。
2.2 透镜畸变:为什么现实世界不完美?
实际摄像头为了进光量更大、成像更亮,会用透镜代替小孔。但透镜一加上,问题就来了——光线穿过透镜会发生折射,导致图像变形。这就是畸变。
畸变主要分两种:径向畸变和切向畸变。
2.2.1 径向畸变
说白了,就是光线在透镜边缘弯曲得比中心厉害。你拍一张棋盘格,如果边缘的格子看起来像桶形或者枕形,那就是径向畸变。
数学上,我们用多项式来建模:
x_corrected = x * (1 + k1 * r^2 + k2 * r^4 + k3 * r^6)
y_corrected = y * (1 + k1 * r^2 + k2 * r^4 + k3 * r^6)
这里的 r 是像素点到图像中心的距离。k1、k2、k3 就是径向畸变系数。一般用 k1 和 k2 就够了,鱼眼镜头才需要 k3。
我的经验:标定时如果只给两个径向参数(k1、k2),大部分普通镜头都能搞定。但如果你用广角或鱼眼,一定要加上 k3。我曾经偷懒只用了 k1、k2,结果标定出来的图像边缘还是有明显弯曲,后来加上 k3 才解决。
2.2.2 切向畸变
这个畸变是因为透镜和成像平面不平行造成的。说白了,就是镜头装歪了。它会导致图像看起来像被“拉扯”了一下。
公式长这样:
x_corrected = x + [2 * p1 * x * y + p2 * (r^2 + 2 * x^2)]
y_corrected = y + [p1 * (r^2 + 2 * y^2) + 2 * p2 * x * y]
p1、p2 就是切向畸变系数。一般标定时会一起优化。
注意:切向畸变通常比径向畸变小得多。如果你用的镜头质量不错,切向畸变甚至可以忽略。但做多摄像头标定时,我建议还是把它加上,因为哪怕一点点误差,在多个相机拼接时都会被放大。
2.3 内参矩阵:相机自己的“身份证”
好,现在我们把针孔模型和畸变模型结合起来。但还有一个问题:成像平面上的物理坐标 (x, y) 怎么变成我们看到的像素坐标 (u, v)?
这就引出了内参矩阵。它描述了相机内部的几何属性,包括焦距、主点位置(光轴与成像平面的交点)。
内参矩阵 K 长这样:
K = [fx, 0, cx]
[ 0, fy, cy]
[ 0, 0, 1]
- fx, fy:分别是 x 和 y 方向的焦距(以像素为单位)。通常 fx 和 fy 很接近,但不完全相等。
- cx, cy:主点坐标,也就是光轴与成像平面的交点。理想情况下在图像中心,但实际会有偏移。
有了内参矩阵,我们就可以把三维点投影到像素坐标了:
u = fx * (X / Z) + cx
v = fy * (Y / Z) + cy
一句话总结:内参矩阵告诉你,相机是怎么把物理世界“翻译”成像素的。每个相机都有自己的内参,而且一旦焦距或分辨率变了,内参就得重新标定。
2.4 外参矩阵:相机在空间中的“位置和姿态”
内参管的是“相机内部怎么成像”,那外参管的就是“相机在世界坐标系里怎么摆放”。
外参矩阵由两部分组成:旋转矩阵 R 和 平移向量 t。
[R | t] 是一个 3x4 的矩阵
它的作用是把世界坐标系下的点,转换到相机坐标系下。公式很简单:
X_cam = R * X_world + t
其中:
- R:3x3 旋转矩阵,描述相机的朝向。
- t:3x1 平移向量,描述相机的位置。
你想想看,多摄像头标定要干什么?就是要找到每个相机的外参,知道它们之间相对位置和朝向。这样你才能把多个相机的图像拼到一起,或者做立体视觉。
避坑指南:我曾经在标定双目相机时,发现左右相机的外参总是对不上。查了半天,原来是标定板放得太近,导致平移向量的尺度模糊。后来我把标定板放到不同距离,多采几组数据,才稳定下来。所以记住:外参标定时,标定板的距离要多样化。
2.5 完整成像流程:从世界坐标到像素坐标
把上面所有东西串起来,一个三维点怎么变成像素点?流程如下:
- 世界坐标 → 相机坐标:用外参矩阵 [R | t] 做旋转和平移。
- 相机坐标 → 归一化平面:除以 Z,得到 (x, y)。
- 加畸变:用径向和切向畸变公式修正 (x, y)。
- 归一化平面 → 像素坐标:用内参矩阵 K 映射到 (u, v)。
写成矩阵形式就是:
s * [u, v, 1]^T = K * [R | t] * [X, Y, Z, 1]^T
这里的 s 是尺度因子,因为从三维到二维丢失了深度信息。
核心理解:整个成像过程,就是内参和外参的联合作用。内参是“相机自己的脾气”,外参是“相机在哪儿、朝哪儿看”。标定的本质,就是同时估计这两套参数。
2.6 小结与实战建议
嗯,这一章内容不少。我帮你理一下重点:
- 针孔模型是基础,但实际要用畸变模型修正。
- 径向畸变(k1, k2, k3)和切向畸变(p1, p2)是标定时必须考虑的。
- 内参矩阵是相机的固有属性,标定一次后只要焦距不变就可以复用。
- 外参矩阵描述相机位置,多摄像头标定的核心就是求外参。
我个人建议,刚开始做标定时,先用 OpenCV 的 calibrateCamera() 跑一遍,看看内参和畸变系数。然后手动验证一下:用标定结果把棋盘格角点重投影回去,看看误差有多大。如果重投影误差超过 0.5 像素,说明标定质量不够好,需要重新采集数据。
下一章,我们会聊标定板的设计和角点提取。到时候你会看到,这些理论怎么落地到实际代码里。