UAV/post_pro/conv_obj.py
2025-02-06 19:01:19 +08:00

165 lines
6.5 KiB
Python
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 os
import subprocess
import json
import shutil
import logging
class ConvertOBJ:
def __init__(self, output_dir: str, center_lat: float, center_lon: float):
self.output_dir = output_dir
self.center_lat = center_lat
self.center_lon = center_lon
self.logger = logging.getLogger('UAV_Preprocess.ConvertOBJ')
def convert_grid_obj(self, grid_points):
"""转换每个网格的OBJ文件为OSGB格式"""
os.makedirs(os.path.join(self.output_dir, "osgb", "Data"), exist_ok=True)
tile_infos = []
for grid_id in grid_points.keys():
try:
tile_info = self._convert_single_grid(grid_id, grid_points)
tile_infos.append(tile_info)
except Exception as e:
self.logger.error(f"网格 {grid_id} 转换失败: {str(e)}")
self._create_merged_metadata(tile_infos)
def _convert_single_grid(self, grid_id, grid_points):
"""转换单个网格的OBJ文件"""
# 1. 构建相关路径
grid_name = f"grid_{grid_id[0]}_{grid_id[1]}"
project_dir = os.path.join(self.output_dir, grid_name, "project")
texturing_dir = os.path.join(project_dir, "odm_texturing")
opensfm_dir = os.path.join(project_dir, "opensfm")
# 检查输入文件是否存在
obj_file = os.path.join(texturing_dir, "odm_textured_model_geo.obj")
if not os.path.exists(obj_file):
raise FileNotFoundError(f"找不到OBJ文件: {obj_file}")
# 2. 执行格式转换
self.logger.info(f"开始转换网格 {grid_id} 的OBJ文件")
output_osgb = os.path.join(texturing_dir, "Tile.osgb")
# 计算当前网格相对于中心点的偏移
grid_data = grid_points[grid_id]
lats = [point['lat'] for point in grid_data]
lons = [point['lon'] for point in grid_data]
min_lat = min(lats)
min_lon = min(lons)
# 计算偏移量(米)
offset_x = self._calculate_distance(self.center_lat, self.center_lon, self.center_lat, min_lon)
offset_y = self._calculate_distance(self.center_lat, self.center_lon, min_lat, self.center_lon)
# 修改转换命令,使用正确的参数格式
cmd = (
f"osgconv {obj_file} {output_osgb} "
f"--compressed --smooth --fix-transparency "
f"-t {offset_x},{offset_y},0 " # 使用 -t 参数进行平移
f"-o 0,1,0-0,0,-1"
)
try:
subprocess.run(cmd, shell=True, check=True, cwd=texturing_dir)
except subprocess.CalledProcessError as e:
raise RuntimeError(f"OSGB转换失败: {str(e)}")
# 3. 读取地理信息
ref_lla_file = os.path.join(opensfm_dir, "reference_lla.json")
with open(ref_lla_file, 'r') as f:
ref_lla = json.load(f)
# 4. 创建OSGB目录结构
osgb_base_dir = os.path.join(self.output_dir, "osgb")
data_dir = os.path.join(osgb_base_dir, "Data")
tile_dir = os.path.join(data_dir, f"Tile_{grid_id[0]}_{grid_id[1]}")
os.makedirs(tile_dir, exist_ok=True)
# 5. 复制OSGB文件
target_osgb = os.path.join(tile_dir, f"Tile_{grid_id[0]}_{grid_id[1]}.osgb")
shutil.copy2(output_osgb, target_osgb)
# 计算当前网格的边界框
grid_data = grid_points[grid_id]
# 假设grid_data是一个列表每个元素都是包含lat和lon的字典
lats = [point['lat'] for point in grid_data]
lons = [point['lon'] for point in grid_data]
min_lat = min(lats)
max_lat = max(lats)
min_lon = min(lons)
max_lon = max(lons)
# 计算相对于中心点的偏移
offset_x = self._calculate_distance(self.center_lat, self.center_lon, self.center_lat, min_lon)
offset_y = self._calculate_distance(self.center_lat, self.center_lon, min_lat, self.center_lon)
tile_info = {
'id': f"{grid_id[0]}_{grid_id[1]}",
'bounds': {
'min_lat': min_lat,
'max_lat': max_lat,
'min_lon': min_lon,
'max_lon': max_lon
},
'offset': (offset_x, offset_y)
}
return tile_info
def _calculate_distance(self, lat1, lon1, lat2, lon2):
"""计算两点间的距离(米)"""
from math import sin, cos, sqrt, atan2, radians
R = 6371000 # 地球半径(米)
lat1, lon1, lat2, lon2 = map(radians, [lat1, lon1, lat2, lon2])
dlat = lat2 - lat1
dlon = lon2 - lon1
a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
c = 2 * atan2(sqrt(a), sqrt(1-a))
return R * c
def _create_merged_metadata(self, tile_infos):
"""创建合并后的metadata.xml文件"""
metadata_content = f"""<?xml version="1.0" encoding="utf-8"?>
<ModelMetadata version="1">
<SRS>EPSG:4326</SRS>
<SRSOrigin>{self.center_lon},{self.center_lat},0.000000</SRSOrigin>
<TileStructure>
<RootNode>
<BoundingBox>
<MinLat>{min([t['bounds']['min_lat'] for t in tile_infos])}</MinLat>
<MaxLat>{max([t['bounds']['max_lat'] for t in tile_infos])}</MaxLat>
<MinLon>{min([t['bounds']['min_lon'] for t in tile_infos])}</MinLon>
<MaxLon>{max([t['bounds']['max_lon'] for t in tile_infos])}</MaxLon>
</BoundingBox>
<Tiles>"""
for tile in tile_infos:
metadata_content += f"""
<Tile id="{tile['id']}">
<Offset>{tile['offset'][0]},{tile['offset'][1]},0</Offset>
<BoundingBox>
<MinLat>{tile['bounds']['min_lat']}</MinLat>
<MaxLat>{tile['bounds']['max_lat']}</MaxLat>
<MinLon>{tile['bounds']['min_lon']}</MinLon>
<MaxLon>{tile['bounds']['max_lon']}</MaxLon>
</BoundingBox>
</Tile>"""
metadata_content += """
</Tiles>
</RootNode>
</TileStructure>
<Texture>
<ColorSource>Visible</ColorSource>
</Texture>
</ModelMetadata>"""
metadata_file = os.path.join(self.output_dir, "osgb", "metadata.xml")
with open(metadata_file, 'w', encoding='utf-8') as f:
f.write(metadata_content)