合并ply和obj代码

This commit is contained in:
龙澳 2024-12-29 12:03:53 +08:00
parent 28126bc3be
commit f08584d13a
7 changed files with 405 additions and 96 deletions

View File

@ -18,6 +18,8 @@ from utils.logger import setup_logger
from utils.visualizer import FilterVisualizer from utils.visualizer import FilterVisualizer
from post_pro.merge_tif import MergeTif from post_pro.merge_tif import MergeTif
from tools.test_docker_run import run_docker_command from tools.test_docker_run import run_docker_command
from post_pro.merge_obj import MergeObj
from post_pro.merge_ply import MergePly
@dataclass @dataclass
@ -228,63 +230,22 @@ class ImagePreprocessor:
self.logger.info(f"网格 {grid_idx + 1} 包含 {len(points)} 张图像") self.logger.info(f"网格 {grid_idx + 1} 包含 {len(points)} 张图像")
def merge_tif(self, grid_points: Dict[int, pd.DataFrame]): def merge_tif(self, grid_points: Dict[int, pd.DataFrame]):
"""合并所有网格的TIF影像""" """合并所有网格的影像产品"""
self.logger.info("开始合并TIF影像") self.logger.info("开始合并所有影像产品")
merger = MergeTif(self.config.output_dir)
merger.merge_all_tifs(grid_points)
# 检查是否有多个网格需要合并 def merge_obj(self, grid_points: Dict[int, pd.DataFrame]):
if len(grid_points) < 2: """合并所有网格的OBJ模型"""
self.logger.info("只有一个网格无需合并TIF影像") self.logger.info("开始合并OBJ模型")
return merger = MergeObj(self.config.output_dir)
merger.merge_grid_obj(grid_points)
input_tif1, input_tif2 = None, None def merge_ply(self, grid_points: Dict[int, pd.DataFrame]):
merge_count = 0 """合并所有网格的PLY点云"""
self.logger.info("开始合并PLY点云")
try: merger = MergePly(self.config.output_dir)
for grid_idx, points in grid_points.items(): merger.merge_grid_ply(grid_points)
grid_tif = os.path.join(
self.config.output_dir,
f"grid_{grid_idx + 1}",
"project",
"odm_orthophoto",
"odm_orthophoto.original.tif"
)
# 检查TIF文件是否存在
if not os.path.exists(grid_tif):
self.logger.error(
f"网格 {grid_idx + 1} 的TIF文件不存在: {grid_tif}")
continue
if input_tif1 is None:
input_tif1 = grid_tif
self.logger.info(f"设置第一个输入TIF: {input_tif1}")
else:
input_tif2 = grid_tif
output_tif = os.path.join(
self.config.output_dir, "merged_orthophoto.tif")
self.logger.info(
f"开始合并第 {merge_count + 1} 次:\n"
f"输入1: {input_tif1}\n"
f"输入2: {input_tif2}\n"
f"输出: {output_tif}"
)
merge_tif = MergeTif(input_tif1, input_tif2, output_tif)
merge_tif.merge()
merge_count += 1
input_tif1 = output_tif
input_tif2 = None
self.logger.info(
f"TIF影像合并完成共执行 {merge_count} 次合并,"
f"最终输出文件: {input_tif1}"
)
except Exception as e:
self.logger.error(f"TIF影像合并过程中发生错误: {str(e)}", exc_info=True)
raise
def process(self): def process(self):
"""执行完整的预处理流程""" """执行完整的预处理流程"""
@ -297,19 +258,10 @@ class ImagePreprocessor:
self.copy_images(grid_points) self.copy_images(grid_points)
self.logger.info("预处理任务完成") self.logger.info("预处理任务完成")
# for grid_idx in grid_points.keys():
# grid_dir = os.path.abspath(os.path.join(
# self.config.output_dir, f'grid_{grid_idx + 1}'
# ))
# grid_dir = grid_dir[0].lower() + grid_dir[1:].replace("\\", "/")
# command = f"docker run -ti --rm -v {grid_dir}:/datasets opendronemap/odm --project-path /datasets project --max-concurrency 10 --force-gps --feature-quality lowest --orthophoto-resolution 10 --fast-orthophoto --skip-3dmodel --rerun-all"
# print(command)
# stdout, stderr = run_docker_command(command)
# print(stdout)
# print(stderr)
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_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)
raise raise
@ -318,8 +270,8 @@ class ImagePreprocessor:
if __name__ == "__main__": if __name__ == "__main__":
# 创建配置 # 创建配置
config = PreprocessConfig( config = PreprocessConfig(
image_dir=r"G:\error_data\20241104140457\code\images", image_dir=r"E:\datasets\UAV\1009\project\images",
output_dir=r"G:\ODM_output\20241104140457", output_dir=r"G:\ODM_output\1009",
cluster_eps=0.01, cluster_eps=0.01,
cluster_min_samples=5, cluster_min_samples=5,
@ -335,11 +287,11 @@ if __name__ == "__main__":
filter_dense_distance_threshold=10, filter_dense_distance_threshold=10,
filter_time_threshold=timedelta(minutes=5), filter_time_threshold=timedelta(minutes=5),
grid_size=1000, grid_size=300,
grid_overlap=0.03, grid_overlap=0.03,
mode="快拼模式", mode="重建模式",
) )
# 创建处理器并执行 # 创建处理器并执行

