4. OpenCV相机标定:cv2.findChessboardCorners、cv2.calibrateCamera详解
相机标定,说白了就是给相机做一次「视力矫正」。
我刚开始接触这个领域时,总觉得标定是个黑盒子——拍几张棋盘格照片,跑几个函数,内参外参就出来了。但后来在实际项目中吃过亏,才明白每个参数背后的门道。
今天咱们就掰开揉碎,把OpenCV里最核心的两个标定函数讲透。
4.1 标定的本质:你在求解什么?
先问一个问题:相机为什么需要标定?
因为镜头有畸变,传感器有安装误差。说白了,真实世界的一条直线,拍出来可能是弯的。我们需要找到一组参数,把弯的拉直。
标定求解的核心是两套参数:
- 内参矩阵:焦距、光心位置——这是相机本身的「出厂设置」
- 畸变系数:径向畸变、切向畸变——这是镜头带来的「变形因子」
嗯,这里要注意:外参(旋转矩阵、平移向量)每次拍摄都会变,但内参和畸变系数是固定的。所以标定一次,可以反复用。
4.2 cv2.findChessboardCorners:找到棋盘格的角点
这个函数是标定的第一步。它的任务很简单:在图像里找到棋盘格的内角点。
函数签名:
ret, corners = cv2.findChessboardCorners(image, patternSize, flags=None)
参数说明:
| 参数 | 含义 | 我的经验 |
|---|---|---|
| image | 输入图像(灰度图效果更好) | 我习惯先转成灰度再传入 |
| patternSize | 内角点数量,如(9,6)表示9列6行 | 注意是内角点,不是棋盘格数量 |
| flags | 可选标志,如自适应阈值 | 光线不好时我会用CALIB_CB_ADAPTIVE_THRESH |
返回值:
ret:布尔值,True表示找到了角点corners:角点坐标,形状为(N,1,2)
我的小技巧:
如果棋盘格反光严重,可以试试先做直方图均衡化。我在一个工厂项目中遇到过强光照射,棋盘格一半亮一半暗,加个cv2.equalizeHist()就解决了。
4.3 亚像素级角点优化
找到角点后,我建议再做一步亚像素优化。为什么?因为整数像素坐标不够精确。
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
corners2 = cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), criteria)
这段代码把角点精度从像素级提升到亚像素级。说白了,就是让角点坐标带小数,更接近真实位置。
我曾经做过对比实验:用亚像素优化后,标定重投影误差从0.5像素降到了0.15像素。效果很明显。
4.4 cv2.calibrateCamera:核心标定函数
这个函数才是真正的「重头戏」。它根据多张棋盘格图像,计算出相机内参和畸变系数。
函数签名:
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
objectPoints, imagePoints, imageSize,
cameraMatrix=None, distCoeffs=None,
flags=None, criteria=None
)
参数详解:
objectPoints:世界坐标系中的3D点。注意,这里每个棋盘格角点的Z坐标都是0imagePoints:对应的图像2D点,就是findChessboardCorners找到的那些imageSize:图像尺寸,格式为(width, height)cameraMatrix:内参矩阵初始值,一般传None让OpenCV自己算distCoeffs:畸变系数初始值,同样传Noneflags:标定选项,后面细说criteria:迭代终止条件
返回值:
| 返回值 | 含义 | 形状 |
|---|---|---|
| ret | 重投影误差(越小越好) | 标量 |
| mtx | 内参矩阵 | 3x3 |
| dist | 畸变系数 | (5,1)或更多 |
| rvecs | 每张图的旋转向量 | 列表,每个元素是(3,1) |
| tvecs | 每张图的平移向量 | 列表,每个元素是(3,1) |
4.5 标定流程:从拍照到出参
我整理了一个标准流程,你照着做就行:
- 准备棋盘格:打印一张棋盘格,贴在硬纸板上。格子数量建议10x7或9x6
- 拍摄多张照片:从不同角度拍15-20张。注意:棋盘格要占画面1/4以上
- 提取角点:对每张图调用findChessboardCorners
- 亚像素优化:用cornerSubPix提高精度
- 执行标定:调用calibrateCamera
- 评估结果:看重投影误差,一般小于0.3像素就算合格
避坑指南:
我曾经犯过一个低级错误:拍摄时棋盘格离镜头太近,导致角点提取失败。后来我总结了一个经验:棋盘格在画面中的大小,至少要有200x200像素。
另外,照片数量不是越多越好。我试过拍50张,结果因为角度重复,反而降低了标定精度。15-20张不同角度的照片,效果最好。
4.6 标定选项 flags 详解
这个参数容易被忽略,但它能显著影响标定结果。
flags = cv2.CALIB_RATIONAL_MODEL # 使用更精确的畸变模型
# 或者组合使用
flags = (cv2.CALIB_FIX_K1 | cv2.CALIB_FIX_K2 | cv2.CALIB_FIX_K3)
常用选项:
CALIB_RATIONAL_MODEL:使用6个径向畸变系数(k1-k6),精度更高CALIB_FIX_K1:固定某个畸变系数不变CALIB_ZERO_TANGENT_DIST:假设切向畸变为0CALIB_USE_INTRINSIC_GUESS:使用你提供的内参初始值
我个人习惯:对于普通镜头,用默认参数就够了。但对于广角镜头,一定要加上CALIB_RATIONAL_MODEL,否则边缘畸变校正不干净。
4.7 完整代码示例
import cv2
import numpy as np
import glob
# 棋盘格内角点数量
pattern_size = (9, 6)
# 准备世界坐标系中的3D点
objp = np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2)
# 存储所有图像的点
objpoints = [] # 3D点
imgpoints = [] # 2D点
images = glob.glob('calib_images/*.jpg')
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 找角点
ret, corners = cv2.findChessboardCorners(gray, pattern_size, None)
if ret:
objpoints.append(objp)
# 亚像素优化
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
corners2 = cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), criteria)
imgpoints.append(corners2)
# 执行标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
objpoints, imgpoints, gray.shape[::-1], None, None
)
print(f"重投影误差: {ret:.4f}")
print(f"内参矩阵:\n{mtx}")
print(f"畸变系数:\n{dist}")
4.8 知识体系总览
下面这张图,帮你理清整个标定流程的逻辑关系:
4.9 标定结果验证
标定完成后,一定要验证结果。我常用的方法是:
# 用标定结果去畸变
img = cv2.imread('test.jpg')
h, w = img.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)
# 裁剪图像
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]
cv2.imshow('校正结果', dst)
cv2.waitKey(0)
如果校正后的图像边缘直线变直了,说明标定成功。如果还有弯曲,说明畸变系数没算准,需要重新标定。
经验之谈:
重投影误差小于0.3像素,基本就够用了。但如果你做的是高精度测量,比如工业检测,我建议控制在0.1像素以内。
另外,标定完成后记得保存参数:
np.savez('calib_params.npz', mtx=mtx, dist=dist, rvecs=rvecs, tvecs=tvecs)
好了,关于cv2.findChessboardCorners和cv2.calibrateCamera的核心内容,就讲到这里。这两个函数是相机标定的基石,掌握了它们,你就能自己动手给任何相机做「视力矫正」了。