4、单摄像头标定:棋盘格标定法、张正友标定法、OpenCV标定流程、畸变校正实战
单摄像头标定,说白了就是给相机做「体检」。你得知道它的内参(焦距、主点)、畸变系数,才能把拍到的扭曲画面还原成真实世界的样子。我刚开始做多摄像头融合时,觉得标定就是跑个脚本的事。结果呢?两台相机各自标定误差差了 0.3 个像素,融合出来的点云全是重影。嗯,从那以后我再也不敢小看这一步了。
4.1 棋盘格标定法:为什么选棋盘格?
你想想看,标定需要知道「世界坐标系中的 3D 点」和「图像上的 2D 点」之间的对应关系。棋盘格的好处是:角点检测算法成熟、精度高,而且黑白相间的图案对光照变化不敏感。
我个人习惯用 9×6 内角点的棋盘格(也就是 10×7 个格子)。为什么?因为 OpenCV 的 findChessboardCorners 默认支持这个尺寸,省得改参数。我在项目中遇到过用 7×5 的棋盘格,结果标定出来的焦距偏差 2%,后来换成 9×6 就稳了。
- 内角点数量:至少 8×6,推荐 9×6 或 11×8
- 格子大小:20-30mm 最常用,太大或太小都会影响角点检测精度
- 打印后贴在平整硬板上,不要用软纸——我见过有人贴在海报纸上,标定结果飘忽不定
4.2 张正友标定法:核心思想
张正友在 1998 年提出的这个方法,解决了「用 2D 平面标定板标定 3D 相机」的问题。核心就三步:
- 计算单应性矩阵:每张棋盘格图像都能算出一个 H 矩阵,它连接了世界坐标和图像坐标
- 求解内参初值:利用多张图像的 H 矩阵,构建线性方程组,解出内参矩阵 K
- 非线性优化:用 Levenberg-Marquardt 算法最小化重投影误差,同时优化畸变系数
我曾经用 15 张图像标定,重投影误差 0.12 像素;换成 5 张图像,误差直接跳到 0.4 像素。所以我的经验是:至少拍 10-15 张不同角度、不同距离的棋盘格图像。
- 标定板是平面(Z=0)
- 相机模型为针孔模型(带径向和切向畸变)
- 图像数量足够覆盖视场各个区域
4.3 OpenCV 标定流程:从图像到参数
OpenCV 把张正友法封装得非常好用。我一般按这个流程走:
| 步骤 | 函数/操作 | 说明 |
|---|---|---|
| 1. 准备棋盘格 | 打印并拍摄 10-15 张图像 | 覆盖视场中心、边缘、倾斜、旋转 |
| 2. 角点检测 | cv::findChessboardCorners |
返回亚像素精度的角点坐标 |
| 3. 角点细化 | cv::cornerSubPix |
提升到亚像素级别,我一般设 winSize=11 |
| 4. 标定计算 | cv::calibrateCamera |
输出内参、畸变系数、旋转/平移向量 |
| 5. 评估误差 | 计算重投影误差 | 小于 0.3 像素算合格,超过 0.5 像素要重拍 |
calibrateCamera,结果误差 0.8 像素。排查了半天,发现是 cornerSubPix 的迭代次数设成了 1。改成 30 次后,误差降到 0.15 像素。所以——亚像素细化这一步千万别省。
4.4 畸变校正实战:代码与效果
标定完成后,畸变校正就很简单了。OpenCV 提供了两个函数:
cv::undistort:一步到位,适合单张图像cv::initUndistortRectifyMap+cv::remap:适合视频流,提前算好映射表
我个人习惯用第二种方案。为什么?因为视频流里每帧都调用 undistort 太慢了,而 remap 是查表操作,速度快一个数量级。
// C++ 示例:畸变校正(视频流方案)
cv::Mat cameraMatrix, distCoeffs;
// 从标定结果加载内参和畸变系数
cv::Mat map1, map2;
cv::Size imageSize(1920, 1080);
cv::initUndistortRectifyMap(
cameraMatrix, distCoeffs, cv::Mat(),
cameraMatrix, imageSize,
CV_32FC1, map1, map2
);
// 每帧调用 remap
cv::Mat frame, undistortedFrame;
cv::remap(frame, undistortedFrame, map1, map2, cv::INTER_LINEAR);
校正后的效果:原本弯曲的直线变直了,边缘的色差也减轻了。我在一个项目中用这个方案处理 4 路 1080p 视频流,每路耗时不到 3ms,完全满足实时要求。
- 标定后保存内参和畸变系数到 YAML 文件,方便后续加载
- 如果校正后图像边缘出现黑色区域,可以调整
newCameraMatrix的缩放因子(alpha 参数) - alpha=0 时裁剪所有黑色区域,alpha=1 时保留全部像素
4.5 知识体系:单摄像头标定全景图
下面这张图把整个标定流程串起来了。从棋盘格准备到畸变校正,每一步都有对应的 OpenCV 函数和注意事项。
嗯,到这里单摄像头标定的核心内容就讲完了。记住:标定精度直接影响后续多摄像头融合的效果。我见过太多人跳过这一步直接做立体匹配,结果重投影误差 2 个像素,点云全是噪声。所以,老老实实把棋盘格拍好、把参数标准,后面的事才能顺起来。