yolov5 target detection multi-threaded Qt interface

Previous article: yolov5 target detection multi-threaded C++ deployment

V1 basic function realization

mainwindow.h

#pragma once

#include <iostream>

#include <QMainWindow>
#include <QFileDialog>
#include <QThread>

#include <opencv2/opencv.hpp>

#include "yolov5.h"
#include "blockingconcurrentqueue.h"


QT_BEGIN_NAMESPACE
namespace Ui {
    
     class MainWindow; }
using namespace moodycamel;
QT_END_NAMESPACE


class Infer1 : public QThread
{
    
    
  Q_OBJECT

public slots:
    void receive_image(){
    
    };

private:
    void run();

private:
cv::Mat input_image;
cv::Mat blob;
cv::Mat output_image;
std::vector<cv::Mat> network_outputs;

signals:
    void send_image();
};

class Infer2 : public QThread
{
    
    
  Q_OBJECT

public slots:
    void receive_image(){
    
    };

private:
    void run();

private:
cv::Mat input_image;
cv::Mat blob;
cv::Mat output_image;
std::vector<cv::Mat> network_outputs;

signals:
    void send_image();
};


class MainWindow : public QMainWindow
{
    
    
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);

    ~MainWindow();

private slots:
    void on_pushButton_open_video_clicked();

    void receive_image();

private:
    Ui::MainWindow *ui;

    Infer1 *infer1;

    Infer2 *infer2;

signals:
    void send_image();
};

mainwindow.cpp

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

bool stop = false;
BlockingConcurrentQueue<cv::Mat> bcq_capture1, bcq_infer1;
BlockingConcurrentQueue<cv::Mat> bcq_capture2, bcq_infer2;


void print_time(int id)
{
    
    
    auto now = std::chrono::system_clock::now();
    uint64_t dis_millseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count()
        - std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count() * 1000;
    time_t tt = std::chrono::system_clock::to_time_t(now);
    auto time_tm = localtime(&tt);
    char time[100] = {
    
     0 };
    sprintf(time, "%d-%02d-%02d %02d:%02d:%02d %03d", time_tm->tm_year + 1900,
        time_tm->tm_mon + 1, time_tm->tm_mday, time_tm->tm_hour,
        time_tm->tm_min, time_tm->tm_sec, (int)dis_millseconds);
    std::cout << "infer" << std::to_string(id)  << " 当前时间为:" << time << std::endl;
}

void Infer1::run()
{
    
    
    cv::dnn::Net net = cv::dnn::readNet("yolov5n-w640h352.onnx");
    while (true)
    {
    
    
        if(stop)    break;

        if(bcq_capture1.try_dequeue(input_image))
        {
    
    
            pre_process(input_image, blob);
            process(blob, net, network_outputs);
            post_process(input_image, output_image, network_outputs);
            bcq_infer1.enqueue(output_image);
            emit send_image();
            print_time(1);
        }
    }
}

void Infer2::run()
{
    
    
    cv::dnn::Net net = cv::dnn::readNet("yolov5s-w640h352.onnx");
    while (true)
    {
    
    
        if(stop)    break;

        if(bcq_capture2.try_dequeue(input_image))
        {
    
    
            pre_process(input_image, blob);
            process(blob, net, network_outputs);
            post_process(input_image, output_image, network_outputs);
            bcq_infer2.enqueue(output_image);
            emit send_image();
            print_time(2);
        }
    }
}


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    
    
    ui->setupUi(this);

    infer1 = new Infer1;
    infer2 = new Infer2;

    connect(infer1, &Infer1::send_image, this, &MainWindow::receive_image);
    connect(infer2, &Infer2::send_image, this, &MainWindow::receive_image);
}

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

void MainWindow::receive_image()
{
    
    
    cv::Mat output_image;
    if(bcq_infer1.try_dequeue(output_image))
    {
    
    
        QImage image = QImage((const uchar*)output_image.data, output_image.cols, output_image.rows, QImage::Format_RGB888).rgbSwapped();
        ui->label_1->clear();
        ui->label_1->setPixmap(QPixmap::fromImage(image));
        ui->label_1->show();
    }
    if(bcq_infer2.try_dequeue(output_image))
    {
    
    
        QImage image = QImage((const uchar*)output_image.data, output_image.cols, output_image.rows, QImage::Format_RGB888).rgbSwapped();
        ui->label_2->clear();
        ui->label_2->setPixmap(QPixmap::fromImage(image));
        ui->label_2->show();
    }
}

