这些年凡是跟三维重建、SLAM 沾边的项目,基本绕不开 COLMAP。它是我见过把运动恢复结构(SfM)和多视角立体视觉(MVS)整合得最完整的开源工具,从特征提取、匹配、几何验证,到增量式重建、稠密重建、网格生成,一条龙全给你安排得明明白白。我的日常工作流里,无论是做相机标定、生成 NeRF 训练数据,还是给无人机影像做地形重建,COLMAP 都是最核心的那一环。
不过,官方预编译的二进制包虽然省事,但到了自己服务器上总有各种不适配:可能没启用 CUDA、可能带的 GUI 缺东西、可能依赖库版本被锁死。所以我这次直接在 Ubuntu 24.04 LTS 上从源码编译了 COLMAP 3.13.0,配合 CUDA 12.9 工具链。折腾了两天,踩了一堆坑,总算把整套环境跑通。这篇文章不打算写成一个干巴巴的编译手册,我尽量把背后的原理、每一步的取舍、以及那些文档里从来不写的细节都讲清楚,让你照着走能少趟几个雷。
1. 整体设计与思路拆解
1.1 为什么是 COLMAP 3.13.0 + CUDA 12.9 + Ubuntu 24.04 这个组合
先说版本选择。COLMAP 3.13.0 是当前一个功能比较稳定的版本,对 CUDA 12 系列的支持已经比较成熟,不需要再去迁就早期那种“CUDA 11.x + 老驱动”的配置。更关键的是,3.13.0 的 CMake 构建逻辑更清爽,依赖项识别更干脆,编译过程中少了很多乱七八糟的手工 hack。
CUDA 12.9 是 NVIDIA 当前比较新的工具链版本。有人可能觉得“用这么新的 CUDA 有必要吗”?我的看法是,如果你的显卡本来就是 Ampere(RTX 30 系)或 Ada Lovelace(RTX 40 系)架构,新版 CUDA 对计算能力 8.x/9.x 的优化会更到位,编译出来的代码跑起来理论上也更稳。当然,CUDA 版本并不需要过分追新,但对于全新部署的环境,直接用 12.9 完全没毛病,反正 COLMAP 3.13.0 跟它配合得很好。
Ubuntu 24.04 LTS 是 2024 年 4 月发布的长期支持版本,背后有 5 年安全维护期,系统库版本新(比如 GCC 13.2、CMake 3.28),对 CUDA 12.9 的兼容性也非常好。三个版本放一起,基本就是一套“当前时间点上的标准新环境”,不用为了兼容老系统去折腾各种降级操作。
提示:如果你手头是特别老的显卡,比如 GTX 10 系列或者更早的 Maxwell 架构,建议先查一下计算能力(Compute Capability)是否在 CUDA 12.9 的官方支持列表里。虽然旧卡通常也能编过,但架构参数设置不对,编译出来的程序运行时会直接报“no kernel image is available”错。
1.2 三种部署方式的对比:预编译、Docker、源码编译
很多朋友问我:既然 COLMAP GitHub Releases 里有编译好的二进制包,为什么还要自己从源码折腾一遍?
我把三条路做过一次对比,优劣挺明显的。
| 部署方式 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| 官方预编译二进制 | 开箱即用,几分钟跑起来 | 不一定带 CUDA 加速;GUI 特性可能不全;无法针对自己显卡微调架构参数 | 快速验证流程、跑通 demo |
| Docker 镜像 | 环境隔离,拉下来就有,不污染宿主机 | GPU 透传要配 NVIDIA Container Toolkit;数据卷、权限管理麻烦;开发调试不直观 | 临时测试、给团队发统一环境 |
| 源码编译 | 完全可控,可自定义 CUDA 架构、GUI、第三方库版本;方便二次开发 | 耗时较长,依赖管理需要自己操心 | 长期开发、集成自有数据流、部署到生产服务器 |
我最终选择源码编译,核心原因是 COLMAP 很多时候不是终点,而是起点。比如我要往里面加自定义的特征提取模块、改 UI、或者把它的重建结果接入我自己的 MVS 流程,这时候拿着别人的二进制就很被动。另外自己编译可以把CMAKE_CUDA_ARCHITECTURES精确设到自己显卡的计算能力上,性能能吃到最大。
1.3 先把依赖树理清楚:COLMAP 编译到底需要哪些库
COLMAP 不是一个大孤岛,它依赖了一大堆第三方库。我习惯把依赖分成四层,方便理解和排查问题。
第一层是基础构建工具:gcc/g++、CMake、Ninja、git。CMake 负责生成构建系统,Ninja 负责并行编译,git 不用说,拉源码用的。
第二层是图像 IO 相关库:FreeImage(加载各种图片格式)、JPEG/PNG/TIFF编解码库。COLMAP 处理的就是图像数据,这一步绕不开。
第三层是数值计算和优化相关库:Eigen3(线性代数核心)、Ceres Solver(做 Bundle Adjustment 的主力)、SuiteSparse(稀疏线性求解)、glog/gflags(日志和命令行参数)。这一层是重建质量的关键,也是编译过程中最容易出幺蛾子的地方。
第四层是 GUI 相关:Qt5、QScintilla、GLEW、OpenGL。如果你不需要图形界面,可以把 GUI 关掉省不少事;但 COLMAP 的 GUI 确实好用,查看稀疏点云、各个视角的相机位置非常直观。
我用一个类比来理解这套结构:CMake 像施工队总指挥,负责把各路人马组织起来;第三方依赖就是提前做好的建材,比如 Ceres Solver 相当于一块高精度预制板,直接拿来用,不需要自己从零烧水泥。编译 COLMAP 本质上就是把“建材”备齐,然后叫“施工队”开工。
2. 核心依赖安装与关键细节
2.1 安装 CUDA 12.9 Toolkit:先装驱动还是只装 Toolkit
装 CUDA 是这整套流程里最容易被绊倒的地方,先把这个搞定再谈别的。CUDA 的安装分为两种主流方式:一种是 deb 包方式,一种是 runfile 方式。还有一个前置问题:显卡驱动是装新版还是保留旧版。
我这次用的是 runfile 方式,理由是它可控性最高。deb 方式会把 CUDA 和显卡驱动绑在一起装,如果你系统里已经有一个稳定运行的 NVIDIA 驱动,deb 方式很可能直接把你的驱动换掉,重则导致图形界面黑屏。runfile 方式可以选择不装驱动,只装 Toolkit 本体,就像给软件单独装一个“运行时环境”而不去动系统已有的“驱动固件”。
具体步骤:
去 NVIDIA 官网下载 CUDA Toolkit 12.9 的 runfile 安装包。步骤是:官网 → 选择 Linux → x86_64 → Ubuntu → 24.04 → runfile (local),官网直接拷贝下载命令即可。
如果系统里已经有可用驱动,先别急,确认一下版本:
nvidia-smi能看到显卡信息且驱动版本不太老的话,就能放心用下面的命令跳过驱动安装。
- 给 runfile 加执行权限,并执行:
chmod +x cuda_12.9.0_*.run sudo ./cuda_12.9.0_*.run --toolkit --toolkit-path=/usr/local/cuda-12.9 --no-opengl-libs这里的--toolkit指的是只装 CUDA 工具包;--no-opengl-libs避免覆盖系统的 OpenGL 库,如果机器还要跑桌面环境,这一项很重要;装完后再补一句:
sudo ln -sf /usr/local/cuda-12.9 /usr/local/cuda- 环境变量配置,这是很多人忽视的一步。把下面这四行写进
~/.bashrc:
export CUDA_HOME=/usr/local/cuda export PATH=$PATH:/usr/local/cuda/bin export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda/lib64然后source ~/.bashrc,通过nvcc -V验证版本。
整个过程听起来不复杂,但我见过太多人死在环境变量上。nvcc找得到,但 CMake 或运行时找不到libcudart.so,就是LD_LIBRARY_PATH没配对。
注意:如果你的机器本来就要做桌面 UI,千万别手贱装成整个 CUDA deb 包带的新驱动,极易跟桌面环境起冲突,最后落得“开机进不了图形界面”的下场。runfile 安装是你在这块最稳的靠山。
2.2 一套干净的系统依赖:apt 包的选择与调配
Ubuntu 24.04 的好处在于,很多 COLMAP 的依赖都能直接用 apt 装上,不用一个个去手动编译。我建议在编译 COLMAP 之前先把这些包装齐:
sudo apt update && sudo apt upgrade -y sudo apt install -y \ build-essential \ cmake \ ninja-build \ git \ libboost-all-dev \ libeigen3-dev \ libfreeimage-dev \ libgoogle-glog-dev \ libgflags-dev \ libglew-dev \ qtbase5-dev \ libqt5opengl5-dev \ libcgal-dev \ libflann-dev \ libsqlite3-dev \ libmetis-dev \ libsuitesparse-dev \ libqscintilla2-qt5-dev这里有几个点要特别注意:
第一,libqscintilla2-qt5-dev在 Ubuntu 24.04 的仓库里可能不叫这个名字,不同小版本下会有包名变动。如果 apt 提示找不到,可以先apt search qscintilla搜一下实际的包名,再决定装那个。QScintilla 是 COLMAP GUI 用来做脚本编辑器的组件,没有它 GUI 编译会挂。
第二,libmetis-dev和libsuitesparse-dev对 Ceres Solver 和最终的稀疏求解非常重要。Metis 是做图剖分的工具库,SuiteSparse 里面的CXSparse、CHOLMOD是 COLMAP 做大规模稀疏线性求解的底层依赖。少了它们,编译会报一些跟SuiteSparse相关的找不到符号错误。
第三,libceres-dev在 Ubuntu 24.04 里是存在的,但版本不一定够新。COLMAP 3.13.0 对 Ceres 有最低版本要求(通常要 2.2+),不同时期仓库里的 Ceres 版本线也不好说。为了保证不在这卡壳,我一般宁可从源码编译 Ceres,这在下一节展开。
2.3 为什么建议源码编译 Ceres Solver
Ceres Solver 是 COLMAP 做 Bundle Adjustment(光束法平差)的核心优化库。它的作用贯穿 SfM 的每一次迭代优化,可以说是重建精度的一个决定性因素。
从 apt 仓库直接装libceres-dev确实方便,但版本往往滞后。以我这次的环境为例,apt 仓库的 Ceres 版本在编译 COLMAP 3.13.0 时差点不够格,与其做各种“版本下限”的妥协,不如直接源码编译最新稳定版,一劳永逸。源码编译 Ceres 也不难,大概就是标准的三步走:
git clone https://ceres-solver.googlesource.com/ceres-solver cd ceres-solver mkdir build && cd build cmake .. -GNinja -DBUILD_SHARED_LIBS=ON -DBUILD_EXAMPLES=OFF -DBUILD_TESTING=OFF ninja sudo ninja install关键参数解释:
-DBUILD_SHARED_LIBS=ON:编译成动态库而不是静态库。虽然静态库链接时更方便,但后续如果有多个项目共用同一份 Ceres,动态库可以显著减小二进制体积,也方便升级。-DBUILD_EXAMPLES=OFF和-DBUILD_TESTING=OFF:跳过示例和测试代码,能省下不少编译时间,毕竟我们这里只是把它当作依赖。
安装完 Ceres 之后,最好跑一下ldconfig,确保系统能找到刚安装的动态库:
sudo ldconfig这个动作很关键,因为如果 Ceres 装进了/usr/local/lib,而系统的动态库缓存没更新,后面 COLMAP 链接时就会报“找不到 libceres.so”的错。
3. 完整编译流程实操
3.1 源码获取与构建目录规划
源码获取没有太多技巧,直接从 GitHub 拉 COLMAP 的官方仓库。注意要拉对 release 版本,不要在 master 分支上浪,master 分支的代码往往在快速演进中,依赖要求可能也在变。这次要的是 3.13.0:
git clone https://github.com/colmap/colmap.git cd colmap git checkout 3.13.0目录规划这块看起来老生常谈,但很影响后续的幸福程度。我的习惯是,源码目录和构建目录分开,构建目录建议建在源码目录内的build/,而安装目录单独指定,不要默认铺到系统目录。这样卸载的时候非常干净,直接删掉安装目录就行。我习惯在构建时通过 CMake 参数显式指定安装前缀,比如-DCMAKE_INSTALL_PREFIX=/opt/colmap-3.13.0。
3.2 CMake 配置:唯一不能写错的参数是 CUDA 架构
CMake 配置是整个编译流程里信息量最大的一步,也是新手最容易翻车的地方。先直接给出我这次实际使用的完整命令,然后逐个解释:
cd colmap mkdir build && cd build cmake .. -GNinja \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/opt/colmap-3.13.0 \ -DCMAKE_CUDA_ARCHITECTURES=89 \ -DCUDA_ENABLED=ON \ -DGUI_ENABLED=ON \ -DCMAKE_CUDA_COMPILER=/usr/local/cuda/bin/nvcc很多人配置 CMake 时最容易栽在CMAKE_CUDA_ARCHITECTURES上。这个参数的实质是把 CUDA 的汇编器需要支持的计算能力列表告诉编译器。我的显卡是 Ada Lovelace 架构的 RTX 40 系,计算能力是 8.9,所以写成89。NVIDIA 对不同架构的计算能力有个对应表:
| 显卡架构 | 典型型号 | 计算能力 |
|---|---|---|
| Turing | RTX 20 系列 | 75 |
| Ampere | RTX 30 系列、A100 | 80 / 86 |
| Ada Lovelace | RTX 40 系列 | 89 |
| Hopper | H100 | 90 |
如果你不清楚自己的显卡计算能力,可以在终端直接跑:
nvidia-smi --query-gpu=compute_cap --format=csv如果代码要部署到多种显卡上,可以写成75;80;86;89,用分号隔开。但也没必要无限堆砌,每种架构都会让编译时间变长、二进制体积变大,实际用哪种卡就写哪一种,这也是源码编译的价值之一。
-DCUDA_ENABLED=ON是 COLMAP 自己定义的选项,用来控制是否启用 CUDA 相关模块。如果你在装好了 CUDA 的情况下这里显示 OFF,多半是 CMake 没探测到 CUDA,后面排查章节我会细说。
-DGUI_ENABLED=ON默认一般是开着,如果你不需要图形界面或者 Qt 相关依赖出了问题,可以临时关掉OFF把核心编译跑通,之后再回头处理 GUI 问题。
-DCMAKE_CUDA_COMPILER=/usr/local/cuda/bin/nvcc属于防御性写法。系统里同时存在多个 CUDA 版本时,CMake 可能找到旧的 nvcc,显式指定才能确保用的是 12.9 的那一套。
3.3 Ninja 并行编译与安装
配置完成后,进入编译阶段。Ninja 相比 make 的优势是全量构建时的依赖调度更精细,增量编译更快,输出也更清爽。不过并行编译的线程数选择需要稍微动点脑子:
ninja -j8-j后面的数字不是越大越好。CUDA 的编译过程其实分两步:先是nvcc把.cu源码编译成 PTX 或 cubin,这步非常吃内存。我实测下来,一个 nvcc 编译线程峰值能吃掉 2~3GB 内存,如果机器只有 16GB 内存却开-j16,编译器很容易在链接阶段因为内存不足被 OOM Killer 干掉,报一些看起来非常诡异的“internal compiler error”。保险的做法是查看物理内存总量来估算线程数:
free -h nproc比如内存 32GB、CPU 12 核,那么-j6或-j8是比较舒服的档位,既能让 CPU 满负荷运转,又不会在内存上翻车。
编译顺利的话,几分钟后就能看到ninja输出的完成信息。接下来执行安装:
sudo ninja install由于配置时指定了CMAKE_INSTALL_PREFIX=/opt/colmap-3.13.0,所有编译好的二进制、头文件、库文件都会被放到这个目录里。验证是否安装成功:
/opt/colmap-3.13.0/bin/colmap -h能看到 COLMAP 的帮助信息那一刻,就说明命令行版本已经没问题了。如果想验证 GUI 是否正常编译出来:
/opt/colmap-3.13.0/bin/colmap gui正常情况下会弹出一个 Qt 窗口,左侧是场景树,右侧是三维视图区域。
4. 常见问题与排查技巧实录
4.1 CUDA 检测失败 / 版本识别不对
这是源码编译 COLMAP 时最容易碰到的问题之一。症状一般是 CMake 配置阶段输出的日志里,CUDA_ENABLED显示 OFF,或者编译到一半报CUDA_cublas_LIBRARY not found、CUDA_cudart_LIBRARY not found。
排查思路按照从用户态到配置态的先后顺序来:
第一步,验证nvcc -V是否能正常输出版本信息。这一步是最低级别检查,如果nvcc: command not found,说明环境变量没配对,回到 2.1 节把~/.bashrc重新检查一遍。
第二步,验证 CMake 能否找到 CUDA:
cmake --find-package -DNAME=CUDA -DCOMPILER_ID=GNU -DLANGUAGE=CXX -DMODE=EXIST如果找不到,多半是 CMake 的CUDAToolkit_ROOT没有指向正确位置。在 3.2 节的 CMake 命令里补一项-DCUDAToolkit_ROOT=/usr/local/cuda,CMake 就会沿着这个路径去找include/cuda.h和lib64/libcudart.so。
第三步,清掉旧的 CMakeCache 重新配置。这个灵感是我从一次极其痛苦的排查中得来的。有时候第一次 CMake 配置用的环境是坏的,CMake 会把错误的路径缓存到CMakeCache.txt,后面怎么改环境变量都不生效。解决方式简单粗暴:
rm -rf CMakeCache.txt CMakeFiles然后再重新跑 CMake 配置命令,往往问题就奇迹般地消失了。
4.2 GUI 编译报错:QScintilla、Qt 相关的坑
GUI 相关的编译错误非常折磨人,因为报错信息往往晦涩难懂,比如“找不到Qsci/qscilexer.h”或者“QT5::Widgets相关 target not found”。
这两类报错说明的核心问题是一致的:Qt5 或者 QScintilla 的开发头文件没有正确安装,要么是装错了包,要么是版本不一致。在 Ubuntu 下,Qt5 开发头文件由qtbase5-dev提供,QScintilla 由libqscintilla2-qt5-dev提供,这点可以回看 2.2 节。
如果确实搜不到 QScintilla 或者安装后依旧报错,一个务实的备选方案是先关掉 GUI 编译:
-DGUI_ENABLED=OFF先把核心的 COLMAP 命令行编译跑通,确保 CUDA 相关的功能和重建流程没有问题。之后再回过头来专门解决 QScintilla 的版本问题,这样可以避免 GUI 依赖的问题阻塞整条编译链路。如果必须要 GUI,可以下载 QScintilla 的源码单独编译,再用CMAKE_PREFIX_PATH告诉 COLMAP 到哪找它。
4.3 链接期 OOM 与并行度控制
前面提到过,Ceres、SuiteSparse 这类大型 C++ 库在链接阶段会吃很多内存,APR 阶段更是会并发产生大量.o文件。很多人第一次跑ninja时图省事直接-j$(nproc),然后眼睁睁看到进程被系统杀掉,日志里甚至不出现编译器报错,只有一句 “Killed”。
解决方案是动态调整并行度。我建议用watch -n 2 free -h开一个终端实时监控内存,核心编译线程单独开另一个终端跑。如果内存占用眼看要见底,就打断编译,改用更小的并行数重新执行:
kill -INT $(pgrep -f ninja) # 中断编译 ninja -j4 # 降低线程数Ninja 有断点续传的能力,已经编译完的目标不会重复编译,所以中断重来并不可怕。
4.4 其他小坑速查表
我把这几天的其他零散问题整理成一张速查表,照着查能省不少时间:
| 症状 | 可能原因 | 快速对策 |
|---|---|---|
| CMake 找不到 Boost | libboost-all-dev 没装或版本冲突 | apt install libboost-all-dev;必要时指定-DBOOST_ROOT |
| Eigen 报错 | 系统有多个 Eigen 版本 | 卸载旧版,统一用/usr/include/eigen3,或设置-DEIGEN3_INCLUDE_DIR |
| Ceres 报错“Your Ceres version is too old” | apt 的 Ceres 太旧 | 源码编译新版 Ceres,重新ldconfig |
| 编译时 NVCC 报错 “unsupported GNU version” | CUDA 与 GCC 版本不匹配 | CUDA 12.9 对 GCC 13 兼容没问题;老 CUDA 需要加--allow-unsupported-compiler或换低版本 GCC |
找不到libfreeimage.so | FreeImage 没装 | apt install libfreeimage-dev |
| 运行时报 “error while loading shared libraries” | 动态链接库路径缺失 | 在/etc/ld.so.conf.d/colmap.conf写入/usr/local/lib,运行sudo ldconfig |
其中的“NVCC 报错 unsupported GNU version”值得多说两句。CUDA 和 GCC 的版本兼容矩阵是真实存在的,新版 CUDA 通常只支持到某个 GCC 大版本。如果你用老 CUDA 配 Ubuntu 24.04 自带的 GCC 13,很可能会被 NVCC 拒绝。这种情况下两个选择:一个是装一个老版本 GCC,通过update-alternatives切换默认 gcc/g++;另一个是在 NVCC 编译参数里加--allow-unsupported-compiler。第一个办法更稳,第二个办法属于把安全阀门拔了硬上,我建议你就算用也要多留个心眼,编译产物是否正常要以最终运行测试为准。
5. 最后再分享一个小技巧
编译安装完 COLMAP 后,我建议你立刻做一个“全流程冒烟测试”,别急着拿真实数据集跑。用 COLMAP 仓库自带的测试数据或者随便一张图片做feature_extractor,能跑通就说明 CUDA 模块、图像 IO、数据库读写这些链路都正常。再跑一次mapper,输入端到端 SfM 管线,这一步能同时验证 Ceres Solver 和稀疏求解器是否有问题。比起拿着一大堆无人机影像跑到一半才发现环境没配对,先花一根烟的功夫做冒烟测试要划算得多。
这套 COLMAP 3.13.0 + CUDA 12.9 + Ubuntu 24.04 的组合,我这段时间跑下来很稳。个人经验是:编译环境这块,版本组合千万别盲目追新,用之前先把兼容性矩阵查清楚,能避免绝大多数莫名其妙的报错。如果读者只是验证算法跑,直接下载官方二进制或拉一个 Docker 镜像确实更省心;但要真正做二次开发、往生产环境里部署,自己编译一遍的价值是无可替代的——你对自己手里这套环境会有一种“心里有底”的确定感。希望这篇能帮你跳过我最开始走过的那些弯路。