
简介面向Ubuntu 20.04用户的OpenCV 3.3.1编译资源包专为需要在较新系统上复现旧版OpenCV的开发者与计算机视觉学习者准备。该资源针对OpenCV 3.3.1与新版FFmpeg接口不匹配导致的三个典型编译错误CODEC_FLAG_GLOBAL_HEADER未声明、AVFMT_RAWPICTURE未声明、PyString_AsString类型转换失败做了修复适配Ubuntu 20.04默认工具链拿到后可直接进行源码编译与二次开发。整个zip压缩包共5630个文件除cpp源码与h/hpp头文件外还包含大量png/jpg图像资源、构建脚本、markdown说明文档以及java、python接口示例文件压缩后约85.64MB目录结构完整便于按模块检索与定制。目前已有1780人学习下载。打包内容既涵盖核心库与contrib扩展模块也提供测试用例和demo工程适合深入理解图像处理、特征提取、视频分析等API的调用方式其中针对FFmpeg兼容性问题的修复思路对排查Linux下音视频接口编译报错同样具有参考价值。1. 为什么是 opencv-3.3.1Ubuntu 20.04 编译老版本的第一道坎如果你维护过 2017 年前后的人工智能项目大概率经历过这种场景代码栈固定在 linux 环境下里面写着 cv2.xfeatures2d.SIFT_create()依赖的还是 OpenCV 3.3.1 的模块结构而 Ubuntu 20.04 的 apt 仓库里只有 OpenCV 4.2pip 上的 opencv-python 也都是 4.x把老代码直接跑起来全是接口报错。唯一的出路就是源码编译但 20.04 默认的 GCC 9.3、FFmpeg 4.2、Python 3.8和当年围绕 GCC 5、FFmpeg 3.x、Python 2.7 构建的 3.3.1 之间刚好存在三类编译断裂。这份资源不是干净的官方源码而是已经处理完 videoio 与 python 模块这三类错误的修改版解压后可以直接进入 CMake 阶段。适合被迫维护老模型的算法工程师也适合想把老代码当参考、却不想在编译环境上耗一天的新手。2. 编译前准备依赖库、Python 版本与工具链检查OpenCV 3.3.1 的官方源码默认假设编译环境是 2017 年的主流配置。Ubuntu 20.04 比它晚了两个大版本工具链变化集中在三块编译器从 GCC 6 跳到 GCC 9.3FFmpeg 从 3.x 跳到 4.2.x默认 Python 从 3.5 跳到 3.8。这三处变化恰好全部落在 OpenCV 编译链路的敏感位置上——videoio 模块在编译期直接绑定 FFmpeg APIpython 模块在编译期直接绑定 Python C APIhighgui 则依赖 GTK 的检测结果。所以编译前花十分钟把环境看清楚比遇到报错再回头排查要省事得多。2.1 Ubuntu 20.04 的默认工具链与 3.3.1 的兼容水位先看一组系统默认值。Ubuntu 20.04 自带 gcc/g 9.3.0CMake 3.16.3Python 3.8.xFFmpeg 4.2.x对应 libavcodec 58GTK 有 2 和 3 两套可选。而 OpenCV 3.3.1 发布时对应的主流组合是 GCC 5/6、CMake 3.8、Python 2.7 或 3.5、FFmpeg 3.x。编译器层面 GCC 9 对 C11 的默认特性检查更严格隐式类型转换会被直接判错FFmpeg 4.x 是主版本升级删除了一批 3.x 时代的老 APIPython 3.8 的 C API 对 Python 2 时代的函数名做了清除或重定义。为什么不能直接用 apt 装Ubuntu 20.04 官方源里的 libopencv-dev 是 4.2 版本4.x 把 xfeatures2d、SIFT 这类经典特征算法挪进了 opencv_contrib并且部分算法还改了命名空间老代码在 cv::xfeatures2d 路径下根本找不到符号。pip 的 opencv-python 同样只有 4.x。所以对于代码栈固定在 3.3.1 的项目源码编译是唯一可控的路径。判断是否需要这份修改版资源不用看报错直接看系统里的 libavcodec 版本版本号以 58 开头第三章里的两个 FFmpeg 错误就必然出现如果系统里根本没装 FFmpeg 开发库错误路径又完全不同。2.2 安装基础依赖build-essential、CMake 与 GTK2在 Ubuntu 20.04 上编译 OpenCV 3.3.1依赖安装可以一次到位sudo apt update sudo apt install build-essential cmake git pkg-config \ libgtk2.0-dev libavcodec-dev libavformat-dev libswscale-dev \ libv4l-dev libtbb-dev libjpeg-dev libpng-dev libtiff-dev \ python3-dev python3-numpy python3-distutils这里有两个容易被忽略的点。第一个是 libgtk2.0-dev 而不是 libgtk-3-devOpenCV 3.3.1 的 highgui 对 GTK2 的适配是完整且有测试覆盖的对 GTK3 的适配在 2017 年只停留在“能检测到但链接期容易翻车”的状态选 GTK2 能少一类 undefined reference。第二个是 python3-distutilsUbuntu 20.04 的 Python 3.8 把 distutils 单独拆出去了不装的话make install 阶段跑到 python 模块时会直接抛出 ModuleNotFoundError: No module named distutils.util。这两项都属于装了不亏、漏了必炸的依赖。python3-numpy 也不能省OpenCV 的 python 绑定在编译期要引用 numpy 的头文件做数组类型检查缺失会在 CMake 阶段直接报 Python numpy 未找到。v4l 库对应的是 V4L2 视频采集接口如果你后续要接 USB 摄像头或者用 VideoCapture 读设备节点这一项是硬依赖否则编译出来只有文件读写没有设备读写排错的时候会非常迷惑。2.3 检查环境版本确认需要打补丁依赖装完后把下面的命令逐条跑一遍把输出记下来gcc --version | head -1 cmake --version | head -1 python3 --version pkg-config --modversion libavcodec pkg-config --modversion gtk-2.0我一般关注三个关键判断python3 是否 3.8 及以上libavcodec 是否 58.x 开头gtk-2.0 是否能被 pkg-config 查到。这三个条件同时满足说明当前环境正好落在 OpenCV 3.3.1 的兼容水位之外第三章的源码修改是不可跳过的。反过来如果 libavcodec 是 54/55/56 这种 FFmpeg 2.x/3.x 老版本那源码不需要动直接跳到第四章的 CMake 配置即可。这个检查把“要不要打补丁”从玄学变成了确定判断也避免拿着旧源码在配置阶段反复绕圈。3. 修复三类编译错误FFmpeg API 更名与 Python 2 接口残留这一章是资源的核心价值。OpenCV 3.3.1 在 Ubuntu 20.04 上编译最典型的三类报错分别来自 FFmpeg 的两个 API 变更和 python 绑定层的一个类型转换问题。下面逐个拆开说明报错为什么会冒出来以及这份资源里的修改手法。3.1 CODEC_FLAG_GLOBAL_HEADERFFmpeg 4.x 的宏更名先看第一个报错error: ‘CODEC_FLAG_GLOBAL_HEADER’ was not declared in this scope这个错误出现在 modules/videoio 底下的 cap_ffmpeg 实现文件里。OpenCV 3.3.1 写视频输出时需要设置编码器的 header 标志用的是 FFmpeg 3.x 时代的旧名字 CODEC_FLAG_GLOBAL_HEADER。FFmpeg 4.0 把所有 AVCodecContext 相关的标志宏从 CODEC_FLAG_* 统一改名为 AV_CODEC_FLAG_*旧名字在头文件里被删除编译时 preprocessor 找不到这个宏直接在作用域检查阶段报错。修复方式是把旧宏替换成新宏。这份资源里的处理是搜索 videoio 模块下的全部 cap_ffmpeg 相关文件避免遗漏同族旧宏。如果你拿到的是未修改的官方源码可以手动执行grep -rn CODEC_FLAG_GLOBAL_HEADER modules/videoio/src/ sed -i s/CODEC_FLAG_GLOBAL_HEADER/AV_CODEC_FLAG_GLOBAL_HEADER/g \ modules/videoio/src/cap_ffmpeg.cpp modules/videoio/src/cap_ffmpeg_impl.hpp这里有个容易出错的地方有些人图省事在 OpenCV 根目录做全局 sed结果把文档和 contrib 里的引用也一并替换反而引入新的不一致。我一般会把替换范围局限在 videoio/src 目录替换完再 grep 一遍确认没有残留。另外CODEC_FLAG_LOOP_FILTER 这类同族旧宏也可能在同一个文件里出现可以一起检查grep -rn CODEC_FLAG_ modules/videoio/src/输出里所有 CODEC_FLAG_ 开头的旧宏都按 AV_CODEC_FLAG_ 前缀规则处理避免编译到 70% 再抛出第二个同名错误。还有一点值得提醒不要用 -fpermissive 把错误降级成警告跳过。那个开关只会掩盖掉类型检查FFmpeg 4.x 头文件里根本没有这个宏所谓警告背后实际是符号缺失运行时必然在 avcodec 初始化阶段崩溃。3.2 AVFMT_RAWPICTURE被移除的输出格式标志第二个报错紧跟着第一个出现error: ‘AVFMT_RAWPICTURE’ was not declared in this scopeAVFMT_RAWPICTURE 原本是 AVOutputFormat 结构上的一个 flag用来告诉 muxer 输入是原始图像帧。FFmpeg 4.0 在重构 libavformat 时把这个 flag 从公共头文件里摘掉了因为 muxer 的行为不再依赖这个标志位。OpenCV 3.3.1 的写视频分支还在设置它于是编译器报未声明。修复方式比第一个更值得讲究。直接删掉该行在 FFmpeg 4.x 下没有任何问题可一旦将来要回到 FFmpeg 3.x 环境写视频逻辑会缺少原本依赖的行为。推荐的写法是用条件编译把设置语句包起来#ifdef AVFMT_RAWPICTURE oc-oformat-flags | AVFMT_RAWPICTURE; #endif逻辑很直接宏存在时保持旧行为宏被删除时跳过设置前后两个 FFmpeg 主版本都能编译。这份资源采用的就是这个手法而不是无脑删行。自己改的时候注意同文件里可能不止一处引用 AVFMT_RAWPICTURE先把所有引用位置用 grep 找全再统一包 ifdef避免改了一半编译到后半段又报同样的错误。3.3 PyString_AsStringPython 2 时代绑定接口的残留第三个错误来自 python 绑定层报错信息带着具体行号error: invalid conversion from ‘const char*’ to ‘char*’ [-fpermissive] 856 | char* str PyString_AsString(obj);这个错误的根因和前两个不同不是接口被删除而是接口的返回类型在 Python 3 下发生了改变。PyString_AsString 是 Python 2 的字符串 C API在 Python 3.8 的头文件里被映射到 PyUnicode 或 PyBytes 的对应函数有些分支返回 const char*而 OpenCV 3.3.1 源码里声明的局部变量是 char*。C 对 const char* 到 char* 的隐式转换在 GCC 9 下直接报错。正确的修复是把字符串获取逻辑按 Python 版本分开处理。在实际打补丁时做法类似下面这样#if PY_MAJOR_VERSION 3 const char* str nullptr; if (PyUnicode_Check(obj)) { str PyUnicode_AsUTF8(obj); } else if (PyBytes_Check(obj)) { str PyBytes_AsString(obj); } #else char* str PyString_AsString(obj); #endif注意两点。第一str 的声明类型要改成 const char*因为 PyUnicode_AsUTF8 返回 const char*不匹配下一行又会报同样的 -fpermissive 错误第二PyString_AsString 在同文件里经常不是孤例PyString_Size、PyString_FromString 也属于同一批 Python 2 接口要一并搜出来处理grep -rn PyString_ modules/python/src2/另外OpenCV 3.3.1 的 python 绑定不是直接手写的而是通过 gen2.py 模板生成 cv2.cpp。直接改生成文件是临时方案重新跑生成流程时修改会丢这份资源是在生成器层面处理的所以不会出现“改了 cv2.cpp 但重新生成后又恢复原样”的情况。把三个错误的对应关系整理成表报错原文根因修复手法CODEC_FLAG_GLOBAL_HEADER was not declaredFFmpeg 4.0 宏更名CODEC_FLAG_ 前缀改为 AV_CODEC_FLAG_AVFMT_RAWPICTURE was not declaredFFmpeg 4.0 移除 flag用 #ifdef AVFMT_RAWPICTURE 条件编译包裹invalid conversion from const char* to char*Python 3 字符串 C API 返回类型改变按 PY_MAJOR_VERSION 分支改用 PyUnicode_AsUTF84. CMake 配置与编译落地参数组合、并发度与安装路径源码准备好之后CMake 参数是决定成败的下一步。OpenCV 3.3.1 的 CMake 检测能力比 4.x 弱很多参数需要显式写出来否则它会按自己的默认值走用户对编译产物没有掌控感。这一节给出的配置是 Ubuntu 20.04 上反复验证过的组合兼顾功能完整性和编译速度。4.1 生成构建脚本显式指定 Python 与模块开关在 opencv-3.3.1 根目录下执行cd opencv-3.3.1 mkdir -p build cd build cmake -D CMAKE_BUILD_TYPERELEASE \ -D CMAKE_INSTALL_PREFIX/usr/local \ -D BUILD_opencv_python2OFF \ -D BUILD_opencv_python3ON \ -D PYTHON3_EXECUTABLE$(which python3) \ -D PYTHON3_INCLUDE_DIR/usr/include/python3.8 \ -D PYTHON3_PACKAGES_PATH$(python3 -c import site; print(site.getsitepackages()[0])) \ -D PYTHON3_NUMPY_INCLUDE_DIRS$(python3 -c import numpy; print(numpy.get_include())) \ -D WITH_FFMPEGON \ -D WITH_V4LON \ -D WITH_GTKON \ -D WITH_TBBON \ -D WITH_CUDAOFF \ -D WITH_OPENCLON \ -D BUILD_EXAMPLESON \ -D INSTALL_PYTHON_EXAMPLESON ..逐个说参数的含义。CMAKE_BUILD_TYPE 用 RELEASEOpenCV 3.3.1 在 Release 下会开 -O3图像算法吃优化Debug 版性能差到没法用。BUILD_opencv_python2OFF 是为了避免系统里没有 Python 2 时给出误导性警告BUILD_opencv_python3ON 配合下面三个 PYTHON3_ 参数把 Python 3.8 的头文件、numpy 头文件和包安装路径全部钉死。这里的常见坑是不显式指定 PYTHON3_PACKAGES_PATH 时CMake 偶尔会指向一个不存在的目录导致 import cv2 时模块找不到。WITH_FFMPEGON 确保 videoio 走 FFmpeg 后端这是读 mp4 的基本前提WITH_GTKON 配合第二章装的 libgtk2.0-dev保证 imshow 可以工作。WITH_CUDAOFF 是这类老版本编译的一致选择OpenCV 3.3.1 对 Ubuntu 20.04 的驱动和 CUDA 版本没有适配打开反而要处理 GPU 架构检测失败的问题纯 CPU 编译对大多数老项目已经足够。WITH_TBBON 启用线程构建块做并行加速对多核机器上的图像处理有明显收益前提是已经装了 libtbb-dev。WITH_OPENCLON 利用 CPU 端的 OpenCL 运行时做部分算子加速没有独显也不会报错OpenCL 检测失败时 CMake 会自动降级。cmake 命令末尾的 .. 指向源码根目录。执行完先看输出末尾的配置摘要重点确认三行FFMPEG 是 YES、GTK 是 YES、Python 3 是 YES。如果 FFMPEG 显示 NO说明 pkg-config 没找到 libavcodec回去检查 libavcodec-dev 是否安装。4.2 编译并发数按内存挑不按核心数挑配置通过后开始编译make -j4-j 参数不建议直接写 nproc。OpenCV 3.3.1 的编译单元非常吃内存尤其是 opencv_core 和 opencv_imgproc单个编译进程吃 1.5GB 到 2GB 很正常。8 核机器用 -j8 在 16GB 内存上问题不大8GB 内存用 -j8 大概率在编译中期出现 cc1plus 进程被 kill 的报错。我一般的习惯内存小于等于 8GB 用 -j216GB 用 -j432GB 以上才放开到 -j8。这条经验比只看核心数靠谱。编译中断也没关系make 支持增量编译。中断后重新执行相同命令即可续编不需要重新 cmake。如果编译过程中再次出现第三章修过的错误名先停掉 make回源码目录执行一次 grep确认修改落在正确文件上。cap_ffmpeg.cpp 和 cap_ffmpeg_impl.hpp 这两个文件经常被弄混只改其中一个并不能让 videoio 编过。如果是没见过的新错误先看报错文件路径和行号多半是依赖版本问题不要盲目加 -fpermissive 掩盖。4.3 安装与软链配置编译完成后安装sudo make install sudo ldconfigmake install 会把头文件放到 /usr/local/include/opencv2库文件放到 /usr/local/libpython 模块按 PYTHON3_PACKAGES_PATH 指定的目录放pkg-config 文件 opencv.pc 放到 /usr/local/lib/pkgconfig。ldconfig 必须执行否则动态库缓存里没有 /usr/local/libpython 加载 cv2 时会报找不到 libopencv_core.so.3.3。通常还需要把 pkg-config 路径导出方便后续编译 C 工程时能找到 opencv.pcexport PKG_CONFIG_PATH/usr/local/lib/pkgconfig pkg-config --modversion opencv第二条命令输出 3.3.1 说明安装链路正常。这里值得提醒OpenCV 3.3.1 与系统里其他 OpenCV 版本不会冲突因为库名带版本后缀但如果系统里还有一份 apt 安装的 opencvpkg-config 结果可能被抢先截获出现版本对不上的假象排错时要留意。5. 避坑清单从 configure 到 import cv2 的五条实战记录写配置、改源码、make install每一步都有对应的坑。下面五条是 Ubuntu 20.04 上编译 opencv-3.3.1 期间实际踩过的按出现阶段排列每条按现象、原因、解决来写。5.1 ippicv 下载卡死现象cmake 执行到 3rdparty 检测时长时间停在 Downloading ippicv_xxxx 不动或者直接报网络 timeout整个配置流程中断。原因OpenCV 3.3.1 的 CMake 在检测 IPP 加速库时会从 github 下载 ippicv 压缩包。这个下载源在国内网络环境下成功率很低而 CMake 没有合理的重试机制卡住就是一直卡住。解决手动下载对应的 ippicv tarball放进 build/3rdparty/ippicv/downloads/ 目录文件名要和 CMake 脚本校验的 checksum 对应。更省事的做法是给 cmake 命令追加 -DIPPICV_DOWNLOAD_URLfile:///path/to/ippicv.tgz指定本地源让配置阶段直接读文件。这一步只花一分钟但能避免在 configure 阶段耗掉半天。注意这个坑和源码修改无关是 OpenCV 3.3.1 的共性问题。5.2 GTK 版本误选导致链接期错误现象CMake 配置输出里 GTK 显示 YES编译也全部通过但写代码调用 cv::imshow 时出现 undefined reference或者样例程序链接失败。原因系统里同时存在 GTK2 和 GTK3 时OpenCV 3.3.1 的 CMake 优先走到 GTK3 分支。那个版本的 highgui 对 GTK3 的适配不完整部分函数在链接期缺符号。Ubuntu 20.04 默认同时提供两套库很容易踩中。解决第二章只装 libgtk2.0-dev不装 libgtk-3-dev。已经装了的话检查 CMake 配置摘要里 GTK 指向的版本号如果显示 3.x把 CMakeCache.txt 里相关 GTK 路径清掉重配或者干净环境下重新跑 cmake。GTK 选错不会影响编译期但会让 imshow 系列功能在运行期瘫痪排查起来比编译错误更隐蔽。5.3 distutils 模块缺失现象make install 执行到 python 模块安装阶段抛出 ModuleNotFoundError: No module named distutils.util前面的 C 库都装好了唯独 python 接口没装上。原因Ubuntu 20.04 的 Python 3.8 把 distutils 从基础安装里去掉了OpenCV 3.3.1 的 python 安装脚本依赖它解析路径并执行 setup 逻辑。解决sudo apt install python3-distutils 装完后重新执行 sudo make install。不需要重新编译安装阶段会再次调用 python 脚本这次就能通过。如果你是在 venv 虚拟环境里做 python 绑定还要确认 venv 内也具备 distutils否则同样会报。5.4 import cv2 报动态库找不到现象编译、安装都成功python3 里执行 import cv2 却抛 ImportError: libopencv_core.so.3.3: cannot open shared object file。原因/usr/local/lib 里的动态库没有进入 ldconfig 缓存。ldconfig 只有在系统启动或手动执行时才会刷新安装后忘了执行是最常见的原因。解决sudo ldconfig 刷新一次。如果系统里 /usr/local/lib 本身没有被 ldconfig 配置需要手动写入echo /usr/local/lib | sudo tee /etc/ld.so.conf.d/opencv.conf sudo ldconfig之后重新打开 python3import cv2 前先确认 sys.path 里没有指向其他版本 cv2 的路径。如果有多个 python 环境混用优先检查 PYTHON3_PACKAGES_PATH 是否真的存在于当前 python3 的 site-packages 搜索范围内。5.5 同一个错误在 make 后期再次出现现象make 编译到 60% 左右又报出 CODEC_FLAG_GLOBAL_HEADER 未声明和第三章修掉的错误完全一样。原因修改只作用到了 cap_ffmpeg_impl.hpp而实际编译单元最终 include 的是 cap_ffmpeg.cpp或者另一个文件里还有残留引用。OpenCV 3.3.1 的 videoio 模块里这两个文件双向包含很容易改漏。解决不要只 grep 单个文件名直接全目录搜索grep -rn CODEC_FLAG_GLOBAL_HEADER modules/videoio/src/返回值应该为空。还有内容就继续替换替换完再 grep 一次确认干净然后重新 make。这个习惯能省下半小时的无效重编译时间。6. 验证安装一张图、一段视频与一份构建信息安装完成不算结束还要证明修过的三个错误确实让功能恢复正常。我习惯用一个最小脚本把核心链路全部过一遍。import cv2 print(OpenCV version:, cv2.__version__) info cv2.getBuildInformation() for key in [FFMPEG, V4L/V4L2, GTK, Python 3]: for line in info.split(\n): if key in line: print(line.strip()) break img cv2.imread(test.jpg) if img is None: raise SystemExit(test.jpg read failed) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) edges cv2.Canny(gray, 50, 150) cv2.imwrite(edges.jpg, edges) cap cv2.VideoCapture(test.mp4) if not cap.isOpened(): raise SystemExit(test.mp4 open failed) ok, frame cap.read() print(video frame:, frame.shape if ok else read failed) cap.release()脚本里每一段都有对应目的。cv2.getBuildInformation 里检查 FFMPEG、GTK、Python 3 三行的值确认核心模块编进了运行版本imread 和 imwrite 验证 imgcodecs 对 jpg 的编解码Canny 验证 imgproc 的算法链路VideoCapture 读 mp4 验证 videoio 的 FFmpeg 后端。只要视频能读到 frame第三章那两个 FFmpeg 错误就说明改到位了。如果 VideoCapture 报 open failed优先查构建信息里 FFMPEG 那行是不是 NO而不是怀疑代码。脚本跑完我会顺手复制官方 samples 里的一个 C 例子编译一遍确认 pkg-config 链路也对得上cd opencv-3.3.1/samples/cpp g -o example example.cpp $(pkg-config --cflags --libs opencv) ./example从那以后每次 Ubuntu 版本升级后重装这个环境我都强制把上面这个脚本完整走一遍再继续其他工作。它能在两分钟内把最容易翻车的四个模块全部验完也能在项目交接时给对方一个明确的验收标准。编译环境这种事动手前多确认一遍版本动手后多跑一遍验证就能把不确定性压到最低。希望帮到你。本文还有配套的精品资源点击获取