简介:这份资源面向计算机、人工智能、自动化等专业的在校学生与教师,以及希望进阶三维重建技能的开发者,提供一套基于无人机航拍数据的三维场景重建Python实现方案,可作为毕业设计、课程设计或项目立项演示的完整参考。压缩包共54个文件,约20.66MB,以41个Python脚本为核心,配合3个yaml配置、3个txt说明及2个ipynb笔记本,覆盖数据加载、模型训练、位姿评估与渲染等环节。数据集建设流程清晰:先采集地面多角度图片,用colmap估计位姿作为训练基准,再借助Behindthescenes算法生成航拍深度图,无人机数据集含深度图可通过说明文档中的网盘链接获取。训练与测试入口明确,运行train.py配合wurenji.yaml即可启动,随后用evaluation/eval.py完成评估。资源还附带results结果图、正射投影与高清演示视频,直观展示重建效果,并包含轨迹对齐、ATE计算、体积测量等工具脚本,便于读者理解从数据预处理到精度评估的完整链路。目前已有149人学习。
1. 从一堆航拍图到能跑的三维场景:这套 Python 重建源码到底解决了什么
手里有一批无人机拍的工地、山坡或建筑多角度照片,想快速得到可量测的三维场景,却卡在“位姿怎么估、深度图从哪来、NeRF 怎么训、轨迹误差怎么算”这几道坎上——这套基于无人机航拍数据的三维场景重建 Python 实现源码,就是冲着这条链路来的。它把 COLMAP 位姿估计、深度图生成、NeRF 训练、ATE 轨迹对齐与误差评估串成了一条能跑通的流水线,配套数据集和部署说明,适合做毕业设计、课程设计或项目初期立项演示的在校学生与工程从业者。你不需要从零搭框架,重点是理解每个模块的输入输出,再按自己的数据替换。
2. 拆开源码看结构:从 dataloading 到 evaluation 的模块分工
2.1 目录骨架与数据流走向
拿到压缩包先别急着python train.py,花十分钟把目录过一遍,后面调参和排错会省很多事。这套代码的骨架大致分四层:数据层、模型层、训练层、评估层,外加一个third_party放外部依赖。
数据层集中在dataloading/目录,dataset.py负责把磁盘上的图片、位姿、深度图读成张量,dataloading.py做批处理组装,common.py放共用的小工具函数。配置层在configs/下,Tanks/里是针对 Tank 场景的 yaml,default.yaml是默认参数基线,preprocess2.yaml管预处理阶段。模型层在model/目录,network.py是 NeRF 主干网络,losses.py定义损失,training.py管训练循环,poses.py和intrinsics.py处理相机位姿与内参,rendering.py负责体渲染采样。评估层在evaluation/下,eval.py和eval_poses.py分别做图像质量和位姿精度评估。
轨迹评估是这套代码比较完整的一块,单独放在根目录:align_trajectory.py做轨迹对齐,compute_trajectory_errors.py算 ATE,trajectory_utils.py和align_utils.py是底层工具,results_writer.py把结果落盘。utils_poses/里还有lie_group_helper.py处理李群运算,vis_cam_traj.py可视化相机轨迹。scripts/目录放的是后处理脚本,get_depth.py提深度,get_volume.py和get_volume_by_surface_reconstruction.py算体积,get_orthographic_by_arcgis.ipynb做正射投影,get_matrix_by_sfm.py从 SfM 结果提矩阵。
理解这个分工后,你就能判断自己该动哪一块:换数据改dataloading,调网络改model,换评估指标改evaluation。
2.2 环境依赖与配置文件的读法
环境这块,常见做法是 Python 3.8 以上配 PyTorch,再装 numpy、opencv-python、scipy、matplotlib 这几个基础库。third_party/pytorch_ssim是内置的 SSIM 实现,不用额外 pip 装。如果要用 COLMAP 估位姿,得单独装 COLMAP 可执行文件,代码里通过子进程调用。
配置文件是 yaml 格式,以configs/Tanks/wurenji.yaml为例,里面通常包含这几类参数:数据路径(datadir、depthdir)、训练超参(N_iters、batch_size、lr)、网络结构(netdepth、netwidth)、渲染参数(N_samples、N_importance)。读配置时重点看路径类参数,因为换数据集时最容易在这里翻车。
# 先确认 Python 和 PyTorch 版本 python --version python -c "import torch; print(torch.__version__, torch.cuda.is_available())" # 安装基础依赖(按需补全,不要盲目全装) pip install numpy opencv-python scipy matplotlib pyyaml上面这段先验证环境,torch.cuda.is_available()返回 False 说明没吃到 GPU,训练会慢到没法接受。依赖只列了必需的几个,pytorch_ssim已经在third_party里,不用重复装。
提示:配置文件里的路径建议用绝对路径,相对路径在不同工作目录下执行时容易找不到文件,这是新手最常见的翻车点之一。
3. 数据准备与位姿估计:COLMAP 到深度图的完整链路
3.1 用 COLMAP 估计位姿并导出基准
三维重建的第一步是知道每张照片是从哪个位置、哪个角度拍的。这套代码的做法是用 COLMAP 做 SfM,得到相机位姿作为训练基准。流程是:把多角度航拍图放进一个文件夹,跑 COLMAP 的特征提取、匹配、稀疏重建,导出相机内外参。
# COLMAP 稀疏重建典型命令(按自己路径改) colmap feature_extractor \ --database_path ./colmap.db \ --image_path ./images colmap exhaustive_matcher \ --database_path ./colmap.db colmap mapper \ --database_path ./colmap.db \ --image_path ./images \ --output_path ./sparsefeature_extractor提取每张图的 SIFT 特征写进数据库,exhaustive_matcher做两两匹配,mapper做增量式重建输出稀疏点云和相机位姿。航拍图数量多时,exhaustive_matcher会很慢,常见做法是改用sequential_matcher或vocab_tree_matcher,前提是拍摄顺序有规律。
导出后你会得到cameras.txt、images.txt、points3D.txt。get_matrix_by_sfm.py这个脚本就是用来从这些文件里提取投影矩阵和内参的,训练时 dataloading 会读它。
3.2 深度图生成与数据集组织
位姿有了,还需要每张图的深度图作为监督信号。摘要里提到用 Behind the Scenes 算法获得航拍深度图,代码里preprocess/dpt_depth.py和dpt_depth.ipynb是深度估计的入口,用的是 DPT 这类单目深度估计模型。
# preprocess/dpt_depth.py 的核心逻辑示意 import torch from transformers import DPTForDepthEstimation, DPTImageProcessor processor = DPTImageProcessor.from_pretrained("Intel/dpt-large") model = DPTForDepthEstimation.from_pretrained("Intel/dpt-large").cuda().eval() def estimate_depth(image_path): image = processor(images=image_path, return_tensors="pt").to("cuda") with torch.no_grad(): depth = model(**image).predicted_depth # 插值回原图尺寸,归一化后保存 depth = torch.nn.functional.interpolate( depth.unsqueeze(1), size=image_orig_size, mode="bicubic" ) return depth这段代码加载 DPT 模型,对单张图推理出相对深度,再插值回原分辨率。注意 DPT 输出的是相对深度不是绝对尺度,训练时要么做尺度对齐,要么让网络自己学尺度。get_log_to_txt.py负责把推理日志转成文本,方便排查哪张图深度估计失败。
数据集组织上,图片、位姿、深度图要按场景分文件夹,路径写进 yaml。摘要里说无人机数据集含深度图,上传在网盘,提取码 tcmh,下载后按说明文档的目录结构摆放即可。
注意:深度图的质量直接决定重建效果。如果某张图深度估计明显错误(比如天空区域出现近景深度),训练时会产生鬼影,建议先肉眼抽查几张再开训。
4. 训练与评估:train.py 参数怎么设、eval.py 看什么指标
4.1 启动训练与关键参数含义
训练入口是train.py,按摘要给的命令是python train.py configs/Tanks/wurenji.yaml。这条命令把配置文件路径作为参数传进去,代码内部用configloading.py解析。
# 训练(单卡) python train.py configs/Tanks/wurenji.yaml # 指定 GPU 并后台跑,日志重定向 CUDA_VISIBLE_DEVICES=0 nohup python train.py configs/Tanks/wurenji.yaml > train.log 2>&1 &训练时重点盯几个参数。N_iters是总迭代次数,太小欠拟合,太大过拟合且费时间,Tank 场景常见从 20 万起步。batch_size受显存限制,航拍图分辨率高时只能设小。N_samples和N_importance控制每条光线的采样数,直接影响渲染质量和速度,粗采样加细采样是 NeRF 的标准做法。学习率lr一般从 5e-4 开始,配合指数衰减。
loss.txt和psnr.txt是训练过程落盘的日志,前者记损失曲线,后者记 PSNR。训练中途想看进度,直接tail -f train.log或看这两个文件。
4.2 评估指标与轨迹误差计算
训练完跑python evaluation/eval.py configs/Tanks/wurenji.yaml,输出图像质量指标。eval_images.py算 PSNR 和 SSIM,third_party/pytorch_ssim提供 SSIM 实现。PSNR 高说明渲染图和真值接近,但 PSNR 高不代表几何对,还得看深度和位姿。
位姿评估走另一条线。eval_pose_one_epoch.py在训练中周期性评估位姿,compute_trajectory_errors.py算绝对轨迹误差 ATE。ATE 的计算逻辑是:把估计轨迹和真值轨迹用align_trajectory.py做对齐(通常是 Sim3 或 SE3 对齐),再算逐点欧氏距离的 RMSE。
# compute_trajectory_errors.py 的核心思路 import numpy as np def compute_ate(est_poses, gt_poses): # est_poses, gt_poses: (N, 3) 位置序列 # 先做 Umeyama 对齐,求旋转、平移、尺度 R, t, s = umeyama_alignment(est_poses, gt_poses) aligned = s * (R @ est_poses.T).T + t # 逐点误差 errors = np.linalg.norm(aligned - gt_poses, axis=1) ate_rmse = np.sqrt(np.mean(errors ** 2)) return ate_rmse, errorsumeyama_alignment求最优相似变换,aligned是对齐后的估计轨迹,errors是逐点误差,ate_rmse是最终指标。ATE 越小说明轨迹越准。results_writer.py把误差序列和统计量写进文件,vis_cam_traj.py把两条轨迹画在一起,肉眼就能看出偏差在哪一段。
提示:ATE 对对齐方式敏感。如果估计轨迹和真值尺度差很多,先确认用的是 Sim3 对齐(带尺度)还是 SE3 对齐(不带尺度),选错了指标会失真。
5. 避坑与排查:这套代码最容易翻车的五个地方
5.1 位姿文件格式对不上
现象:训练一启动就报KeyError或位姿矩阵形状不对。原因:COLMAP 导出的images.txt是四元数加平移,代码里期望的是 4x4 变换矩阵,中间少了一步转换。解决:用get_matrix_by_sfm.py先把四元数转成旋转矩阵再拼成 4x4,或者检查poses.py里的读取逻辑,确认它读的是哪种格式。
5.2 深度图尺度不一致
现象:训练损失降不下去,渲染图整体偏亮或偏暗。原因:DPT 输出的是相对深度,不同图之间的尺度不统一,网络学不到一致的几何。解决:对每张深度图做归一化,或者用稀疏点云做尺度对齐,把相对深度拉到统一尺度再喂给网络。
5.3 显存爆掉
现象:CUDA out of memory。原因:航拍图分辨率高,batch_size或N_samples设太大。解决:先把batch_size降到 1,再降N_samples,或者把图片下采样到一半分辨率训练,收敛后再上全分辨率微调。
5.4 配置文件路径写错
现象:FileNotFoundError,找不到图片或深度图。原因:yaml 里用了相对路径,而执行目录和预期不一致。解决:全部改成绝对路径,或者统一在项目根目录执行命令,别在子目录里跑。
5.5 轨迹对齐后 ATE 仍然很大
现象:对齐了但 ATE 还是几十厘米甚至米级。原因:估计轨迹和真值轨迹的时间戳没对齐,或者中间有丢帧导致序列长度不一致。解决:先检查两条轨迹的帧数是否一致,不一致就按时间戳插值对齐,再算 ATE。align_utils.py里有对齐工具,别自己手写。
6. 进阶玩法:从重建结果到体积计算与正射投影
训练和评估跑通只是起点,这套代码真正有意思的是后处理脚本。scripts/get_volume.py和get_volume_by_surface_reconstruction.py能从重建结果算体积,这对工地土方量、堆料体积这类场景很实用。思路是先提取地表点云,再做表面重建,最后对参考面做积分。
# get_volume_by_surface_reconstruction.py 的思路 import open3d as o3d import numpy as np # 读点云 pcd = o3d.io.read_point_cloud("recon.ply") # 泊松表面重建 mesh, _ = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(pcd, depth=9) # 对参考平面做积分求体积 volume = mesh.get_volume() print(f"重建体积: {volume:.3f} 立方米")create_from_point_cloud_poisson做泊松重建得到封闭网格,get_volume算网格体积。注意这个体积是相对网格自身闭合面的,要算真实土方量得先裁掉地面以下的冗余部分,再减去参考面。get_orthographic_by_arcgis.ipynb走的是另一条路,用 ArcGIS 做正射投影,适合出二维成果图。
验证重建质量有个土办法:拿results/里的wuren_test.png和正射投影.png跟原图对比,看边缘是否对齐、纹理是否清晰。mountain2.gif和gaoqing.mp4是渲染动画,转一圈看有没有明显的几何塌陷或漂浮物。我一般会先跑一遍eval.py看 PSNR,再肉眼过一遍渲染动画,两个都过了才认为这次训练没白跑。从那以后我每次换数据集,都强制先跑一遍小分辨率快速验证,确认链路通了再上全量。希望帮到你。
本文还有配套的精品资源,点击获取