ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

无人机航拍三维重建:从COLMAP位姿到NeRF训练实战

无人机航拍三维重建:从COLMAP位姿到NeRF训练实战 简介基于无人机航拍数据的三维场景重建Python源码包面向计算机视觉、遥感测绘等专业的毕业设计、课程大作业及三维重建入门实践者提供从航拍图像到场景模型的完整训练与评估流程。包内共54个文件以41个Python源码为主辅以YAML配置、Jupyter Notebook、txt说明、结果图片与演示视频等覆盖数据预处理、模型训练、位姿估计、深度图生成和结果可视化等模块压缩包大小20.65MB。资源包含无人机数据集含深度图的获取方案与百度网盘链接并附带项目说明文档可帮助理解基于colmap的位姿估计和Behindthesences深度图生成思路。已有775人学习下载代码经测试可运行适合在现有框架上修改扩展直接用于毕设、课设或项目初期演示。1. 无人机航拍三维重建拿到手先别急着跑 train.py无人机绕着山头飞一圈几十张航拍照片到手回办公室却导不出一个能转的 3D 模型——这是不少人在课程设计或者毕业设计里卡住的第一道坎。这个项目正好把这条路走通了它基于无人机航拍数据完成三维场景重建Python 源码、项目说明、训练脚本、评估脚本和无人机数据集都打包在一起。核心链路是先让 COLMAP 估计出每一帧图像的相机位姿再用 BehindTheSences 思路配合深度估计脚本生成稠密深度图最后用一个 NeRF 类的神经辐射场网络训练体素场景输出新视角视频、正射投影和体积量测结果。整个包里有训练代码、评估脚本、结果演示 mp4 和 gif适合课程设计、毕业设计、项目初期立项演示也适合想入门神经三维重建的开发者照着复现。文件里自带一份项目说明.md数据集的下载地址和提取码也在里面拿到资源后建议先把它读完再动手。2. 数据准备从无人机多角度照片到 COLMAP 位姿基准2.1 井字形航拍与数据目录组织这个资源里真正决定重建质量的不是网络有多深而是输入图像和位姿准不准。摘要里的“数据集建设”三条写得精简实际操作要比这细不少。第一步“获得地面的多角度图片”听着简单但无人机航拍并不是随便绕着飞一圈就行。我一般用井字形航线无人机飞到目标区域中心悬停之后让云台俯仰 0°、30°、60° 各拍一圈相机朝下的同时也要兼顾到建筑物的侧面。如果只拍正射视角后面的 NeRF 类方法会把立体的楼体、山体重建成一坨没有体积感的贴图。拍完的照片需要按照可被代码直接读取的目录结构摆放。资源的处理脚本和训练配置默认从一个标准数据集根目录读图建议在你自己的数据目录里按下面这种方式组织drone_data/ ├── images/ # 无人机航拍原图jpg/png 均可 │ ├── img_0000.jpg │ ├── img_0001.jpg │ └── ... ├── depths/ # 深度估计脚本输出的稠密深度图 │ ├── img_0000.png │ └── ... └── colmap_output/ # COLMAP 稀疏重建输出目录 └── sparse/ └── 0/ ├── cameras.bin ├── images.bin └── points3D.binimages 目录放原始航拍图depths 放后面生成的深度图colmap_output 放 COLMAP 的稀疏重建结果。这个结构不是硬性的但后面改配置时你至少要知道每一项对应磁盘上的哪个目录。目录组织对新手来说是最容易被忽略的一步——文件放乱了训练脚本报错时你不会第一时间想到是路径问题。2.2 运行 COLMAP把二维照片变成带位姿的稀疏点云COLMAP 在这个项目里的角色是提供相机位姿基准也就是“哪些照片是从哪个位置、哪个朝向拍的”。NeRF 类方法非常依赖初始位姿位姿错一点点重建出来的场景就会出现重影和漂浮物。安装 COLMAP 通常用 conda 一条命令搞定conda install -c conda-forge colmap然后在数据集根目录下执行特征提取、特征匹配和稀疏重建三连# 特征提取对 images 下所有图提取 SIFT 特征 colmap feature_extractor \ --database_path database.db \ --image_path images \ --SiftExtraction.use_gpu 0 # 特征匹配无人机航拍重叠率高用暴力匹配最稳 colmap exhaustive_matcher \ --database_path database.db \ --SiftMatching.use_gpu 0 # 稀疏重建输出相机位姿和稀疏点云 mkdir -p colmap_output colmap mapper \ --database_path database.db \ --image_path images \ --output_path colmap_output/sparsefeature_extractor 把每张图的 SIFT 特征写进 database.db 数据库exhaustive_matcher 对所有图像对做暴力匹配无人机航拍序列虽然是按航线飞的但盘旋拍摄时视角变化不规律sequential_matcher 那种按顺序匹配的方式容易漏掉回环这里用 exhaustive 更稳mapper 是真正干重活的一步输出的 sparse/0 目录里有三个关键文件cameras.bin 存相机内参images.bin 存每帧图像的位姿四元数 平移向量points3D.bin 存三角化出来的稀疏点云。加--SiftExtraction.use_gpu 0是为了避免在显存紧张的环境下 GPU 特征提取崩溃纯 CPU 跑慢一点但稳定。如果你的机器 GPU 够用并且显存空闲可以去掉这个参数。2.3 位姿质量检查宁可少图不要错图COLMAP 跑完后不要急着进下一步先检查一下位姿质量。最直接的办法是打开 COLMAP 的 GUI 界面看一眼重建结果colmap gui \ --import_path colmap_output/sparse/0 \ --image_path images在 GUI 的“Show a 3D visualization”里能直观看到相机位置连成的轨迹。正常的户外航拍轨迹应该是平滑的曲线或者井字折线相机之间没有大跳变。如果某几帧的相机位姿明显飘出去一大截大概率是这些图像特征太少、匹配错了。建筑物玻璃反光、大片水面、纯色路面都是弱纹理区域最容易出现这种问题。处理办法不是硬留着这些坏位姿而是直接把这几张图从 images 目录挪走再重新跑一遍 COLMAP。不要觉得删图可惜对 NeRF 训练来说一张位姿错的图对 loss 的破坏力远大于它提供的纹理信息。这个资源里还有 get_matrix_by_sfm.py 脚本它的作用就是把 COLMAP 的稀疏模型转成矩阵形式的位姿文件喂给后面的训练脚本。记住 images.txt 里每一行是四元数在前、平移向量在后顺序不能搞反否则后面训练出来的轨迹会比真实轨迹多一个镜像变换。3. 深度图生成与 BehindTheSences 数据预处理3.1 为什么航拍三维重建离不开深度图NeRF 类网络本身的监督信息只有图像像素颜色但在无人机航拍的大场景里单纯靠颜色监督很难把空间结构约束住。同一栋楼从远处看和从侧面看色调几乎一样网络很容易在深度方向上产生歧义。这就是 BehindTheSences 算法在这个项目里存在的意义它利用多视角关系为每个像素补充一个“距离面”的先验也就是稠密深度图。深度图告诉网络这个像素对应的空间点离相机有多远把 NeRF 原本需要纯靠颜色反复试错才能收敛的几何结构变成一个有约束的最优化问题。资源里 preprocess 目录下的 dpt_depth.py 和 preprocess_dpt_depth.ipynb 就是做这件事的。DPT 是一个用 Vision Transformer 做稠密预测的深度估计模型它对单张图像输出逐像素的相对深度再结合前面 COLMAP 得到的稀疏点云尺度信息可以换算成带物理单位或至少尺度一致的深度图。BehindTheSences 的思路类似先给出一个基本的几何框架再用优化去细化表面细节两者配合能在航拍数据上得到比较干净的初始几何。3.2 跑 DPT 深度估计脚本命令行和 Notebook 两种方式资源里既有 dpt_depth.py 脚本也有对应的 ipynb。命令行适合批量处理和一键复现Notebook 适合边跑边看中间结果。两种入口我都建议试一下先打开 Notebook 跑通再看命令行。假设你的数据放在drone_data/images深度图输出到drone_data/depths命令行的用法大致如下# 先装依赖一般用 conda 环境PyTorch 1.10 都能跑 python preprocess/dpt_depth.py \ --root_dir drone_data/images \ --save_dir drone_data/depths \ --model_type dpt_hybrid脚本内部做的事情可以概括成这几步先用 COLMAP 的 cameras.bin 读内参再用 images.bin 读每张图的位姿然后对每一帧运行 DPT 前向推理得到深度图最后按原图文件名保存到 depths 目录文件名一一对应。--model_type参数我习惯用dpt_hybrid它对中等分辨率图像的深度估计质量比较均衡如果航拍图分辨率特别高比如 4000 像素以上可以试试dpt_large但显存占用会明显涨。输出深度图的尺寸跟输入图一致单通道、以 16 位 PNG 保存最佳因为 8 位 PNG 只有 256 个深度等级在远近对比强烈的场景会丢掉很多细节。3.3 深度图质量检查三种常见异常与处理DPT 输出的深度图不是拿来就能直接用我每次跑完都会抽十几张图出来做检查。最常见的异常有三种。第一种是深度方向反了。不同预训练模型的输出约定不一样有的图是“越亮越近”有的是“越亮越远”。你在检查时随便选一张航拍图看看天空区域是亮还是暗——如果天空最亮而且地面是深色的那就是方向反了。处理方式很简单把深度值取反再归一化即可。第二种是天空和水面区域出现大块黑色空洞。深度估计模型在无纹理区域经常给不出可靠预测尤其是高光和反光表面。表现是深度图里出现密集的黑色斑块训练时这些区域的梯度会干扰几何收敛。我的处理习惯是把这些区域做成掩膜在 loss 计算时忽略掉而不是强行填充一个假深度值。第三种是深度图边缘与图像内容错位比如树冠边缘出现一圈锯齿状过渡。这通常是因为深度图尺寸和原图尺寸不一致或者 DPT 内部做了 resize 后没有正确还原到原分辨率。检查一下输出的 PNG 尺寸和输入 jpg 是否完全一致不一致就在保存前加一步插值缩放。4. 模型训练train.py 与 wurenji.yaml 配置拆解4.1 训练启动命令与配置层级数据链路打通之后训练这一步在摘要里其实只有两行python train.py configs/Tanks/wurenji.yaml和python evaluation/eval.py configs/Tanks/wurenji.yaml。但我拆这个项目的时候翻了一下 configs 目录里面不止 wurenji.yaml还有 default.yaml、preprocess2.yaml 和 Tanks 目录下的多个配置。这说明训练入口是统一的但场景相关参数被拆到了不同层级的 yaml 文件里。default.yaml 里是通用默认参数wurenji.yaml 只覆盖无人机场景需要改的部分。训练的时候命令行指定 wurenji.yaml实际生效的是两者合并后的结果。这种分层配置的好处是换数据集时不需要动 default.yaml只需要新建一个场景 yaml。以你下载到的 default.yaml 为准无人机场景里我一般会重点检查下面这些字段data_root: /path/to/drone_data # 数据集根目录 scene_name: wurenji # 场景名决定输出目录名 images_path: images # 原图目录相对路径 colmap_dir: colmap_output/sparse/0 # 位姿目录相对路径 depth_dir: depths # 深度图目录相对路径 ray_batch_size: 4096 # 每次迭代采样的光线数 lrate: 5e-4 # 初始学习率 iterations: 30000 # 总迭代次数 render_chunk_size: 2048 # 渲染时的分块大小 eval_interval: 5000 # 每多少轮保存一次评估结果数据路径相关的字段是最容易出问题的。我自己一般会把 data_root 直接写成绝对路径避免在命令行切换目录时相对路径失效。ray_batch_size 决定每次迭代采样多少条光线这个值直接吃显存8G 显存建议从 2048 开始16G 显存可以开到 4096。lrate 是初始学习率NeRF 类方法通常用 5e-4后面配置里一般还有学习率衰减策略不需要手动干预。iterations 对无人机场景 30000 轮是够的如果数据集特别大或者场景特别复杂可以提到 50000但要留意过拟合风险。4.2 训练过程观察点与日志解读启动训练后终端会定期打印当前的 loss、学习率和当前迭代数。项目根目录会生成 psnr.txt记录每个评估轮次在测试视角上的 PSNR 值训练结束后 results 目录下会输出渲染视频。第一次跑通时不要盯着 PSNR 数字焦虑前几千轮渲染出来的画面基本是模糊的灰色块这是位置编码的高频分量还没学起来的正常现象。# 进入项目根目录执行训练 python train.py configs/Tanks/wurenji.yaml如果 loss 在前 2000 轮内没有下降不用急先让它跑到 5000 轮左右再看。如果 loss 从一开始就是 nan那就是位姿文件里有非法值回到第 2 章检查 COLMAP 输出如果 loss 在后期反复震荡下不去常见原因是 ray_batch_size 太大导致单 batch 噪声过高可以调小一档再继续训。训练过程中我有个习惯在手头留一个静态视角每次 checkpoint 保存后渲染同一视角的画面对比这个视角的纹理是否逐渐清晰。看渲染视频比看 PSNR 数字更能发现问题——比如某个立面出现半透明重影说明该区域的位姿可能有小角度偏差比如整体向左偏移说明尺度或者相机内参有问题。resources 里 results/wurenji_small_resolution.mp4 就是一个典型的训练过程演示视频你可以拿它当参照对比自己训练时的渲染效果。4.3 断点续训与多分辨率细节项目代码里明显包含 checkpoints.py 这类断点管理模块支持从中间 checkpoint 继续训练。训练中断后可以用类似下面的方式续跑# 项目里一般通过 train.py 的续跑参数实现具体以代码为准 python train.py configs/Tanks/wurenji.yaml \ --resume_from results/wurenji/checkpoints/latest.ckpt--resume_from指定上一次保存的 checkpoint 路径续跑时会把 optimizer 状态、学习率调度器的进度一起恢复不需要重新从零开始。无人机航拍图像分辨率往往偏高如果直接全分辨率训练显存不够我建议先在低分辨率下训练几万轮把几何定住再切到高分辨率精修表面纹理。资源里的配置也有类似思路configs 目录下存在 preprocess2.yamlREADME 步骤是先做数据预处理再训练中间有降采样和归一化的环节。5. 评估与避坑ATE 轨迹对齐、错误率与常见故障5.1 用位姿对齐脚本算 ATE衡量重建精度的硬指标这个项目里除了渲染视频之外还附带了一套轨迹误差评估工具align_trajectory.py、align_utils.py、compute_trajectory_errors.py、transformations.py 和 lie_group_helper.py。这一组工具的作用是评估网络估计出来的相机轨迹和参考轨迹之间的差距也就是 ATEAbsolute Trajectory Error。无人机航拍数据一般没有 RTK 级的真值位姿所以常见做法是把 COLMAP 稀疏重建得到的位置当作参考把 NeRF 训练过程中学习到的相机位姿拿出来跟它对齐。评估入口在 evaluation 目录一般这样跑# 训练完成后评估渲染质量 python evaluation/eval.py configs/Tanks/wurenji.yaml # 单独评估位姿轨迹误差 python evaluation/eval_poses.py configs/Tanks/wurenji.yamleval_poses.py 内部流程大致是从 checkpoint 恢复网络对每个训练视角重渲染并求深度反算相机位姿然后用 align_utils 里的 Umeyama 相似变换把估计轨迹对齐到参考轨迹上最后输出 RMSE 形式的 ATE 指标。为什么要做相似变换对齐因为 COLMAP 输出的位姿是稀疏重建的度量尺度而 NeRF 网络内部使用的坐标系存在一个整体尺度和平移的不确定性直接比较会放大系统误差。对齐之后得到的 RMSE 才是真正的轨迹一致性指标。看这个指标时注意一点ATE 小不代表渲染画面好它衡量的是“相机轨迹和参考轨迹接近程度”而渲染质量还受深度图质量、图像清晰度影响。所以我一般把 eval.py 输出的 PSNR/SSIM 和 eval_poses.py 输出的 ATE 放在一起看。如果 ATE 很小但渲染画面糊问题大概率出在训练不充分而不是位姿上如果 ATE 很大但渲染画面看着还行那可能是评估用的参考轨迹本身有问题。5.2 常见踩坑记录从现象到根因到解决资源代码本身测试过但换数据集复现时坑基本集中在数据侧。以下 5 条是我拆这个项目过程中遇到过的典型问题。坑一训练启动后立刻报路径错误现象执行 train.py 后报错说找不到 images 目录下的某个文件或者读取数据集时 FileNotFoundError。原因configs 里的 yaml 用的是相对路径默认以项目根目录为基准。你如果在别的目录下执行 train.py路径就失效了。解决统一在解压后的项目根目录下运行所有命令或者把 default.yaml 里的 data_root 改成绝对路径。我在自己的机器上习惯把数据集放在项目外用绝对路径指过去这样不管从哪个目录启动脚本都不会互相踩。坑二深度图与 COLMAP 位姿尺度不一致导致场景漂移现象训练初期 loss 下降正常到中期开始训练视角上的画面逐渐偏移场景像在“漂移”。原因DPT 输出的深度图是相对深度不是物理尺度而 COLMAP 位姿的平移量是真实尺度。两套尺度不一致时深度监督会给网络一个互相矛盾的学习信号。解决用稀疏点云的中值深度对深度图做归一化。具体做法是统计 COLMAP points3D.bin 中所有点的深度分布算出中位数然后把深度图除以这个中位数值让深度先验和位姿尺度在同一量级。这也是我在 3.3 节强调深度图要抽检的原因——尺度问题通过抽检很难发现但因为影响大每次换数据集都必须处理。坑三显存溢出 OOM现象训练第一次迭代就报 CUDA out of memory。原因ray_batch_size 设置太大或者 render_chunk_size 过大。默认配置是为正常显存设计的如果你用的是 8G 显存的卡跑 4096 条光线容易爆。解决把 ray_batch_size 调到 1024 或 2048render_chunk_size 减半。如果还爆把输入分辨率降低一档比如从原始尺寸降到 1/2 分辨率再训练。要注意降低分辨率会影响纹理精细度所以先用低分辨率跑通流程再逐步提升。坑四深度图全黑或全白现象渲染结果没有任何立体轮廓深度图看起来像一个纯色平面。原因图像通道顺序不对。DPT 预训练模型通常期望 RGB 输入但如果你用 OpenCV 读图默认是 BGR 通道顺序。模型把 RGB 当成了 BGR 或者反过来深度估计结果就会崩掉。解决在 dpt_depth.py 的预处理里加一行img cv2.cvtColor(img, cv2.COLOR_BGR2RGB)。这个坑特别隐蔽因为从单张图上看不出来得把深度图可视化之后才能发现颜色方向不对。坑五重建画面出现重影双像现象训练出的场景边缘有类似错位照片的“重影”尤其是建筑物轮廓处。原因位姿和图像文件名顺序不匹配。COLMAP 在 images.txt 中保存的注册顺序和按文件名排序的顺序不一致训练代码如果按 glob 排序读取图像再按 COLMAP 顺序读取位姿就会出现图像和位姿错位。解决让数据加载模块以 images.txt 的 name 字段为准做映射也就是“先读位姿再根据位姿记录里的图像文件名去读对应图片”而不是两边各自独立排序。resources 里 data loading 相关的 dataset.py 通常已经有这个逻辑但如果你自己改了数据目录结构要确认映射关系没有被破坏。6. 进阶应用正射投影、体积量测与复现验证习惯6.1 用正射投影结果反查重建质量训练跑完results 目录里一般会输出正射投影.png 这类产物。正射投影是把三维重建结果从斜视视角转为垂直向下的“顶视图”它跟原始无人机正射影像的差异能直观反映重建的几何精度。资源里的 get_orthographic_by_arcgis.ipynb 提供了一条用 ArcGIS 生成正射投影的路径核心思路是把深度图按相机内外参投影到地面栅格上再逐个像素赋颜色值。如果你没有 ArcGIS也可以用脚本里读出的相机矩阵把重建点云直接变换到地面坐标系再栅格化。这个步骤对工程场景特别有用。拿正射投影和实际地貌对比如果道路边缘、房屋轮廓重合度好说明位姿和深度基本可靠如果轮廓线出现波浪状偏移说明前面第 3 章的深度图还有系统误差。6.2 体积量测从深度图到料堆体积同一套重建结果还能支撑体积计算。资源里有 get_volume.py、get_volume2.py 和 get_volume_by_surface_reconstruction.py 三个脚本它们代表了两种量算思路。get_volume.py 直接基于深度图积分把每个像素对应的地面单元格面积乘以该点的相对高度累加得到体积。这种方式快但容易受单点深度噪声影响。get_volume_by_surface_reconstruction.py 先把所有深度点云做一次表面重建生成三角网格再对网格包围的体积做精确积分。这种方式慢但抗噪能力强得多。实测中如果只是估算一堆砂石料的大致方量深度图积分方式足够了但如果你要做的是土方工程结算这种对精度有要求的场景建议走表面重建的脚本它输出的体积值更符合测量规范。关键是把参考平面设对——体积是相对于某个基准平面的脚本里通常有 ground_plane 参数传入航拍区域内已知高度的地面点或者一个平面方程即可。6.3 我为每次复现固定的四步验证流程拆完这个项目后我自己形成了一套固定的复现路径每次拿到新的无人机数据都会强制走一遍。第一步检查位姿可视化结果确认 COLMAP 轨迹没有大的跳变第二步抽 20% 的深度图做可视化确认方向、尺度和空洞区域都符合预期第三步训练跑到一半时手动渲染同一视角画面对比纹理是否逐步清晰第四步训练结束后同时看 PSNR、ATE 数值和渲染视频三者互相印证不接受只看一个指标就下结论。这套流程也许不花哨但确实能挡住绝大多数坑。希望帮到你。本文还有配套的精品资源点击获取
返回列表