当前位置: 首页 > news >正文

yolov5目标检测多线程Qt界面

上一篇文章:yolov5目标检测多线程C++部署

V1 基本功能实现

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_NAMESPACEclass Infer1 : public QThread
{Q_OBJECTpublic 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_OBJECTpublic 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_OBJECTpublic: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);}
}

这里引入的第三方库moodycamel::ConcurrentQueue是一个用C++11实现的多生产者、多消费者无锁队列。
程序输出:

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
...

界面效果:
在这里插入图片描述

可以看到,上面的程序实现了两个模型的多线程推理,但由于不同模型推理速度有差异,导致画面显示不同步。另外,把读取视频帧的实现写入主线程时,一旦视频帧读取结束则无法处理后面的帧,导致显示卡死。

V2 修正画面不同步问题

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_NAMESPACEclass Capture : public QThread
{Q_OBJECTpublic:void set_video(QString video){cap.open(video.toStdString());}private:void run();private:cv::VideoCapture cap;
};class Infer1 : public QThread
{Q_OBJECTpublic 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_OBJECTpublic 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_OBJECTpublic: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();
}

界面显示:
在这里插入图片描述

V3 修正视频播放完成界面显示问题

和V2比较,V3的改动不大,仅增加在视频播放完成时发出信号调用清除界面显示的功能。
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_NAMESPACEclass Capture : public QThread
{Q_OBJECTpublic:void set_video(QString video){cap.open(video.toStdString());}private:void run();private:cv::VideoCapture cap;signals:void stop();
};class Infer1 : public QThread
{Q_OBJECTprivate: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_OBJECTprivate: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_OBJECTpublic: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 通过Qt自带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_NAMESPACEclass Capture : public QThread
{Q_OBJECTpublic:void set_video(QString video){cap.open(video.toStdString());}private:void run();private:cv::VideoCapture cap;signals:void stop();
};class Infer1 : public QThread
{Q_OBJECTpublic: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_OBJECTpublic: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_OBJECTpublic: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 通过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_NAMESPACEclass Capture : public QThread
{Q_OBJECTpublic: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_OBJECTpublic: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_OBJECTpublic: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_OBJECTpublic: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();
}

相关文章:

yolov5目标检测多线程Qt界面

上一篇文章&#xff1a;yolov5目标检测多线程C部署 V1 基本功能实现 mainwindow.h #pragma once#include <iostream>#include <QMainWindow> #include <QFileDialog> #include <QThread>#include <opencv2/opencv.hpp>#include "yolov5.…...

[ubuntu]创建root权限的用户 该用户登录后自动切换为root用户

一、创建新用户 1、创建新用户 sudo useradd -r -m -s /bin/bash 用户名 # -r&#xff1a;建立系统账号 -m&#xff1a;自动建立用户的登入目录 -s&#xff1a;指定用户登入后所使用的shell2、手动为用户设置密码 passwd 用户名 二、为用户增加root权限 1、添加写权限 ch…...

大连交通大学813软件工程考研习题

1.什么是软件生存周期模型?有哪些主要模型? 生存周期模型&#xff1a;描述软件开发过程中各种活动如何执行的模型。对软件开发提供强有力的支持&#xff0c;为开发过程中的活动提供统一的政策保证&#xff0c;为参与开发的人员提供帮助和指导&#xff0c;是软件生存周期模型…...

分布式协议与算法——Paxos算法

目录 Paxos算法Basic Paxos算法三种角色如何达成共识&#xff08;协商过程&#xff09;小结&#xff1a; Multi-Paxos算法关于 Multi-Paxos 的思考领导者优化Basic PaxosChubby 的 Multi-Paxos 实现小结 参考 Paxos算法 Paxos论文 Paxos Made Simple 、author&#xff1a;Lesli…...

Spring中的Websocket身份验证和授权

目录 一、需要了解的事项二、依赖三、WebSocket 配置3.1 、简单的消息代理3.2 、Spring安全配置 一、需要了解的事项 http和WebSocket的安全链和安全配置是完全独立的。SpringAuthenticationProvider根本不参与 Websocket 身份验证。将要给出的示例中&#xff0c;身份验证不会…...

【果树农药喷洒机器人】Part7:静态PWM变量喷药实验

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…...

C++ 网络编程项目fastDFS分布是文件系统(一)

目录 1.项目架构图 1. 项目架构图 1.1 一些概念 1.2 项目架构图 2. 分布式文件系统 2.1 传统文件系统 2.2 分布式文件系统 3. FastDFS 3.1 fastDFS介绍 3.2 fastDFS安装 3.3 fastDFS配置文件 3.4 fastDFS的启动 4. fastDFS状态检测 4.1 对file_id的解释 4. 2上传…...

PoseiSwap 开启“Poseidon”池,治理体系或将全面开启

PoseiSwap 曾在前不久分别以 IDO、IEO 的方式推出了 POSE 通证&#xff0c;但 PoseiSwap DEX 中并未向除 Zepoch 节点外的角色开放 POSE 资产的交易。而在前不久&#xff0c;PoseiSwap 推出了全新的“Poseidon”池&#xff0c;该池将向所有用户开放&#xff0c;并允许用户自由的…...

【C/C++】重载运算符特性

重载运算符是 C 中的一个重要特性&#xff0c;它允许程序员自定义类类型的运算符行为。重载运算符的使用场景包括&#xff1a; 使类类型的对象能够像内置类型一样进行运算&#xff1a;例如&#xff0c;可以重载加号运算符&#xff0c;使两个对象相加时能够像两个整数相加一样。…...

HTML+JavaScript构建一个将C/C++定义的ANSI字符串转换为MASM32定义的DWUniCode字符串的工具

公文一键排版系统基本完成&#xff0c;准备继续完善SysInfo&#xff0c;增加用户帐户信息&#xff0c;其中涉及到Win32_Account结构&#xff0c;其C定义如下&#xff1a; [Dynamic, Provider("CIMWin32"), UUID("{8502C4CC-5FBB-11D2-AAC1-006008C78BC7}"…...

24届近3年南京信息工程大学自动化考研院校分析

今天给大家带来的是南京信息工程大学控制考研分析 满满干货&#xff5e;还不快快点赞收藏 一、南京信息工程大学 学校简介 南京信息工程大学位于南京江北新区&#xff0c;是一所以大气科学为特色的全国重点大学&#xff0c;由江苏省人民政府、中华人民共和国教育部、中国气…...

【LeetCode】1572.矩阵对角线元素的和

题目 给你一个正方形矩阵 mat&#xff0c;请你返回矩阵对角线元素的和。 请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。 示例 1&#xff1a; 输入&#xff1a;mat [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;25 解释&#xff1a;对角线的和为&a…...

2023-08-08 Ubuntu 挂载U盘 fdisk -l 、sudo mount /dev/sdb1 /mnt/mydisk

一、基本命令 1、插入U盘&#xff0c;查看U盘是否被系统识别&#xff1a; 打开终端&#xff0c;输入&#xff1a; sudo fdisk -l 查看系统是否识别U盘&#xff0c;如果识别&#xff0c;会显示U盘的相关信息&#xff0c;如果没有识别&#xff0c;则说明系统没有识别U盘。 2…...

make/makefile/cmake/nmake

最近&#xff0c;有没有被make、cmake、makefile... ... 这些东西绕晕了&#xff0c;看看下面的文章&#xff0c;也许就可以理解清楚了。 1. gcc 它是GNU Compiler Collection&#xff08;就是GNU编译器套件&#xff09;&#xff0c;也可以简单认为是编译器&#xff0c;它可以…...

实用的开源应用全新开源源码Vue3+TS的前后台分离的开发平台源码开源技术社区平台

开源全新开源源码Vue3TS的前后台分离的开发平台源码。创意、新奇、有趣、实用的开源应用、系统、软件、硬件及技术&#xff0c;一个探索、发现、分享、使用与互动交流的开源技术社区平台。致力于打造活力开源社区&#xff0c;共建开源新生态&#xff01; 功能简介 个人办公&a…...

C++复习笔记——primer第五版

文章目录 一、引用和指针1.引用2.指针2.1利用指针访问对象2.2指针的值或指针所指对象的值的改变 3.赋值和指针4.指向指针的引用 二、String1.初始化String对象的方式2. string对象上的操作3.使用getline读取一整行4.字面值和字符串相加5.使用for循环改变字符串中的字符 三、Vec…...

ElasticSearch:项目实战(2)

ElasticSearch: 项目实战 (1) 需求&#xff1a; 新增文章审核通过后同步数据到es索引库 1、文章服务中添加消息发送方法 在service层文章新增成功后&#xff0c;将数据通过kafka消息同步发送到搜索服务 Autowiredprivate KafkaTemplate<String,String> kafkaTemplate;/…...

Eleastisearch5.2.2利用镜像迁移构建实例后ES非健康状态

正常迁移完成后启动服务&#xff0c;查看ES非健康状态 此时观察ES集群状态&#xff1a;curl -XGET -u elastic:xxx localhost:9200/_cluster/health?pretty 注意到"active_shards_percent_as_number" : 88.8888 该项的值不产生变化;集群状态"status" : “…...

sealos安装k8s

一、前言 1、我前面文章有写过使用 kubeadm 安装的方式&#xff0c;大家可以去参考 &#xff08;二&#xff09;k8s集群安装&#xff0c;有一系列的k8s文章说明 2、安装k8s的方式有很多 kubeadmsealoskubespray等等 3、关于sealos来安装 k8s &#xff0c;也是非常建议大家去…...

经典贪吃蛇游戏 - 用 C 语言实现控制台版

在本篇博客中&#xff0c;我们将一起来实现经典的贪吃蛇游戏&#xff0c;使用 C 语言编写&#xff0c;并在控制台中运行。这个小游戏会让你回忆起童年的经典游戏体验。我们将从游戏的初始化开始&#xff0c;逐步实现游戏的各个功能&#xff0c;包括蛇的移动、食物的生成、得分的…...

VSCode主题定制:CSS个性化你的编程世界

在今天的数字世界&#xff0c;编程环境已成为开发者的第二大脑&#xff0c;而主题正是个性化你的创意空间的关键。本文将指导你如何使用CSS自定义VSCode的主题&#xff0c;让你的IDE不仅功能强大&#xff0c;更具视觉个性。 思路分析 设计思路&#xff1a; 创建主色调基调和…...

几种简单的排序算法(C语言)

目录 1 简介 2 冒泡排序 2.1 基本思路 2.2 代码实现 3 选择排序 3.1 基本思路 3.2 代码实现 4 插入排序 4.1 基本思路 4.2 代码实现 5 快速排序 5.1 基本思路 5.2 代码实现 6 归并排序 6.1 基本思路 6.2 代码实现 7 基数排序 7.1 基本思路 7.2 代码实现 8 …...

RT-Thread内核组成——内核移植

内核移植就是指将 RT-Thread 内核在不同的芯片架构、不同的板卡上运行起来&#xff0c;能够具备线程管理和调度&#xff0c;内存管理&#xff0c;线程间同步和通信、定时器管理等功能。移植可分为 CPU 架构移植和 BSP&#xff08;Board support package&#xff0c;板级支持包&…...

越狱蒸馏-可再生安全基准测试

大家读完觉得有帮助记得关注&#xff01;&#xff01;&#xff01; 摘要 大型语言模型&#xff08;LLMs&#xff09;正迅速部署在关键应用中&#xff0c;这引发了对稳健安全基准测试的迫切需求。我们提出了越狱提炼&#xff08;JBDISTILL&#xff09;&#xff0c;这是一种新颖…...

GitHub 趋势日报 (2025年06月05日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 1472 onlook 991 HowToCook 752 ChinaTextbook 649 quarkdown 451 scrapy 324 age…...

【方案分享】蓝牙Beacon定位精度优化(包含KF、EKF与UKF卡尔曼滤波算法详解)

蓝牙Beacon定位精度优化&#xff1a;KF、EKF与UKF卡尔曼滤波算法详解 标签&#xff1a;蓝牙定位&#xff5c;Beacon&#xff5c;卡尔曼滤波&#xff5c;UKF&#xff5c;EKF&#xff5c;RSSI&#xff5c;室内定位&#xff5c;滤波算法&#xff5c;精度优化 相关分享&#xff1a;…...

30.【新型数据架构】-区块链数据架构

30.【新型数据架构】-区块链数据架构:分布式账本,不可篡改性,用于数据溯源 一、区块链数据架构的本质:分布式账本的革新 区块链的核心是分布式账本技术(Distributed Ledger Technology, DLT),它颠覆了传统中心化数据库的架构模式: 去中心化存储: 账本数据不再集中存储…...

AI Agent开发第78课-大模型结合Flink构建政务类长公文、长文件、OA应用Agent

开篇 AI Agent2025确定是进入了爆发期,到处都在冒出各种各样的实用AI Agent。很多人、组织都投身于开发AI Agent。 但是从3月份开始业界开始出现了一种这样的声音: AI开发入门并不难,一旦开发完后没法用! 经历过至少一个AI Agent从开发到上线的小伙伴们其实都听到过这种…...

VTK 显示文字、图片及2D/3D图

1. 基本环境设置 首先确保你已经安装了VTK库&#xff0c;并配置好了C开发环境。 #include <vtkSmartPointer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkRenderer.h> 2. 显示文字 2D文字 #include &l…...

为什么说数列是特殊的函数

文章目录 前情概要函数特性特殊之处典例剖析前情概要 高三的学生几乎都听老师说过,数列是特殊的函数,那么如何理解这句话呢,无外乎需要关注两点:①函数性,②特殊性,以下举例说明,帮助各位学子理解。 函数特性 既然是按照一定的次序排列而成的一列数字,那么这些数字(…...