RoboMaster视觉笔记Qt(二)创建Qt+OpenCV控件应用

文章目录

一 信号与槽

相比于标准C++编程,Qt框架最重要的一点是增加了信号与槽机制,这也是Qt如此简单易学且功能强大的原因,同时这也是Qt框架与其他框架之间最重要的区别。可以把该机制理解为Qt对象和类之间的消息传递方法(或根据含义将其命名为“信号”)。每个Qt对象都可以发出信号,该信号可以连接到另一个(或相同的)对象中的一个槽。

RM中需要Qt和Open CV结合。关于这个内容,强烈建议看伊朗外国人的书《Open CV3和Qt5计算机视觉应用开发》,2018年刚出了中文版本。本文关于Qt的学习也主要来自于这本书,主要是在这本书所给的代码上写注释,帮助理解。可以从http://www.packtpub.com或者http://www.hzbook.com,通过注册并登录个人账号下载所有代码工程。

单纯看Qt框架,建议看视频吴健的《Qt入门精讲》,b站找或者找我分享百度网盘。

手边常备一本书《Qt5.9C++开发指南》

二 中值滤波以及高斯滤波

平滑处理(smoothing)也称模糊处理(bluring),用来减少图像上的噪点,需要合理选择领域,但是又不至于丢失边缘信息。

滤波和模糊:滤波是将信号中特定波段频率滤除的操作。但是滤的是高频还是低频不确定。滤高频低通就是模糊,滤低频高通就是锐化。

线性滤波:即两个信号之和的响应和它们各自响应之和相等。换句话说,每个像素的输出值是一些输入像素的加权和。线性滤波器易于构造,并且易于从频率响应角度来进行分析。

高斯滤波:属于线性滤波。高斯滤波是指用高斯函数作为滤波函数的滤波操作;高斯模糊就是高斯低通滤波。通俗地讲,高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。

非线性滤波:在噪声是散粒噪声而不是高斯噪声,即图像偶尔会出现很大的值的时候,用高斯滤波器对图像进行模糊的话,噪声像素是不会被去除的,它们只是转换为更为柔和但仍然可见的散粒。这就到了中值滤波登场的时候了。

中值滤波:是一种典型的非线性滤波技术,基本思想是用像素点邻域灰度值的中值来代替该像素点的灰度值,该方法在去除脉冲噪声、椒盐噪声的同时又能保留图像的边缘细节。

函数怎么用,大家去看书。毛星云的书第六章

void medianBlur(InputArray src, OutputArray dst, int ksize)

InputArray src: 输入图像,图像为1、3、4通道的图像,当模板尺寸为3或5时,图像深度只能为CV_8U、CV_16U、CV_32F中的一个,如而对于较大孔径尺寸的图片,图像深度只能是CV_8U。
. OutputArray dst: 输出图像,尺寸和类型与输入图像一致,可以使用Mat::Clone以原图像为模板来初始化输出图像dst
. int ksize: 滤波模板的尺寸大小,必须是大于1的奇数,如3、5、7……

void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, int borderType=BORDER_DEFAULT);

src,输入图像,即源图像,填Mat类的对象即可。它可以是单独的任意通道数的图片,但需要注意,图片深度应该为CV_8U,CV_16U, CV_16S, CV_32F 以及 CV_64F之一。

dst,即目标图像,需要和源图片有一样的尺寸和类型。比如可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图。

ksize,高斯内核的大小。其中ksize.width和ksize.height可以不同,但他们都必须为正数和奇数(并不能理解)。或者,它们可以是零的,它们都是由sigma计算而来。

sigmaX,表示高斯核函数在X方向的的标准偏差。

sigmaY,表示高斯核函数在Y方向的的标准偏差。若sigmaY为零,就将它设为sigmaX,如果sigmaX和sigmaY都是0,那么就由ksize.width和ksize.height计算出来。

今天的任务便可以添加一个输入控件,控制这两种滤波的核大小。

三 新建项目

项目功能介绍

