From c8eaf997a2a769b3cd46d588c35d6d4808fd70cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=99=E6=BE=B3?= Date: Tue, 31 Dec 2024 22:29:24 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=88=E5=B9=B6obj=E7=AE=97=E6=B3=95?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- odm_preprocess.py | 18 ++-- odm_preprocess_fast.py | 23 +++-- post_pro/merge_obj.py | 211 +++++++++++++++++++++++++++++------------ utils/grid_divider.py | 73 +++++++++++++- 4 files changed, 242 insertions(+), 83 deletions(-) diff --git a/odm_preprocess.py b/odm_preprocess.py index 5977ddf..da61dda 100644 --- a/odm_preprocess.py +++ b/odm_preprocess.py @@ -2,7 +2,7 @@ import os import shutil from datetime import timedelta from dataclasses import dataclass -from typing import Dict +from typing import Dict, Tuple import matplotlib.pyplot as plt import pandas as pd @@ -197,14 +197,14 @@ class ImagePreprocessor: 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})") grid_divider = GridDivider( overlap=self.config.grid_overlap, 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 ) grid_points = grid_divider.assign_to_grids(self.gps_points, grids) @@ -212,7 +212,7 @@ class ImagePreprocessor: # -1是因为包含了grid_divider self.logger.info(f"成功划分为 {len(grid_points)} 个网格") - return grid_points + return grid_points, translations def copy_images(self, grid_points: Dict[tuple, pd.DataFrame]): """复制图像到目标文件夹""" @@ -238,11 +238,11 @@ class ImagePreprocessor: merger = MergeTif(self.config.output_dir) 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模型""" self.logger.info("开始合并OBJ模型") 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]): """合并所有网格的PLY点云""" @@ -257,13 +257,13 @@ class ImagePreprocessor: self.cluster() # self.filter_time_group_overlap() self.filter_points() - grid_points = self.divide_grids() + grid_points, translations = self.divide_grids() self.copy_images(grid_points) 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_obj(grid_points) + # self.merge_obj(grid_points, translations) # self.merge_ply(grid_points) except Exception as e: self.logger.error(f"处理过程中发生错误: {str(e)}", exc_info=True) diff --git a/odm_preprocess_fast.py b/odm_preprocess_fast.py index 8035277..0903226 100644 --- a/odm_preprocess_fast.py +++ b/odm_preprocess_fast.py @@ -2,7 +2,7 @@ import os import shutil from datetime import timedelta from dataclasses import dataclass -from typing import Dict +from typing import Dict, Tuple import matplotlib.pyplot as plt import pandas as pd @@ -197,20 +197,25 @@ class ImagePreprocessor: 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})") grid_divider = GridDivider( overlap=self.config.grid_overlap, 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 ) grid_points = grid_divider.assign_to_grids(self.gps_points, grids) self.logger.info(f"成功划分为 {len(grid_points)} 个网格") - return grid_points + return grid_points, translations def copy_images(self, grid_points: Dict[tuple, pd.DataFrame]): """复制图像到目标文件夹""" @@ -235,11 +240,11 @@ class ImagePreprocessor: merger = MergeTif(self.config.output_dir) 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模型""" self.logger.info("开始合并OBJ模型") 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]): """合并所有网格的PLY点云""" @@ -254,14 +259,14 @@ class ImagePreprocessor: self.cluster() # self.filter_time_group_overlap() self.filter_points() - grid_points = self.divide_grids() + grid_points, translations = self.divide_grids() # self.copy_images(grid_points) self.logger.info("预处理任务完成") # self.odm_monitor.process_all_grids(grid_points) # self.merge_tif(grid_points) # self.merge_ply(grid_points) - self.merge_obj(grid_points) + self.merge_obj(grid_points, translations) except Exception as e: self.logger.error(f"处理过程中发生错误: {str(e)}", exc_info=True) raise diff --git a/post_pro/merge_obj.py b/post_pro/merge_obj.py index edeb48f..a69c581 100644 --- a/post_pro/merge_obj.py +++ b/post_pro/merge_obj.py @@ -3,6 +3,7 @@ import logging import numpy as np from typing import Dict import pandas as pd +import shutil class MergeObj: @@ -14,17 +15,19 @@ class MergeObj: """读取.obj文件,返回顶点列表和面列表""" vertices = [] faces = [] - + with open(file_path, 'r') as file: for line in file: parts = line.split() if len(parts) == 0: continue 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': # 面 - 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 def write_obj(self, file_path, vertices, faces): @@ -32,7 +35,7 @@ class MergeObj: with open(file_path, 'w') as file: for vertex in vertices: file.write(f"v {vertex[0]} {vertex[1]} {vertex[2]}\n") - + for face in faces: file.write(f"f {face[0]} {face[1]} {face[2]}\n") @@ -44,7 +47,7 @@ class MergeObj: """合并两个OBJ文件""" try: self.logger.info(f"开始合并OBJ模型:\n输入1: {obj1_path}\n输入2: {obj2_path}") - + # 检查输入文件是否存在 if not os.path.exists(obj1_path) or not os.path.exists(obj2_path): raise FileNotFoundError("输入模型文件不存在") @@ -52,93 +55,181 @@ class MergeObj: # 读取两个obj文件 vertices1, faces1 = self.read_obj(obj1_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_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文件 self.write_obj(output_path, all_vertices, all_faces) - + self.logger.info(f"模型合并成功,已保存至: {output_path}") except Exception as e: self.logger.error(f"合并OBJ模型时发生错误: {str(e)}", exc_info=True) raise - def calculate_translation(self, grid_id: tuple, grid_size: float) -> tuple: - """根据网格坐标和大小计算平移量""" - # 直接使用网格的二维坐标计算平移量 - col, row = grid_id # grid_id是(width_idx, height_idx)格式 - - # 计算平移量,考虑到重叠 - x_translation = col * grid_size - y_translation = row * grid_size - - self.logger.info( - f"网格 ({col},{row}) 的平移量: x={x_translation}, y={y_translation}" - ) - - return (x_translation, y_translation, 0) # z轴不需要平移 + def read_mtl(self, file_path): + """读取.mtl文件内容""" + with open(file_path, 'r') as file: + return file.read() - def merge_grid_obj(self, grid_points: Dict[tuple, pd.DataFrame], grid_size: float = 500): - """合并所有网格的OBJ模型""" + 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) + + # 复制所有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}") + + 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) + + def merge_grid_obj(self, grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple]): + """合并所有网格的OBJ模型和纹理""" self.logger.info("开始合并所有网格的OBJ模型") if len(grid_points) < 2: self.logger.info("只有一个网格,无需合并") return - input_obj1, input_obj2 = None, None - merge_count = 0 - 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(): - grid_obj = os.path.join( + grid_base_dir = os.path.join( self.output_dir, f"grid_{grid_id[0]}_{grid_id[1]}", "project", - "odm_texturing", - "odm_textured_model_geo.obj" + "odm_texturing" ) + 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): - self.logger.warning(f"网格 ({grid_id[0]},{grid_id[1]}) 的OBJ文件不存在: {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或MTL文件不存在") continue - if input_obj1 is None: - input_obj1 = grid_obj - self.logger.info(f"设置第一个输入OBJ: {input_obj1}") - else: - input_obj2 = grid_obj - output_obj = os.path.join(self.output_dir, f"merged_model_{merge_count}.obj") + grid_objs[grid_id] = { + 'obj': grid_obj, + 'mtl': grid_mtl, + 'base_dir': grid_base_dir + } - # 计算当前网格的平移量 - translation = self.calculate_translation(grid_id, grid_size) - - self.logger.info( - f"开始合并第 {merge_count + 1} 次:\n" - f"平移量: {translation}\n" - f"输出: {output_obj}" - ) + if not grid_objs: + self.logger.error("没有找到有效的OBJ文件") + return - self.merge_two_objs(input_obj1, input_obj2, output_obj, translation) - merge_count += 1 + # 使用第一个网格作为参考 + 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]}) 作为参考网格") - input_obj1 = output_obj - input_obj2 = None + # 依次合并其他网格 + 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平移 - # 最后的结果重命名为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) + 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"OBJ模型合并完成,共执行 {merge_count} 次合并," - f"最终输出文件: {final_output}" + 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: diff --git a/utils/grid_divider.py b/utils/grid_divider.py index 7f603e0..2ce817c 100644 --- a/utils/grid_divider.py +++ b/utils/grid_divider.py @@ -16,7 +16,12 @@ class GridDivider: self.num_grids_height = 0 def divide_grids(self, points_df, grid_size=500): - """计算边界框并划分网格""" + """计算边界框并划分网格 + Returns: + tuple: (grids, translations) + - grids: 网格边界列表 + - translations: 网格平移量字典 + """ self.logger.info("开始划分网格") 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 grids = [] + grid_translations = {} # 存储每个网格相对于第一个网格的平移量 + # 先创建所有网格 for i in range(self.num_grids_height): for j in range(self.num_grids_width): 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_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( f"网格[{j},{i}]: 纬度[{grid_min_lat:.6f}, {grid_max_lat:.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( f"成功划分为 {len(grids)} 个网格 ({self.num_grids_width}x{self.num_grids_height})") - # 添加可视化调用 self.visualize_grids(points_df, grids) - return grids + return grids, grid_translations def assign_to_grids(self, points_df, grids): @@ -141,3 +163,44 @@ class GridDivider: self.logger.info(f"网格划分可视化图已保存至: {save_path}") 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)