Qt+OpenCV利用幀差法實現車輛識別

一、目標

Qt界面實現 點擊 線程啟動按鈕播放視頻

左邊界面顯示原視頻 右邊界面顯示車輛識別視頻

結果展示如下:

初始界面

點擊線程啟動後,即可車輛識別

二、使用Qt界面

設計好界面後最好先保存

對按鈕設置槽函數

三、代碼實現

難點在於:線程同步問題

需要使用到connect函數中的第五個參數【第五個參數 具體說明如下】

1 AutoConnection 為默認參數,由發送信號決定,如果發送信號和接受信號是同一個線程,則調用DirectConnection。如果不在同一個線程則調用QueuedConnection;

2 DirectConnection 槽函數運行於信號發送者所在的線程,效果上就像是直接在信號發送的位置調用瞭槽函數

3 QueuedConnection 槽函數在控制回到接收者所在線程的事件循環時被調用,槽函數運行於信號接收者所在線程。發送信號後,槽函數不會立即被調用,等到接收者當前函數執行完,進入事件循環之後,槽函數才會被調用。多線程下用這個類型

4 BlockingQueuedConnection 槽函數的調用時機與Qt::QueuedConnection 一致,不過在發送完信號後,發送者所在線程會阻塞,直到槽函數運行完。接收者和發送者絕對不能在一個線程,否則會死鎖。在多線程間需要同步的場合會用到這個

5 UniqueConnection 此類型可通過 “|” 與以上四個結合在一起使用。此類型為當某個信號和槽已經連接時,在進行重復連接時就會失敗,可避免重復連接。如果重復連接,槽函數會重復執行

Widget

頭文件導入OpenCV包

#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<opencv2/opencv.hpp>
#include"videothread.h"
 
using namespace cv;
 
namespace Ui {
class Widget;
}
 
class Widget : public QWidget
{
    Q_OBJECT
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
    void paintEvent(QPaintEvent *e);
private slots:
    void on_pushButton_clicked();
public slots:
    //綁定線程 需要兩幀畫面 原圖和處理之後的圖   接收由同一個信號發送來的兩幀畫面
    void ChangeImg(Mat oldimg,Mat newimg);
private:
    Ui::Widget *ui;
    videothread *pthread;
    QImage oldimg;
    QImage newimg;
};
 
#endif // WIDGET_H

源文件 界面實現 

#include "widget.h"
#include "ui_widget.h"
 
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->pthread = new videothread("D:/00000000000003jieduanshipincailliao/carMove.mp4");
    //由於線程同步問題 需要使用第五個參數
    connect(this->pthread,SIGNAL(send2UI(Mat,Mat)),this,SLOT(ChangeImg(Mat,Mat)),Qt::BlockingQueuedConnection);
}
 
Widget::~Widget()
{
    delete ui;
}
 
void Widget::paintEvent(QPaintEvent *e)
{
    ui->label->setPixmap(QPixmap::fromImage(this->oldimg));
    ui->label_2->setPixmap(QPixmap::fromImage(this->newimg));
    //qDebug()<<"paintEvent";
}
 
void Widget::on_pushButton_clicked()
{
    this->pthread->start();
}
 
void Widget::ChangeImg(Mat oldimg,Mat newimg)
{
    //Mat是BGR 而QImage是RGB 需要轉換顏色
    cvtColor(oldimg,oldimg,CV_BGR2RGB);
    cvtColor(newimg,newimg,CV_BGR2RGB);
 
    this->oldimg = QImage(oldimg.data,oldimg.cols,oldimg.rows,QImage::Format_RGB888);
    this->oldimg = this->oldimg.scaled(ui->label->width(),ui->label->height());
 
    this->newimg = QImage(newimg.data,newimg.cols,newimg.rows,QImage::Format_RGB888);
    this->newimg = this->newimg.scaled(ui->label_2->width(),ui->label_2->height());
    //update();
}

VideoThread

頭文件導入OpenCV包

#ifndef VIDEOTHREAD_H
#define VIDEOTHREAD_H
#include<QThread>
#include<opencv2/opencv.hpp>
#include<vector>
#include<QDebug>
#include <QObject>
 
using namespace std;
using namespace cv;
 
class videothread : public QThread
{
    Q_OBJECT
public:
    //explicit videothread(QObject *parent = 0);
    //線程傳參視頻路徑
    videothread(char *path);
    void run();
private:
    VideoCapture cap;
    Mat frame;//讀一幀
    Mat temp;//保存上一幀
signals:
    //發送信號
    void send2UI(Mat oldimg,Mat newimg);
public slots:
};
 
#endif // VIDEOTHREAD_H

源文件 幀差法 車輛識別 

#include "videothread.h"
 
videothread::videothread(char *path):QThread()
{
    //打開一個視頻
    cap.open(path);
}
 
void videothread::run()
{
    int count = 0;
    Mat resFrame,diff;
    Mat frontGray,afterGray;
    vector<vector<Point>>contours;
 
    Mat element = cv::getStructuringElement(MORPH_RECT,Size(3,3));
    Mat element2 = cv::getStructuringElement(MORPH_RECT,Size(20,20));
    int x,y,w,h;
 
    while (cap.read(frame))
    {
        count++;
        if(count == 1)
        {
            //保存第一幀
            temp = frame.clone();
            continue;
        }
        else {
            //繪制矩形 使用此幀
            resFrame = frame.clone();
            //1 灰度處理 目的 RGB三通道轉灰度單通道 壓縮到原圖片三分之一大小
            cvtColor(temp,frontGray,CV_RGB2GRAY);
            cvtColor(frame,afterGray,CV_RGB2GRAY);
            //2 幀差處理 目的 找到幀與幀之間的差異(正在運動的物體)
            absdiff(frontGray,afterGray,diff);
            //3 二值化處理 目的 將灰度圖繼續識別轉換為黑白分明的圖像
            threshold(diff,diff,25,255,CV_THRESH_BINARY);
            //4 圖像降噪
            //4-1 腐蝕處理 目的 去除白色噪點
            erode(diff,diff,element);
            //4-2 膨脹 目的 把白色區域變大
            dilate(diff,diff,element2);
            //5 提取關鍵點
            //5-1 查找特征點
            findContours(diff,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE,Point(0,0));
            //qDebug()<<contours.size();
            //5-2 提取關鍵點
            vector<vector<Point>>contours_poly(contours.size());
            vector<Rect>boundRect(contours.size());
            //5-3 確定下四個點來用於框選目標物體
            int num=contours.size();
            for(int i = 0;i < num;i++)
            {
                approxPolyDP(Mat(contours[i]),contours_poly[i],3,true);
                //多邊擬合
                boundRect[i]=boundingRect(Mat(contours_poly[i]));
                x=boundRect[i].x;
                y=boundRect[i].y;
                w=boundRect[i].width;
                h=boundRect[i].height;
                //繪制矩形
                rectangle(resFrame,Point(x,y),Point(x+w,y+h),Scalar(0,0,255),2);
            }
        }
        temp = frame.clone();
        emit send2UI(frame,resFrame);
        msleep(1);
    }
}

主入口Qt窗口顯示

#include "widget.h"
#include <QApplication>
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
 
    return a.exec();
}

到此這篇關於Qt+OpenCV利用幀差法實現車輛識別的文章就介紹到這瞭,更多相關Qt OpenCV車輛識別內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: