RoboMaster視覺筆記Qt(二)創建Qt+OpenCV控件應用

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. 當用戶想要關閉該應用程序時,應有提示信息。

作爲乙方,我們不應該不按交付要求進行功能增減,這是在進行用戶界面設計時的一條重要原則。這意味着應該保證所有需求都能得到滿足,同時沒有添加任何不需要的功能。

最後生成如下應用:

在這裏插入圖片描述

四 操作步驟

在這裏只是大概給大家演示一下怎麼建工程,在哪裏寫代碼,如何拖控件,大家參考一下就行。原理以及更詳細的內容,關於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());
    }
    
  2. 放到正確的位置
    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的縮放因子。

代碼字體

調節代碼字體使用ctrl + 滾輪

unity-tweak-tool

sudo apt-get install unity-tweak-tool

在軟件中心搜索unity-tweak,這個軟件可以縮放整個Linux系統的字體,同樣可以縮放qt的,大家有興趣可以玩一玩。如果調節過大,恢復默認即可。
在這裏插入圖片描述
在這裏插入圖片描述

微信公衆號

歡迎大家關注我的個人公衆號,現階段主要總結Robomaster相關的計算機視覺知識。
公衆號名稱:三豐雜貨鋪

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章