1. 相机标定基础概念当你第一次接触相机标定时可能会被各种坐标系和参数搞得晕头转向。别担心我们先从最基础的概念开始用生活中的例子帮你理解这些专业术语。想象一下你正在用手机拍照。当你对准一个物体按下快门时手机是如何把这个三维世界的物体变成二维照片的呢这个过程就是相机成像的核心。相机标定就是要搞清楚这个转换过程的数学规律就像给相机做一次体检测量它的视力参数。在计算机视觉中我们常用四个坐标系来描述这个过程世界坐标系就像地球的经纬度给所有物体一个统一的参考系。比如你可以把棋盘格的左上角设为原点(0,0,0)相机坐标系以相机镜头为中心建立的3D坐标系Z轴指向拍摄方向图像坐标系在相机成像平面上建立的2D坐标系单位通常是毫米像素坐标系最终照片上的像素位置原点在图像左上角这四个坐标系之间的转换关系就是我们标定要确定的参数。其中最重要的两个转换是世界坐标系→相机坐标系需要知道相机的位置和朝向这就是外参旋转矩阵R和平移向量T相机坐标系→像素坐标系由相机本身的特性决定这就是内参焦距、主点坐标等在实际应用中相机镜头还会引入畸变就像哈哈镜会让图像变形一样。常见的畸变包括径向畸变图像边缘的直线变弯曲像通过鱼眼镜头看东西切向畸变由于镜头安装不完美导致的图像倾斜# OpenCV中表示相机内参的矩阵 import numpy as np camera_matrix np.array([ [fx, 0, cx], [0, fy, cy], [0, 0, 1] ]) # 畸变系数通常表示为[k1,k2,p1,p2,k3] dist_coeffs np.array([k1,k2,p1,p2,k3])2. 张正友标定法详解张正友教授在1998年提出的标定方法因其简单实用而成为业界标准。这个方法只需要打印一张棋盘格图案从不同角度拍摄几张照片就能完成标定。2.1 单应性矩阵求解棋盘格有个很好的特性它的所有角点都在同一个平面上。当我们从不同角度拍摄时这些平面上的点会投影到图像的不同位置。这种平面到平面的映射关系数学上称为单应性矩阵Homography。计算单应性矩阵需要至少4组对应点。实际操作中我们会检测棋盘格的所有角点比如8×6的棋盘有35个内角点用最小二乘法求最优解。OpenCV中的findChessboardCorners和cornerSubPix函数可以高精度地完成这个任务。# 查找棋盘格角点 ret, corners cv2.findChessboardCorners(gray, (8,6), None) if ret: # 亚像素级精确化 corners cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001))2.2 内参矩阵分解单应性矩阵H实际上是内参矩阵A和外参[R|t]的乘积。张正友的巧妙之处在于通过旋转矩阵的正交约束可以建立关于内参的方程。具体步骤是对每张图像计算单应性矩阵H利用H的列向量与内参的关系建立约束方程收集多张图像的约束求解内参的闭合解这个过程中我们需要至少3张不同角度的棋盘图像。实际使用时建议拍摄10-20张覆盖不同角度和位置的照片这样结果更稳定。2.3 外参估计一旦得到内参矩阵A外参就很容易计算了。对于每张图像外参可以通过以下公式得到[r1 r2 t] λA⁻¹H r3 r1 × r2其中λ是比例因子×表示叉积。这样我们就得到了每张图像中棋盘格相对于相机的位置和姿态。2.4 畸变参数估计前面几步假设相机没有畸变但实际上镜头畸变总是存在的。张正友方法可以同时估计径向和切向畸变系数。这个过程是非线性的通常采用Levenberg-Marquardt算法优化求解。畸变模型一般表示为x_corrected x(1 k1*r² k2*r⁴ k3*r⁶) 2p1xy p2(r²2x²) y_corrected y(1 k1*r² k2*r⁴ k3*r⁶) p1(r²2y²) 2p2xy其中k1,k2,k3是径向畸变系数p1,p2是切向畸变系数。3. 双目标定与极线校正单目标定解决了单个相机的参数问题但要想获取深度信息我们需要双目相机。双目标定除了要确定每个相机的内参外还需要找到两个相机之间的相对位置关系。3.1 双目标定原理双目标定的核心是计算两个相机之间的基础矩阵Fundamental Matrix或本质矩阵Essential Matrix。这些矩阵包含了两个相机之间的旋转和平移信息。具体步骤分别对左右相机进行单目标定得到各自的内参和畸变系数使用同一棋盘格同时拍摄左右相机的图像匹配左右图像中的对应角点计算基础矩阵或本质矩阵分解得到旋转矩阵R和平移向量t# OpenCV中的双目标定 ret, K1, D1, K2, D2, R, T, E, F cv2.stereoCalibrate( objectPoints, imagePoints1, imagePoints2, K1, D1, K2, D2, imageSize, flagscv2.CALIB_FIX_INTRINSIC)3.2 极线几何与校正双目相机的一个重要概念是极线几何。对于左图像中的一个点它在右图像中的对应点必定位于一条特殊的直线上这条线就是极线。极线校正的目的是让两个相机的图像平面平行这样极线就变成水平线大大简化立体匹配的过程。校正后的系统有以下特点两相机光轴平行像平面共面对应点在同一扫描线上OpenCV提供了stereoRectify和initUndistortRectifyMap函数来完成这个任务# 极线校正 R1, R2, P1, P2, Q, roi1, roi2 cv2.stereoRectify( K1, D1, K2, D2, imageSize, R, T) # 计算校正映射 map1x, map1y cv2.initUndistortRectifyMap( K1, D1, R1, P1, imageSize, cv2.CV_32FC1) map2x, map2y cv2.initUndistortRectifyMap( K2, D2, R2, P2, imageSize, cv2.CV_32FC1) # 应用校正 img1_rect cv2.remap(img1, map1x, map1y, cv2.INTER_LINEAR) img2_rect cv2.remap(img2, map2x, map2y, cv2.INTER_LINEAR)4. 实战OpenCV标定全流程现在让我们用一个完整的例子展示如何使用OpenCV实现从单目标定到极线校正的全过程。4.1 准备标定图像首先需要准备棋盘格图案比如8×6的棋盘每个方格30mm并从不同角度拍摄至少10张照片。建议覆盖图像的不同区域中心、四角包含不同倾斜角度确保棋盘格完整出现在画面中4.2 单目标定步骤import numpy as np import cv2 import glob # 设置棋盘格参数 chessboard_size (8,6) square_size 30.0 # mm # 准备对象点 (0,0,0), (1,0,0), (2,0,0) ..., (7,5,0) 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 # 存储对象点和图像点 objpoints [] # 3D点 imgpoints [] # 2D点 images glob.glob(calib_images/*.jpg) for fname in images: img cv2.imread(fname) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 查找角点 ret, corners cv2.findChessboardCorners(gray, chessboard_size, None) if ret: objpoints.append(objp) # 亚像素精确化 corners2 cv2.cornerSubPix(gray,corners, (11,11), (-1,-1), (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)) imgpoints.append(corners2) # 绘制并显示角点 cv2.drawChessboardCorners(img, chessboard_size, corners2, ret) cv2.imshow(img, img) cv2.waitKey(500) cv2.destroyAllWindows() # 标定相机 ret, mtx, dist, rvecs, tvecs cv2.calibrateCamera( objpoints, imgpoints, gray.shape[::-1], None, None) print(相机矩阵:\n, mtx) print(畸变系数:, dist.ravel())4.3 双目标定步骤完成单目标定后使用同步拍摄的左右图像对进行双目标定# 假设已经完成了左右相机的单目标定得到K1,D1和K2,D2 # 现在使用同步拍摄的棋盘格图像进行双目标定 # 准备对象点 (同上) objpoints [] # 左图像点 imgpoints_l [] # 右图像点 imgpoints_r [] # 读取同步图像对 image_pairs [...] # 左右图像路径对列表 for left_path, right_path in image_pairs: img_l cv2.imread(left_path) img_r cv2.imread(right_path) gray_l cv2.cvtColor(img_l, cv2.COLOR_BGR2GRAY) gray_r cv2.cvtColor(img_r, cv2.COLOR_BGR2GRAY) # 在左右图像中查找角点 ret_l, corners_l cv2.findChessboardCorners(gray_l, chessboard_size, None) ret_r, corners_r cv2.findChessboardCorners(gray_r, chessboard_size, None) if ret_l and ret_r: objpoints.append(objp) corners_l cv2.cornerSubPix(gray_l, corners_l, (11,11), (-1,-1), (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)) corners_r cv2.cornerSubPix(gray_r, corners_r, (11,11), (-1,-1), (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)) imgpoints_l.append(corners_l) imgpoints_r.append(corners_r) # 双目标定 ret, K1, D1, K2, D2, R, T, E, F cv2.stereoCalibrate( objpoints, imgpoints_l, imgpoints_r, K1, D1, K2, D2, gray_l.shape[::-1], criteria(cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-5)) print(旋转矩阵:\n, R) print(平移向量:, T)4.4 极线校正实现# 极线校正 R1, R2, P1, P2, Q, roi1, roi2 cv2.stereoRectify( K1, D1, K2, D2, gray_l.shape[::-1], R, T, flagscv2.CALIB_ZERO_DISPARITY, alpha0.9) # 计算校正映射 map1x, map1y cv2.initUndistortRectifyMap( K1, D1, R1, P1, gray_l.shape[::-1], cv2.CV_32FC1) map2x, map2y cv2.initUndistortRectifyMap( K2, D2, R2, P2, gray_r.shape[::-1], cv2.CV_32FC1) # 读取测试图像 img_l cv2.imread(test_left.jpg) img_r cv2.imread(test_right.jpg) # 应用校正 img_l_rect cv2.remap(img_l, map1x, map1y, cv2.INTER_LINEAR) img_r_rect cv2.remap(img_r, map2x, map2y, cv2.INTER_LINEAR) # 绘制水平线检查校正效果 for i in range(0, img_l_rect.shape[0], 30): cv2.line(img_l_rect, (0,i), (img_l_rect.shape[1],i), (0,255,0), 1) cv2.line(img_r_rect, (0,i), (img_r_rect.shape[1],i), (0,255,0), 1) # 并排显示 total np.hstack((img_l_rect, img_r_rect)) cv2.imshow(Rectified, total) cv2.waitKey(0) cv2.destroyAllWindows()5. 常见问题与解决方案在实际标定过程中你可能会遇到各种问题。下面是一些常见问题及其解决方法5.1 标定结果不准确症状重投影误差很大1像素或者标定后的图像矫正效果不明显。可能原因棋盘格角点检测不准确标定图像数量不足或角度变化不够棋盘格平面度不够比如纸张弯曲解决方案使用质量更好的棋盘格打印在硬质材料上增加标定图像数量15-20张确保图像覆盖整个视野包含不同角度检查角点检测结果确保所有角点都被正确识别5.2 极线校正后图像出现黑边症状校正后的图像边缘有大片黑色区域。原因这是正常现象因为极线校正需要对图像进行旋转和变形。解决方案调整stereoRectify中的alpha参数0-1之间控制保留多少有效区域裁剪掉黑色区域使用roi参数获取有效区域5.3 立体匹配效果差症状校正后左右图像对应点不在同一水平线上。原因标定不准确相机在标定后位置发生移动动态场景中存在运动物体解决方案重新进行精确标定确保相机固定牢固对于动态场景考虑使用在线标定方法5.4 畸变矫正后图像仍然变形症状应用畸变校正后图像边缘仍然有可见的变形。原因畸变模型不够复杂如只使用了k1,k2而实际畸变需要更高阶项标定过程中棋盘格没有覆盖图像边缘区域解决方案使用更高阶的畸变模型如包含k3确保标定图像中包含棋盘格位于图像边缘的情况6. 进阶技巧与优化建议当你掌握了基础标定方法后可以尝试以下进阶技巧来提升标定精度和效果6.1 使用非对称棋盘格传统棋盘格在旋转180度后看起来一样这可能导致角点排序错误。使用非对称棋盘格如ChArUco板可以避免这个问题提高标定精度。6.2 多阶段标定法先使用简单模型仅k1,k2进行初步标定应用初步标定结果去除大部分畸变在矫正后的图像上使用更复杂的模型进行精细标定这种方法可以避免高阶参数之间的耦合提高标定稳定性。6.3 温度补偿工业应用中相机和镜头的参数会随温度变化。可以在不同温度下进行标定建立参数与温度的关系模型实现动态补偿。6.4 自动标定系统对于需要频繁标定的场景可以开发自动标定系统自动检测标定板位置机械臂带动标定板到预设位置自动采集图像并计算参数验证标定结果并提示6.5 标定结果验证良好的标定系统应该包含验证环节计算重投影误差应0.5像素检查校正后的极线是否水平使用已知尺寸的物体测试三维重建精度# 计算重投影误差 mean_error 0 for i in range(len(objpoints)): imgpoints2, _ cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist) error cv2.norm(imgpoints[i], imgpoints2, cv2.NORM_L2)/len(imgpoints2) mean_error error print(平均重投影误差: {:.2f}像素.format(mean_error/len(objpoints)))7. 实际应用案例相机标定技术在各种计算机视觉应用中发挥着关键作用。下面介绍几个典型应用场景7.1 三维重建通过标定的双目相机可以计算场景中每个点的三维坐标。基本原理是利用视差disparityZ f * B / d其中Z是深度f是焦距像素单位B是两个相机的基线距离d是视差同一物体在左右图像中的水平位置差OpenCV提供了StereoBM和StereoSGBM等算法来计算视差图。7.2 物体测量在工业检测中标定后的相机可以用于精确测量物体尺寸。例如测量PCB板上元件的间距检测机械零件的尺寸公差物流行业中测量包裹体积7.3 增强现实AR应用需要将虚拟物体准确地叠加到真实场景中这需要精确的相机标定来确定虚拟物体的位置和姿态。7.4 视觉SLAM同时定位与地图构建SLAM系统依赖相机标定参数来正确估计相机运动和重建环境三维结构。标定误差会直接影响SLAM的精度和稳定性。7.5 人脸三维建模通过多相机系统拍摄人脸不同角度的图像利用标定参数可以将这些图像融合成完整的三维模型应用于影视制作、医疗美容等领域。8. 性能评估与误差分析了解如何评估标定结果的质量至关重要。以下是关键的评估指标和方法8.1 重投影误差这是最直接的评估指标表示标定后的参数将3D点重新投影到图像平面时与原始检测点的距离。理想情况下应小于0.5像素。8.2 极线误差对于双目标定校正后的对应点应该位于同一水平线上。可以测量实际y坐标的差异来评估校正质量。8.3 参数稳定性多次标定的参数应该保持一致。可以计算同一相机多次标定结果的方差来评估稳定性。8.4 三维测量误差使用已知尺寸的测试物体如标定板进行三维重建比较测量值与真实值的差异。8.5 影响因素分析图像数量通常10-20张足够更多图像对精度提升有限棋盘格姿态包含不同倾斜角度和位置能提高标定鲁棒性角点检测精度亚像素优化能显著提高标定精度镜头畸变广角镜头需要更高阶的畸变模型噪声水平高ISO或运动模糊会降低标定精度# 评估极线校正效果 def evaluate_epipolar_error(pts1, pts2, F): 计算极线误差 :param pts1: 左图像点(Nx2) :param pts2: 右图像点(Nx2) :param F: 基础矩阵 :return: 平均误差 lines1 cv2.computeCorrespondEpilines(pts2.reshape(-1,1,2), 2, F) lines1 lines1.reshape(-1,3) error 0 for (x1,y1), (a,b,c) in zip(pts1, lines1): error abs(a*x1 b*y1 c) / np.sqrt(a*a b*b) return error / len(pts1)
张正友标定法原理详解与极线校正实战指南
发布时间:2026/7/14 11:41:44
1. 相机标定基础概念当你第一次接触相机标定时可能会被各种坐标系和参数搞得晕头转向。别担心我们先从最基础的概念开始用生活中的例子帮你理解这些专业术语。想象一下你正在用手机拍照。当你对准一个物体按下快门时手机是如何把这个三维世界的物体变成二维照片的呢这个过程就是相机成像的核心。相机标定就是要搞清楚这个转换过程的数学规律就像给相机做一次体检测量它的视力参数。在计算机视觉中我们常用四个坐标系来描述这个过程世界坐标系就像地球的经纬度给所有物体一个统一的参考系。比如你可以把棋盘格的左上角设为原点(0,0,0)相机坐标系以相机镜头为中心建立的3D坐标系Z轴指向拍摄方向图像坐标系在相机成像平面上建立的2D坐标系单位通常是毫米像素坐标系最终照片上的像素位置原点在图像左上角这四个坐标系之间的转换关系就是我们标定要确定的参数。其中最重要的两个转换是世界坐标系→相机坐标系需要知道相机的位置和朝向这就是外参旋转矩阵R和平移向量T相机坐标系→像素坐标系由相机本身的特性决定这就是内参焦距、主点坐标等在实际应用中相机镜头还会引入畸变就像哈哈镜会让图像变形一样。常见的畸变包括径向畸变图像边缘的直线变弯曲像通过鱼眼镜头看东西切向畸变由于镜头安装不完美导致的图像倾斜# OpenCV中表示相机内参的矩阵 import numpy as np camera_matrix np.array([ [fx, 0, cx], [0, fy, cy], [0, 0, 1] ]) # 畸变系数通常表示为[k1,k2,p1,p2,k3] dist_coeffs np.array([k1,k2,p1,p2,k3])2. 张正友标定法详解张正友教授在1998年提出的标定方法因其简单实用而成为业界标准。这个方法只需要打印一张棋盘格图案从不同角度拍摄几张照片就能完成标定。2.1 单应性矩阵求解棋盘格有个很好的特性它的所有角点都在同一个平面上。当我们从不同角度拍摄时这些平面上的点会投影到图像的不同位置。这种平面到平面的映射关系数学上称为单应性矩阵Homography。计算单应性矩阵需要至少4组对应点。实际操作中我们会检测棋盘格的所有角点比如8×6的棋盘有35个内角点用最小二乘法求最优解。OpenCV中的findChessboardCorners和cornerSubPix函数可以高精度地完成这个任务。# 查找棋盘格角点 ret, corners cv2.findChessboardCorners(gray, (8,6), None) if ret: # 亚像素级精确化 corners cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001))2.2 内参矩阵分解单应性矩阵H实际上是内参矩阵A和外参[R|t]的乘积。张正友的巧妙之处在于通过旋转矩阵的正交约束可以建立关于内参的方程。具体步骤是对每张图像计算单应性矩阵H利用H的列向量与内参的关系建立约束方程收集多张图像的约束求解内参的闭合解这个过程中我们需要至少3张不同角度的棋盘图像。实际使用时建议拍摄10-20张覆盖不同角度和位置的照片这样结果更稳定。2.3 外参估计一旦得到内参矩阵A外参就很容易计算了。对于每张图像外参可以通过以下公式得到[r1 r2 t] λA⁻¹H r3 r1 × r2其中λ是比例因子×表示叉积。这样我们就得到了每张图像中棋盘格相对于相机的位置和姿态。2.4 畸变参数估计前面几步假设相机没有畸变但实际上镜头畸变总是存在的。张正友方法可以同时估计径向和切向畸变系数。这个过程是非线性的通常采用Levenberg-Marquardt算法优化求解。畸变模型一般表示为x_corrected x(1 k1*r² k2*r⁴ k3*r⁶) 2p1xy p2(r²2x²) y_corrected y(1 k1*r² k2*r⁴ k3*r⁶) p1(r²2y²) 2p2xy其中k1,k2,k3是径向畸变系数p1,p2是切向畸变系数。3. 双目标定与极线校正单目标定解决了单个相机的参数问题但要想获取深度信息我们需要双目相机。双目标定除了要确定每个相机的内参外还需要找到两个相机之间的相对位置关系。3.1 双目标定原理双目标定的核心是计算两个相机之间的基础矩阵Fundamental Matrix或本质矩阵Essential Matrix。这些矩阵包含了两个相机之间的旋转和平移信息。具体步骤分别对左右相机进行单目标定得到各自的内参和畸变系数使用同一棋盘格同时拍摄左右相机的图像匹配左右图像中的对应角点计算基础矩阵或本质矩阵分解得到旋转矩阵R和平移向量t# OpenCV中的双目标定 ret, K1, D1, K2, D2, R, T, E, F cv2.stereoCalibrate( objectPoints, imagePoints1, imagePoints2, K1, D1, K2, D2, imageSize, flagscv2.CALIB_FIX_INTRINSIC)3.2 极线几何与校正双目相机的一个重要概念是极线几何。对于左图像中的一个点它在右图像中的对应点必定位于一条特殊的直线上这条线就是极线。极线校正的目的是让两个相机的图像平面平行这样极线就变成水平线大大简化立体匹配的过程。校正后的系统有以下特点两相机光轴平行像平面共面对应点在同一扫描线上OpenCV提供了stereoRectify和initUndistortRectifyMap函数来完成这个任务# 极线校正 R1, R2, P1, P2, Q, roi1, roi2 cv2.stereoRectify( K1, D1, K2, D2, imageSize, R, T) # 计算校正映射 map1x, map1y cv2.initUndistortRectifyMap( K1, D1, R1, P1, imageSize, cv2.CV_32FC1) map2x, map2y cv2.initUndistortRectifyMap( K2, D2, R2, P2, imageSize, cv2.CV_32FC1) # 应用校正 img1_rect cv2.remap(img1, map1x, map1y, cv2.INTER_LINEAR) img2_rect cv2.remap(img2, map2x, map2y, cv2.INTER_LINEAR)4. 实战OpenCV标定全流程现在让我们用一个完整的例子展示如何使用OpenCV实现从单目标定到极线校正的全过程。4.1 准备标定图像首先需要准备棋盘格图案比如8×6的棋盘每个方格30mm并从不同角度拍摄至少10张照片。建议覆盖图像的不同区域中心、四角包含不同倾斜角度确保棋盘格完整出现在画面中4.2 单目标定步骤import numpy as np import cv2 import glob # 设置棋盘格参数 chessboard_size (8,6) square_size 30.0 # mm # 准备对象点 (0,0,0), (1,0,0), (2,0,0) ..., (7,5,0) 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 # 存储对象点和图像点 objpoints [] # 3D点 imgpoints [] # 2D点 images glob.glob(calib_images/*.jpg) for fname in images: img cv2.imread(fname) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 查找角点 ret, corners cv2.findChessboardCorners(gray, chessboard_size, None) if ret: objpoints.append(objp) # 亚像素精确化 corners2 cv2.cornerSubPix(gray,corners, (11,11), (-1,-1), (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)) imgpoints.append(corners2) # 绘制并显示角点 cv2.drawChessboardCorners(img, chessboard_size, corners2, ret) cv2.imshow(img, img) cv2.waitKey(500) cv2.destroyAllWindows() # 标定相机 ret, mtx, dist, rvecs, tvecs cv2.calibrateCamera( objpoints, imgpoints, gray.shape[::-1], None, None) print(相机矩阵:\n, mtx) print(畸变系数:, dist.ravel())4.3 双目标定步骤完成单目标定后使用同步拍摄的左右图像对进行双目标定# 假设已经完成了左右相机的单目标定得到K1,D1和K2,D2 # 现在使用同步拍摄的棋盘格图像进行双目标定 # 准备对象点 (同上) objpoints [] # 左图像点 imgpoints_l [] # 右图像点 imgpoints_r [] # 读取同步图像对 image_pairs [...] # 左右图像路径对列表 for left_path, right_path in image_pairs: img_l cv2.imread(left_path) img_r cv2.imread(right_path) gray_l cv2.cvtColor(img_l, cv2.COLOR_BGR2GRAY) gray_r cv2.cvtColor(img_r, cv2.COLOR_BGR2GRAY) # 在左右图像中查找角点 ret_l, corners_l cv2.findChessboardCorners(gray_l, chessboard_size, None) ret_r, corners_r cv2.findChessboardCorners(gray_r, chessboard_size, None) if ret_l and ret_r: objpoints.append(objp) corners_l cv2.cornerSubPix(gray_l, corners_l, (11,11), (-1,-1), (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)) corners_r cv2.cornerSubPix(gray_r, corners_r, (11,11), (-1,-1), (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)) imgpoints_l.append(corners_l) imgpoints_r.append(corners_r) # 双目标定 ret, K1, D1, K2, D2, R, T, E, F cv2.stereoCalibrate( objpoints, imgpoints_l, imgpoints_r, K1, D1, K2, D2, gray_l.shape[::-1], criteria(cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-5)) print(旋转矩阵:\n, R) print(平移向量:, T)4.4 极线校正实现# 极线校正 R1, R2, P1, P2, Q, roi1, roi2 cv2.stereoRectify( K1, D1, K2, D2, gray_l.shape[::-1], R, T, flagscv2.CALIB_ZERO_DISPARITY, alpha0.9) # 计算校正映射 map1x, map1y cv2.initUndistortRectifyMap( K1, D1, R1, P1, gray_l.shape[::-1], cv2.CV_32FC1) map2x, map2y cv2.initUndistortRectifyMap( K2, D2, R2, P2, gray_r.shape[::-1], cv2.CV_32FC1) # 读取测试图像 img_l cv2.imread(test_left.jpg) img_r cv2.imread(test_right.jpg) # 应用校正 img_l_rect cv2.remap(img_l, map1x, map1y, cv2.INTER_LINEAR) img_r_rect cv2.remap(img_r, map2x, map2y, cv2.INTER_LINEAR) # 绘制水平线检查校正效果 for i in range(0, img_l_rect.shape[0], 30): cv2.line(img_l_rect, (0,i), (img_l_rect.shape[1],i), (0,255,0), 1) cv2.line(img_r_rect, (0,i), (img_r_rect.shape[1],i), (0,255,0), 1) # 并排显示 total np.hstack((img_l_rect, img_r_rect)) cv2.imshow(Rectified, total) cv2.waitKey(0) cv2.destroyAllWindows()5. 常见问题与解决方案在实际标定过程中你可能会遇到各种问题。下面是一些常见问题及其解决方法5.1 标定结果不准确症状重投影误差很大1像素或者标定后的图像矫正效果不明显。可能原因棋盘格角点检测不准确标定图像数量不足或角度变化不够棋盘格平面度不够比如纸张弯曲解决方案使用质量更好的棋盘格打印在硬质材料上增加标定图像数量15-20张确保图像覆盖整个视野包含不同角度检查角点检测结果确保所有角点都被正确识别5.2 极线校正后图像出现黑边症状校正后的图像边缘有大片黑色区域。原因这是正常现象因为极线校正需要对图像进行旋转和变形。解决方案调整stereoRectify中的alpha参数0-1之间控制保留多少有效区域裁剪掉黑色区域使用roi参数获取有效区域5.3 立体匹配效果差症状校正后左右图像对应点不在同一水平线上。原因标定不准确相机在标定后位置发生移动动态场景中存在运动物体解决方案重新进行精确标定确保相机固定牢固对于动态场景考虑使用在线标定方法5.4 畸变矫正后图像仍然变形症状应用畸变校正后图像边缘仍然有可见的变形。原因畸变模型不够复杂如只使用了k1,k2而实际畸变需要更高阶项标定过程中棋盘格没有覆盖图像边缘区域解决方案使用更高阶的畸变模型如包含k3确保标定图像中包含棋盘格位于图像边缘的情况6. 进阶技巧与优化建议当你掌握了基础标定方法后可以尝试以下进阶技巧来提升标定精度和效果6.1 使用非对称棋盘格传统棋盘格在旋转180度后看起来一样这可能导致角点排序错误。使用非对称棋盘格如ChArUco板可以避免这个问题提高标定精度。6.2 多阶段标定法先使用简单模型仅k1,k2进行初步标定应用初步标定结果去除大部分畸变在矫正后的图像上使用更复杂的模型进行精细标定这种方法可以避免高阶参数之间的耦合提高标定稳定性。6.3 温度补偿工业应用中相机和镜头的参数会随温度变化。可以在不同温度下进行标定建立参数与温度的关系模型实现动态补偿。6.4 自动标定系统对于需要频繁标定的场景可以开发自动标定系统自动检测标定板位置机械臂带动标定板到预设位置自动采集图像并计算参数验证标定结果并提示6.5 标定结果验证良好的标定系统应该包含验证环节计算重投影误差应0.5像素检查校正后的极线是否水平使用已知尺寸的物体测试三维重建精度# 计算重投影误差 mean_error 0 for i in range(len(objpoints)): imgpoints2, _ cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist) error cv2.norm(imgpoints[i], imgpoints2, cv2.NORM_L2)/len(imgpoints2) mean_error error print(平均重投影误差: {:.2f}像素.format(mean_error/len(objpoints)))7. 实际应用案例相机标定技术在各种计算机视觉应用中发挥着关键作用。下面介绍几个典型应用场景7.1 三维重建通过标定的双目相机可以计算场景中每个点的三维坐标。基本原理是利用视差disparityZ f * B / d其中Z是深度f是焦距像素单位B是两个相机的基线距离d是视差同一物体在左右图像中的水平位置差OpenCV提供了StereoBM和StereoSGBM等算法来计算视差图。7.2 物体测量在工业检测中标定后的相机可以用于精确测量物体尺寸。例如测量PCB板上元件的间距检测机械零件的尺寸公差物流行业中测量包裹体积7.3 增强现实AR应用需要将虚拟物体准确地叠加到真实场景中这需要精确的相机标定来确定虚拟物体的位置和姿态。7.4 视觉SLAM同时定位与地图构建SLAM系统依赖相机标定参数来正确估计相机运动和重建环境三维结构。标定误差会直接影响SLAM的精度和稳定性。7.5 人脸三维建模通过多相机系统拍摄人脸不同角度的图像利用标定参数可以将这些图像融合成完整的三维模型应用于影视制作、医疗美容等领域。8. 性能评估与误差分析了解如何评估标定结果的质量至关重要。以下是关键的评估指标和方法8.1 重投影误差这是最直接的评估指标表示标定后的参数将3D点重新投影到图像平面时与原始检测点的距离。理想情况下应小于0.5像素。8.2 极线误差对于双目标定校正后的对应点应该位于同一水平线上。可以测量实际y坐标的差异来评估校正质量。8.3 参数稳定性多次标定的参数应该保持一致。可以计算同一相机多次标定结果的方差来评估稳定性。8.4 三维测量误差使用已知尺寸的测试物体如标定板进行三维重建比较测量值与真实值的差异。8.5 影响因素分析图像数量通常10-20张足够更多图像对精度提升有限棋盘格姿态包含不同倾斜角度和位置能提高标定鲁棒性角点检测精度亚像素优化能显著提高标定精度镜头畸变广角镜头需要更高阶的畸变模型噪声水平高ISO或运动模糊会降低标定精度# 评估极线校正效果 def evaluate_epipolar_error(pts1, pts2, F): 计算极线误差 :param pts1: 左图像点(Nx2) :param pts2: 右图像点(Nx2) :param F: 基础矩阵 :return: 平均误差 lines1 cv2.computeCorrespondEpilines(pts2.reshape(-1,1,2), 2, F) lines1 lines1.reshape(-1,3) error 0 for (x1,y1), (a,b,c) in zip(pts1, lines1): error abs(a*x1 b*y1 c) / np.sqrt(a*a b*b) return error / len(pts1)