109
post_pro/merge_obj.py Normal file
View File

@ -0,0 +1,109 @@
import os
import logging
import numpy as np
from typing import Dict
import pandas as pd
import open3d as o3d
class MergeObj:
def __init__(self, output_dir: str):
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}")
# 检查输入文件是否存在
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("保存合并后的模型失败")
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]):
"""合并所有网格的OBJ模型"""
self.logger.info("开始合并所有网格的OBJ模型")
if len(grid_points) < 2:
self.logger.info("只有一个网格,无需合并")
return
input_obj1, input_obj2 = None, None
merge_count = 0
try:
for grid_idx, points in grid_points.items():
grid_obj = os.path.join(
self.output_dir,
f"grid_{grid_idx + 1}",
"project",
"odm_texturing",
"odm_textured_model_geo.obj"
)
if not os.path.exists(grid_obj):
self.logger.warning(f"网格 {grid_idx + 1} 的OBJ文件不存在: {grid_obj}")
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, "merged_model.obj")
self.logger.info(
f"开始合并第 {merge_count + 1} 次:\n"
f"输入1: {input_obj1}\n"
f"输入2: {input_obj2}\n"
f"输出: {output_obj}"
)
self.merge_two_objs(input_obj1, input_obj2, output_obj)
merge_count += 1
input_obj1 = output_obj
input_obj2 = None
self.logger.info(
f"OBJ模型合并完成共执行 {merge_count} 次合并,"
f"最终输出文件: {input_obj1}"
)
except Exception as e:
self.logger.error(f"OBJ模型合并过程中发生错误: {str(e)}", exc_info=True)
raise

108
post_pro/merge_ply.py Normal file
View File

@ -0,0 +1,108 @@
import os
import logging
import numpy as np
from typing import Dict
import pandas as pd
import open3d as o3d
class MergePly:
def __init__(self, output_dir: str):
self.output_dir = output_dir
self.logger = logging.getLogger('UAV_Preprocess.MergePly')
def merge_two_plys(self, ply1_path: str, ply2_path: str, output_path: str):
"""合并两个PLY文件"""
try:
self.logger.info("开始合并PLY点云")
self.logger.info(f"输入点云1: {ply1_path}")
self.logger.info(f"输入点云2: {ply2_path}")
self.logger.info(f"输出点云: {output_path}")
# 检查输入文件是否存在
if not os.path.exists(ply1_path) or not os.path.exists(ply2_path):
raise FileNotFoundError("输入点云文件不存在")
# 读取点云
pcd1 = o3d.io.read_point_cloud(ply1_path)
pcd2 = o3d.io.read_point_cloud(ply2_path)
if pcd1 is None or pcd2 is None:
raise ValueError("无法读取点云文件")
# 获取点云中心
center1 = pcd1.get_center()
center2 = pcd2.get_center()
# 计算平移向量
translation_vector = center2 - center1
# 对齐点云
pcd2.translate(translation_vector)
# 合并点云
combined_pcd = pcd1 + pcd2
# 保存合并后的点云
if not o3d.io.write_point_cloud(output_path, combined_pcd):
raise RuntimeError("保存合并后的点云失败")
self.logger.info(f"点云合并成功,已保存至: {output_path}")
except Exception as e:
self.logger.error(f"合并PLY点云时发生错误: {str(e)}", exc_info=True)
raise
def merge_grid_ply(self, grid_points: Dict[int, pd.DataFrame]):
"""合并所有网格的PLY点云"""
self.logger.info("开始合并所有网格的PLY点云")
if len(grid_points) < 2:
self.logger.info("只有一个网格,无需合并")
return
input_ply1, input_ply2 = None, None
merge_count = 0
try:
for grid_idx, points in grid_points.items():
grid_ply = os.path.join(
self.output_dir,
f"grid_{grid_idx + 1}",
"project",
"odm_georeferencing",
"odm_georeferenced_model.ply"
)
if not os.path.exists(grid_ply):
self.logger.warning(f"网格 {grid_idx + 1} 的PLY文件不存在: {grid_ply}")
continue
if input_ply1 is None:
input_ply1 = grid_ply
self.logger.info(f"设置第一个输入PLY: {input_ply1}")
else:
input_ply2 = grid_ply
output_ply = os.path.join(self.output_dir, "merged_pointcloud.ply")
self.logger.info(
f"开始合并第 {merge_count + 1} 次:\n"
f"输入1: {input_ply1}\n"
f"输入2: {input_ply2}\n"
f"输出: {output_ply}"
)
self.merge_two_plys(input_ply1, input_ply2, output_ply)
merge_count += 1
input_ply1 = output_ply
input_ply2 = None
self.logger.info(
f"PLY点云合并完成共执行 {merge_count} 次合并,"
f"最终输出文件: {input_ply1}"
)
except Exception as e:
self.logger.error(f"PLY点云合并过程中发生错误: {str(e)}", exc_info=True)
raise

