合并obj算法更新

This commit is contained in:
龙澳 2024-12-31 22:29:24 +08:00
parent b3d7c37399
commit c8eaf997a2
4 changed files with 242 additions and 83 deletions

View File

@ -2,7 +2,7 @@ import os
import shutil import shutil
from datetime import timedelta from datetime import timedelta
from dataclasses import dataclass from dataclasses import dataclass
from typing import Dict from typing import Dict, Tuple
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import pandas as pd import pandas as pd
@ -197,14 +197,14 @@ class ImagePreprocessor:
return self.gps_points return self.gps_points
def divide_grids(self) -> Dict[tuple, pd.DataFrame]: def divide_grids(self) -> Tuple[Dict[tuple, pd.DataFrame], Dict[tuple, tuple]]:
"""划分网格""" """划分网格"""
self.logger.info(f"开始划分网格 (重叠率: {self.config.grid_overlap})") self.logger.info(f"开始划分网格 (重叠率: {self.config.grid_overlap})")
grid_divider = GridDivider( grid_divider = GridDivider(
overlap=self.config.grid_overlap, overlap=self.config.grid_overlap,
output_dir=self.config.output_dir output_dir=self.config.output_dir
) )
grids = grid_divider.divide_grids( grids, translations = grid_divider.divide_grids(
self.gps_points, grid_size=self.config.grid_size self.gps_points, grid_size=self.config.grid_size
) )
grid_points = grid_divider.assign_to_grids(self.gps_points, grids) grid_points = grid_divider.assign_to_grids(self.gps_points, grids)
@ -212,7 +212,7 @@ class ImagePreprocessor:
# -1是因为包含了grid_divider # -1是因为包含了grid_divider
self.logger.info(f"成功划分为 {len(grid_points)} 个网格") self.logger.info(f"成功划分为 {len(grid_points)} 个网格")
return grid_points return grid_points, translations
def copy_images(self, grid_points: Dict[tuple, pd.DataFrame]): def copy_images(self, grid_points: Dict[tuple, pd.DataFrame]):
"""复制图像到目标文件夹""" """复制图像到目标文件夹"""
@ -238,11 +238,11 @@ class ImagePreprocessor:
merger = MergeTif(self.config.output_dir) merger = MergeTif(self.config.output_dir)
merger.merge_all_tifs(grid_points) merger.merge_all_tifs(grid_points)
def merge_obj(self, grid_points: Dict[int, pd.DataFrame]): def merge_obj(self, grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple]):
"""合并所有网格的OBJ模型""" """合并所有网格的OBJ模型"""
self.logger.info("开始合并OBJ模型") self.logger.info("开始合并OBJ模型")
merger = MergeObj(self.config.output_dir) merger = MergeObj(self.config.output_dir)
merger.merge_grid_obj(grid_points) merger.merge_grid_obj(grid_points, translations)
def merge_ply(self, grid_points: Dict[int, pd.DataFrame]): def merge_ply(self, grid_points: Dict[int, pd.DataFrame]):
"""合并所有网格的PLY点云""" """合并所有网格的PLY点云"""
@ -257,13 +257,13 @@ class ImagePreprocessor:
self.cluster() self.cluster()
# self.filter_time_group_overlap() # self.filter_time_group_overlap()
self.filter_points() self.filter_points()
grid_points = self.divide_grids() grid_points, translations = self.divide_grids()
self.copy_images(grid_points) self.copy_images(grid_points)
self.logger.info("预处理任务完成") self.logger.info("预处理任务完成")
self.odm_monitor.process_all_grids(grid_points) # self.odm_monitor.process_all_grids(grid_points)
# self.merge_tif(grid_points) # self.merge_tif(grid_points)
# self.merge_obj(grid_points) # self.merge_obj(grid_points, translations)
# self.merge_ply(grid_points) # self.merge_ply(grid_points)
except Exception as e: except Exception as e:
self.logger.error(f"处理过程中发生错误: {str(e)}", exc_info=True) self.logger.error(f"处理过程中发生错误: {str(e)}", exc_info=True)

