4、相机模型与标定:针孔相机模型、畸变模型(径向、切向)、张正友标定法、相机内参与外参
说到相机模型,我脑子里第一个蹦出来的就是针孔模型。说白了,这就是最朴素的成像原理——光线穿过一个小孔,在后面的感光平面上倒立成像。你想想看,我们平时用的手机、单反,本质上都在模拟这个过程。
但现实世界哪有那么完美。镜头不是针孔,是透镜组。透镜会带来各种畸变,就像哈哈镜一样。我当年第一次做视觉SLAM时,就被畸变坑过——明明特征点匹配上了,三角化出来的深度却乱七八糟。后来才发现,是没做畸变校正。
4.1 针孔相机模型
针孔模型的核心,就是四个坐标系之间的转换:世界坐标系 → 相机坐标系 → 图像坐标系 → 像素坐标系。嗯,这里要注意,很多人把图像坐标系和像素坐标系搞混。
数学上,一个三维点 \(P(X, Y, Z)\) 投影到像素坐标 \((u, v)\) 的过程可以写成:
Z * [u, v, 1]^T = K * [R | t] * [X, Y, Z, 1]^T
其中 \(K\) 是内参矩阵,\([R | t]\) 是外参矩阵。内参决定了相机本身的属性,外参决定了相机在空间中的位置和朝向。
内参矩阵 K 的标准形式:
K = [fx, 0, cx;
0, fy, cy;
0, 0, 1]
fx, fy 是焦距(以像素为单位),cx, cy 是主点偏移。我习惯把 cx, cy 理解为光轴与成像平面的交点。
4.2 畸变模型
镜头畸变主要分两类:径向畸变和切向畸变。径向畸变是光线通过透镜边缘时弯曲造成的,说白了就是「桶形」或「枕形」效果。切向畸变则是透镜与成像平面不平行导致的。
我曾经在标定一个广角镜头时,径向畸变参数 k1 达到了 -0.35,图像边缘都快卷到中间了。校正后,整个画面舒展开来,那种感觉真的很爽。
4.2.1 径向畸变
数学模型很简单,用泰勒级数展开:
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 是径向畸变系数。一般用 k1, k2 就够了,k3 是给鱼眼镜头准备的。
4.2.2 切向畸变
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 是切向畸变系数。实际项目中,切向畸变通常很小,但也不能忽略。我见过一个案例,因为切向畸变没校正,导致三维重建的平面出现了微小的弯曲。
我的经验:标定时至少拍 15-20 张不同角度的棋盘格照片。角度要覆盖各个方向,尤其是边缘。别偷懒,拍少了内参不稳定。
4.3 张正友标定法
张正友标定法,可以说是计算机视觉领域最经典的标定方法之一。它的核心思想是:用一张平面棋盘格,在不同角度下拍摄多张图像,然后通过单应性矩阵来求解内参和外参。
我记得第一次实现这个算法时,被单应性矩阵的分解卡了很久。后来才明白,张正友的巧妙之处在于利用了「平面」这个约束——棋盘格上的点都在同一个平面上,所以可以用单应性矩阵来描述从世界坐标到像素坐标的映射。
算法流程大致如下:
- 检测角点:在每张图像中提取棋盘格的角点坐标。
- 计算单应性矩阵:对每张图像,求解从棋盘格平面到图像平面的单应性矩阵 H。
- 求解内参:利用单应性矩阵的约束条件,建立线性方程组,求解内参矩阵 K。
- 求解外参:根据内参和单应性矩阵,计算每张图像的旋转矩阵 R 和平移向量 t。
- 非线性优化:用 Levenberg-Marquardt 算法对所有参数进行全局优化,最小化重投影误差。
避坑指南:我曾经在标定时发现重投影误差很大,检查后发现是棋盘格打印精度不够。建议用高精度打印机,或者直接买陶瓷棋盘格。另外,拍摄时保证光照均匀,避免反光。
4.4 相机内参与外参
内参和外参,说白了就是「你是谁」和「你在哪」的问题。
| 参数类型 | 包含内容 | 物理意义 |
|---|---|---|
| 内参 | fx, fy, cx, cy, k1, k2, p1, p2, k3 | 焦距、主点、畸变系数 |
| 外参 | R (3x3), t (3x1) | 旋转矩阵、平移向量 |
内参是相机出厂后就固定的(除非你拆镜头)。外参则随着相机移动而变化。在SLAM中,我们每帧都在估计外参,而内参通常只标定一次。
你想想看,如果内参不准,外参估计就会跟着偏。就像你戴了一副度数不对的眼镜,看东西都是歪的。所以标定这一步,值得花时间做扎实。
核心公式总结:
// 世界坐标到像素坐标的完整流程
1. 世界坐标 P_w → 相机坐标 P_c = R * P_w + t
2. 相机坐标 P_c → 归一化坐标 p = (x/z, y/z)
3. 畸变校正 p_d = distort(p, k1, k2, p1, p2, k3)
4. 归一化坐标 → 像素坐标 u = fx * x_d + cx, v = fy * y_d + cy
4.5 知识体系总览
下面这张图,是我梳理的本章知识结构。你可以把它当作一个思维导图来看:
这张图把四个核心模块串起来了。从针孔模型出发,理解成像几何;然后处理畸变,让图像变「正常」;再用张正友法标定出内参外参。每一步都环环相扣。
实用建议:如果你用 OpenCV 做标定,记得用 cv::calibrateCamera() 函数。输入棋盘格角点坐标和图像点坐标,输出内参、畸变系数和外参。我一般还会用 cv::initUndistortRectifyMap() 生成校正映射,然后一次性应用到所有图像上,效率高很多。
好了,相机模型和标定就聊到这儿。这些东西看着理论性强,但实际动手做一遍标定,你就能体会到其中的门道。尤其是当你看到校正后的图像变得横平竖直,那种成就感,嗯,值得。
可可精品资料库