void MainWindow::on_pushButton_open_video_clicked()
{
    
    
    QString qstr = QFileDialog::getOpenFileName(this, tr("Open Video"), "", tr("(*.mp4 *.avi *.mkv)"));
    if(qstr.isEmpty())  return;

    infer1->start();
    infer2->start();

    cv::VideoCapture cap;
    cap.open(qstr.toStdString());
    while (cv::waitKey(1) < 0)
    {
    
    
        cv::Mat frame;
        cap.read(frame);
        if (frame.empty())
        {
    
    
            stop = true;
            break;
        }

        bcq_capture1.enqueue(frame);
        bcq_capture2.enqueue(frame);
    }
}

The third-party library moodycamel::ConcurrentQueue introduced here is a multi-producer, multi-consumer lock-free queue implemented in C++11.
Program output:

infer1 当前时间为:2023-08-12 13:17:14 402
infer2 当前时间为:2023-08-12 13:17:14 424
infer1 当前时间为:2023-08-12 13:17:14 448
infer2 当前时间为:2023-08-12 13:17:14 480
infer1 当前时间为:2023-08-12 13:17:14 494
infer2 当前时间为:2023-08-12 13:17:14 532
infer1 当前时间为:2023-08-12 13:17:14 544
infer2 当前时间为:2023-08-12 13:17:14 586
infer1 当前时间为:2023-08-12 13:17:14 590
infer1 当前时间为:2023-08-12 13:17:14 637
infer2 当前时间为:2023-08-12 13:17:14 645
infer1 当前时间为:2023-08-12 13:17:14 678
infer2 当前时间为:2023-08-12 13:17:14 702
infer1 当前时间为:2023-08-12 13:17:14 719
infer2 当前时间为:2023-08-12 13:17:14 758
infer1 当前时间为:2023-08-12 13:17:14 760
infer1 当前时间为:2023-08-12 13:17:14 808
infer2 当前时间为:2023-08-12 13:17:14 817
infer1 当前时间为:2023-08-12 13:17:14 852
infer2 当前时间为:2023-08-12 13:17:14 881
...

Interface effect:
insert image description here

It can be seen that the above program realizes the multi-threaded reasoning of the two models, but due to the difference in the reasoning speed of different models, the screen display is out of sync. In addition, when writing the implementation of reading video frames into the main thread, once the reading of video frames is completed, the subsequent frames cannot be processed, resulting in the display being stuck.

V2 fix screen out of sync problem

mainwindow.h

#pragma once

#include <iostream>

#include <QMainWindow>
#include <QFileDialog>
#include <QThread>

#include <opencv2/opencv.hpp>

#include "yolov5.h"
#include "blockingconcurrentqueue.h"


QT_BEGIN_NAMESPACE
namespace Ui {
    
     class MainWindow; }
using namespace moodycamel;
QT_END_NAMESPACE

class Capture : public QThread
{
    
    
  Q_OBJECT

public:
    void set_video(QString video)
    {
    
    
        cap.open(video.toStdString());
    }

private:
    void run();

private:
    cv::VideoCapture cap;
};

class Infer1 : public QThread
{
    
    
  Q_OBJECT

public slots:
    void receive_image(){
    
    };

private:
    void run();

private:
cv::Mat input_image;
cv::Mat blob;
cv::Mat output_image;
std::vector<cv::Mat> network_outputs;

signals:
    void send_image();
};

class Infer2 : public QThread
{
    
    
  Q_OBJECT

public slots:
    void receive_image(){
    
    };

private:
    void run();

private:
cv::Mat input_image;
cv::Mat blob;
cv::Mat output_image;
std::vector<cv::Mat> network_outputs;

signals:
    void send_image();
};


class MainWindow : public QMainWindow
{
    
    
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_open_video_clicked();
    void receive_image();

private:
    Ui::MainWindow *ui; 
    QString video;
    Capture *capture;
    Infer1 *infer1;
    Infer2 *infer2;

signals:
    void send_image();
};

mainwindow.cpp

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

bool stop = false;
BlockingConcurrentQueue<cv::Mat> bcq_capture1, bcq_infer1;
BlockingConcurrentQueue<cv::Mat> bcq_capture2, bcq_infer2;


