别再死记硬背了!用Python+OpenCV手把手教你标定相机内参外参(附完整代码)
·
从零实现相机标定:Python+OpenCV实战指南
刚接触计算机视觉时,相机标定总让人望而生畏——那些矩阵参数、坐标系转换公式像天书一样难以理解。但当我第一次用自己拍摄的棋盘格图片成功计算出相机参数,并看到矫正后的图像时,那种"原来如此"的顿悟感至今难忘。本文将带你用Python和OpenCV一步步完成这个神奇的过程,把抽象的内外参概念转化为可运行的代码和可视化的结果。
1. 环境准备与工具搭建
工欲善其事,必先利其器。我们需要准备以下工具和环境:
-
硬件准备:一个普通的USB摄像头或智能手机(建议分辨率至少720p)、一张打印的棋盘格标定板(建议A4尺寸以上)。棋盘格可以从OpenCV官网下载标准模板,通常使用8x6或9x7的方格布局。
-
软件环境:
pip install opencv-python numpy matplotlib这三个库构成了我们实验的基础工具链:OpenCV提供计算机视觉算法,NumPy处理矩阵运算,Matplotlib用于可视化。
-
棋盘格注意事项:
- 打印时确保方格是完美的正方形
- 贴在平整的硬纸板或亚克力板上防止弯曲
- 光照均匀,避免反光和阴影
2. 数据采集:获取标定图像集
好的标定始于高质量的数据采集。建议在不同角度和距离拍摄15-20张棋盘格照片,覆盖相机视野的各个区域:
import cv2
cap = cv2.VideoCapture(0) # 如果是USB摄像头
img_count = 0
while img_count < 20:
ret, frame = cap.read()
cv2.imshow('Preview', frame)
key = cv2.waitKey(1)
if key == ord('s'): # 按s键保存
cv2.imwrite(f'calib_{img_count}.jpg', frame)
img_count += 1
elif key == 27: # ESC退出
break
cap.release()
cv2.destroyAllWindows()
采集技巧:
- 让棋盘格占据图像1/3到2/3的面积
- 尝试不同倾斜角度(30-60度)
- 包含棋盘格靠近图像边缘的样本
- 避免运动模糊(快门速度足够快)
3. 角点检测与参数计算
有了图像集后,我们开始自动检测棋盘格角点并计算相机参数:
import numpy as np
# 设置棋盘格规格(内角点数量)
pattern_size = (8, 6) # 比如8x6的棋盘格有7x5=35个内角点
# 准备对象点:(0,0,0), (1,0,0), (2,0,0), ..., (7,5,0)
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)
# 存储对象点和图像点
obj_points = [] # 3D世界空间中的点
img_points = [] # 2D图像平面中的点
# 遍历所有图像
for fname in glob.glob('calib_*.jpg'):
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 查找角点
ret, corners = cv2.findChessboardCorners(gray, pattern_size, None)
if ret: # 如果找到角点
obj_points.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)
img_points.append(corners2)
# 可视化(可选)
cv2.drawChessboardCorners(img, pattern_size, corners2, ret)
cv2.imshow('Corners', img)
cv2.waitKey(500)
cv2.destroyAllWindows()
4. 相机标定与参数解析
现在我们可以进行实际的相机标定了:
# 执行标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
obj_points, img_points, gray.shape[::-1], None, None)
print("相机矩阵:\n", mtx)
print("\n畸变系数:", dist.ravel())
参数解读:
-
相机矩阵(内参矩阵):
[[fx, 0, cx], [0, fy, cy], [0, 0, 1]]fx,fy:x和y方向的焦距(像素单位)cx,cy:主点坐标(通常接近图像中心)
-
畸变系数:
k1,k2,k3:径向畸变系数p1,p2:切向畸变系数
-
外参:
rvecs:每张图像的旋转向量(可通过cv2.Rodrigues()转换为矩阵)tvecs:每张图像的平移向量
5. 结果验证与图像矫正
理论需要实践验证,让我们用标定结果矫正一张测试图像:
# 读取测试图像
test_img = cv2.imread('test_image.jpg')
h, w = test_img.shape[:2]
# 优化相机矩阵和获取ROI
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))
# 方法1:使用cv2.undistort
dst = cv2.undistort(test_img, mtx, dist, None, newcameramtx)
# 方法2:使用remapping(更灵活)
mapx, mapy = cv2.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w,h), 5)
dst = cv2.remap(test_img, mapx, mapy, cv2.INTER_LINEAR)
# 裁剪图像(可选)
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]
# 并排显示
cv2.imshow('Original vs Undistorted', np.hstack((test_img, dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
效果评估:
- 观察图像边缘的直线是否变得笔直
- 比较矫正前后棋盘格的形变程度
- 测量已知长度的物体在图像中的比例是否一致
6. 高级技巧与常见问题
在实际项目中,你可能会遇到以下情况:
提高标定精度的技巧:
- 使用更多样化的拍摄角度(建议至少15张高质量图像)
- 确保棋盘格平整,打印精度高
- 在标定时使用
cv2.cornerSubPix提高角点定位精度 - 考虑使用非对称圆形网格标定板(对遮挡更鲁棒)
典型问题排查:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 找不到棋盘格 | 方格数设置错误 | 确认pattern_size是内角点数 |
| 角点检测不准确 | 图像模糊或反光 | 重新采集更清晰的图像 |
| 矫正后图像边缘扭曲 | 畸变模型不匹配 | 尝试更高阶的畸变系数 |
| 重投影误差大 | 标定板移动或变形 | 确保标定板刚性且固定 |
保存和加载标定参数:
import pickle
# 保存标定结果
calib_data = {
'camera_matrix': mtx,
'dist_coeffs': dist,
'rvecs': rvecs,
'tvecs': tvecs
}
with open('calibration.pkl', 'wb') as f:
pickle.dump(calib_data, f)
# 加载标定结果
with open('calibration.pkl', 'rb') as f:
calib_data = pickle.load(f)
mtx = calib_data['camera_matrix']
dist = calib_data['dist_coeffs']
7. 实际应用案例
相机标定不只是学术练习,它在许多实际应用中至关重要:
增强现实(AR):
- 将虚拟物体准确叠加到真实世界
- 需要精确的相机参数来保证虚实对齐
三维重建:
# 简化的视差计算示例
def compute_disparity(imgL, imgR):
stereo = cv2.StereoSGBM_create(
minDisparity=0,
numDisparities=64,
blockSize=11
)
disparity = stereo.compute(imgL, imgR)
return disparity
机器人导航:
- 通过标定后的相机进行精确的距离测量
- 视觉里程计中的位姿估计
工业检测:
- 消除镜头畸变对测量精度的影响
- 实现像素到实际尺寸的准确转换
在最近的一个项目中,我们使用标定结果将无人机拍摄的图像拼接成高精度地图。最初由于忽略了对每个相机的单独标定,拼接处总是出现明显的错位。重新进行严格标定后,拼接精度提高了近70%。这让我深刻体会到,好的标定是计算机视觉应用的基石。
更多推荐


所有评论(0)