Multithreading Qt+;OpenCV使用std::thread播放视频

Multithreading Qt+;OpenCV使用std::thread播放视频,multithreading,qt,opencv,Multithreading,Qt,Opencv,这是我的GUI, 我想在这里做的是一起显示四个不同的视频。用户输入特定视频文件的路径并单击“打开”,应创建一个新线程来播放该视频 我有一个名为startThread()的插槽,以及一个名为openCamera()的线程中要调用的函数 在mainwindow.hpp中,它具有 public: static void openCamera(const std::string& address, QLabel* label); private slots: void star

这是我的GUI,

我想在这里做的是一起显示四个不同的视频。用户输入特定视频文件的路径并单击“打开”,应创建一个新线程来播放该视频

我有一个名为startThread()的插槽,以及一个名为openCamera()的线程中要调用的函数

在mainwindow.hpp中,它具有

public:
    static void openCamera(const std::string& address, QLabel* label);
private slots:
    void startThread();
void MainWindow::startThread() {
    std::string address;
    QLabel* label;
    // Get the input from GUI and assign it to std::string address
    // and find the label corresponding to clicked button.
    // ...
    std::thread t(openCamera, address, label);
}
void MainWindow::openCamera(std::string address, QLabel* label) {
    cv::VideoCapture cap(address);
    cv::Mat frame;
    while (cap.isOpened()) {
        cap >> frame;
        // cv::imwrite("/Users/wking/Desktop/test.jpg", frame); // This function works!
        label->setPixmap(QPixmap::fromImage(QImage(frame.data, frame.cols, frame.rows, frame.step, QImage::Format_RGB888).rgbSwapped()));
    }
}
在mainwindow.cpp中,它具有

public:
    static void openCamera(const std::string& address, QLabel* label);
private slots:
    void startThread();
void MainWindow::startThread() {
    std::string address;
    QLabel* label;
    // Get the input from GUI and assign it to std::string address
    // and find the label corresponding to clicked button.
    // ...
    std::thread t(openCamera, address, label);
}
void MainWindow::openCamera(std::string address, QLabel* label) {
    cv::VideoCapture cap(address);
    cv::Mat frame;
    while (cap.isOpened()) {
        cap >> frame;
        // cv::imwrite("/Users/wking/Desktop/test.jpg", frame); // This function works!
        label->setPixmap(QPixmap::fromImage(QImage(frame.data, frame.cols, frame.rows, frame.step, QImage::Format_RGB888).rgbSwapped()));
    }
}

我想通过使用std::thread来实现这一点。然而,它没有显示任何东西。在我用正确的输入单击按钮后,程序不会更新标签。我错过什么了吗?谢谢。

在Qt中,您只能从主线程访问小部件。您可以使用Qt信号插槽机制将数据从工作线程传递到主线程:

class MainWindow
{
    ...

    static void openCamera(const std::string& address, QLabel* label, MainWindow *window);

signals:
    void updateLabelSig(QLabel *label, QPixmap pic);

private slots:
    void updateLabel(QLabel *label, QPixmap pic);

    ...
}

MainWindow::MainWindow()
{
    connect(this, SIGNAL(updateLabelSig(QLabel*, QPixmap), this, SLOT(QLabel*, QPixmap));
    ...
}

void MainWindow::startThread() 
{
    std::string address;
    QLabel* label;
    // Get the input from GUI and assign it to std::string address
    // and find the label corresponding to clicked button.
    // ...
    std::thread t(openCamera, address, label, this);
}

void MainWindow::openCamera(std::string address, QLabel* label, MainWindow *window) 
{
    cv::VideoCapture cap(address);
    cv::Mat frame;
    while (cap.isOpened()) {
        cap >> frame;
        // cv::imwrite("/Users/wking/Desktop/test.jpg", frame); // This function works!

        QPixmap pic = QPixmap::fromImage(QImage(frame.data, frame.cols, frame.rows, frame.step, QImage::Format_RGB888).rgbSwapped());

        window->updateLabelSig(label, pic);
    }
}

void MainWindow::updateLabel(QLabel *label, QPixmap pic)
{
     label->setPixmap(pic);
}
只有主线程(GUI线程)可以更新UI。
我有一个类似的项目。我使用QThread和Worker类。 工人类有一个插槽来读取视频文件。如果读取完一帧,它将发出一个信号。
在主窗口中,我创建了一些QThread,将辅助对象移动到线程中。
将Worker的信号框与在标签上显示cv::Mat的功能连接起来

class Worker : public QObject
{
    Q_OBJECT
public:
    Worker(QString path, int id);
    ~Worker();

public slots:
    void readVideo(QString path = "");

signals:
    // frame and index of label which frame will be displayed
    void frameFinished(cv::Mat frame, int index);

    void finished(int index);

private:
    QString filepath;
    int index;
};
这是worker.cpp

#include "worker.h"
#include <QDebug>
#include <QThread>
#include <QTime>
Worker::Worker(QString path, int id) : filepath(path), index(id)
{

}

Worker::~Worker()
{
}

void Worker::readVideo(QString path)
{
    if (path.length() > 0)
        filepath = path;

    cv::VideoCapture cap(filepath.toStdString());

    if (! cap.isOpened())
    {
        qDebug() << "Can't open video file " << filepath;
        emit finished(index);
        return;
    }

    cv::Mat frame;
    while (true)
    {
        cap >> frame;
        if (frame.empty())
        {
            frame = cv::Mat(cv::Size(720, 576), CV_8UC3, cv::Scalar(192, 0, 0));
            emit frameFinished(frame, index);
            break;
        }


        emit frameFinished(frame.clone(), index);
        QThread::msleep(30);
    }

    emit finished(index);
}
#包括“worker.h”
#包括
#包括
#包括
Worker::Worker(QString路径,int-id):文件路径(path),索引(id)
{
}
工人::~Worker()
{
}
void Worker::readVideo(QString路径)
{
if(path.length()>0)
filepath=path;
cv::VideoCapture cap(filepath.toStdString());
如果(!cap.isOpened())
{
qDebug()框架;
if(frame.empty())
{
frame=cv::Mat(cv::Size(720576),cv_8UC3,cv::Scalar(192,0,0));
发射frameFinished(帧,索引);
打破
}
emit frameFinished(frame.clone(),index);
QThread::msleep(30);
}
发射完成(索引);
}
主窗口

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <opencv2/opencv.hpp>

#include "worker.h"

#define MAX_NUM_CAM 8

namespace Ui {
class MainWindow;
}

class QThread;
class QLabel;

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    void init();

private slots:
    void displayFrame(cv::Mat frame, int index);


private:
    Ui::MainWindow *ui;

    int numCams;
    QLabel *labels[MAX_NUM_CAM];
    QThread* threads[MAX_NUM_CAM];
    Worker* workers[MAX_NUM_CAM];
};

