3、OpenCV环境搭建与核心函数概览
说实话,标定这事儿,环境没搭好,后面全是白搭。我见过不少新手,代码写好了,一运行报错,结果发现是OpenCV版本不对,或者编译时少了模块。嗯,咱们先把地基打牢。
3.1 环境搭建:别在这步翻车
我个人习惯用Python + OpenCV,调试快,改起来也方便。你如果做嵌入式或者需要极致性能,再考虑C++版本。
推荐组合:
- Python 3.8 - 3.11(别追最新,有些旧版API不兼容)
- OpenCV 4.5.x 或 4.6.x(稳定,文档全)
- NumPy(矩阵运算,OpenCV的底层依赖)
- Matplotlib(可视化标定结果)
安装其实就一行命令。但我建议你用虚拟环境,别污染系统Python。
# 创建虚拟环境(推荐)
python -m venv opencv_calib
source opencv_calib/bin/activate # Linux/Mac
opencv_calib\Scripts\activate # Windows
# 安装核心库
pip install opencv-python==4.6.0.66
pip install opencv-contrib-python==4.6.0.66 # 包含额外模块
pip install numpy matplotlib
我曾经踩过的坑: 直接用 pip install opencv-python 装了最新版,结果 cv2.findChessboardCorners 的参数变了,跑了一周的老代码全崩。所以,版本锁定很重要。
验证安装是否成功,打开Python终端,敲下面两行:
import cv2
print(cv2.__version__) # 输出应为 4.6.0
看到版本号,环境就通了。就这么简单。
3.2 核心函数初识:三剑客
标定流程说白了就三步:找角点 → 算参数 → 去畸变。对应三个核心API。咱们先混个脸熟,后面章节再深挖。
3.2.1 cv2.findChessboardCorners — 找棋盘格角点
这个函数的作用,就是从图像里找到棋盘格的内角点。注意,是内角点,不是棋盘格边缘的角。
ret, corners = cv2.findChessboardCorners(image, patternSize, flags)
| 参数 | 说明 | 我的经验 |
|---|---|---|
image |
输入图像(灰度图最好) | 彩色图也能用,但内部会转灰度,不如直接传灰度图快 |
patternSize |
棋盘格内角点数,如 (9, 6) | 别搞错行列顺序,我见过有人把 (9,6) 写成 (6,9),找了半天找不到角点 |
flags |
可选标志,如自适应阈值 | 默认值够用,复杂光照下加 CALIB_CB_ADAPTIVE_THRESH |
ret |
布尔值,是否找到角点 | 如果返回 False,别硬算,换张图或者调参数 |
corners |
检测到的角点坐标 | 形状为 (N, 1, 2),N 是角点总数 |
小技巧: 找到角点后,用 cv2.cornerSubPix() 做亚像素精细化,精度能提升一个量级。后面章节会细讲。
3.2.2 cv2.calibrateCamera — 算相机参数
这是标定的核心。你给它一堆棋盘格的世界坐标和图像坐标,它就能算出相机内参、畸变系数、旋转和平移向量。
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs, flags, criteria
)
| 输出 | 含义 | 备注 |
|---|---|---|
ret |
重投影误差 | 越小越好,一般 < 0.5 算优秀 |
mtx |
相机内参矩阵 (3x3) | 包含 fx, fy, cx, cy |
dist |
畸变系数 (1x5) | k1, k2, p1, p2, k3 |
rvecs |
每张图的旋转向量 | 用 Rodrigues 转成旋转矩阵 |
tvecs |
每张图的平移向量 | 表示相机相对于棋盘格的位置 |
你想想看,这个函数其实是在解一个超定方程组。棋盘格越多,角度越丰富,结果越准。我建议至少拍15-20张不同角度的图。
3.2.3 cv2.undistort — 去畸变
参数算出来了,就该干活了。去畸变就是把拍歪了的图拉直。
undistorted = cv2.undistort(img, mtx, dist, None, newCameraMatrix)
这里有个细节:newCameraMatrix 如果不传,默认用 mtx。但如果你想要完全无畸变的图,可以用 cv2.getOptimalNewCameraMatrix() 算一个更合适的矩阵,它会自动裁剪掉边缘的黑边。
核心流程总结:
- 准备棋盘格图片(多角度)
- 用
findChessboardCorners找角点 - 用
calibrateCamera算内参和畸变系数 - 用
undistort去畸变
3.3 知识体系图:标定流程一目了然
下面这张图,把整个标定流程串起来了。你跟着箭头走,就知道每一步该干什么。
3.4 第一个完整示例:跑通再说
光说不练假把式。下面这个代码,你复制粘贴就能跑。前提是准备一张棋盘格图片,命名为 chessboard.jpg。
import cv2
import numpy as np
# 1. 读取图片
img = cv2.imread('chessboard.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 2. 找角点(棋盘格内角点 9x6)
pattern_size = (9, 6)
ret, corners = cv2.findChessboardCorners(gray, pattern_size, None)
if ret:
print(f"找到 {len(corners)} 个角点")
# 3. 亚像素精细化
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
corners_refined = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
# 4. 准备世界坐标(假设棋盘格方格尺寸为 1 单位)
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)
# 5. 标定(这里只用了一张图,实际需要多张)
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
[objp], [corners_refined], gray.shape[::-1], None, None
)
print(f"内参矩阵:\n{mtx}")
print(f"畸变系数:\n{dist}")
print(f"重投影误差: {ret:.4f}")
# 6. 去畸变
h, w = img.shape[:2]
new_mtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))
undistorted = cv2.undistort(img, mtx, dist, None, new_mtx)
# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Undistorted', undistorted)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print("没找到角点,检查图片或 pattern_size")
注意: 上面代码只用了一张图做演示,实际标定至少需要15-20张。单张图算出来的参数不靠谱,重投影误差会很大。后面章节我会教你怎么批量处理。
3.5 避坑指南:我踩过的雷
- 棋盘格打印不平整: 我曾经用普通A4纸打印,贴在硬纸板上,结果角点检测总是飘。后来换了亚光相纸,贴在玻璃板上,精度立马提升。
- 图片数量不够: 有人拍了5张就开标,重投影误差0.8,怎么调都下不去。加到20张,误差直接降到0.3。说白了,数据量决定上限。
- 忽略亚像素优化: 直接拿
findChessboardCorners的粗坐标去标定,精度损失很大。加一行cornerSubPix,效果天差地别。
嗯,环境搭好了,函数也认识了。接下来咱们就一步步深入,把每个API的细节吃透。