这一节,假设我们利用Qt开发一个应用程序,要求如下:

  1. 能够将图像(可接受的图像类型必须至少包括jpg,.png以及.bmp文件格式)作为输入。
  2. 能够应用模糊滤波器。用户必须能够选择中值模糊或高斯模糊类型对输入图像进行滤波处理(使用一组默认参数)。
  3. 能够保存输出图像以及输出图像的文件类型(即扩展名),文件类型必须可由用户选择(如jpg,.png或者.bmp)。
  4. 在保存图像时,用户应该可以选择查看输出图像。
  5. 用户界面上设置的所有选项,包括模糊滤波器类型以及最后打开和保存的图像文件,都应当被保存,并在重启应用程序时被重新加载。
  6. 当用户想要关闭该应用程序时,应有提示信息。

作为乙方,我们不应该不按交付要求进行功能增减,这是在进行用户界面设计时的一条重要原则。这意味着应该保证所有需求都能得到满足,同时没有添加任何不需要的功能。

最后生成如下应用:

RoboMaster视觉笔记Qt(二)创建Qt+OpenCV控件应用_控件

四 操作步骤

在这里只是大概给大家演示一下怎么建工程,在哪里写代码,如何拖控件,大家参考一下就行。原理以及更详细的内容,关于qt的书里都有。而且大家去看看在一里介绍的视频,人家讲的很详细了。或者去qt creator里,点击帮助,可以搜索各种类,鼠标放到类上,按F1也可以看到详细介绍,不过是英文的。能够使用的工程也发出来了。

总而言之,强烈建议先把吴健的视频撸完,再照着步骤学习Qt和Open Cv的结合内容。

  1. 拖动界面和控件,符合上面那张图的排布。
  2. 改变控件的objectname属性,为了在写代码时能够分清楚哪个控件是什么名字。
topHorizontalLayout
inputLabel
inputLineEdit
inputPushButton
bottomHorizontalLayout
outputLabel
outputLineEdit
outputPushButton
displayImageCheckBox
filterTypeGroupBox
gaussianBlurRadioButton
medianBlurRadioButton
  1. 向项目中添加Open CV
    更改.pro
    和g++的pkgconfig对比一下
win32: {
include(c:/dev/opencv/opencv.pri)
}

unix: !macx{
CONFIG += link_pkgconfig
PKGCONFIG += opencv
}

unix: macx{
INCLUDEPATH += /usr/local/include
LIBS += -L/usr/local/lib \
-lopencv_world
}

构建(编译和链接)代码时,上述代码行会翻译成相应OpenCV头文件、库文件和二进制文件,并包含在代码中,方便使用。
4. 添加头文件

#include <QFileDialog>
#include <QDir>
#include <QFile>
#include "opencv2/opencv.hpp"
#include <QCloseEvent>
#include <QMessageBox>
#include <QSettings>
  1. 为控件写代码
void MainWindow::on_inputPushButton_pressed()
{
QString fileName = QFileDialog::getOpenFileName(this, "Open Input Image", QDir::currentPath(), "Images (*.jpg *.png *.bmp)");
if(QFile::exists(fileName))
{
ui->inputLineEdit->setText(fileName);
}
}
//输入按钮按下后,弹出对话框,打开当前程序运行的路径,返回用户选择的文件名称
void MainWindow::on_outputPushButton_pressed()
{
QString fileName = QFileDialog::getSaveFileName(this, "Select Output Image", QDir::currentPath(), "*.jpg;;*.png;;*.bmp");
//打开方式和上文一致
if(!fileName.isEmpty())
{
ui->outputLineEdit->setText(fileName);
using namespace cv;
Mat inpImg, outImg;
inpImg = imread(ui->inputLineEdit->text().toStdString());
if(ui->medianBlurRadioButton->isChecked())
cv::medianBlur(inpImg, outImg, 5);
else if(ui->gaussianBlurRadioButton->isChecked())
cv::GaussianBlur(inpImg, outImg, Size(5, 5), 1.25);
imwrite(fileName.toStdString(), outImg);
if(ui->displayImageCheckBox->isChecked())
imshow("Output Image", outImg);
}
}
//如果这个文件路径字符串不空,则输出到行编辑器,并且根据路径读取图像文件,进行图像处理
  1. 添加关闭程序的代码
    在mainwindow类中覆盖并使用closeEvent()函数。也就是事件重写
protected:
void closeEvent(QCloseEvent *event);
void MainWindow::closeEvent(QCloseEvent *event)
{
int result = QMessageBox::warning(this, "Exit",
"Are you sure you want to close this program?",
QMessageBox::Yes,
QMessageBox::No);
if(result == QMessageBox::Yes)
{
//saveSettings();
event->accept();
}
else
{
event->ignore();
}
}//弹出对话框
  1. 添加私有类的加载和保存参数的函数
