4. 标定工具实战:使用OpenCV进行相机标定、获取畸变系数与相机矩阵
好了,前面我们把理论讲了个七七八八。现在该动手了。
这一章,我们就用 OpenCV 把相机标定跑一遍。我会带着你从准备棋盘格图片开始,一直到拿到畸变系数和相机矩阵。整个过程,说白了就是让相机自己报出它的“内参”和“毛病”。
4.1 准备工作:棋盘格与图片采集
标定需要一张已知尺寸的棋盘格图案。你打印出来,贴在硬纸板上就行。
棋盘格的关键参数:
- 内角点数量:比如 (9, 6),指的是内部角点的行列数,不是格子数。
- 方格尺寸:比如 25mm。这个值决定了标定的尺度。
我的习惯: 棋盘格不要太小,A4 纸打印的 (9,6) 棋盘,方格 25-30mm 最顺手。太小了,远距离拍不清楚;太大了,近距离又装不下。
采集图片时,记住几个要点:
- 拍 15-20 张不同角度、不同距离的图片。
- 棋盘格要出现在画面的各个角落——边缘、中心、倾斜都要有。
- 光照均匀,不要有反光或阴影遮挡角点。
我曾经踩过的坑: 有次我偷懒只拍了 8 张,结果标定出来的畸变系数偏差很大,校正后图像边缘反而更扭曲了。后来老老实实补到 20 张,效果才稳定。所以,数量别省。
4.2 核心流程:从角点检测到参数输出
整个标定流程,我用一张图给你理清楚:
4.3 代码实战:一步步来
下面这段代码,是我在实际项目中反复用过的模板。你直接拿去改改路径就能跑。
import cv2
import numpy as np
import glob
# 1. 设置棋盘格参数
chessboard_size = (9, 6) # 内角点数量
square_size = 25.0 # 方格尺寸 (mm)
# 2. 准备世界坐标系中的点
objp = np.zeros((chessboard_size[0] * chessboard_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:chessboard_size[0], 0:chessboard_size[1]].T.reshape(-1, 2)
objp *= square_size
# 3. 存储所有图片的角点数据
objpoints = [] # 世界坐标系点
imgpoints = [] # 图像坐标系点
# 4. 读取图片
images = glob.glob('calib_images/*.jpg')
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 5. 查找棋盘格角点
ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None)
if ret:
objpoints.append(objp)
# 6. 亚像素精化
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)
# 7. 可视化(可选)
cv2.drawChessboardCorners(img, chessboard_size, corners2, ret)
cv2.imshow('Corners', img)
cv2.waitKey(100)
cv2.destroyAllWindows()
# 8. 执行标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
objpoints, imgpoints, gray.shape[::-1], None, None
)
print("相机矩阵 (Camera Matrix):\n", mtx)
print("\n畸变系数 (Distortion Coefficients):\n", dist)
print("\n重投影误差 (Reprojection Error):", ret)
4.4 输出参数解读
跑完上面的代码,你会得到几个关键输出。我解释一下它们分别代表什么:
| 参数 | 含义 | 典型值示例 |
|---|---|---|
| 相机矩阵 (mtx) | 3x3 矩阵,包含焦距 (fx, fy) 和主点 (cx, cy) | [[fx, 0, cx], [0, fy, cy], [0, 0, 1]] |
| 畸变系数 (dist) | 1x5 向量,顺序为 k1, k2, p1, p2, k3 | [[-0.32, 0.12, -0.01, 0.00, -0.05]] |
| 重投影误差 (ret) | 标定质量的量化指标,越小越好 | 0.15 (像素) |
怎么看畸变系数?
- k1, k2, k3:径向畸变系数。k1 是主要项,k2 和 k3 用于大畸变镜头(比如鱼眼)。
- p1, p2:切向畸变系数。通常很小,如果很大说明镜头装配有问题。
- 重投影误差:一般小于 0.5 像素就算合格。我习惯控制在 0.2 以下。
4.5 避坑指南:我踩过的几个雷
标定这件事,看着简单,实操起来坑不少。我分享几个亲身经历:
- 棋盘格不平整:有一次我把棋盘格贴在弯曲的纸箱上,标定出来的畸变系数全是错的。后来换了硬纸板,问题解决。
- 图片数量不够:前面说过,少于 12 张容易过拟合。我建议 15-20 张,覆盖不同角度。
- 角点检测失败:如果 findChessboardCorners 老是返回 False,检查一下棋盘格是否完整出现在画面中,或者光照是否均匀。
- 方格尺寸写错:这个错误很低级,但很容易犯。我有一回把 25mm 写成了 2.5mm,结果标定出来的焦距完全不对。记得检查单位。
我的小技巧: 标定完成后,用 cv2.undistort 对一张图片做校正,看看边缘的直线是否变直了。如果校正后直线反而更弯,说明标定参数有问题,需要重新采集图片。
4.6 小结
这一章,我们走完了 OpenCV 相机标定的完整流程。从准备棋盘格、采集图片,到运行代码、解读参数,每一步我都把实际项目中容易出问题的地方点出来了。
拿到相机矩阵和畸变系数之后,下一步就是用它来做畸变校正了。不过那是下一章的事,这里先不展开。
你先把标定跑通,看看你的相机参数长什么样。有问题随时回来翻翻这章的避坑指南。