
简介适用于Ubuntu 20.04的OpenCV 3.3.1适配版本修复了旧版OpenCV在较新Linux环境下编译时频繁出现的FFmpeg接口冲突与Python字符串转换报错。作者针对CODEC_FLAG_GLOBAL_HEADER、AVFMT_RAWPICTURE未声明以及PyString_AsString类型转错等典型兼容性问题逐一完成源码修正可直接在Ubuntu 20.04上顺利通过编译适合部署OpenCV 3.3.1的开发者、计算机视觉与人工智能研究人员使用。压缩包共5630个文件以cpp源码、h/hpp头文件、png/jpg图像资源、cmake构建配置及py脚本为主兼有markdown说明文档整体大小约85.64MB目录结构完整清晰。目前已有1780人学习下载。下载后可获得完整的OpenCV 3.3.1源码树与作者排错修改思路能帮助读者快速完成环境搭建省去自行查阅新旧依赖适配问题的时间和精力对深度学习、图像处理项目的基础环境建设具有直接参考价值。1. 在 Ubuntu 20.04 上装 OpenCV 3.3.1一次被迫的源码编译但值得很多同行把老视觉项目迁到 Ubuntu 20.04 时第一反应是pip install opencv-python装完一跑发现findContours、solvePnP这些接口行为和旧代码对不上再一看版本号已经是 4.x整个人都愣住。OpenCV 3.3.1 是 2017 年底发布的版本而 Ubuntu 20.04 默认带的是 Python 3.8 和较新的 GCC两者之间存在明显的兼容断层导致 pip 与 apt 都不能直接交付可用的 3.3.1。这篇文章把「适用于 Ubuntu 20.04 的 opencv-3.3.1 资源」这件事拆开讲清楚为什么这个组合不能靠包管理器解决、源码编译的完整命令与参数、装完怎么让 Python 3.8 正确导入 cv2、以及我实际编译中踩过的坑。适合想做图像处理项目、物体识别任务又被老代码绑定的从业者目标是让cv2.__version__稳定输出 3.3.1。2. 为什么 20.04 装不上 3.3.1Python 3.8、C 标准和 pip 的三重错位2.1 3.3.1 出生在 Python 3.6 时代编译产物与解释器严格绑定OpenCV 的 Python 绑定本质上是 C 扩展模块编译完成后是一个cv2.so文件这个文件与编译时所用的 Python 解释器版本严格绑定。OpenCV 3.3.1 发布时PyPI 上对应的 wheel 主要面向 Python 3.5 和 3.6今天的 Ubuntu 20.04 默认解释器是 Python 3.8.10老 wheel 中的 ABI 标识例如cp36-cp36m与当前解释器完全不匹配pip 在解析阶段就会拒绝安装。这不是换个 pip 源或者加--no-cache-dir能解决的问题。即使你强行从某个第三方渠道拿到一个编译好的cv2.so它链接的 libpython3.6 在 20.04 上根本不存在import cv2时大概率直接报undefined symbol或libpython3.6m.so.1.0: cannot open shared object file。可以先做一次小验证确认当前环境的 Python 版本以及旧 wheel 的解析结果python3 -c import sys; print(sys.version) # 通常是 3.8.10 pip download opencv-python3.3.1 --no-deps -d /tmp/cv331这段命令第一行确认解释器版本第二行尝试拉取 3.3.1 的 wheel。如果下载阶段报错说明当前 pip 版本认为该 wheel 与平台不兼容即使能下载你也会注意到文件名里带的是cp36或cp35后缀与 3.8 对不上。这个现象从侧面说明3.3.1 时代官方并没有为未来的 Python 3.8 准备二进制包。2.2 apt 与 pip 的默认选择一个 4.x一个太新Ubuntu 20.04 的 apt 仓库里有python3-opencv包你可能会想直接sudo apt install python3-opencv装完就能用。这个思路在多数场景下成立但它的版本是 4.2.0不是 3.3.1。如果你的项目依赖的是 3.3.1 时代的 API 语义4.2.0 带来的问题就不是小版本差异那么简单。以实际项目中最常遇到的差异为例差异点OpenCV 3.3.1OpenCV 4.2.0apt 默认函数返回风格大量 C 接口保留旧式返回值部分接口调整了多返回值约定老解包代码报错对 Caffe 模型导入dnn 模块首次成熟readNetFromCaffe非常顺仍支持但默认更偏向 ONNXC 接口函数大量cv前缀函数仍可用被清理老代码直接编译不过与 Python 3.8 ABI需要源码编译适配apt 预编译好开箱即用很多团队锁 3.3.1 的真实原因是训练好的模型和预处理流程都是基于那个版本调的参换新版后同样的solvePnP标志位、同样的dnn前处理步骤输出结果出现百分之几的偏差。这种偏差在检测任务里可能只是置信度波动在标定任务里就是毫米级误差。所以不能简单说「新版更好」你的约束条件是「代码和模型在哪个版本上验证过」。2.3 结论先行源码编译是这个组合唯一可靠的路径结合上面两点可以发现Ubuntu 20.04 上要拿到的 OpenCV 3.3.1既没有现成 wheel也没有 apt 包唯一可靠的方式是获取 3.3.1 源码在本地用当前系统的编译器与 Python 3.8 头文件重新编译。这个方案的好处是编译产物与系统完全匹配cv2.so不会再出现 ABI 问题。代价是你要自己处理编译依赖、CMake 参数和可能的内存不足等问题接下来两章就是完整操作流程。3. 从源码把 OpenCV 3.3.1 编译出来一整套命令与参数解读3.1 先装依赖一次 apt 命令解决的问题在开始编译之前依赖一定要装齐。OpenCV 3.3.1 的编译需要构建工具、图像编解码库、GTK 开发头文件以及 Python 3.8 的开发头文件。缺少任何一类CMake 都会在配置阶段悄悄放弃某个模块导致编译出来的库缺少功能。我一般在 Ubuntu 20.04 上执行以下安装命令sudo apt update sudo apt install -y build-essential cmake git pkg-config \ libjpeg-dev libtiff5-dev libpng-dev \ libavcodec-dev libavformat-dev libswscale-dev \ libgtk-3-dev \ python3-dev python3-numpy python3-pip这里有几个依赖值得单独说明。libgtk-3-dev决定了 OpenCV 能不能启用 GUI 模块不装它的话cv2.imshow运行时直接报错因为编译产物里根本不含高层的窗口支持。python3-dev提供 Python.h 头文件CMake 在检测 Python 模块时必须有这个包否则它会把BUILD_opencv_python3自动关闭。python3-numpy是 OpenCV Python 绑定的编译期依赖因为生成的cv2.so需要引用 NumPy 的头文件来定义数组接口。如果你后面的项目还需要读取视频文件可以顺手加上libxvidcore-dev libx264-dev不需要的话不装也不影响静态图像处理和 dnn 推理。3.2 下载源码并 checkout 到 3.3.1OpenCV 的源码托管在 GitHub官方仓库的 3.3.1 tag 是稳定发布点。注意不要直接 clone master 分支master 上已经是 4.x 的代码与 3.3.1 差异巨大。cd ~ git clone https://github.com/opencv/opencv.git cd opencv git checkout 3.3.1如果你只是想快速验证也可以直接从 GitHub Releases 页面下载opencv-3.3.1.tar.gz源码包解压后结构是一样的。这里用 git 的好处是后续想切换小版本时方便git checkout 3.4.1就能换过去重新编译。注意 OpenCV 3.3.1 的源码目录里已经包含了modules目录dnn 模块在其中不需要额外下载 contrib 扩展。3.3 cmake 的关键开关为什么这些 -D 参数决定能不能跑起来进入源码目录后创建独立的 build 目录然后执行 CMake 配置。这一步是整个编译流程中最容易出错的地方参数不对会导致 Python 模块不生成、GUI 不可用或者链接到错误的 Python 版本。cd ~/opencv mkdir build cd build cmake -D CMAKE_BUILD_TYPERELEASE \ -D CMAKE_INSTALL_PREFIX/usr/local \ -D PYTHON_EXECUTABLE$(which python3) \ -D BUILD_opencv_python3ON \ -D BUILD_opencv_python2OFF \ -D BUILD_opencv_javaOFF \ -D BUILD_EXAMPLESOFF \ -D WITH_TBBOFF \ -D WITH_OPENMPOFF \ -D WITH_GTKON \ -D WITH_OPENGLON \ -D WITH_V4LON \ ..逐项说明这些参数的作用。CMAKE_INSTALL_PREFIX决定安装位置默认是/usr/local通常不需要改。PYTHON_EXECUTABLE指向当前系统的 python3这一步尤其重要OpenCV 3.3.1 的 CMake 脚本对 Python 3.8 的自动检测能力有限不显式指定时它可能只找到 Python 2或者干脆报告PYTHON3 NOT FOUND。BUILD_opencv_python3ON是强制开启 Python 3 绑定老版本 CMake 默认可能不会为 3.8 生成绑定。BUILD_opencv_python2OFF是因为 20.04 上没有 Python 2开着会在安装阶段报错。WITH_GTKON配合前面装的 libgtk-3-dev让imshow可用。WITH_V4LON用于摄像头读取做物体识别和图像处理项目基本都要用到。执行完 CMake 后建议花三十秒检查输出末尾的摘要重点看这几行Python 3: YES、GTK: YES、Video I/O: V4L。如果输出里 Python 3 显示的是NO先不要继续编译回到这里解决问题不然几个小时编译完才发现没有 Python 绑定非常痛苦。如果 CMake 输出里 Python 3 状态是 NO可以追加两个参数再次运行cmake -D PYTHON_LIBRARY$(python3 -c import sysconfig; print(sysconfig.get_config_var(LIBDIR)))/libpython3.8.so \ -D PYTHON_INCLUDE_DIR$(python3 -c import sysconfig; print(sysconfig.get_paths()[include]) \ ..这里第一个命令从 Python 的 sysconfig 中动态读取库文件目录第二个读取头文件目录避免手写路径时因为虚拟环境或自定义安装位置而出错。3.4 make 编译与安装控制并行度与单独构建 Python 模块CMake 配置成功后进入编译阶段。OpenCV 3.3.1 的源码体量不算小完整编译通常需要 10 到 30 分钟取决于机器性能。make -j4 make install sudo ldconfig-j4是个相对稳妥的并行度。不建议在 2GB 内存的云服务器上盲目使用-j8后面避坑章节会专门讲内存不足的问题。如果编译中途因为某个模块失败你不需要全部重来可以只构建 Python 绑定模块make opencv_python3这条命令单独编译cv2.so生成位置在build/lib/python3/目录下。在调试 Python 绑定问题时这个技巧可以节省大量时间。安装完成后sudo ldconfig让系统动态链接器找到/usr/local/lib下的libopencv_core.so.3.3等库文件少这一步的话Python 导入 cv2 时会报找不到共享库。安装完成后可以立刻做一次基础验证python3 -c import cv2; print(cv2.__version__, cv2.__file__)如果看到3.3.1和对应的.so路径说明编译链路已经完整跑通。如果这一步提示No module named cv2说明 Python 解释器的搜索路径没有包含编译产物的安装位置下一章专门处理这个问题。4. 让 Python 3.8 找到 cv2PYTHONPATH 与虚拟环境的两种路4.1 直接设置 PYTHONPATH适合全局环境按上一章的参数编译安装后cv2.so会被复制到 Python 3.8 的 site-packages 目录中通常是/usr/local/lib/python3.8/dist-packages。多数情况下直接import cv2就能成功。如果你当前系统里之前用 pip 装过其他版本的 opencv-python可能会出现版本被覆盖或者路径冲突的情况。先检查当前 Python 的模块搜索路径python3 -c import sys; print(\n.join(sys.path))看到/usr/local/lib/python3.8/dist-packages在列表里且排在/usr/lib/python3/dist-packages之前那么导入的必然是编译出来的 3.3.1。如果没有看到这个路径临时设置环境变量即可export PYTHONPATH/usr/local/lib/python3.8/dist-packages:$PYTHONPATH python3 -c import cv2; print(cv2.__version__)这个export只对当前终端有效重新开终端会丢失。想永久生效把这一行追加到~/.bashrc末尾然后source ~/.bashrc。我一般不建议在生产机器上永久改全局 PYTHONPATH因为它会影响机器上所有 Python 项目如果这台机器只有一个人用倒是无所谓。4.2 用 venv 隔离老项目的后悔药如果你同一个机器上既需要 OpenCV 3.3.1又跑着其他依赖 OpenCV 4.x 的项目全局安装会让两个项目互相踩踏。此时采用虚拟环境隔离是更好的选择。Python 3.8 自带 venv 模块不需要额外安装 virtualenv。python3 -m venv --system-site-packages ~/venvs/cv331 source ~/venvs/cv331/bin/activate python -c import cv2; print(cv2.__version__)这里的关键参数是--system-site-packages它让虚拟环境继承系统全局 site-packages 中的已安装包。因为我们前面把 OpenCV 3.3.1 编译安装到了/usr/local/lib/python3.8/dist-packages虚拟环境会把它一并继承进来。如果不加这个参数虚拟环境默认是干净的反而找不到 cv2。另一个隔离思路是编译时直接指定独立的安装前缀把 OpenCV 3.3.1 整个装到用户目录不动系统全局。比如重新执行 CMake 时把CMAKE_INSTALL_PREFIX改成$HOME/opencv331cmake -D CMAKE_INSTALL_PREFIX$HOME/opencv331 \ -D PYTHON_EXECUTABLE$(which python3) \ -D BUILD_opencv_python3ON \ .. make -j4 make install安装完成后编译产物在$HOME/opencv331/lib/python3.8/site-packages。使用时设置export PYTHONPATH$HOME/opencv331/lib/python3.8/site-packages:$PYTHONPATH。这个方式的好处是不需要sudo对系统全局零污染换机器迁移时直接拷走整个目录也能用。两种方式的选择原则也很简单如果这台机器以后不会再装其他 OpenCV 版本用全局 ldconfig 最省事如果存在多版本共存的需求用 venv 加系统继承或者独立前缀加 PYTHONPATH 更干净。5. 避坑排查Ubuntu 20.04 装 OpenCV 3.3.1 的五个高频翻车点5.1 cmake 显示 Python 3: NO绑定模块根本没生成现象CMake 配置完成后的输出摘要里Python 3一栏是NO或者Interpreter: NO。此时即使 make 成功也找不到任何 Python 绑定文件。原因OpenCV 3.3.1 的 CMake 脚本使用FindPythonInterp和FindPythonLibs这两个旧模块检测 Python它们对 Python 3.8 的识别逻辑不完善尤其是 libpython 命名方式从libpython3.6m.so变为libpython3.8.so后自动查找经常落空。解决在 CMake 命令中显式指定路径不依赖自动检测cmake -D PYTHON_EXECUTABLE$(which python3) \ -D PYTHON_INCLUDE_DIR$(python3 -c import sysconfig; print(sysconfig.get_paths()[include])) \ -D PYTHON_LIBRARY$(python3 -c import sysconfig; print(sysconfig.get_config_var(LIBDIR)))/libpython3.8.so \ -D BUILD_opencv_python3ON \ ..重新执行后摘要中 Python 3 会变为 YES再继续 make。5.2 imread 读图返回 None但文件路径完全正确现象cv2.imread(/home/user/test.jpg)返回None但用 Python 自带的open()能正常打开文件。原因编译时缺少图像编解码库的开发头文件导致 OpenCV 没有启用 JPEG/PNG 解码器。也有可能是编译时WITH_JPEGOFF或WITH_PNGOFF被传入或者依赖没装全就开始了编译。解决先安装libjpeg-dev libpng-dev libtiff5-dev然后回到 build 目录重新执行 CMake 和 make。注意增量编译可能不会重新检测编解码器最稳妥的做法是删除 build 目录从头配置一次。5.3 make 时进程被杀内存不足与并行度现象编译运行到一半终端没有任何报错只有Killed字样然后 make 退出。原因GCC 编译大型 C 文件时每个编译进程可能占用 1 到 2GB 内存。在 2GB 内存的云服务器上使用make -j4系统内存耗尽OOM killer 随机杀掉编译进程。解决先free -h查看内存2GB 内存用make -j24GB 用make -j4。如果内存实在不够临时添加 swap 也能撑过去sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile这个 swap 是临时性的重启后需要重新swapon想持久化就写入/etc/fstab。编译完成后不想要可以swapoff /swapfile并删除文件。5.4 import cv2 报 libopencv_core.so.3.3 找不到现象import cv2报错libopencv_core.so.3.3: cannot open shared object file: No such file or directory但ls /usr/local/lib里明明有这些库文件。原因动态链接器的缓存没有更新。Linux 下ldconfig会扫描/etc/ld.so.conf中的目录并生成缓存make install把 OpenCV 装到/usr/local/lib后缓存里还没有它。解决执行sudo ldconfig刷新缓存。如果刷新后仍然找不到检查/etc/ld.so.conf.d/下是否有包含/usr/local/lib的配置文件echo /usr/local/lib | sudo tee /etc/ld.so.conf.d/opencv331.conf sudo ldconfig5.5 cv2.version显示 4.2.0而不是 3.3.1现象明明源码编译并且安装了 3.3.1Python 导入的却是另一个版本。原因系统里存在多处 OpenCV 安装。Ubuntu 20.04 的 apt 包python3-opencv会安装到/usr/lib/python3/dist-packagespip 安装的会进/usr/local/lib/python3.8/dist-packages或~/.local。Python 在sys.path里按顺序查找谁排在前就用谁。解决打印cv2.__file__查看实际加载路径python3 -c import cv2; print(cv2.__file__)然后通过调整 PYTHONPATH 让 3.3.1 所在目录排在前面。如果不想和 apt 版本共存直接卸载 apt 版sudo apt remove python3-opencv卸载后重新执行ldconfig再导入确认版本。6. 验证与进阶拿 DNN 模块跑一个模型推理收尾6.1 编译后先跑一遍最小验证脚本环境配好后建议先跑一段覆盖基础能力的脚本确认编译产物没有功能缺失。验证脚本不需要复杂重点是 check 版本、数组读写、GUI 库、图像编解码是否都正常import cv2 import numpy as np print(OpenCV version:, cv2.__version__) img np.random.randint(0, 255, (300, 300, 3), dtypenp.uint8) img cv2.cvtColor(img, cv2.COLOR_RGB2BGR) cv2.imwrite(/tmp/test.png, img) mask cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) edges cv2.Canny(mask, 50, 150) print(edges shape:, edges.shape) net cv2.dnn.readNetFromCaffe(deploy.prototxt, mobilenet_iter_24000.caffemodel) print(dnn module works)这个脚本先验证基础图像处理链路imwrite成功说明 PNG 编码器正常Canny能跑说明核心算法模块没有缺失。最后一步加载一个 MobileNet SSD 的 Caffe 模型验证 dnn 模块可用。如果你手头没有现成的 caffemodel可以把最后两行注释掉不影响前面的验证。6.2 用 3.3.1 的 dnn 模块做物体识别OpenCV 3.3.1 的 dnn 模块已经支持加载 Caffe 模型做物体识别完全够用。下面是一个完整的最小推理流程import cv2 import numpy as np net cv2.dnn.readNetFromCaffe(deploy.prototxt, mobilenet_iter_24000.caffemodel) img cv2.imread(test.jpg) h, w img.shape[:2] blob cv2.dnn.blobFromImage(img, 0.007843, (300, 300), 127.5) net.setInput(blob) detections net.forward() for i in range(detections.shape[2]): confidence detections[0, 0, i, 2] if confidence 0.5: box detections[0, 0, i, 3:7] * np.array([w, h, w, h]) (x1, y1, x2, y2) box.astype(int) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(result.jpg, img)这里blobFromImage的缩放系数0.007843对应 MobileNet-SSD 原模型的归一化参数(300, 300)是模型输入尺寸127.5是均值。这些参数必须和模型训练时一致随意改动会直接导致识别置信度大幅下降。3.3.1 的 dnn 模块对 ONNX 的支持还很弱所以这个版本下优先使用 Caffe 格式的模型文件。我个人的习惯是每次搭完这种老版本环境第一时间把cv2.__version__、编译参数、Python 路径写进项目 README不然过两个月机器重装又要重新走一遍依赖排查。环境配置这类事情记录永远比记忆可靠。希望帮到你。本文还有配套的精品资源点击获取