从零开始:用Python+OpenCV实现双目相机校正(附stereoRectify完整代码)

张开发
2026/4/7 12:12:19 15 分钟阅读

分享文章

从零开始:用Python+OpenCV实现双目相机校正(附stereoRectify完整代码)
从零构建双目视觉系统PythonOpenCV立体校正实战指南双目视觉系统在机器人导航、三维重建和自动驾驶等领域有着广泛应用。本文将带你从相机标定开始逐步实现双目相机的立体校正最终获得可用于立体匹配的校正图像对。我们会使用Python和OpenCV库通过实际代码演示整个流程。1. 双目视觉基础与环境准备双目视觉的核心原理是通过两个相机从不同角度观察同一场景利用视差计算深度信息。要实现这一过程首先需要确保两个相机的相对位置关系已知这就是立体校正要做的工作。在开始前请确保已安装以下Python库pip install opencv-python numpy matplotlib对于硬件准备你需要两个相同的摄像头建议使用工业级USB相机一个稳固的支架确保两个相机位置固定标准棋盘格标定板建议打印A4大小的7x9棋盘提示相机间距基线长度会影响深度测量范围一般建议在5-15cm之间具体取决于应用场景。2. 相机标定获取内参和畸变系数双目校正的第一步是分别对左右相机进行标定获取各自的内参矩阵和畸变系数。下面是完整的标定代码import cv2 import numpy as np import glob def calibrate_camera(image_paths, pattern_size(6,8)): obj_points [] # 3D点 img_points [] # 2D点 # 准备标定板的世界坐标 (0,0,0), (1,0,0), ..., (5,7,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) for fname in image_paths: 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) # 亚像素精确化 corners2 cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)) img_points.append(corners2) # 相机标定 ret, mtx, dist, rvecs, tvecs cv2.calibrateCamera(obj_points, img_points, gray.shape[::-1], None, None) return ret, mtx, dist # 使用示例 left_images glob.glob(left/*.jpg) right_images glob.glob(right/*.jpg) _, left_mtx, left_dist calibrate_camera(left_images) _, right_mtx, right_dist calibrate_camera(right_images)标定过程中需要注意拍摄15-20张不同角度的标定板图像确保标定板覆盖整个画面区域图像中要能清晰看到所有内角点3. 双目系统标定确定相机间相对位置获取单相机参数后我们需要确定两个相机之间的空间关系旋转矩阵R和平移向量T。下面是双目标定的关键代码def stereo_calibrate(left_img_points, right_img_points, obj_points, image_size, left_mtx, left_dist, right_mtx, right_dist): flags cv2.CALIB_FIX_INTRINSIC criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-5) ret, _, _, _, _, R, T, E, F cv2.stereoCalibrate( obj_points, left_img_points, right_img_points, left_mtx, left_dist, right_mtx, right_dist, image_size, criteriacriteria, flagsflags) return ret, R, T # 使用示例需要先收集左右相机对应的标定图像对 image_size (640, 480) # 根据实际图像尺寸调整 _, R, T stereo_calibrate(left_img_points, right_img_points, obj_points, image_size, left_mtx, left_dist, right_mtx, right_dist)双目标定的关键点必须使用同一时刻拍摄的左右图像对每对标定图像中的棋盘格位置应不同平移向量T的范数即为两个相机的基线距离4. 立体校正stereoRectify实战有了上述参数后我们就可以进行立体校正了。下面是使用stereoRectify的核心代码def stereo_rectify(left_mtx, left_dist, right_mtx, right_dist, image_size, R, T): # 计算校正变换 R1, R2, P1, P2, Q, _, _ cv2.stereoRectify( left_mtx, left_dist, right_mtx, right_dist, image_size, R, T, flagscv2.CALIB_ZERO_DISPARITY, alpha0.9) # 计算映射表 left_map1, left_map2 cv2.initUndistortRectifyMap( left_mtx, left_dist, R1, P1, image_size, cv2.CV_16SC2) right_map1, right_map2 cv2.initUndistortRectifyMap( right_mtx, right_dist, R2, P2, image_size, cv2.CV_16SC2) return left_map1, left_map2, right_map1, right_map2, Q # 使用示例 left_map1, left_map2, right_map1, right_map2, Q stereo_rectify( left_mtx, left_dist, right_mtx, right_dist, image_size, R, T)stereoRectify函数参数详解参数名说明常见问题cameraMatrix1左相机内参矩阵必须3x3第三行[0,0,1]distCoeffs1左相机畸变系数通常5个元素向量cameraMatrix2右相机内参矩阵同上distCoeffs2右相机畸变系数同上imageSize图像尺寸(width, height)格式R相机间旋转矩阵从相机1到相机2的旋转T相机间平移向量从相机1到相机2的平移alpha裁剪参数0-1之间推荐0.95. 校正效果验证与应用完成校正后我们可以实时应用校正映射并可视化结果import matplotlib.pyplot as plt def apply_rectification(left_img, right_img, left_map1, left_map2, right_map1, right_map2): left_rect cv2.remap(left_img, left_map1, left_map2, cv2.INTER_LINEAR) right_rect cv2.remap(right_img, right_map1, right_map2, cv2.INTER_LINEAR) # 绘制水平线用于验证 for y in range(0, left_rect.shape[0], 30): cv2.line(left_rect, (0, y), (left_rect.shape[1], y), (0, 255, 0), 1) cv2.line(right_rect, (0, y), (right_rect.shape[1], y), (0, 255, 0), 1) return left_rect, right_rect # 读取测试图像 left_img cv2.imread(left_test.jpg) right_img cv2.imread(right_test.jpg) # 应用校正 left_rect, right_rect apply_rectification(left_img, right_img, left_map1, left_map2, right_map1, right_map2) # 并排显示 plt.figure(figsize(16, 8)) plt.subplot(121); plt.imshow(cv2.cvtColor(left_rect, cv2.COLOR_BGR2RGB)) plt.subplot(122); plt.imshow(cv2.cvtColor(right_rect, cv2.COLOR_BGR2RGB)) plt.show()良好的校正结果应满足左右图像的行对准绿色水平线应在同一物体特征上图像边缘区域无明显畸变有效区域最大化alpha参数控制6. 深度图生成与三维重建校正后的图像可以用于立体匹配生成深度图。以下是简单的BM算法实现def compute_disparity(left_rect, right_rect): # 转换为灰度图 left_gray cv2.cvtColor(left_rect, cv2.COLOR_BGR2GRAY) right_gray cv2.cvtColor(right_rect, cv2.COLOR_BGR2GRAY) # 创建StereoBM对象 stereo cv2.StereoBM_create(numDisparities64, blockSize15) # 计算视差图 disparity stereo.compute(left_gray, right_gray) # 归一化显示 disparity_vis cv2.normalize(disparity, None, alpha0, beta255, norm_typecv2.NORM_MINMAX, dtypecv2.CV_8U) return disparity, disparity_vis # 使用示例 disparity, disparity_vis compute_disparity(left_rect, right_rect) cv2.imshow(Disparity, disparity_vis) cv2.waitKey(0)对于更高质量的结果可以考虑使用SGBM算法def compute_disparity_sgbm(left_rect, right_rect): window_size 3 min_disp 0 num_disp 112 - min_disp stereo cv2.StereoSGBM_create( minDisparitymin_disp, numDisparitiesnum_disp, blockSize16, P18*3*window_size**2, P232*3*window_size**2, disp12MaxDiff1, uniquenessRatio10, speckleWindowSize100, speckleRange32 ) disparity stereo.compute(left_rect, right_rect).astype(np.float32)/16.0 return disparity在实际项目中我们发现以下几个参数调整技巧特别有用调整numDisparities时应该是16的整数倍blockSize取奇数通常在3-15之间预处理图像直方图均衡化可以改善匹配效果

更多文章