4. 标定工具实战:使用OpenCV进行相机标定、获取畸变系数与相机矩阵

好了,前面我们把理论讲了个七七八八。现在该动手了。

这一章,我们就用 OpenCV 把相机标定跑一遍。我会带着你从准备棋盘格图片开始,一直到拿到畸变系数和相机矩阵。整个过程,说白了就是让相机自己报出它的“内参”和“毛病”。

4.1 准备工作:棋盘格与图片采集

标定需要一张已知尺寸的棋盘格图案。你打印出来,贴在硬纸板上就行。

棋盘格的关键参数:

  • 内角点数量:比如 (9, 6),指的是内部角点的行列数,不是格子数。
  • 方格尺寸:比如 25mm。这个值决定了标定的尺度。
我的习惯: 棋盘格不要太小,A4 纸打印的 (9,6) 棋盘,方格 25-30mm 最顺手。太小了,远距离拍不清楚;太大了,近距离又装不下。

采集图片时,记住几个要点:

  • 拍 15-20 张不同角度、不同距离的图片。
  • 棋盘格要出现在画面的各个角落——边缘、中心、倾斜都要有。
  • 光照均匀,不要有反光或阴影遮挡角点。
我曾经踩过的坑: 有次我偷懒只拍了 8 张,结果标定出来的畸变系数偏差很大,校正后图像边缘反而更扭曲了。后来老老实实补到 20 张,效果才稳定。所以,数量别省。

4.2 核心流程:从角点检测到参数输出

整个标定流程,我用一张图给你理清楚:

OpenCV 相机标定核心流程 OpenCV 相机标定核心流程 1. 读取棋盘格图片 2. findChessboardCorners 3. cornerSubPix 精化 4. calibrateCamera 5. 获取相机矩阵 + 畸变系数 关键输入 棋盘格内角点数量 方格实际尺寸 (mm) 15-20 张不同角度图片 关键输出 相机矩阵 (3x3) 畸变系数 (k1,k2,p1,p2,k3) 重投影误差

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 相机标定的完整流程。从准备棋盘格、采集图片,到运行代码、解读参数,每一步我都把实际项目中容易出问题的地方点出来了。

拿到相机矩阵和畸变系数之后,下一步就是用它来做畸变校正了。不过那是下一章的事,这里先不展开。

你先把标定跑通,看看你的相机参数长什么样。有问题随时回来翻翻这章的避坑指南。