Ubuntu 20.04源码编译OpenCV 3.4.16:SLAM十四讲环境搭建与避坑指南
1. 为什么在SLAM十四讲的环境里OpenCV 3.4.16值得单独折腾如果你正在跟着《视觉SLAM十四讲》做练习大概率会遇到一个很具体的问题书上的代码依赖OpenCV 3.x而你手头是Ubuntu 20.04系统默认源里能装到的OpenCV版本要么太新4.x要么装上了但编译书里的例程时各种报错。这不是你代码写错了而是版本匹配的问题。OpenCV 3.4.16是3.4系列的最后一个维护版本发布于2020年底正好卡在Ubuntu 20.04 LTS的生命周期内。它既保留了3.x时代稳定的API接口比如cv::solvePnP、cv::triangulatePoints这些SLAM里高频使用的函数又修复了早期3.4.x版本中一些与C11/14标准库兼容性的问题。对于SLAM十四讲这种大量使用特征点匹配、PnP求解、三角化、光流跟踪的教程来说3.4.16是一个“不折腾”的选择。但问题在于Ubuntu 20.04的默认仓库里没有这个版本。你apt install libopencv-dev装到的是4.2.x而4.x版本移除了一些3.x的旧接口比如CV_LOAD_IMAGE_UNCHANGED这类宏定义被改了名字cv::xfeatures2d模块的编译选项也变了。直接换4.x去跑书上的代码轻则编译报错重则运行时段错误排查起来非常消耗时间。所以这篇内容的核心就是在Ubuntu 20.04上从源码编译安装OpenCV 3.4.16并且让它和SLAM十四讲的环境Eigen、Sophus、Ceres、g2o和平共处。我会把整个流程拆开包括依赖安装、CMake参数选择、编译踩坑、环境变量配置以及装完之后怎么验证、怎么和ROS共存如果你后续要跑ORB-SLAM2之类的项目。适合已经装好Ubuntu 20.04、准备开始SLAM十四讲实践、但被OpenCV版本卡住的读者。2. 安装前的整体思路与版本选型逻辑2.1 为什么不直接用apt装OpenCVUbuntu 20.04的libopencv-dev对应的是OpenCV 4.2.0。这个版本本身很稳定但和SLAM十四讲第二版书上的代码存在几个硬性冲突。最典型的是cv::FileStorage读取cv::Mat时的类型检查更严格书里有些示例用直接读矩阵在4.x下会抛异常。另外cv::viz模块在4.x里对VTK的依赖版本要求更高而SLAM十四讲第13章的可视化部分用的是3.x的写法。还有一个容易被忽略的点如果你后续要编译ORB-SLAM2或者VINS-Mono这类开源SLAM系统它们中的大多数在2020年前后锁定的就是OpenCV 3.4.x。用4.x去编译CMakeLists里的find_package(OpenCV 3.4 REQUIRED)直接找不到改版本号又可能遇到API不兼容。所以从源码装3.4.16本质上是在为整个SLAM学习链路铺一条兼容性更好的路。2.2 源码编译 vs 预编译包有人会想去找现成的.deb包或者第三方PPA。我的建议是对于OpenCV 3.4.16源码编译虽然慢取决于机器性能大概20到40分钟但可控性最强。你可以精确控制哪些模块编译、哪些不编译比如xfeatures2dSLAM十四讲里SIFT/SURF相关章节会用到、viz可视化、cuda如果你有NVIDIA显卡且想加速。预编译包往往把这些模块拆得七零八落装完还得补。另外源码编译可以指定安装路径。我习惯把OpenCV 3.4.16装到/usr/local/opencv3.4.16而不是默认的/usr/local。这样做的好处是系统里可以同时存在多个OpenCV版本通过CMake的OpenCV_DIR变量切换不会和apt装的4.x打架。后面我会详细说这个路径怎么配。2.3 依赖项的选择与避坑编译OpenCV 3.4.16需要一堆依赖。Ubuntu 20.04的软件源里大部分都有但有几个版本敏感项需要注意CMake要求3.5以上Ubuntu 20.04自带3.16够用。GCCUbuntu 20.04默认是GCC 9OpenCV 3.4.16对GCC 9的兼容性还行但编译时会有一些警告不影响。Python如果你要用Python接口注意OpenCV 3.4.16不支持Python 3.9以上的版本Ubuntu 20.04默认是3.8正好。但如果你自己升级过Python需要额外指定PYTHON3_EXECUTABLE。VTKviz模块依赖VTK。Ubuntu 20.04源里的VTK是7.1OpenCV 3.4.16对VTK 7的支持是OK的但如果你装了VTK 9编译会报错。所以不要随便升级VTK。EigenSLAM十四讲本身就要装Eigen 3.3以上OpenCV编译时也会用到。建议先装Eigen再装OpenCV。还有一个隐藏坑libjasper-dev在Ubuntu 20.04的默认源里被移除了但OpenCV 3.4.16的imgcodecs模块默认会找它。解决办法是要么加一个旧源不推荐要么在CMake里关掉Jasper支持-DWITH_JASPEROFF。JPEG 2000格式在SLAM里基本用不到关掉完全没影响。3. 从零开始的完整编译安装实操3.1 依赖安装一条命令搞定大部分先把基础依赖装齐。打开终端执行sudo apt update sudo apt install -y build-essential cmake git pkg-config \ libgtk-3-dev libavcodec-dev libavformat-dev libswscale-dev \ libv4l-dev libxvidcore-dev libx264-dev libjpeg-dev libpng-dev \ libtiff-dev gfortran openexr libatlas-base-dev python3-dev \ python3-numpy libtbb2 libtbb-dev libdc1394-22-dev这些包覆盖了图像编解码、视频流、并行计算TBB、Python绑定等核心需求。其中libdc1394-22-dev是用于工业相机的SLAM里如果接双目相机可能会用到。接下来单独处理几个容易出问题的sudo apt install -y libgstreamer1.0-dev libgstreamer-plugins-base1.0-devGStreamer用于视频流处理ORB-SLAM2里读取视频文件时会依赖。然后装Eigen如果还没装sudo apt install -y libeigen3-devUbuntu 20.04源里的Eigen是3.3.7满足SLAM十四讲的要求。注意不要装libopencv-dev否则后面CMake找OpenCV时会优先找到4.x版本导致混乱。如果你已经装了可以暂时不管后面通过指定路径来覆盖。3.2 下载OpenCV 3.4.16源码从官方仓库拉取指定版本的源码。这里用git clone加checkout的方式比下载zip包更可靠因为可以随时切换分支。cd ~ git clone https://github.com/opencv/opencv.git cd opencv git checkout 3.4.16如果你还需要contrib模块SLAM十四讲里SIFT、SURF、ORB的扩展实现都在这里继续cd ~ git clone https://github.com/opencv/opencv_contrib.git cd opencv_contrib git checkout 3.4.16提示opencv_contrib的版本必须和主仓库完全一致否则CMake配置阶段会报错。3.4.16对应的是3.4.16标签不要用3.4分支因为分支会随时间更新。3.3 CMake配置参数逐个拆解在opencv目录下创建build文件夹然后运行cmake。这一步是整个安装过程中最关键的参数选错了后面编译会出各种幺蛾子。cd ~/opencv mkdir build cd build下面是我实测稳定的一套CMake参数cmake -D CMAKE_BUILD_TYPERELEASE \ -D CMAKE_INSTALL_PREFIX/usr/local/opencv3.4.16 \ -D INSTALL_C_EXAMPLESOFF \ -D INSTALL_PYTHON_EXAMPLESOFF \ -D OPENCV_GENERATE_PKGCONFIGON \ -D OPENCV_EXTRA_MODULES_PATH~/opencv_contrib/modules \ -D WITH_CUDAOFF \ -D WITH_VTKON \ -D WITH_JASPEROFF \ -D WITH_OPENCLON \ -D WITH_TBBON \ -D WITH_EIGENON \ -D BUILD_EXAMPLESOFF \ -D BUILD_opencv_python3ON \ -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 \ -D PYTHON3_PACKAGES_PATH/usr/lib/python3/dist-packages \ ..逐个解释这些参数背后的考量CMAKE_INSTALL_PREFIX/usr/local/opencv3.4.16自定义安装路径避免覆盖系统默认的OpenCV。后面通过OpenCV_DIR来指定这个路径。OPENCV_GENERATE_PKGCONFIGON生成opencv3.pc文件方便用pkg-config查询编译参数。Ubuntu 20.04下这个选项默认是OFF必须手动打开。OPENCV_EXTRA_MODULES_PATH指向opencv_contrib的modules目录这样才会编译xfeatures2d等扩展模块。WITH_CUDAOFF如果你的机器没有NVIDIA显卡或者你不想折腾CUDA关掉。SLAM十四讲的前端部分用CPU足够。WITH_VTKON开启viz模块第13章的可视化需要。WITH_JASPEROFF前面说的避开libjasper-dev缺失的问题。WITH_OPENCLONOpenCL可以在部分图像处理操作上加速即使没有独显Intel核显也支持。PYTHON3_EXECUTABLE等明确指定Python 3.8的路径避免CMake找到其他版本的Python。注意如果你的opencv_contrib不在~/opencv_contrib把OPENCV_EXTRA_MODULES_PATH改成实际路径。另外PYTHON3_NUMPY_INCLUDE_DIRS的路径可能因系统而异可以用python3 -c import numpy; print(numpy.get_include())来确认。3.4 编译与安装时间和线程数的权衡CMake配置成功后会输出一个摘要重点看OpenCV modules里有没有xfeatures2d、viz、python3。确认无误后开始编译make -j$(nproc)$(nproc)会自动获取CPU核心数。比如8核机器就是make -j8。但这里有个经验如果你的内存小于8GB-j8可能会导致编译过程中内存不足而卡死。我试过在4GB内存的虚拟机上用-j4编译到opencv_contrib的xfeatures2d模块时直接OOM。稳妥的做法是内存8GB以上make -j$(nproc)内存4-8GBmake -j4内存4GB以下make -j2虽然慢但不会崩编译完成后安装sudo make install sudo ldconfigldconfig刷新动态链接库缓存让系统能找到新装的OpenCV库。3.5 环境变量配置让CMake和pkg-config找到它装完之后系统里其实有两个OpenCVapt装的4.x在/usr新装的3.4.16在/usr/local/opencv3.4.16。如果不做配置CMake默认会找到4.x。有两种方式指定方式一在CMakeLists.txt里写死路径set(OpenCV_DIR /usr/local/opencv3.4.16/lib/cmake/opencv4) find_package(OpenCV 3.4 REQUIRED)注意路径里的opencv4是CMake配置文件的目录名虽然装的是3.4.16但OpenCV 3.4的CMake配置文件夹就叫opencv4这是历史遗留不用改。方式二通过环境变量全局指定在~/.bashrc里加export OpenCV_DIR/usr/local/opencv3.4.16/lib/cmake/opencv4 export LD_LIBRARY_PATH/usr/local/opencv3.4.16/lib:$LD_LIBRARY_PATH export PKG_CONFIG_PATH/usr/local/opencv3.4.16/lib/pkgconfig:$PKG_CONFIG_PATH然后source ~/.bashrc。这样用pkg-config --modversion opencv查到的就是3.4.16。提示LD_LIBRARY_PATH这种方式在运行程序时生效但如果你用sudo运行环境变量可能不继承。更彻底的方式是在/etc/ld.so.conf.d/下新建一个opencv3.4.16.conf写入/usr/local/opencv3.4.16/lib然后sudo ldconfig。4. 验证安装与SLAM十四讲环境联调4.1 快速验证OpenCV版本和模块写一个最小的测试程序#include opencv2/opencv.hpp #include iostream int main() { std::cout OpenCV version: CV_VERSION std::endl; cv::Mat img cv::imread(test.jpg); if (img.empty()) { std::cout Image not found, but OpenCV is working. std::endl; } cv::Ptrcv::Feature2D sift cv::xfeatures2d::SIFT::create(); std::cout SIFT created successfully. std::endl; return 0; }对应的CMakeLists.txtcmake_minimum_required(VERSION 3.5) project(test_opencv) set(OpenCV_DIR /usr/local/opencv3.4.16/lib/cmake/opencv4) find_package(OpenCV 3.4 REQUIRED) include_directories(${OpenCV_INCLUDE_DIRS}) add_executable(test_opencv main.cpp) target_link_libraries(test_opencv ${OpenCV_LIBS})编译运行mkdir build cd build cmake .. make ./test_opencv如果输出OpenCV version: 3.4.16并且SIFT创建成功说明主模块和contrib模块都装好了。4.2 和Sophus、Ceres、g2o的兼容性检查SLAM十四讲的环境里Sophus依赖EigenCeres依赖Eigen和glogg2o依赖Eigen和OpenCV可选。OpenCV 3.4.16和这些库的兼容性总体很好但有一个点要注意Ceres如果编译时链接了OpenCV 4.x而你的SLAM程序链接了3.4.16可能会出现符号冲突。解决办法是统一用3.4.16或者在Ceres的CMake配置里关掉OpenCV支持-DUSE_OPENCVOFF因为SLAM十四讲里Ceres主要用于后端优化不直接依赖OpenCV。g2o的编译如果开启了G2O_USE_OPENCV它会去找系统里的OpenCV。这时候通过OpenCV_DIR指定3.4.16即可。4.3 多版本共存时的切换技巧如果你同时有OpenCV 4.xapt装的和3.4.16源码装的在不同项目间切换时推荐用CMake的find_package指定版本find_package(OpenCV 3.4 EXACT REQUIRED)EXACT关键字强制要求精确匹配3.4.x这样即使系统里有4.xCMake也会优先找3.4.16。但前提是OpenCV_DIR指向了3.4.16的配置目录。另一种方式是用update-alternatives管理但OpenCV的库文件和头文件太多配置起来繁琐不如直接改OpenCV_DIR来得快。5. 常见编译错误与排查速查5.1 CMake阶段报错错误1Could NOT find Jasper这是最常见的。即使加了-DWITH_JASPEROFF有时候CMake还是会去找。解决办法是在CMake参数里再加一个-DCMAKE_DISABLE_FIND_PACKAGE_JasperTRUE彻底禁用。错误2No package gtk-3.0 found说明libgtk-3-dev没装。执行sudo apt install libgtk-3-dev。如果已经装了还报错检查pkg-config --modversion gtk-3.0是否能输出。错误3Python相关路径找不到如果你不用Python接口直接在CMake里加-DBUILD_opencv_python3OFF省去麻烦。如果要用确认PYTHON3_EXECUTABLE指向的是/usr/bin/python3而不是conda环境里的Python。conda的Python路径和系统Python混用会导致numpy头文件找不到。5.2 编译阶段报错错误4internal compiler error: Killed (program cc1plus)这是内存不足。降低make -j的并行数或者临时增加swap空间。我试过在4GB内存的机器上编译用make -j2加上2GB swap顺利通过。错误5xfeatures2d模块编译失败通常是因为opencv_contrib的版本和主仓库不匹配。确认两个仓库都checkout 3.4.16。如果还是失败检查opencv_contrib/modules/xfeatures2d/CMakeLists.txt里是否有对OpenCV版本的硬性检查。错误6viz模块报VTK相关错误Ubuntu 20.04的VTK 7.1和OpenCV 3.4.16的viz模块兼容性还行但如果报vtkRenderingOpenGL找不到安装libvtk7-dev和libvtk7-qt-dev。如果还是不行直接-DWITH_VTKOFF放弃viz模块SLAM十四讲的可视化部分可以用Pangolin替代。5.3 运行阶段报错错误7error while loading shared libraries: libopencv_core.so.3.4说明ldconfig没生效或者LD_LIBRARY_PATH没配。执行sudo ldconfig然后确认/usr/local/opencv3.4.16/lib在/etc/ld.so.conf.d/的配置里。错误8程序运行时崩溃提示cv::xfeatures2d::SIFT::create()未定义说明xfeatures2d模块没编译进去。检查CMake配置摘要里是否有xfeatures2d如果没有确认OPENCV_EXTRA_MODULES_PATH路径正确并且opencv_contrib已经checkout到3.4.16。错误现象可能原因解决方法CMake找不到Jasper系统缺少libjasper-dev加-DWITH_JASPEROFF -DCMAKE_DISABLE_FIND_PACKAGE_JasperTRUE编译时OOM内存不足降低make -j并行数或增加swapSIFT不可用contrib模块未编译检查OPENCV_EXTRA_MODULES_PATH和contrib版本运行时找不到库ldconfig未刷新sudo ldconfig配置/etc/ld.so.conf.d/Python绑定失败Python路径混乱指定PYTHON3_EXECUTABLE或关闭Python编译提示每次修改CMake参数后建议删除build文件夹重新来避免缓存干扰。rm -rf build mkdir build cd build。6. 装完之后的一些经验之谈编译OpenCV 3.4.16这件事说难不难但细节特别多。我第一次装的时候因为没关Jasper卡了半个小时第二次因为opencv_contrib版本对不上xfeatures2d编译失败第三次因为内存不够编译到一半进程被kill。后来总结下来最稳的流程就是先装齐依赖确认Eigen和VTK版本然后CMake参数一次到位编译时根据内存调整并行数。还有一个点如果你后续要跑ORB-SLAM2它除了OpenCV还依赖Pangolin和DBoW2。Pangolin的安装相对独立但DBoW2的CMakeLists里会find_package(OpenCV 3.4 REQUIRED)这时候你的OpenCV_DIR配置就派上用场了。我试过用apt装的OpenCV 4.2去编译ORB-SLAM2改了一堆代码才通过后来换成3.4.16原版代码直接编译成功省了很多事。最后分享一个小技巧编译完成后可以把/usr/local/opencv3.4.16整个目录打包备份。以后如果在其他机器上部署直接解压到相同路径配好环境变量就能用省去重复编译的时间。这个目录大概1.5GB左右压缩后500MB不到放在移动硬盘里很方便。