View File

@ -2,7 +2,7 @@ import os
import shutil import shutil
from datetime import timedelta from datetime import timedelta
from dataclasses import dataclass from dataclasses import dataclass
from typing import Dict from typing import Dict, Tuple
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import pandas as pd import pandas as pd
@ -197,20 +197,25 @@ class ImagePreprocessor:
return self.gps_points return self.gps_points
def divide_grids(self) -> Dict[tuple, pd.DataFrame]: def divide_grids(self) -> Tuple[Dict[tuple, pd.DataFrame], Dict[tuple, tuple]]:
"""划分网格""" """划分网格
Returns:
tuple: (grid_points, translations)
- grid_points: 网格点数据字典
- translations: 网格平移量字典
"""
self.logger.info(f"开始划分网格 (重叠率: {self.config.grid_overlap})") self.logger.info(f"开始划分网格 (重叠率: {self.config.grid_overlap})")
grid_divider = GridDivider( grid_divider = GridDivider(
overlap=self.config.grid_overlap, overlap=self.config.grid_overlap,
output_dir=self.config.output_dir output_dir=self.config.output_dir
) )
grids = grid_divider.divide_grids( grids, translations = grid_divider.divide_grids(
self.gps_points, grid_size=self.config.grid_size self.gps_points, grid_size=self.config.grid_size
) )
grid_points = grid_divider.assign_to_grids(self.gps_points, grids) grid_points = grid_divider.assign_to_grids(self.gps_points, grids)
self.logger.info(f"成功划分为 {len(grid_points)} 个网格") self.logger.info(f"成功划分为 {len(grid_points)} 个网格")
return grid_points return grid_points, translations
def copy_images(self, grid_points: Dict[tuple, pd.DataFrame]): def copy_images(self, grid_points: Dict[tuple, pd.DataFrame]):
"""复制图像到目标文件夹""" """复制图像到目标文件夹"""
@ -235,11 +240,11 @@ class ImagePreprocessor:
merger = MergeTif(self.config.output_dir) merger = MergeTif(self.config.output_dir)
merger.merge_all_tifs(grid_points) merger.merge_all_tifs(grid_points)
def merge_obj(self, grid_points: Dict[tuple, pd.DataFrame]): def merge_obj(self, grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple]):
"""合并所有网格的OBJ模型""" """合并所有网格的OBJ模型"""
self.logger.info("开始合并OBJ模型") self.logger.info("开始合并OBJ模型")
merger = MergeObj(self.config.output_dir) merger = MergeObj(self.config.output_dir)
merger.merge_grid_obj(grid_points, self.config.grid_size) merger.merge_grid_obj(grid_points, translations)
def merge_ply(self, grid_points: Dict[tuple, pd.DataFrame]): def merge_ply(self, grid_points: Dict[tuple, pd.DataFrame]):
"""合并所有网格的PLY点云""" """合并所有网格的PLY点云"""
@ -254,14 +259,14 @@ class ImagePreprocessor:
self.cluster() self.cluster()
# self.filter_time_group_overlap() # self.filter_time_group_overlap()
self.filter_points() self.filter_points()
grid_points = self.divide_grids() grid_points, translations = self.divide_grids()
# self.copy_images(grid_points) # self.copy_images(grid_points)
self.logger.info("预处理任务完成") self.logger.info("预处理任务完成")
# self.odm_monitor.process_all_grids(grid_points) # self.odm_monitor.process_all_grids(grid_points)
# self.merge_tif(grid_points) # self.merge_tif(grid_points)
# self.merge_ply(grid_points) # self.merge_ply(grid_points)
self.merge_obj(grid_points) self.merge_obj(grid_points, translations)
except Exception as e: except Exception as e:
self.logger.error(f"处理过程中发生错误: {str(e)}", exc_info=True) self.logger.error(f"处理过程中发生错误: {str(e)}", exc_info=True)
raise raise

View File

