尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Ubuntu 20.04源码编译OpenCV 3.3.1与4.2共存指南

Ubuntu 20.04源码编译OpenCV 3.3.1与4.2共存指南 简介这是一份面向Ubuntu 20.04系统的OpenCV 3.3.1完整资源包专为需要在Ubuntu 20.04上安装或编译OpenCV 3.3.1的开发者、人工智能与计算机视觉学习者准备也适合在Linux环境下进行图像处理、视频分析项目的团队参考。原始版本在新环境下存在多个编译错误该包已针对FFmpeg接口变化和Python字符串转换问题做了修改解决了CODEC_FLAG_GLOBAL_HEADER、AVFMT_RAWPICTURE未声明以及PyString_AsString引起的类型转换错误可直接用于环境搭建与二次开发经实际编译验证可顺利通过。压缩包共5630个文件约85.64MB主要包含C源文件1295个cpp、头文件562个h和542个hpp、Python脚本152个py、Java源码148个java、CUDA源文件138个cu以及大量PNG/JPG图像样本和Markdown/TXT说明文档覆盖源码、构建配置与测试数据目录结构完整。已有1780人浏览学习适合计算机视觉入门与进阶开发者对照使用节省排查编译问题的时间并快速投入实际项目开发。1. 为什么Ubuntu 20.04上非要装OpenCV 3.3.1老代码的归宿问题Ubuntu 20.04 的 apt 源里默认是 OpenCV 4.2而 3.3.1 是 2017 年底的版本俩者之间不只是小版本差距cv::xfeatures2d::SIFT的模块归属改了、findContours的函数签名动了、一大批 C 风格 API 被删干净。很多老项目——ROS Melodic 里的感知栈、工业相机厂商 SDK 附带的示例、当年毕业设计留下的图像处理管线——接口就锁死在 3.3.1 上升 4.x 等于把算法层重写一遍。这篇文章要解决的正是这个归宿问题在 Ubuntu 20.04 上把 OpenCV 3.3.1 从源码编译出来支持 C 和 Python并且和系统自带的 4.2 共存而不互相污染。读完你能拿到一套可复现的命令、一组关键参数也知道哪些地方会翻车、值不值得花这个时间。2. 编译前的准备依赖、源码与目录规划2.1 为什么不用 apt 也不用 Docker这个“资源”到底是什么先回答最直接的问题能不能apt install搞定不行。Ubuntu 20.04 官方仓库里只有 4.2根本没有 3.3.1 的安装包。有些第三方 PPA 里存着老版本 OpenCV但那些包多为 18.04 时代编译的依赖库版本对不上装上后大概率遇到libavcodec.so.57找不到之类的运行时错误。还有一条路是用 Docker 拉一个带 OpenCV 3.3.1 的镜像但在工控机、机器人、工业相机场景下容器方案往往被 USB 权限、实时内核模块、相机 SDK 的驱动耦合卡死并不比源码编译省事。所以标题里的“资源”落到实操层面指的就三样东西OpenCV 3.3.1 源码包、对应版本的 opencv_contrib 扩展模块、一套能在 Ubuntu 20.04 上稳定编译通过的配置参数。源码编译虽然慢但可控性最好——装到哪个目录、编哪些模块、要不要 Python 绑定全由你自己定。我一般把源码放~/opencv331/src编译产物放~/opencv331/build安装目录独立指定为/usr/local/opencv331这样的布局后面切换版本非常干净。2.2 下载源码版本 tag 必须严格一致OpenCV 的源码从 GitHub 拉就行核心是版本 tag 不能乱。3.3.1 这个版本号同时存在于主仓库和 opencv_contrib 仓库两者必须完全对应主仓库用 3.3.1contrib 也必须用 3.3.1混用 3.4.x 的 contrib 会在编译xfeatures2d时直接报一堆符号找不到的错误这是新手最容易踩的第一个坑。mkdir -p ~/opencv331/src cd ~/opencv331/src # 下载主仓库和 contrib 扩展仓库指定 3.3.1 tag wget -q https://github.com/opencv/opencv/archive/3.3.1.zip wget -q https://github.com/opencv/opencv_contrib/archive/3.3.1.zip unzip opencv-3.3.1.zip unzip opencv_contrib-3.3.1.zip这里我刻意用 zip 而不是git clone原因是编译阶段不需要 git 历史zip 体积小、解压快。如果你是国内服务器GitHub 下载慢找国内镜像源同步的仓库拉对应 tag 也一样注意校验一下解压出的目录名是不是opencv-3.3.1和opencv_contrib-3.3.1后面 cmake 要拿这个路径拼参数。2.3 依赖安装这一长串 apt 包分别是谁在用Ubuntu 20.04 装 OpenCV 3.3.1 的依赖有个原则够用就行别贪多。下面这组是我反复验证过的精简依赖每一项都有明确用途。sudo apt update sudo apt install -y build-essential cmake git pkg-config \ libgtk2.0-dev libjpeg-dev libpng-dev libtiff-dev \ libdc1394-22-dev python3-dev python3-numpy逐个说build-essential提供 gcc/g/makecmake是构建工具20.04 仓库里的 3.16 版本完全满足 OpenCV 3.3.1 的最低要求libgtk2.0-dev是 highgui 模块显示窗口用的这里注意要装 GTK2 而不是 GTK3OpenCV 3.3.1 对 GTK3 的支持在 20.04 上经常出问题GTK2 则非常稳libjpeg-dev、libpng-dev、libtiff-dev对应imread的三种主流图片格式插件libdc1394-22-dev是 1394 工业相机接口的依赖没有相机也可以先装上编译时少一个警告python3-dev和python3-numpy是 Python 绑定用的不需要 Python 的话这俩可以不装。有一个值得注意的取舍这组依赖里我刻意没有装libavcodec-dev、libavformat-dev、libswscale-dev这三个 FFmpeg 相关包。原因后面第 5 章会详细讲简单说就是 Ubuntu 20.04 的 FFmpeg 4.2 和 OpenCV 3.3.1 的旧接口有硬冲突装上反而编译不过。另外如果你在全新系统上遇到 gcc 安装失败先检查apt update是否执行成功、软件源是否可用换一个国内镜像源通常能解决这是 Ubuntu 安装的常规操作和 OpenCV 本身无关。2.4 目录规划与 CUDA 边界别在 20.04 上碰 3.3.1 的 GPU编译 OpenCV 不怕慢怕的是装到系统目录里和现有环境冲突。Ubuntu 20.04 的/usr/lib/x86_64-linux-gnu下已经躺着系统 OpenCV 4.2 的库文件如果CMAKE_INSTALL_PREFIX沿用默认的/usr/local虽然不会直接覆盖 4.2但后续pkg-config搜索时会同时看到两套 OpenCV环境变量一乱就分不清谁是谁了。所以我把安装目录明确指定为/usr/local/opencv331prefix 带版本号是同类共存场景下的通用做法。另一个必须提前说明的边界是 CUDA。OpenCV 3.3.1 诞生时期配套的是 CUDA 9 和对应的老显卡驱动而 Ubuntu 20.04 的官方驱动栈和 GCC 9 工具链跟 CUDA 9 完全不匹配强行编译只会得到一堆unsupported GNU version的报错。我的建议很直接20.04 上的 OpenCV 3.3.1 老老实实做 CPU 编译需要 GPU 加速就把系统换回 Ubuntu 18.04或者用带 CUDA 镜像的容器。CPU 版对绝大多数图像处理项目已经够用别在这里消耗时间。3. 从 CMake 到 makeOpenCV 3.3.1 编译命令与参数3.1 cmake 配置把 12 个关键参数逐个说清楚编译 OpenCV 的核心是 cmake 阶段这一步决定了你会得到什么样的库。很多人直接抄网上的参数然后抱怨编译失败多半是不理解每个开关在干什么。下面这组是我在 Ubuntu 20.04 上跑通 3.3.1 的完整配置目录结构按第 2 章的规划来。cd ~/opencv331 mkdir -p build cd build cmake -DCMAKE_BUILD_TYPERELEASE \ -DCMAKE_INSTALL_PREFIX/usr/local/opencv331 \ -DBUILD_SHARED_LIBSON \ -DBUILD_TESTSOFF \ -DBUILD_EXAMPLESOFF \ -DBUILD_opencv_python3ON \ -DBUILD_opencv_python2OFF \ -DPYTHON3_EXECUTABLE$(which python3) \ -DPYTHON3_INCLUDE_DIR$(python3 -c import sysconfig; print(sysconfig.get_path(include))) \ -DPYTHON3_PACKAGES_PATH$(python3 -c import sysconfig; print(sysconfig.get_path(purelib))) \ -DWITH_FFMPEGOFF \ -DWITH_GTKON \ -DWITH_V4LON \ -DWITH_OPENCLOFF \ -DOPENCV_EXTRA_MODULES_PATH../opencv_contrib-3.3.1/modules \ ..CMAKE_BUILD_TYPERELEASE决定编译优化级别Release 模式下的运行速度比 Debug 快一个量级这是唯一合理的选择。CMAKE_INSTALL_PREFIX指向/usr/local/opencv331贯彻了第 2 章的隔离思路后面切版本全靠这个前缀。BUILD_SHARED_LIBSON生成动态库.so不要改成 OFF静态库在链接时跟系统 4.2 的符号冲突会非常难排查。BUILD_TESTS和BUILD_EXAMPLES关掉能省下大量编译时间这两项对生产环境没有任何价值。Python 相关的四个参数值得单独看。PYTHON3_EXECUTABLE指定解释器路径PYTHON3_INCLUDE_DIR和PYTHON3_PACKAGES_PATH用sysconfig动态读取不要手写死/usr/include/python3.8因为虚拟环境和 conda 环境下的路径结构不一样动态读取能让 cmake 的 Python 检测更可靠。这里要提醒一句cmake 输出里有一行Python 3: ...的检测结果编译前扫一眼确认它找到了你要的 Python 版本别让这个黑匣子悄悄给你编出个 Python2 绑定来。WITH_FFMPEGOFF是这一版配置里最关键的开关它绕开了 Ubuntu 20.04 的 FFmpeg 4.2 与 3.3.1 的接口冲突。WITH_GTKON配合libgtk2.0-dev提供窗口显示。WITH_V4LON是 Video4Linux 的摄像头接口USB 摄像头项目需要。WITH_OPENCLOFF是刻意的——20.04 上 OpenCL 运行时经常出幺蛾子关了它反而少一个不稳定因素。最后的OPENCV_EXTRA_MODULES_PATH指向 contrib 的 modules 目录SIFT、SURF 这些扩展算法都在里面。3.2 编译用 -j4 而不是 -j8cmake 顺利结束后看输出的摘要重点确认三行FFmpeg: NO说明 FFmpeg 模块确实被关了GTK: YES (GTK2)说明窗口模块正常Python 3: YES说明绑定要编。然后开始 make。make -j4-j4是血的教训。现代机器动辄 8 核 16 核很多人习惯make -j8甚至make -j$(nproc)结果编译到opencv_core或者opencv_world时内存被吃满进程被内核直接Killed前功尽弃。OpenCV 3.3.1 的每个模块都是巨大的编译单元GCC 9 的内存占用比老版本更凶-j4虽然慢一些但稳定得多。4 核机器大约需要四十分钟到一个小时8 核机器用-j4也在二十分钟左右这个时间成本是值得的。编译期间随时用free -h看一眼内存余量心里有个底。如果没有报错make 结束后进行最后一步安装。sudo make install安装过程其实只是把编译产物拷贝到/usr/local/opencv331下很快。装完后进入验证环节先确认库文件真实存在。export PKG_CONFIG_PATH/usr/local/opencv331/lib/pkgconfig ls /usr/local/opencv331/lib/libopencv_core.so* pkg-config --modversion opencvpkg-config --modversion opencv如果输出3.3.1说明安装位置和 pkg-config 搜索路径都对上了。这里注意export PKG_CONFIG_PATH只在当前终端生效新开终端要重新设置这也是后面多版本共存的入口。走到这一步C 库已经可以用了Python 绑定是否就绪是下一章的事。3.3 验证安装目录结构理解 OpenCV 的部署形态安装完成后/usr/local/opencv331下的目录结构大致是这样lib/下一堆libopencv_*.so动态库lib/pkgconfig/里是opencv.pc配置文件include/opencv2/是头文件lib/cmake/opencv/里是供 CMake 的find_package使用的配置文件。记住这个结构后面 CMake 里设置OpenCV_DIR时就要指到lib/cmake/opencv这一层指错一级就会找不到包。4. Python 绑定 cv2让 Python 3.8 和 numpy 认账4.1 自己编的 cv2 和 pip 装的 cv2 是两回事很多人在这一步困惑明明编译成功了python3 -c import cv2却报ModuleNotFoundError或者 import 出来是系统自带的 4.2。根因是你没搞清楚 Python 绑定的加载机制。源码编译产生的cv2*.so是一个原生扩展模块它会被安装到 cmake 指定的PYTHON3_PACKAGES_PATH里跟pip install opencv-python装到site-packages的那份完全不同——它们只是同名而已。pip 的 opencv-python 从 3.4.x 开始就是预编译 wheel官方并不提供 3.3.1 的 Python 3.8 wheel所以你想在 Python 3.8 里用 3.3.1老老实实自己编就是唯一路径。4.2 给 cmake 的 Python 参数补全虚拟环境场景下的注意事项如果你的编译环境是 conda 或者 venv第 3 章的PYTHON3_*动态读取方式依然有效但有两个额外的坑。第一个是 venv 下PYTHON3_EXECUTABLE会指向虚拟环境里的 python而PYTHON3_INCLUDE_DIR指向的通常是系统的/usr/include/python3.8两者版本必须匹配否则 cmake 会报 Python 版本不一致。第二个是PYTHON3_LIBRARYcmake 有时候找不到这个变量导致 Python 绑定静默关闭最好显式补上。cmake -DPYTHON3_LIBRARY$(python3-config --prefix)/lib/libpython3.8.so \ -DBUILD_opencv_python3ON \ -DBUILD_opencv_python2OFF \ # 其余参数同第 3 章python3-config --prefix能正确输出当前 Python 安装根目录比手写/usr/lib/x86_64-linux-gnu可靠。补上这个参数后重新跑 cmake再检查输出里的Python 3: YES。注意修改 cmake 参数后要重新执行 cmake 再 make直接 make 不会重新解析新增参数这是新手常见的无效操作。4.3 import 验证和 numpy 版本锁一个价值千金的参数编译安装完成后进入验证环节。先确认模块文件真实存在于 python 搜索路径里。python3 -c import sys; print(sys.path) ls $(python3 -c import sysconfig; print(sysconfig.get_path(purelib))) | grep cv2如果能看到cv2.cpython-38-x86_64-linux-gnu.so类似的文件说明安装位置正确。然后 import 测试python3 -c import cv2; print(cv2.__version__, cv2.__file__)输出3.3.1加路径就成功了。但如果你在 import 之后调用cv2.imread时遇到ValueError: numpy.ndarray size changed, may indicate binary incompatibility恭喜你踩到了经典的 numpy 版本坑。原因是 OpenCV 3.3.1 的 Python 绑定是 2017 年用老 numpy C API 编译的而 numpy 1.20 之后改了数组对象的内部布局老扩展模块直接崩。解决办法很简单把 numpy 降到 1.19.x。pip install numpy1.19.5numpy1.19.5是 Python 3.8 下最后一个兼容老 OpenCV 绑定的版本。如果你的项目里其他库要求新 numpy那这条老代码之路就走到头了要么用 conda 建一个 Python 3.6 的环境跑 3.3.1要么升级代码去适配 OpenCV 4.x。这两条路选哪条取决于你手里的老代码有多少。4.4 如果项目只用 C关掉 Python 绑定可以省一大半时间如果你的项目是纯 C 工程Python 绑定只是最后的调参辅助那完全可以把BUILD_opencv_python3设为 OFF。这样可以少编译modules/python那一大坨时间省下三分之一也彻底绕开 numpy、Python 版本这些破事。很多工业项目就是这么干的OpenCV 本体用 C 接口Python 只是事后用opencv-python做数据分析。5. 避坑清单OpenCV 3.3.1 在 20.04 上的 4 个翻车点5.1 FFmpeg 4.2 报错AVStream::codec is private现象cmake 保持WITH_FFMPEGON并且装了libavcodec-devmake 进行到opencv_videoio模块时编译直接死在cap_ffmpeg.cpp报错信息类似error: AVStream::codec is private within this context。原因Ubuntu 20.04 的 FFmpeg 是 4.2这个版本把AVStream结构体里的codec成员改成私有属性了。OpenCV 3.3.1 的cap_ffmpeg.cpp写于 FFmpeg 3.x 时代仍然直接访问这个成员接口对不上编译必然失败。这不是配置问题是代码和库的版本代沟。解决最省事的是第 3 章的做法-DWITH_FFMPEGOFF绕开整个 FFmpeg 依赖。代价是VideoCapture读不了 mp4/avi 等封装格式对做摄像头、工业相机、本地图像序列的项目毫无影响。如果你的项目必须要读视频文件两条路一是给cap_ffmpeg.cpp打补丁适配 FFmpeg 4.x 新接口改动量不小二是系统里单独编译一份旧版 FFmpeg 3.x 放进局部目录让 cmake 去那里找。我的经验是90% 的老项目不需要挣扎这条路直接关掉 FFmpeg 反而最干净。5.2 改了 cmake 参数不生效build 目录缓存“中毒”现象你在第 3 章的 cmake 命令里加了-DWITH_FFMPEGOFF或者改PYTHON3_LIBRARY重新执行 cmake 后输出摘要里还是旧配置怎么改都不变。原因CMake 会把第一次配置的变量缓存在build/CMakeCache.txt里新的-D参数在已有缓存时不会全部覆盖旧值部分参数直接失效。这个“缓存中毒”问题在 OpenCV 这种多参数项目里特别明显。解决最稳妥的方案是把 build 目录整个删掉重来。cd ~/opencv331 rm -rf build mkdir build cd build # 重新执行完整的 cmake 命令删缓存虽然慢一点但能保证每次都是从干净状态开始。我见过太多人对着一个中毒的 build 目录折腾一下午最后删掉重编十分钟就好了。这是 OpenCV 编译里最容易被低估的效率杀手。5.3 import cv2 版本不对或直接 ModuleNotFoundError现象import cv2之后cv2.__version__显示4.2.0或者直接报ModuleNotFoundError: No module named cv2。前者是 import 到了系统 OpenCV后者是 Python 搜索路径里根本没有你编译的模块。原因系统里同时存在多份 OpenCV——apt 的python3-opencv、pip 的opencv-python、你源码编译的 3.3.1。Python 的sys.path按顺序搜索模块谁排前面就先 import 谁。ModuleNotFoundError则往往是你编译时指定的PYTHON3_PACKAGES_PATH和当前 python 的purelib路径不一致。解决把干扰源清除掉然后看cv2.__file__确认加载的是哪份。pip uninstall opencv-python opencv-contrib-python sudo apt remove python3-opencv python3 -c import cv2; print(cv2.__version__, cv2.__file__)如果输出3.3.1且路径指向purelib目录就彻底干净了。这里要强调不要依赖PYTHONPATH环境变量去强行覆盖因为sys.path里用户目录和 apt 包的优先级问题会反复咬人卸载冲突源才是治根。5.4 make 被 OOM Kill 或磁盘写满现象make 跑了一半终端显示Killed查看dmesg里有Out of memory记录或者编译某个大模块时No space left on device。原因OpenCV 3.3.1 的每个模块都是大编译单元GCC9 并行编译时内存峰值很高-j8在 16G 内存的机器上也可能被压垮。另外编译过程中间文件体积巨大/tmp分区太小也会触发磁盘错误。解决降并行度到-j2或-j4查剩余空间后清理。free -h df -h / make -j2内存实在不够就临时加 swap注意用完可以撤掉。sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile-j2慢是慢但至少能编完。这条经验尤其重要不要在编译 OpenCV 的同时开着 Chrome 和一堆容器你会后悔的。6. 多版本共存与结果验证让项目跑在老 API 上6.1 用 HoughLines 快速验证 3.3.1 的 C 接口编译安装完成验证的最好方式就是跑一段真实算法。我用 HoughLines 直线检测来验证因为它跨版本接口变化不大而且能直观确认链接的是 3.3.1 而不是 4.2。// hough_test.cpp #include opencv2/opencv.hpp #include iostream int main() { cv::Mat src cv::imread(test.png, cv::IMREAD_GRAYSCALE); if (src.empty()) { std::cerr image not found std::endl; return -1; } cv::Mat edges; cv::Canny(src, edges, 50, 150); std::vectorcv::Vec4i lines; cv::HoughLinesP(edges, lines, 1, CV_PI / 180, 50, 30, 10); cv::Mat color; cv::cvtColor(src, color, cv::COLOR_GRAY2BGR); for (auto l : lines) { cv::line(color, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(0, 0, 255), 2); } cv::imwrite(out.png, color); std::cout lines: lines.size() std::endl; return 0; }这段代码里的CV_PI是 OpenCV 3.x 年代的常量名4.x 里改成了CV_PI仍兼容但会有一堆弃用警告。编译时用 pkg-config 指向我们的独立安装目录。g hough_test.cpp -o hough_test pkg-config --cflags --libs opencv LD_LIBRARY_PATH/usr/local/opencv331/lib ./hough_test如果lines数量符合预期且编译过程没有大量关于 API 弃用的警告说明环境就位了。这一步验证的是编译链路的完整性比单纯pkg-config --modversion更有说服力。6.2 CMake 工程里锁定 OpenCV 版本一个参数避免找错包写 CMakeLists.txt 时最怕find_package(OpenCV)找到系统 4.2。通过显式指定OpenCV_DIR完全可以锁定 3.3.1。cmake_minimum_required(VERSION 3.10) project(opencv331_demo) find_package(OpenCV 3.3 REQUIRED) include_directories(${OpenCV_INCLUDE_DIRS}) add_executable(demo demo.cpp) target_link_libraries(demo ${OpenCV_LIBS})配置时多传一个OpenCV_DIR参数即可。cmake -DOpenCV_DIR/usr/local/opencv331/lib/cmake/opencv ..OpenCV_DIR指向lib/cmake/opencv这一层不是 lib 根目录也不是 include 目录。这里写错一级find_package就会搜索失败或者落到系统版本。这个参数是我每次见到“我明明编译了 3.3.1 为什么还是链接到 4.2”类问题时第一个检查的地方值得记牢。6.3 环境切换的小习惯把版本隔离做在 shell 函数里日常使用中我习惯在~/.bashrc里放一个切换函数而不是把环境变量写死。use_opencv331() { export PKG_CONFIG_PATH/usr/local/opencv331/lib/pkgconfig export LD_LIBRARY_PATH/usr/local/opencv331/lib${LD_LIBRARY_PATH::$LD_LIBRARY_PATH} }需要旧版环境时执行use_opencv331新开终端默认回到系统 4.2。这里有个反直觉的注意点不要把这个路径写进/etc/ld.so.conf.d/因为全局动态库搜索路径会优先于一切系统里所有依赖 OpenCV 的程序都会被迫加载 3.3.1包括一些不该被影响的应用。我一开始图省事加过一次后来发现连ffmpeg命令行工具都被拖累血泪经验。版本隔离要做在项目级、终端级不要做在系统级。希望这个习惯能帮你在 Ubuntu 20.04 上安稳地跑老代码。本文还有配套的精品资源点击获取
返回列表