相比于标准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开发一个应用程序,要求如下:
作为乙方,我们不应该不按交付要求进行功能增减,这是在进行用户界面设计时的一条重要原则。这意味着应该保证所有需求都能得到满足,同时没有添加任何不需要的功能。
最后生成如下应用:
在这里只是大概给大家演示一下怎么建工程,在哪里写代码,如何拖控件,大家参考一下就行。原理以及更详细的内容,关于qt的书里都有。而且大家去看看在一里介绍的视频,人家讲的很详细了。或者去qt creator里,点击帮助,可以搜索各种类,鼠标放到类上,按F1也可以看到详细介绍,不过是英文的。能够使用的工程也发出来了。
总而言之,强烈建议先把吴健的视频撸完,再照着步骤学习Qt和Open Cv的结合内容。
topHorizontalLayout
inputLabel
inputLineEdit
inputPushButton
bottomHorizontalLayout
outputLabel
outputLineEdit
outputPushButton
displayImageCheckBox
filterTypeGroupBox
gaussianBlurRadioButton
medianBlurRadioButton
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
#include
#include
#include "opencv2/opencv.hpp"
#include
#include
#include
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);
}
}
//如果这个文件路径字符串不空,则输出到行编辑器,并且根据路径读取图像文件,进行图像处理
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();
}
}//弹出对话框
添加私有类的加载和保存参数的函数
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());
}
放到正确的位置
loadSettings();放在MainWindow类的构造函数
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
loadSettings();
}
saveSettings();放到closeEvent()中,上面代码已经放置了。
9. 更改控件属性
大小,文本等,比如更改窗口名字为Hello_Qt_OpenCV
把今天的工程一字一句敲一遍,另外,例子程序直接使用了默认的高斯滤波和中值滤波的参数。希望大家能够利用Spin控件、Slider控件或一个漂亮的Dial控件(记得打开QT软件自身的help,看一看自己想要用的类怎么能获取和设置它的属性,如何使用help),从用户那里获取相应的参数(从对话框输入),利用用户的参数进行滤波操作。
这两个控件的介绍,大家可以自己鼓捣。也可以看一下夏曹俊的视频《C++ QT 跨平台界面编程原理和实战大全(QT5)》08节,看QSLIDER的使用。
不知道大家有没有发现,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
Qt对于高DPi屏不太好,所以需要我们自己去配置。
在Windows系上也是相同的方法,需要在环境变量中设置Qt的缩放因子。
在账户的环境变量里,做如下设置即可
调节代码字体使用ctrl + 滚轮
sudo apt-get install unity-tweak-tool
在软件中心搜索unity-tweak,这个软件可以缩放整个Linux系统的字体,同样可以缩放qt的,大家有兴趣可以玩一玩。如果调节过大,恢复默认即可。
欢迎大家关注我的个人公众号,现阶段主要总结Robomaster相关的计算机视觉知识。
公众号名称:三丰杂货铺