583 lines
20 KiB
Python
Raw Normal View History

import numpy as np
import cv2
import math
import os
import pyproj
# import gdal
from osgeo import gdal, osr
from touying.ImageReproject_python.img_types import ImgExif
gdal.UseExceptions() # 显式启用 GDAL 异常
# from img_types import Point, POS, ImgEO, CamParas, ImgExif
# from dpal import DPAL
class ImageReproject:
"""图像重投影类,实现目标定位、红线重投影和图像重投影功能"""
def __init__(self):
"""初始化"""
pass
def ir_target_positioning_new_list(self, us, vs, gimbal_pitch, gimbal_yaw, gimbal_roll, h, l, b,
cam_paras, img_width, img_height, Height_Terras):
"""目标定位(多组坐标版本)
Args:
us: 图像x坐标列表
vs: 图像y坐标列表
gimbal_pitch: 云台俯仰角(弧度)
gimbal_yaw: 云台偏航角(弧度)
gimbal_roll: 云台横滚角(弧度)
h: 高度
l: 经度(弧度)
b: 纬度(弧度)
cam_paras: 相机参数
img_width: 图像宽度
img_height: 图像高度
Height_Terras: 对应的地面高度列表
Returns:
list of tuples: 每个元组包含(经度, 纬度, 高度)
"""
# 验证输入长度是否一致
if len(us) != len(vs) or len(us) != len(Height_Terras):
raise ValueError("us, vs 和 Height_Terras 的长度必须相同")
# 创建ImgExif对象并设置参数
img_exif = ImgExif()
img_exif.width = img_width
img_exif.height = img_height
img_exif.B = b
img_exif.L = l
img_exif.H = h
img_exif.GimbalYaw = gimbal_yaw
img_exif.GimbalPitch = gimbal_pitch
img_exif.GimbalRoll = gimbal_roll
img_exif.camPara = cam_paras
# 计算投影矩阵
img_exif.com_zone()
img_exif.computing_p_mat()
# 创建UTM投影
wgs84 = pyproj.CRS('EPSG:4326')
utm_proj = pyproj.CRS(f'EPSG:{32600 + img_exif.UTMzone}')
transformer = pyproj.Transformer.from_crs(utm_proj, wgs84, always_xy=True)
results = []
for u, v, Height_Terra in zip(us, vs, Height_Terras):
# 去畸变处理
u_undistort, v_undistort = img_exif.undistortion_point(u, v)
plane = [0.0, 0.0, 1.0, -Height_Terra]
new_row = np.array([plane], dtype=np.float64)
P_ = np.vstack([img_exif.P, new_row])
mat_inv = np.linalg.inv(P_)
img_pt = np.array([[u_undistort], [v_undistort], [1], [0]], dtype=np.float64)
target_ptr = np.dot(mat_inv, img_pt)
point = self.normalized(target_ptr)
# utm转经纬度
point = transformer.transform(point[0], point[1], point[2])
results.append((point[0], point[1], point[2])) # 添加经度、纬度和高度到结果列表
return results
def target_positioning_new(self, u, v, gimbal_pitch, gimbal_yaw, gimbal_roll, h, l, b,
cam_paras, img_width, img_height, Height_Terra):
"""目标定位
Args:
u: 图像x坐标
v: 图像y坐标
gimbal_pitch: 云台俯仰角(弧度)
gimbal_yaw: 云台偏航角(弧度)
gimbal_roll: 云台横滚角(弧度)
h: 高度
l: 经度(弧度)
b: 纬度(弧度)
cam_paras: 相机参数
img_width: 图像宽度
img_height: 图像高度
Height_Terra: 当前地面高度
x: 输出经度
y: 输出纬度
z: 输出高度
Returns:
(x, y, z): 目标点的经度纬度和高度
"""
# 创建ImgExif对象并设置参数
img_exif = ImgExif()
img_exif.width = img_width
img_exif.height = img_height
img_exif.B = b
img_exif.L = l
img_exif.H = h
img_exif.GimbalYaw = gimbal_yaw
img_exif.GimbalPitch = gimbal_pitch
img_exif.GimbalRoll = gimbal_roll
img_exif.camPara = cam_paras
# 计算投影矩阵
img_exif.com_zone()
img_exif.computing_p_mat()
# 去畸变处理
u_undistort, v_undistort = img_exif.undistortion_point(u, v)
# 创建UTM投影
wgs84 = pyproj.CRS('EPSG:4326')
utm_proj = pyproj.CRS(f'EPSG:{32600 + img_exif.UTMzone}')
transformer = pyproj.Transformer.from_crs(utm_proj, wgs84, always_xy=True)
plane = [0.0, 0.0, 1.0, -Height_Terra]
new_row = np.array([plane], dtype=np.float64)
P_ = np.vstack([img_exif.P, new_row])
mat_inv = np.linalg.inv(P_)
img_pt = np.array([[u_undistort], [v_undistort], [1], [0]], dtype=np.float64)
target_ptr = np.dot(mat_inv, img_pt)
point = self.normalized(target_ptr)
# utm转经纬度
point = transformer.transform(point[0], point[1], point[2])
return point[0], point[1], point[2] # 返回经度、纬度和高度
def target_positioning(self, u, v, interval, gimbal_pitch, gimbal_yaw, gimbal_roll,
h, l, b, cam_paras, img_width, img_height, dem_file,
x=None, y=None, z=None):
"""目标定位
Args:
u: 图像x坐标
v: 图像y坐标
interval: 间隔
gimbal_pitch: 云台俯仰角(弧度)
gimbal_yaw: 云台偏航角(弧度)
gimbal_roll: 云台横滚角(弧度)
h: 高度
l: 经度(弧度)
b: 纬度(弧度)
cam_paras: 相机参数
img_width: 图像宽度
img_height: 图像高度
dem_file: DEM文件路径
x: 输出经度
y: 输出纬度
z: 输出高度
Returns:
(x, y, z): 目标点的经度纬度和高度
"""
# 创建ImgExif对象并设置参数
img_exif = ImgExif()
img_exif.width = img_width
img_exif.height = img_height
img_exif.B = b
img_exif.L = l
img_exif.H = h
img_exif.GimbalYaw = gimbal_yaw
img_exif.GimbalPitch = gimbal_pitch
img_exif.GimbalRoll = gimbal_roll
img_exif.camPara = cam_paras
# 计算投影矩阵
img_exif.com_zone()
img_exif.computing_p_mat()
# 去畸变处理
u_undistort, v_undistort = img_exif.undistortion_point(u, v)
# 创建UTM投影
wgs84 = pyproj.CRS('EPSG:4326')
utm_proj = pyproj.CRS(f'EPSG:{32600 + img_exif.UTMzone}')
transformer = pyproj.Transformer.from_crs(utm_proj, wgs84, always_xy=True)
# 获取DEM的最小和最大高程
minZ, maxZ = self.get_dem_min_max(dem_file)
planes = []
h = minZ
while h <= maxZ:
planes.append([0.0, 0.0, 1.0, -h])
h += interval
points = []
for plane in planes:
new_row = np.array([plane], dtype=np.float64)
P_ = np.vstack([img_exif.P, new_row])
mat_inv = np.linalg.inv(P_)
img_pt = np.array([[u_undistort], [v_undistort], [1], [0]], dtype=np.float64)
target_ptr = np.dot(mat_inv, img_pt)
point = self.normalized(target_ptr)
# utm转经纬度
point = transformer.transform(point[0], point[1], point[2])
points.append(point)
# 过滤不符合高程的点
filtered_points = []
for pt in points:
projectZ = pt[2]
demZ = self.get_dem_z(dem_file, pt[0], pt[1])
diff = projectZ - demZ
if abs(diff) <= interval:
filtered_points.append(pt)
if len(filtered_points) == 0:
print("未得到采样点!")
return False, None
if len(filtered_points) == 1:
return filtered_points[0][0], filtered_points[0][1], filtered_points[0][2]
else:
cam_center = np.array([img_exif.EO.Xs, img_exif.EO.Ys, img_exif.EO.Zs])
dists = [np.linalg.norm(np.array(pt) - cam_center) for pt in filtered_points]
min_idx = int(np.argmin(dists))
return filtered_points[min_idx][0], filtered_points[min_idx][1], filtered_points[min_idx][2]
def red_line_reproject(self, gimbal_pitch, gimbal_yaw, gimbal_roll, h, l, b,
cam_paras, img_width, img_height, camera_img_width, camera_img_height, points):
"""红线重投影
Args:
gimbal_pitch: 云台俯仰角(弧度)
gimbal_yaw: 云台偏航角(弧度)
gimbal_roll: 云台横滚角(弧度)
h: 高度
l: 经度(弧度)
b: 纬度(弧度)
cam_paras: 相机参数
img_width: 图像宽度
img_height: 图像高度
camera_img_width: 直接使用摄像头拍照的宽度
camera_img_height: 直接使用摄像头拍照的高度
points: 点坐标列表
img_file: 输入图像文件路径
img_save_file: 输出图像文件路径
Returns:
None
"""
# 创建ImgExif对象并设置参数
img_exif = ImgExif()
img_exif.width = img_width
img_exif.height = img_height
img_exif.B = b
img_exif.L = l
img_exif.H = h
img_exif.GimbalYaw = gimbal_yaw
img_exif.GimbalPitch = gimbal_pitch
img_exif.GimbalRoll = gimbal_roll
img_exif.camPara = cam_paras
# 计算投影矩阵
img_exif.com_zone()
img_exif.computing_p_mat()
# # 读取图像
# img = cv2.imread(img_file)
# if img is None:
# print(f"无法读取图像文件: {img_file}")
# return
pixel_list=[]
# 投影点到图像上
for point in points:
# 构建三维点的齐次坐标
point_3d = np.array([point.X, point.Y, point.Z, 1.0])
# 投影到图像平面
point_2d = np.matmul(img_exif.P, point_3d)
# 约束:只处理相机前方的点
if point_2d[2] <= 0:
continue
# 归一化
point_2d = point_2d / point_2d[2]
# 应用畸变
u, v = img_exif.distortion_point(point_2d[0], point_2d[1],img_width,camera_img_width)
# # # 转换到视频上校友之家4D
# u = u - (5280 / 2 - img_width / 2)
# v = v - (3956 / 2 - img_height / 2)
# # 转换到视频上哈密4TD
u = u - (camera_img_width / 2 - img_width / 2)
v = v - (camera_img_height / 2 - img_height / 2)
pixel_list.append({
"u": int(u),
"v": int(v),
"id":point.id
})
# # 检查点是否在图像范围内
# if 0 <= u < img_width and 0 <= v < img_height:
# # 在图像上绘制点
# cv2.circle(img, (int(u), int(v)), 15, (0, 0, 255), -1)
#
# # 保存图像
# cv2.imwrite(img_save_file, img)
return pixel_list
def img_reproject(self, gimbal_pitch, gimbal_yaw, gimbal_roll, h, l, b,
cam_paras, img_width, img_height, obj_longitude, obj_latitude, obj_height,
pixel_x=None, pixel_y=None):
"""图像重投影
Args:
gimbal_pitch: 云台俯仰角(弧度)
gimbal_yaw: 云台偏航角(弧度)
gimbal_roll: 云台横滚角(弧度)
h: 高度
l: 经度(弧度)
b: 纬度(弧度)
cam_paras: 相机参数
img_width: 图像宽度
img_height: 图像高度
obj_longitude: 目标点经度()
obj_latitude: 目标点纬度()
obj_height: 目标点高度
pixel_x: 输出像素x坐标
pixel_y: 输出像素y坐标
Returns:
(pixel_x, pixel_y): 目标点在图像上的像素坐标
"""
# 创建ImgExif对象并设置参数
img_exif = ImgExif()
img_exif.width = img_width
img_exif.height = img_height
img_exif.B = b
img_exif.L = l
img_exif.H = h
img_exif.GimbalYaw = gimbal_yaw
img_exif.GimbalPitch = gimbal_pitch
img_exif.GimbalRoll = gimbal_roll
img_exif.camPara = cam_paras
# 计算投影矩阵
img_exif.com_zone()
img_exif.computing_p_mat()
# 创建UTM投影
wgs84 = pyproj.CRS('EPSG:4326')
utm_proj = pyproj.CRS(f'EPSG:{32600 + img_exif.UTMzone}')
transformer = pyproj.Transformer.from_crs(wgs84, utm_proj, always_xy=True)
# 将目标点经纬度转换为UTM坐标
obj_x, obj_y = transformer.transform(obj_longitude, obj_latitude)
# 构建三维点的齐次坐标
point_3d = np.array([obj_x, obj_y, obj_height, 1.0])
# 投影到图像平面
point_2d = np.matmul(img_exif.P, point_3d)
# 归一化
point_2d = point_2d / point_2d[2]
# 应用畸变
u, v = img_exif.distortion_point(point_2d[0], point_2d[1],img_width)
# 设置输出结果
if pixel_x is not None:
pixel_x = u
if pixel_y is not None:
pixel_y = v
return u, v
def get_dem_min_max(self, dem_file):
"""获取DEM文件的最小和最大高程
Args:
dem_file: DEM文件路径
Returns:
(min_z, max_z): 最小和最大高程
"""
# 打开DEM文件
dataset = gdal.Open(dem_file, gdal.GA_ReadOnly)
if dataset is None:
print(f"无法打开DEM文件: {dem_file}")
return 0, 1000 # 返回默认值
# 获取波段
band = dataset.GetRasterBand(1)
# 获取统计信息
min_z, max_z, mean, std_dev = band.GetStatistics(True, True)
# 关闭数据集
dataset = None
return min_z, max_z
def get_dem_z(self, dem_file, x, y):
"""从DEM文件中获取指定点的高程
Args:
dem_file: DEM文件路径
x: UTM X坐标
y: UTM Y坐标
Returns:
z: 高程值如果无法获取则返回None
"""
# 打开DEM文件
dataset = gdal.Open(dem_file, gdal.GA_ReadOnly)
if dataset is None:
print(f"无法打开DEM文件: {dem_file}")
return None
# 获取地理变换参数
geotransform = dataset.GetGeoTransform()
# 获取波段
band = dataset.GetRasterBand(1)
# 计算像素坐标
px = int((x - geotransform[0]) / geotransform[1])
py = int((y - geotransform[3]) / geotransform[5])
# 检查像素坐标是否在范围内
if px < 0 or px >= dataset.RasterXSize or py < 0 or py >= dataset.RasterYSize:
dataset = None
return None
# 读取高程值
data = band.ReadAsArray(px, py, 1, 1)
if data is None:
dataset = None
return None
# 获取高程值
z = data[0, 0]
# 检查是否为NoData值
no_data_value = band.GetNoDataValue()
if no_data_value is not None and z == no_data_value:
dataset = None
return None
# 关闭数据集
dataset = None
return z
def normalized(self, mat): # 归一化
return [mat[0, 0] / mat[3, 0], mat[1, 0] / mat[3, 0], mat[2, 0] / mat[3, 0]]
# C风格接口
def ir_target_positioning(u, v, interval, gimbal_pitch, gimbal_yaw, gimbal_roll,
h, l, b, cam_paras, img_width, img_height, dem_file,
x=None, y=None, z=None):
"""目标定位的C风格接口"""
reproject = ImageReproject()
return reproject.target_positioning(u, v, interval, gimbal_pitch, gimbal_yaw, gimbal_roll,
h, l, b, cam_paras, img_width, img_height, dem_file,
x, y, z)
def ir_red_line_reproject(gimbal_pitch, gimbal_yaw, gimbal_roll, h, l, b,
cam_paras, img_width, img_height, camera_img_width, camera_img_height, points):
"""红线重投影的C风格接口"""
reproject = ImageReproject()
pixel_list=reproject.red_line_reproject(gimbal_pitch, gimbal_yaw, gimbal_roll, h, l, b,
cam_paras, img_width, img_height, camera_img_width, camera_img_height, points)
return pixel_list
def ir_img_reproject(gimbal_pitch, gimbal_yaw, gimbal_roll, h, l, b,
cam_paras, img_width, img_height, obj_longitude, obj_latitude, obj_height,
pixel_x=None, pixel_y=None):
"""图像重投影的C风格接口"""
reproject = ImageReproject()
return reproject.img_reproject(gimbal_pitch, gimbal_yaw, gimbal_roll, h, l, b,
cam_paras, img_width, img_height, obj_longitude, obj_latitude, obj_height,
pixel_x, pixel_y)
def ir_target_positioning_new(u, v, gimbal_pitch, gimbal_yaw, gimbal_roll,
h, l, b, cam_paras, img_width, img_height, Height_Terra):
"""目标定位的C风格接口"""
reproject = ImageReproject()
return reproject.target_positioning_new(u, v, gimbal_pitch, gimbal_yaw, gimbal_roll,
h, l, b, cam_paras, img_width, img_height, Height_Terra)
def ir_target_positioning_new_list(self, us, vs, gimbal_pitch, gimbal_yaw, gimbal_roll, h, l, b,
cam_paras, img_width, img_height, Height_Terras):
"""目标定位(多组坐标版本)
Args:
us: 图像x坐标列表
vs: 图像y坐标列表
gimbal_pitch: 云台俯仰角(弧度)
gimbal_yaw: 云台偏航角(弧度)
gimbal_roll: 云台横滚角(弧度)
h: 高度
l: 经度(弧度)
b: 纬度(弧度)
cam_paras: 相机参数
img_width: 图像宽度
img_height: 图像高度
Height_Terras: 对应的地面高度列表
Returns:
list of tuples: 每个元组包含(经度, 纬度, 高度)
"""
# 验证输入长度是否一致
if len(us) != len(vs) or len(us) != len(Height_Terras):
raise ValueError("us, vs 和 Height_Terras 的长度必须相同")
# 创建ImgExif对象并设置参数
img_exif = ImgExif()
img_exif.width = img_width
img_exif.height = img_height
img_exif.B = b
img_exif.L = l
img_exif.H = h
img_exif.GimbalYaw = gimbal_yaw
img_exif.GimbalPitch = gimbal_pitch
img_exif.GimbalRoll = gimbal_roll
img_exif.camPara = cam_paras
# 计算投影矩阵
img_exif.com_zone()
img_exif.computing_p_mat()
# 创建UTM投影
wgs84 = pyproj.CRS('EPSG:4326')
utm_proj = pyproj.CRS(f'EPSG:{32600 + img_exif.UTMzone}')
transformer = pyproj.Transformer.from_crs(utm_proj, wgs84, always_xy=True)
results = []
for u, v, Height_Terra in zip(us, vs, Height_Terras):
# 去畸变处理
u_undistort, v_undistort = img_exif.undistortion_point(u, v)
plane = [0.0, 0.0, 1.0, -Height_Terra]
new_row = np.array([plane], dtype=np.float64)
P_ = np.vstack([img_exif.P, new_row])
mat_inv = np.linalg.inv(P_)
img_pt = np.array([[u_undistort], [v_undistort], [1], [0]], dtype=np.float64)
target_ptr = np.dot(mat_inv, img_pt)
point = self.normalized(target_ptr)
# utm转经纬度
point = transformer.transform(point[0], point[1], point[2])
results.append((point[0], point[1], point[2])) # 添加经度、纬度和高度到结果列表
return results
# 对应的C风格接口也需要修改
def ir_target_positioning_new_list(us, vs, gimbal_pitch, gimbal_yaw, gimbal_roll,
h, l, b, cam_paras, img_width, img_height, Height_Terras):
"""目标定位的C风格接口多组坐标版本"""
reproject = ImageReproject()
return reproject.ir_target_positioning_new_list(us, vs, gimbal_pitch, gimbal_yaw, gimbal_roll,
h, l, b, cam_paras, img_width, img_height, Height_Terras)