最近在AutoDL上把一条经典的三维重建链路完整跑通了:先用COLMAP对一组照片做稀疏重建,得到相机位姿和稀疏点云,然后把这些数据整理成CasMVSNet需要的格式,在云端GPU上推理出稠密深度图,最后融合成点云。相信很多做MVS的同行都有类似经历——论文代码看起来明明白白,自己一上手,光是把COLMAP导出的相机参数转换成网络能读的格式,就能卡掉半天,更别说AutoDL上环境版本不匹配这类基础问题。这篇文章就把我从零跑通的经验整理出来,适合两类人:一是想用AutoDL复现CasMVSNet的同学,二是有COLMAP结果但不知道怎么喂给MVS深度学习模型的研究者。
1. 项目全貌:COLMAP、CasMVSNet、AutoDL三者到底怎么配合
1.1 它们各负责哪一段
先把这条链路上的角色理清楚。COLMAP是一个开源的Structure-from-Motion工具,输入是一组有重叠区域的普通照片,输出是每张照片对应的相机内参、外参,以及一个稀疏的三维点云。这个阶段解决的是“每张照片是在哪个位置、朝哪个方向拍的”这个几何问题。
CasMVSNet是一个基于深度学习的多视图立体匹配网络,核心思路是用级联的代价体从粗到细估计深度图。它拿到的是COLMAP给出的相机参数和图像序列,输出的是每个参考视角的稠密深度图。这个阶段解决的是“照片中每个像素点在三维空间中距离相机多远”的问题。
AutoDL在这个流程里承担的是算力平台角色。CasMVSNet推理过程中需要GPU,尤其是处理高分辨率图像时显存需求不小。相比自己装一台带GPU的机器,租一台云端服务器明显更灵活,按小时计费,用完直接释放,环境坏了也能快速重建。整条链路可以概括成一句话:COLMAP负责算姿态,CasMVSNet负责算深度,AutoDL负责提供算力。
1.2 为什么选择AutoDL而不是本地或传统云主机
我选择AutoDL的最直接原因是省事。本地GPU服务器需要处理驱动、CUDA、torch版本匹配,这一套环境问题就能劝退不少人。AutoDL的镜像市场里已经有打包好的PyTorch环境,创建一个实例后顺手python -c "import torch; print(torch.cuda.is_available())",能输出True就可以直接进入正题。
传统云主机(比如ECS一类)虽然也能装GPU驱动,但整个流程需要自己一步步配置,效率低不少。AutoDL在校园和研究群体里很流行,社区镜像和教程积累使得大部分常见框架都有现成环境,基本上能做到“开箱即用”。对于复现CasMVSNet这种有一定依赖复杂度、又需要快速验证想法的项目,AutoDL的体验是明显更顺滑的。
1.3 完整流程一览
从照片到点云,我的操作顺序是这样的:
- 本地用COLMAP对照片做特征提取、匹配和稀疏重建,导出相机参数。
- 写一个转换脚本,把COLMAP的稀疏重建结果转成CasMVSNet要求的数据格式(cam文件、pair.txt、图像目录)。
- 把数据和预训练权重打包上传到AutoDL的
/root/autodl-tmp数据盘。 - 在AutoDL上配置CasMVSNet运行环境,跑推理脚本。
- 得到深度图后,用融合代码生成稠密点云并可视化。
这个流程看着简单,实际上每一步都有细节要注意。接下来我会把从AutoDL选购实例到最终出点云的完整过程拆开讲。
2. AutoDL服务器选购与远程连接:镜像、显卡、VSCode一条龙
2.1 镜像怎么选:PyTorch版本与CUDA版本搭配
创建AutoDL实例时,最重要的一步是选镜像。点开“镜像市场”会发现有大量预置镜像,有Miniconda版、PyTorch版、TensorFlow版等。我的建议是选择带Miniconda且预装了PyTorch的镜像,版本上挑Python 3.8、PyTorch 2.0.0、CUDA 11.8的组合。
为什么这么选?CasMVSNet这类MVS项目的代码大多是2020年前后发布的,很多扩展库对较新CUDA的兼容性并不好。如果选了CUDA 12以上的镜像,编译某些算子时容易遇到undefined symbol或头文件找不到的问题。CUDA 11.8则处在“新老兼顾”的位置,既能跑最新版PyTorch,又能兼容老代码。
注意:如果镜像里没有合适的conda环境,别慌。可以直接用镜像自带的base环境,只需要
pip install项目缺的依赖就行。除非源码明确要求某个Python版本,否则不必自己再折腾一个conda环境。
2.2 GPU怎么选:显存多大才够CasMVSNet折腾
CasMVSNet的显存占用取决于输入图像分辨率和邻居视图数量。以一张1200×1600的图、5个邻居视图为例,常见的官方配置在24G显存显卡上跑得比较从容。如果分辨率降到800×600,邻居视图降到3个,那么11G甚至8G显存也能跑,但图像太小时重建质量会明显下降。
我在AutoDL上一般优先选24G显存的型号,比如RTX 3090或4090。两者的推理速度差别不算质变,看预算选就行。值得注意的是,很多MVS代码默认从环境变量读取GPU编号,实例创建好后用nvidia-smi确认一下显卡状态,再在代码里设置CUDA_VISIBLE_DEVICES。
2.3 用VSCode Remote SSH连AutoDL,比网页终端顺手多了
AutoDL网页端提供的终端适合快速敲命令,但真正常时间改代码、调试,我还是推荐用VSCode Remote SSH。在AutoDL控制台找到实例的SSH登录指令,其中包括主机名和端口。然后在本地~/.ssh/config里加上一段配置:
Host autodl HostName region-X.autodl.com Port 12345 User root IdentityFile ~/.ssh/id_rsa用VSCode安装Remote-SSH插件,点击连接后输入私钥或密码,就能像本地一样浏览文件、运行终端、调试Python。这个过程本质是SSH,所以如果你习惯用其他终端工具,原理也是一样的,只是VSCode的文件树和代码跳转体验更好。
2.4 数据上传:文件从本机到AutoDL的几种途径
AutoDL的数据盘挂在/root/autodl-tmp,实例关机后数据不丢。强烈建议把数据集、权重文件放这里,放系统盘有实例释放后丢失的风险。上传文件最直接的方式是scp:
scp -P 12345 data.zip root@region-X.autodl.com:/root/autodl-tmp/文件多、体积大的时候,先压缩再传是常识。如果数据量小,也可以直接用AutoDL网页端的文件管理功能上传,它能直接上传本地文件到指定路径,省去记忆scp命令的麻烦。上传完成后用unzip解压到数据盘对应目录即可。
3. CasMVSNet环境搭建:从拉源码到能跑通测试
3.1 项目目录结构和源码准备
CasMVSNet的开源实现有好几个版本,我在GitHub上找的是比较常用的PyTorch实现。clone下来后先看目录结构,核心文件通常是models下的网络定义、dataset下的数据读取、以及根目录的test.py和train.py。这个结构基本延续了MVSNet系列的代码风格,理清楚入口脚本的参数列表比什么都重要。
在AutoDL上执行:
git clone https://github.com/你的源地址/CasMVSNet.git cd CasMVSNet如果clone速度不理想,也可以在本地下好再通过scp传上去,反正大小通常不大。克隆完先别急着跑,打开test.py看一遍argparse定义的参数,了解每个参数的含义,这一步能省下后面大量排查时间。
3.2 依赖安装:重点处理torch、cv2、plyfile这些老面孔
虽然AutoDL镜像自带PyTorch,但不代表项目所有依赖都齐了。我实际运行中至少需要补装这几个包:
pip install opencv-python numpy plyfile open3d tensorboard如果项目用到torch_scatter或torch_sparse这类扩展包,安装会比较麻烦。先检查源码里有没有import torch_scatter,有的话再考虑安装。另外,如果源码里需要编译CUDA算子(例如某些setup.py),建议直接在AutoDL镜像环境下编译,因为本地的CUDA路径不一定和云端一致。
3.3 编译与自检:先让Python把环境跑起来
依赖装好后,最简单的自检方式是:
python -c "import torch, cv2, numpy; print(torch.__version__, torch.cuda.is_available(), cv2.__version__)"如果输出True,说明环境基本就绪。然后找一个最小的数据集样例跑一次test.py。CasMVSNet官方仓库通常会提供一个简单的测试样例,哪怕是单张参考图也行,目标是确认模型能加载预训练权重、前向推理不报错。这一步跑通了,后面换成自己的数据时心里才有底。
4. COLMAP数据转换:让CasMVSNet吃下你的相机参数
4.1 COLMAP输出的文件到底有哪些
用COLMAP跑完稀疏重建后,项目目录下会生成一个sparse目录,里面是cameras.bin、images.bin、points3D.bin这三个文件。如果用的是COLMAP GUI,路径通常是sparse/0/;命令行跑的话也一样。
这三个文件分别是相机内参、图像位姿、稀疏点云。要从二进制里读取内容,可以用官方Python脚本read_write_model.py,也可以先用COLMAP的模型转换器导出成txt:
colmap model_converter --input_path ./sparse/0 --output_path ./sparse/0_txt --output_type TXT导出txt后,images.txt里每一行的qvec和tvec就是该图像在世界坐标系到相机坐标系的旋转四元数和平移向量,这是后续转CasMVSNet格式的核心输入。
4.2 坐标系统差异:COLMAP与CasMVSNet之间的经典大坑
COLMAP遵循的是计算机视觉常见的坐标系约定,而CasMVSNet在读取相机参数时,具体使用R和t的方式取决于仓库实现。很多初次接触的人在这里踩坑,表现是深度图预测出来像是乱码,或者点云整体翻转。原因是外参矩阵存的是世界到相机(W2C)还是相机到世界(C2W),以及旋转矩阵是否转置,两个框架的约定不完全一致。
建议调试时不要一次性批量处理,先拿一张参考图,把COLMAP稀疏点云按同一坐标投影到该视角,验证下相机位姿是否和稀疏点云对齐。这一步能直观地判断坐标转换写对了没有,比盯着数字猜效率高得多。
4.3 转换脚本核心步骤:cam文件、pair.txt、深度范围
CasMVSNet读取数据的核心依赖两个东西:一是每个参考视角对应的cam文件,二是pair.txt。cam文件一般按以下格式组织(具体以你clone的仓库read_cam_file为准):
extrinsics 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 intrinsics 1000 0 640 0 1000 480 0 0 1 0.5 8.0其中第1到4行是4×4外参矩阵,第5行是固定的intrinsics标记,第6到8行是3×3内参矩阵,第9行是深度最小值与最大值。转换脚本要做的就是把COLMAP的每张图像的旋转矩阵和平移向量,转换成这个外参矩阵;内参直接从COLMAP对应camera读取即可。
pair.txt的作用是指定每个参考视图用哪些邻居视图做代价体构建,CasMVSNet在test.py里会用pair信息去cam目录下找对应index的cam文件。pair.txt的格式大致是:第一行为视图总数,随后每个参考视图一行索引,再一行邻居数目,再一行邻居视图编号列表。如果你的场景只有少数视图,最简单的策略是按顺序取前后相邻的图作为邻居。
深度范围的确定直接关系到重建质量。COLMAP的稀疏点云提供了场景的真实尺度参考,把稀疏点投影到每个相机坐标系,统计深度最小值与最大值,再外扩10%~20%作为该视角的深度范围。如果场景尺度差异大,可以每个视角单独算范围,CasMVSNet是支持单视角范围设定的。
4.4 数据目录怎么组织:图片、cam、pair一一对应
CasMVSNet测试时,数据目录通常需要组织成这种形式:
/root/autodl-tmp/dataset/scan1/ ├── images/ │ ├── 00000.jpg │ ├── 00001.jpg ├── cams/ │ ├── 00000_cam.txt │ ├── 00001_cam.txt ├── pair.txt注意图片文件名和cam文件名必须能对应上,通常用8位数字编号(00000000.jpg)。如果COLMAP导出的图片名本来就是数字,就按排序后的索引重命名所有文件。数据准备好后,整个目录压缩传到AutoDL数据盘,解压出来就能跑。
5. 执行CasMVSNet推理与结果可视化
5.1 测试脚本关键参数解析
运行CasMVSNet的测试脚本时,核心参数是数据路径、权重路径、输出路径和视图数量。具体命令因仓库而异,但大体长这样:
python test.py \ --loadckpt ./checkpoints/casmvsnet.ckpt \ --dataset general \ --testpath /root/autodl-tmp/dataset \ --testlist scan1 \ --outdir /root/autodl-tmp/outputs \ --numview 5参数里最容易忽略的是--testlist,它指定了在testpath下要测试哪些scan,有些版本要求传一个txt文件路径,有些直接传scan名称。--numview控制每个参考视图使用的邻居视图数量,初学时设5左右比较稳妥,太多会增加显存压力。
5.2 运行与日志解读
跑起来后终端会输出每个参考视图的深度估计进度。我第一次跑的时候最常犯的错是忽略输入分辨率,原始图可能超过两千万像素,直接把显存打爆。解决方法是先降采样图像到合适尺寸,或者在数据准备阶段把图片缩放到宽1200左右,兼顾速度和显存消耗。
如果日志里出现CUDA out of memory,优先降低图像分辨率,其次减少--numview。如果出现找不到cam文件,检查cams目录和pair.txt里的编号是否一致。跑完一个scan,输出目录下会生成对应视图的深度图文件,一般是.pfm格式,需要用专门的工具读取。
5.3 从深度图到融合点云:后处理流程
单张深度图不等于最终点云,MVS标准流程里还有一步深度图融合,把多个视角的深度图融合到一个一致的三维点云中。CasMVSNet官方通常推荐使用fusibile或类似工具,输入是所有视角的深度图、相机参数和图像列表,输出一个.ply文件。
如果只是想快速查看重建效果,可以用Open3D加载融合后的点云:
import open3d as o3d pcd = o3d.io.read_point_cloud("result.ply") o3d.visualization.draw_geometries([pcd])Open3D在AutoDL上可视化时,如果没有图形界面窗口,可以先把点云保存下来下载到本地查看,或者转发X11端口。对于验证结果,本地查看是最省事的方案。
6. 踩坑实录:常见问题与排查清单
6.1 环境与编译问题
环境问题集中体现在CUDA版本不匹配和依赖缺失上。CUDA版本不匹配的报错往往是运行时undefined symbol,比如libcudart.so.11.0缺少。这种问题靠重装PyTorch或者切换镜像能解决。依赖缺失则直接看ModuleNotFoundError,缺哪个补哪个,pip install即可。
编译CUDA算子失败时,多半是gcc版本太高或者CUDA_HOME路径没找到。AutoDL镜像自带的gcc版本一般没问题,如果遇到gcc: error: unrecognized command line option,需要降低gcc版本。这些情况相对少见,但遇到了会很浪费时间,建议按报错信息精确搜索。
6.2 数据与坐标转换问题
坐标问题是最难排查的,因为报了错可能不是显性报错,而是结果错误。深度图看起来一片混沌,点云位置错乱,通常就是外参矩阵的行列顺序有问题。我遇到过的具体表现为:点云前后颠倒、上下颠倒、或者姿态明显偏斜。应对办法很简单,先可视化COLMAP稀疏点云和相机位姿,确认COLMAP阶段没问题,再用单张图转换去对照投影结果,逐项排除。
另一个高频问题出现在图像尺寸变化后内参没缩放。COLMAP重建时用的是原图分辨率,如果你测试时把图缩小了,却没有对应缩放内参矩阵的fx、fy、cx、cy,那深度图必然不对。这个细节非常容易忽视,转换脚本里最好显式做一次缩放。
6.3 资源耗尽与性能问题
显存不足是最常见的问题,但很多人第一时间想的是换更大的显卡,其实优化空间更大的是输入分辨率和邻居视图数量。我实测把分辨率从1600降到1200,显存占用能降三分之一以上。另外,CasMVSNet级联结构在不同stage的代价体分辨率不同,--numdepth设置偏大也会显著增加显存占用,可以适当减少最粗stage的深度采样数。
6.4 常见问题速查表
| 报错或现象 | 可能原因 | 解决方法 |
|---|---|---|
ModuleNotFoundError: No module named 'plyfile' | 缺少依赖 | pip install plyfile |
CUDA error: out of memory | 分辨率或视图数过大 | 缩小图像、减少--numview |
FileNotFoundError: 00000000_cam.txt | cam目录或文件名不匹配 | 统一数字编号,检查路径 |
| 深度图整体错乱/翻转 | 外参矩阵格式不对 | 验证R和t的转置关系、W2C/C2W约定 |
| 点云尺度异常大/小 | 深度范围设置不当 | 用稀疏点投影统计深度范围 |
undefined symbol报错 | CUDA/torch版本不匹配 | 切换镜像或重装对应版本PyTorch |
这套流程我前后折腾了两天,最大的心得是:COLMAP转CasMVSNet格式时,坐标系的坑远大于环境配置,先把单个scan用可视化的方式验证相机姿态和稀疏点云能对上,再批量喂给网络,能省下很多排查时间。如果你也在跑这条路,建议先下载一个官方DTU样例数据集跑通,再切换成自己的COLMAP数据,这样至少能确定是代码问题还是数据问题。最后再分享一个小技巧:AutoDL实例不要同时跑太多任务,数据盘和系统盘分开用,权重和数据集放在/root/autodl-tmp,代码放系统盘,重装实例后恢复成本低很多。