void print_time(int id)
{
    
    
    auto now = std::chrono::system_clock::now();
    uint64_t dis_millseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count()
        - std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count() * 1000;
    time_t tt = std::chrono::system_clock::to_time_t(now);
    auto time_tm = localtime(&tt);
    char time[100] = {
    
     0 };
    sprintf(time, "%d-%02d-%02d %02d:%02d:%02d %03d", time_tm->tm_year + 1900,
        time_tm->tm_mon + 1, time_tm->tm_mday, time_tm->tm_hour,
        time_tm->tm_min, time_tm->tm_sec, (int)dis_millseconds);
    std::cout << "infer" << std::to_string(id)  << " 当前时间为:" << time << std::endl;
}

void Capture::run()
{
    
    
    while (cv::waitKey(50) < 0)
    {
    
    
        cv::Mat frame;
        cap.read(frame);
        if (frame.empty())
        {
    
    
            stop = true;
            break;
        }

        bcq_capture1.enqueue(frame);
        bcq_capture2.enqueue(frame);
    }
}

void Infer1::run()
{
    
    
    cv::dnn::Net net = cv::dnn::readNet("yolov5n-w640h352.onnx");
    while (true)
    {
    
    
        if(stop)    break;

        if(bcq_capture1.try_dequeue(input_image))
        {
    
    
            pre_process(input_image, blob);
            process(blob, net, network_outputs);
            post_process(input_image, output_image, network_outputs);
            bcq_infer1.enqueue(output_image);
            emit send_image();
            print_time(1);
        }
    }
}

void Infer2::run()
{
    
    
    cv::dnn::Net net = cv::dnn::readNet("yolov5s-w640h352.onnx");
    while (true)
    {
    
    
        if(stop)    break;

        if(bcq_capture2.try_dequeue(input_image))
        {
    
    
            pre_process(input_image, blob);
            process(blob, net, network_outputs);
            post_process(input_image, output_image, network_outputs);
            bcq_infer2.enqueue(output_image);
            emit send_image();
            print_time(2);
        }
    }
}


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    
    
    ui->setupUi(this);

    capture = new Capture;
    infer1 = new Infer1;
    infer2 = new Infer2;

    connect(infer1, &Infer1::send_image, this, &MainWindow::receive_image);
    connect(infer2, &Infer2::send_image, this, &MainWindow::receive_image);
}

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

void MainWindow::receive_image()
{
    
    
    cv::Mat output_image;
    if(bcq_infer1.try_dequeue(output_image))
    {
    
    
        QImage image = QImage((const uchar*)output_image.data, output_image.cols, output_image.rows, QImage::Format_RGB888).rgbSwapped();
        ui->label_1->clear();
        ui->label_1->setPixmap(QPixmap::fromImage(image));
        ui->label_1->show();
    }
    if(bcq_infer2.try_dequeue(output_image))
    {
    
    
        QImage image = QImage((const uchar*)output_image.data, output_image.cols, output_image.rows, QImage::Format_RGB888).rgbSwapped();
        ui->label_2->clear();
        ui->label_2->setPixmap(QPixmap::fromImage(image));
        ui->label_2->show();
    }
}

void MainWindow::on_pushButton_open_video_clicked()
{
    
    
    video = QFileDialog::getOpenFileName(this, tr("Open Video"), "", tr("(*.mp4 *.avi *.mkv)"));
    if(video.isEmpty())  return;

    capture->set_video(video);

    capture->start();
    infer1->start();
    infer2->start();
}

The interface displays:
insert image description here

V3 corrected the display problem of the video playback completion interface

Compared with V2, V3 has little changes, only adding the function of sending a signal to clear the interface display when the video playback is completed.
mainwindow.h

#pragma once

#include <iostream>

#include <QMainWindow>
#include <QFileDialog>
#include <QThread>

#include <opencv2/opencv.hpp>

#include "yolov5.h"
#include "blockingconcurrentqueue.h"


QT_BEGIN_NAMESPACE
namespace Ui {
    
     class MainWindow; }
using namespace moodycamel;
QT_END_NAMESPACE

class Capture : public QThread
{
    
    
  Q_OBJECT

public:
    void set_video(QString video)
    {
    
    
        cap.open(video.toStdString());
    }

private:
    void run();

private:
    cv::VideoCapture cap;

signals:
    void stop();
};

