algN/util/LocationUtils.py

156 lines
6.7 KiB
Python
Raw Normal View History

2025-08-23 10:12:26 +08:00
import os,math
import numpy as np
# WGS-84经纬度转Web墨卡托
def wgs_to_mercator(x, y):
y = 85.0511287798 if y > 85.0511287798 else y
y = -85.0511287798 if y < -85.0511287798 else y
x2 = x * 20037508.34 / 180.0
y2 = math.log(math.tan((90.0 + y) * math.pi / 360.0)) / (math.pi / 180.0)
#print( ' y:',y, " before Log:",math.tan((90.0 + y) * math.pi / 360.0), ' log:' , math.log(math.tan((90.0 + y) * math.pi / 360.0)))
y2 = y2 * 20037508.34 / 180.0
return x2, y2
def mercator_to_wgs(x, y):
"""
将墨卡托投影坐标转换为WGS-84经纬度坐标
:param x: 墨卡托投影的X坐标
:param y: 墨卡托投影的Y坐标
:return: 经度longitude和纬度latitude
"""
# 地球半径(米)
R = 6378137.0
# 墨卡托投影的X坐标转换为经度
lon = x / R * 180.0 / math.pi
# 墨卡托投影的Y坐标转换为纬度
lat = math.atan(math.sinh(y / R)) * 180.0 / math.pi
return lon, lat
def ImageCorToCamCor(p0,w=1920,h=1080):
x,y=p0[0:2]
return x-w/2.,(h-y)-h/2.
def wgs84_to_gcj02(lat, lon):
"""将 WGS-84 坐标转换为 GCJ-02 坐标 (高德地图坐标)"""
A = 6378245.0 # 长半轴
EE = 0.00669342162296594323 # 偏心率平方
if out_of_china(lat, lon):
return lat, lon # 如果在中国以外,直接返回 WGS-84 坐标
# 坐标转换
dlat = transform_lat(lon - 105.0, lat - 35.0)
dlon = transform_lon(lon - 105.0, lat - 35.0)
radlat = lat / 180.0 * math.pi
magic = math.sin(radlat)
magic = 1 - EE * magic * magic
sqrt_magic = math.sqrt(magic)
dlat = (dlat * 180.0) / (A * (1 - EE) / (magic * sqrt_magic) * math.pi)
dlon = (dlon * 180.0) / (A / sqrt_magic * math.cos(radlat) * math.pi)
mg_lat = lat + dlat
mg_lon = lon + dlon
return mg_lat, mg_lon
def out_of_china(lat, lon):
"""检查坐标是否在中国以外"""
return lon < 72.004 or lon > 137.8347 or lat < 0.8293 or lat > 55.8271
def transform_lat(lon, lat):
"""辅助函数: 进行纬度转换"""
ret = (-100.0 + 2.0 * lon + 3.0 * lat + 0.2 * lat * lat +
0.1 * lon * lat + 0.2 * math.sqrt(abs(lon)))
ret += (20.0 * math.sin(6.0 * lon * PI) + 20.0 * math.sin(2.0 * lon * PI)) * 2.0 / 3.0
ret += (20.0 * math.sin(lat * PI) + 40.0 * math.sin(lat / 3.0 * PI)) * 2.0 / 3.0
ret += (160.0 * math.sin(lat / 12.0 * PI) + 320.0 * math.sin(lat * PI / 30.0)) * 2.0 / 3.0
return ret
def transform_lon(lon, lat):
"""辅助函数: 进行经度转换"""
ret = (300.0 + lon + 2.0 * lat + 0.1 * lon * lon +
0.1 * lon * lat + 0.1 * math.sqrt(abs(lon)))
ret += (20.0 * math.sin(6.0 * lon * PI) + 20.0 * math.sin(2.0 * lon * PI)) * 2.0 / 3.0
ret += (20.0 * math.sin(lon * PI) + 40.0 * math.sin(lon / 3.0 * PI)) * 2.0 / 3.0
ret += (150.0 * math.sin(lon / 12.0 * PI) + 300.0 * math.sin(lon / 30.0 * PI)) * 2.0 / 3.0
return ret
def cam2word(p0,pUAV,yaw,delta=1.55e-3 * 4056.0/1920,pitch=-45,f=4.5,camH=50e3,igW=1920,igH=1080):
pitch = pitch/180.0*np.pi
sinp = np.sin(pitch );cosp= np.cos(pitch)
p0_new = ImageCorToCamCor(p0,igW,igH)
Xc0 = p0_new[0]*delta;Zc0 = p0_new[1]*delta;
#(Zw0,Xw0)--相对于光心XZ并未校正到正东和正北。
#f=4.5*f/24.00
Zw0=camH*( -f*sinp + Zc0*cosp )/( f*cosp + Zc0*sinp)*1e-3
Xw0= camH*Xc0/(f*cosp + Zc0*sinp)*1e-3
#print(' %4.0f %4.0f %4.8f %4.8f %4.8f %4.8f f:%.2f'%( p0[0],p0[1], Xc0, Zc0,Xw0,Zw0,f ) )
#yaw定义为拍摄方向即图片的高方位Z方向偏离正北的方向北偏东为正。
yaw_rad = yaw/180.0*np.pi
siny=np.sin(yaw_rad);cosy=np.cos(yaw_rad)
Zx0_rot = Xw0*cosy + Zw0*siny + pUAV[0]
Zw0_rot = -Xw0*siny + Zw0*cosy + pUAV[1]
return Zx0_rot,Zw0_rot
def location( point,igW,igH,PlanWgs84,PlanH,yaw,delta,pitch,focal,outFormat='wgs84'):
'''
输入图像中点的XY坐标及无人机相关信息相机相关信息输出该点的Wgs84经纬度坐标
point--点在图像上的坐标左上角为0,0X方向为宽度方向Y方向为高度方向
igW--图像的宽度
igH--图像的高度
PlanWgs84--无人机的Wgs84坐标(lon,lat),(经度纬度)
PlanH--无人机拍照时的相对高度用mm表示
yaw--云台的yaw
delta--单个像素的长度值用mm表示
pitch--无人的pitch
focal--真实焦距用mm表示
'''
PlanX,PlanY = wgs_to_mercator(PlanWgs84[0],PlanWgs84[1])
#print('location:',PlanX,PlanY)
#PlanX,PlanY--东西、南北方向的墨卡托投影坐标
#print( 'line268:',point,PlanX,PlanY,yaw, delta,pitch,focal,PlanH,igW,igH )
cor_world = cam2word( point,(PlanX,PlanY),yaw, delta,pitch,focal,PlanH,igW,igH)
cor_world = mercator_to_wgs(cor_world[0], cor_world[1])
if outFormat=='GCJ02' or outFormat=='gcj02':
cor_world = wgs84_to_gcj02(cor_world[0], cor_world[1])
return cor_world
def locate_byMqtt(box,igW,igH,camParas,outFormat='wgs84'):
#camParas--{'lon': 3479.8250608, 'lat': 3566.7630802, 'gpssingal': 4, 'satcount': 6896, 'alt': 3.256, 'hspeed': 86.0, 'vspeed': 4.447911, 'ysingal': 0, 'tsingal': 0, 'voltage': 24.971, 'flytime': 0, 'datetime': 1739315683895, 'yaw': 70.243252, 'roll': -0.89436062, 'pitch': 0.89897547, 'armed': 'false', 'mode': 'stabilize', 'distToHome': 7.132033, 'deviceid': 'THJSQ03A2302KSPYGJ2G', 'mileage': '0', 'altasl': 21.26, 'altasl2': -20.74, 'landing_target_x': 0, 'landing_target_y': 0, 'landing_target_z': 0}
#模型输出的点的格式是-[(486, 264), (505, 264), (505, 290), (486, 290)]
box_np = np.array(box);
point = int(np.mean( box_np[:,0] )) , int(np.mean( box_np[:,1] ))
PlanWgs84 = (float(camParas['lon']),float(camParas['lat'])) #
PlanH = float(camParas['alt'])#
yaw = float(camParas['camerayaw'])
#delta = camParas['']
delta = 1.55e-3 * 4056.0/1920
pitch = float(camParas['camerapitch'])
#focal = camParas['']
focal = 3.5
out = location( point,igW,igH,PlanWgs84,PlanH,yaw,delta,pitch,focal,outFormat='wgs84')
return out
if __name__=="__main__":
srt="videos/DJI_20221220133918_0001_W_0.SRT"
videoUrl = "videos/DJI_20221220133918_0001_W.MP4"
imgOut= "videos/imgs"
fpbeg=17273;fpend=17830
nums = list(range(fpbeg,fpend,16))
#generateNewSRT(srt)
#captureImages(videoUrl,nums,imgOut)
process(videoUrl,srt,nums,imW=1920,imH=1080,txtDir='videos/labels' )
#draw_results()
#rotate_example()
#rotate_example3()