You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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()

关键逻辑说明

  1. 映射表生成:遍历鱼眼每个像素,将其转换为球面坐标,再根据球面坐标落在Cube的对应面,计算该面在全景图中的像素位置,填充到map_x和map_y。
  2. 重映射:用cv.remap完成全景图到鱼眼的转换,INTER_LINEAR插值保证画质平滑。
  3. D面外圈叠加:单独生成D面的环形映射表,将D面拉伸为鱼眼的外圈,通过掩码精准替换鱼眼图像的对应区域。

注意事项

  • 确保所有Cube面都是正方形,否则需要调整坐标计算逻辑。
  • 修改fisheye_radius可调整鱼眼视野范围(设为fisheye_size//2对应180度鱼眼)。
  • d_ring_width控制D面外圈的宽度,可根据视觉效果自行调整。

内容的提问来源于stack exchange,提问作者gokdumano

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 14:41:26