class Infer1 : public QThread
{
    
    
  Q_OBJECT

private:
    void run();

private:
cv::Mat input_image;
cv::Mat blob;
cv::Mat output_image;
std::vector<cv::Mat> network_outputs;

signals:
    void send_image();
};

class Infer2 : public QThread
{
    
    
  Q_OBJECT

private:
    void run();

private:
cv::Mat input_image;
cv::Mat blob;
cv::Mat output_image;
std::vector<cv::Mat> network_outputs;

signals:
    void send_image();
};


class MainWindow : public QMainWindow
{
    
    
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_open_video_clicked();
    void receive_image();
    void clear_image();

private:
    Ui::MainWindow *ui; 
    QString video;
    Capture *capture;
    Infer1 *infer1;
    Infer2 *infer2;
};

mainwindow.cpp

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

bool flag = false;
BlockingConcurrentQueue<cv::Mat> bcq_capture1, bcq_infer1;
BlockingConcurrentQueue<cv::Mat> bcq_capture2, bcq_infer2;


void print_time(int id)
{
    
    
    auto now = std::chrono::system_clock::now();
    uint64_t dis_millseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count()
        - std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count() * 1000;
    time_t tt = std::chrono::system_clock::to_time_t(now);
    auto time_tm = localtime(&tt);
    char time[100] = {
    
     0 };
    sprintf(time, "%d-%02d-%02d %02d:%02d:%02d %03d", time_tm->tm_year + 1900,
        time_tm->tm_mon + 1, time_tm->tm_mday, time_tm->tm_hour,
        time_tm->tm_min, time_tm->tm_sec, (int)dis_millseconds);
    std::cout << "infer" << std::to_string(id)  << " 当前时间为:" << time << std::endl;
}

void Capture::run()
{
    
    
    while (cv::waitKey(50) < 0)
    {
    
    
        cv::Mat frame;
        cap.read(frame);
        if (frame.empty())
        {
    
    
            flag = true;
            emit stop();
            break;
        }

        bcq_capture1.enqueue(frame);
        bcq_capture2.enqueue(frame);
    }
}

void Infer1::run()
{
    
    
    cv::dnn::Net net = cv::dnn::readNet("yolov5n-w640h352.onnx");
    while (true)
    {
    
    
        if(flag)    break;

        if(bcq_capture1.try_dequeue(input_image))
        {
    
    
            pre_process(input_image, blob);
            process(blob, net, network_outputs);
            post_process(input_image, output_image, network_outputs);
            bcq_infer1.enqueue(output_image);
            emit send_image();
            print_time(1);
        }
        std::this_thread::yield();
    }
}

void Infer2::run()
{
    
    
    cv::dnn::Net net = cv::dnn::readNet("yolov5s-w640h352.onnx");
    while (true)
    {
    
    
        if(flag)    break;

        if(bcq_capture2.try_dequeue(input_image))
        {
    
    
            pre_process(input_image, blob);
            process(blob, net, network_outputs);
            post_process(input_image, output_image, network_outputs);
            bcq_infer2.enqueue(output_image);
            emit send_image();
            print_time(2);
        }
        std::this_thread::yield();
    }
}


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    
    
    ui->setupUi(this);

    capture = new Capture;
    infer1 = new Infer1;
    infer2 = new Infer2;

    connect(infer1, &Infer1::send_image, this, &MainWindow::receive_image);
    connect(infer2, &Infer2::send_image, this, &MainWindow::receive_image);
    connect(capture, &Capture::stop, this, &MainWindow::clear_image);
}

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

void MainWindow::on_pushButton_open_video_clicked()
{
    
    
    video = QFileDialog::getOpenFileName(this, tr("Open Video"), "", tr("(*.mp4 *.avi *.mkv)"));
    if(video.isEmpty())  return;

    capture->set_video(video);

    capture->start();
    infer1->start();
    infer2->start();
}

void MainWindow::receive_image()
{
    
    
    cv::Mat output_image;
    if(bcq_infer1.try_dequeue(output_image))
    {
    
    
        QImage image = QImage((const uchar*)output_image.data, output_image.cols, output_image.rows, QImage::Format_RGB888).rgbSwapped();
        ui->label_1->clear();
        ui->label_1->setPixmap(QPixmap::fromImage(image));
        ui->label_1->show();
    }
    if(bcq_infer2.try_dequeue(output_image))
    {
    
    
        QImage image = QImage((const uchar*)output_image.data, output_image.cols, output_image.rows, QImage::Format_RGB888).rgbSwapped();
        ui->label_2->clear();
        ui->label_2->setPixmap(QPixmap::fromImage(image));
        ui->label_2->show();
    }
}

