diff --git a/main.py b/main.py index a51cd62..526a90f 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,6 @@ import argparse from datetime import timedelta -from odm_preprocess import PreprocessConfig, ImagePreprocessor +from odm_preprocess_fast import PreprocessConfig, ImagePreprocessor def parse_args(): parser = argparse.ArgumentParser(description='ODM预处理工具') diff --git a/odm_preprocess.py b/odm_preprocess.py index 094e00a..351b913 100644 --- a/odm_preprocess.py +++ b/odm_preprocess.py @@ -18,7 +18,7 @@ from utils.grid_divider import GridDivider from utils.logger import setup_logger from utils.visualizer import FilterVisualizer from post_pro.merge_tif import MergeTif -from post_pro.merge_obj import MergeObj +from post_pro.obj_post_pro import ObjPostProcessor from post_pro.merge_laz import MergePly @@ -257,8 +257,8 @@ class ImagePreprocessor: 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, translations) + processor = ObjPostProcessor(self.config.output_dir) + processor.process_obj_files() def post_process(self, successful_grid_points: Dict[tuple, pd.DataFrame], grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple]): """后处理:合并或复制处理结果""" diff --git a/odm_preprocess_fast.py b/odm_preprocess_fast.py index d97a795..02f6a15 100644 --- a/odm_preprocess_fast.py +++ b/odm_preprocess_fast.py @@ -18,7 +18,7 @@ from utils.grid_divider import GridDivider from utils.logger import setup_logger from utils.visualizer import FilterVisualizer from post_pro.merge_tif import MergeTif -from post_pro.merge_obj import MergeObj +from post_pro.obj_post_pro import ObjPostProcessor from post_pro.merge_laz import MergePly @@ -187,6 +187,21 @@ class ImagePreprocessor: self.visualizer.visualize_filter_step( self.gps_points, previous_points, "3-Time Group Overlap") + def filter_alternate_images(self): + """按时间顺序隔一个删一个图像来降低密度""" + previous_points = self.gps_points.copy() + + # 按时间戳排序 + self.gps_points = self.gps_points.sort_values('date') + + # 保留索引为偶数的行(即隔一个保留一个) + self.gps_points = self.gps_points.iloc[::2].reset_index(drop=True) + + self.visualizer.visualize_filter_step( + self.gps_points, previous_points, "4-Alternate Images") + + self.logger.info(f"交替过滤后剩余 {len(self.gps_points)} 个点") + def divide_grids(self) -> Tuple[Dict[tuple, pd.DataFrame], Dict[tuple, tuple]]: """划分网格 Returns: @@ -220,7 +235,7 @@ class ImagePreprocessor: os.makedirs(output_dir, exist_ok=True) - for point in tqdm(points, desc=f"复制网格 ({grid_id[0]},{grid_id[1]}) 的图像"): + for point in points: src = os.path.join(self.config.image_dir, point["file"]) dst = os.path.join(output_dir, point["file"]) shutil.copy(src, dst) @@ -242,8 +257,8 @@ class ImagePreprocessor: 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, translations) + processor = ObjPostProcessor(self.config.output_dir) + processor.process_obj_files() def post_process(self, successful_grid_points: Dict[tuple, pd.DataFrame], grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple]): """后处理:合并或复制处理结果""" @@ -259,8 +274,8 @@ class ImagePreprocessor: self.merge_ply(successful_grid_points) self.merge_obj(successful_grid_points, translations) else: - self.merge_tif(successful_grid_points, self.config.produce_dem) - self.merge_ply(successful_grid_points) + # self.merge_tif(successful_grid_points, self.config.produce_dem) + # self.merge_ply(successful_grid_points) self.merge_obj(successful_grid_points, translations) def process(self): @@ -270,6 +285,7 @@ class ImagePreprocessor: self.cluster() self.filter_isolated_points() self.filter_time_group_overlap() + # self.filter_alternate_images() grid_points, translations = self.divide_grids() # self.copy_images(grid_points) self.logger.info("预处理任务完成") diff --git a/post_pro/obj_post_pro.py b/post_pro/obj_post_pro.py new file mode 100644 index 0000000..6676a04 --- /dev/null +++ b/post_pro/obj_post_pro.py @@ -0,0 +1,182 @@ +import os +import logging +import subprocess +import shutil +from typing import List, Tuple +import numpy as np + + +class ObjPostProcessor: + def __init__(self, output_dir: str): + self.output_dir = output_dir + self.logger = logging.getLogger('UAV_Preprocess.ObjPostProcessor') + # 用于存储所有grid的UTM范围 + self.min_east = float('inf') + self.min_north = float('inf') + self.max_east = float('-inf') + self.max_north = float('-inf') + + def process_obj_files(self): + """处理所有grid中的obj文件""" + try: + # 1. 遍历所有grid文件夹 + grid_dirs = [d for d in os.listdir( + self.output_dir) if d.startswith('grid_')] + + # 第一次遍历:获取所有grid的UTM范围 + for grid_dir in grid_dirs: + grid_path = os.path.join(self.output_dir, grid_dir) + log_file = os.path.join( + grid_path, 'project', 'odm_orthophoto', 'odm_orthophoto_log.txt') + east_offset, north_offset = self.read_utm_offset(log_file) + + # 更新UTM范围 + self.min_east = min(self.min_east, east_offset) + self.min_north = min(self.min_north, north_offset) + self.max_east = max(self.max_east, east_offset) + self.max_north = max(self.max_north, north_offset) + + # 创建osgb输出目录 + osgb_dir = os.path.join(self.output_dir, 'osgb') + os.makedirs(os.path.join(osgb_dir, 'Data'), exist_ok=True) + + # 第二次遍历:处理每个grid + for grid_dir in grid_dirs: + grid_path = os.path.join(self.output_dir, grid_dir) + self.process_single_grid(grid_path, osgb_dir) + + # 创建metadata.xml + self.create_metadata_xml(osgb_dir) + + self.logger.info("所有grid处理完成") + return True + except Exception as e: + self.logger.error(f"处理obj文件时发生错误: {str(e)}") + return False + + def process_single_grid(self, grid_path: str, osgb_dir: str): + """处理单个grid的obj文件""" + try: + # 1. 读取UTM偏移量 + log_file = os.path.join( + grid_path, 'project', 'odm_orthophoto', 'odm_orthophoto_log.txt') + utm_offset = self.read_utm_offset(log_file) + + # 2. 修改obj文件的顶点坐标 + obj_file = os.path.join( + grid_path, 'project', 'odm_texturing', 'odm_textured_model_geo.obj') + modified_obj = self.modify_obj_coordinates(obj_file, utm_offset) + + # 3. 使用osgconv转换为osgb + grid_name = os.path.basename(grid_path) + self.convert_to_osgb(modified_obj, grid_name, osgb_dir) + + except Exception as e: + self.logger.error(f"处理grid {grid_path} 时发生错误: {str(e)}") + raise + + def read_utm_offset(self, log_file: str) -> Tuple[float, float]: + """读取UTM偏移量""" + try: + east_offset = None + north_offset = None + + with open(log_file, 'r') as f: + lines = f.readlines() + for i, line in enumerate(lines): + if 'utm_north_offset' in line and i + 1 < len(lines): + north_offset = float(lines[i + 1].strip()) + elif 'utm_east_offset' in line and i + 1 < len(lines): + east_offset = float(lines[i + 1].strip()) + + if east_offset is None or north_offset is None: + raise ValueError("未找到UTM偏移量") + + return east_offset, north_offset + except Exception as e: + self.logger.error(f"读取UTM偏移量时发生错误: {str(e)}") + raise + + def modify_obj_coordinates(self, obj_file: str, utm_offset: Tuple[float, float]) -> str: + """修改obj文件中的顶点坐标,使用相对坐标系""" + east_offset, north_offset = utm_offset + output_obj = obj_file.replace('.obj', '_utm.obj') + + try: + with open(obj_file, 'r') as f_in, open(output_obj, 'w') as f_out: + for line in f_in: + if line.startswith('v '): + # 处理顶点坐标行 + parts = line.strip().split() + # 使用相对于整体最小UTM坐标的偏移 + x = float(parts[1]) + (east_offset - self.min_east) + y = float(parts[2]) + (north_offset - self.min_north) + z = float(parts[3]) + f_out.write(f'v {x:.6f} {y:.6f} {z:.6f}\n') + else: + # 其他行直接写入 + f_out.write(line) + + # 复制材质文件 + mtl_file = obj_file.replace('.obj', '.mtl') + if os.path.exists(mtl_file): + shutil.copy2(mtl_file, mtl_file.replace('.mtl', '_utm.mtl')) + + return output_obj + except Exception as e: + self.logger.error(f"修改obj坐标时发生错误: {str(e)}") + raise + + def convert_to_osgb(self, obj_file: str, grid_name: str, osgb_dir: str): + """使用osgconv将obj转换为osgb""" + try: + # 创建tile目录 + tile_dir = os.path.join(osgb_dir, 'Data', grid_name) + os.makedirs(tile_dir, exist_ok=True) + + output_osgb = os.path.join(tile_dir, f'{grid_name}.osgb') + + # 构建osgconv命令 + cmd = [ + 'osgconv', + '--compressed', + '--smooth', + '--fix-transparency', + '-o', '0,1,0-0,0,-1', + obj_file, + output_osgb + ] + + # 执行命令 + self.logger.info(f"执行osgconv命令:{cmd}") + result = subprocess.run(cmd, capture_output=True, text=True) + + if result.returncode != 0: + raise Exception(f"osgconv执行失败: {result.stderr}") + + self.logger.info(f"转换完成: {output_osgb}") + + except Exception as e: + self.logger.error(f"转换osgb时发生错误: {str(e)}") + raise + + def create_metadata_xml(self, osgb_dir: str): + """创建metadata.xml文件,包含UTM偏移信息""" + try: + # 这里需要将UTM坐标转换为WGS84经纬度坐标 + # 这里使用示例值,实际应用中需要进行真实的坐标转换 + metadata_content = f''' + + EPSG:32649 + {self.min_east:.6f},{self.min_north:.6f},0.000000 + + Visible + +''' + + with open(os.path.join(osgb_dir, 'metadata.xml'), 'w') as f: + f.write(metadata_content) + + except Exception as e: + self.logger.error(f"创建metadata.xml时发生错误: {str(e)}") + raise