View File

@ -1,34 +1,33 @@
from osgeo import gdal from osgeo import gdal
import logging import logging
import os import os
from typing import Dict
import pandas as pd
import sys import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
class MergeTif: class MergeTif:
def __init__(self, input_tif1, input_tif2, output_tif): def __init__(self, output_dir: str):
self.input_tif1 = input_tif1 self.output_dir = output_dir
self.input_tif2 = input_tif2
self.output_tif = output_tif
self.logger = logging.getLogger('UAV_Preprocess.MergeTif') self.logger = logging.getLogger('UAV_Preprocess.MergeTif')
def merge(self): def merge_two_tifs(self, input_tif1: str, input_tif2: str, output_tif: str):
"""合并两张TIF影像""" """合并两张TIF影像"""
try: try:
self.logger.info("开始合并TIF影像") self.logger.info("开始合并TIF影像")
self.logger.info(f"输入影像1: {self.input_tif1}") self.logger.info(f"输入影像1: {input_tif1}")
self.logger.info(f"输入影像2: {self.input_tif2}") self.logger.info(f"输入影像2: {input_tif2}")
self.logger.info(f"输出影像: {self.output_tif}") self.logger.info(f"输出影像: {output_tif}")
# 检查输入文件是否存在 # 检查输入文件是否存在
if not os.path.exists(self.input_tif1) or not os.path.exists(self.input_tif2): if not os.path.exists(input_tif1) or not os.path.exists(input_tif2):
error_msg = "输入影像文件不存在" error_msg = "输入影像文件不存在"
self.logger.error(error_msg) self.logger.error(error_msg)
raise FileNotFoundError(error_msg) raise FileNotFoundError(error_msg)
# 打开影像,检查投影是否一致 # 打开影像,检查投影是否一致
datasets = [gdal.Open(tif) datasets = [gdal.Open(tif) for tif in [input_tif1, input_tif2]]
for tif in [self.input_tif1, self.input_tif2]]
if None in datasets: if None in datasets:
error_msg = "无法打开输入影像文件" error_msg = "无法打开输入影像文件"
self.logger.error(error_msg) self.logger.error(error_msg)
@ -47,20 +46,14 @@ class MergeTif:
# 创建 GDAL Warp 选项 # 创建 GDAL Warp 选项
warp_options = gdal.WarpOptions( warp_options = gdal.WarpOptions(
format="GTiff", format="GTiff",
resampleAlg="average", # 设置重采样方法为平均值 resampleAlg="average",
srcNodata=0, # 输入影像中的无效值 srcNodata=0,
dstNodata=0, # 输出影像中的无效值 dstNodata=0,
multithread=True # 启用多线程优化 multithread=True
) )
self.logger.info("开始执行影像拼接...") self.logger.info("开始执行影像拼接...")
result = gdal.Warp(output_tif, [input_tif1, input_tif2], options=warp_options)
# 使用 GDAL 的 Warp 方法进行拼接
result = gdal.Warp(
self.output_tif,
[self.input_tif1, self.input_tif2], # 输入多张影像
options=warp_options
)
if result is None: if result is None:
error_msg = "影像拼接失败" error_msg = "影像拼接失败"
@ -68,19 +61,109 @@ class MergeTif:
raise RuntimeError(error_msg) raise RuntimeError(error_msg)
# 获取输出影像的基本信息 # 获取输出影像的基本信息
output_dataset = gdal.Open(self.output_tif) output_dataset = gdal.Open(output_tif)
if output_dataset: if output_dataset:
width = output_dataset.RasterXSize width = output_dataset.RasterXSize
height = output_dataset.RasterYSize height = output_dataset.RasterYSize
bands = output_dataset.RasterCount bands = output_dataset.RasterCount
self.logger.info(f"拼接完成,输出影像大小: {width}x{height},波段数: {bands}") self.logger.info(f"拼接完成,输出影像大小: {width}x{height},波段数: {bands}")
self.logger.info(f"影像拼接成功,输出文件保存至: {self.output_tif}") self.logger.info(f"影像拼接成功,输出文件保存至: {output_tif}")
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
def merge_grid_tif(self, grid_points: Dict[int, pd.DataFrame], product_info: dict):
"""合并指定产品的所有网格"""
product_name = product_info['name']
product_path = product_info['path']
filename = product_info['filename']
self.logger.info(f"开始合并{product_name}")
if len(grid_points) < 2:
self.logger.info("只有一个网格,无需合并")
return
input_tif1, input_tif2 = None, None
merge_count = 0
try:
for grid_idx, points in grid_points.items():
grid_tif = os.path.join(
self.output_dir,
f"grid_{grid_idx + 1}",
"project",
product_path,
filename
)
if not os.path.exists(grid_tif):
self.logger.warning(f"网格 {grid_idx + 1}{product_name}不存在: {grid_tif}")
continue
if input_tif1 is None:
input_tif1 = grid_tif
self.logger.info(f"设置第一个输入{product_name}: {input_tif1}")
else:
input_tif2 = grid_tif
output_tif = os.path.join(self.output_dir, f"merged_{product_info['output']}")
self.logger.info(
f"开始合并{product_name}{merge_count + 1} 次:\n"
f"输入1: {input_tif1}\n"
f"输入2: {input_tif2}\n"
f"输出: {output_tif}"
)
self.merge_two_tifs(input_tif1, input_tif2, output_tif)
merge_count += 1
input_tif1 = output_tif
input_tif2 = None
self.logger.info(
f"{product_name}合并完成,共执行 {merge_count} 次合并,"
f"最终输出文件: {input_tif1}"
)
except Exception as e:
self.logger.error(f"{product_name}合并过程中发生错误: {str(e)}", exc_info=True)
raise
def merge_all_tifs(self, grid_points: Dict[int, pd.DataFrame]):
"""合并所有产品正射影像、DSM和DTM"""
try:
products = [
{
'name': '正射影像',
'path': 'odm_orthophoto',
'filename': 'odm_orthophoto.original.tif',
'output': 'orthophoto.tif'
},
{
'name': 'DSM',
'path': 'odm_dem',
'filename': 'dsm.original.tif',
'output': 'dsm.tif'
},
{
'name': 'DTM',
'path': 'odm_dem',
'filename': 'dtm.original.tif',
'output': 'dtm.tif'
}
]
for product in products:
self.merge_grid_product(grid_points, product)
self.logger.info("所有产品合并完成")
except Exception as e:
self.logger.error(f"产品合并过程中发生错误: {str(e)}", exc_info=True)
raise
if __name__ == "__main__": if __name__ == "__main__":
from utils.logger import setup_logger from utils.logger import setup_logger
@ -95,5 +178,5 @@ if __name__ == "__main__":
setup_logger(output_dir) setup_logger(output_dir)
# 执行拼接 # 执行拼接
merge_tif = MergeTif(input_tif1, input_tif2, output_tif) merge_tif = MergeTif(output_dir)
merge_tif.merge() merge_tif.merge_two_tifs(input_tif1, input_tif2, output_tif)

View File

@ -6,3 +6,4 @@ piexif
geopy geopy
psutil psutil
docker>=6.1.3 docker>=6.1.3
open3d

34
tools/merge_obj.py Normal file
View File

@ -0,0 +1,34 @@
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])

22
tools/merge_ply.py Normal file
View File

@ -0,0 +1,22 @@
import open3d as o3d
import numpy as np
# 读取第一个PLY文件
pcd1 = o3d.io.read_point_cloud("path_to_first_file.ply")
# 读取第二个PLY文件
pcd2 = o3d.io.read_point_cloud("path_to_second_file.ply")
# 可选:如果需要调整坐标系,可以通过平移、旋转来对齐点云
# 例如,平移第二个点云
offset = np.array([1000, 2000, 3000])
pcd2.translate(offset)
# 合并点云
combined_pcd = pcd1 + pcd2
# 保存合并后的点云为PLY文件
o3d.io.write_point_cloud("merged_output.ply", combined_pcd)
# 可视化
o3d.visualization.draw_geometries([combined_pcd])