From 86940bd1b976893413ee5d70a06acb14dd02a643 Mon Sep 17 00:00:00 2001 From: weixin_46229132 Date: Sat, 15 Feb 2025 14:53:02 +0800 Subject: [PATCH] =?UTF-8?q?obj=E5=88=B0osgb=E7=9A=84=E5=9D=90=E6=A0=87?= =?UTF-8?q?=E7=B3=BB=E5=8F=98=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 2 +- odm_preprocess.py | 15 ++--- odm_preprocess_fast.py | 15 ++--- post_pro/conv_obj.py | 130 ++++++++++++++++++++++++----------------- 4 files changed, 89 insertions(+), 73 deletions(-) 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 3c4a5ce..702a600 100644 --- a/odm_preprocess.py +++ b/odm_preprocess.py @@ -265,13 +265,13 @@ class ImagePreprocessor: merger = MergeObj(self.config.output_dir) merger.merge_grid_obj(grid_points, translations) - def convert_obj(self, grid_points: Dict[tuple, pd.DataFrame], center_lat: float, center_lon: float): + def convert_obj(self, grid_points: Dict[tuple, pd.DataFrame]): """转换OBJ模型""" self.logger.info("开始转换OBJ模型") - converter = ConvertOBJ(self.config.output_dir, center_lat, center_lon) + converter = ConvertOBJ(self.config.output_dir) converter.convert_grid_obj(grid_points) - def post_process(self, successful_grid_points: Dict[tuple, pd.DataFrame], grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple], center_lat: float, center_lon: float): + def post_process(self, successful_grid_points: Dict[tuple, pd.DataFrame], grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple]): """后处理:合并或复制处理结果""" if len(successful_grid_points) < len(grid_points): self.logger.warning( @@ -284,12 +284,12 @@ class ImagePreprocessor: elif self.config.mode == "三维模式": # self.merge_ply(successful_grid_points) # self.merge_obj(successful_grid_points, translations) - self.convert_obj(successful_grid_points, center_lat, center_lon) + self.convert_obj(successful_grid_points) else: self.merge_tif(successful_grid_points, self.config.produce_dem) # self.merge_ply(successful_grid_points) # self.merge_obj(successful_grid_points, translations) - self.convert_obj(successful_grid_points, center_lat, center_lon) + self.convert_obj(successful_grid_points) def process(self): """执行完整的预处理流程""" @@ -297,9 +297,6 @@ class ImagePreprocessor: self.extract_gps() self.cluster() self.filter_isolated_points() - self.filter_time_group_overlap() - # self.filter_alternate_images() - center_lat, center_lon = self.calculate_center_coordinates() grid_points, translations = self.divide_grids() self.copy_images(grid_points) self.logger.info("预处理任务完成") @@ -308,7 +305,7 @@ class ImagePreprocessor: grid_points, self.config.produce_dem) self.post_process(successful_grid_points, - grid_points, translations, center_lat, center_lon) + grid_points, translations) 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 805dc88..519854a 100644 --- a/odm_preprocess_fast.py +++ b/odm_preprocess_fast.py @@ -265,13 +265,13 @@ class ImagePreprocessor: merger = MergeObj(self.config.output_dir) merger.merge_grid_obj(grid_points, translations) - def convert_obj(self, grid_points: Dict[tuple, pd.DataFrame], center_lat: float, center_lon: float): + def convert_obj(self, grid_points: Dict[tuple, pd.DataFrame]): """转换OBJ模型""" self.logger.info("开始转换OBJ模型") - converter = ConvertOBJ(self.config.output_dir, center_lat, center_lon) + converter = ConvertOBJ(self.config.output_dir) converter.convert_grid_obj(grid_points) - def post_process(self, successful_grid_points: Dict[tuple, pd.DataFrame], grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple], center_lat: float, center_lon: float): + def post_process(self, successful_grid_points: Dict[tuple, pd.DataFrame], grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple]): """后处理:合并或复制处理结果""" if len(successful_grid_points) < len(grid_points): self.logger.warning( @@ -284,12 +284,12 @@ class ImagePreprocessor: elif self.config.mode == "三维模式": # self.merge_ply(successful_grid_points) # self.merge_obj(successful_grid_points, translations) - self.convert_obj(successful_grid_points, center_lat, center_lon) + self.convert_obj(successful_grid_points) else: self.merge_tif(successful_grid_points, self.config.produce_dem) # self.merge_ply(successful_grid_points) # self.merge_obj(successful_grid_points, translations) - self.convert_obj(successful_grid_points, center_lat, center_lon) + self.convert_obj(successful_grid_points) def process(self): """执行完整的预处理流程""" @@ -297,9 +297,6 @@ class ImagePreprocessor: self.extract_gps() self.cluster() self.filter_isolated_points() - self.filter_time_group_overlap() - center_lat, center_lon = self.calculate_center_coordinates() - # self.filter_alternate_images() grid_points, translations = self.divide_grids() # self.copy_images(grid_points) # self.logger.info("预处理任务完成") @@ -308,7 +305,7 @@ class ImagePreprocessor: # grid_points, self.config.produce_dem) successful_grid_points = grid_points self.post_process(successful_grid_points, - grid_points, translations, center_lat, center_lon) + grid_points, translations) except Exception as e: self.logger.error(f"处理过程中发生错误: {str(e)}", exc_info=True) diff --git a/post_pro/conv_obj.py b/post_pro/conv_obj.py index 70901bb..a026e2d 100644 --- a/post_pro/conv_obj.py +++ b/post_pro/conv_obj.py @@ -4,13 +4,12 @@ import json import shutil import logging from pyproj import Transformer +import cv2 class ConvertOBJ: - def __init__(self, output_dir: str, center_lat: float, center_lon: float): + def __init__(self, output_dir: str): self.output_dir = output_dir - self.center_lat = center_lat - self.center_lon = center_lon # 用于存储所有grid的UTM范围 self.min_east = float('inf') self.min_north = float('inf') @@ -59,25 +58,24 @@ class ConvertOBJ: grid_name = f"grid_{grid_id[0]}_{grid_id[1]}" project_dir = os.path.join(self.output_dir, grid_name, "project") texturing_dir = os.path.join(project_dir, "odm_texturing") + texturing_dst_dir = os.path.join(project_dir, "odm_texturing_dst") opensfm_dir = os.path.join(project_dir, "opensfm") - obj_file = os.path.join(texturing_dir, "odm_textured_model_geo.obj") log_file = os.path.join( project_dir, "odm_orthophoto", "odm_orthophoto_log.txt") - if not os.path.exists(obj_file): - raise FileNotFoundError(f"找不到OBJ文件: {obj_file}") + os.makedirs(texturing_dst_dir, exist_ok=True) - # 2. 读取UTM偏移量,修改obj文件顶点坐标 + # 2. 在新文件夹下,利用UTM偏移量,修改obj文件顶点坐标,纹理文件下采样 utm_offset = self.read_utm_offset(log_file) modified_obj = self.modify_obj_coordinates( - obj_file, utm_offset) + texturing_dir, texturing_dst_dir, utm_offset) + self.downsample_texture(texturing_dir, texturing_dst_dir) # 3. 执行格式转换 self.logger.info(f"开始转换网格 {grid_id} 的OBJ文件") - output_osgb = os.path.join(texturing_dir, "Tile.osgb") + output_osgb = os.path.join(texturing_dst_dir, "Tile.osgb") cmd = ( f"osgconv {modified_obj} {output_osgb} " f"--compressed --smooth --fix-transparency " - # f"-o -90-1,0,0" ) self.logger.info(f"执行osgconv命令:{cmd}") @@ -86,63 +84,33 @@ class ConvertOBJ: except subprocess.CalledProcessError as e: raise RuntimeError(f"OSGB转换失败: {str(e)}") - # 4. 读取地理信息 + # 4. 读取地理信息,计算中心点坐标 ref_lla_file = os.path.join(opensfm_dir, "reference_lla.json") with open(ref_lla_file, 'r') as f: ref_lla = json.load(f) - # 5. 创建OSGB目录结构 + # 5. 创建OSGB目录结构,复制文件 osgb_base_dir = os.path.join(self.output_dir, "osgb") data_dir = os.path.join(osgb_base_dir, "Data") tile_dir = os.path.join(data_dir, f"Tile_{grid_id[0]}_{grid_id[1]}") os.makedirs(tile_dir, exist_ok=True) - - # 5. 复制OSGB文件 target_osgb = os.path.join( tile_dir, f"Tile_{grid_id[0]}_{grid_id[1]}.osgb") shutil.copy2(output_osgb, target_osgb) - # 计算当前网格的边界框 - grid_data = grid_points[grid_id] - # 假设grid_data是一个列表,每个元素都是包含lat和lon的字典 - lats = [point['lat'] for point in grid_data] - lons = [point['lon'] for point in grid_data] - - min_lat = min(lats) - max_lat = max(lats) - min_lon = min(lons) - max_lon = max(lons) - - tile_info = { - 'id': f"{grid_id[0]}_{grid_id[1]}", - 'bounds': { - 'min_lat': min_lat, - 'max_lat': max_lat, - 'min_lon': min_lon, - 'max_lon': max_lon - }, - } - return tile_info - - def _calculate_distance(self, lat1, lon1, lat2, lon2): - """计算两点间的距离(米)""" - from math import sin, cos, sqrt, atan2, radians - R = 6371000 # 地球半径(米) - - lat1, lon1, lat2, lon2 = map(radians, [lat1, lon1, lat2, lon2]) - dlat = lat2 - lat1 - dlon = lon2 - lon1 - - a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2 - c = 2 * atan2(sqrt(a), sqrt(1-a)) - return R * c + return ref_lla def _create_merged_metadata(self, tile_infos): """创建合并后的metadata.xml文件""" + center_lon = sum(ref_lla['longitude'] + for ref_lla in tile_infos) / len(tile_infos) + center_lat = sum(ref_lla['latitude'] + for ref_lla in tile_infos) / len(tile_infos) + metadata_content = f""" EPSG:4326 - {self.center_lon},{self.center_lat},0.000000 + {center_lon},{center_lat},0.000000 Visible @@ -174,13 +142,19 @@ class ConvertOBJ: self.logger.error(f"读取UTM偏移量时发生错误: {str(e)}") raise - def modify_obj_coordinates(self, obj_file: str, utm_offset: tuple) -> str: + def modify_obj_coordinates(self, texturing_dir: str, texturing_dst_dir: str, utm_offset: tuple) -> str: """修改obj文件中的顶点坐标,使用相对坐标系""" + obj_file = os.path.join(texturing_dir, "odm_textured_model_geo.obj") + obj_dst_file = os.path.join( + texturing_dst_dir, "odm_textured_model_geo_utm.obj") + if not os.path.exists(obj_file): + raise FileNotFoundError(f"找不到OBJ文件: {obj_file}") + shutil.copy2(os.path.join(texturing_dir, "odm_textured_model_geo.mtl"), + os.path.join(texturing_dst_dir, "odm_textured_model_geo.mtl")) 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: + with open(obj_file, 'r') as f_in, open(obj_dst_file, 'w') as f_out: for line in f_in: if line.startswith('v '): # 处理顶点坐标行 @@ -189,12 +163,60 @@ class ConvertOBJ: 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} {z:.6f} {y:.6f}\n') + f_out.write(f'v {x:.6f} {z:.6f} {-y:.6f}\n') else: # 其他行直接写入 f_out.write(line) - return output_obj + return obj_dst_file except Exception as e: self.logger.error(f"修改obj坐标时发生错误: {str(e)}") raise + + def downsample_texture(self, src_dir: str, dst_dir: str): + """复制并重命名纹理文件,对大于100MB的文件进行多次下采样,直到文件小于100MB + Args: + src_dir: 源纹理目录 + dst_dir: 目标纹理目录 + """ + for file in os.listdir(src_dir): + if file.lower().endswith(('.png')): + src_path = os.path.join(src_dir, file) + dst_path = os.path.join(dst_dir, file) + + # 检查文件大小(以字节为单位) + file_size = os.path.getsize(src_path) + if file_size <= 100 * 1024 * 1024: # 如果文件小于等于100MB,直接复制 + shutil.copy2(src_path, dst_path) + else: + # 文件大于100MB,进行下采样 + img = cv2.imread(src_path, cv2.IMREAD_UNCHANGED) + if_first_ds = True + while file_size > 100 * 1024 * 1024: # 大于100MB + self.logger.info(f"纹理文件 {file} 大于100MB,进行下采样") + + if if_first_ds: + # 计算新的尺寸(长宽各变为1/4) + new_size = (img.shape[1] // 4, + img.shape[0] // 4) # 逐步减小尺寸 + # 使用双三次插值进行下采样 + resized_img = cv2.resize( + img, new_size, interpolation=cv2.INTER_CUBIC) + if_first_ds = False + else: + # 计算新的尺寸(长宽各变为1/2) + new_size = (img.shape[1] // 2, + img.shape[0] // 2) # 逐步减小尺寸 + # 使用双三次插值进行下采样 + resized_img = cv2.resize( + img, new_size, interpolation=cv2.INTER_CUBIC) + + # 更新文件路径为下采样后的路径 + cv2.imwrite(dst_path, resized_img, [ + cv2.IMWRITE_PNG_COMPRESSION, 9]) + + # 更新文件大小和图像 + file_size = os.path.getsize(dst_path) + img = cv2.imread(dst_path, cv2.IMREAD_UNCHANGED) + self.logger.info( + f"下采样后文件大小: {file_size / (1024 * 1024):.2f} MB")