363 lines
13 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import numpy as np
import cv2
import math
import os
import pyproj
# import gdal
from osgeo import gdal, osr
gdal.UseExceptions() # 显式启用 GDAL 异常
from touying.ImageReproject_python.img_types import Point, POS, ImgEO, CamParas, ImgExif
from touying.ImageReproject_python.dpal import DPAL
class ImageReproject:
"""图像重投影类,实现目标定位、红线重投影和图像重投影功能"""
def __init__(self):
"""初始化"""
pass
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, points, img_file, img_save_file):
"""红线重投影
Args:
gimbal_pitch: 云台俯仰角(弧度)
gimbal_yaw: 云台偏航角(弧度)
gimbal_roll: 云台横滚角(弧度)
h: 高度
l: 经度(弧度)
b: 纬度(弧度)
cam_paras: 相机参数
img_width: 图像宽度
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()
# 创建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坐标
utm_points = []
for point in points:
# 将经纬度转换为UTM坐标
x, y = transformer.transform(point.Y , point.X )
utm_point = Point(x, y, point.Z)
utm_points.append(utm_point)
# 读取图像
img = cv2.imread(img_file)
if img is None:
print(f"无法读取图像文件: {img_file}")
return
pixel_lists=[]
# 投影点到图像上
for point in utm_points:
# 构建三维点的齐次坐标
point_3d = np.array([point.X, point.Y, point.Z, 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])
#
# # 检查点是否在图像范围内
if 0 <= u < img_width and 0 <= v < img_height:
pixel_lists.append({
"u": int(u),
"v": int(v)
})
# # 在图像上绘制点
# cv2.circle(img, (int(u), int(v)), 5, (0, 0, 255), -1)
# 保存图像
# cv2.imwrite(img_save_file, img)
return pixel_lists
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])
# 设置输出结果
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, points, img_file, img_save_file):
"""红线重投影的C风格接口"""
reproject = ImageReproject()
pixel_list=reproject.red_line_reproject(gimbal_pitch, gimbal_yaw, gimbal_roll, h, l, b,
cam_paras, img_width, img_height, points, img_file, img_save_file)
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)