1. 为什么在 Ubuntu 20.04 上装 OpenCV 3.4.16 这件事值得单独写一篇
如果你正在跟着《视觉SLAM十四讲》做练习,大概率会在第 4 讲或第 5 讲前后撞上同一个坎:书里的示例代码依赖 OpenCV 3.x 的 API,而你手头这台 Ubuntu 20.04 系统,默认源里能装到的 OpenCV 版本是 4.x。直接apt install libopencv-dev装出来的 4.x 版本,编译书里的imageBasics、undistortImage、stereoVision这些例子时,轻则一堆CV_LOAD_IMAGE_COLOR未定义的报错,重则链接阶段直接找不到符号。这不是你代码写错了,是版本 API 断层导致的。
所以“在 Ubuntu 20.04 上从源码编译安装 OpenCV 3.4.16”这件事,本质上不是一道可做可不做的选择题,而是跟着这本书做实验的必经之路。3.4.16 是 3.4.x 系列里比较靠后的一个维护版本,兼容性好、bug 少,社区里针对 SLAM 十四讲的踩坑记录大多也是围绕这个版本展开的,选它比选 3.4.10 或 3.4.14 更稳妥。
这篇文章面向的是刚上手 SLAM、对 Linux 编译流程还不算特别熟的同学,也适合那些装过一次但被各种依赖报错劝退、想重新理一遍流程的人。我会把整个编译安装过程拆成“依赖准备—源码配置—编译安装—环境验证—与十四讲工程对接”几个阶段,每一步都讲清楚为什么这么做、参数为什么这么设,以及我自己踩过的那些坑。装完之后,你不仅能跑通书里的例子,还能顺手把这套 OpenCV 用到自己的小项目里。
需要提前说明的是,源码编译 OpenCV 是个吃时间和 CPU 的活,一台普通四核笔记本跑make -j4大概要 20 到 40 分钟,内存不足 4G 的机器还可能在中途被 OOM 杀掉进程。所以动手之前,先把心态放平,把依赖装全,比什么都重要。
2. 装之前的整体思路与方案选型
2.1 为什么不直接用 apt 装,非要源码编译
Ubuntu 20.04 的官方软件源里,libopencv-dev对应的版本是 4.2。这个版本本身没问题,问题在于它和 OpenCV 3.x 之间存在大量 API 不兼容。举几个十四讲里高频出现的例子:
CV_LOAD_IMAGE_COLOR、CV_LOAD_IMAGE_GRAYSCALE这些宏在 4.x 里被改成了cv::IMREAD_COLOR、cv::IMREAD_GRAYSCALE,旧宏直接移除。cv::solvePnP的部分重载签名在 4.x 里调整过,传参方式变了。- 一些
CvMat、IplImage相关的旧 C 接口在 4.x 里被进一步弱化。
你当然可以手动把书里的代码改成 4.x 的写法,但十四讲配套的slambook2工程里代码量不小,逐个改既费时又容易引入新错误。更省事的做法就是装一个 3.4.16,让代码去适配库,而不是让库去迁就代码。
另一个原因是,源码编译能让你自由控制编译选项。比如 SLAM 里常用的viz模块(依赖 VTK 做 3D 可视化)、contrib扩展模块,apt 装出来的包不一定带全,而源码编译时你可以通过-D WITH_VTK=ON、-D OPENCV_EXTRA_MODULES_PATH=...精确控制要哪些功能。
2.2 版本选择:为什么是 3.4.16 而不是别的 3.4.x
3.4.x 系列从 3.4.0 一路维护到 3.4.16 之后还有零星更新,但 3.4.16 是社区里被验证得最充分的一个节点。它修复了早期版本在 GCC 9(Ubuntu 20.04 默认编译器)下的一些编译警告和错误,对 C++11 标准的支持也更完整。相比之下,3.4.10 在 GCC 9 环境下编译时,modules/core/src/system.cpp里会有几处因为std::命名空间问题导致的报错,需要手动打补丁。选 3.4.16 能省掉这些麻烦。
2.3 安装路径规划:装到 /usr/local 还是自定义目录
默认情况下,make install会把 OpenCV 装到/usr/local下,头文件进/usr/local/include,库文件进/usr/local/lib。这个路径是系统级路径,好处是编译其他工程时不用额外指定-I和-L,CMake 的find_package(OpenCV)也能直接找到。坏处是如果你以后想装第二个版本的 OpenCV,会互相覆盖,管理起来很乱。
我的建议是:如果你这台机器就是专门用来做 SLAM 实验的,装到/usr/local最省心。如果你还想同时保留系统自带的 4.x 或者以后要装别的版本,那就装到一个独立目录,比如/usr/local/opencv-3.4.16,然后在 CMake 里通过OpenCV_DIR变量指定。这篇文章按装到/usr/local来讲,因为这是十四讲读者最常走的路,也是最不容易在find_package环节出问题的路。
3. 依赖准备:这一步偷懒,后面全是坑
3.1 基础编译工具链
先把编译必需的工具装齐。Ubuntu 20.04 默认可能没装build-essential和cmake,这两个是编译任何 C++ 项目的地基。
sudo apt update sudo apt install -y build-essential cmake git pkg-configbuild-essential里包含了gcc、g++、make等核心工具,pkg-config在后续 CMake 查找依赖时会用到,别漏。git用来拉源码,虽然你也可以直接下压缩包,但 git 方式更方便切换版本。
3.2 图像与视频编解码依赖
OpenCV 要处理 jpg、png、视频流,就得靠这些库。缺了它们,编译出来的 OpenCV 可能读不了图片,或者imshow直接报错。
sudo apt install -y libjpeg-dev libpng-dev libtiff-dev sudo apt install -y libavcodec-dev libavformat-dev libswscale-dev libv4l-dev sudo apt install -y libxvidcore-dev libx264-dev这里libavcodec、libavformat、libswscale是 FFmpeg 的核心组件,负责视频编解码。libv4l-dev是视频采集相关的,如果你以后要接摄像头做实时 SLAM,这个必须有。
3.3 GUI 与可视化依赖
十四讲里不少例子会弹窗显示图像,比如imageBasics里的imshow,还有visualizeGeometry里的 3D 显示。这些依赖 GTK 和 OpenGL。
sudo apt install -y libgtk-3-dev libcanberra-gtk-module libcanberra-gtk3-module sudo apt install -y libgl1-mesa-dev libglu1-mesa-devlibcanberra-gtk-module这个包很多人会漏,漏了之后imshow弹窗时终端会刷一堆 GTK 警告,虽然不影响运行,但看着很烦。
3.4 数学与优化库依赖
SLAM 里大量用到矩阵运算和优化,OpenCV 内部会调用这些库来加速。
sudo apt install -y libatlas-base-dev gfortran sudo apt install -y libeigen3-dev libsuitesparse-devlibatlas-base-dev提供 BLAS 和 LAPACK 的优化实现,libeigen3-dev是 Eigen 库,十四讲后面讲李代数、非线性优化时会直接用到。libsuitesparse-dev是稀疏矩阵求解库,对solvePnP之类的函数有加速作用。
3.5 Python 绑定依赖(可选但推荐)
如果你打算用 Python 跑十四讲的例子,或者想用cv2做点快速验证,那 Python 绑定也得配好。
sudo apt install -y python3-dev python3-numpy python3-pip注意这里装的是python3-dev,不是python-dev。Ubuntu 20.04 默认 Python 是 3.8,装错了会导致 CMake 找不到 Python 头文件,编译出来的cv2模块导入时报ImportError。
提示:依赖这一步,宁可多装几个,不要少装。少装一个库,编译可能照样过,但运行时某个功能就是不好使,排查起来非常费劲。我自己的习惯是把上面这些一次性全装上,反正占不了多少磁盘。
4. 源码下载与 CMake 配置:参数怎么设才不翻车
4.1 拉取 3.4.16 源码
找个空间够的目录,比如~/opencv_build,然后拉源码。
mkdir -p ~/opencv_build && cd ~/opencv_build git clone -b 3.4.16 https://github.com/opencv/opencv.git git clone -b 3.4.16 https://github.com/opencv/opencv_contrib.gitopencv_contrib是扩展模块仓库,包含aruco、sfm、viz等模块。十四讲里viz模块用得不少,所以这个也一起拉下来。两个仓库的版本号必须一致,都是 3.4.16,否则 CMake 配置阶段会报版本不匹配。
如果 git 拉取速度慢,也可以去官网下opencv-3.4.16.zip和opencv_contrib-3.4.16.zip的压缩包,解压后效果一样。
4.2 创建 build 目录并进入 CMake 配置
源码目录里直接编译会污染源码树,标准做法是新建一个build目录。
cd ~/opencv_build/opencv mkdir build && cd build接下来是核心的 CMake 命令。我先把完整命令贴出来,再逐条解释每个参数。
cmake -D CMAKE_BUILD_TYPE=RELEASE \ -D CMAKE_INSTALL_PREFIX=/usr/local \ -D INSTALL_C_EXAMPLES=ON \ -D INSTALL_PYTHON_EXAMPLES=ON \ -D OPENCV_GENERATE_PKGCONFIG=ON \ -D OPENCV_EXTRA_MODULES_PATH=~/opencv_build/opencv_contrib/modules \ -D BUILD_EXAMPLES=ON \ -D WITH_VTK=ON \ -D WITH_GTK=ON \ -D WITH_OPENGL=ON \ -D WITH_TBB=ON \ -D WITH_EIGEN=ON \ -D PYTHON3_EXECUTABLE=/usr/bin/python3 \ -D PYTHON3_INCLUDE_DIR=/usr/include/python3.8 \ -D PYTHON3_NUMPY_INCLUDE_DIRS=/usr/lib/python3/dist-packages/numpy/core/include \ ..4.3 关键参数逐个拆解
CMAKE_BUILD_TYPE=RELEASE:编译类型设为 Release,会开启-O3优化,编译出来的库运行速度快很多。如果你要调试 OpenCV 内部代码,可以改成Debug,但那样库会大好几倍,运行也慢,日常用 Release 就够。
CMAKE_INSTALL_PREFIX=/usr/local:安装路径。前面说过,装这里最省心。
OPENCV_GENERATE_PKGCONFIG=ON:生成opencv.pc文件。这个文件是给pkg-config用的,有些工程(尤其是非 CMake 的 Makefile 工程)靠它来找 OpenCV 的头文件和库路径。不生成的话,pkg-config --cflags opencv会报找不到包。
OPENCV_EXTRA_MODULES_PATH:指向opencv_contrib/modules目录。注意路径要写绝对路径,写相对路径容易出错。
WITH_VTK=ON:开启 VTK 支持,viz模块依赖它。前提是你系统里装了 VTK,如果没装,这个选项开了也会在配置阶段报错。Ubuntu 20.04 可以sudo apt install libvtk7-dev来装。
WITH_TBB=ON:开启 TBB 并行加速。OpenCV 里很多算法支持多线程,开了这个在多核机器上性能提升明显。
PYTHON3_EXECUTABLE等三个参数:显式指定 Python 3 的路径。Ubuntu 20.04 上 Python 3.8 的路径就是上面写的这些,如果你用的是别的 Python 版本,路径要相应调整。这三个参数不写,CMake 有时会找到 Python 2 或者找错 numpy 路径,导致cv2编译失败。
4.4 配置阶段的常见报错与处理
CMake 配置跑完,终端会打印一份摘要,列出哪些模块会被编译、哪些依赖找到了、哪些没找到。重点看Python 3那一栏,如果Interpreter、Libraries、numpy三项都是YES,说明 Python 绑定没问题。如果哪项是NO,往上翻日志找原因,通常是路径写错了或者依赖没装。
常见的报错有:
Could NOT find PythonInterp:Python 解释器没找到,检查PYTHON3_EXECUTABLE路径。Could NOT find NumPy:numpy 没装或者路径不对,sudo apt install python3-numpy装上,再确认PYTHON3_NUMPY_INCLUDE_DIRS指向的目录里确实有numpy/arrayobject.h。VTK not found:VTK 没装,要么装上,要么把WITH_VTK改成OFF。
配置成功后,build 目录里会生成Makefile,接下来就可以编译了。
5. 编译与安装:时间、内存与并行度的权衡
5.1 make 并行编译的核数选择
编译命令很简单:
make -j4关键是-j后面的数字。这个数字是并行编译的线程数,理论上越大越快,但受两个因素制约:CPU 核心数和内存大小。
一个粗略的经验公式是:每个编译线程大约需要 1.5G 到 2G 内存。如果你机器是 8G 内存,-j4比较稳;16G 内存可以上-j8;4G 内存的机器建议-j2甚至-j1,否则很容易在编译opencv_contrib里某些大模块时被系统 OOM Killer 杀掉,报c++: fatal error: Killed signal terminated program cc1plus。
怎么查自己机器有多少核、多少内存:
nproc # 查看 CPU 核心数 free -h # 查看内存大小我自己的机器是 4 核 8G,用-j4编译,全程大概 35 分钟,中间内存峰值到过 6G 多,没出问题。如果你不确定,保守一点用-j2,慢是慢点,但稳。
5.2 编译过程中的正常输出与异常判断
make跑起来之后,终端会刷大量编译信息。正常情况下你会看到一行行[ xx%] Building CXX object ...,百分比慢慢往上走。偶尔会有一些 warning,比如unused variable、deprecated declaration,这些一般不用管,不影响结果。
真正要警惕的是error。一旦出现error:,编译会在当前文件结束后停下来。常见的错误类型有:
- 头文件找不到:
fatal error: xxx.h: No such file or directory,说明某个依赖没装全。 - 链接错误:
undefined reference to ...,通常是某个库没链接上,检查 CMake 配置里对应的WITH_xxx选项。 - 内存不足被杀:前面说的
Killed signal,降低-j数值重来。
如果编译中断了,不用从头再来。make支持增量编译,修好问题后重新执行make -j4,它会从断掉的地方继续。
5.3 安装与动态库缓存刷新
编译到 100% 后,执行安装:
sudo make install sudo ldconfigmake install把编译好的库和头文件复制到/usr/local下。ldconfig刷新动态链接器的缓存,让系统能找到新装的.so文件。这一步不做的话,运行程序时可能报error while loading shared libraries: libopencv_core.so.3.4。
装完之后可以验证一下:
pkg-config --modversion opencv如果输出3.4.16,说明opencv.pc生成正确,pkg-config 能找到它。如果报Package opencv was not found,检查/usr/local/lib/pkgconfig/opencv.pc是否存在,以及PKG_CONFIG_PATH环境变量是否包含这个目录。
6. 环境验证:怎么确认装对了、能用
6.1 用 C++ 小程序做冒烟测试
写一个最简单的 C++ 程序,读一张图片并打印尺寸,能跑通就说明 C++ 接口没问题。
// test_opencv.cpp #include <opencv2/opencv.hpp> #include <iostream> int main() { cv::Mat img = cv::imread("test.jpg", cv::IMREAD_COLOR); if (img.empty()) { std::cout << "image not found" << std::endl; return -1; } std::cout << "width: " << img.cols << ", height: " << img.rows << std::endl; std::cout << "OpenCV version: " << CV_VERSION << std::endl; return 0; }编译命令:
g++ test_opencv.cpp -o test_opencv `pkg-config --cflags --libs opencv`注意这里用的是pkg-config方式,前提是前面OPENCV_GENERATE_PKGCONFIG=ON生效了。运行./test_opencv,如果打印出图片尺寸和3.4.16,说明 C++ 环境完全就绪。
6.2 Python 绑定验证
Python 这边验证更简单:
python3 -c "import cv2; print(cv2.__version__)"输出3.4.16就对了。如果报ModuleNotFoundError: No module named 'cv2',说明 Python 绑定没编译进去,或者装到了别的 Python 版本下。可以用python3 -c "import sys; print(sys.path)"看看cv2.so所在的目录在不在搜索路径里。cv2.so通常在/usr/local/lib/python3.8/site-packages/或者/usr/local/lib/python3.8/dist-packages/下。
6.3 与十四讲工程对接的 CMake 写法
十四讲的slambook2工程用的是 CMake 构建。它的CMakeLists.txt里通常这么写:
find_package(OpenCV REQUIRED) include_directories(${OpenCV_INCLUDE_DIRS}) target_link_libraries(your_target ${OpenCV_LIBS})如果你把 OpenCV 装到了/usr/local,find_package默认就能找到,不用额外配置。如果装到了自定义目录,需要在find_package之前加一行:
set(OpenCV_DIR /your/custom/path/opencv-3.4.16/lib/cmake/opencv4)注意 3.4.x 的 CMake 配置文件目录名可能是opencv4而不是opencv3,这是 OpenCV 的一个历史遗留命名,别被名字误导。
7. 常见问题与排查技巧实录
7.1 编译期问题速查表
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
fatal error: Eigen/Core: No such file | Eigen 没装 | sudo apt install libeigen3-dev |
undefined reference to cv::imshow | 链接时没带 highgui 库 | 检查pkg-config --libs opencv输出是否含-lopencv_highgui |
Killed signal terminated program cc1plus | 内存不足 | 降低make -j数值,或加 swap |
Could NOT find PythonLibs | Python 开发头文件缺失 | sudo apt install python3-dev |
VTK not found | VTK 未安装 | sudo apt install libvtk7-dev或关闭WITH_VTK |
error: ‘CV_LOAD_IMAGE_COLOR’ was not declared | 误用了 4.x 头文件 | 确认pkg-config --modversion opencv输出 3.4.16 |
7.2 运行期问题与排查思路
问题一:程序运行时找不到libopencv_core.so.3.4
这是最典型的动态库路径问题。make install之后没跑sudo ldconfig,或者/usr/local/lib不在/etc/ld.so.conf的搜索路径里。先跑sudo ldconfig,如果还不行,检查/etc/ld.so.conf.d/下有没有包含/usr/local/lib的配置文件。没有的话,新建一个:
echo "/usr/local/lib" | sudo tee /etc/ld.so.conf.d/opencv.conf sudo ldconfig问题二:Python 里import cv2报ImportError: libopencv_core.so.3.4: cannot open shared object file
和上面是同一类问题,只是发生在 Python 环境里。除了ldconfig,还要确认cv2.so文件确实存在。可以用find /usr/local -name "cv2*.so"找一下。
问题三:imshow弹窗报 GTK 相关错误
通常是libcanberra-gtk-module没装。装上之后如果还报,检查DISPLAY环境变量是否设置。如果你是在纯命令行环境(没有图形界面)下跑,imshow本来就没法用,这不是 OpenCV 的问题。
问题四:编译十四讲代码时提示OpenCV 4.x的 API 找不到
说明 CMake 找到的是系统自带的 4.x,而不是你装的 3.4.16。在CMakeLists.txt里find_package(OpenCV REQUIRED)之后加一行message(STATUS "OpenCV version: ${OpenCV_VERSION}"),看看实际找到的是哪个版本。如果是 4.x,通过set(OpenCV_DIR ...)强制指向 3.4.16 的 CMake 配置目录。
7.3 几个我踩过的坑
第一个坑是opencv_contrib的路径写成了相对路径。CMake 配置时没报错,但编译到viz模块时提示找不到源文件。后来改成绝对路径就好了。这个坑的教训是:CMake 里涉及外部目录的路径,一律写绝对路径。
第二个坑是 Python 绑定装到了 Python 2 下。Ubuntu 20.04 虽然默认 Python 3,但系统里可能还残留 Python 2 的相关文件,CMake 有时会优先找到 Python 2。解决办法就是前面说的,显式指定PYTHON3_EXECUTABLE等三个参数。
第三个坑是编译到 90% 多的时候被 OOM 杀掉。当时用的是-j8,机器只有 8G 内存。改成-j4重来,顺利跑完。所以并行度不是越高越好,得看机器实际配置。
8. 装完之后的一些延伸建议
OpenCV 3.4.16 装好只是第一步。十四讲后面还会用到 Ceres、g2o、Sophus 这些库,它们和 OpenCV 之间也有版本兼容关系。比如某些版本的 Sophus 依赖特定版本的 Eigen,而 Eigen 又和 OpenCV 的编译选项有关联。建议在装完 OpenCV 之后,先把十四讲第 3 讲的环境配置部分过一遍,把 Eigen、Sophus、Ceres、g2o 按顺序装好,再回头跑第 4、5 讲的视觉里程计例子,这样能避免很多交叉依赖导致的奇怪报错。
另外,如果你以后要同时维护多个 OpenCV 版本的项目,可以考虑用 Docker 或者 conda 环境来隔离。Docker 里可以基于ubuntu:20.04镜像,把整套编译流程写进 Dockerfile,一次构建到处运行。conda 的话,conda install -c conda-forge opencv=3.4.16也能装,但 conda 版的 OpenCV 在 GUI 支持和 contrib 模块上可能和源码编译版有差异,SLAM 项目里还是推荐源码编译版。
最后分享一个小技巧:编译完成后,把build目录整个打包备份一份。以后如果换了机器或者重装了系统,直接解压build目录,跑sudo make install && sudo ldconfig就能快速恢复,省去重新编译的几十分钟。当然前提是新机器的系统环境和原机器基本一致。