简介:从源代码构建OpenCV是许多Windows开发者避开ABI兼容陷阱的通用思路。MSVC与MinGW采用不同的C++运行时和链接库格式,官方预编译包无法直接在GCC工具链下使用。通过CMake生成MinGW Makefiles工程,可以控制模块选择、关闭非必要加速项,并经本地缓存解决第三方依赖下载问题。编译完成后,在Qt 5.15或Qt 6工程中通过find_package指向独立安装目录,即可完成图像读写、滤波、视频处理等能力的集成。本文以OpenCV 4.8.0为主线,给出从环境选型、CMake参数配置到常见编译错误的完整实践记录,帮助需要构建自有MinGW版本OpenCV的开发者显著缩短踩坑时间。
1. 为什么要自己编译 OpenCV 4.8.0 + MinGW
装好 Qt 之后想用 OpenCV,很多人第一步是去官网下载 exe 安装包,双击、下一步、完成,然后回到 Qt 里一链接,扑面而来几十个undefined reference。原因很简单:官方 Windows 预编译包是用 MSVC 编译的,和 MinGW 工具链根本不是一套 ABI。OpenCV 4.8.0 + MinGW 编译这件事,就是让你用 GCC 工具链从源码构建出一套能放进 Qt 工程里的动态库,把opencv_core、opencv_imgproc这些库的 MinGW 版本真正拿到手。这篇笔记把从环境准备、CMake 配置、编译命令到 Qt 集成验证的完整路径写出来,顺带记录我在这个版本上踩过的坑。适合正在用 Qt 5.15 + MinGW 开发、以及想在 Windows 上拿到一套纯 GCC 构建 OpenCV 的人。
2. 选型:为什么是 4.8.0,为什么 MinGW 8.1.0
2.1 官方预编译包在 MinGW 下用不了的根因:MSVC 和 MinGW 的 ABI 差异
要理解这个标题为什么存在,先得说清楚 msvc 和 mingw 区别。MSVC 编译出的静态库是 COFF 格式,链接库后缀是.lib;MinGW 用的是 GNU 格式,导入库后缀是.dll.a。两套工具链的 C++ 标准库实现也不同——MSVC 对应vcruntime,MinGW 对应libstdc++,名字修饰规则在 C++ 层面虽然都是 Itanium ABI(MinGW 用)和 MSVC 自己的命名规则,但二进制不互通。
OpenCV 4.8.0 官方 Windows 包提供的是 vc16/vc17 版本,也就是 Visual Studio 2019/2022 编译的产物。你即便把.lib文件强行拷给 MinGW 链接器,结果也是大量符号找不到或类型不匹配。所以“OpenCV + MinGW”只能走自己编译这条路。而 4.8.0 这个版本恰好在 CMake 配置上已经趋于稳定:它所需的第三方依赖下载路径、构建脚本、模块划分都比 3.x 时代规整,在 Windows 上用 MinGW 编译的教程也最多,遇到问题相对好搜。
2.2 选 MinGW 8.1.0 而不是 13.x:线程模型与异常模型怎么定
很多人在这一步纠结“是不是越新的 GCC 越好”。我一般不建议用最新版 GCC 去编 OpenCV 4.8.0。我自己踩过 GCC 13 在编译部分 OpenCV 模块时出现Internal Compiler Error的坑,换回 8.1.0 一次通过。对于 4.8.0 这个时间节点的版本,Qt 5.15.2 官方安装包自带的 MinGW 8.1.0 是最省事的组合,和 OpenCV 4.8.0 几乎没有兼容性问题。
MinGW 8.1.0 的分发包里有几个细节值得注意。第一是线程模型,有posix和win32两个版本:win32模型不完整支持std::thread,OpenCV 底层的并发原语跑起来容易出问题;posix模型对标准库支持更全,OpenCV 里的并行模块能正常调度。第二是异常模型,64 位环境下选seh,32 位才需要考虑sjlj或dwarf。最直接的办法:如果你已经装了 Qt 5.15.2,用它安装目录下的Tools/mingw810_64就行,这个自带版本就是 x86_64、posix、seh,不需要另外去 MinGW 官网下载安装。
下面这个表是我在 OpenCV 4.8.0 上实测过的主要 MinGW 版本表现,给你做选型参考:
| MinGW 版本 | 与 4.8.0 兼容性 | 适合场景 | 备注 |
|---|---|---|---|
| GCC 8.1.0 (posix, seh) | 稳定 | Qt 5.15 + OpenCV 4.8.0 开发 | 首选,社区资料最多 |
| GCC 11.2 / 12.x (posix, seh) | 稳定 | Qt 6.2 + OpenCV 4.8.0 | 需要手动安装或随 Qt6 自带 |
| GCC 13.x (posix, seh) | 有概率翻车 | 新工具链测试环境 | 遇到 ICE 时先关优化级别再试 |
2.3 编译前要备好的四样东西
开始编译之前,把这几样先确认齐了,能省半小时排查时间。
第一是 CMake。OpenCV 4.x 对 CMake 版本要求不高,3.20 左右都行,但别用太老比如 3.10,很多新选项识别不了。第二是 MinGW,路径建议放在纯英文目录,比如D:/Qt/Tools/mingw810_64,不要往带空格带中文的路径里放。第三,如果想要 Python 绑定,额外装对应版本的 Python 和 NumPy;只想要 C++ 接口的话,在 CMake 配置里关掉 Python 相关选项即可。第四是网络,配置阶段 OpenCV 要从 GitHub 和 SourceForge 拉 IPP、FFMPEG 等第三方包,这部分下载失败是最常见的卡点,后续我单独写一节处理办法。
PATH 顺序也有讲究:把mingw810_64/bin放在系统 PATH 前面,确保命令行里gcc、g++、mingw32-make定位到的是同一个工具链,避免同时装了多个 GCC 时版本错乱。可以用gcc --version验证一次,确认当前生效的就是你要用的那个。
3. 用 CMake + MinGW Makefiles 跑通 OpenCV 4.8.0:完整配置与编译命令
3.1 最小可用的编译脚本:从源码目录到 install
下面是我在 OpenCV 4.8.0 上实测可用的配置命令。先建一个独立的构建目录,不要把编译产物直接放到源码树里,这是 OpenCV 官方支持的规范做法,也为后面增量重编留了余地。
# 进入 OpenCV 源码根目录,创建独立构建目录 cd opencv-4.8.0 mkdir build-mingw && cd build-mingw # 配置工程,生成 MinGW Makefiles cmake .. \ -G "MinGW Makefiles" \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=D:/libs/opencv-4.8.0-mingw \ -DCMAKE_MAKE_PROGRAM=D:/Qt/Tools/mingw810_64/bin/mingw32-make.exe \ -DCMAKE_C_COMPILER=D:/Qt/Tools/mingw810_64/bin/gcc.exe \ -DCMAKE_CXX_COMPILER=D:/Qt/Tools/mingw810_64/bin/g++.exe \ -DBUILD_TESTS=OFF \ -DBUILD_PERF_TESTS=OFF \ -DBUILD_EXAMPLES=OFF \ -DBUILD_opencv_world=ON \ -DWITH_IPP=OFF \ -DWITH_OPENCL=OFF \ -DWITH_FFMPEG=ON \ -DOPENCV_ENABLE_PRECOMPILED_HEADERS=OFF # 开始编译,-j 后面跟的数字参考物理核心数 mingw32-make -j8 # 安装到指定前缀目录,包含 include / x64/mingw/lib / x64/mingw/bin mingw32-make install这段命令最关键的是-G "MinGW Makefiles"。如果不指定,CMake 在 Windows 上默认生成 Visual Studio 工程,MinGW 的mingw32-make根本读不了。CMAKE_MAKE_PROGRAM必须显式指向mingw32-make.exe的完整路径,CMake 在 MinGW 环境下有时自我探测失败,不写的话配置阶段就会报错找不到 make 程序。
CMAKE_INSTALL_PREFIX用来指定最终安装位置,我习惯装到D:/libs/opencv-4.8.0-mingw这类独立目录,和源码、构建目录分离。后续在 Qt 工程里通过find_package(OpenCV)引用时,指向的就是这个前缀下的x64/mingw/lib。编译阶段先关掉测试和示例能明显缩短时间;BUILD_opencv_world=ON会把所有模块合并成一个大库,部署时少拷贝一堆 dll,但代价是每次改模块都要重链这个大库,个人开发可以设 OFF。
3.2 值得单独调整的 5 个 CMake 开关
配置阶段不是所有默认值都适合 MinGW 编译场景,下面这几个开关我每次都会过一遍。
| CMake 开关 | 默认值 | 推荐值 | 原因 |
|---|---|---|---|
BUILD_LIST | 空(全模块) | 按需填写 | 只编需要的模块能大幅缩短编译时间 |
WITH_IPP | ON | OFF | IPP 是 Intel 的闭源加速包,MinGW 下配置容易卡下载,关掉不影响功能 |
WITH_OPENCL | ON | OFF | 打开后 OpenCL 相关代码会尝试加载显卡驱动 ICD,MinGW 下兼容性问题多于收益 |
OPENCV_ENABLE_PRECOMPILED_HEADERS | ON | OFF | MinGW 下 PCH 容易产生莫名其妙的依赖错误,关闭更稳定 |
BUILD_opencv_world | OFF | 按需 | 想要单 dll 发布就 ON,想加快重编就 OFF |
BUILD_LIST是省时间的利器。比如你只需要图像读取和基本处理,可以写成-DBUILD_LIST=core,imgproc,imgcodecs,videoio,编译时间能从两小时压到十几分钟。但要注意,highgui模块依赖imgproc和imgcodecs,如果你要显示图像窗口,highgui也要加进去。模块之间的依赖关系,配置完成后的输出日志会列清楚,漏掉依赖时 CMake 会直接提示缺哪个模块。
3.3 编译完成后的目录结构:哪些文件是给谁用的
mingw32-make install完成之后,到D:/libs/opencv-4.8.0-mingw下的结构是:
include/opencv2/... 头文件,编译时用 x64/mingw/bin/opencv_core480.dll 运行时 dll x64/mingw/bin/opencv_imgproc480.dll x64/mingw/lib/libopencv_core480.dll.a 链接时用的导入库 x64/mingw/lib/libopencv_imgproc480.dll.a x64/mingw/lib/OpenCVConfig.cmake CMake 的 find_package 入口注意 4.8.0 的库命名规则是主版本加次版本,所以是480而不是4.8.0,实际文件以你本机 install 出来的为准。MinGW 生成的导入库都是lib前缀加.dll.a后缀,这和 MSVC 的.lib命名不同,但用法上 CMake 的${OpenCV_LIBS}会自动处理好。运行时,bin目录必须加入PATH,否则编译出来的 exe 启动时找不到 dll,这属于新手最容易忽略的一步。
4. 避坑:OpenCV 4.8.0 + MinGW 编译最常见的 5 个翻车点
4.1 配置阶段卡在 IPPICV 下载失败
现象:cmake 执行到IPPICV: Downloading时长时间不动,最后报超时或校验和不匹配,整个配置中断。
原因:OpenCV 在 Windows 上默认集成 Intel IPP 加速包,第三方包不是随源码发布的,而是在配置阶段从 GitHub 实时下载。网络环境不好时,这个下载就是第一个拦路虎。
解决:手动下载。打开opencv-4.8.0/3rdparty/ippicv/ippicv.cmake,找到文件头部的OPENCV_IPPICV_URL,用浏览器把对应版本的 zip 包下载到本地;然后回到 build 目录,把 zip 放到.cache/ippicv/下,文件名改成 cmake 日志里提示的哈希值名称。这样 CMake 检测到本地缓存,就不会再走网络下载。之后只要你不删除 build 目录,这个缓存会一直在,重编不会二次卡住。
4.2 编译成功但 VideoCapture 打不开 mp4:FFMPEG 下载失败
现象:编译一切正常,代码里imread读图片也没问题,但VideoCapture("test.mp4")返回空,打开失败。
原因:OpenCV 的视频解码依赖 FFMPEG 预编译包。Windows 下编译时如果 FFMPEG 相关 dll 没有成功下载或没被放进最终可执行环境,视频读取就在运行时静默失败。配置阶段如果 FFMPEG 下载失败,CMake 不会报错停掉,而是关闭WITH_FFMPEG继续。
解决:两个方向。第一个是手动下载 FFMPEG 的 dll:查一下 build 目录下3rdparty/ffmpeg里生成的下载地址,下载后放到install/x64/mingw/bin。文件名类似opencv_videoio_ffmpeg480_64.dll,这是运行时动态加载的,exe 启动时必须能在PATH或当前目录找到它。第二个是省事方案:在 CMake 配置里显式加-DWITH_FFMPEG=OFF,视频读取改用 Windows 自带的MSMF后端。需要注意,关掉 FFMPEG 后部分编码格式的读取和写入能力会受限,测试时尽量用 MP4 或 AVI 之外的常规格式验证。
4.3 GCC 13 编译时报 Internal Compiler Error
现象:编译到某个模块时报Internal Compiler Error,或者 GCC 进程崩溃,日志里没有任何明确错误指向。
原因:OpenCV 4.8.0 发布时 GCC 13 尚未普及,OpenCV 某些模板元代码在 GCC 13 的实例化阶段会触发内存占用暴涨甚至崩溃。这个问题在旧版本 OpenCV 配合过新工具链时比较常见。
解决:优先换回 GCC 8.1.0,也就是 Qt 5.15.2 自带的 MinGW,兼容性最稳。如果因为项目其他依赖必须用 GCC 13,可以尝试在配置时降低优化级别,比如-DCMAKE_CXX_FLAGS_RELEASE="-O1",有概率绕开编译器崩溃,但实测编译时间会变长,运行性能也有损失。我的建议是:4.8.0 的周边生态,选 8.1.0 而不是最新版。
4.4 路径带空格或中文导致编译命令错乱
现象:cmake 配置通过,但mingw32-make跑到一半报找不到文件,或提示No such file or directory,路径在输出里看起来被截断了。
原因:MinGW 的 make 对路径中的空格和中文支持不完善。源码目录、build 目录、安装目录只要一个带空格或中文,编译过程中脚本拼接路径时就容易翻车。
解决:确保opencv-4.8.0源码、build-mingw、CMAKE_INSTALL_PREFIX三个路径全用纯英文、无空格的短路径,比如D:/build/opencv。这里没有玄学,就是 Windows 下 GNU 工具链的经典脾气,路径越短越干净它越稳定。
4.5 改一个模块参数却全量重编:缓存被 CMakeCache 坑了两次
现象:第一次编译已经完成,想加一个新的模块,直接在同一个 build 目录里重新跑 cmake 加参数,然后make发现几乎所有模块都重新编译了。
原因:在已有构建目录里修改BUILD_LIST等选项,CMake 会认为配置整体变化,把原本已经编好的模块目标全部重新生成。部分情况下直接改参数还会残留旧缓存,导致链接到一半的旧目标文件和新配置混在一起。
解决:每次变更关键配置,新建一个 build 目录,比如build-mingw-full、build-mingw-core,不同配置各用各的构建目录。.cache目录可以通用——把上一次成功编译的 build 目录下的.cache复制到新 build 目录下,已经下载过的 IPP、FFMPEG 包就不用重新下载了。这是最快的后悔药。
5. 验证与集成:在 Qt/MinGW 工程里确认 OpenCV 真正可用
5.1 写一个最小的 OpenCV 验证程序
编译完成后,先别急着接进 Qt 工程,写一个十几行的小程序验证库本身能不能用。
#include <opencv2/opencv.hpp> #include <iostream> int main() { // 读取一张测试图片,路径换成你自己机器上的 cv::Mat src = cv::imread("D:/test.jpg"); if (src.empty()) { std::cout << "imread failed" << std::endl; return -1; } // 转灰度再模糊,验证 imgproc 模块工作 cv::Mat gray; cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); cv::Mat blurred; cv::GaussianBlur(gray, blurred, cv::Size(5, 5), 0); // 保存结果,验证 imgcodecs 模块工作 cv::imwrite("D:/test_blur.jpg", blurred); std::cout << "size: " << src.cols << "x" << src.rows << std::endl; return 0; }这段程序同时覆盖了imgcodecs(读图、写图)、imgproc(色彩转换、高斯模糊)这几个最常用模块。如果这几个模块跑通,你的 MinGW 版 OpenCV 基础功能就没有问题。编译这条程序时用下面这段 CMakeLists,可以直接打包进 Qt 工程,也可以单独用 cmake 测。
cmake_minimum_required(VERSION 3.16) project(opencv_mingw_demo) # 指向 install 目录下的 lib 文件夹,里面放着 OpenCVConfig.cmake set(OpenCV_DIR "D:/libs/opencv-4.8.0-mingw/x64/mingw/lib") find_package(OpenCV REQUIRED) add_executable(demo main.cpp) target_link_libraries(demo ${OpenCV_LIBS})find_package(OpenCV REQUIRED)能成功的前提是OpenCV_DIR指向的路径里有OpenCVConfig.cmake。如果不写这个变量,CMake 会按默认路径找,十有八九找不到。链接时用${OpenCV_LIBS}这个变量,CMake 自动展开成所有要链接的.dll.a文件,不需要手动写这些库名。
5.2 在 Qt Widgets 工程里接入 OpenCV
Qt 工程里接入编译好的 OpenCV,CMake 写法和上面基本一致。如果你用 qmake,核心配置是这些:
INCLUDEPATH += D:/libs/opencv-4.8.0-mingw/include LIBS += -LD:/libs/opencv-4.8.0-mingw/x64/mingw/lib \ -lopencv_core480 \ -lopencv_imgproc480 \ -lopencv_imgcodecs480qmake 的LIBS写法里,-l后面跟的是libopencv_core480.dll.a去掉前缀和后缀的中间部分。如果你用 CMake + Qt,那直接把上一节的CMakeLists.txt嵌进去,Qt 6 和 Qt 5 都适用。两种情况编译没问题后,运行时都要把D:/libs/opencv-4.8.0-mingw/x64/mingw/bin加入系统PATH,或者把bin里用到的 dll 拷贝到 exe 所在目录。
这里还容易踩一个隐蔽坑:如果系统里同时装了 MSVC 编译的 OpenCV,Qt 工程链接时把两个版本的库目录都写在路径里,链接器优先匹配到.lib或错误的.dll.a,会出现莫名其妙的重复符号。我习惯只把 MinGW 版的lib目录写进工程,不给链接器留选择余地。
5.3 验证链接的确是 MinGW 版而不是 MSVC 版
MinGW 和 MSVC 编译的 OpenCV 在 API 上完全相同,所以代码编译通过不代表你链接的东西一定对。最可靠的验证方式是在程序里打印cv::getBuildInformation():
std::cout << cv::getBuildInformation() << std::endl;这段输出里,重点看Compiler一段。如果显示GNU加 GCC 版本号,说明这套库确实是 MinGW 工具链编译的;如果显示MSVC,说明你find_package或LIBS路径里混入了官方预编译包,属于交叉误用,后续迟早出问题。再看一眼Media I/O段里的FFMPEG是否字段,确认视频后端是否如你配置的那样开启了。这个打印建议保存在测试代码里,以后换库、换机器时随时能查。
6. 增量重编的提速经验:把二次编译时间压到 20 分钟内
OpenCV 全量编译一次在普通桌面 CPU 上大概一到两个小时,但如果只是日常开发里小改配置或加个模块,完全没必要次次全量。这里分享三个我一直在用的提速习惯。
第一个习惯:编译时按物理核心数加一给-j参数。比如 8 核 16 线程的 CPU,mingw32-make -j9通常是最稳妥的平衡点。不要盲目拉满-j32,GCC 在 OpenCV 这种大型项目里内存占用很高,开太多线程会把内存吃满,反而触发编译器崩溃或系统卡死。
第二个习惯:用BUILD_LIST保留一个“最小构建集”。我在build-mingw-core目录里专门保存了一份只编核心模块的配置:
cmake .. \ -G "MinGW Makefiles" \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_LIST=core,imgproc,imgcodecs,highgui,videoio \ -DWITH_IPP=OFF \ -DWITH_OPENCL=OFF \ -DBUILD_TESTS=OFF \ -DBUILD_EXAMPLES=OFF这份配置从零编译大约十五分钟,足够日常图像处理调试用。一旦需要完整功能,再切换到全量构建目录,两份产物互不干扰。
第三个习惯:不要把.cache目录随手删掉。它保存了 IPP、FFMPEG 等所有配置阶段下载过的第三方包,是整个编译过程里唯一会重新从外网下载的东西。换 build 目录时,把旧目录的.cache整体复制过去,配置阶段就不再触发任何下载。我现在拿到新版 OpenCV 后的第一件事,是先把 3rdparty 里所有会外网下载的包挨个存进.cache,再跑 cmake——这两步做完,后面基本静音,希望帮到你。
本文还有配套的精品资源,点击获取