在现代摄影测量技术中,将图像坐标转换为真实世界坐标是一项重要任务。这涉及到相机内参矩阵、外参矩阵的计算,以及各种坐标系之间的转换。本文将详细介绍如何使用Python进行这些转换,并逐步讲解相关的知识点和代码实现。
一、相机内参矩阵的计算
1.1 什么是相机内参矩阵?
相机内参矩阵(Intrinsic Matrix)用于描述相机的内部特性,包括焦距、主点位置(光学中心)以及像素尺寸等。这些参数帮助我们将图像坐标与相机坐标系联系起来。
1.2 所需参数
我们需要以下参数来计算相机内参矩阵:
focal_length = 35 # 焦距为35mm
sensor_width = 35.9 # 传感器宽度为35.9mm
sensor_height = 24 # 传感器高度为24mm
resolution_x = 8192 # 图像水平分辨率
resolution_y = 5460 # 图像垂直分辨率
principal_point_x = 4077.9153
principal_point_y = 2759.38247
1.3 计算每个像素的宽度和高度
首先,计算每个像素的宽度和高度:
pixel_width = sensor_width / resolution_x
pixel_height = sensor_height / resolution_y
接下来,定义相机内参矩阵K:
a = np.array([focal_length / pixel_width, 0, principal_point_x, 0]) # 第一行
b = np.array([0, focal_length / pixel_height, principal_point_y, 0]) # 第二行
c = np.array([0, 0, 1, 0]) # 第三行
K = np.array([a, b, c])
print("相机内参矩阵 K:")
print(K)
此矩阵用于将图像平面的坐标转换为相机坐标系下的坐标。
二、相机外参矩阵的计算
2.1 什么是相机外参矩阵?
相机外参矩阵(Extrinsic Matrix)用于描述相机在世界坐标系中的位置和姿态。它包括旋转矩阵和平移向量,帮助我们将相机坐标系下的坐标转换为世界坐标系下的坐标。
2.2 指定相机姿态和地理位置
我们需要指定相机的偏航角、俯仰角和横滚角,以及相机的位置(LBH坐标:经度、纬度、高度):
yaw = -75.8
pitch = -89.9
roll = 0
longitude = 112.772631094
latitude = 37.768497756
height = 96.8736
2.3 地理坐标转换
首先,将地理坐标转换为弧度:
longitude1 = 117 # 中央子午线
a = 6378137.0
f = 1 / 298.257222101
iPI = 0.0174532925199433 # 3.1415926535898/180.0
longitude0 = longitude1 * iPI
longitude1 = longitude * iPI
latitude1 = latitude * iPI
2.4 计算投影坐标
进行地理坐标到投影坐标的转换,并计算xval和yval:
e2 = 2 * f - f**2
ee = e2 * (1.0 - e2)
NN = a / np.sqrt(1.0 - e2 * np.sin(latitude1)**2)
T = np.tan(latitude1)**2
C = ee * np.cos(latitude1)**2
A = (longitude1 - longitude0) * np.cos(latitude1)
M = a * ((1 - e2 / 4 - 3 * e2**2 / 64 - 5 * e2**3 / 256) * latitude1 -
(3 * e2 / 8 + 3 * e2**2 / 32 + 45 * e2**3 / 1024) * np.sin(2 * latitude1) +
(15 * e2**2 / 256 + 45 * e2**3 / 1024) * np.sin(4 * latitude1) -
(35 * e2**3 / 3072) * np.sin(6 * latitude1))
xval = NN * (A + (1 - T + C) * A**3 / 6 + (5 - 18 * T + T**2 + 72 * C - 58 * ee) * A**5 / 120)
yval = M + NN * np.tan(latitude1) * (A**2 / 2 + (5 - T + 9 * C + 4 * C**2) * A**4 / 24 +
(61 - 58 * T + T**2 + 600 * C - 330 * ee) * A**6 / 720)
X0 = 500000
Y0 = 0
xval = xval + X0
yval = yval + Y0
radius = a / np.sqrt(1 - e2 * np.sin(latitude1)**2)
height = height + radius
X = xval
Y = yval
Z = height
print("转换后的坐标:")
print(f"X: {X}, Y: {Y}, Z: {Z}")
2.5 计算旋转矩阵
定义函数以计算旋转矩阵:
def calculate_rotation_matrix(yaw, pitch, roll):
yaw = yaw * np.pi / 180
pitch = pitch * np.pi / 180
roll = roll * np.pi / 180
Rz = np.array([[np.cos(yaw), -np.sin(yaw), 0],
[np.sin(yaw), np.cos(yaw), 0],
[0, 0, 1]])
Ry = np.array([[np.cos(pitch), 0, np.sin(pitch)],
[0, 1, 0],
[-np.sin(pitch), 0, np.cos(pitch)]])
Rx = np.array([[1, 0, 0],
[0, np.cos(roll), -np.sin(roll)],
[0, np.sin(roll), np.cos(roll)]])
R = np.dot(np.dot(Rz, Ry), Rx)
return R
R = calculate_rotation_matrix(yaw, pitch, roll)
T = np.array([[X, Y, Z]])
T = np.transpose(T)
camera_extrinsics = np.hstack((R, T))
三、坐标转换
3.1 定义坐标转换函数
定义函数将图像坐标转换为世界坐标:
def inverse_project(u, v, z_c, K, R, T):
camera_matrix = K
extrinsics_matrix = np.hstack((R, T))
extrinsics_matrix = np.vstack((extrinsics_matrix, [0, 0, 0, 1]))
uv_homogeneous = np.array([u, v, 1])
M = np.dot(K, extrinsics_matrix)
coefficient_matrix = M[:, :3]
column_vector = M[:, 3]
Right = (uv_homogeneous * z_c) - column_vector
world_coords = np.linalg.solve(coefficient_matrix, Right)
return world_coords
u = resolution_x / 2
v = resolution_y / 2
z_c = 96.8736100
world_coords = inverse_project(u, v, z_c, K, R, T)
print("世界坐标:")
print(world_coords)
3.2 解释坐标转换过程
这个函数inverse_project
将图像坐标(u, v)转换为世界坐标。首先,它构建了一个扩展的相机矩阵,然后使用线性代数方法解方程以获得世界坐标。
四、CGS2000坐标转换为WGS84坐标
4.1 定义转换函数
定义将CGS2000坐标转换为WGS84坐标的函数:
def CGS2000ToWGS84(x, y):
output = np.zeros(2)
longitude1 = 117 # 中央子午线 根据实际进行配置
a = 6378137.0
f = 1 / 298.257222101
iPI = 0.0174532925199433 # 3.1415926535898/180.0
X0 = 500000
Y0 = 0
xval = x - X0
yval = y - Y0
longitude0 = longitude1 * iPI
e2 = 2 * f - f**2
M = yval
u = M / (a * (1 - e2 / 4 - 3 * e2**2 / 64 - 5 * e2**3 / 256))
e1 = (1 - np.sqrt(1 - e2)) / (1 + np.sqrt(1 - e2))
e1_2 = e1**2 / (1 - e1**2)
fai_u = u + (3 * e1 / 2 - 27 * e1**3 / 32) * np.sin(2 * u) + (21 * e1**2 / 16 - 55 * e1**4 / 32) * np.sin(4 * u) + \
(151 * e1**3 / 96) * np.sin(6 * u) + (1097 * e1**4 / 512) * np.sin(8 * u)
C1 = np.sqrt(1 + e1_2 * np.cos(fai_u)**2)
T1 = np.tan(fai_u)**2
R1 = a * (1 - e2) / (C1**3)
N1 = a / C1
D = xval / N1
latitude1 = fai_u - (N1 * np.tan(fai_u) / R1) * \
(D**2 / 2 - (5 + 3 * T1 + 10 * C1 - 4 * C1**2 - 9 * e1_2) * D**4 / 24 +
(61 + 90 * T1 + 298 * C1 + 45 * T1**2 - 252 * e1_2 - 3 * C1**2) * D**6 / 720)
longitude1 = longitude0 + (D - (1 + 2 * T1 + C1) * D**3 / 6 +
(5 - 2 * C1 + 28 * T1 - 3 * C1**2 + 8 * e1_2 + 24 * T1**2) * D**5 / 120) / np.cos(fai_u)
# 转换为度
output[0] = longitude1 / iPI
output[1] = latitude1 / iPI
return output
本文详细介绍了如何计算相机的内参矩阵和外参矩阵,并进行了图像坐标到世界坐标的转换。最后,展示了如何将CGS2000坐标转换为WGS84坐标。这些过程对于从图像获取地理信息、进行三维重建等应用至关重要。通过Python编程实现这些转换,能够使我们更好地理解和应用摄影测量技术。
如果您对YOLOv8模型的改进和深度学习技术感兴趣,欢迎关注我的微信公众号 "AI代码 Insights"。在这里,我会定期分享最新的人工智能技术、深度学习算法和实践经验,与大家共同探讨AI领域的前沿动态。同时需要实现代码的可以通过公众号来找我要。
4o