如何将Cube Map转换为Fisheye图像?OpenCV实现求助
从Cube Map全景图生成鱼眼图像(含D面外圈叠加)
核心原理
仿射/透视变换属于平面变换,无法模拟鱼眼的球面投影效果,必须用cv.remap实现像素级的球面→平面映射。核心逻辑是:给鱼眼图像的每个像素,计算它在Cube全景图中对应的原始像素坐标,通过预先生成的映射表完成重采样。
步骤1:明确Cube全景图的布局对应关系
你拼接的全景图是3×3网格(单Cube面尺寸为face_size x face_size),各面位置:
- 第二行中间:U面(朝上,对应球顶)
- 第二行左侧:F面(朝前)
- 第二行右侧:B面(朝后)
- 第一行中间:L面(朝左)
- 第三行中间:R面(朝右)
- D面单独处理,用来填充鱼眼外圈
步骤2:完整实现代码
下面的代码包含映射表生成、全景图重映射、D面外圈叠加的全部逻辑:
import numpy as np import cv2 as cv # ---------------------- 配置参数 ---------------------- face_size = 512 # 单个Cube面的尺寸(确保是正方形) fisheye_size = 1024 # 生成的鱼眼图像尺寸(建议设为正方形) fisheye_radius = fisheye_size // 2 # 鱼眼有效视野半径 d_ring_width = face_size // 4 # D面外圈的宽度,可按需调整 # ---------------------- 读取并拼接Cube全景图 ---------------------- # 替换为你的本地图片路径 u = cv.rotate(cv.imread("u.jpg"), cv.ROTATE_90_CLOCKWISE) b = cv.rotate(cv.imread("b.jpg"), cv.ROTATE_90_COUNTERCLOCKWISE) l = cv.rotate(cv.imread("l.jpg"), cv.ROTATE_180) f = cv.rotate(cv.imread("f.jpg"), cv.ROTATE_90_CLOCKWISE) d = cv.imread("d.jpg") r = cv.imread("r.jpg") _ = np.zeros_like(r) panorama = np.vstack([ np.hstack([_, l, _]), np.hstack([f, u, b]), np.hstack([_, r, _]) ]) panorama_h, panorama_w = panorama.shape[:2] # ---------------------- 生成鱼眼映射表 ---------------------- map_x = np.zeros((fisheye_size, fisheye_size), dtype=np.float32) map_y = np.zeros((fisheye_size, fisheye_size), dtype=np.float32) cx = fisheye_size // 2 cy = fisheye_size // 2 for y in range(fisheye_size): for x in range(fisheye_size): dx = x - cx dy = y - cy r = np.sqrt(dx**2 + dy**2) # 超出鱼眼有效范围的像素先占位,后续用D面填充 if r > fisheye_radius: map_x[y, x] = 0 map_y[y, x] = 0 continue # 转换为球面坐标:θ为极角(0=球顶/鱼眼中心,π/2=赤道/鱼眼边缘),φ为方位角 theta = (r / fisheye_radius) * (np.pi / 2) phi = np.arctan2(dy, dx) # 计算球面点的3D坐标(单位球) x_sphere = np.sin(theta) * np.cos(phi) y_sphere = np.cos(theta) z_sphere = np.sin(theta) * np.sin(phi) # 根据绝对值最大的轴,确定对应的Cube面 max_axis = max(abs(x_sphere), abs(y_sphere), abs(z_sphere)) if max_axis == abs(y_sphere): # U面(朝上) face_x = (x_sphere / y_sphere + 1) / 2 * face_size face_y = (z_sphere / y_sphere + 1) / 2 * face_size map_x[y, x] = face_size + face_x map_y[y, x] = face_size + face_y elif max_axis == abs(x_sphere): if x_sphere > 0: # R面(朝右) face_x = (-z_sphere / x_sphere + 1) / 2 * face_size face_y = (y_sphere / x_sphere + 1) / 2 * face_size map_x[y, x] = face_size + face_x map_y[y, x] = 2 * face_size + face_y else: # L面(朝左) face_x = (z_sphere / x_sphere + 1) / 2 * face_size face_y = (y_sphere / x_sphere + 1) / 2 * face_size map_x[y, x] = face_size + face_x map_y[y, x] = 0 + face_y elif max_axis == abs(z_sphere): if z_sphere > 0: # F面(朝前) face_x = (x_sphere / z_sphere + 1) / 2 * face_size face_y = (-y_sphere / z_sphere + 1) / 2 * face_size map_x[y, x] = 0 + face_x map_y[y, x] = face_size + face_y else: # B面(朝后) face_x = (-x_sphere / z_sphere + 1) / 2 * face_size face_y = (-y_sphere / z_sphere + 1) / 2 * face_size map_x[y, x] = 2 * face_size + face_x map_y[y, x] = face_size + face_y # ---------------------- 生成基础鱼眼图像 ---------------------- fisheye_img = cv.remap(panorama, map_x, map_y, interpolation=cv.INTER_LINEAR) # ---------------------- 叠加D面到鱼眼外圈 ---------------------- d_face_size = d.shape[0] d_map_x = np.zeros((fisheye_size, fisheye_size), dtype=np.float32) d_map_y = np.zeros((fisheye_size, fisheye_size), dtype=np.float32) inner_radius = fisheye_radius - d_ring_width for y in range(fisheye_size): for x in range(fisheye_size): dx = x - cx dy = y - cy r = np.sqrt(dx**2 + dy**2) # 仅处理外圈区域 if not (inner_radius <= r <= fisheye_radius): continue # 将D面映射为环形:φ对应D面的x轴,r对应D面的y轴(从内到外) phi = np.arctan2(dy, dx) u_d = (phi + np.pi) / (2 * np.pi) * d_face_size v_d = (r - inner_radius) / d_ring_width * d_face_size d_map_x[y, x] = u_d d_map_y[y, x] = v_d # 生成D面环形图像并叠加 d_ring = cv.remap(d, d_map_x, d_map_y, interpolation=cv.INTER_LINEAR) mask = (d_map_x != 0) & (d_map_y != 0) fisheye_img[mask] = d_ring[mask] # 保存或显示结果 cv.imwrite("fisheye_with_d_ring.jpg", fisheye_img) cv.imshow("Fisheye Image", fisheye_img) cv.waitKey(0) cv.destroyAllWindows()
关键逻辑说明
- 映射表生成:遍历鱼眼每个像素,将其转换为球面坐标,再根据球面坐标落在Cube的对应面,计算该面在全景图中的像素位置,填充到
map_x和map_y。 - 重映射:用
cv.remap完成全景图到鱼眼的转换,INTER_LINEAR插值保证画质平滑。 - D面外圈叠加:单独生成D面的环形映射表,将D面拉伸为鱼眼的外圈,通过掩码精准替换鱼眼图像的对应区域。
注意事项
- 确保所有Cube面都是正方形,否则需要调整坐标计算逻辑。
- 修改
fisheye_radius可调整鱼眼视野范围(设为fisheye_size//2对应180度鱼眼)。 d_ring_width控制D面外圈的宽度,可根据视觉效果自行调整。
内容的提问来源于stack exchange,提问作者gokdumano
相关产品推荐
相关产品推荐

