Windows + MSVC 2022 + CMake 搭建 OpenCV 4.x 环境
发布时间:2026/10/1 21:13:26来源:尧图网络
Windows MSVC 2022 CMake 搭建 OpenCV 4.x 环境目标15 分钟内跑通第一段 C OpenCV 代码并且建立一套不污染系统环境的工程习惯。写作基线OpenCV 4.13.0 / MSVC 2022 (v143) / CMake 3.20 / Windows 10 x64⚠️本文的诚实声明本文写作时手边没有可用的 C OpenCV 环境只有 Python 侧因此CMake 配置与命令没有在本机实际编译验证过。文中的变量名与目录结构来自官方文档与社区实践凡是依赖具体发行版的细节我都会标注出来并给你一条能自己确认的命令。请把本文当作「有依据的路线图」而不是「已验证的流水账」。一、先选路线Windows 上装 OpenCV C 有三条路。先把结论放前面路线冷启动耗时含 contrib配置复杂度适合谁官方预编译包~5 分钟下载解压❌ 不含极低默认选这条vcpkg2060 分钟源码编译✅中需要 contrib 时MSYS2 / MinGW~15 分钟✅中用 MSVC 的人不要选第三条直接排除它产出的是 MinGW ABI 的二进制和你的 MSVC 2022 工具链不兼容。混用会得到一堆看起来毫无道理的链接错误。后面不再提。默认走官方预编译包。理由很简单初学者在「等编译」这件事上流失率最高而预编译包能覆盖本系列 24 篇里的 22 篇。真正需要 contrib 的只有SURF / BRIEF / FREAK3-1 的对照项可以用 ORB/SIFT 替代、cv::freetype1-4 画中文、ximgproc、cv::text—— 全是可选内容。二、路线 A官方预编译包2.1 下载与解压从 opencv.org 的 releases 页面下载 Windows 自解压包文件名形如opencv-4.x.0-windows.exe。解压到C:\opencv。第二条要求不是随手写的路径必须全 ASCII、不能有空格。放到D:\我的项目\opencv会让 CMake / MSBuild 报出一堆难以定位的噪音错误而且这些错误会和 01-02 要讲的中文路径问题叠加在一起排查成本成倍上升。图 1官方预编译包的典型目录结构。vc16/vc17的可用性随发行版变化以你解压后的实际内容为准。2.2 一个必须先搞清楚的概念OpenCV_DIR指向哪里这是新手最容易搞错的一点OpenCV_DIR指向含OpenCVConfig.cmake的那一层也就是C:\opencv\build不是指向具体的工具集子目录build\x64\vc17。因为 CMake 的find_package机制是这样的它会去OpenCV_DIR里找OpenCVConfig.cmake读这个文件然后由它来决定用哪个工具集、头文件在哪、要链哪些库。图 2CMake 定位 OpenCV 的变量流转。你只需要提供 OpenCV_DIR其余由 OpenCVConfig.cmake 解析。解压完先自己确认一下这个文件在哪dir /s /b C:\opencv\OpenCVConfig.cmake打印出来的路径去掉文件名就是OpenCV_DIR该填的值。三、第一个程序刻意不读文件。第一个例子只做三件事打印版本、生成一张图、显示出来。这样如果出问题一定是环境问题不可能是路径问题 —— 把两类问题解耦能省掉大量排错时间。3.1 工程结构hello_opencv\ ├── CMakeLists.txt └── src\ └── hello_opencv.cpp3.2src/hello_opencv.cpp// hello_opencv.cpp —— 本系列的第一个程序// 不读任何外部文件只验证头文件能找到、库能链上、运行时 DLL 能加载#includeopencv2/core.hpp// cv::Mat / cv::Scalar / 版本宏#includeopencv2/imgproc.hpp// cv::circle#includeopencv2/highgui.hpp// cv::imshow / cv::waitKey#includeiostreamintmain(){// ---- 1. 打印版本信息这是排错时最先要看的东西 ----std::coutCV_VERSION 宏 : CV_VERSIONstd::endl;std::coutcv::getVersionString : cv::getVersionString()std::endl;std::coutgetVersionMajor.Minor.Revision : cv::getVersionMajor().cv::getVersionMinor().cv::getVersionRevision()std::endl;#ifdef_DEBUGstd::cout构建配置: Debugstd::endl;#elsestd::cout构建配置: Releasestd::endl;#endif// ---- 2. 造一张图不依赖任何图片文件----constintwidth640;constintheight480;cv::Mat canvascv::Mat::zeros(height,width,CV_8UC3);// 注意 OpenCV 的默认通道顺序是 BGR不是 RGB。// cv::Scalar(b, g, r) —— 下面这个是橙色constcv::Scalarorange(0,165,255);cv::circle(canvas,cv::Point(width/2,height/2),// 圆心120,// 半径orange,cv::FILLED,// 实心cv::LINE_AA);// 抗锯齿cv::putText(canvas,OpenCV works!,cv::Point(180,60),cv::FONT_HERSHEY_SIMPLEX,1.0,cv::Scalar(255,255,255),2,cv::LINE_AA);// ---- 3. 显示 ----conststd::string winhello_opencv;cv::namedWindow(win,cv::WINDOW_AUTOSIZE);cv::imshow(win,canvas);std::cout按任意键退出...std::endl;cv::waitKey(0);cv::destroyAllWindows();return0;}两个细节值得单独说cv::Scalar(b, g, r)的顺序是 BGR。这是 OpenCV 的一个历史包袱几乎每个初学者都被它坑过一次。你写cv::Scalar(255, 0, 0)想得到红色实际上会得到蓝色。cv::waitKey(0)不能省。imshow只是把画布交给 GUI 层真正的事件循环在waitKey里。少了这一行窗口会一闪而过或者根本不出现。3.3CMakeLists.txtcmake_minimum_required(VERSION 3.20) project(hello_opencv LANGUAGES CXX) # ---------------------------------------------------------------- C 标准 # OpenCV 4.x 要求 C11 起5.0 要求 C17。 # 取 17 是唯一能同时覆盖 4.x 与 5.0 的档位详见 00-03。 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) # ---------------------------------------------------------------- MSVC 选项 if(MSVC) # /utf-8 必须加原因见第 5 节 add_compile_options(/utf-8 /W4) # CRT 必须与 OpenCV 一致。 # 官方预编译包与 vcpkg 默认都是「动态 CRT」也就是 /MD 与 /MDd。 # 这里若不设置MSVC 默认也是 /MD但显式写出来能防住以后有人往 /MT 改。 # 注意该变量需要 CMake 3.15且 cmake_minimum_required 3.15 才生效。 set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded$$CONFIG:Debug:DebugDLL) endif() # ---------------------------------------------------------------- 找 OpenCV # 官方预编译包配置时传 -DOpenCV_DIRC:/opencv/build # vcpkg传 -DCMAKE_TOOLCHAIN_FILE... # 若想同时接受 5.x把 4 去掉写成 find_package(OpenCV REQUIRED) find_package(OpenCV 4 REQUIRED) message(STATUS OpenCV 版本 : ${OpenCV_VERSION}) message(STATUS OpenCV_DIR : ${OpenCV_DIR}) message(STATUS 头文件目录 : ${OpenCV_INCLUDE_DIRS}) message(STATUS 库列表 : ${OpenCV_LIBS}) add_executable(hello_opencv src/hello_opencv.cpp) target_include_directories(hello_opencv PRIVATE ${OpenCV_INCLUDE_DIRS}) target_link_libraries(hello_opencv PRIVATE ${OpenCV_LIBS}) # ---------------------------------------------------------------- 部署 DLL # 治疗「编译通过、一运行就报找不到 opencv_world4xxx.dll」。 # 下面的路径按你的实际安装布局二选一 —— 先看上面 message 打印出来的 OpenCV_DIR # 再 dir 一下确认 bin 目录在哪然后解开对应的一行。 # # 官方预编译包 OpenCV_DIR/x64/vc17/bin 或 vc16 # vcpkg Releasevcpkg/installed/x64-windows/bin # vcpkg Debug vcpkg/installed/x64-windows/debug/bin set(OCV_DLL_DIR ${OpenCV_DIR}/x64/vc17/bin) if(WIN32 AND EXISTS ${OCV_DLL_DIR}) file(GLOB OCV_DLLS ${OCV_DLL_DIR}/*.dll) add_custom_command(TARGET hello_opencv POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${OCV_DLLS} $TARGET_FILE_DIR:hello_opencv COMMENT 复制 OpenCV 运行时 DLL 到输出目录) else() message(WARNING 未找到 ${OCV_DLL_DIR}请按你机器上的实际路径修改 OCV_DLL_DIR 否则运行时会报「找不到 opencv_world4xxx.dll」) endif()3.4 构建与运行cmake -S . -B build -G Visual Studio 17 2022 -A x64 -DOpenCV_DIRC:/opencv/build cmake --build build --config Release build\Release\hello_opencv.exe-G Visual Studio 17 2022是 2022 的生成器名如果你用 VS2019换成-G Visual Studio 16 2019。四、C 标准为什么定在 17这不是拍脑袋定的是被两个版本夹出来的消费者OpenCV 4.xOpenCV 5.0最低要求C11C17用 C11 能编过吗✅❌用 C17 能编过吗✅✅C17 是唯一能同时覆盖两代的档位所以全系列统一用它。这样你按本系列写的代码以后想升 5.0 时不需要改编译选项。一个额外的好处C17 带来了std::filesystem这是 01-02 解决「中文路径读不进来」的关键工具。关于 MSVC 的 ABI澄清一个中文网络上流传很广的误解你在 Linux/GCC 那边可能听过_GLIBCXX_USE_CXX11_ABI和std::string的 ABI 断裂问题 —— 那是 GCC 独有的。MSVC 的 STL 在/std:c14与/std:c17之间保持二进制兼容所以官方预编译的 4.x 可以被 C17 的消费者正常链接。在 Windows/MSVC 上你不需要为这件事焦虑。五、/utf-8一个不加就会出乱码的选项源文件里的中文字符串字面量需要有正确的编码处理。现象不加/utf-8时MSVC 会按系统 ANSI 代码页中文 Windows 上是 GBK/CP936去解析源文件。而你的源文件是 UTF-8 保存的 —— 于是中文字面量在编译期就被解错了运行时输出乱码。为什么这个坑特别难查它的症状和 01-04 要讲的「cv::putText画不出中文」一模一样都是乱码。但两者病因完全不同 —— 一个是编译选项问题一个是字体根本没有那些字形。你会很容易在错误的方向上折腾半天。解法CMake 里统一加/utf-8源文件保存为 UTF-8。上面CMakeLists.txt里那行add_compile_options(/utf-8)就是干这个的。顺带一句如果你有 NX 8.5 的老工程那边的结论是相反的 —— 老工具集需要 UTF-8带 BOM。两套工程的标准不同别互相套用。六、ABI 与运行时四个必须一致的维度这一节是本文最值钱的部分。C 的链接错误大多不是「代码写错了」而是四个维度里有一个没对齐。维度规则不一致的后果架构全线 x64LNK1112: module machine type conflict或运行时0xc000007b配置Debug 配opencv_worldverd.libRelease 配不带d的LNK2038: _ITERATOR_DEBUG_LEVEL mismatch运行库统一动态 CRT/MD//MDdLNK2038: RuntimeLibrary mismatch也可能是链接通过但运行时堆损坏工具集v140–v143 二进制兼容但优先选匹配的那个混用时可能表现为「Debug 下cv::Mat是空的」这类诡异现象第一条特别提醒用它自己的篇幅讲Visual Studio 新建 C 项目时默认平台是 Win32x86而且Debug 和 Release 两个配置要分别改。这是 Windows 上 C 新手的头号坑 —— 改完 Debug 忘了改 Release于是「调试能跑、发布就崩」。而近年的官方预编译包只提供 x64。所以你可能遇到x86 的工程去链 x64 的库报一个和「架构」两个字毫无关系的错误。诊断命令:: 看一个 exe/dll 到底是什么架构 dumpbin /headers your.exe | findstr machine :: x64 - machine (x64) :: x86 - machine (x86)出错时先跑这一条能省半小时。七、DLL 部署为什么「编译通过但跑不起来」这是 C 新手最困惑的一类问题根源是一句容易被忽略的话链接期找的是.lib运行期找的是.dll。这是两件互相独立的事。find_package(OpenCV)只解决前者。程序跑起来时Windows 加载器会按自己的固定顺序去找.dll完全不关心你的 CMake 配置。图 3编译期是可预测、有报错的运行期是静默的。Windows 加载器的搜索顺序默认顺序简化版exe 所在目录← 最可靠系统目录System32\Windows 目录当前工作目录PATH环境变量本系列统一采用第 1 条用 CMake 的POST_BUILD命令把 DLL 拷到 exe 同目录上面CMakeLists.txt已实现。这样做的三个好处不污染系统PATH在 VS 调试器、VS Code、双击、命令行四种运行方式下都成立换一台机器直接把整个输出目录拷过去就能跑最容易被漏掉的一个文件opencv_videoio_ffmpeg版本_64.dll。从 OpenCV 4.1 起videoio 后端可以作为运行时插件加载FFmpeg 就是以插件形式存在的。这个 DLL 缺失时的症状非常具有迷惑性图像读写一切正常只有视频打不开。你会以为是代码写错了其实是少拷了一个文件。01-02 会精确命中这个坑。常见错误码对照表症状病因弹窗「找不到 opencv_world4xxx.dll」DLL 不在搜索路径0xc000007b(STATUS_INVALID_IMAGE_FORMAT)架构不匹配x86 DLL 混进了 x64 进程或依赖链不一致0xC0000135缺少vcruntime140.dll/msvcp140.dll—— 装 VC RedistributableLNK1107: invalid or corrupt fileDebug/Release 的 lib 用错了LNK2019: unresolved external symbollib 名拼错或平台选成了 Win32LNK2038: RuntimeLibrary mismatch/MD与/MT混用LNK2038: _ITERATOR_DEBUG_LEVEL mismatchDebug 与 Release 混用LNK1112: module machine type conflictx86 库链到 x64 目标八、路线 Bvcpkg需要 contrib 时预编译包不含 contrib。如果你确实需要 SURF、cv::freetype或ximgproc用 vcpkg。端口名是opencv4不是opencvopencv是跟随最新大版本的元端口。推荐用manifest 模式项目内vcpkg.json可以固定版本、可复现{name:opencv-blog-samples,version:0.1.0,dependencies:[{name:opencv4,default-features:true,features:[contrib,ffmpeg]}]}配置命令cmake -S . -B build -G Visual Studio 17 2022 -A x64 ^ -DCMAKE_TOOLCHAIN_FILEC:/vcpkg/scripts/buildsystems/vcpkg.cmake ^ -DVCPKG_TARGET_TRIPLETx64-windows四个已知的坑VCPKG_TARGET_TRIPLET必须显式写成x64-windows。vcpkg 默认是 x86不指定就会装出一个 32 位的库然后你在链接时看到 machine type 冲突。default-features在 x64-windows 上已包含dnn/jpeg/png/tiff/webp但不含contrib和ffmpeg—— 这两个必须显式列出否则 03-05 的 DNN 示例能跑、3-1 的 SURF 却编不过。如果之前跑过vcpkg installclassic 模式残留的vcpkg_installed/目录会让 CMake 误判并忽略vcpkg.json。解法是删掉build/和vcpkg_installed/后重新配置。Visual Studio 2022 内置的 vcpkg 只支持 manifest 模式而且它的目录在 VS 安装目录下可能没有写权限。关于编译时间vcpkg 是从源码编译 OpenCV 的冷启动 2060 分钟视 CPU 核数磁盘占用 3 GB 起建议预留 10 GB 以上。做好心理准备泡杯茶。九、这套方案什么时候会失败需要 contrib 模块时。预编译包不含 contrib必须切到 vcpkg 或自编译。本系列 24 篇里只有 1-4画中文和 3-1SURF 对照会碰到这个问题且都有替代方案。需要 32 位x86目标时。近年官方预编译包只提供 x64。如果你必须产出 32 位程序得走 vcpkg 或自编译。需要静态链接到单个 exe 时。预编译包提供的是动态库。静态链接需要自编译BUILD_SHARED_LIBSOFF。公司内网无法访问外网时。下载预编译包或 vcpkg 拉源码都会失败需要提前准备离线包。用 Visual Studio IDE 而不是 CMake 时。本文讲的是 CMake 路线。如果你要在 VS 里手工建工程需要在「属性 → VC 目录」里填 include 和 lib 路径、在「属性 → 链接器 → 输入」里填 lib 名 —— 但 CMake 路线可复现性更好建议优先。十、Python 对照Python 侧这件事只需要一行pip install opencv-python4.13.0.88# 版本号与 C 侧对齐importcv2print(cv2.__version__)# 4.13.0print(cv2.__file__)# 装在哪儿三个值得知道的差异Python 包自带全部运行时 DLL不需要处理 DLL 搜索路径问题。这是 Python 在这个环节唯一也是最大的优势。opencv-python与opencv-contrib-python是两个不同的包。需要 contrib 时装后者且两个包不能同时装—— 会互相覆盖症状是某些函数时有时无。import 名是cv2而不是opencv。这个名字来自已经消失的 OpenCV 2 时代纯属历史遗留官方为了兼容性一直没改。第一次接触的人常常找不到对应关系。十一、待实测清单以下几条本文未能实测本机无 C OpenCV 环境建议你装好后花五分钟验证一遍有出入以你本地为准你下载的那一版预编译包里x64\下到底是vc16还是vc17或者两者都有OpenCVConfig.cmake的确切位置跑一次dir /s /b C:\opencv\OpenCVConfig.cmakebin\目录的确切路径据此修改CMakeLists.txt里的OCV_DLL_DIRopencv_videoio_ffmpeg*_64.dll是否随包提供message(STATUS ...)打印出的OpenCV_LIBS是opencv_world还是拆分的多个库
网站建设高端定制企业官网