每天四小时学习opencv+qt系列(第一天)

我用的是qt5.14.1和opencv4.2

一、第一个opencv+qt的程序

1.在.pro文件里加入

LIBS += -LD:/opencv4.2/opencv/newbuild/newbuild/install/x64/vc14/lib/ -lopencv_world420d
INCLUDEPATH += D:/opencv4.2/opencv/newbuild/newbuild/install/include/
D:/opencv4.2/opencv/newbuild/newbuild/install/include/opencv2/


#注意Debug加入的是opencv_world420d,release则是opencv_world420

2.绘制ui

我按照书上的图画出来的ui是这样的:

qt opencv中文乱码 qt开发opencv_qt opencv中文乱码

分析一下:

首先新建工程的时候选择的是QMainWindow,我平时的个人习惯是选择QWidget,因为QWidget是QMainWindow的基类嘛,也就是QMainWindow继承于QWidget,QWidget又继承于QObject。用QWidget就很好,但是这里就先用MainWindow。

输入和输出图片那里包括的有三个部分:标签(Imput/Output image),路径,浏览按钮

标签就用QLabel,路径用QLineEdit,按钮用QPushbutton,最后再将三个控件水平布局就完成了

关于滤波器类型选择部分用了一个QGroupBox框架里面有两个按钮,这里使用的是单选按钮QRadioButton。

最后还有一个部分就是左下角的复选框QCheckBox,这时可以对所有控件字体、颜色等做更改,直接在右边的属性栏里修改styleSheet即可,也可以代码控制。我主要就修改字体大小,控件显示的文字设置为:font: 12pt “黑体”; 路径的框里我设置为font: 10pt “黑体”;

控件对应其功能不同可以对其名称进行修改,我修改后的显示如下图所示,这有利于代码的编写:

qt opencv中文乱码 qt开发opencv_#include_02

3.代码实现

在ui设计界面直接右击控件转到槽,选择触发信号为pressed()压下,点击ok,就会直接创建好信号的槽的联系无需自己再在.h文件中进行定义声明,你只需要加入槽所要实现的功能即可。

因为在后续操作中会用opencv和对文件进行操作,所以在.h文件中加入

#include<opencv2/opencv.hpp>
#include<QFileDialog>
#include<QDir>
#include<QFile>

在mainwindow.cpp中头文件下方加入

using namespace cv;

(1)输入图片的浏览按钮:

qt opencv中文乱码 qt开发opencv_ui_03

void MainWindow::on_input_button_pressed()
{
    QString filename=QFileDialog::getOpenFileName(this,
                                                  "open input image",
                                                  QDir::currentPath(),
                                                  "Images (*.jpg *.png *.bmp)");
    if(QFile::exists(filename))
    {
        ui->input_lineEdit->setText(filename);
    }
}
/*分析:
定义filename这个QString类型的字符串来存储地址
用到了QFileDialog这个对话框的getOpenFileName()来获取信息
官方文档:
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
                                                  "/home",
                                                  tr("Images (*.png *.xpm *.jpg)"));
代码中“open input image”其实就是对话框的title,而QDir是用来访问计算机上的文件,currentPath()是当前路径,筛选的类型就是.jpg .png .bmp三种。

(2)输出图片的按钮

同样是选择pressed()信号

void MainWindow::on_output_button_pressed()
{
    QString filename=QFileDialog::getSaveFileName(this,
                                                  "select output image",
                                                  QDir::currentPath(),
                                                  "*.jpg;;*.png;;*.bmp");
    if(!filename.isEmpty())
    {
        ui->output_lineEdit->setText(filename);
        Mat inputImage,outputImage;
        inputImage=imread(ui->input_lineEdit->text().toStdString());
        if(ui->MedianBlur_button->isChecked())
            cv::medianBlur(inputImage,outputImage,5);
        /*
         * void medianBlur( InputArray src, OutputArray dst, int ksize );
         * 中值滤波:把数字图像或数字序列中一点的值用该点的邻域中各点值的中值代替,让周围像素灰度值的差比较大的像素改取与周围的像素值接近的值,从而可以消除孤立的噪声点。
         * ksize:滤波模板的尺寸大小,必须是大于1的奇数。
        */
        else if(ui->GaussianBlur_button->isChecked())
            cv::GaussianBlur(inputImage,outputImage,Size(5,5),1.25);
        /*
         * void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
                                double sigmaX, double sigmaY = 0,
                                int borderType = BORDER_DEFAULT )
         * 高斯滤波:用一个模板(或称卷积、掩模)扫描图像中的每一个像素,用模板确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值。
         * 第四个参数表示在x方向的标准差为1.25
        */
        imwrite(filename.toStdString(),outputImage);
        if(ui->display_checkBox->isChecked())
        {
            imshow("input image",inputImage);
            imshow("output image",outputImage);
        }
    }
}

网上随便找了一张有噪声的图片测试:(记得copy一份图片,output image选择copy的那张这样方便对比,防止覆盖了)

高斯滤波:

qt opencv中文乱码 qt开发opencv_opencv_04

中值滤波:

qt opencv中文乱码 qt开发opencv_ui_05

到这里其实程序的功能实现了,但是书中还进行了重写关闭事件而且还有保存设置和加载设置两个函数,这个挺好的。也就是说用户的操作会被记录,并且关闭窗口时还多了确认环节,以免产生误操作。

(3)重写关闭事件,当关闭时会提示你是否要关闭程序,在把你选择的结果存在一个整形的数据里进行匹配,再通过if语句来进行操作判断。

