关于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
相关产品推荐
相关产品推荐