#endif // MAINWINDOW_H
\ifndef主窗口
#定义主窗口
#包括
#包括
#包括“worker.h”
#定义最大数量凸轮8
名称空间用户界面{
类主窗口;
}
类QThread;
类QLabel;
类主窗口:公共QMainWindow
{
Q_对象
公众:
显式主窗口(QWidget*parent=0);
~main窗口();
void init();
专用插槽:
void displayFrame(cv::Mat frame,int index);
私人:
Ui::MainWindow*Ui;
int numCams;
QLabel*标签[MAX_NUM_CAM];
QThread*线程[MAX_NUM_CAM];
工人*工人[MAX_NUM_CAM];
};
#endif//main窗口
mainwindow.cpp

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

#include <QDebug>
#include <QThread>
#include <QLabel>
#include <QGridLayout>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    qRegisterMetaType< cv::Mat >("cv::Mat");

    qDebug() << "Main thread " << QThread::currentThreadId();
    init();
}

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

void MainWindow::init()
{
    QGridLayout *grid = new QGridLayout;
    int numCols = 2;

    numCams = 4;

    int row = 0, col = 0;
    for (int i = 0; i < numCams; i++)
    {
        labels[i] = new QLabel;

        row = i / numCols;
        col = i % numCols;
        grid->addWidget(labels[i], row, col);


        threads[i] = new QThread;
        workers[i] = new Worker(QString("/home/shang/Videos/%1.mp4").arg(i+1), i);
        workers[i]->moveToThread(threads[i]);

        connect(workers[i], SIGNAL(frameFinished(cv::Mat, int)), this, SLOT(displayFrame(cv::Mat,int)));
        connect(threads[i], SIGNAL(started()), workers[i], SLOT(readVideo()));

        connect(workers[i], SIGNAL(finished(int)), threads[i], SLOT(quit()));
        connect(workers[i], SIGNAL(finished(int)), workers[i], SLOT(deleteLater()));

        connect(threads[i], SIGNAL(finished()), threads[i], SLOT(deleteLater()));

        threads[i]->start();
    }

    this->centralWidget()->setLayout(grid);

}

void MainWindow::displayFrame(cv::Mat frame, int index)
{
    QPixmap p = QPixmap::fromImage(QImage(frame.data, frame.cols, frame.rows, frame.step, QImage::Format_RGB888).rgbSwapped());
    p = p.scaled(QSize(frame.cols/2, frame.rows/2));
    labels[index]->setPixmap(p);
}
#包括“mainwindow.h”
#包括“ui_main window.h”
#包括
#包括
#包括
#包括
主窗口::主窗口(QWidget*父窗口):
QMainWindow(父级),
用户界面(新用户界面::主窗口)
{
用户界面->设置用户界面(此);
qRegisterMetaType(“cv::Mat”);
qDebug()移动到线程(threads[i]);
连接(workers[i],信号(frameFinished(cv::Mat,int)),这个,插槽(displayFrame(cv::Mat,int));
连接(线程[i]、信号(已启动()、工作线程[i]、插槽(readVideo());
连接(worker[i]、信号(finished(int))、线程[i]、插槽(quit());
连接(workers[i],信号(finished(int)),workers[i],插槽(deleteLater());
连接(线程[i]、信号(finished()、线程[i]、插槽(deleteLater());
线程[i]->start();
}
此->centralWidget()->setLayout(网格);
}
void主窗口::displayFrame(cv::Mat frame,int索引)
{
QPixmap p=QPixmap::fromImage(QImage(frame.data,frame.cols,frame.rows,frame.step,QImage::Format_RGB888).rgbSwapped());
p=p.scaled(QSize(frame.cols/2,frame.rows/2));
标签[index]>setPixmap(p);
}

1.可能您错过了
t.detach()


2.更新ui:您可以从子线程发出信号,然后在插槽函数中更新ui。(插槽函数将在主线程中运行)

在Qt中,您只能从主线程访问小部件。如果我想从其他线程中提取小部件,该怎么办?我想同时播放几个视频。你有更好的处理线程的方法吗?