首先在.h文件里

#include<QMessageBox>
#include<QCloseEvent>

protected:
    void closeEvent(QCloseEvent *event);

在.cpp里

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)
    {
        event->accept();
    }
    else
    {
        event->ignore();
    }
}

(4)保存设置saveSettings()和加载设置loadSettings()

首先在.h文件里面声明这两个自定义函数,你可以private也可以private slots,用法差不多。一种是定义一个私有函数,一种是定义一个私有的槽函数。

#include<QSettings>

private:
    void loadSettings();
    void saveSettings();

.cpp里面:

void MainWindow::loadSettings()
{
    QSettings settings("Packt",
                       "opencv+qt",
                       this);
    ui->input_lineEdit->setText(settings.value("input_lineEdit","").toString());
    ui->output_lineEdit->setText(settings.value("output_lineEdit","").toString());
    ui->MedianBlur_button->setChecked(settings.value("MedianBlur_button",true).toBool());
    ui->GaussianBlur_button->setChecked(settings.value("GaussianBlur_button",false).toBool());
    ui->display_checkBox->setChecked(settings.value("display_checkBox",false).toBool());
}
void MainWindow::saveSettings()
{
    QSettings settings("Packt",
                       "opencv+qt",
                       this);
    settings.setValue("input_lineEdit",ui->input_lineEdit->text());
    settings.setValue("output_lineEdit",ui->output_lineEdit->text()); 
    settings.setValue("MedianBlur_button",ui->MedianBlur_button->isChecked());
    settings.setValue("GaussianBlur_button",ui->GaussianBlur_button->isChecked());
    settings.setValue("display_checkBox",ui->display_checkBox->isChecked());    
}

最后将调用这两个函数,在ui->setupUi(this)初始化程序后loadSettings(),再在关闭事件中的

event->accept()前加上saveSettings()也就是在关闭事件之前保存设置。
整理一下代码:

.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include<QFileDialog>
#include<QDir>
#include<QFile>
#include<opencv2/opencv.hpp>
#include<QMessageBox>
#include<QCloseEvent>
#include<QSettings>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
protected:
    void closeEvent(QCloseEvent *event);
private slots:
    void on_input_button_pressed();

    void on_output_button_pressed();

private:
    Ui::MainWindow *ui;
    void loadSettings();
    void saveSettings();
};
#endif // MAINWINDOW_H

MainWindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

using namespace cv;
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    loadSettings();
}

MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::on_input_button_pressed()
{
    QString filename=QFileDialog::getOpenFileName(this,
                                                  "open input image",
                                                  QDir::currentPath(),
                                                  "Images (*.jpg *.png *.bmp)");
    if(QFile::exists(filename))
    {
        ui->input_lineEdit->setText(filename);
    }
}

void MainWindow::on_output_button_pressed()
{
    QString filename=QFileDialog::getSaveFileName(this,
                                                  "select output image",
                                                  QDir::currentPath(),
                                                  "*.jpg;;*.png;;*.bmp");
    if(!filename.isEmpty())
    {
        ui->output_lineEdit->setText(filename);
        Mat inputImage,outputImage;
        inputImage=imread(ui->input_lineEdit->text().toStdString());
        if(ui->MedianBlur_button->isChecked())
            cv::medianBlur(inputImage,outputImage,5);
        /*
         * void medianBlur( InputArray src, OutputArray dst, int ksize );
         * 中值滤波:把数字图像或数字序列中一点的值用该点的邻域中各点值的中值代替,让周围像素灰度值的差比较大的像素改取与周围的像素值接近的值,从而可以消除孤立的噪声点。
         * ksize:滤波模板的尺寸大小,必须是大于1的奇数。
        */
        else if(ui->GaussianBlur_button->isChecked())
            cv::GaussianBlur(inputImage,outputImage,Size(5,5),1.25);
        /*
         * void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
                                double sigmaX, double sigmaY = 0,
                                int borderType = BORDER_DEFAULT )
         * 高斯滤波:用一个模板(或称卷积、掩模)扫描图像中的每一个像素,用模板确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值。
         * 第四个参数表示在x方向的标准差为1.25
        */
        imwrite(filename.toStdString(),outputImage);
        if(ui->display_checkBox->isChecked())
        {
            imshow("input image",inputImage);
            imshow("output image",outputImage);
        }
    }
}
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 MainWindow::loadSettings()
{
    QSettings settings("Packt",
                       "opencv+qt",
                       this);
    ui->input_lineEdit->setText(settings.value("input_lineEdit","").toString());
    ui->output_lineEdit->setText(settings.value("output_lineEdit","").toString());
    ui->MedianBlur_button->setChecked(settings.value("MedianBlur_button",true).toBool());
    ui->GaussianBlur_button->setChecked(settings.value("GaussianBlur_button",false).toBool());
    ui->display_checkBox->setChecked(settings.value("display_checkBox",false).toBool());
}
void MainWindow::saveSettings()
{
    QSettings settings("Packt",
                       "opencv+qt",
                       this);
    settings.setValue("input_lineEdit",ui->input_lineEdit->text());
    settings.setValue("output_lineEdit",ui->output_lineEdit->text());
    settings.setValue("MedianBlur_button",ui->MedianBlur_button->isChecked());
    settings.setValue("GaussianBlur_button",ui->GaussianBlur_button->isChecked());
    settings.setValue("display_checkBox",ui->display_checkBox->isChecked());
    
}

总结一下:qt跨平台,opencv也跨平台,所以两者结合就很香。