3. 相机模型与标定原理
张正友标定法,说白了就是拿一块棋盘格,拍几张照片,然后算出相机的内参和畸变。这个方法在工业界用得特别广,因为它操作简单,精度也够用。我最早接触这个算法是在做环视系统的时候,当时被畸变搞得焦头烂额,后来才发现,张正友的方法其实已经把坑都填好了。
3.1 棋盘格角点检测
角点检测是整个标定的第一步。你想想看,如果角点都找不准,后面算出来的内参肯定也是错的。OpenCV 里提供了 cv::findChessboardCorners 这个函数,但它的内部逻辑其实挺讲究的。
大致流程是这样的:
- 对图像做自适应二值化,把棋盘格的黑白格子分开。
- 用轮廓检测找到四边形区域,筛选出可能的棋盘格内角点。
- 用亚像素精度细化角点位置。
我个人习惯在检测前先做一次直方图均衡化,尤其是光照不均匀的场景。有一次在车间里标定,光线从侧面打过来,棋盘格一半亮一半暗,直接检测根本找不到角点。后来加了均衡化,问题就解决了。
3.2 单应性矩阵
单应性矩阵是连接世界坐标系和图像坐标系的桥梁。对于棋盘格上的每个角点,我们假设它位于 Z=0 的平面上,这样世界坐标就简化为 (X, Y, 0)。
单应性矩阵 H 是一个 3x3 的矩阵,它满足:
s * [u, v, 1]^T = H * [X, Y, 1]^T
其中 s 是尺度因子。求解 H 至少需要 4 对匹配点,但实际中我们会用所有角点来做最小二乘拟合,这样更鲁棒。
我记得有一次在实车上标定,发现单应性矩阵算出来总是有偏差。后来排查发现,是因为棋盘格没有完全贴平在地面上,导致世界坐标的 Z=0 假设不成立。嗯,这里要注意,棋盘格一定要贴平,哪怕有一点点翘曲,都会影响结果。
3.3 内参矩阵求解
有了多张图像的单应性矩阵,就可以开始解内参了。张正友的方法利用了旋转矩阵的正交性,把问题转化成了线性方程组。
内参矩阵 K 的形式是:
K = [fx, 0, cx;
0, fy, cy;
0, 0, 1]
其中 fx 和 fy 是焦距,cx 和 cy 是主点坐标。求解过程大致分两步:
- 利用单应性矩阵的约束,构建关于内参的线性方程组。
- 用 SVD 分解或 Cholesky 分解求解。
你可能会问,为什么不用非线性优化直接解?其实张正友的方法先用线性解给出一个初始值,然后再用非线性优化做精调。这样既保证了速度,又保证了精度。
| 参数 | 含义 | 典型值范围 |
|---|---|---|
| fx | 水平方向焦距(像素) | 500 ~ 2000 |
| fy | 垂直方向焦距(像素) | 500 ~ 2000 |
| cx | 主点 x 坐标 | 图像宽度的一半 |
| cy | 主点 y 坐标 | 图像高度的一半 |
3.4 畸变系数优化
畸变主要分两种:径向畸变和切向畸变。径向畸变是镜头本身造成的,表现为图像边缘的弯曲;切向畸变是镜头和传感器不平行造成的。
张正友的方法通常考虑 5 个畸变系数:k1, k2, k3(径向)和 p1, p2(切向)。优化过程是这样的:
- 先用线性方法求出内参和畸变的初始值。
- 然后用 Levenberg-Marquardt 算法做非线性优化,最小化重投影误差。
重投影误差就是角点在图像上的实际位置和根据模型计算出来的位置之间的像素距离。这个值越小,说明标定越准。我一般要求重投影误差小于 0.5 个像素,如果超过 1 个像素,我会重新检查数据。
k1, k2, p1, p2 四个系数就够了。k3 通常只在广角镜头或鱼眼镜头时才需要。加太多系数反而容易过拟合。
3.5 整体流程与知识结构
下面这张图把整个张正友标定法的流程串起来了,你可以对照着看:
整个流程环环相扣,每一步的输出都是下一步的输入。我个人建议你在实际项目中,把每一步的中间结果都可视化出来,这样一旦出问题,能快速定位是哪个环节出了岔子。