diff --git a/odm_preprocess.py b/odm_preprocess.py index b948447..a58afc1 100644 --- a/odm_preprocess.py +++ b/odm_preprocess.py @@ -208,7 +208,11 @@ class ImagePreprocessor: 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)} 个网格") + + # 将grid_divider添加到grid_points中 + grid_points['grid_divider'] = grid_divider + + self.logger.info(f"成功划分为 {len(grid_points)-1} 个网格") # -1是因为包含了grid_divider # 生成image_groups.txt文件 try: diff --git a/post_pro/merge_obj.py b/post_pro/merge_obj.py index dc6f8d0..72f6252 100644 --- a/post_pro/merge_obj.py +++ b/post_pro/merge_obj.py @@ -10,50 +10,89 @@ class MergeObj: self.output_dir = output_dir self.logger = logging.getLogger('UAV_Preprocess.MergeObj') - def merge_two_objs(self, obj1_path: str, obj2_path: str, output_path: str): - """使用Open3D合并两个OBJ文件""" - try: - self.logger.info("开始合并OBJ模型") - self.logger.info(f"输入模型1: {obj1_path}") - self.logger.info(f"输入模型2: {obj2_path}") - self.logger.info(f"输出模型: {output_path}") + def read_obj(self, file_path): + """读取.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])]) + elif parts[0] == 'f': # 面 + 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): + """将修改后的顶点和面列表写入到.obj文件""" + 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") + + 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文件""" + 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("输入模型文件不存在") - # 读取OBJ文件 - mesh1 = o3d.io.read_triangle_mesh(obj1_path) - mesh2 = o3d.io.read_triangle_mesh(obj2_path) - - if mesh1.is_empty() or mesh2.is_empty(): - raise ValueError("无法读取OBJ文件或文件为空") - - # # 计算并对齐中心点 - # center1 = mesh1.get_center() - # center2 = mesh2.get_center() - # translation_vector = center2 - center1 - # mesh2.translate(translation_vector) - - # 不对齐,直接合并网格 - combined_mesh = mesh1 + mesh2 - - # 优化合并后的网格 - combined_mesh.remove_duplicated_vertices() - combined_mesh.remove_duplicated_triangles() - combined_mesh.compute_vertex_normals() - - # 保存合并后的模型 - if not o3d.io.write_triangle_mesh(output_path, combined_mesh): - raise RuntimeError("保存合并后的模型失败") - + # 读取两个obj文件 + vertices1, faces1 = self.read_obj(obj1_path) + vertices2, faces2 = self.read_obj(obj2_path) + + # 平移第二个模型的顶点 + 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] + + # 写入合并后的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 merge_grid_obj(self, grid_points: Dict[int, pd.DataFrame]): + def calculate_translation(self, grid_idx: int, grid_points: Dict[int, pd.DataFrame], grid_size: float) -> tuple: + """根据网格索引和大小计算平移量""" + # 从grid_points中获取网格划分器 + grid_divider = grid_points.get('grid_divider', None) + if grid_divider is None: + # 如果没有grid_divider,使用默认的计算方式 + row = grid_idx // 2 + col = grid_idx % 2 + else: + # 使用grid_divider获取正确的网格坐标 + row, col = grid_divider.get_grid_coordinates(grid_idx) + + # 计算平移量,考虑到重叠 + overlap_factor = 0.9 # 重叠因子,与grid_divider中的overlap对应 + x_translation = col * grid_size * overlap_factor + y_translation = row * grid_size * overlap_factor + + self.logger.info( + f"网格 {grid_idx} 的位置: 行={row}, 列={col}" + ) + + return (x_translation, y_translation, 0) # z轴不需要平移 + + def merge_grid_obj(self, grid_points: Dict[int, pd.DataFrame], grid_size: float = 500): """合并所有网格的OBJ模型""" self.logger.info("开始合并所有网格的OBJ模型") @@ -66,17 +105,19 @@ class MergeObj: try: for grid_idx, points in grid_points.items(): + if grid_idx == 'grid_divider': # 跳过grid_divider对象 + continue + grid_obj = os.path.join( self.output_dir, f"grid_{grid_idx + 1}", "project", - "odm_texturing_25d", + "odm_texturing", "odm_textured_model_geo.obj" ) if not os.path.exists(grid_obj): - self.logger.warning( - f"网格 {grid_idx + 1} 的OBJ文件不存在: {grid_obj}") + self.logger.warning(f"网格 {grid_idx + 1} 的OBJ文件不存在: {grid_obj}") continue if input_obj1 is None: @@ -84,25 +125,31 @@ class MergeObj: self.logger.info(f"设置第一个输入OBJ: {input_obj1}") else: input_obj2 = grid_obj - output_obj = os.path.join( - self.output_dir, "merged_model.obj") + output_obj = os.path.join(self.output_dir, f"merged_model_{merge_count}.obj") + # 计算当前网格的平移量 + translation = self.calculate_translation(grid_idx, grid_points, grid_size) + self.logger.info( f"开始合并第 {merge_count + 1} 次:\n" - f"输入1: {input_obj1}\n" - f"输入2: {input_obj2}\n" + f"平移量: {translation}\n" f"输出: {output_obj}" ) - self.merge_two_objs(input_obj1, input_obj2, output_obj) + self.merge_two_objs(input_obj1, input_obj2, output_obj, translation) merge_count += 1 input_obj1 = output_obj input_obj2 = None + # 最后的结果重命名为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( f"OBJ模型合并完成,共执行 {merge_count} 次合并," - f"最终输出文件: {input_obj1}" + f"最终输出文件: {final_output}" ) except Exception as e: diff --git a/tools/merge_obj.py b/tools/merge_obj.py deleted file mode 100644 index 2b3e714..0000000 --- a/tools/merge_obj.py +++ /dev/null @@ -1,34 +0,0 @@ -import open3d as o3d -import numpy as np - -# 读取 .obj 文件 -def load_obj(file_path): - mesh = o3d.io.read_triangle_mesh(file_path) - if not mesh.is_empty(): - return mesh - else: - raise ValueError(f"Failed to load {file_path}") - -# 合并两个网格 -def merge_meshes(mesh1, mesh2): - # 直接合并网格 - combined_mesh = mesh1 + mesh2 - return combined_mesh - -# 保存合并后的网格 -def save_merged_mesh(mesh, output_path): - o3d.io.write_triangle_mesh(output_path, mesh) - print(f"Saved merged mesh to {output_path}") - -# 示例用法 -mesh1 = load_obj("model1.obj") -mesh2 = load_obj("model2.obj") - -# 合并两个网格 -merged_mesh = merge_meshes(mesh1, mesh2) - -# 保存合并后的网格 -save_merged_mesh(merged_mesh, "merged_model.obj") - -# 可视化合并后的网格 -o3d.visualization.draw_geometries([merged_mesh]) diff --git a/tools/merge_two_obj.py b/tools/merge_two_obj.py new file mode 100644 index 0000000..a7f0c42 --- /dev/null +++ b/tools/merge_two_obj.py @@ -0,0 +1,65 @@ +def read_obj(file_path): + """ + 读取.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])]) + elif parts[0] == 'f': # 面 + faces.append([int(parts[1].split('/')[0]), int(parts[2].split('/')[0]), int(parts[3].split('/')[0])]) + + return vertices, faces + +def write_obj(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") + + for face in faces: + file.write(f"f {face[0]} {face[1]} {face[2]}\n") + +def translate_vertices(vertices, translation): + """ + 平移顶点,translation 是一个三维向量,例如 (500, 0, 0) 会沿 X 轴平移 500 米 + """ + return [[v[0] + translation[0], v[1] + translation[1], v[2] + translation[2]] for v in vertices] + +def merge_objs(obj1_path, obj2_path, output_path, translation=(500, 0, 0)): + """ + 合并两个.obj文件,并对第二个文件的顶点进行平移 + obj1_path 和 obj2_path 是输入的.obj文件路径 + output_path 是输出的合并后的.obj文件路径 + translation 是平移向量,默认为沿 X 轴平移 500 米 + """ + # 读取第一个 obj 文件 + vertices1, faces1 = read_obj(obj1_path) + + # 读取第二个 obj 文件 + vertices2, faces2 = read_obj(obj2_path) + + # 平移第二个 obj 文件的顶点 + vertices2_translated = 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] + + # 写入合并后的 obj 文件 + write_obj(output_path, all_vertices, all_faces) + print(f"合并完成,结果保存在 {output_path}") + +# 示例调用 +obj1_path = 'model1.obj' # 第一个 .obj 文件 +obj2_path = 'model2.obj' # 第二个 .obj 文件 +output_path = 'merged_model.obj' # 输出合并后的 .obj 文件 +merge_objs(obj1_path, obj2_path, output_path, translation=(500, 0, 0)) diff --git a/utils/grid_divider.py b/utils/grid_divider.py index f4fd251..459d07d 100644 --- a/utils/grid_divider.py +++ b/utils/grid_divider.py @@ -12,10 +12,12 @@ class GridDivider: self.output_dir = output_dir self.logger = logging.getLogger('UAV_Preprocess.GridDivider') self.logger.info(f"初始化网格划分器,重叠率: {overlap}") + self.num_grids_width = 0 # 添加网格数量属性 + self.num_grids_height = 0 def divide_grids(self, points_df, grid_size=500): - """计算边界框并划分九宫格""" - self.logger.info("开始划分九宫格") + """计算边界框并划分网格""" + self.logger.info("开始划分网格") min_lat, max_lat = points_df['lat'].min(), points_df['lat'].max() min_lon, max_lon = points_df['lon'].min(), points_df['lon'].max() @@ -24,45 +26,55 @@ class GridDivider: width = geodesic((min_lat, min_lon), (min_lat, max_lon)).meters height = geodesic((min_lat, min_lon), (max_lat, min_lon)).meters - self.logger.info( - f"区域宽度: {width:.2f}米, 高度: {height:.2f}米" - ) + self.logger.info(f"区域宽度: {width:.2f}米, 高度: {height:.2f}米") # 计算需要划分的网格数量 - num_grids_width = int( - width / grid_size) if int(width / grid_size) > 0 else 1 - num_grids_height = int( - height / grid_size) if int(height / grid_size) > 0 else 1 + self.num_grids_width = max(int(width / grid_size), 1) + self.num_grids_height = max(int(height / grid_size), 1) # 计算每个网格对应的经纬度步长 - lat_step = (max_lat - min_lat) / num_grids_height - lon_step = (max_lon - min_lon) / num_grids_width + lat_step = (max_lat - min_lat) / self.num_grids_height + lon_step = (max_lon - min_lon) / self.num_grids_width grids = [] - for i in range(num_grids_height): - for j in range(num_grids_width): - grid_min_lat = min_lat + i * lat_step - self.overlap * lat_step - grid_max_lat = min_lat + \ - (i + 1) * lat_step + self.overlap * lat_step - grid_min_lon = min_lon + j * lon_step - self.overlap * lon_step - grid_max_lon = min_lon + \ - (j + 1) * lon_step + self.overlap * lon_step - grids.append((grid_min_lat, grid_max_lat, - grid_min_lon, grid_max_lon)) + grid_indices = {} # 存储网格的二维索引 + grid_idx = 0 + 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 + grid_max_lat = min_lat + (i + 1) * lat_step + self.overlap * lat_step + grid_min_lon = min_lon + j * lon_step - self.overlap * lon_step + grid_max_lon = min_lon + (j + 1) * lon_step + self.overlap * lon_step + + grids.append((grid_min_lat, grid_max_lat, grid_min_lon, grid_max_lon)) + grid_indices[grid_idx] = (i, j) # 存储每个网格的行列索引 + self.logger.debug( - f"网格[{i},{j}]: 纬度[{grid_min_lat:.6f}, {grid_max_lat:.6f}], " + f"网格[{i},{j}] (索引{grid_idx}): 纬度[{grid_min_lat:.6f}, {grid_max_lat:.6f}], " f"经度[{grid_min_lon:.6f}, {grid_max_lon:.6f}]" ) + grid_idx += 1 self.logger.info( - f"成功划分为 {len(grids)} 个网格 ({num_grids_width}x{num_grids_height})") - + f"成功划分为 {len(grids)} 个网格 ({self.num_grids_width}x{self.num_grids_height})") + + # 保存网格索引信息 + self.grid_indices = grid_indices + # 添加可视化调用 self.visualize_grids(points_df, grids) return grids + def get_grid_coordinates(self, grid_idx): + """获取网格的二维坐标""" + return self.grid_indices.get(grid_idx, (0, 0)) + + def get_grid_dimensions(self): + """获取网格的维度""" + return self.num_grids_width, self.num_grids_height + def assign_to_grids(self, points_df, grids): """将点分配到对应网格""" self.logger.info(f"开始将 {len(points_df)} 个点分配到网格中")