如何在OpenCV Python中获取适配不同距离的跨相机Homography矩阵以叠加图像?
不同距离下RGB与DVS图像匹配解决方案
问题本质
单应性(Homography)矩阵只适用于两种场景:
- 拍摄目标是严格平面
- 相机只有旋转,没有平移/距离变化
当拍摄距离改变时,相机存在平移,三维场景的投影会产生透视差异,单应性矩阵没法建模这种和深度相关的变换,所以靠多组距离的棋盘格求解单应性根本没用。
正确解决思路:双目联合标定+透视矫正
要实现任意距离下的图像匹配,必须先完成两台相机的联合标定,拿到各自的内参、畸变系数,以及相机之间的相对位姿(旋转矩阵R、平移向量T),之后通过透视矫正把两台相机的图像对齐到同一平面,就能完美叠加。
步骤1:双目联合标定
用棋盘格拍多组不同姿态(不同距离、角度)的图像,用OpenCV的stereoCalibrate完成标定:
import cv2 import numpy as np # 棋盘格参数:格子数量(内角点)、单格实际尺寸(单位:米) chessboard_size = (8, 5) square_size = 0.02 # 生成世界坐标系下的棋盘格点 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) * square_size # 存储标定用的点集 obj_points = [] # 世界坐标系点 rgb_img_points = [] # RGB相机像素点 dvs_img_points = [] # DVS相机像素点 # 遍历所有标定图像对 for rgb_path, dvs_path in zip(rgb_path_list, dvs_path_list): rgb_img = cv2.imread(rgb_path) dvs_img = cv2.imread(dvs_path) rgb_gray = cv2.cvtColor(rgb_img, cv2.COLOR_BGR2GRAY) dvs_gray = cv2.cvtColor(dvs_img, cv2.COLOR_BGR2GRAY) # 检测棋盘格角点 ret_rgb, corners_rgb = cv2.findChessboardCorners(rgb_gray, chessboard_size, cv2.CALIB_CB_ADAPTIVE_THRESH) ret_dvs, corners_dvs = cv2.findChessboardCorners(dvs_gray, chessboard_size, cv2.CALIB_CB_ADAPTIVE_THRESH) if ret_rgb and ret_dvs: # 亚像素优化角点,提升标定精度 corners_rgb = cv2.cornerSubPix(rgb_gray, corners_rgb, (11,11), (-1,-1), (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)) corners_dvs = cv2.cornerSubPix(dvs_gray, corners_dvs, (11,11), (-1,-1), (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)) obj_points.append(objp) rgb_img_points.append(corners_rgb) dvs_img_points.append(corners_dvs) # 执行双目标定 ret, mtx_rgb, dist_rgb, mtx_dvs, dist_dvs, R, T, E, F = cv2.stereoCalibrate( obj_points, rgb_img_points, dvs_img_points, None, None, None, None, rgb_gray.shape[::-1], criteria=(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-5), flags=cv2.CALIB_FIX_INTRINSIC # 如果已经单独标定过内参,改成CALIB_USE_INTRINSIC_GUESS )
步骤2:透视矫正与图像叠加
利用标定得到的参数,把两台相机的图像矫正到同一平面,之后直接叠加即可:
# 获取图像尺寸 h_rgb, w_rgb = rgb_img.shape[:2] h_dvs, w_dvs = dvs_img.shape[:2] # 计算双目矫正参数 R1, R2, P1, P2, Q, roi1, roi2 = cv2.stereoRectify( mtx_rgb, dist_rgb, mtx_dvs, dist_dvs, (w_rgb, h_rgb), R, T, alpha=1.0 # alpha=1保留所有像素,alpha=0只保留有效区域 ) # 生成矫正映射表 map_rgb_x, map_rgb_y = cv2.initUndistortRectifyMap(mtx_rgb, dist_rgb, R1, P1, (w_rgb, h_rgb), cv2.CV_32FC1) map_dvs_x, map_dvs_y = cv2.initUndistortRectifyMap(mtx_dvs, dist_dvs, R2, P2, (w_rgb, h_rgb), cv2.CV_32FC1) # 对图像进行矫正 rgb_rectified = cv2.remap(rgb_img, map_rgb_x, map_rgb_y, cv2.INTER_LINEAR) dvs_rectified = cv2.remap(dvs_img, map_dvs_x, map_dvs_y, cv2.INTER_LINEAR) # 叠加显示 dst = cv2.addWeighted(rgb_rectified, 0.5, dvs_rectified, 0.5, 0) cv2.imshow('Aligned Overlay', dst) cv2.waitKey(0)
重要提示
- 标定图像要够多:至少拍10组不同姿态的图像,棋盘格要出现在画面的不同位置、不同角度,避免标定误差过大。
- 畸变必须先矫正:不管是单相机还是双目,畸变都会严重影响匹配精度,必须在标定后进行矫正。
- 三维场景的特殊处理:如果拍摄的是复杂三维场景,仅靠矫正还不够,需要结合深度信息(比如通过双目匹配生成深度图)才能让RGB图像精准投影到DVS视角。
内容的提问来源于stack exchange,提问作者Robin
相关产品推荐
相关产品推荐

