准备开始学习一本新书,名字叫做《OpenCV3和Qt5计算机视觉应用开发》,之前也接触过一些Qt和Opencv应用的例子,不过都不成系统,也比较粗浅,基本上是一边查资料一边临时用上去的,这次学习这本书一方面想补充整理学习到的知识,另一方面也接触学习运用Qt和Opencv组合的计算机视觉方法,说不好会对以后的学习研究有没有帮助,不过倒是可以打发打发这段时间。
相关的配置在之前的帖子里面已经记录过了,那今天就记录整理一下这本书的第二章内容——创建第一个Qt+OpenCV项目。这一章呢,大概是介绍Qt的一些基础应用,然后有一个Qt+Opencv的比较简单的小项目例子。
章节的内容比较多,本着只记录一些自己认为比较重要的内容,就简单一些。
目录
1 Qt Creator
1.1 Qt Creator的模式
1.2 Qt Creator的选项窗口
2 OpenCV+Qt的简易项目
1 Qt Creator
首先比较简单的介绍一下Qt Creator。这是一个由Qt框架创建并为其服务的IDE,简单理解的话,就是可以在这上面创建Qt的项目,当然我们也可以在VS等编译器上使用Qt框架,不过初学的话,我还是比较喜欢轻量开源的Qt Creator的界面风格。以下是它的一些重要特性:
(1)使用会话管理多个IDE状态
(2)多个Qt项目管理
(3)用户界面设计
(4)代码编辑
(5)跨平台
(6)应用程序调试
(7)上下文相关帮助
最早接触Qt是大学课程设计时需要做用户界面,然后稍微接触了一些,对上面的一些特性有了一些了解,但是因为本人做事凌乱懒散的特点,其实对于Qt的理解掌握应该可能只在入门的边缘徘徊吧……不过没关系,希望通过后面个人的学习慢慢加深印象,更好地理解掌握Qt的内容。
1.1 Qt Creator的模式
如图,Qt Creator共有6种模式,见右侧边栏,分别是欢迎、编辑、设计、调试、项目和帮助模式,每一种模式下,主页面都呈现不同的内容,通过点击左侧对应的选项可以切换不同的模式,以下分别阐述欢迎、编辑、设计和帮助模式,其他两种模式留待后续学习。
1.1.1 欢迎模式
首先是欢迎模式,这是Qt Creator打开后第一个看到的页面即初始模式。该模式下有三个子模式:项目、示例和教程。
项目子模式可以用于创建新的项目或打开最近的项目,此外session部分可以用于存储和恢复IDE状态,这部分书中没有讨论,就留到以后再学习吧。
示例子模式和教程子模式比较类似,前者提供很多带注释的例子以供学习,后者包含视频演示与示例,定期浏览教程内容可以及时了解最新特性和使用方法。
1.1.2 编辑模式
接下来是编辑模式,进行项目开发时将频繁使用到该模式。
首先看编辑模式下窗体的不同部分,序号①、③是左右两侧的工具条,序号②是主要的代码编写区域。默认情况下,只有左侧工具条可见,但是可以使用屏幕辆车底部的按钮关闭或打开工具条。
主代码区是一个轻量级的代码编辑器,具备代码完成、代码高亮和上下文相关帮助等功能,当鼠标光标位于代码编辑器的Qt类上时,可以按下F1键查看上下文有关的帮助信息。
两侧顶部的箭头可以改变工具条的模式,可用的工具条模式有:
这些模式的内容包括:
项目 | 包含已打开项目及其包含文件的列表 |
打开文档 | 只显示已经打开的文件,可以点击文件旁的“X”手动关闭文件 |
书签 | 显示在代码中所建的书签 |
文件系统 | 文件浏览器,显示工程项目文件夹的所有文件,也可浏览计算机上的其他文件夹 |
类视图 | 查看当前项目中的类层次结构 |
大纲 | 相似当前已打开文件中的所有方法与符号的层次结构 |
测试 | 显示项目中的所有可用测试 |
类型层次及 Include Hierarchy |
查看类层次结构以及所包含头文件的层次结构 |
1.1.3 设计模式
接下里是设计模式,与其他模式不同,设计模式并不能通过左侧边栏进行切换,要打开设计模式,首先要打开一个用户界面文件(*.ui),即可以通过双击打开左窗体的mainwindow.ui文件。
设计模式提供用户界面设计器的所有工具,它有一个所见即所得类型的用户界面GUI编辑器,该编辑器可用于对Qt控件实施添加、删除、编辑或编写代码等操作,控件也可以根据需要从用户界面中添加或删除。
设计模式的界面也可以分为三个部分。中间主要区域可以任意拖放/删除控件、调整控件的大小,是一个可视化编辑用户界面的取悦;左侧是一个可以添加到用户界面的空间列表;右侧可以查看用户界面控件的的分层视图并修改每个控件的属性。
在中间主体部分的底部,可以看到“Action Editor”和“Signals_Slots Editor”。为了理解它们的工作原理,首先需要了解Qt中信号和槽的概念,简单来说这是一张消息传递的方法,发出信息->槽响应,因为之前接触过,所以就不记录太多了。
接下里记录一下每个控件的大概作用,其实按我对自己的了解,除了常用的一些,其他大部分的控件作用和使用方法我估计是记不住的,不过没关系,需要的时候能够从书中或者网络查找到也可以,用得多了自然就熟悉了。
垂直布局 | 等价Qt类QVBoxLayout,垂直分布 |
水平布局 | 等价Qt类QHBoxLayout,水平分布 |
网格布局 | 等价Qt类QGridLayout,建立具有任意数量的行和列的控件网格 |
表单布局 | 等价Qt类QFormLayout,建立包含一些标签和对应的输入控件的表单状外观 |
水平间隔符 | 用于在同处一行的两个控件之间插入空白,形似弹簧 |
垂直间隔符 | 用于在同处一列的两个控件之间插入空白,形似弹簧 |
按键按钮 | 最常用的简单按钮,QPushButton |
工具按钮 | 与按键按钮相似,通常要添加到工具栏中 |
单选按钮 |
从众多其定义的选项中选中或取消某个选项,QRadioButton |
复选框 | 用于启用或禁用选项,QCheckBox |
命令链接按钮 |
windows vista样式的命令链接按钮,可以代替向导中的单选按钮 类似于使用单选框选择一个选项,然后在向导对话框点击Next,QCommandLinkButton |
对话框按钮 | 让按钮能够在对话框中使用操作系统样式,QDialogButtonBox |
菜单栏(QMenuBar) | 位于窗口顶部的水平朱菜单栏,可以有任意数量的对象元素和子对象元素,每一个对象元素可以触发某个动作(QAction) 唯一 |
工具栏(QToolBar) | 可移动的窗体,包含与特定任务对应的工具按钮 可以有任意数量 |
状态栏(QStatusBar) | 位于底部的水平信息栏 唯一 |
列表视图(List View) | 简单列表显示, QListView |
树状视图(Tree View) | 层次结构的形式显示,QTreeView |
表格视图(Table View) | 以具有任意数量的行和列的表格显示,QTableView |
列视图(Column View) | 与列表视图类似,还显示模型中存储的层次数据,QColumnView |
对象元素控件
列表控件 类似列表视图,但有基于对象元素的API,可用来增加、移除修改其对象元素,QListWidget 树状控件 类似树状视图,但有基于对象元素的API,可用来增加、移除修改其对象元素,QTreeWidget 表格控件 类似列表视图,但有基于对象元素的API,可用来增加、移除修改其对象元素,QTabletWidget
组框 | 简单的带标题和边框的分组框,QGroupBox |
滚动区 | 可滚动的区域,QScrollArea |
工具箱 | 用于在不同选项卡组或列中对控件分组,每个选项卡可展开其包含的组件,同时隐藏其他选项卡的内容,QToolBox |
选项卡控件 | 用于在选项卡页面显示不同组的控件,可以通过单击对应的选项卡来切换每个页面或一组控件,QTabWidget |
堆叠控件 | 类似选项卡控件,但只有一页且一直可见,如果希望把不同的用户界面设计到一页并根据用户的操作切换成不同的页面,这个控件就特别有用,QStackedWidget |
框架 | 用作所有带框架的控件的占位符,也是所有带框架控件的基类,qframe |
控件 | 与QWidget类相同,所有Qt空间的基类 |
MDI区域 | 可用于在一个窗口或Qt控件内创建所谓的多文档界面,右键MDI区域可添加新窗口,QMdiArea |
用作Active-X控件的封装器,只适用于WIndows用户且依赖一个名为axcontainer的Qt模块,QAXWidget |
用作特定控件的占位符,可以停靠在一个窗口内部或者移到其外面作为一个独立的顶部窗口,QDockWidget |
组合框 | 下拉列表框,QComboBox |
字体下拉列表框 | 类似下拉列表框,但可选择计算机上的可用字体创建字体列表 |
行编辑器 | 输入和显示一行文本,QLineEdit |
文本编辑 | 输入和显示多格式文本,实际是成熟的所见即所得格式文本编辑器,QTextEdit |
纯文本编辑 | 可用于查看和编辑多行文本,可以看作简单的记事本控件,QPlainTextEdit |
选值框 | 可用于输入一个整数或离散值集合,QSpinBox |
双精度选值框 | 类似选值框,但可接受双精度值,QDoubleSpinBox |
时间编辑 | 输入时间值,QTimeEdit |
日期编辑 | 输入日期值,QDateEdit |
日期/时间编辑框 | 输入时间值和日期值,QDateTimeEdit |
表盘 | 与滚动条类似,但形状为圆形的表盘,QDial |
水平/竖直栏 | 水平或竖直方向的滚动条,QScrollBar |
水平/垂直滑块 | 输入指定范围内的整数值,QSlider |
键盘序列编辑 | 可用于输入一个键盘快捷键,QKeySequenceEdit |
标签 |
显示数字、文本、图像、日期等输出数据,QLabel |
文本浏览器 | 除增加连接之间的导航外,其余与文本编辑框控件几乎一样,QTextBrowser |
图形视图 | 用于显示图形场景的内容,QGraphicsView |
日历控件 | 用于在按月显示的日历中查看和选择日期,QCalendarWidget |
LCD数字 | 用于在类似液晶屏的显示器中显示数字,QLCDNumber |
进度条 | 用于显示垂直的或水平的进度进度指示器,QProgressBar |
水平线/垂直线 | 用于绘制简单的垂直线或水平线作为分隔符 |
OpenGl控件 | 用作渲染OpenGL输出的一个表面,QOPenGLWidget |
QQuickWideget | 用于显示Qt Quick用户界面,该界面使用QML语言设计用户界面 |
1.1.4 帮助模式
帮助模式不仅可以按文字搜索与Qt相关的所有信息,查看每一个类与模块的大量应用示例,可以用它查找每个类所需的正确模块,要做到这一点只需要切换到索引模式进行检索即可。
1.2 Qt Creator的选项窗口
接下来了解一下Qt Creator的选项窗口。
在主菜单中,选择工具->选项,即可进入选项窗口。
下面根据书籍内容,简要介绍介绍部分选项内容。
环境 | 外观相关的设置,更改主题、字体、文本大小、语言及其所有设置 |
文本编辑器 | 设置包括代码编辑器相关的所有内容 |
FakeVim | 为熟悉Vim编辑器的人准备的,启用并配置Vim样式的代码编辑 |
帮助 | 包含帮助与上下文帮助相关的所有选项 |
C++ | 与C++编码和代码编辑相关的设置 |
Qt Quick | 影响Qt Quick设计器以及QML代码编辑的选项 |
构建和运行 | 可能是Qt Creator中最重要的选项页,这里的设置会直接影响应用程序的构建和运行体验 |
调试器 | 与调试模式相关 |
设计师 | 配置模板项目以及与设计模式相关的其他设置 |
分析器 | 包括Clang代码分析器、QML分析器等相关设置 |
版本控制 | Qt提供了众多版本控制系统的非常可靠的集成,如Git和SVN,这里提供与版本控制相关的所有设置 |
设备 | 可以用它为安卓开发配置Qt Creator,包括与设备相关的所有设置 |
代码粘贴 | 用于配置一些第三方服务,这些服务可以用于代码共享之类的任务 |
测试 | Qt测试相关的设置等 |
2 OpenCV+Qt的简易项目
最后,是一个OpenCV+Qt的简易项目,主要用到了编辑模式和设计模式。
首先展示项目的大致结果
如图可以完成一个简易的图像过滤程序,选择对原始图像进行中值过滤或高斯过滤,保存处理后的图像并选择是否显示。
首先是设计用户界面:
然后开始代码编辑,首先不要忘记在PRO文件中添加代码保证Opencv的正常使用。
INCLUDEPATH += E:\OpenCV\opencv\opencv_build\install\include
LIBS += E:\OpenCV\opencv\opencv_build\lib\libopencv_*.a
接下来为每一个需求以及用户界面上的相关控件编写代码。
以inputPushButton为例,切换到设计器,右击选择转到槽,在该控件可以发出的所有信号中选择pressed。
然后将自动添加代码到头文件和.cpp文件。
接下来同样编辑其他控件的代码。mainwindow.h和mainwindow.cpp的文件内容如下,其中包含部分注释解释代码含义。
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include<QFileDialog>
#include<QDir>
#include<QFile>
#include<QMessageBox>
#include<QCloseEvent>
#include<QSettings>
#include<QTranslator>
#include"opencv2/opencv.hpp"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
protected:
//QCloseEvent类:用来传递有关窗口关闭事件的参数
void closeEvent(QCloseEvent*event);
void changeEvent(QEvent*event);
private slots:
void on_inputPushButton_pressed();
void on_outputPushButton_pressed();
void on_actionTurkish_triggered();
void on_actionGerman_triggered();
void on_actionEnglish_triggered();
private:
Ui::MainWindow *ui;
void loadSettings();
void saveSettings();
QTranslator *turkishTranslator;
QTranslator*germanTranslator;//土耳其语翻译 德语翻译
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
//#include <opencv2/core/core.hpp>
//#include <opencv2/highgui/highgui.hpp>
//#include <opencv2/imgproc/imgproc.hpp>
//using namespace cv;
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
// 测试opencv与Qt配置
// Mat img=imread("../learn_opencv/data/test1.png");
// namedWindow( "Display window", WINDOW_AUTOSIZE );
// imshow("Display window", img);
ui->setupUi(this);
loadSettings();
turkishTranslator=new QTranslator(this);
turkishTranslator->load(":/translation/translation_tr.qm");
germanTranslator=new QTranslator(this);
germanTranslator->load(":/translation/translation_de.qm");
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_inputPushButton_pressed()
{
//QString类:表示Unicode字符串 执行存储 转换 修改 等操作
//QFileDialog类:选择计算机上的文件或文件夹
//QDir类:用于访问计算机上的文件夹,并获取有关文件夹的各种类型信息
//QFile类:访问文件 读取或写入文件
//getOpenFileName函数:
//1 父控件 告诉编译器 QT 该类负责处理 QFileDialog类实例
//2 窗口标题
//3 当前路径
//4 确保应用程序只显示三种文件类型
QString fileName = QFileDialog::getOpenFileName(this,
tr( "Open Input Image"),
QDir::currentPath(),
"Images(*.jpg *.png *.bmp)");
if(QFile::exists(fileName)){
ui->inputLineEdit->setText(fileName);
}
}
void MainWindow::on_outputPushButton_pressed()
{
//getSaveFileName函数:保存输出图像
//4 保存时可单独选择每个图像类型
//一般情况下 路径不能含中文
QString fileName = QFileDialog::getSaveFileName(this,
tr("select Output Image"),
QDir::currentPath(),
"*.jpg;;*.png *;;.bmp");
if(!fileName.isEmpty()){
ui->outputLineEdit->setText(fileName);
//所有OpenCV函数都包含在cv命名空间中 要确保正在使用cv
using namespace cv;
Mat inpImg,outImg;
//注意:OpenCV使用C++ std::string 要将QString转换过来
//QString是控件的text()函数的返回值类型
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);
}
}
}
void MainWindow::closeEvent(QCloseEvent *event){
//QMessageBox类:显示简单的图标、文本或消息
int result=QMessageBox::warning(this,
tr("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 MainWindow::loadSettings(){
QSettings settings("Packt","learn_opencv",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(){
//QSetting类:为其提供组织名称 应用程序名称
//可记录传递给setVale函数所有内容并用value返回
QSettings settings("Packt","learn_opencv",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());
}
void MainWindow::on_actionTurkish_triggered()
{
qApp->installTranslator(turkishTranslator);
}
void MainWindow::on_actionGerman_triggered()
{
qApp->installTranslator(germanTranslator);
}
void MainWindow::on_actionEnglish_triggered()
{
qApp->removeTranslator(turkishTranslator);
qApp->removeTranslator(germanTranslator);
}
void MainWindow::changeEvent(QEvent *event){
//如果更改事件是语言更改 则翻译窗口 否则照常
if(event->type()==QEvent::LanguageChange){
ui->retranslateUi(this);
}
else{
QMainWindow::changeEvent(event);
}
}
最后,简单记录一下学习的几个新的Qt类。
QString类 | 可能是Qt最重要并被广泛使用的类之一,表示Unicode字符串,可以用来对字符串执行存储、转换、修改或无数其他操作 |
QFileDialog类 | 用于选择计算机上的文件或文件夹。使用底层操作系统的API,因此操作系统不同,对话框可能也有不同 |
QDir类 | 用于访问计算机上的文件夹,并获取有关文件夹的各种类型信息 |
QFile类 | 用于访问文件,读取或写入文件 |
QMessBox类 | 用来显示简单的图标、文本或消息,取决于消息的用途 |
QCloseEvent类 | Qt的众多事件类的一个,用于传递有关窗口关闭的时间参数 |
QSettings类 | 为其提供组织名称和应用程序名称,它可以记录下来传递给setValue函数的所有内容并用value函数返回它 |