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

关于OpenCV projectPoints函数参考点与坐标系的技术疑问

OpenCV projectPoints函数坐标系理解与投影问题

问题背景

研究OpenCV的projectPoints函数多日,始终搞不懂该函数的基础参考点,对输入的世界坐标系与参考点定义完全模糊。

基础示例的矛盾结果

编写了一个简单测试代码:

import numpy as np
import cv2 as cv
camera_matrix = np.array([
    [1062.39, 0., 943.93],
    [0., 1062.66, 560.88],
    [0., 0., 1.]
    ])

points_3d = xyz_to_np_point(10, 0, 0)
rvec = np.zeros((3, 1), np.float32)
tvec = np.zeros((3, 1), np.float32)
dist_coeffs = np.zeros((5, 1), np.float32)

points_2d, _ = cv.projectPoints(points_3d,
                                rvec, tvec,
                                camera_matrix,
                                dist_coeffs)

print(points_2d)

设置相机无旋转(rvec=(0,0,0)),且相机位置为世界原点(tvec=(0,0,0)),待投影的3D点预期在相机前方10单位处,期望输出(0,0),但实际输出为(11567.83, 560.88)。

实际船只投影需求与问题

真实需求是在图像中投影船只位置:

  • 已知相机和船只的GPS坐标,已转换为以东为X轴、以北为Y轴的平面坐标,船只3D点设为(X,Y,0)
  • 相机外参:以相机为世界参考,tvec仅考虑相机距海平面高度(tvec=(0,0,4)),通过IMU获取欧拉角转换为rvec(相机平行于XY平面)
  • 已完成相机标定,camera_matrix和dist_coeffs有效,畸变校正正常

但projectPoints输出结果完全不符合预期,同方向附近的船只投影结果差异极大。相关代码如下:

import numpy as np
import cv2 as cv
from scipy.spatial.transform import Rotation as R
from haversine import haversine, Unit

def distance_between_coordinates(c1, c2):
    x = haversine(c1, (c1[0], c2[1]), Unit.METERS)
    if c1[1] > c2[1]:
        x = -x
    y = haversine(c1, (c2[0], c1[1]), Unit.METERS)
    if c1[1] > c2[1]:
        y = -y
    dist = haversine(c1, c2, Unit.METERS)
    return dist, x, y


def rotvec_from_euler(orientation):
    r = R.from_euler('xyz', [orientation[0], orientation[1], orientation[2]], degrees=True)
    return r.as_rotvec()


if __name__ == '__main__':
    camera_matrix = np.array([
        [1062.39, 0., 943.93],
        [0., 1062.66, 560.88],
        [0., 0., 1.]
        ])

    dist_coeffs = np.array([-0.33520254,  0.14872426,  0.00057997, -0.00053154, -0.03600385])

    camera_p = (37.4543785, 126.59113666666666)
    ship_p = (37.448312, 126.5781)

    # 其他邻近船只坐标
    # ship_p = (37.450693, 126.577617)
    # ship_p = (37.4509, 126.58565)
    # ship_p = (37.448635, 126.578202)
    camera_orientation = (206.6925, 0, 0) # 欧拉角姿态

    rvec = rotvec_from_euler(camera_orientation)
    tvec = np.zeros((3, 1), np.float32)

    _, x, y = distance_between_coordinates(camera_p, ship_p)
    points_3d = np.array([[[x, y, 0]]], np.float32)

    points_2d, _ = cv.projectPoints(points_3d,
                                    rvec, tvec,
                                    camera_matrix,
                                    dist_coeffs)
    print(points_2d)

坐标系示意图

坐标系示意图


问题根源与解决方法

1. 基础示例的错误原因

OpenCV的projectPoints遵循相机坐标系定义:

  • 相机坐标系Z轴为向前(朝向拍摄场景),X轴向右,Y轴向下
  • 设置的points_3d = (10,0,0)是世界坐标系X轴10单位处,当rvec和tvec为0时,世界坐标系与相机坐标系重合,这个点在相机右侧10单位,而非前方
  • 计算过程:相机内参fx=1062.39,cx=943.93,投影x坐标为 fx*(X/Z) + cx = 1062.39*(10/1) + 943.93 = 11567.83,与实际输出一致
  • 正确的前方点应为(0,0,10),此时投影x坐标为0 + 943.93 = 943.93,y坐标为0 + 560.88 = 560.88(对应图像中心,cx、cy为主点坐标)

2. 实际船只投影的核心问题

(1)坐标系不匹配

自定义的世界坐标系为东X、北Y、海平面Z=0,但相机坐标系Z轴向前,两者转换关系未明确:

  • rvec转换可能错误:需确认IMU欧拉角的基准(航向角以北还是东为基准?)、旋转顺序是否与R.from_euler('xyz')匹配,以及航向角方向是否正确(比如206.69度是否对应相机朝向西南)
  • OpenCV的rvec是世界坐标系到相机坐标系的旋转向量,即相机姿态相对于世界坐标系的旋转,必须严格对应定义

(2)tvec定义错误

tvec是世界坐标系原点在相机坐标系中的位置,而非相机原点在世界坐标系中的位置。你设定“以相机为世界参考,tvec=(0,0,4)”不符合OpenCV外参定义,需根据世界坐标系与相机坐标系的位置关系重新计算。

(3)GPS转平面坐标的符号错误

distance_between_coordinates函数中y坐标的符号判断逻辑错误:

y = haversine(c1, (c2[0], c1[1]), Unit.METERS)
if c1[1] > c2[1]:
    y = -y

y是南北方向距离,应根据纬度差判断符号:若相机纬度c1[0] > c2[0](船只在相机南方),y应为负,而非用经度c1[1]判断。

3. 修正步骤总结

  • 统一所有坐标系定义:明确世界坐标系(东X、北Y、上Z)与相机坐标系(右X、下Y、前Z)的转换关系
  • 纠正基础认知:相机前方是Z轴正方向,而非X轴
  • 修正GPS转平面坐标的符号判断逻辑
  • 确认外参正确性:rvec是世界到相机的旋转,tvec是世界原点在相机坐标系中的位置,严格对应OpenCV定义
  • 用简单点验证:比如相机正前方的点,确认投影位置是否符合预期

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 00:45:56