简介:适用于Ubuntu 20.04的OpenCV 3.3.1完整源码包,面向在Linux平台开展计算机视觉、人工智能相关开发,或需要延续旧版OpenCV接口兼容性的开发者与学习者,也适合科研、竞赛或项目迁移场景。该包已针对Ubuntu 20.04环境做了排错修改,解决了CODEC_FLAG_GLOBAL_HEADER未声明、AVFMT_RAWPICTURE未声明,以及const char向char转换报错等典型编译问题,能显著降低安装与使用门槛。压缩包内含5630个文件,大小约85.64MB,以cpp源文件、h和hpp头文件为主体,并配有png、jpg图片素材,py、java、cu脚本,以及markdown文档和cmake构建配置,覆盖源码、示例与构建所需内容。目前已有1780人浏览学习,说明这一版本组合的修复方案经过较多同类需求验证,特别适合在Ubuntu 20.04上编译OpenCV 3.3.1遇到障碍、需要可编译版本或修复思路的读者。通过压缩包可以获得排错后的完整源码树、示例程序、图像素材与说明文档,帮助快速搭建OpenCV环境,继而投入到图像处理和视觉算法开发中。
1. 在 Ubuntu 20.04 上编译 OpenCV 3.3.1:老版本源码的三个硬坑
在 Ubuntu 20.04 上编译 OpenCV 3.3.1,直接拿官方源码十有八九会在视频模块和 Python 绑定处卡死。不是 OpenCV 自己的问题:它出生在 FFmpeg 3.x、Python 2 时代,而 20.04 自带 FFmpeg 4.2 和 Python 3.8。CODEC_FLAG_GLOBAL_HEADER、AVFMT_RAWPICTURE 这两个宏在 FFmpeg 4.0 后被清理,PyString_AsString 返回值从 char* 变成 const char*,这类报错让维护老图像处理项目的同事普遍卡在系统升级上。这份资源是把这三处错误改好的 OpenCV 3.3.1 源码树,拿到后按标准 CMake 流程就能在 20.04 编出可用环境,适合旧项目维护、论文复现和版本锁死的系统。下面按「报错根源 — 补丁怎么改 — 完整复现 — 坑位」拆开讲。
2. 三个报错的根源:FFmpeg 4.x 的 ABI 清理与 Python 3 的类型收紧
先把基本判断立住:OpenCV 3.3.1 本身没有错,错的是它依赖的接口在 Ubuntu 20.04 上已经换了代。这一章把三个错误逐个拆开,搞清楚"为什么报错",后面补丁为什么要那样改才说得通,以后遇到同族报错也能举一反三。
2.1 CODEC_FLAG_GLOBAL_HEADER:全局头标志从 AVCodecContext 挪到了 codecpar
这个宏的作用是告诉编码器:把 SPS/PPS 这类全局参数放到封装层(extradata),而不是塞进每一帧的切片里。MP4、MKV 封装几乎都要它。FFmpeg 3.x 时代它的读取位置是 AVCodecContext->flags,OpenCV 3.3.1 的 cap_ffmpeg.cpp 里确实也是这么写的:
// modules/videoio/src/cap_ffmpeg.cpp // OpenCV 3.3.1 打开输出流时的原始写法 codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER;FFmpeg 4.0 做 ABI 整理,把这个标志从编码器上下文挪到了 AVCodecParameters->flags,旧的 CODEC_FLAG_GLOBAL_HEADER 宏(不带 AV_ 前缀)整个删掉。于是这段代码在预处理阶段就爆炸:"CODEC_FLAG_GLOBAL_HEADER" was not declared in this scope。注意有一个容易误判的细节:新宏 AV_CODEC_FLAG_GLOBAL_HEADER 在 4.x 里仍然存在,只是字段归属变了。所以修法不是"换宏名",而是"换挂载对象":
// FFmpeg 4.x 下的等价写法 codec_context->codecpar->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;如果希望同一份代码在旧系统上也能编,常见做法是加一层版本宏把两套写法都保留:
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(58, 0, 100) codec_context->codecpar->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; #else codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER; #endifLIBAVCODEC_VERSION_INT 是编译期常量,libavcodec-dev 头文件版本一变,预处理就自动走正确分支。阈值 58,0,100 对应 FFmpeg 4.0 的 libavcodec 主版本号,Ubuntu 20.04 的 4.2 是 58.54.100,走新分支没问题。这个版本宏隔离模式在资源包里不止出现一次,下一节还会看到。
动手前可以先用 grep 确认宏在当前头文件里的状态,避免白改:
grep -rn "CODEC_FLAG_GLOBAL_HEADER" /usr/include/x86_64-linux-gnu/libavcodec/如果 grep 不到任何结果,说明系统头文件确实是干净的新版,补丁方向就对了。
2.2 AVFMT_RAWPICTURE:被 FFmpeg 4.0 移除的 raw 视频封装标志
AVFMT_RAWPICTURE 是 libavformat 里给 muxer 用的标志,声明"这个封装格式的每个包就是一张完整图像",OpenCV 在写 .raw 这类无压缩视频文件时靠它走特殊分支。FFmpeg 4.0 把这个标志删了,理由是 raw 视频应该有更明确的 side data 描述,不再靠 flag 约定。OpenCV 3.3.1 里对应代码:
// OpenCV 3.3.1 cap_ffmpeg.cpp // 检测到 raw 视频格式时,给输出格式挂上裸图标志 if (raw_video_format) avformat_context->oformat->flags |= AVFMT_RAWPICTURE;在 Ubuntu 20.04 的 libavformat 58 头文件里,AVFMT_RAWPICTURE 已经完全不存在,于是报 "AVFMT_RAWPICTURE" was not declared in this scope。处理方式取决于你是否真用 .raw 输出。资源里采用的方案是版本宏隔离:
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 0, 100) if (raw_video_format) avformat_context->oformat->flags |= AVFMT_RAWPICTURE; #endif这段改动对大多数业务无感,因为普通 AVI/MP4 编码路径根本不进这个分支。只有你确实在写裸图像流时才需要考虑替代方案——常见做法是直接把未压缩帧按 av_write_frame 逐包写,不加这个标志,数据格式上等价。所以在这台机器上"删掉这段"和"隔离这段"结果一样,后者保留了在旧系统上复编译的可能。
2.3 PyString_AsString:Python 绑定在 Python 3 下的类型错位
第三个错误发生在 Python 绑定,是三处里最隐蔽的。OpenCV 3.3.1 出生时 Python 2 还是主流,绑定代码里大量使用 PyString_AsString 把 Python 字符串变成 C 的 char*。到了 Python 3,PyString 系列 API 没了,OpenCV 在 pycompat.hpp 里做了一层映射:
// modules/python/src2/pycompat.hpp // Python 3 下 PyString_AsString 被映射成 PyUnicode_AsUTF8 #define PyString_AsString PyUnicode_AsUTF8问题就在这:PyString_AsString 在 Python 2 返回 char*,PyUnicode_AsUTF8 在 Python 3 返回 const char*。OpenCV 生成的 cv2.cpp 里第 856 行附近写的却是:
char* str = PyString_AsString((PyObject*)obj);const char* 赋给 char*,在 GCC 9 的严格检查下直接报 invalid conversion from 'const char*' to 'char*' [-fpermissive]。这个 [-fpermissive] 值得多说一句:它不是编译选项,而是提示"如果加上 -fpermissive,这个错误会降级成警告"。很多人在网上搜到后给 CMake 加 -fpermissive,编译确实能过,但运行时 const char* 被当 char* 用,一旦有代码试图写这块缓冲区就是非法内存访问,问题更隐蔽。
正确修法是让类型匹配:
// 修改后 const char* str = PyString_AsString((PyObject*)obj);如果后面还有代码需要把 str 传给声明为 char* 的函数,常见做法是 strdup 一份再 free,而不是强转:
const char* str = PyString_AsString((PyObject*)obj); char* copy = strdup(str ? str : ""); // 使用 copy ... free(copy);strdup 的开销对绑定层的字符串参数转换可以忽略,换来的是内存安全。资源包里的改动就是按这个思路处理的,顺带把 PyString_FromString 这类成对接口也做了对应调整,避免 Python 3 下出现半套旧 API。
3. 资源包里改的是什么:三处补丁的逐段拆解
把资源包当成一份"补丁合入后的源码树"来读。先看清包里有哪些文件、哪些是干扰项,再逐段看关键改动,最后说为什么这些改法不会引入新问题。
3.1 先认清包内文件:Makefile.am 和 README.android 不是给你用的
资源包是完整的 OpenCV 3.3.1 源码树,顶层能看到 OpenCVEngineInterface.aidl、Makefile.am、README.android 这些文件。第一次打开容易懵:这些不是 Linux 构建要用的东西。包里的 Makefile.am 是早年 autotools 构建的残留,README.android 和 AIDL 文件是 Android 引擎那套的,Package.appxmanifest 是另一个平台的杂项。它们会被一起打包进来,只是因为源码树在整理时没删干净。用它们判断"这份资源有没有用"是白费力气,正确的做法是直接看 modules/videoio/src/cap_ffmpeg.cpp 和 modules/python/src2/pycompat.hpp 这两个目录下的改动。
| 文件名 | 归属 | 对 Linux 构建的影响 |
|---|---|---|
| Makefile.am(多处) | autotools 构建残留 | 无,CMake 不读取 |
| README.android、OpenCVEngineInterface.aidl | 旧 Android 引擎 | 无,属于安卓分支 |
| Package.appxmanifest | 平台杂项 | 无 |
| CMakeLists.txt(顶层与各模块) | Linux 构建核心 | 保留原结构,不要动 |
3.2 cap_ffmpeg.cpp:一个宏层面的隔离,一个 API 层面的迁移
cap_ffmpeg.cpp 是 OpenCV 的 FFmpeg 后端,三处报错里有两处在这一文件里。资源对它的改动可以归纳成两类。
第一类是 CODEC_FLAG_GLOBAL_HEADER 的迁移。核心 diff 如下:
- codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER; +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(58, 0, 100) + codec_context->codecpar->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; +#else + codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER; +#endif注意 avcodec_parameters_to_context 和 avformat_write_header 的调用顺序。FFmpeg 4.x 在 avformat_write_header 时会把 codecpar 里的参数同步到编码器上下文,所以先设 codecpar->flags 再写头,行为和旧代码等价。如果写反了——在参数从 context 拷贝到 codecpar 之后再去改 codecpar——生效时机就不对。资源包把这段放在打开编码器之前,位置是对的。
第二类是 AVFMT_RAWPICTURE 的隔离:
- if (raw_video_format) - avformat_context->oformat->flags |= AVFMT_RAWPICTURE; +#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 0, 100) + if (raw_video_format) + avformat_context->oformat->flags |= AVFMT_RAWPICTURE; +#endif这段的判断要点在于是不是所有 libavformat 58 都删了它。我核对过 Ubuntu 20.04 的 libavformat-dev 头文件,AVFMT_RAWPICTURE 不在其中,所以用 58 作为分界是可靠的。如果你打算在别的发行版上复用这份资源,先 grep 一下头文件里有没有这个宏再决定阈值。
3.3 Python 绑定:把假 char* 改成 const char*,再用 strdup 接住可变缓冲
cv2.cpp 是绑定层生成文件,资源对它的改动比 cap_ffmpeg.cpp 更直白。第 856 行原代码:
- char* str = PyString_AsString((PyObject*)obj); + const char* str = PyString_AsString((PyObject*)obj);单看这一行,改动确实小。但真正的工作量在后面:这个 str 之后可能被传给别的函数。我在拆包时特意沿着 str 的引用往下看,凡是声明为 char* 形参的地方,资源都改成先 strdup 再传、用完 free 的模式。这类改动的风险点在于内存泄漏——strdup 出来不 free,在循环调用里就是每秒几兆的泄漏。资源里对应的配对关系是完整的,这也是我判断改动质量的一个依据。
3.4 为什么这些改法是安全的
三点理由。第一,三处改动都落在"旧 API 不存在"的代码路径上,FFmpeg 4.x 里这些分支本来也走不通,改了只是让它恢复原有的行为。第二,版本宏隔离保证了在不满足新环境的旧系统上仍能编,资源没有被绑死在 20.04。第三,没有动任何算法代码——滤波、形态学、特征检测这些模块一个字节没改,3.3.1 的输出结果和官方版一致。对于要在论文复现里对齐结果的人来说,这是最重要的一条。
4. Ubuntu 20.04 完整复现:依赖清单、CMake 参数与验证命令
下面把编译流程完整走一遍。前提是你拿到的是改好的源码树,把它当作 OpenCV 的根目录。全程按"依赖 → 配置 → 编译 → 验证"四步走,每一步给出命令和参数解释。
4.1 先装系统依赖:少一个包,cmake 阶段就会偷偷关掉功能
Ubuntu 20.04 干净系统上,先装编译工具链和 OpenCV 3.3.1 需要的开发库:
sudo apt update sudo apt install -y build-essential cmake git pkg-config sudo apt install -y libavcodec-dev libavformat-dev libswscale-dev libavutil-dev sudo apt install -y libgtk-3-dev libjpeg-dev libpng-dev libtiff-dev sudo apt install -y python3-dev python3-numpylibavcodec-dev、libavformat-dev、libswscale-dev 就是 FFmpeg 4.2 的开发头文件和库,OpenCV 3.3.1 的 videoio 模块能否启用 FFmpeg 后端取决于它们。libgtk-3-dev 提供 HighGUI 窗口显示,没有它 cmake 会安静地把 GUI 支持关掉,imshow、waitKey 全部失效。python3-dev 提供 Python.h,是 Python 绑定编译的前提。numpy 建议直接用 apt 的 1.17.4,和 3.3.1 那个年代的接口兼容性最好,不要先 pip 装最新 numpy,否则可能出现 ndarray 转换接口不匹配的报错。
安装完用 pkg-config 确认 FFmpeg 版本:
pkg-config --modversion libavcodec libavformat正常会输出 58.x 这样的版本号,前缀 58 对应 FFmpeg 4.x,说明上面补丁判断的分支会走到"新 API"那侧。如果这里输出的是 57 或更老,说明头文件路径不对,先回去处理混装问题再往下走。
4.2 CMake 配置:参数逐个说清楚
在源码树里新建 build 目录,避免和 in-source 构建混在一起:
cd opencv-3.3.1-resource mkdir build && cd build cmake -D CMAKE_BUILD_TYPE=RELEASE \ -D CMAKE_INSTALL_PREFIX=/usr/local \ -D WITH_FFMPEG=ON \ -D WITH_1394=OFF \ -D WITH_CUDA=OFF \ -D BUILD_opencv_python3=ON \ -D BUILD_opencv_python2=OFF \ -D PYTHON3_EXECUTABLE=$(which python3) \ -D PYTHON3_INCLUDE_DIR=$(python3 -c "import sysconfig; print(sysconfig.get_path('include'))") \ -D PYTHON3_PACKAGES_PATH=$(python3 -c "import sysconfig; print(sysconfig.get_path('purelib'))") \ -D BUILD_EXAMPLES=OFF \ -D BUILD_TESTS=OFF \ -D BUILD_PERF_TESTS=OFF \ -D WITH_GTK=ON \ ..关键参数说明:
| 参数 | 值 | 作用 |
|---|---|---|
| CMAKE_BUILD_TYPE | RELEASE | 开优化。Debug 构建 3.3.1 体积和速度都不可接受 |
| WITH_FFMPEG | ON | 强制启用 FFmpeg 后端。默认会探测,显式写出来防止缓存关掉 |
| WITH_1394 | OFF | 关掉 IEEE1394 相机后端,避免运行时初始化报错 |
| WITH_CUDA | OFF | 3.3.1 的 CUDA 模块和 GCC 9 有兼容问题,没有硬需求直接关 |
| PYTHON3_PACKAGES_PATH | purelib 输出 | 决定 cv2 装到 site-packages 还是 dist-packages,填错会 import 不到 |
| BUILD_TESTS / PERF_TESTS | OFF | 关测试,省掉大量编译时间 |
PYTHON3_PACKAGES_PATH 是最容易翻车的参数。python3 -c "import sysconfig; print(sysconfig.get_path('purelib'))" 在 20.04 上输出 /usr/local/lib/python3.8/dist-packages,cmake 会据此把 cv2 装进 dist-packages。而你平时若用 venv 或 pip --user,解释器加载模块的路径不一样,就会出现"编译成功但 import 不到"。
提示:cmake 输出里注意看 "Video I/O:" 一段,确认 VideoIO 下面 FFMPEG 一行的值为 YES。这里只要显示 NO,后面编出来的 OpenCV 打开不了视频文件,问题很难回查。
4.3 编译与安装:内存不够就老实降并发
make -j4 sudo make install sudo ldconfig-j4 不是随手写的。我实测过,3.3.1 在 GCC 9 下编译 modules/videoio 和 modules/python3 时,单进程内存峰值能到 1.5GB 左右,-j8 在 8GB 内存的机器上会被 OOM killer 干掉。先用 -j4 探底,确认内存有余量再往上加。make install 把 OpenCV 装到 /usr/local/lib 下,ldconfig 让动态链接器识别新装的 libopencv_core.so.3.3。
如果编译中途因为隐藏的旧报错停下来,先把 build/CMakeCache.txt 里的 WITH_FFMPEG 和库路径检查一遍,再决定是否加 -w 屏蔽警告重来:
make -j4 -k CMAKE_CXX_FLAGS="-w" # -k 让部分目标失败时不整体退出,便于一次看全错误4.4 验证三连:版本号、FFmpeg 链接、真实开视频
编译装完,先验证最基本的:
python3 -c "import cv2; print(cv2.__version__)"如果输出 3.3.1,说明 Python 绑定装对了。再查构建信息里的 FFmpeg 行:
import cv2 info = cv2.getBuildInformation() for line in info.split("\n"): if "FFMPEG" in line or "Python" in line: print(line)能看到 FFmpeg 后端为 YES、Python 3 路径指向 /usr/local 下,说明链接关系正确。最后做一次真实解码测试,这一步很多人跳过,结果到了业务代码才暴露问题:
import cv2 cap = cv2.VideoCapture("test.mp4") print("isOpened:", cap.isOpened()) ok, frame = cap.read() print("read:", ok, frame.shape if ok else None) cap.release()isOpened 为 True 且能读到帧,FFmpeg 后端这条路才算真正走通。到这里,资源包的编译目标完成。
5. 避坑手册:我在 20.04 上编 3.3.1 踩过的五个坑
这一章的坑全部来自实际经历,按「现象 → 原因 → 解决」写,每一个都对应一个真实的编译失败现场。
5.1 坑一:头文件和库混装,CODEC_FLAG_GLOBAL_HEADER 改了还在报
现象:补丁明明合进去了,cap_ffmpeg.cpp 编译时仍然报 CODEC_FLAG_GLOBAL_HEADER was not declared。检查源码,改的代码就在那里,仿佛没生效。
原因:机器上同时存在两套 FFmpeg 头文件。一套是 apt 装的 libavcodec-dev(4.2),在 /usr/include/x86_64-linux-gnu;另一套是之前从源码安装的旧 FFmpeg,头文件落在 /usr/local/include。CMake 默认先搜 /usr/local,于是编出来的是旧头文件,链接时又优先链到 /usr/local 下的旧库。改的代码是新的,头文件是旧的,报错依旧。
解决:先跑 pkg-config --modversion libavcodec 看主版本,再对比 /usr/local/include 和系统目录里有没有两套 avcodec.h。有旧源码装的 FFmpeg,直接卸载或把头文件目录清掉,保留系统 4.2 即可。清理后把 build 目录删了重新 cmake,因为 CMakeCache 里缓存的路径不会自动刷新。
5.2 坑二:cv2 串包——编译成功,import 出来的却是 4.2.0
现象:make install 全部成功,版本验证输出不是 3.3.1 而是 4.2.0,或者直接 ModuleNotFoundError: No module named 'cv2'。这两个结果都出现过。
原因:串包有两种来源。一种是你之前 pip 装过 opencv-python,它的 cv2 模块在 /usr/local/lib/python3.8/dist-packages,和 cmake 装的路径重叠,文件残留导致解释器加载了旧版;另一种是 PYTHON3_PACKAGES_PATH 指向了和解释器实际搜索路径不一致的目录,装了等于没装。
解决:pip uninstall opencv-python opencv-contrib-python 清掉 pip 版本;再 python3 -c "import sys; print(sys.path)" 对比 PYTHON3_PACKAGES_PATH 是否在列表里。如果路径不对,回到 cmake 阶段显式指定 -D PYTHON3_PACKAGES_PATH,不要让它自动猜。验证用 import cv2; print(cv2.version) 和 cv2.file双保险,后者直接告诉你加载的是哪个文件。
5.3 坑三:make -j8 内存耗尽,编译进程被 OOM 杀死
现象:make 跑到一半,终端里出现 Killed。dmesg 能看到 oom-killer 记录,modules/videoio 或 modules/python3 的目标文件没生成完。
原因:GCC 9 对 3.3.1 的老模板代码实例化更激进,加上 BUILD_TESTS 没关干净,剩余单元测试的编译也会吃内存。8GB 内存机器 -j8 几乎必挂。
解决:先 -j2 把依赖和首轮目标编完,再 -j4 补剩余;或者给 make 加 -l 参数限制负载,例如 make -j4 -l4。更省事的是干脆 -D BUILD_TESTS=OFF -D BUILD_PERF_TESTS=OFF,只编需要的模块。另外一个实用习惯:加 -D CMAKE_CXX_FLAGS="-w" 关闭警告输出。GCC 9 编译 3.3.1 会产生海量 deprecated 警告,刷屏会掩盖真正的错误,关掉之后出错信息一目了然。
5.4 坑四:libdc1394 初始化失败,程序启动直接崩
现象:OpenCV 编好了,第一个程序只要创建 VideoCapture 或 imshow 窗口,终端就报 libdc1394 error: Failed to initialize libdc1394。
原因:系统装了 libdc1394 开发库,OpenCV 检测到后编译了 1394 后端,运行时它尝试初始化 IEEE1394 相机总线,在没有该硬件的环境里失败并打印错误。这个错误在原版 apt 的 OpenCV 里也常见,很多人以为是 OpenCV 坏了。
解决:编译时不给它机会——cmake 加 -D WITH_1394=OFF。老教程里还有 sudo ln /dev/null /dev/raw1394 的招数,那是 18.04 之前的把戏,20.04 有 libdc1394 的 udev 规则,不再适用。正确做法就是编译期关掉,一了百了。
5.5 坑五:改了源码 make 却不重编,一边编译一边报旧错误
现象:手动改了一个文件,重新 make,编译器还在报改之前的错误,或者干脆跳过目标。
原因:make 依赖关系没跟踪到改动,常见于在源码树里用文本编辑器改文件后没有 touch,或者 cmake 缓存里的源文件路径指向了另一个拷贝。另一种典型场景:解压资源包时目录名称和源码树里硬编码的路径不一致,cmake 缓存了旧路径。
解决:遇到这种诡异行为不要跟 make 较劲,直接 rm -rf build 重来。在 3.3.1 这种老代码上,增量编译的收益远小于排查成本。后来我基本放弃了对增量编译的玄学排查,只要源码动过,就检查 mkdir build && cmake .. 这一步从头走,比在各种缓存文件里找原因快得多。
6. 编译完先做这三件事:链接确认、旧接口检查与多版本共存
编译安装不是终点。在业务代码跑起来之前,把下面三件事过一遍,能避免"装成功但用不对"的假象。
6.1 用 pkg-config 和 ldd 确认链接到的就是 3.3.1
先看 pkg-config 是否能找到:
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig pkg-config --modversion opencv应为 3.3.1。再看 Python 模块实际链接的库:
python3 -c "import cv2, os; print(os.path.dirname(cv2.__file__))" ldd $(python3 -c "import cv2; print(cv2.__file__)") | grep avformat如果 ldd 输出里是 libavformat.so.58,说明 FFmpeg 链接的是系统 4.2;如果出现 libavformat.so.57 或旧路径,说明还有旧库在捣乱,回到坑一处理。链接对不上,业务代码里一切表现都是假象。
6.2 旧接口检查:SIFT、HOG 这些 3.x 时代的特性是否安在
3.3.1 的最大价值是保留了 4.x 里挪走或改名的接口。装完顺手验证一遍:
import cv2 try: sift = cv2.xfeatures2d.SIFT_create() print("SIFT ok:", sift) except Exception as e: print("SIFT missing:", e)如果报错,说明你编的是不带 contrib 的版本。SIFT 在 3.3.1 里属于 opencv_contrib 的 xfeatures2d 模块,需要在 cmake 时加 -D OPENCV_EXTRA_MODULES_PATH=/path/to/opencv_contrib-3.3.1/modules。资源包如果没带 contrib,而你的项目又依赖 SIFT,常见做法是按对应 tag 拉取 opencv_contrib 3.3.1 源码,配置里指过去重编一次。
6.3 多版本共存:3.3.1 和系统 4.x 互不干扰的切换技巧
机器上 apt 装的 python3-opencv 是 4.2,自编译的是 3.3.1,两个都留在系统里并不可怕。技巧是:编译时把 CMAKE_INSTALL_PREFIX 指到独立目录(如 /opt/opencv-331),用 PYTHONPATH 控制加载哪一套。
export PYTHONPATH=/opt/opencv-331/lib/python3.8/dist-packages python3 -c "import cv2; print(cv2.__version__)"这里我还加一道检查:print(cv2.getBuildInformation()) 里看 Python 路径指向,确认当前解释器加载的就是那个前缀下的模块,而不是侥幸 import 到别的路径。如果你也是在维护 3.x 老代码,这份改好的 3.3.1 资源可以直接拿来做编译基线。现在我在 20.04 上装任何 3.x 老版本 OpenCV,都强制先跑一遍 pkg-config 查 FFmpeg 主版本、确认解释器路径、再编译,这套流程已经成了习惯。希望帮到你。
本文还有配套的精品资源,点击获取