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

OmniCV鱼眼相机fisheye2equirect步骤2结果异常及参数存储求助

OmniCV鱼眼转等距柱状投影参数调试问题

问题描述

使用Python调用OmniCV库处理鱼眼相机图像,预期复现官方参数GUI校准流程步骤2的正确输出效果,实际运行结果不符合预期,需要完成两个目标:

  • 输出正确的鱼眼到等距柱状投影转换结果
  • 准确将RADIUS、aperture、delx、dely参数写入fisheyeParams.txt文件,保证映射坐标准确

相关素材

  • 失败运行结果截图:鱼眼转换失败效果
  • 原始输入鱼眼图像:原始鱼眼输入素材

原始使用代码

import cv2
import numpy as np
from omnicv import fisheyeImgConv

def nothing(x):
    pass


WINDOW_NAME = "image"
Image_Path = './input/fisheye.png'

RADIUS = None

# Example of using the converter class
frame = cv2.imread(Image_Path)
frame = cv2.resize(frame, (800,800))
cv2.namedWindow(WINDOW_NAME, cv2.WINDOW_NORMAL)
cv2.resizeWindow(WINDOW_NAME, 800, 800)
N = 2 * max(frame.shape)
cv2.createTrackbar("radius", WINDOW_NAME, 0, N, nothing)
cv2.createTrackbar("Cx", WINDOW_NAME, N // 2, N, nothing)
cv2.createTrackbar("Cy", WINDOW_NAME, N // 2, N, nothing)

while True:
    if True:
        frame = cv2.imread(Image_Path)
        radius = cv2.getTrackbarPos("radius", WINDOW_NAME)
        Cx = cv2.getTrackbarPos("Cx", WINDOW_NAME)
        Cy = cv2.getTrackbarPos("Cy", WINDOW_NAME)
        frame = cv2.circle(frame, (Cx, Cy), radius, (0, 200, 0), 2)
        cv2.imshow(WINDOW_NAME, frame)
        if cv2.waitKey(1) & 0xFF == 27:
            RADIUS = radius
            cv2.destroyAllWindows()
            break

WINDOW_NAME = "set aperture"

cv2.namedWindow(WINDOW_NAME, cv2.WINDOW_NORMAL)
cv2.resizeWindow(WINDOW_NAME, 1200, 600)
cv2.createTrackbar("aperture", WINDOW_NAME, 0, 1000, nothing)
cv2.createTrackbar("del Cx", WINDOW_NAME, 500, 1000, nothing)
cv2.createTrackbar("del Cy", WINDOW_NAME, 500, 1000, nothing)

# Example of using the converter class

frame = cv2.imread(Image_Path)

frame = cv2.circle(frame, (frame.shape[1] // 2, frame.shape[0] // 2), 4, (255, 255, 255), -1)
frame = cv2.circle(frame, (frame.shape[1] // 2, frame.shape[0] // 2), 2, (0, 0, 0), -1)

outShape = [400,800]
inShape = frame.shape[:2]

mapper = fisheyeImgConv()
mapper.fisheye2equirect(frame, outShape, edit_mode=True)

while True:
    if True:
        aperture = cv2.getTrackbarPos("aperture", WINDOW_NAME)
        delx = cv2.getTrackbarPos("del Cx", WINDOW_NAME) - 500
        dely = cv2.getTrackbarPos("del Cy", WINDOW_NAME) - 500
        frame2 = mapper.fisheye2equirect(
            frame,
            outShape,
            aperture=aperture,
            delx=delx,
            dely=dely,
            radius=RADIUS,
            edit_mode=True)
        cv2.imshow("image3", frame2)

        frame2 = cv2.line(
            frame2,
            (int(frame2.shape[1] * 0.25), 0),
            (int(frame2.shape[1] * 0.25), int(frame2.shape[0])),
            (0, 180, 0),
            1,
        )
        frame2 = cv2.line(
            frame2,
            (int(frame2.shape[1] * 0.75), 0),
            (int(frame2.shape[1] * 0.75), int(frame2.shape[0])),
            (0, 180, 0),
            1,
        )
        frame2 = cv2.line(
            frame2,
            (0, int(frame2.shape[0] * 0.5)),
            (int(frame2.shape[1]), int(frame2.shape[0] * 0.5)),
            (0, 180, 0),
            1,
        )
        frame2 = cv2.line(
            frame2,
            (int(frame2.shape[1] * 0.5), 0),
            (int(frame2.shape[1] * 0.5), int(frame2.shape[0])),
            (0, 180, 0),
            1,
        )
        cv2.imshow(WINDOW_NAME, frame2)
        if cv2.waitKey(1) & 0xFF == 27:
            print("aperture : ", aperture)
            print("delx : ", delx)
            print("dely : ", dely)
            f = open("./input/fisheyeParams.txt", "w+")
            f.write(str(RADIUS) + "\n")
            f.write(str(aperture) + "\n")
            f.write(str(delx) + "\n")
            f.write(str(dely) + "\n")
            f.close()
            break
        frame2[:, :, :] = 0
        # cv2.imshow("image", frame2)

问题根因

代码存在5个核心错误,直接导致转换结果异常:

  • 第一步标定鱼眼圆时,初始读取的帧被resize到800*800,但循环内每次重新读取的原图没有做相同resize,导致画圆的坐标和实际图像尺寸不匹配,选出来的半径值完全错误
  • 第一步只保存了半径RADIUS,没有保存标定的鱼眼光心坐标Cx、Cy,第二步默认以图像中心为光心,和实际鱼眼圆位置存在偏移
  • aperture(鱼眼视场角)滑块范围设置错误,官方要求取值范围为0-180(单位:度),代码设置为0-1000,初始值为0时视场角为0,无法输出有效投影
  • 第二步循环末尾手动将frame2所有像素置0,同时额外创建了image3显示窗口,导致画面闪烁、显示内容错位
  • 第一步滑块最大值N设置为2倍resize后图像的长边,和循环内读取的未resize原图尺寸不匹配,拖动滑块时容易出现坐标越界报错

修正后可运行代码

import cv2
import numpy as np
from omnicv import fisheyeImgConv

def nothing(x):
    pass

# 配置参数
WINDOW_NAME_RADIUS = "标定鱼眼圆范围"
WINDOW_NAME_APERTURE = "标定视场与偏移"
IMAGE_PATH = './input/fisheye.png'
PARAM_SAVE_PATH = './input/fisheyeParams.txt'
RESIZE_STEP1 = (800, 800)
OUT_SHAPE = [400, 800]  # 等距柱状投影输出尺寸 高*宽

# 第一步:标定鱼眼圆半径与光心位置
frame_raw = cv2.imread(IMAGE_PATH)
frame_step1 = cv2.resize(frame_raw, RESIZE_STEP1)
h_resize, w_resize = frame_step1.shape[:2]
cv2.namedWindow(WINDOW_NAME_RADIUS, cv2.WINDOW_NORMAL)
cv2.resizeWindow(WINDOW_NAME_RADIUS, w_resize, h_resize)
max_slider = max(h_resize, w_resize)
# 初始化滑块:半径默认设为短边一半,光心默认在图像中心
cv2.createTrackbar("radius", WINDOW_NAME_RADIUS, min(h_resize, w_resize)//2, max_slider, nothing)
cv2.createTrackbar("Cx", WINDOW_NAME_RADIUS, w_resize//2, w_resize, nothing)
cv2.createTrackbar("Cy", WINDOW_NAME_RADIUS, h_resize//2, h_resize, nothing)

RADIUS = None
Cx = None
Cy = None
while True:
    frame_show = frame_step1.copy()
    radius = cv2.getTrackbarPos("radius", WINDOW_NAME_RADIUS)
    cx = cv2.getTrackbarPos("Cx", WINDOW_NAME_RADIUS)
    cy = cv2.getTrackbarPos("Cy", WINDOW_NAME_RADIUS)
    # 画鱼眼圆参考线
    cv2.circle(frame_show, (cx, cy), radius, (0, 200, 0), 2)
    cv2.circle(frame_show, (cx, cy), 3, (0,0,255), -1)
    cv2.imshow(WINDOW_NAME_RADIUS, frame_show)
    key = cv2.waitKey(1) & 0xFF
    if key == 27:  # ESC键确认第一步参数
        # 把resize后的坐标换算回原图尺寸
        scale = frame_raw.shape[1] / w_resize
        RADIUS = int(radius * scale)
        Cx = int(cx * scale)
        Cy = int(cy * scale)
        cv2.destroyAllWindows()
        break

# 第二步:标定视场角与光心偏移
cv2.namedWindow(WINDOW_NAME_APERTURE, cv2.WINDOW_NORMAL)
cv2.resizeWindow(WINDOW_NAME_APERTURE, 1200, 600)
# 修正滑块范围:aperture为0-180度,偏移范围-200到200像素
cv2.createTrackbar("aperture(度)", WINDOW_NAME_APERTURE, 180, 180, nothing)
cv2.createTrackbar("del Cx", WINDOW_NAME_APERTURE, 200, 400, nothing)
cv2.createTrackbar("del Cy", WINDOW_NAME_APERTURE, 200, 400, nothing)

mapper = fisheyeImgConv()
# 预计算映射表
mapper.fisheye2equirect(frame_raw, OUT_SHAPE, edit_mode=True)

while True:
    aperture = cv2.getTrackbarPos("aperture(度)", WINDOW_NAME_APERTURE)
    # 偏移初始值为0,滑块0对应-200,滑块400对应+200
    delx = cv2.getTrackbarPos("del Cx", WINDOW_NAME_APERTURE) - 200
    dely = cv2.getTrackbarPos("del Cy", WINDOW_NAME_APERTURE) - 200
    # 传入正确的光心坐标:基准Cx/Cy相对中心的偏移 + 微调偏移
    equi_frame = mapper.fisheye2equirect(
        frame_raw,
        OUT_SHAPE,
        aperture=aperture,
        delx=delx + (Cx - frame_raw.shape[1]//2),
        dely=dely + (Cy - frame_raw.shape[0]//2),
        radius=RADIUS,
        edit_mode=False
    )
    # 画等距柱状投影参考线
    h_equi, w_equi = equi_frame.shape[:2]
    cv2.line(equi_frame, (w_equi//4, 0), (w_equi//4, h_equi), (0,180,0), 1)
    cv2.line(equi_frame, (w_equi*3//4, 0), (w_equi*3//4, h_equi), (0,180,0), 1)
    cv2.line(equi_frame, (0, h_equi//2), (w_equi, h_equi//2), (0,180,0), 1)
    cv2.line(equi_frame, (w_equi//2, 0), (w_equi//2, h_equi), (0,180,0), 1)
    
    cv2.imshow(WINDOW_NAME_APERTURE, equi_frame)
    key = cv2.waitKey(1) & 0xFF
    if key == 27:  # ESC键确认参数并保存
        final_delx = delx + (Cx - frame_raw.shape[1]//2)
        final_dely = dely + (Cy - frame_raw.shape[0]//2)
        print(f"保存参数:RADIUS={RADIUS}, aperture={aperture}, delx={final_delx}, dely={final_dely}")
        with open(PARAM_SAVE_PATH, "w+", encoding="utf-8") as f:
            f.write(f"{RADIUS}\n{aperture}\n{final_delx}\n{final_dely}\n")
        cv2.destroyAllWindows()
        break

使用说明

  1. 运行代码后第一个窗口会显示鱼眼图,拖动三个滑块让绿色圆圈刚好贴合鱼眼图像的有效圆形边缘,红色点对准鱼眼光心,按ESC进入下一步
  2. 第二个窗口会显示实时转换的等距柱状投影图,拖动aperture(度)滑块调整视场角直到鱼眼圆边缘刚好填满投影的垂直范围,拖动del Cx/del Cy微调让参考线对齐场景中的水平/垂直线条,调整完成后按ESC,参数会自动写入指定的fisheyeParams.txt文件

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 16:36:39