已知相机内外参,3D点投影2D图像出错及坐标系疑问
问题
已知一张图像的相机内参(K)和外参(旋转矩阵R、平移向量t),需要将3D点(X,Y,Z)投影到图像上得到像素坐标(u,v),已知该点的正确像素坐标为(5126, 1482)。
按照常规投影步骤(先转换到相机坐标系,计算x'、y'再推导图像坐标)得到的结果偏差极大:u: [9050.37868283],v: [1490.14136818]。
相关代码如下:
import numpy as np np.set_printoptions(suppress=True) class NadirImage(): def __init__(self): self.name = 'Leica CityMapper-2_96616_S150051-146040_MFC150_Backward' self.width = 14192 self.height = 10640 # ----------------------- Camera matrix -------------------------------------- self.pixel_size_mm = 0.00376 self.focal_length_mm = 112 self.focal_length = self.focal_length_mm / self.pixel_size_mm self.xo = 7095.5 self.yo = 5319.5 self.Camera_matrix = np.array([[self.focal_length, 0., self.xo ], [ 0., self.focal_length, self.yo ], [ 0., 0., 1. ]], dtype=np.float64) # --------------------------------------------------------------------------- # ----------------------- Rotation Matrix ----------------------------------- self.omega_deg = 0.09444 self.phi_deg = -0.02652 self.kappa_deg = -92.21505 self.Rotation_matrix = self.rotation_matrix() # --------------------------------------------------------------------------- # ----------------------- Translation Vector -------------------------------- self.X = 409971.705 self.Y = 4201193.819 self.Z = 1480.099 self.Translation_vector = np.array([[self.X], [self.Y], [self.Z]]) # --------------------------------------------------------------------------- def rotation_matrix(self): # Convert degrees to radians omega = np.radians(self.omega_deg) phi = np.radians(self.phi_deg) kappa = np.radians(self.kappa_deg) # Rotation matrix around X-axis R_x = np.array([[1, 0, 0], [0, np.cos(omega), np.sin(omega)], [0, -np.sin(omega), np.cos(omega)]]) # Rotation matrix around Y-axis R_y = np.array([[ np.cos(phi), 0, -np.sin(phi)], [ 0, 1, 0], [ np.sin(phi), 0, np.cos(phi)]]) # Rotation matrix around Z-axis R_z = np.array([[ np.cos(kappa), np.sin(kappa), 0], [ -np.sin(kappa), np.cos(kappa), 0], [ 0, 0, 1]]) # Combined rotation matrix Rotation_matrix = R_z @ R_y @ R_x return Rotation_matrix # Create the NadirImage instance image = NadirImage() test_point = np.array([[410163.605], #uv: 5126.559809474944, 1482.3423009603664 [4201284.701], [20.58]]) R = image.Rotation_matrix t = image.Translation_vector point_on_camera = R @ (test_point - t) x, y, z, = point_on_camera[0], point_on_camera[1], point_on_camera[2] #print the point on the camera coordinate system print(f'Point on the camera coordinate system: {x[0], y[0], z[0]}') # Calculate x_prime and y_prime x_prime = x/z y_prime = y/z print('x_prime:', x_prime) print('y_prime:', y_prime, '\n') # Calculate image coordinates u = image.focal_length * x_prime + image.xo v = image.focal_length * y_prime + image.yo print('image.focal_length:', image.focal_length) print('image.xo:', image.xo) print('image.yo:', image.yo) print('u:', u) print('v:', v)
编辑补充:修改以下计算方式后结果正确,但不明白对应的坐标系含义:
x_prime = x/-z y_prime = y/z
问题分析与解答
核心问题是这台相机的坐标系轴向约定和常规计算机视觉标准不一致,具体体现在Z轴方向:
常规计算机视觉的相机坐标系约定
- 相机光轴指向场景方向,Z轴向外(远离相机、朝向场景),只有Z为正的点才在相机视野内,投影公式为
x' = x/z、y' = y/z,对应像素坐标系u轴向右、v轴向下的存储逻辑。
- 相机光轴指向场景方向,Z轴向外(远离相机、朝向场景),只有Z为正的点才在相机视野内,投影公式为
你的相机的特殊约定
- 从修改
x' = x/-z能得到正确结果可以推断:这台Leica CityMapper-2的相机坐标系Z轴是向内的(朝向相机自身),场景中的3D点转换到相机坐标系后,Z值为负数。 - 常规公式用
x/z会因为Z为负导致x'符号反转,最终u坐标偏离正确值;x/-z相当于修正符号,让投影后的x'符合像素坐标系的轴向要求。 - y方向无需修改符号,说明相机坐标系Y轴的约定和常规一致,和像素v轴的映射方向匹配。
- 从修改
这个修改本质是修正了相机Z轴方向与常规投影公式的不匹配,确保投影坐标的符号符合像素坐标系的定义。
内容的提问来源于stack exchange,提问作者mkr
相关产品推荐
相关产品推荐

