2024-12-29 12:03:53 +08:00
|
|
|
|
import os
|
|
|
|
|
import logging
|
|
|
|
|
import numpy as np
|
|
|
|
|
from typing import Dict
|
|
|
|
|
import pandas as pd
|
2024-12-31 22:29:24 +08:00
|
|
|
|
import shutil
|
2024-12-29 12:03:53 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MergeObj:
|
|
|
|
|
def __init__(self, output_dir: str):
|
|
|
|
|
self.output_dir = output_dir
|
|
|
|
|
self.logger = logging.getLogger('UAV_Preprocess.MergeObj')
|
|
|
|
|
|
2024-12-31 20:37:35 +08:00
|
|
|
|
def read_obj(self, file_path):
|
|
|
|
|
"""读取.obj文件,返回顶点列表和面列表"""
|
|
|
|
|
vertices = []
|
|
|
|
|
faces = []
|
2024-12-31 22:29:24 +08:00
|
|
|
|
|
2024-12-31 20:37:35 +08:00
|
|
|
|
with open(file_path, 'r') as file:
|
|
|
|
|
for line in file:
|
|
|
|
|
parts = line.split()
|
|
|
|
|
if len(parts) == 0:
|
|
|
|
|
continue
|
|
|
|
|
if parts[0] == 'v': # 顶点
|
2024-12-31 22:29:24 +08:00
|
|
|
|
vertices.append(
|
|
|
|
|
[float(parts[1]), float(parts[2]), float(parts[3])])
|
2024-12-31 20:37:35 +08:00
|
|
|
|
elif parts[0] == 'f': # 面
|
2024-12-31 22:29:24 +08:00
|
|
|
|
faces.append([int(parts[1].split(
|
|
|
|
|
'/')[0]), int(parts[2].split('/')[0]), int(parts[3].split('/')[0])])
|
|
|
|
|
|
2024-12-31 20:37:35 +08:00
|
|
|
|
return vertices, faces
|
|
|
|
|
|
|
|
|
|
def write_obj(self, file_path, vertices, faces):
|
|
|
|
|
"""将修改后的顶点和面列表写入到.obj文件"""
|
|
|
|
|
with open(file_path, 'w') as file:
|
|
|
|
|
for vertex in vertices:
|
|
|
|
|
file.write(f"v {vertex[0]} {vertex[1]} {vertex[2]}\n")
|
2024-12-31 22:29:24 +08:00
|
|
|
|
|
2024-12-31 20:37:35 +08:00
|
|
|
|
for face in faces:
|
|
|
|
|
file.write(f"f {face[0]} {face[1]} {face[2]}\n")
|
|
|
|
|
|
|
|
|
|
def translate_vertices(self, vertices, translation):
|
|
|
|
|
"""平移顶点"""
|
|
|
|
|
return [[v[0] + translation[0], v[1] + translation[1], v[2] + translation[2]] for v in vertices]
|
|
|
|
|
|
|
|
|
|
def merge_two_objs(self, obj1_path: str, obj2_path: str, output_path: str, translation):
|
|
|
|
|
"""合并两个OBJ文件"""
|
2024-12-29 12:03:53 +08:00
|
|
|
|
try:
|
2024-12-31 20:37:35 +08:00
|
|
|
|
self.logger.info(f"开始合并OBJ模型:\n输入1: {obj1_path}\n输入2: {obj2_path}")
|
2024-12-31 22:29:24 +08:00
|
|
|
|
|
2024-12-29 12:03:53 +08:00
|
|
|
|
# 检查输入文件是否存在
|
|
|
|
|
if not os.path.exists(obj1_path) or not os.path.exists(obj2_path):
|
|
|
|
|
raise FileNotFoundError("输入模型文件不存在")
|
|
|
|
|
|
2024-12-31 20:37:35 +08:00
|
|
|
|
# 读取两个obj文件
|
|
|
|
|
vertices1, faces1 = self.read_obj(obj1_path)
|
|
|
|
|
vertices2, faces2 = self.read_obj(obj2_path)
|
2024-12-31 22:29:24 +08:00
|
|
|
|
|
2024-12-31 20:37:35 +08:00
|
|
|
|
# 平移第二个模型的顶点
|
2024-12-31 22:29:24 +08:00
|
|
|
|
vertices2_translated = self.translate_vertices(
|
|
|
|
|
vertices2, translation)
|
|
|
|
|
|
2024-12-31 20:37:35 +08:00
|
|
|
|
# 合并顶点和面
|
|
|
|
|
all_vertices = vertices1 + vertices2_translated
|
2024-12-31 22:29:24 +08:00
|
|
|
|
all_faces = faces1 + \
|
|
|
|
|
[[f[0] + len(vertices1), f[1] + len(vertices1),
|
|
|
|
|
f[2] + len(vertices1)] for f in faces2]
|
|
|
|
|
|
2024-12-31 20:37:35 +08:00
|
|
|
|
# 写入合并后的obj文件
|
|
|
|
|
self.write_obj(output_path, all_vertices, all_faces)
|
2024-12-31 22:29:24 +08:00
|
|
|
|
|
2024-12-29 12:03:53 +08:00
|
|
|
|
self.logger.info(f"模型合并成功,已保存至: {output_path}")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"合并OBJ模型时发生错误: {str(e)}", exc_info=True)
|
|
|
|
|
raise
|
|
|
|
|
|
2024-12-31 22:29:24 +08:00
|
|
|
|
def read_mtl(self, file_path):
|
|
|
|
|
"""读取.mtl文件内容"""
|
|
|
|
|
with open(file_path, 'r') as file:
|
|
|
|
|
return file.read()
|
|
|
|
|
|
|
|
|
|
def copy_texture_files(self, src_dir: str, dst_dir: str, grid_id: tuple):
|
|
|
|
|
"""复制并重命名纹理文件
|
|
|
|
|
Args:
|
|
|
|
|
src_dir: 源纹理文件目录
|
|
|
|
|
dst_dir: 目标纹理文件目录
|
|
|
|
|
grid_id: 网格ID,用于重命名
|
|
|
|
|
"""
|
|
|
|
|
# 确保目标目录存在
|
|
|
|
|
os.makedirs(dst_dir, exist_ok=True)
|
2024-12-31 20:37:35 +08:00
|
|
|
|
|
2024-12-31 22:29:24 +08:00
|
|
|
|
# 复制所有png文件并重命名
|
|
|
|
|
for file in os.listdir(src_dir):
|
|
|
|
|
if file.endswith('.png'):
|
|
|
|
|
src_file = os.path.join(src_dir, file)
|
|
|
|
|
# 在文件名前添加网格ID前缀
|
|
|
|
|
new_name = f"grid_{grid_id[0]}_{grid_id[1]}_{file}"
|
|
|
|
|
dst_file = os.path.join(dst_dir, new_name)
|
|
|
|
|
shutil.copy2(src_file, dst_file)
|
|
|
|
|
self.logger.debug(f"复制纹理文件: {file} -> {new_name}")
|
2024-12-31 20:37:35 +08:00
|
|
|
|
|
2024-12-31 22:29:24 +08:00
|
|
|
|
return dst_dir
|
|
|
|
|
|
|
|
|
|
def update_mtl_content(self, mtl_content: str, grid_id: tuple) -> str:
|
|
|
|
|
"""更新MTL文件内容,修改纹理文件路径
|
|
|
|
|
Args:
|
|
|
|
|
mtl_content: 原MTL文件内容
|
|
|
|
|
grid_id: 网格ID,用于重命名纹理文件
|
|
|
|
|
Returns:
|
|
|
|
|
更新后的MTL文件内容
|
|
|
|
|
"""
|
|
|
|
|
updated_content = []
|
|
|
|
|
for line in mtl_content.split('\n'):
|
|
|
|
|
if line.startswith('map_Kd'): # 纹理文件路径行
|
|
|
|
|
# 获取原始文件名
|
|
|
|
|
original_file = line.split()[-1]
|
|
|
|
|
# 添加网格ID前缀
|
|
|
|
|
new_file = f"grid_{grid_id[0]}_{grid_id[1]}_{os.path.basename(original_file)}"
|
|
|
|
|
# 更新行内容
|
|
|
|
|
line = f"map_Kd {new_file}"
|
|
|
|
|
updated_content.append(line)
|
|
|
|
|
return '\n'.join(updated_content)
|
2024-12-31 20:37:35 +08:00
|
|
|
|
|
2024-12-31 22:29:24 +08:00
|
|
|
|
def merge_grid_obj(self, grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple]):
|
|
|
|
|
"""合并所有网格的OBJ模型和纹理"""
|
2024-12-29 12:03:53 +08:00
|
|
|
|
self.logger.info("开始合并所有网格的OBJ模型")
|
|
|
|
|
|
|
|
|
|
if len(grid_points) < 2:
|
|
|
|
|
self.logger.info("只有一个网格,无需合并")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
try:
|
2024-12-31 22:29:24 +08:00
|
|
|
|
# 创建输出目录
|
|
|
|
|
output_model_dir = os.path.join(self.output_dir, "merged_model")
|
|
|
|
|
os.makedirs(output_model_dir, exist_ok=True)
|
|
|
|
|
|
|
|
|
|
# 获取所有有效的网格OBJ文件
|
|
|
|
|
grid_objs = {}
|
2024-12-31 21:37:44 +08:00
|
|
|
|
for grid_id, points in grid_points.items():
|
2024-12-31 22:29:24 +08:00
|
|
|
|
grid_base_dir = os.path.join(
|
2024-12-29 12:03:53 +08:00
|
|
|
|
self.output_dir,
|
2024-12-31 21:37:44 +08:00
|
|
|
|
f"grid_{grid_id[0]}_{grid_id[1]}",
|
2024-12-29 12:03:53 +08:00
|
|
|
|
"project",
|
2024-12-31 22:29:24 +08:00
|
|
|
|
"odm_texturing"
|
2024-12-29 12:03:53 +08:00
|
|
|
|
)
|
2024-12-31 22:29:24 +08:00
|
|
|
|
grid_obj = os.path.join(grid_base_dir, "odm_textured_model_geo.obj")
|
|
|
|
|
grid_mtl = os.path.join(grid_base_dir, "odm_textured_model_geo.mtl")
|
2024-12-29 12:03:53 +08:00
|
|
|
|
|
2024-12-31 22:29:24 +08:00
|
|
|
|
if not os.path.exists(grid_obj) or not os.path.exists(grid_mtl):
|
|
|
|
|
self.logger.warning(
|
|
|
|
|
f"网格 ({grid_id[0]},{grid_id[1]}) 的OBJ或MTL文件不存在")
|
2024-12-29 12:03:53 +08:00
|
|
|
|
continue
|
|
|
|
|
|
2024-12-31 22:29:24 +08:00
|
|
|
|
grid_objs[grid_id] = {
|
|
|
|
|
'obj': grid_obj,
|
|
|
|
|
'mtl': grid_mtl,
|
|
|
|
|
'base_dir': grid_base_dir
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if not grid_objs:
|
|
|
|
|
self.logger.error("没有找到有效的OBJ文件")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 使用第一个网格作为参考
|
|
|
|
|
reference_id = list(grid_objs.keys())[0]
|
|
|
|
|
merged_obj = grid_objs[reference_id]['obj']
|
|
|
|
|
|
|
|
|
|
# 复制参考网格的纹理文件
|
|
|
|
|
self.copy_texture_files(
|
|
|
|
|
grid_objs[reference_id]['base_dir'],
|
|
|
|
|
output_model_dir,
|
|
|
|
|
reference_id
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 复制并更新参考网格的MTL文件
|
|
|
|
|
ref_mtl_content = self.read_mtl(grid_objs[reference_id]['mtl'])
|
|
|
|
|
updated_mtl = self.update_mtl_content(ref_mtl_content, reference_id)
|
|
|
|
|
|
|
|
|
|
self.logger.info(
|
|
|
|
|
f"使用网格 ({reference_id[0]},{reference_id[1]}) 作为参考网格")
|
|
|
|
|
|
|
|
|
|
# 依次合并其他网格
|
|
|
|
|
for grid_id, grid_files in list(grid_objs.items())[1:]:
|
|
|
|
|
# 复制当前网格的纹理文件
|
|
|
|
|
self.copy_texture_files(
|
|
|
|
|
grid_files['base_dir'],
|
|
|
|
|
output_model_dir,
|
|
|
|
|
grid_id
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 更新当前网格的MTL内容
|
|
|
|
|
current_mtl = self.read_mtl(grid_files['mtl'])
|
|
|
|
|
updated_mtl += '\n' + self.update_mtl_content(current_mtl, grid_id)
|
|
|
|
|
|
|
|
|
|
# 获取平移量
|
|
|
|
|
translation = translations[grid_id]
|
|
|
|
|
translation = (translation[0], translation[1], 0) # 添加z轴的0平移
|
|
|
|
|
|
|
|
|
|
output_obj = os.path.join(
|
|
|
|
|
output_model_dir,
|
|
|
|
|
f"merged_model_{reference_id[0]}_{reference_id[1]}_{grid_id[0]}_{grid_id[1]}.obj"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
self.logger.info(
|
|
|
|
|
f"合并网格 ({grid_id[0]},{grid_id[1]}):\n"
|
|
|
|
|
f"平移量: x={translation[0]:.2f}m, y={translation[1]:.2f}m\n"
|
|
|
|
|
f"输出: {output_obj}"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
self.merge_two_objs(merged_obj, grid_files['obj'],
|
|
|
|
|
output_obj, translation)
|
|
|
|
|
merged_obj = output_obj
|
|
|
|
|
|
|
|
|
|
# 最后的结果
|
|
|
|
|
final_obj = os.path.join(output_model_dir, "merged_model.obj")
|
|
|
|
|
final_mtl = os.path.join(output_model_dir, "merged_model.mtl")
|
|
|
|
|
|
|
|
|
|
# 保存最终的OBJ和MTL文件
|
|
|
|
|
if os.path.exists(merged_obj) and merged_obj != final_obj:
|
|
|
|
|
shutil.copy2(merged_obj, final_obj)
|
|
|
|
|
os.remove(merged_obj)
|
|
|
|
|
|
|
|
|
|
# 保存合并后的MTL文件
|
|
|
|
|
with open(final_mtl, 'w') as f:
|
|
|
|
|
f.write(updated_mtl)
|
2024-12-31 20:37:35 +08:00
|
|
|
|
|
2024-12-29 12:03:53 +08:00
|
|
|
|
self.logger.info(
|
2024-12-31 22:29:24 +08:00
|
|
|
|
f"模型合并完成,输出目录: {output_model_dir}\n"
|
|
|
|
|
f"- OBJ文件: merged_model.obj\n"
|
|
|
|
|
f"- MTL文件: merged_model.mtl\n"
|
|
|
|
|
f"- 纹理文件: {len(os.listdir(output_model_dir)) - 2}个PNG文件"
|
2024-12-29 12:03:53 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
self.logger.error(f"OBJ模型合并过程中发生错误: {str(e)}", exc_info=True)
|
2024-12-31 14:23:45 +08:00
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
import sys
|
|
|
|
|
sys.path.append(os.path.dirname(
|
|
|
|
|
os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
|
from utils.logger import setup_logger
|
|
|
|
|
import pandas as pd
|
|
|
|
|
|
|
|
|
|
# 设置输出目录和日志
|
|
|
|
|
output_dir = r"G:\ODM_output\1009"
|
|
|
|
|
setup_logger(output_dir)
|
|
|
|
|
|
|
|
|
|
# 构造测试用的grid_points字典
|
|
|
|
|
grid_points = {
|
2024-12-31 21:37:44 +08:00
|
|
|
|
(0, 0): pd.DataFrame({
|
2024-12-31 14:23:45 +08:00
|
|
|
|
'latitude': [39.9, 39.91],
|
|
|
|
|
'longitude': [116.3, 116.31],
|
|
|
|
|
'altitude': [100, 101]
|
|
|
|
|
}),
|
2024-12-31 21:37:44 +08:00
|
|
|
|
(0, 1): pd.DataFrame({
|
2024-12-31 14:23:45 +08:00
|
|
|
|
'latitude': [39.92, 39.93],
|
|
|
|
|
'longitude': [116.32, 116.33],
|
|
|
|
|
'altitude': [102, 103]
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 创建MergeObj实例并执行合并
|
|
|
|
|
merge_obj = MergeObj(output_dir)
|
|
|
|
|
merge_obj.merge_grid_obj(grid_points)
|