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

cv2.omnidir.projectPoints两种投影路径返回结果不一致问题

全景相机标定中cv2.omnidir.projectPoints投影结果偏差问题

问题背景

使用棋盘格靶标开展全景相机与其他传感器的外参标定工作时,两次调用cv2.omnidir.projectPoints函数得到的返回结果存在明显不符合数学逻辑的差异。
测试了两种棋盘格外角点到图像的投影实现:

  • 实现1:直接将角点从棋盘格坐标系投影到图像坐标系
  • 实现2:先手动将角点从棋盘格坐标系转换到相机坐标系,再传入rvec、tvec均为零向量的参数完成到图像的投影

按照坐标变换的数学逻辑,两种投影路径的返回结果应当完全一致,但实际运行得到的像素坐标存在明显偏差。

测试代码与运行结果

import numpy as np
import cv2

# --- 相机参数
K = np.array([[939.265,   0.   , 965.693],
              [  0.   , 942.402, 645.578],
              [  0.   ,   0.   ,   1.   ]])
D = np.array([-0.156, -0.03 ,  0.   ,  0.001], dtype=float32)

# --- 棋盘格坐标系下的外角点坐标
outer_corners_bcs = np.array([[-0.175, -0.17 ,  0.   ],
                              [ 0.8  , -0.17 ,  0.   ],
                              [-0.175,  0.67 ,  0.   ],
                              [ 0.8  ,  0.67 ,  0.   ]])

# --- 棋盘格靶标到相机的旋转向量、平移向量
rvec = np.array([[-1.138, -1.421,  2.827]])
tvec = np.array([[-6.852, -5.473,  4.549]])

# --- 实现1:直接从棋盘格坐标系投影到图像
uv, _ = cv2.omnidir.projectPoints(outer_corners_bcs.reshape(1, -1, 3).astype('float64'),
                              rvec.reshape(1, 3),
                              tvec.reshape(1, 3), K, 1, D)

print(uv)
# 输出结果:
# [[[556.417 320.275]
# [504.347 320.397]
# [562.968 272.863]
# [509.031 272.395]]]

 # --- 实现2:先转换到相机坐标系再投影到图像
R = eulerAnglesToRotationMatrix(rvec)
outer_corners_ccs = (R @ outer_corners_bcs.T + tvec).T
uv, _ = cv2.omnidir.projectPoints(outer_corners_ccs.reshape(1, -1, 3),
                              np.zeros(3).reshape(1, 3),
                              np.zeros(3).reshape(1, 3), K, 1, D)
print(uv)
# 输出结果:
# [[[699.384 304.698]
# [639.438 180.463]
# [876.787 527.013]
# [752.581 334.091]]]

问题根因与解决方案

  • 核心错误:手动坐标变换环节的旋转矩阵转换逻辑错误。OpenCV标定类接口传入的rvec为罗德里格斯旋转向量,并非欧拉角,代码中调用的eulerAnglesToRotationMatrix以欧拉角为输入做转换,得到的旋转矩阵完全错误,直接导致相机坐标系下的点坐标计算偏差。
  • 修复方法:替换旋转矩阵转换逻辑,使用OpenCV官方的罗德里格斯变换接口计算旋转矩阵,修正后两种投影路径结果完全一致,对应修改代码如下:
# 错误写法:将rvec作为欧拉角转换旋转矩阵
# R = eulerAnglesToRotationMatrix(rvec)
# 正确写法:通过罗德里格斯公式从旋转向量计算旋转矩阵
R, _ = cv2.Rodrigues(rvec)
# 注意维度匹配,tvec转为列向量做广播运算
outer_corners_ccs = (R @ outer_corners_bcs.T + tvec.reshape(3, 1)).T
  • 额外注意事项:
    • 所有传入cv2.omnidir.projectPoints的数组统一使用float64类型,避免float32精度截断带来的微小误差
    • 矩阵运算时检查数组维度,避免广播逻辑错误导致的计算结果异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 06:57:14