ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

VS2019配置Qt与OpenCV:环境搭建、代码实现与踩坑全记录

VS2019配置Qt与OpenCV:环境搭建、代码实现与踩坑全记录 简介面向在Visual Studio 2019中整合Qt与OpenCV的开发者这份实例演示了从环境配置到图像加载、显示与处理的全过程。工程基于Qt Widgets模板清晰展示了OpenCV的Mat与Qt的QImage之间的转换方法并附有灰度化、平滑滤波等常用操作示例适合计算机视觉初学者快速上手也为有经验的开发者提供可复用的工程结构参考。压缩包共46个文件主要包括C源文件与头文件、Qt界面设计文件ui、项目配置文件vcxproj、sln、props以及测试图片、编译日志和可执行程序exe整体大小12.34MB可直接打开sln工程查看代码并运行验证。已有867人学习下载配置说明完整目录结构清晰。通过实际运行和修改代码读者能直观理解OpenCV处理结果在Qt界面中的实时更新方式从而掌握两种工具协同开发的基本思路与调试技巧。1. 在VS2019里打通Qt和OpenCV难点从来不在写代码而在环境打开搜索引擎一半人在问“VS2019配Qt哪个版本”“OpenCV怎么装”另一半人已经配好环境却被一条fatal: cannot mix incompatible qt library卡在原地。VS2019、Qt、OpenCV这三样单独拿出来都不难难的是让Windows下的MSVC编译器、Qt的元对象系统、OpenCV的预编译库在同一套工程里互相认账。这篇文章不打算讲泛泛的图像处理原理而是带你走一遍我常用的完整落地路径版本怎么选、工程怎么建、代码怎么写、踩坑怎么排最终让一张图片从文件读到界面显示再经过OpenCV处理回到界面全程在VS2019里跑通。适合刚接触QtOpenCV组合、或者在环境配置上反复翻车的C开发者。2. 版本选型是第一个坑VS2019、Qt 5.15.2、OpenCV 4.x 的对应关系2.1 为什么是64位加MSVC而不是MinGW先把结论放在前面在VS2019里测Qt和OpenCV唯一省心的组合是“Windows 64位 MSVC编译器 Qt 5.15.2 msvc2019_64 OpenCV官方Windows预编译包”。这个组合里的每一环都有明确理由。VS2019默认使用v142工具集对应的C运行时是Universal CRTQt官方针对这个工具集发布了msvc2019_64套件。注意看套件名字里的msvc三个字母它明确告诉你要配合Visual Studio使用。另一个常见套件mingw73_64用的是GCC编译器生成的代码和MSVC互不兼容如果你在VS里选错了套件链接阶段会出现一大堆unresolved external symbol这类报错和代码一行关系都没有纯粹是工具链错位。我见过有人在这上面耗了一下午反复清理重编最后发现属性管理器里选中的是MinGW的Qt库目录。位数同样别将就。现在的OpenCV预编译包在build/x64下提供64位库文件Qt 5.15.2的msvc套件默认也是64位。32位方案要额外下载32位Qt包、32位OpenCV包还要把VS的解决方案平台切成Win32收益为零手续费一堆。直接全部64位Debug和Release都选x64后面几乎所有DLL加载问题都不会出现。2.2 OpenCV选预编译包还是自己用CMake编译标题里的“测试qt和opencv”告诉你目标只是验证链路不是做OpenCV二次开发。那OpenCV直接用官方发布的Windows预编译包就好解压到一个路径配置好包含目录和库目录就能用。没必要走cmake编译步骤除非你明确需要CUDA加速、contrib扩展模块、或者想裁剪库体积。顺带说明一下“安装OpenCV”这件事的正确理解Windows下的OpenCV没有传统意义的安装程序官方给的zip包解压后就是opencv_dir\build里面是include头文件、x64\vc15\lib静态库/导入库、x64\vc15\bin运行DLL。VS2019的v142工具集可以直接使用新版vc15/vc16库。如果你查到的教程让你先安装CMake、再折腾源码编译那个流程适合改源码的人不适合验证集成。只有在你需要opencv_cuda这类预编译包里没有的组件时才值得碰源码编译。2.3 目录规划和路径约定环境问题里有一类特别隐蔽不是没配而是配了多个版本编译器拿到的是A版本头文件、B版本库文件。我一般会强制自己遵守一套目录约定避免这种混乱。一个干净的目录长这样C:\Dev\ opencv\ # OpenCV解压目录 build\ Qt\5.15.2\ msvc2019_64\ Workspace\ # 放你的VS工程Qt和OpenCV都放C盘固定路径、固定名字别放Program Files权限容易出幺蛾子也别在系统PATH里同时挂两个OpenCV版本。版本字符串真乱起来非常头痛打开Visual StudioQt VS Tools里显示一个Qt版本项目属性里的Qt Installation又指到另一个版本两个地方不一致时链接器拿到的库混着来报错信息往往莫名其妙。后文避坑章节会专门讲cannot mix incompatible qt library这条经典报错根源十有八九就在这里。大原则记三条只用64位、只用MSVC套件、全机只留一份Qt和一份OpenCV。做到这三条等于排掉了后期一半的疑难杂症。3. 在VS2019里建Qt工程并配置OpenCV从安装到链接3.1 先装对VS2019的工作负载和Qt VS Tools开始配工程前先确认VS2019本体装了什么。打开Visual Studio Installer查看“使用C的桌面开发”这个工作负载是否打了勾。没有它就没有MSVC编译器和Windows SDK后面Qt项目连编译按钮都是灰的。然后安装Qt VS Tools扩展。VS2019菜单栏的“扩展→管理扩展”里搜索“Qt Visual Studio Tools”装上后重启VS菜单栏会出现一个“Qt VS Tools”入口。这个扩展是Qt官方维护的Visual Studio集成工具负责识别Qt安装路径、帮你生成带.ui文件的Qt Widgets项目、自动注入moc元对象编译步骤。没有它你也可以手写一个Qt工程但moc和资源文件的生成规则要自己配非常不划算。提示VS2019社区版对个人和小团队是免费的本地学习完全够用不需要为企业版许可证去折腾。3.2 创建Qt Widgets项目并设置Qt版本Qt VS Tools安装好之后在VS2019里新建项目时会出现Qt分类。选择“Qt Widgets Application”给项目起名比如QtOpenCVDemo。向导会让你选Qt模块默认的core、gui、widgets就够了不要勾一堆用不上的模块链接时间会变长还容易引入版本不一致的风险。项目创建后第一件事配置Qt版本。在“Qt VS Tools→Qt Versions”里添加你的Qt路径C:\Dev\Qt\5.15.2\msvc2019_64它会自动识别出5.15.2 msvc2019_64。注意这一步和项目属性里看到的Qt设置是两套配置经常有人只配项目属性不配全局版本或者反过来。保险起见全局Versions里注册之后打开项目属性找到Qt相关页签确认Qt Installation下拉框选中的也是同一条路径。之后右键项目名选“Qt→Project Settings”你能看到这个工程实际链接了哪些Qt模块。通常QtCore、QtGui、QtWidgets三个打勾即可。这一页保存后VS会自动把对应的Qt头文件目录、库文件和DLL目录加进项目属性不需要你手工维护$(QTDIR)之类的环境变量。3.3 OpenCV的包含目录、库目录与附加依赖项Qt说完接着配OpenCV。先解压OpenCV到C:\Dev\opencv然后打开项目属性页右键项目→属性切到“VC 目录”“包含目录”里追加C:\Dev\opencv\build\include“库目录”里追加C:\Dev\opencv\build\x64\vc15\lib注意库目录的路径里必须带x64这是32位/64位最容易错的地方。如果你的OpenCV压缩包解出来vc15不存在而是vc16也正常VS2019对应使用即可。然后切到“链接器→输入→附加依赖项”这里分Debug和Release两套配配置附加依赖项Debugx64opencv_world470d.libReleasex64opencv_world470.lib那个d后缀是Debug库的标记很多人漏掉它。Debug配置下加不带d的库编译能过运行时会提示“找不到opencv_world470d.dll”反过来Release配置加了带d的库会报_ITERATOR_DEBUG_LEVEL不匹配的C运行库错误。版本号按你实际下载的版本写4.5、4.8、4.10都行但Debug和Release的两个文件名只能差一个字母d这点不能变。3.4 运行时DLL用PATH还是拷贝到exe目录编译完成后项目能生成exe但双击运行大概率提示缺少opencv_world470.dll或Qt5Widgets.dll。解决办法有两类我都常用第一类是给系统加PATH把C:\Dev\opencv\build\x64\vc15\bin和C:\Dev\Qt\5.15.2\msvc2019_64\bin加进系统环境变量的Path里重开VS或电脑后生效。优点是Debug和Release都能用缺点是如果机器上存在多个Qt或OpenCV版本PATH的顺序会决定程序加载哪个版本换工程时容易踩雷。第二类是把需要的DLL拷贝到exe目录。好处是分发程序时干净坏处是每次换个配置或换库版本都要重新拷。我的建议是本机调试用PATH发布前用工具把依赖DLL收集到exe同目录。判断一个exe缺什么库用Process Explorer看加载失败的DLL是最快的黑匣子解法。注意VS2019里按F5调试时环境变量PATH是继承Windows系统变量的所以改完PATH必须重启Visual Studio否则VS进程里还是旧的PATH运行照样报找不到DLL。4. 用Qt显示图像、用OpenCV处理图像最小可跑代码4.1 界面与信号槽两个QLabel、一个按钮工程配置就绪开始写代码。我要在同一个窗口里完成两件事把磁盘上的图片读进来显示在Qt界面上同时对图片做一次真实的图像处理再把处理结果显示到另一个Label。这是“显示图像和处理图像”最直白的落地形态也正好能同时验证Qt的信号槽机制和OpenCV的Mat运算链路。界面不用打开Qt Designer画.ui文件直接在MainWindow构造函数里手工创建控件代码量更少也更容易看清结构。在mainwindow.h里加上成员函数和成员变量声明。// mainwindow.h 关键部分 #pragma once #include opencv2/opencv.hpp #include QMainWindow class QLabel; class QPushButton; class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent nullptr); private slots: void onProcessImage(); private: void showMatInLabel(const cv::Mat mat, QLabel* label); QLabel* m_labelSrc; QLabel* m_labelDst; QPushButton* m_btnProcess; cv::Mat m_srcImage; };showMatInLabel是显式转换函数核心职责是把cv::Mat转成Qt能消费的QImage再转成QPixmap显示。这个转换是Qt和OpenCV集成里最绕不开的一个坎下一小节细说。4.2 imread加载与QImage显示BGR转RGB这一步不能省MainWindow构造函数里把三个控件摆到水平布局里然后读一张固定的测试图片。图片路径要么写死一个你确定存在的绝对路径要么放到工程目录下用相对路径我建议初学时直接写绝对路径排错简单。MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { m_labelSrc new QLabel(原图, this); m_labelDst new QLabel(处理结果, this); m_btnProcess new QPushButton(执行处理, this); QHBoxLayout* layout new QHBoxLayout; layout-addWidget(m_labelSrc); layout-addWidget(m_labelDst); layout-addWidget(m_btnProcess); QWidget* central new QWidget(this); central-setLayout(layout); setCentralWidget(central); m_srcImage cv::imread(D:/test.jpg, cv::IMREAD_COLOR); if (m_srcImage.empty()) { m_labelSrc-setText(图片加载失败请检查路径); } else { showMatInLabel(m_srcImage, m_labelSrc); } connect(m_btnProcess, QPushButton::clicked, this, MainWindow::onProcessImage); }注意cv::imread第二个参数我写的是cv::IMREAD_COLOR强制以三通道BGR格式读入。如果省略这个参数遇到带透明通道的PNG会读出四通道Mat后面转QImage时格式对不上显示会花屏或者整体错位。加上它所有图片统一成三通道后续处理一个通道数跑到底。显示函数实现如下void MainWindow::showMatInLabel(const cv::Mat mat, QLabel* label) { cv::Mat rgb; if (mat.channels() 1) { cv::cvtColor(mat, rgb, cv::COLOR_GRAY2RGB); } else if (mat.channels() 3) { cv::cvtColor(mat, rgb, cv::COLOR_BGR2RGB); } else { return; } QImage image( rgb.data, rgb.cols, rgb.rows, static_castint(rgb.step), QImage::Format_RGB888); QPixmap pixmap QPixmap::fromImage(image); label-setPixmap(pixmap.scaled( label-size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); }这里最关键的逻辑OpenCV里Mat的默认通道顺序是BGR而Qt的QImage::Format_RGB888期望RGB顺序。直接拿Mat数据构造QImage不先做cvtColor转换图片整体颜色会偏蓝偏红这是初学者最容易观察到的“玄学”现象。rgb.data指向Mat内部缓冲区rgb.step是每行字节数这两项必须用Mat的真实值传给QImage。第三个参数后面要小心rgb一旦离开函数作用域缓冲区释放QImage就变成悬空指针。上面的写法中QPixmap::fromImage(image)会深拷贝像素数据所以函数返回后界面仍然能正常绘制没有踩这个坑。4.3 一次真实的处理流程高斯模糊Canny边缘检测按钮槽函数执行图像处理。我选择的处理链是“高斯模糊 → Canny边缘检测”一方面这两步是OpenCV图像处理里最基础也最常用的组合另一方面Canny输出的边缘图是后续“检测直线”“轮廓提取”这类项目的前置输入做一次完整流程对做OpenCV图像处理项目的人更有参考价值。void MainWindow::onProcessImage() { if (m_srcImage.empty()) { return; } cv::Mat gray, blurred, edges; cv::cvtColor(m_srcImage, gray, cv::COLOR_BGR2GRAY); cv::GaussianBlur(gray, blurred, cv::Size(5, 5), 0, 0); cv::Canny(blurred, edges, 60, 120); showMatInLabel(edges, m_labelDst); }逻辑很简单先把三通道转灰度去掉颜色干扰然后高斯模糊核大小取5×5标准差两个参数传0表示让OpenCV根据核大小自动计算最后Canny双阈值低阈值60、高阈值120。模糊这一步是为了减少噪声Canny导数计算对噪声非常敏感不模糊直接做边缘检测会得到大量细碎假边这在真实项目中会直接导致直线检测结果离谱。双阈值的意思是梯度幅值高于120的像素必为边缘低于60的必不是边缘介于两者之间的只看它是否与高阈值边缘连通。工业上的经验值一般是低阈值取高阈值的一半上下60和120这个组合对多数室内场景图片效果均衡。这段跑完后showMatInLabel(edges, m_labelDst)会把单通道边缘图显示在右侧Label里。整个界面呈现原图和边缘图并排Qt负责界面OpenCV负责图像处理VS2019作为宿主完成编译和调试三者的集成验证到此闭环。5. 三个必踩的坑从编译期到运行期5.1 fatal: cannot mix incompatible qt library (version ex50601)这是我看到提问频率最高的一条报错。整句格式类似fatal: cannot mix incompatible qt library (version ex50601) with this library (version 0x50c01)前半段是你实际定义的Qt版本后半段是链接器拿到的版本。现象项目编译到链接阶段直接失败报错定位在一个头文件里且完全没说是哪个函数出了问题。原因机器上存在多个Qt版本编译时用的头文件来自5.6.1链接时却指向了另一个次要版本或者相反。VS里Qt VS Tools的全局Qt Versions设置和项目属性的Qt Installation设置指向不一致Powershell环境变量或系统PATH里又恰好挂着旧版Qt的bin目录三者叠加时编译器拿到的是A套件的路径、链接器按自己的搜索顺序找到B套件的lib。解决先清理嫌疑对象。把系统PATH里除目标Qtmsvc2019_64\bin外的所有Qt相关路径删干净。打开VS2019“Qt VS Tools→Qt Versions”只保留5.15.2 msvc2019_64一条。再打开项目属性确认Qt Installation下拉框选中的是和全局一致的那条路径。最后执行“生成→重新清理解决方案”再重新生成。这个坑一旦排除运行期很扎实它本质上就是一个环境变量与IDE配置打架的问题。5.2 运行时找不到opencv_world.dll或者提示could not find the qt platform plugin现象一编译成功VS里按F5或直接双击exe弹窗提示找不到opencv_world470.dll。原因OpenCV的DLL目录没有进PATH。这个好办把C:\Dev\opencv\build\x64\vc15\bin加进系统PATH重启VS后重跑即可。还有一种隐蔽情况Debug配置下你链接的是opencv_world470d.lib但PATH里的OpenCV是Release版本DLL文件名少一个d系统找不到带d的那个文件。检查附加依赖项和DLL目录是否配套。现象二程序能起来但窗口还没显示就崩溃输出窗口里有qt.qpa.plugin: could not find the qt platform plugin windows in 。原因Qt程序运行时需要platforms\qwindows.dll这个插件Qt的插件目录是Qt\5.15.2\msvc2019_64\plugins里面有个platforms子目录。VS调试时Qt VS Tools会自动把插件路径告诉程序但你如果直接把exe拷贝到别的机器或别的目录运行程序找不到platforms目录就会报这个错。解决把plugins目录完整拷贝到exe旁边并保持platforms子目录结构不变。程序发布时这是一套标准动作用windeployqt.exe可以自动完成工具路径在Qt\5.15.2\msvc2019_64\bin\windeployqt.exe。命令行执行windeployqt 你的exe路径它会自动扫描依赖项把要用到的DLL和插件全部拷过来。这是发布Qt程序的后悔药不用再手动猜缺什么。5.3 图片显示整体偏蓝偏红或者按钮点击直接闪退现象图片能显示但颜色明显不对蓝天变成了橙黄色人脸发蓝。另一个现象是点击处理按钮后程序无征兆退出。原因颜色不对基本可以断定是BGR和RGB没转换。OpenCV的imread读进来是BGR顺序你如果用cv::Mat的data直接构造QImage::Format_RGB888Qt会把B当作R、R当作B来解析红和蓝整体互换。按钮闪退多半是imread返回了空Mat但代码没有判空继续调cvtColor空指针引发访问违规。还有一种情况是读入了四通道PNG通道数处理分支没覆盖直接用了不匹配的QImage格式。解决颜色问题用cv::cvtColor(mat, rgb, cv::COLOR_BGR2RGB)再做QImage。闪退问题在imread之后立即加空判断空Mat直接显示失败提示并返回。四通道问题用cv::imread(path, cv::IMREAD_COLOR)强制转三通道从源头杜绝。另外调试这类崩溃时调出VS的“异常设置”窗口开启所有C异常崩溃时刻调用栈会直接定位到具体行比自己瞎猜快得多。5.4 OpenCV函数提示“未定义标识符”但编译却能过现象代码里写cv::Canny(...)VS的IntelliSense在函数名下面画红线提示Canny 未定义标识符但点“生成”居然编译通过。反过来也有编译报contourArea 未定义标识符但红线不提示。原因窗口里显示红色波浪线的是IntelliSense它读取的“编辑器智能感知上下文”和编译器实际使用的包含路径可能不一致。常见于OpenCV头文件已经加进“VC目录”的包含目录里但VS索引还没刷新或者代码用了using namespace cv;而IntelliSense索引状态卡在旧文件上。解决忽略智能提示以“输出窗口”里编译器的实际报错为准。如果编译器也报未定义检查是否漏了#include opencv2/imgproc.hpp特别是contourArea、Canny这类imgproc模块的函数只包含opencv.hpp全家桶通常够用但单独写头文件时容易漏。项目菜单里“生成→重新扫描解决方案”可以强制IntelliSense重建索引能消除大部分红色波浪线误报。6. 最后一招把“显示处理”封装成能验证的函数到这一步你的VS2019工程已经能完整跑通Qt界面显示和OpenCV处理。接下来我建议做一件事把处理链从按钮槽函数里抽出来变成一个不依赖任何Qt类型的纯函数。比如cv::Mat processPipeline(const cv::Mat src, double lowThresh, double highThresh) { cv::Mat gray, blurred, edges; cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); cv::GaussianBlur(gray, blurred, cv::Size(5, 5), 0, 0); cv::Canny(blurred, edges, lowThresh, highThresh); return edges; }这样做的价值在于界面代码只能人工观察而它对错说不清。抽成纯函数后你可以脱离界面验证处理结果。写一个临时测试函数读标准测试图调用processPipeline把edges通过cv::imwrite写到磁盘然后统计边缘图的非零像素数量int edgeCount cv::countNonZero(edges);用countNonZero的数值做回归指标调参时就能量化判断低阈值从60改成40边缘数是变多了还是变少了预期趋势是否符合理论。这是自动化图像处理项目最基本也最实在的验证手段。以后再接直线检测、轮廓提取这个纯函数可以直接复用。我的习惯是新算法先在纯函数里用一张固定图片跑通、写出中间结果图确认再接按钮和界面。这样做的原因是排查效率高处理链出了偏差问题一定在算法参数而不是Qt的信号槽协作。界面只负责展示最终结果而算法正确性必须由数据证明。你按这个顺序做Qt和OpenCV的集成调试时间能压到很短。希望帮到你。本文还有配套的精品资源点击获取
返回列表