如何基于OpenCV remap预计算映射图计算校正图像边界框角点坐标?
鱼眼图像校正后边界框角点坐标计算方案
问题背景
用180°鱼眼镜头采集图像,先检测目标得到边界框,触发事件时通过OpenCV的remap函数结合预计算映射图校正图像,代码及参数如下:
map1, map2 = cv2.fisheye.initUndistortRectifyMap(K, D, np.eye(3), P, DIM, cv2.CV_16SC2) undistorted_img = cv2.remap(img, map1, map2, interpolation=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT)
参数:
K = [[169.53432726 0. 322.5714669 ] [ 0. 169.39413982 289.12455088] [ 0. 0. 1. ]] D = [[ 0.11919361] [ 0.29904975] [-0.20295709] [ 0.05526085]] P = [[121.41853905 0. 322.57040373] [ 0. 121.31813842 289.11177491] [ 0. 0. 1. ]] DIM = (640, 640)
需求:基于预计算的x、y映射图,高效计算校正图像中对应边界框的角点坐标。此前尝试相关方法未得到正确结果。
可行解决方案
方法一:基于鱼眼畸变模型直接计算(推荐)
利用已有的内参、畸变系数和投影矩阵,直接对原始图像的边界框角点进行坐标转换,无需依赖预计算映射图,效率高且准确。
步骤说明
- 像素坐标转归一化相机坐标:去除内参影响,得到归一化坐标
- 鱼眼畸变校正:用OpenCV鱼眼模型(θ多项式)校正归一化坐标
- 归一化坐标转校正图像像素坐标:通过投影矩阵P得到最终校正后的像素坐标
代码实现
import numpy as np # 你的参数(转为numpy数组方便计算) K = np.array([[169.53432726, 0., 322.5714669], [0., 169.39413982, 289.12455088], [0., 0., 1.]]) D = np.array([[0.11919361], [0.29904975], [-0.20295709], [0.05526085]]) P = np.array([[121.41853905, 0., 322.57040373], [0., 121.31813842, 289.11177491], [0., 0., 1.]]) def original_to_undistorted(x, y): # 1. 转换为归一化坐标 x_norm = (x - K[0,2]) / K[0,0] y_norm = (y - K[1,2]) / K[1,1] # 2. 鱼眼畸变校正 r = np.sqrt(x_norm**2 + y_norm**2) if r == 0: x_norm_undist, y_norm_undist = 0, 0 else: theta = np.arctan(r) theta_d = theta * (1 + D[0]*theta**2 + D[1]*theta**4 + D[2]*theta**6 + D[3]*theta**8) x_norm_undist = (theta_d / r) * x_norm y_norm_undist = (theta_d / r) * y_norm # 3. 投影到校正图像像素坐标 u = P[0,0] * x_norm_undist + P[0,2] v = P[1,1] * y_norm_undist + P[1,2] return round(u, 2), round(v, 2) # 示例:原始图像边界框角点转换 original_corners = [(100, 100), (500, 100), (500, 500), (100, 500)] undistorted_corners = [original_to_undistorted(x, y) for x, y in original_corners] # 生成校正后的边界框 u_min, u_max = min([u for u, v in undistorted_corners]), max([u for u, v in undistorted_corners]) v_min, v_max = min([v for u, v in undistorted_corners]), max([v for u, v in undistorted_corners]) print(f"校正后边界框范围:({u_min}, {v_min}) 到 ({u_max}, {v_max})")
方法二:基于现有映射图构建反向索引
如果必须依赖预计算的map1和map2,可以构建反向映射表,实现原始点到校正点的快速查找。
步骤说明
- 遍历校正后图像的所有像素(u, v),读取
map1[v][u]和map2[v][u]得到对应的原始图像坐标(x, y) - 用字典存储反向映射:键为(x, y)元组,值为所有对应校正后坐标(u, v)的列表
- 查找原始边界框角点对应的校正后坐标集合,取极值得到边界框范围
代码实现(简化版)
import numpy as np import cv2 # 假设已经生成map1和map2 # map1, map2 = cv2.fisheye.initUndistortRectifyMap(K, D, np.eye(3), P, DIM, cv2.CV_16SC2) # 构建反向映射字典 reverse_map = {} h, w = DIM for v in range(h): for u in range(w): x = map1[v][u] y = map2[v][u] key = (x, y) if key not in reverse_map: reverse_map[key] = [] reverse_map[key].append((u, v)) # 查找原始角点对应的校正后坐标 original_corner = (100, 100) if original_corner in reverse_map: undistorted_points = reverse_map[original_corner] # 处理多对一情况,取极值或均值 u_vals = [p[0] for p in undistorted_points] v_vals = [p[1] for p in undistorted_points] print(f"校正后对应坐标范围:({min(u_vals)}, {min(v_vals)}) 到 ({max(u_vals)}, {max(v_vals)})") else: print("该原始点无对应校正后坐标")
注意事项
- 方法一无需依赖映射图,单点点计算耗时极短,适合批量或实时处理场景,优先推荐。
- 方法二需预构建反向索引,占用一定内存,且部分原始像素可能无对应校正后坐标(因校正裁剪),需额外处理。
内容的提问来源于stack exchange,提问作者Mackie Messer
相关产品推荐
相关产品推荐

