VS2019+opencv4.5.1+Qt5.12.10配置+图片阈值可调的二值化+QThread多线程读取处理写入图片 全记录
寒假进组,硬件软件都有任务,软件方面要求:
用Qt设计一个图片处理的GUI(Graphical User Interface 图形用户界面),功能包括
1、读取图片
2、直方图分析
3、阈值可调的二值化处理
4、多线程批量读取、灰度转换和保存图片,并能显示处理时长以及处理进度。
从零开始(几乎没有c++基础)到完成大概用了4天时间,搜资料的时候发现这样的demo没什么人写,虽然代码也是从网上各种帖子上拼拼凑凑改改写完的,但也值得汇总一下,鉴于自己也是新手,就从环境配置到编写代码的每一个过程都写一写,希望也能帮助到入门的新手们,下面开始叭
VS2019+opencv4.5.1+Qt5.12.10配置
VS2019+opencv4.5.1配置
主要参考这篇文章:VS2017配置opencv教程(超详细!!!)
确实超详细…其中的第六步勾选微软符号服务器我觉得挺重要的,我是补上了这一步之后程序才跑成功的。
第五步属性管理需要说一下,我在配置的时候,点开Debug|X64,并没有Microsoft.Cpp.x64.user这个文件,所以自己新建了一个项目属性表命名为property,然后按照步骤配置好之后就直接把这个属性表保存了下来,这样的话以后每新建一个需要用到opencv的项目时,都可以在Debug|X64里添加进这个属性表,就不需要再重新设置了,挺方便的。
VS2019+Qt5.12.10配置
主要参考这两篇文章:VS2017专业版使用最新版Qt5.9.2教程
我装Qt的时候遇到了一些麻烦,主要是在官网上下载的Qt6打开QtCreator后说我没有license就用不了,所以重新下了个5.12,因为它是长期支持的版本。
Demo实操全记录
图像处理demo测试
因为自己c++几乎无基础,加上第一次接触opencv,所以第一步打算先将任务里涉及到的用opencv做的图片处理过程先实现了,也算是给自己涨涨信心。
学习opencv推荐它的官方教程网站:opencv官方教程(英文)
选择自己的版本,再点选各个模块
图像处理模块Imgproc module还有一个大佬的翻译版:opencv(c++)图像处理(Imgproc模块)
读取图片
OpenCV里用来读取图片的是imread函数
直方图分析
opencv官方教程里有直方图分析的示例代码,我用的是Histogram Calculation 直方图计算。
它的代码里使用的每一个函数都可以通过点击直接跳转到定义,而且每一个示例还有主要代码的解释(Explanation),对于初学者来说太友好了!吹吹吹吹吹!
自己尝试的话可以新建一个项目,添加属性表,copy这个代码然后改一下需要打开的图片,就可以看到RGB三通道的直方图。
附上代码链接:Histogram Calculation
二值化且阈值可调
不出意外,在官方教程文档里找到了示例,OpenCV Tutorials->Image Processing (imgproc module) ->Basic->Basic Thresholding Operations
二话不说打开示例开始学习叭:Basic Thresholding Operations
附:这些示例里找文件路径的代码都比较复杂,自己试的时候可以直接定义读取文件的路径
//.cpp
String path = "D:\\VS\\Repo\\picture\\3.jpg";
Mat img = imread(path,IMREAD_COLOR);
图像处理的单独测试到此结束,下面把Qt结合起来。
Vs创建Qt项目
创建Qt Widgets Application
1、VS2019首页->创建新项目->搜索Qt->Qt Widgets Application(也许在其他版本里是Qt GUI Application)
然后会弹出来一个Qt Widgets Application Wizard,由于我没有Release的需求,所以只选择了Debug
最后Finish。
2、由于我们要用到opencv所以要设置属性:打开属性管理器,Debug|x64,添加设置好使用opencv相关的属性表Property。
设计Demo界面
1、解决方案资源管理器里的Resource Files中有一个.ui文件,左键双击后会弹出一个Qt Designer界面,在这里面可以将组件拖进图形界面来进行设计。
设计好之后记得保存,然后在解决方案管理器里点击该.ui文件,右键->编译,在\source\repos\QtWidgetsApplication2\x64\Debug\uic下就会出现一个编译后的.h头文件。
这样一来,刚建好项目时出现的这个报错就会消失了,因为现在工程里有ui_QtWidgetsApplication2.h这个文件了。
现在根据题目要求先设计好GUI界面如下,并且每一个组件都按照功能命名(部分label除外),编写组件控制函数时方便区分,设计好之后记得保存和编译。
界面设计好了,接下来就开始编写组件的控制函数
读取图片
1、首先在QtWidgetsApplication2.h头文件里添加slot槽函数
//.h文件
private slots:
//函数名的命名方式:on_控件类名_触发方式()
//声明读取图片函数
void on_action_Button_ReadImg_clicked();
2、在QtWidgetsApplication2.cpp源文件里将组件和槽函数用connect函数连接起来
//.cpp
//connect(控件, 触发方式, this, 触发的槽函数);
connect(ui.ReadImg, SIGNAL(clicked()), this, SLOT(on_action_Button_ReadImg_clicked()));
ui.ReadImg是一个QPushButton类,继承于QAbstractButton类,它包含signal:clicked,当按钮被激活时就发送信号,触发槽函数。QT官方参考文档:QAbstractButton clicked
3、然后定义on_action_Button_ReadImg_clicked()函数
//.cpp
//因为用到了opencv里的函数,所以需要添上用到的库
#include "opencv2/highgui.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
using namespace cv;
//为了方便调试代码,声明了一个全局变量
Mat img = imread("D:\\VS\\Repo\\QtWidgetsApplication1\\picture\\3.jpg", IMREAD_COLOR);
void QtWidgetsApplication2::on_action_Button_ReadImg_clicked()
{
Mat img1;
//将imread得到的BRG Mat转换成RGB Mat
cvtColor(img, img1, COLOR_BGR2RGB);
//将RGB Mat格式转化为QImage格式
QImage disImage = QImage((const unsigned char*)(img1.data), img1.cols, img1.rows, QImage::Format_RGB888);
//用QPixmap获得QImage图像,用label显示QPixmap格式图像,并根据label的大小来缩放QImage的大小
ui.dislabel->setPixmap(QPixmap::fromImage(disImage.scaled(ui.dislabel->size(), Qt::KeepAspectRatio)));
}
这里是用Qt里的label来显示图片,参考了这篇博文 Qt OpenCV 在界面显示图片 通过Lable方式 和GraphicsView 方式.
(目前只注重实现,Mat转QImage以及label显示QImage的原理暂不做深入研究,挖个坑待更新)
贴一个演示动图
直方图分析
1、首先在QtWidgetsApplication2.h头文件里添加slot槽函数
//.h文件
private slots:
//函数名的命名方式:on_控件类名_触发方式()
//声明直方图分析函数
void on_action_Button_HistAna_clicked();
2、在QtWidgetsApplication2.cpp源文件里将组件和槽函数用connect函数连接起来
//.cpp
//connect(控件, 触发方式, this, 触发的槽函数);
connect(ui.HistAna, SIGNAL(clicked()), this, SLOT(on_action_Button_HistAna_clicked()));
3、然后是直方图分析的实现
//.cpp
//Mat Hist(Mat src)里用到了std::Vector
using namespace std;
//定义了一个Hist函数,输入图像,返回它的直方图,格式均为Mat类
//从官方示例copy来的代码,没仔细研究,注重功能实现
Mat Hist(Mat src)
{
//! [Separate the image in 3 places ( B, G and R )]
vector<Mat> bgr_planes;
split(src, bgr_planes);
//! [Establish the number of bins]
int histSize = 256;
//! [Set the ranges ( for B,G,R) )]
float range[] = {
0, 256 }; //the upper boundary is exclusive
const float* histRange = {
range };
//! [Set histogram param]
bool uniform = true, accumulate = false;
//! [Compute the histograms]
Mat b_hist, g_hist, r_hist;
calcHist(&bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate);
calcHist(&bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate);
calcHist(&bgr_planes[2], 1,