void loadSettings();
void saveSettings();
void MainWindow::loadSettings()
{
QSettings settings("Packt", "Hello_OpenCV_Qt", this);
ui->inputLineEdit->setText(settings.value("inputLineEdit", "").toString());
ui->outputLineEdit->setText(settings.value("outputLineEdit", "").toString());
ui->medianBlurRadioButton->setChecked(settings.value("medianBlurRadioButton", true).toBool());
ui->gaussianBlurRadioButton->setChecked(settings.value("gaussianBlurRadioButton", false).toBool());
ui->displayImageCheckBox->setChecked(settings.value("displayImageCheckBox", false).toBool());
}

void MainWindow::saveSettings()
{
QSettings settings("Packt", "Hello_OpenCV_Qt", this);
settings.setValue("inputLineEdit", ui->inputLineEdit->text());
settings.setValue("outputLineEdit", ui->outputLineEdit->text());
settings.setValue("medianBlurRadioButton", ui->medianBlurRadioButton->isChecked());
settings.setValue("gaussianBlurRadioButton", ui->gaussianBlurRadioButton->isChecked());
settings.setValue("displayImageCheckBox", ui->displayImageCheckBox->isChecked());
}
  1. 放到正确的位置
    loadSettings();放在MainWindow类的构造函数
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
loadSettings();
}

saveSettings();放到closeEvent()中,上面代码已经放置了。
9. 更改控件属性
大小,文本等,比如更改窗口名字为Hello_Qt_OpenCV

五 任务

  1. 把今天的工程一字一句敲一遍,另外,例子程序直接使用了默认的高斯滤波和中值滤波的参数。希望大家能够利用Spin控件、Slider控件或一个漂亮的Dial控件(记得打开QT软件自身的help,看一看自己想要用的类怎么能获取和设置它的属性,​​如何使用help​​​),从用户那里获取相应的参数(从对话框输入),利用用户的参数进行滤波操作。
    这两个控件的介绍,大家可以自己鼓捣。也可以看一下夏曹俊的视频《C++ QT 跨平台界面编程原理和实战大全(QT5)》08节,看QSLIDER的使用。

六 Qt界面字体调节

不知道大家有没有发现,qt软件的界面的字体都有种缩到一块儿的感觉,特别小。放大qt字体的方法:

sudo vim ~/.profile
在最末尾添加:
export QT_SCALE_FACTOR=1.5

重启即可(或者在右上角账户Log out注销再登录,就不需要重启,更方便)。
此外,也可以在.bash_profile文件中添加环境变量
其他类似参数还有:

QT_AUTO_SCREEN_SCALE_FACTOR

QT_DEVICE_PIXEL_RATIO
QT_SCREEN_SCALE_FACTORS

QT_DEVICE_PIXEL_RATIO

  1. 高分屏就是在同样大小的屏幕面积上显示更多的像素点,也就是更多的可视信息。
  2. DPI。即Dots Per Inch,它表示每英寸的像素点数。经常用来衡量高分屏。
    以上. 高分屏即高DPI屏。

Qt对于高DPi屏不太好,所以需要我们自己去配置。

在Windows系上也是相同的方法,需要在环境变量中设置Qt的缩放因子。

在账户的环境变量里,做如下设置即可

RoboMaster视觉笔记Qt(二)创建Qt+OpenCV控件应用_opencv_02

代码字体

调节代码字体使用ctrl + 滚轮

unity-tweak-tool

sudo apt-get install

在软件中心搜索unity-tweak,这个软件可以缩放整个Linux系统的字体,同样可以缩放qt的,大家有兴趣可以玩一玩。如果调节过大,恢复默认即可。

RoboMaster视觉笔记Qt(二)创建Qt+OpenCV控件应用_c++_03


RoboMaster视觉笔记Qt(二)创建Qt+OpenCV控件应用_linux_04

微信公众号

欢迎大家关注我的个人公众号,现阶段主要总结Robomaster相关的计算机视觉知识。
公众号名称:三丰杂货铺

RoboMaster视觉笔记Qt(二)创建Qt+OpenCV控件应用_c++_05