void MainWindow::clear_image()
{
    
    
    ui->label_1->clear();
    ui->label_2->clear();
}

V4 is realized through Qt's own QThread, QMutex, QWaitCondition

mainwindow.h

#pragma once

#include <iostream>

#include <QMainWindow>
#include <QFileDialog>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>

#include <opencv2/opencv.hpp>

#include "yolov5.h"


QT_BEGIN_NAMESPACE
namespace Ui {
    
     class MainWindow; }
QT_END_NAMESPACE

class Capture : public QThread
{
    
    
  Q_OBJECT

public:
    void set_video(QString video)
    {
    
    
        cap.open(video.toStdString());
    }

private:
    void run();

private:
    cv::VideoCapture cap;

signals:
    void stop();
};

class Infer1 : public QThread
{
    
    
  Q_OBJECT

public:
    void set_model(QString model)
    {
    
    
        net = cv::dnn::readNet(model.toStdString());
    }

private:
    void run();

private:
    cv::dnn::Net net;
    cv::Mat input_image;
    cv::Mat blob;
    cv::Mat output_image;
    std::vector<cv::Mat> network_outputs;

signals:
    void send_image();
    void stop();
};

class Infer2 : public QThread
{
    
    
  Q_OBJECT

public:
    void set_model(QString model)
    {
    
    
        net = cv::dnn::readNet(model.toStdString());
    }

private:
    void run();

private:
    cv::dnn::Net net;
    cv::Mat input_image;
    cv::Mat blob;
    cv::Mat output_image;
    std::vector<cv::Mat> network_outputs;

signals:
    void send_image();
    void stop();
};


class MainWindow : public QMainWindow
{
    
    
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_open_video_clicked();
    void receive_image();
    void clear_image();

private:
    Ui::MainWindow *ui; 
    QString video;
    Capture *capture;
    Infer1 *infer1;
    Infer2 *infer2;
};

mainwindow.cpp

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


bool video_end = false;
QMutex mutex1, mutex2;
QWaitCondition qwc1, qwc2;
cv::Mat g_frame1, g_frame2;
cv::Mat g_result1, g_result2;


void print_time(int id)
{
    
    
    auto now = std::chrono::system_clock::now();
    uint64_t dis_millseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count()
        - std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count() * 1000;
    time_t tt = std::chrono::system_clock::to_time_t(now);
    auto time_tm = localtime(&tt);
    char time[100] = {
    
     0 };
    sprintf(time, "%d-%02d-%02d %02d:%02d:%02d %03d", time_tm->tm_year + 1900,
        time_tm->tm_mon + 1, time_tm->tm_mday, time_tm->tm_hour,
        time_tm->tm_min, time_tm->tm_sec, (int)dis_millseconds);
    std::cout << "infer" << std::to_string(id)  << " 当前时间为:" << time << std::endl;
}

void Capture::run()
{
    
    
    while (cv::waitKey(1) < 0)
    {
    
    
        cv::Mat frame;
        cap.read(frame);
        if (frame.empty())
        {
    
    
            video_end = true;
            cap.release();
            emit stop();
            break;
        }

        g_frame1 = frame;
        qwc1.wakeAll();

        g_frame2 = frame;
        qwc2.wakeAll();
    }
}

void Infer1::run()
{
    
    
    while (true)
    {
    
    
        if(video_end)
        {
    
    
            emit stop();
             break;
        }

        mutex1.lock();
        qwc1.wait(&mutex1);

        input_image = g_frame1;
        pre_process(input_image, blob);
        process(blob, net, network_outputs);
        post_process(input_image, output_image, network_outputs);

        g_result1 = output_image;
        emit send_image();
        mutex1.unlock();
        print_time(1);
    }
}

void Infer2::run()
{
    
    
    while (true)
    {
    
    
        if(video_end)
        {
    
    
            emit stop();
             break;
        }

        mutex2.lock();
        qwc2.wait(&mutex2);

        input_image = g_frame2;
        pre_process(input_image, blob);
        process(blob, net, network_outputs);
        post_process(input_image, output_image, network_outputs);

        g_result2 = output_image;
        emit send_image();
        mutex2.unlock();
        print_time(2);
    }
}


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    
    
    ui->setupUi(this);

    capture = new Capture;
    infer1 = new Infer1;
    infer2 = new Infer2;

    connect(capture, &Capture::stop, this, &MainWindow::clear_image);
    connect(infer1, &Infer1::send_image, this, &MainWindow::receive_image);
    connect(infer1, &Infer1::stop, this, &MainWindow::clear_image);
    connect(infer2, &Infer2::send_image, this, &MainWindow::receive_image);
    connect(infer2, &Infer2::stop, this, &MainWindow::clear_image);
}

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

