You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

298 lines
10 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import math
import numpy as np
import cameramodel
def calc_distance(cameraModel, x: float, y: float) -> tuple[float, float]:
"""计算像素坐标 (x, y) 对应的世界坐标 (Xw, Yw)。
参数:
x, y: 像素坐标
cameraModel: 包含相机参数的类实例,需有以下属性:
principal_point: 主点坐标 (cx, cy)
pixel_size_x, pixel_size_y: 像素尺寸
rotation_camera: 相机旋转角度
focal_length: 焦距
rotation_alpha, rotation_beta: 旋转角度
height: 相机高度
返回:
世界坐标系下的 (Xw, Yw)
"""
# 1. 像素坐标转归一化平面坐标(考虑主点和旋转)
Xp = (x - cameraModel.principal_point[0]) * cameraModel.pixel_size_x
Yp = -(y - cameraModel.principal_point[1]) * cameraModel.pixel_size_y
# 2. 应用相机旋转
cos_rot = math.cos(cameraModel.rotation_camera)
sin_rot = math.sin(cameraModel.rotation_camera)
Xp_rotation = Xp * cos_rot + Yp * sin_rot
Yp_rotation = -Xp * sin_rot + Yp * cos_rot
# 3. 预计算常用中间变量
focal_sq = cameraModel.focal_length ** 2
Yp_sq = Yp_rotation ** 2
sqrt_focal_Yp = math.sqrt(focal_sq + Yp_sq)
# 4. 计算角度 eta 和 seta
eta = math.atan2(Yp_rotation, cameraModel.focal_length) # 改用 atan2 避免除零
tan_alpha = math.tan(cameraModel.rotation_alpha)
tan_beta = math.tan(cameraModel.rotation_beta)
seta = math.atan2(1, math.sqrt(tan_beta ** 2 + 1 / tan_alpha ** 2))
# 5. 计算距离 d 和世界坐标
seta_eta = seta + eta
OM = cameraModel.height / math.tan(seta_eta)
MP = cameraModel.height * abs(Xp_rotation) / (sqrt_focal_Yp * math.sin(seta_eta))
# epsilon = math.atan2(MP, OM) # 改用 atan2 提高数值稳定性
epsilon = math.atan((cameraModel.height * Xp_rotation / (sqrt_focal_Yp * math.sin(seta_eta))) / OM)
d = math.hypot(MP, OM) # 等价于 sqrt(MP**2 + OM**2),但更高效
gamma = math.atan2(1, tan_alpha * tan_beta)
# 6. 计算最终世界坐标
Xw = d * math.cos(gamma + epsilon)
Yw = -d * math.sin(gamma + epsilon)
return Xw*cameraModel.zoom, Yw*cameraModel.zoom
def calc_distance2(cameraModel, Xw: float, Yw: float) -> tuple[int, int]:
"""从世界坐标 (Xw, Yw) 反推像素坐标 (x, y)。
参数:
Xw, Yw: 世界坐标系下的坐标
cameraModel: 包含相机参数的类实例,需有以下属性:
rotation_alpha, rotation_beta: 旋转角度
focal_length (f): 焦距
height (H): 相机高度
rotation_camera (q): 相机旋转角度
pixel_size_x (dx), pixel_size_y (dy): 像素尺寸
principal_point (u0, v0): 主点坐标
返回:
像素坐标 (x, y),类型为整数
"""
# 1. 计算距离 d 和角度 seta、gamma
d = math.hypot(Xw/cameraModel.zoom, Yw/cameraModel.zoom)
tan_alpha = math.tan(cameraModel.rotation_alpha)
tan_beta = math.tan(cameraModel.rotation_beta)
seta = math.atan2(1, math.sqrt(tan_beta ** 2 + 1 / tan_alpha ** 2))
gamma = math.atan2(1, tan_alpha * tan_beta) # 用 atan2 避免除零错误
# 2. 计算组合角度 epsilon调整到 [-π, π]
angle = math.atan2(-Yw, Xw) # 使用 atan2 处理所有象限
epsilon = (angle - gamma + math.pi) % (2 * math.pi) - math.pi # 规范化角度
# 3. 计算 OM 和 MP
OM = abs(d * math.cos(epsilon))
MP = abs(d * math.sin(epsilon))
# 4. 计算 eta 和 Yp1、Xp1
eta = math.atan2(cameraModel.height, OM) - seta # 替换 H
Yp_rotation = math.tan(eta) * cameraModel.focal_length # 替换 f
sqrt_term = math.sqrt(cameraModel.focal_length ** 2 + Yp_rotation ** 2)
sin_term = math.sin(seta + eta)
Xp_rotation = MP * sqrt_term * sin_term / cameraModel.height # 替换 H
Xp_rotation = Xp_rotation if epsilon > 0 else -Xp_rotation # 简化条件判断
# 5. 应用相机旋转(逆旋转)
cos_q = math.cos(-cameraModel.rotation_camera) # 替换 q
sin_q = math.sin(-cameraModel.rotation_camera)
Xp = Xp_rotation * cos_q + Yp_rotation * sin_q
Yp = -Xp_rotation * sin_q + Yp_rotation * cos_q
# 6. 转换为像素坐标
x = int(Xp / cameraModel.pixel_size_x + cameraModel.principal_point[0]) # 替换 dx, u0
y = int(-Yp / cameraModel.pixel_size_y + cameraModel.principal_point[1]) # 替换 dy, v0
return x, y
def calc_height(
cameraModel, # 包含相机参数的类实例
x_bot: float,
y_bot: float,
x_top: float,
y_top: float,
) -> float:
"""计算物体高度 Zw。
参数:
x_bot, y_bot: 物体底部像素坐标
x_top, y_top: 物体顶部像素坐标
cameraModel: 包含相机参数的类实例,需有以下属性:
rotation_alpha, rotation_beta: 旋转角度
principal_point: 主点坐标 (u0, v0)
pixel_size_x, pixel_size_y: 像素尺寸 (dx, dy)
rotation_camera: 相机旋转角度 (q)
focal_length: 焦距 (f)
height: 相机高度 (H)
返回:
物体高度 Zw
"""
# 预计算常用值和三角函数
cos_q = math.cos(cameraModel.rotation_camera)
sin_q = math.sin(cameraModel.rotation_camera)
tan_alpha = math.tan(cameraModel.rotation_alpha)
tan_beta = math.tan(cameraModel.rotation_beta)
# 1. 底部点计算
# 像素坐标转归一化坐标
Xp_bot = (x_bot - cameraModel.principal_point[0]) * cameraModel.pixel_size_x
Yp_bot = -(y_bot - cameraModel.principal_point[1]) * cameraModel.pixel_size_y
# 应用相机旋转
Xp_bot_rotation = Xp_bot * cos_q + Yp_bot * sin_q
Yp_bot_rotation = -Xp_bot * sin_q + Yp_bot * cos_q
# 计算角度和距离
eta_bot = math.atan2(Yp_bot_rotation, cameraModel.focal_length)
seta = math.atan2(1, math.sqrt(tan_beta ** 2 + 1 / tan_alpha ** 2))
# 计算OM
OM = cameraModel.height / math.tan(seta + eta_bot)
# 2. 顶部点计算
# 像素坐标转归一化坐标
Xp_top = (x_top - cameraModel.principal_point[0]) * cameraModel.pixel_size_x
Yp_top = -(y_top - cameraModel.principal_point[1]) * cameraModel.pixel_size_y
# 应用相机旋转
Xp_top_rotation = Xp_top * cos_q + Yp_top * sin_q
Yp_top_rotation = -Xp_top * sin_q + Yp_top * cos_q
# 计算角度
eta_top = math.atan2(Yp_top_rotation, cameraModel.focal_length)
# 3. 计算高度
Zw = cameraModel.height - OM * math.tan(seta + eta_top)
return Zw
"""
计算图像中车辆坐标系Xw、Yw所对应的轴线
参数:
返回值轴线上点在图像中的二维坐标x、y
"""
def calc_zeros_yto0(cameraModel, val):
# 定义搜索范围和步长
x_range = np.linspace(0, 1280, 2560)
y_range = np.linspace(0, 960, 1920)
# 存储解
solutions = []
tolerance = 1e-3 # 容忍误差
# 网格搜索
for x in x_range:
for y in y_range:
error = equations_yto0(cameraModel, x, y, val)
if error < tolerance:
solutions.append((x, y))
# 去除近似重复的解
unique_solutions = []
for sol in solutions:
# 检查是否与已找到的解近似
is_unique = True
for usol in unique_solutions:
if np.allclose(sol, usol, atol=1):
is_unique = False
break
if is_unique:
unique_solutions.append(sol)
x = []
y = []
# print("找到的解:")
for sol in unique_solutions:
# print(f"x = {sol[0]:.4f}, y = {sol[1]:.4f}")
x.append(sol[0])
y.append(sol[1])
return x, y
def equations_yto0(cameraModel, x, y, val):
Xw, Yw = calc_distance(cameraModel, x, y)
return (Yw - val)**2 # 误差平方和
def calc_zeros_xto0(cameraModel, val):
# 定义搜索范围和步长
x_range = np.linspace(0, 1280, 2560)
y_range = np.linspace(0, 960, 1920)
# 存储解
solutions = []
tolerance = 1e-3 # 容忍误差
# 网格搜索
for x in x_range:
for y in y_range:
error = equations_xto0(cameraModel, x, y, val)
if error < tolerance:
solutions.append((x, y))
# 去除近似重复的解
unique_solutions = []
for sol in solutions:
# 检查是否与已找到的解近似
is_unique = True
for usol in unique_solutions:
if np.allclose(sol, usol, atol=1):
is_unique = False
break
if is_unique:
unique_solutions.append(sol)
x = []
y = []
# print("找到的解:")
for sol in unique_solutions:
# print(f"x = {sol[0]:.4f}, y = {sol[1]:.4f}")
x.append(sol[0])
y.append(sol[1])
return x, y
def equations_xto0(cameraModel, x, y, val):
Xw, Yw = calc_distance(cameraModel, x, y)
return (Xw - val)**2 # 误差平方和
def calc_zeros_xandyto0(cameraModel, xval, yval):
# 定义搜索范围和步长
x_range = np.linspace(0, 1280, 5000)
y_range = np.linspace(0, 960, 5000)
# 存储解
solutions = []
tolerance = 1e-1 # 容忍误差
# 网格搜索
for x in x_range:
for y in y_range:
error = equations_xandyto0(cameraModel, x, y, xval, yval)
if error < tolerance:
solutions.append((x, y))
# 去除近似重复的解
unique_solutions = []
for sol in solutions:
# 检查是否与已找到的解近似
is_unique = True
for usol in unique_solutions:
if np.allclose(sol, usol, atol=1):
is_unique = False
break
if is_unique:
unique_solutions.append(sol)
x = []
y = []
# print("找到的解:")
for sol in unique_solutions:
# print(f"x = {sol[0]:.4f}, y = {sol[1]:.4f}")
x.append(sol[0])
y.append(sol[1])
return x, y
def equations_xandyto0(cameraModel, x, y, xval, yval):
Xw, Yw = calc_distance(cameraModel, x, y)
return (Xw - xval)**2 + (Yw - yval)**2 # 误差平方和
# model = cameramodel.CameraModel("updated_config.json")
# print(calc_distance(714,370,model))
# print(calc_distance2(638,-665,model))