别光记公式用PythonOpenCV手把手带你标定相机内参外参附完整代码在计算机视觉项目中相机标定是构建三维感知系统的第一步。很多开发者能背诵内参矩阵的数学形式却对如何用代码实际获取这些参数一头雾水。本文将用最直白的方式带你用办公室常见的A4纸打印棋盘格配合PythonOpenCV完成从数据采集到参数应用的全流程。1. 准备工作从棋盘格到拍摄技巧标定过程需要一组从不同角度拍摄的棋盘格图像。建议使用A4纸打印8x6的棋盘格每个方格边长建议2-3cm贴在平整的硬纸板上。这个尺寸在1米距离拍摄时能占满画面约1/3区域既保证角点检测精度又方便多角度采集。拍摄注意事项保持棋盘格完全展开无弯曲建议用硬质底板固定至少准备15-20张不同角度照片倾斜、旋转、远近变化确保部分照片包含棋盘格的边缘区域这对畸变校正很重要避免强光反射导致过曝自然光环境最理想import cv2 import numpy as np import glob # 定义棋盘格规格 pattern_size (7, 5) # 内部角点数量比实际方格数少1 square_size 2.5 # 每个方格的实际尺寸厘米 # 准备标定用的三维坐标点 obj_points [] for _ in range(pattern_size[0]*pattern_size[1]): obj_points.append([(i//pattern_size[0])*square_size, (i%pattern_size[0])*square_size, 0]) objp np.array(obj_points, dtypenp.float32)2. 角点检测与数据收集OpenCV的findChessboardCorners函数能自动检测棋盘格角点但实际应用中常会遇到检测失败的情况。以下是增强检测成功率的实用技巧# 改进版角点检测 def enhanced_find_corners(img): gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, corners cv2.findChessboardCorners(gray, pattern_size, None) if not ret: # 尝试自适应阈值处理 gray cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2) ret, corners cv2.findChessboardCorners(gray, pattern_size, None) if ret: # 亚像素级精确化 criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) corners cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), criteria) return ret, corners收集到的数据需要组织成标定所需的格式obj_points_list [] # 三维世界坐标 img_points_list [] # 二维图像坐标 img_size None # 图像尺寸 images glob.glob(calibration_photos/*.jpg) for fname in images: img cv2.imread(fname) if img_size is None: img_size (img.shape[1], img.shape[0]) ret, corners enhanced_find_corners(img) if ret: obj_points_list.append(objp) img_points_list.append(corners) # 可视化调试用 cv2.drawChessboardCorners(img, pattern_size, corners, ret) cv2.imshow(Corners, img) cv2.waitKey(500)3. 核心标定与参数解读执行标定的核心代码虽然简单但输出结果需要正确解读ret, mtx, dist, rvecs, tvecs cv2.calibrateCamera( obj_points_list, img_points_list, img_size, None, None) print(内参矩阵:\n, mtx) print(\n畸变系数:, dist.ravel())内参矩阵mtx解析[[fx 0 cx] [ 0 fy cy] [ 0 0 1]]fx,fyx和y方向的焦距像素单位cx,cy主点坐标通常接近图像中心畸变系数dist顺序k1, k2径向畸变系数桶形/枕形畸变p1, p2切向畸变系数k3高阶径向畸变通常影响较小外参rvecs/tvecs特点每组图像对应一个旋转向量罗德里格斯格式和平移向量可通过cv2.Rodrigues()转换为3x3旋转矩阵表示从世界坐标系到相机坐标系的变换4. 标定结果验证与应用标定质量可通过重投影误差评估mean_error 0 for i in range(len(obj_points_list)): img_points2, _ cv2.projectPoints( obj_points_list[i], rvecs[i], tvecs[i], mtx, dist) error cv2.norm(img_points_list[i], img_points2, cv2.NORM_L2)/len(img_points2) mean_error error print(\n平均重投影误差: {:.2f} 像素.format(mean_error/len(obj_points_list)))误差评估标准0.5像素优秀0.5-1像素良好1像素建议重新标定实际应用示例——图像去畸变# 读取测试图像 test_img cv2.imread(test_photo.jpg) h, w test_img.shape[:2] # 优化内参矩阵去除黑边 new_mtx, roi cv2.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h)) x, y, w, h roi # 去畸变 dst cv2.undistort(test_img, mtx, dist, None, new_mtx) dst dst[y:yh, x:xw] # 裁剪有效区域 # 对比显示 cv2.imshow(Original, test_img) cv2.imshow(Undistorted, dst) cv2.waitKey(0)5. 实战技巧与问题排查常见问题解决方案问题现象可能原因解决方法角点检测失败光照不均/反光使用漫反射光源避免直射光重投影误差大棋盘格移动不充分增加拍摄角度变化范围畸变校正异常标定图像不足至少使用15张不同角度图像主点偏离中心图像尺寸错误检查img_size是否正确设置AR应用中的外参使用示例# 虚拟物体投影假设z0平面为棋盘格平面 cube_points np.float32([[0,0,0], [0,3,0], [3,3,0], [3,0,0], [0,0,-3],[0,3,-3],[3,3,-3],[3,0,-3]]) # 获取当前视角的外参 idx 0 # 选择某张标定图像 rvec, tvec rvecs[idx], tvecs[idx] # 投影立方体 cube_2d, _ cv2.projectPoints(cube_points, rvec, tvec, mtx, dist) cube_2d np.int32(cube_2d).reshape(-1,2) # 绘制立方体 img cv2.imread(images[idx]) img cv2.drawContours(img, [cube_2d[:4]], -1, (0,255,0), 3) for i,j in zip(range(4), range(4,8)): img cv2.line(img, tuple(cube_2d[i]), tuple(cube_2d[j]), (255,0,0), 3) img cv2.drawContours(img, [cube_2d[4:]], -1, (0,0,255), 3) cv2.imshow(AR Demo, img) cv2.waitKey(0)性能优化技巧对于固定镜头相机只需首次标定参数可持久化保存使用cv2.fisheye模块处理鱼眼镜头的极端畸变标定时开启CALIB_USE_LU标志加速计算移动设备考虑使用ChArUco板替代传统棋盘格