void MainWindow::on_pushButton_open_video_clicked()
{
    
    
    video = QFileDialog::getOpenFileName(this, tr("Open Video"), "", tr("(*.mp4 *.avi *.mkv)"));
    if(video.isEmpty())  return;

    video_end = false;
    capture->set_video(video);
    infer1->set_model("yolov5n-w640h352.onnx");
    infer2->set_model("yolov5s-w640h352.onnx");

    capture->start();
    infer1->start();
    infer2->start();
}

void MainWindow::receive_image()
{
    
    
    QImage image1 = QImage((const uchar*)g_result1.data, g_result1.cols, g_result1.rows, QImage::Format_RGB888).rgbSwapped();
    ui->label_1->clear();
    ui->label_1->setPixmap(QPixmap::fromImage(image1));
    ui->label_1->show();

    QImage image2 = QImage((const uchar*)g_result2.data, g_result2.cols, g_result2.rows, QImage::Format_RGB888).rgbSwapped();
    ui->label_2->clear();
    ui->label_2->setPixmap(QPixmap::fromImage(image2));
    ui->label_2->show();
}

void MainWindow::clear_image()
{
    
    
    ui->label_1->clear();
    ui->label_2->clear();
    capture->quit();
    infer1->quit();
    infer2->quit();
}

V5 implements through std::mutex, std::condition_variable, std::promise

mainwindow.h

#pragma once

#include <iostream>
#include <string>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <ctime>
#include <windows.h>

#include <QMainWindow>
#include <QFileDialog>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>

#include <opencv2/opencv.hpp>

#include "yolov5.h"


QT_BEGIN_NAMESPACE
namespace Ui {
    
     class MainWindow; }
QT_END_NAMESPACE

class Capture : public QThread
{
    
    
  Q_OBJECT

public:
    void set_capture(QString video)
    {
    
    
        cap.open(video.toStdString());
    }

private:
    void run();

private:
    cv::VideoCapture cap;

signals:
    void show();
    void stop();
};

class Infer1 : public QThread
{
    
    
  Q_OBJECT

public:
    void set_model(QString model)
    {
    
    
        net = cv::dnn::readNet(model.toStdString());
    }

private:
    void run();

private:
    cv::dnn::Net net;
    cv::Mat input_image;
    cv::Mat blob;
    cv::Mat output_image;
    std::vector<cv::Mat> network_outputs;
};

class Infer2 : public QThread
{
    
    
  Q_OBJECT

public:
    void set_model(QString model)
    {
    
    
        net = cv::dnn::readNet(model.toStdString());
    }

private:
    void run();

private:
    cv::dnn::Net net;
    cv::Mat input_image;
    cv::Mat blob;
    cv::Mat output_image;
    std::vector<cv::Mat> network_outputs;
};


class MainWindow : public QMainWindow
{
    
    
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void receive_image();
    void clear_image();
    void on_pushButton_open_video_clicked();

private:
    Ui::MainWindow *ui; 
    QString video;
    Capture *capture;
    Infer1 *infer1;
    Infer2 *infer2;
};

mainwindow.cpp

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


struct Job
{
    
    
    cv::Mat input_image;
    std::shared_ptr<std::promise<cv::Mat>> output_image;
};

std::queue<Job> jobs1, jobs2;

std::mutex lock1, lock2;

std::condition_variable cv1, cv2;

cv::Mat result1, result2;

const int limit = 10;

bool video_end = false;


void print_time(int id)
{
    
    
    auto now = std::chrono::system_clock::now();
    uint64_t dis_millseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count()
        - std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count() * 1000;
    time_t tt = std::chrono::system_clock::to_time_t(now);
    auto time_tm = localtime(&tt);
    char time[100] = {
    
     0 };
    sprintf(time, "%d-%02d-%02d %02d:%02d:%02d %03d", time_tm->tm_year + 1900,
        time_tm->tm_mon + 1, time_tm->tm_mday, time_tm->tm_hour,
        time_tm->tm_min, time_tm->tm_sec, (int)dis_millseconds);
    std::cout << "infer" << std::to_string(id)  << ": 当前时间为:" << time << std::endl;
}