@ -3,6 +3,7 @@ import logging
import numpy as np import numpy as np
from typing import Dict from typing import Dict
import pandas as pd import pandas as pd
import shutil
class MergeObj: class MergeObj:
@ -21,9 +22,11 @@ class MergeObj:
if len(parts) == 0: if len(parts) == 0:
continue continue
if parts[0] == 'v': # 顶点 if parts[0] == 'v': # 顶点
vertices.append([float(parts[1]), float(parts[2]), float(parts[3])]) vertices.append(
[float(parts[1]), float(parts[2]), float(parts[3])])
elif parts[0] == 'f': # 面 elif parts[0] == 'f': # 面
faces.append([int(parts[1].split('/')[0]), int(parts[2].split('/')[0]), int(parts[3].split('/')[0])]) faces.append([int(parts[1].split(
'/')[0]), int(parts[2].split('/')[0]), int(parts[3].split('/')[0])])
return vertices, faces return vertices, faces
@ -54,11 +57,14 @@ class MergeObj:
vertices2, faces2 = self.read_obj(obj2_path) vertices2, faces2 = self.read_obj(obj2_path)
# 平移第二个模型的顶点 # 平移第二个模型的顶点
vertices2_translated = self.translate_vertices(vertices2, translation) vertices2_translated = self.translate_vertices(
vertices2, translation)
# 合并顶点和面 # 合并顶点和面
all_vertices = vertices1 + vertices2_translated all_vertices = vertices1 + vertices2_translated
all_faces = faces1 + [[f[0] + len(vertices1), f[1] + len(vertices1), f[2] + len(vertices1)] for f in faces2] all_faces = faces1 + \
[[f[0] + len(vertices1), f[1] + len(vertices1),
f[2] + len(vertices1)] for f in faces2]
# 写入合并后的obj文件 # 写入合并后的obj文件
self.write_obj(output_path, all_vertices, all_faces) self.write_obj(output_path, all_vertices, all_faces)
@ -69,76 +75,161 @@ class MergeObj:
self.logger.error(f"合并OBJ模型时发生错误: {str(e)}", exc_info=True) self.logger.error(f"合并OBJ模型时发生错误: {str(e)}", exc_info=True)
raise raise
def calculate_translation(self, grid_id: tuple, grid_size: float) -> tuple: def read_mtl(self, file_path):
"""根据网格坐标和大小计算平移量""" """读取.mtl文件内容"""
# 直接使用网格的二维坐标计算平移量 with open(file_path, 'r') as file:
col, row = grid_id # grid_id是(width_idx, height_idx)格式 return file.read()
# 计算平移量,考虑到重叠 def copy_texture_files(self, src_dir: str, dst_dir: str, grid_id: tuple):
x_translation = col * grid_size """复制并重命名纹理文件
y_translation = row * grid_size Args:
src_dir: 源纹理文件目录
dst_dir: 目标纹理文件目录
grid_id: 网格ID用于重命名
"""
# 确保目标目录存在
os.makedirs(dst_dir, exist_ok=True)
self.logger.info( # 复制所有png文件并重命名
f"网格 ({col},{row}) 的平移量: x={x_translation}, y={y_translation}" 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}")
return (x_translation, y_translation, 0) # z轴不需要平移 return dst_dir
def merge_grid_obj(self, grid_points: Dict[tuple, pd.DataFrame], grid_size: float = 500): def update_mtl_content(self, mtl_content: str, grid_id: tuple) -> str:
"""合并所有网格的OBJ模型""" """更新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)
def merge_grid_obj(self, grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple]):
"""合并所有网格的OBJ模型和纹理"""
self.logger.info("开始合并所有网格的OBJ模型") self.logger.info("开始合并所有网格的OBJ模型")
if len(grid_points) < 2: if len(grid_points) < 2:
self.logger.info("只有一个网格,无需合并") self.logger.info("只有一个网格,无需合并")
return return
input_obj1, input_obj2 = None, None
merge_count = 0
try: try:
# 创建输出目录
output_model_dir = os.path.join(self.output_dir, "merged_model")
os.makedirs(output_model_dir, exist_ok=True)
# 获取所有有效的网格OBJ文件
grid_objs = {}
for grid_id, points in grid_points.items(): for grid_id, points in grid_points.items():
grid_obj = os.path.join( grid_base_dir = os.path.join(
self.output_dir, self.output_dir,
f"grid_{grid_id[0]}_{grid_id[1]}", f"grid_{grid_id[0]}_{grid_id[1]}",
"project", "project",
"odm_texturing", "odm_texturing"
"odm_textured_model_geo.obj"
) )
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")
if not os.path.exists(grid_obj): 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文件不存在: {grid_obj}") self.logger.warning(
f"网格 ({grid_id[0]},{grid_id[1]}) 的OBJ或MTL文件不存在")
continue continue
if input_obj1 is None: grid_objs[grid_id] = {
input_obj1 = grid_obj 'obj': grid_obj,
self.logger.info(f"设置第一个输入OBJ: {input_obj1}") 'mtl': grid_mtl,
else: 'base_dir': grid_base_dir
input_obj2 = grid_obj }
output_obj = os.path.join(self.output_dir, f"merged_model_{merge_count}.obj")
# 计算当前网格的平移量 if not grid_objs:
translation = self.calculate_translation(grid_id, grid_size) self.logger.error("没有找到有效的OBJ文件")
return
self.logger.info( # 使用第一个网格作为参考
f"开始合并第 {merge_count + 1} 次:\n" reference_id = list(grid_objs.keys())[0]
f"平移量: {translation}\n" merged_obj = grid_objs[reference_id]['obj']
f"输出: {output_obj}"
)
self.merge_two_objs(input_obj1, input_obj2, output_obj, translation) # 复制参考网格的纹理文件
merge_count += 1 self.copy_texture_files(
grid_objs[reference_id]['base_dir'],
output_model_dir,
reference_id
)
input_obj1 = output_obj # 复制并更新参考网格的MTL文件
input_obj2 = None ref_mtl_content = self.read_mtl(grid_objs[reference_id]['mtl'])
updated_mtl = self.update_mtl_content(ref_mtl_content, reference_id)
# 最后的结果重命名为merged_model.obj
final_output = os.path.join(self.output_dir, "merged_model.obj")
if os.path.exists(input_obj1) and input_obj1 != final_output:
os.rename(input_obj1, final_output)
self.logger.info( self.logger.info(
f"OBJ模型合并完成共执行 {merge_count} 次合并," f"使用网格 ({reference_id[0]},{reference_id[1]}) 作为参考网格")
f"最终输出文件: {final_output}"
# 依次合并其他网格
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)
self.logger.info(
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文件"
) )
except Exception as e: except Exception as e:

View File

@ -16,7 +16,12 @@ class GridDivider:
self.num_grids_height = 0 self.num_grids_height = 0
def divide_grids(self, points_df, grid_size=500): def divide_grids(self, points_df, grid_size=500):
"""计算边界框并划分网格""" """计算边界框并划分网格
Returns:
tuple: (grids, translations)
- grids: 网格边界列表
- translations: 网格平移量字典
"""
self.logger.info("开始划分网格") self.logger.info("开始划分网格")
min_lat, max_lat = points_df['lat'].min(), points_df['lat'].max() min_lat, max_lat = points_df['lat'].min(), points_df['lat'].max()
@ -37,7 +42,9 @@ class GridDivider:
lon_step = (max_lon - min_lon) / self.num_grids_width lon_step = (max_lon - min_lon) / self.num_grids_width
grids = [] grids = []
grid_translations = {} # 存储每个网格相对于第一个网格的平移量
# 先创建所有网格
for i in range(self.num_grids_height): for i in range(self.num_grids_height):
for j in range(self.num_grids_width): for j in range(self.num_grids_width):
grid_min_lat = min_lat + i * lat_step - self.overlap * lat_step grid_min_lat = min_lat + i * lat_step - self.overlap * lat_step
@ -46,21 +53,36 @@ class GridDivider:
grid_max_lon = min_lon + (j + 1) * lon_step + self.overlap * lon_step grid_max_lon = min_lon + (j + 1) * lon_step + self.overlap * lon_step
grid_id = (j, i) # 使用(width_idx, height_idx)元组作为网格标识 grid_id = (j, i) # 使用(width_idx, height_idx)元组作为网格标识
grids.append((grid_min_lat, grid_max_lat, grid_min_lon, grid_max_lon)) grid_bounds = (grid_min_lat, grid_max_lat, grid_min_lon, grid_max_lon)
grids.append(grid_bounds)
self.logger.debug( self.logger.debug(
f"网格[{j},{i}]: 纬度[{grid_min_lat:.6f}, {grid_max_lat:.6f}], " f"网格[{j},{i}]: 纬度[{grid_min_lat:.6f}, {grid_max_lat:.6f}], "
f"经度[{grid_min_lon:.6f}, {grid_max_lon:.6f}]" f"经度[{grid_min_lon:.6f}, {grid_max_lon:.6f}]"
) )
# 计算每个网格相对于第一个网格的平移量
reference_grid = grids[0]
for i in range(self.num_grids_height):
for j in range(self.num_grids_width):
grid_id = (j, i)
grid_idx = i * self.num_grids_width + j
if grid_idx == 0: # 参考网格
grid_translations[grid_id] = (0, 0)
else:
translation = self.calculate_grid_translation(reference_grid, grids[grid_idx])
grid_translations[grid_id] = translation
self.logger.debug(
f"网格[{j},{i}]相对于参考网格的平移量: x={translation[0]:.2f}m, y={translation[1]:.2f}m"
)
self.logger.info( self.logger.info(
f"成功划分为 {len(grids)} 个网格 ({self.num_grids_width}x{self.num_grids_height})") f"成功划分为 {len(grids)} 个网格 ({self.num_grids_width}x{self.num_grids_height})")
# 添加可视化调用 # 添加可视化调用
self.visualize_grids(points_df, grids) self.visualize_grids(points_df, grids)
return grids return grids, grid_translations
def assign_to_grids(self, points_df, grids): def assign_to_grids(self, points_df, grids):
@ -141,3 +163,44 @@ class GridDivider:
self.logger.info(f"网格划分可视化图已保存至: {save_path}") self.logger.info(f"网格划分可视化图已保存至: {save_path}")
plt.close() plt.close()
def get_grid_center(self, grid_bounds) -> tuple:
"""计算网格中心点的经纬度
Args:
grid_bounds: (min_lat, max_lat, min_lon, max_lon)
Returns:
(center_lat, center_lon)
"""
min_lat, max_lat, min_lon, max_lon = grid_bounds
return ((min_lat + max_lat) / 2, (min_lon + max_lon) / 2)
def calculate_grid_translation(self, reference_grid: tuple, target_grid: tuple) -> tuple:
"""计算目标网格相对于参考网格的平移距离(米)
Args:
reference_grid: 参考网格的边界 (min_lat, max_lat, min_lon, max_lon)
target_grid: 目标网格的边界 (min_lat, max_lat, min_lon, max_lon)
Returns:
(x_translation, y_translation): 在米制单位下的平移量
"""
ref_center = self.get_grid_center(reference_grid)
target_center = self.get_grid_center(target_grid)
# 计算经度方向的距离x轴
x_distance = geodesic(
(ref_center[0], ref_center[1]),
(ref_center[0], target_center[1])
).meters
# 如果目标在参考点西边,距离为负
if target_center[1] < ref_center[1]:
x_distance = -x_distance
# 计算纬度方向的距离y轴
y_distance = geodesic(
(ref_center[0], ref_center[1]),
(target_center[0], ref_center[1])
).meters
# 如果目标在参考点南边,距离为负
if target_center[0] < ref_center[0]:
y_distance = -y_distance
return (x_distance, y_distance)