看到“I found an abandoned castle”这类探险视频时,多数人只当作猎奇内容刷过去。但如果站在三维数字化和内容资产化的角度,这类废墟素材反而是最合适的实景重建样本:城堡结构复杂、纹理丰富、天然带有时间痕迹,非常容易测试一套三维重建流程能还原到什么程度。这篇博文不讨论视频本身的观赏性,而是把“发现废弃城堡”当作一个拍摄与采集场景,拆解一条从视频素材到 NeRF / 3D Gaussian Splatting 可漫游场景的完整工作流。你会看到它用到哪些开源工具、部署环境需要什么、显存占用大概落在什么范围、如何做批量重建,以及怎么把整个流程封装成接口服务。内容偏工程落地,适合对三维重建、数字资产保存和自动化工具有兴趣的读者收藏。
整套方案并不是某个单一“开箱即用”项目,而是 COLMAP + nerfstudio / gsplat + FFmpeg + FastAPI 的组合。好处是每个组件都可以独立替换:COLMAP 负责相机位姿估算,nerfstudio 可以训练 NeRF 并自带 Web 可视化,gsplat 适合训练 3D Gaussian Splatting,FFmpeg 负责视频抽帧,FastAPI 负责把重建链路变成可并发调用的服务。先看核心能力,再看本地部署和实测验证路径。
1. 核心能力速览
| 能力项 | 说明 |
|---|---|
| 工作流类型 | 视频 / 照片输入到三维场景重建,再到 Web 可视化预览 |
| 核心开源组件 | COLMAP、nerfstudio / gsplat、FFmpeg、FastAPI |
| 主要功能 | 视频抽帧、关键帧筛选、稀疏重建、NeRF / 3DGS 训练、新视角渲染、接口调用、批量队列 |
| 推荐硬件 | NVIDIA 独立显卡,建议 CUDA 可用;CPU 可以跑 COLMAP 预处理,但训练阶段不推荐 CPU |
| 显存占用 | 取决于场景数量、图像分辨率、训练分辨率和步数,需以实际模型和参数为准 |
| 支持平台 | Windows 10/11、Ubuntu 20.04+,以 COLMAP 和 PyTorch 安装支持为准 |
| 启动方式 | 命令行 + WebUI 查看器 |
| 是否支持 API | 可以用 FastAPI 自行封装上传、重建、查询任务接口 |
| 是否支持批量任务 | 支持;按场景目录遍历执行,或通过消息队列并发处理 |
| 适合场景 | 遗址数字化、文旅导览、影视 / 游戏资产、探险视频内容复用、古建筑保护记录 |
这套流程的最终产出不是一张图,而是一组相机位姿、点云和训练好的 NeRF / 3DGS 模型。你可以在浏览器里自由拖动视角,也可以固定一条相机轨道渲染成视频。对于 PC 端和移动端预览,通常导出为视频或轻量化点云即可。
2. 适用场景与使用边界
这套工作流解决的最核心问题是:把现实世界中的物理空间变成计算机可以交互的三维数据。
适合的人大概有四类。第一类是内容创作者,特别是拍过废墟、城堡、老建筑、自然景区视频的人,采集素材时只需要多拍几圈环绕镜头,后期就能得到一份可漫游的三维版本,能大幅提高素材复用率。第二类是文旅和文物保护从业者,需要给不可移动的建筑物留下高精度数字档案。第三类是游戏和影视美术,想用真实场景做资产参考,例如把某座老建筑快速变成 3D 底图,再在建模软件里清理和建模。第四类是技术学习者,想亲眼看一遍从多视图几何到神经渲染的完整链路。
不适合的情况也很明确。如果你只是想把一段视频剪得更好看、配个字幕或发到短视频平台,那不需要这套流程,直接用剪辑软件更快。如果你需要严格尺寸测量级的模型,也还差一步:COLMAP 稀疏重建默认只恢复相对坐标,需要添加已知尺寸的标定物或导入控制点做绝对尺度对齐。如果完全没有 NVIDIA GPU,训练 NeRF 或 3DGS 会很吃力,即使能跑通,渲染速度也基本不可用。
合规和安全边界必须单独强调。拍摄废弃城堡、老建筑或私人领地前,要先确认场地归属和管理规定。无人机拍摄还要确认是否属于禁飞区或需要申报。素材中如果拍到人脸、车牌、身份信息,最好做模糊处理。涉及文物、军事设施、敏感场所的场景一律不要采集和发布。训练模型使用的图片和视频,必须是自有素材或已获授权的素材。批量处理和接口服务上线前,也应限制访问来源,避免被外部滥用。
3. 环境准备与前置条件
环境准备分硬件、系统软件、Python 环境三个层面。
3.1 硬件要求
第一阶段抽帧和筛选主要吃 CPU,基本没有门槛。第二阶段 COLMAP 做特征提取和特征匹配,可以用 CPU,但速度偏慢。第三阶段 NeRF / 3DGS 训练强烈建议使用 NVIDIA 显卡,因为大多数实现都依赖 CUDA 加速。显存方面,不同实现差别很大:NeRF 类模型对 batch size 和采样点数量敏感,3D Gaussian Splatting 对高斯数量和图像分辨率敏感。稳妥的建议是从 8G 显存开始尝试小场景,实际占用要到训练启动后观察nvidia-smi才能确定。
磁盘空间同样重要。一段 5 分钟的视频可以拆出几百张 1080P 图片,原始素材加中间产物,一个场景占用 5GB 到 30GB 都很正常。如果是多场景批量处理,尽量把工作目录放在剩余空间充足的 SSD 上。
3.2 系统软件清单
建议预先安装以下组件。
| 组件 | 用途 | 检查方式 |
|---|---|---|
| FFmpeg | 视频抽帧、视频导出 | ffmpeg -version |
| COLMAP | 特征提取、匹配、稀疏重建 | colmap -h |
| Python 3.8+ | 运行训练脚本和接口服务 | python --version |
| CUDA 驱动 | 让 PyTorch 能调用 GPU | nvidia-smi |
| PyTorch | NeRF / 3DGS 训练框架 | python -c "import torch; print(torch.cuda.is_available())" |
COLMAP 安装方式比较多样:Windows 可以通过官方 COLMAP 发布包安装,也可以从源码编译;Linux 用户可以用 apt 安装二进制包,也可以使用 conda 安装。nerfstudio 和 gsplat 的安装建议直接参考对应开源仓库的 README,因为依赖版本变化较快。需要特别注意的是,PyTorch 的安装版本要和本机 CUDA 版本匹配,否则会出现torch.cuda.is_available()返回False的情况。
3.3 环境自检命令
拿到一台新机器后,先执行下面这段检查。
nvidia-smi ffmpeg -version colmap -h python -c "import torch; print(torch.cuda.is_available())"如果最后一行输出True,说明 GPU 环境没问题。如果输出False,先确认显卡驱动、CUDA 和 PyTorch 三者版本是否匹配,不要急着进入重建环节。
4. 素材采集与预处理
这一步决定了重建上限。模型再强,输入素材质量不够也一样失败。
4.1 拍摄建议
采集时围绕场景主体,走一个尽量完整的环绕路线。重叠率是核心指标:相邻画面之间至少要有 60% 以上的重叠,否则 COLMAP 很难找到足够多的同名点。最好在不同高度各拍一圈,例如先在地面高度环绕一圈,再找高点或使用无人机增加俯视角度。不要把镜头怼在纯色墙面、玻璃或水面长距离移动,这类特征稀疏的区域会让特征匹配点迅速减少。
如果用视频拍摄,建议保持匀速缓慢转动,避免快速甩镜头。虽然有抽帧,但快速移动产生的运动模糊会让后续特征提取失败。最好是边走边拍,让相机有一定平移,而不是站在原地原地转圈。
4.2 使用 FFmpeg 进行视频抽帧
拿到视频后,先抽帧。下面是一个按 2 帧每秒抽帧的示例。
mkdir -p frames ffmpeg -i input.mp4 -vf "fps=2" -qscale:v 2 frames/%04d.jpgfps=2表示每秒抽 2 帧。如果素材较短且场景复杂,可以提高为 3 或 5。如果视频很长,建议先降低到 1 帧每秒,避免产生大量冗余帧。抽帧后,手动浏览一遍,删除严重模糊、重复或曝光异常的画面。
4.3 用 Python 筛选模糊帧
视频抽帧可能产生一部分模糊帧,这里提供一个基于 Laplacian 方差的简单筛选脚本,值越低代表图像越模糊。
import os import cv2 frame_dir = "frames" output_dir = "selected_frames" os.makedirs(output_dir, exist_ok=True) threshold = 80 for name in sorted(os.listdir(frame_dir)): path = os.path.join(frame_dir, name) img = cv2.imread(path) if img is None: print(f"跳过无法读取的图片: {name}") continue gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) score = cv2.Laplacian(gray, cv2.CV_64F).var() if score >= threshold: out_path = os.path.join(output_dir, name) os.rename(path, out_path) print(f"{name}: 清晰度 {score:.2f},已保留") else: print(f"{name}: 清晰度 {score:.2f},已过滤")这个脚本会把低于阈值的帧留在原目录,高于阈值的移动到selected_frames。阈值需要根据实际素材调整。比如画面本身就偏暗、纹理较少,阈值可以降到 40 到 60;如果素材很锐利,阈值可以提到 100 以上。
5. 三维重建与效果验证
预处理结束后,进入核心阶段:先用 COLMAP 恢复相机位姿,再用 NeRF 或 3DGS 学习场景几何和颜色。
5.1 COLMAP 稀疏重建
COLMAP 的作用是从多张图片中找出同名点,估计每张照片的拍摄位置和朝向,并生成一个稀疏点云。下面是命令行方式的三步操作。
mkdir -p sparse colmap feature_extractor \ --database_path database.db \ --image_path selected_frames colmap exhaustive_matcher \ --database_path database.db colmap mapper \ --database_path database.db \ --image_path selected_frames \ --output_path sparse如果图片数量较多,建议把exhaustive_matcher换成sequential_matcher,因为穷举匹配在图片多时非常耗时。运行完成后,可以用 COLMAP GUI 打开sparse/0目录查看。看到相机位姿和稀疏点云铺开时,说明第一步成功。
这一步最常见的失败是注册图片率过低。正常情况下,90% 以上的图片应该被成功注册。如果很多图片未注册,优先检查素材重叠率,其次是图片清晰度和重复纹理。
5.2 训练 NeRF / 3DGS
得到稀疏重建结果后,后续训练工具可以直接读取。以 nerfstudio 为例,常见的训练命令是:
ns-train nerfacto \ --data ./sparse/0 \ colmap不同版本参数差异较大,实际运行时以 nerfstudio 官方文档为准。如果是 3D Gaussian Splatting,可以参考 gsplat 仓库的官方训练入口,通常需要传入一个包含图片和 COLMAP 结果的目录,以及一个输出目录。
训练是整套流程里最吃显存的部分。看到显存占用低于预期时,先确认 PyTorch 是否真正调用了 GPU。nvidia-smi中进程列表里应该有 python 进程,且 GPU 利用率不是 0%。如果明显在 CPU 上训练,检查 CUDA 工具包和 PyTorch 的匹配关系。
5.3 效果验证维度
验证重建效果,不只看生成图是否好看,而要看以下几点:
- 相机轨迹:COLMAP 输出的轨迹在缩略图上是否连续、无突变。跳变的相机位姿会导致新视角渲染错位。
- 新视角一致性:在浏览器查看器中,将视角移动到训练样本没有覆盖的位置,画面是否还能保持结构合理。如果出现严重重影或形状崩坏,说明训练集覆盖不够。
- 渲染指标:常见指标是 PSNR 和 SSIM。小场景训练较充分时,PSNR 通常能到 20 以上,但这是经验范围,不是绝对标准;纹理复杂的大场景可能只有 15 左右。
- 遮挡边界:观察建筑屋檐、栏杆、窗户边缘是否出现半透明雾状噪点。如果是 3DGS,出现大量长条飞点通常意味着训练步骤不足或场景中存在高反光物体。
功能测试完成后,可以固定一条相机路径渲染一段漫游视频,例如以对象为中心旋转 360 度,再结合 FFmpeg 输出最终视频。
ffmpeg -framerate 30 -i render_frames/%04d.png -c:v libx264 -pix_fmt yuv420p tour.mp4render_frames目录是渲染器输出的图像序列,帧率可以按实际需要修改。
6. 接口 API 与批量任务
三维重建通常不只搞一个场景。当你手上有一批“废弃建筑”视频,且每个视频都需要产出三维模型时,手动一条条敲命令效率太低。此时可以包一个简单的 Python API,把“上传素材 -> 排队重建 -> 查询结果”做成服务。
6.1 用 FastAPI 封装重建服务
下面是一个简化示例,用于接收一组图片文件,放入按任务 ID 创建的目录,再触发后台重建脚本。它只负责收集文件和服务化,真正的重建逻辑由run_recon.sh执行。
import os import uuid import subprocess from fastapi import FastAPI, UploadFile, File app = FastAPI() WORK_DIR = "./recon_jobs" os.makedirs(WORK_DIR, exist_ok=True) @app.post("/reconstruct") async def reconstruct(files: list[UploadFile] = File(...)): job_id = str(uuid.uuid4()) job_dir = os.path.join(WORK_DIR, job_id) os.makedirs(job_dir, exist_ok=True) for f in files: data = await f.read() with open(os.path.join(job_dir, f.filename), "wb") as fp: fp.write(data) subprocess.Popen( ["bash", "run_recon.sh", job_dir], stdout=open(os.path.join(job_dir, "stdout.log"), "w"), stderr=open(os.path.join(job_dir, "stderr.log"), "w") ) return {"job_id": job_id, "status": "queued"} @app.get("/status/{job_id}") def status(job_id: str): job_dir = os.path.join(WORK_DIR, job_id) if not os.path.exists(job_dir): return {"status": "not_found"} if os.path.exists(os.path.join(job_dir, "done.flag")): return {"status": "done"} if os.path.exists(os.path.join(job_dir, "error.flag")): return {"status": "error", "log": open(os.path.join(job_dir, "stderr.log")).read()[-500:]} return {"status": "running"}启动方式:
uvicorn app:app --host 127.0.0.1 --port 8000这里只监听 127.0.0.1,外部无法直接访问。如果想在局域网中供内部调用,可以改成0.0.0.0,但必须加防火墙或 Token 鉴权。实际项目中不要直接暴露公网,也不要让接口解析用户上传的可执行文件。
6.2 批量任务与失败重试
批量处理可以先设计一个输入根目录,例如input_scenes,下面每个子目录对应一个场景:
input_scenes/ scene_01/ video.mp4 scene_02/ video.mp4 scene_03/ video.mp4然后用一个 shell 或 Python 脚本遍历目录,对每个场景执行“抽帧 -> 筛选 -> COLMAP -> 训练 -> 导出视频”。批量任务最容易出现的问题是中间某个场景失败导致整个脚本中断。因此建议每个场景单独记录日志,并加入失败重试和超时。
例如在 Python 脚本里可以用subprocess.run(..., timeout=3600)给每个阶段设置超时,超时则把错误写进该场景的error.log,然后继续处理下一个场景。完成任务后,检查哪些场景生成成功,哪些失败,再做针对性处理。
7. 资源占用与性能观察
三维重建不是单靠显存就能判断性能的边角任务,需要同时观察显存、显存利用率、GPU 利用率和 CPU 负载。
7.1 观察命令
训练过程中常用两个命令:
watch -n 1 nvidia-smi这个命令可以实时刷新显存和 GPU 利用率。另一个是:
gpustat它会把多个 GPU 状态、进程名和显存占用统一展示,比较直观。如果系统没有gpustat,可以执行pip install gpustat安装。
7.2 影响资源消耗的关键因素
- 图像分辨率:原图越大,COLMAP 特征提取和训练解码都更慢、更耗显存。
- 训练分辨率:NeRF / 3DGS 都会把输入图像按比例缩放。训练分辨率调低,显存占用下降明显,但精细结构也会损失。
- Batch size:NeRF 类模型对 batch size 很敏感,显存不够时优先降低 batch size。
- 高斯数量:3DGS 的高斯数量会随着优化动态变化,场景越复杂,最终保留的高斯越多,显存和渲染压力越大。
- 并行进程:批量任务如果同时开多个训练进程,每个进程都会占用一份显存,非常容易 OOM。
7.3 如何降低显存占用
如果显存只有 6G 或 8G,建议先做四件事:降低视频抽帧分辨率、降低训练分辨率、关闭浏览器预览、减少同时运行的进程。补充一点,NeRF 训练时的空间采样量也是显存大头,不少实现支持通过--batch_size或--num_rays_per_batch调整。找不到参数时,直接缩小训练分辨率是最省事的方法。
端口冲突也是常见问题。nerfstudio 查看器默认会占用一个端口,如果端口被占用,启动时会失败。排查方式:
lsof -i:7007如果看到已有进程占用,可以换一个端口,或者杀掉旧进程再启动。
8. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| FFmpeg 抽帧后图片数量过少 | fps参数太低或视频本身太短 | 检查视频时长和frames目录图片数量 | 提高抽帧频率或补拍素材 |
| COLMAP 特征点很少 | 画面模糊、重叠率低、弱纹理区域多 | 打开 COLMAP GUI 查看特征点分布 | 提升图片清晰度,增加相机视角,避免大片纯色区域 |
| COLMAP 注册图片率低 | 图片间重叠不够、曝光差异过大 | 查看未注册图片在轨迹图中的分布 | 补充更多角度和高度,尽量覆盖被遗漏方向 |
| PyTorch 无法调用 GPU | CUDA 驱动与 PyTorch 版本不匹配 | 执行python -c "import torch; print(torch.cuda.is_available())" | 按 PyTorch 官方提供的方式重装匹配本机 CUDA 的版本 |
| 训练时显存不足(OOM) | 训练分辨率过高或 batch size 过大 | 查看nvidia-smi中已用显存 | 降低训练分辨率、调小 batch、停止其他 GPU 进程 |
| Web 查看器启动后无法打开 | 端口被占用或服务未启动 | 查看终端日志,执行lsof -i:7007 | 更换端口,或清理残留进程 |
| 渲染视频出现明显重影 | 相机位姿估计有错误 | 检查 COLMAP 稀疏点云和相机轨迹 | 删除错误相机帧,重新跑 COLMAP,再训练 |
| API 调用返回失败 | 路径错误、端口不可达或上传文件超限 | 先执行本机 curl 测试 | 确认服务进程和文件路径,调整超时和文件大小限制 |
| 批量任务中某个场景卡住 | 脚本没有设置超时或该场景数据异常 | 查看该场景独立日志和进程状态 | 为每个阶段设置超时,失败后加错误标记并继续 |
| 3DGS 出现大量飞点 | 训练步数不足或场景有反光玻璃材质 | 增加训练步数,检查素材中反光区域占比 | 降低该区域曝光,或使用更保守的训练参数 |
上面的排查逻辑同样适用于 NeRF:先看数据,再看硬件,最后看训练参数。不要一上来就换模型,很多时候问题出在素材和相机姿态上。
9. 最佳实践与使用建议
第一次跑通全流程,建议使用一个小规模场景:20 到 30 张图片即可,不要直接用一段 10 分钟的视频抽帧。小场景训练速度快,出了问题能快速定位是素材问题、标定问题还是训练参数问题。跑通后,再逐步增加训练集规模和更高分辨率。
场景文件目录最好统一,方便批量任务处理。下面是一个推荐的目录结构:
data/ scenes/ scene_01/ video.mp4 frames/ selected_frames/ sparse/ config.yml renders/ logs/每个场景保留原始视频、中间帧、COLMAP 输出、训练配置、渲染结果和日志。这样做的好处很多:训练失败时能看到是哪个阶段失败;换不同模型对比时,可以共用同一份 COLMAP 输出;批量任务完成后,也更容易判断哪些场景需要重跑。
批量任务一定要加日志和失败重试。三维重建流程较长,任何一个场景出错都会中断整体。更稳妥的做法是每个阶段独立记录日志,写入该场景的logs目录。如果某个场景失败,先看日志,再针对性补拍或调整参数,而不是盲目重跑整条命令。
接口服务要限制访问范围。即使是内部工具,也建议只监听 127.0.0.1,或者加 Token 验证。宿主机上如果跑了多个 Web 服务,启动前先查看端口占用情况,避免端口冲突。上传文件做大小限制,并拒绝执行用户提交的任意命令。后台脚本也应固定为服务端预置的run_recon.sh,不应让请求参数决定要执行的命令。
涉及人脸、车牌和人名时需要先做隐私处理后再参与重建。如果素材包含未授权人物或版权内容,不要用于商业发布。拍摄废弃建筑之前,先确认场地所有权和管理方规定。无人机拍摄还要检查所在地区的空域管理要求,不冒险拍摄。
10. 总结与下一步
这套流程最值得尝试的点,是把随手拍的废墟视频变成可交互的三维数字资产。最先应该验证的功能是 COLMAP,因为整个流程的成败在相机位姿估计这一步就基本确定了。最容易踩的坑有两个:一个是素材重叠率不够,导致 COLMAP 注册失败;另一个是显存限制下强行使用高分辨率训练,导致 OOM。先用小场景把链路跑通,再扩大到完整城堡,是更务实的路线。
后续可以在此基础上做的扩展也不少:把训练好的 3DGS / NeRF 模型部署到 Web 页面,做成在线漫游;接入 AR/VR 设备,让用户走进重建后的城堡内部;把整个流程做成定时导入,自动处理每天新到的采集素材;再加上模型压缩和 LOD 简化,就能比较接近可商用的数字孪生工作流了。建议收藏备用,下次拍废墟视频时,可以留一批素材,按本文顺序亲自走一遍流程。