void Capture::run()
{
    
    
    while (cv::waitKey(1) < 0)
    {
    
    
        Job job1, job2;
        cv::Mat frame;

        cap.read(frame);
        if (frame.empty())
        {
    
    
            video_end = true;
            emit stop();
            break;
        }

        {
    
    
            std::unique_lock<std::mutex> l1(lock1);
            cv1.wait(l1, [&]() {
    
     return jobs1.size()<limit; });

            job1.input_image = frame;
            job1.output_image.reset(new std::promise<cv::Mat>());
            jobs1.push(job1);
        }

        {
    
    
            std::unique_lock<std::mutex> l2(lock2);
            cv1.wait(l2, [&]() {
    
     return  jobs2.size() < limit; });

            job2.input_image = frame;
            job2.output_image.reset(new std::promise<cv::Mat>());
            jobs2.push(job2);
        }

        result1 = job1.output_image->get_future().get();
        result2 = job2.output_image->get_future().get();

        emit show();
    }
}

void Infer1::run()
{
    
    
    while (true)
    {
    
    
        if (video_end)
            break; //不加线程无法退出

        if (!jobs1.empty())
        {
    
    
            std::lock_guard<std::mutex> l1(lock1);
            auto job = jobs1.front();
            jobs1.pop();
            cv1.notify_all();

            cv::Mat input_image = job.input_image, blob, output_image;
            pre_process(input_image, blob);

            std::vector<cv::Mat> network_outputs;
            process(blob, net, network_outputs);

            post_process(input_image, output_image, network_outputs);

            job.output_image->set_value(output_image);

            print_time(0);
        }
        std::this_thread::yield(); //不加线程无法退出
    }
}

void Infer2::run()
{
    
    
    cv::dnn::Net net = cv::dnn::readNet("yolov5s-w640h352.onnx");
    while (true)
    {
    
    
        if (video_end)
            break;

        if (!jobs2.empty())
        {
    
    
            std::lock_guard<std::mutex> l2(lock2);
            auto job = jobs2.front();
            jobs2.pop();
            cv2.notify_all();

            cv::Mat input_image = job.input_image, blob, output_image;
            pre_process(input_image, blob);

            std::vector<cv::Mat> network_outputs;
            process(blob, net, network_outputs);

            post_process(input_image, output_image, network_outputs);

            job.output_image->set_value(output_image);

            print_time(1);
        }
        std::this_thread::yield();
    }
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    
    
    ui->setupUi(this);

    capture = new Capture;
    infer1 = new Infer1;
    infer2 = new Infer2;

    connect(capture, &Capture::stop, this, &MainWindow::clear_image);
    connect(capture, &Capture::show, this, &MainWindow::receive_image);
}

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

void MainWindow::receive_image()
{
    
    
    QImage image1 = QImage((const uchar*)result1.data, result1.cols, result1.rows, QImage::Format_RGB888).rgbSwapped();
    ui->label_1->clear();
    ui->label_1->setPixmap(QPixmap::fromImage(image1));
    ui->label_1->show();

    QImage image2 = QImage((const uchar*)result2.data, result2.cols, result2.rows, QImage::Format_RGB888).rgbSwapped();
    ui->label_2->clear();
    ui->label_2->setPixmap(QPixmap::fromImage(image2));
    ui->label_2->show();
}

void MainWindow::clear_image()
{
    
    
    ui->label_1->clear();
    ui->label_2->clear();
    capture->quit();
    infer1->quit();
    infer2->quit();
}

void MainWindow::on_pushButton_open_video_clicked()
{
    
    
    video = QFileDialog::getOpenFileName(this, tr("Open Video"), "", tr("(*.mp4 *.avi *.mkv *mpg *wmv)"));
    if(video.isEmpty())  return;

    video_end = false;
    capture->set_capture(video);
    infer1->set_model("yolov5n-w640h352.onnx");
    infer2->set_model("yolov5s-w640h352.onnx");

    capture->start();
    infer1->start();
    infer2->start();
}

Complete project download link: yolov5 target detection multi-threaded Qt interface

Guess you like

Origin blog.csdn.net/taifyang/article/details/132220062