单线程
首先我们在Qt designer中创建一个简单的界面,只包含一个label用来显示相机图像,和两个pushButton分别用来开启和关闭相机。
其对应的ui_mainwindow.h内容为:
/********************************************************************************
** Form generated from reading UI file 'mainwindow.ui'
**
** Created by: Qt User Interface Compiler version 5.14.2
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/
#ifndef UI_MAINWINDOW_H
#define UI_MAINWINDOW_H
#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QLabel>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QStatusBar>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
class Ui_MainWindow
{
public:
QWidget *centralwidget;
QLabel *label;
QPushButton *pushButton_open_camera;
QPushButton *pushButton_close_camera;
QMenuBar *menubar;
QStatusBar *statusbar;
void setupUi(QMainWindow *MainWindow)
{
if (MainWindow->objectName().isEmpty())
MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
MainWindow->resize(800, 600);
centralwidget = new QWidget(MainWindow);
centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
label = new QLabel(centralwidget);
label->setObjectName(QString::fromUtf8("label"));
label->setGeometry(QRect(80, 20, 640, 480));
label->setStyleSheet(QString::fromUtf8("background:rgb(255, 255, 255)"));
pushButton_open_camera = new QPushButton(centralwidget);
pushButton_open_camera->setObjectName(QString::fromUtf8("pushButton_open_camera"));
pushButton_open_camera->setGeometry(QRect(150, 520, 120, 30));
pushButton_close_camera = new QPushButton(centralwidget);
pushButton_close_camera->setObjectName(QString::fromUtf8("pushButton_close_camera"));
pushButton_close_camera->setGeometry(QRect(530, 520, 120, 30));
MainWindow->setCentralWidget(centralwidget);
menubar = new QMenuBar(MainWindow);
menubar->setObjectName(QString::fromUtf8("menubar"));
menubar->setGeometry(QRect(0, 0, 800, 21));
MainWindow->setMenuBar(menubar);
statusbar = new QStatusBar(MainWindow);
statusbar->setObjectName(QString::fromUtf8("statusbar"));
MainWindow->setStatusBar(statusbar);
retranslateUi(MainWindow);
QMetaObject::connectSlotsByName(MainWindow);
} // setupUi
void retranslateUi(QMainWindow *MainWindow)
{
MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "MainWindow", nullptr));
label->setText(QString());
pushButton_open_camera->setText(QCoreApplication::translate("MainWindow", "\346\211\223\345\274\200\347\233\270\346\234\272", nullptr));
pushButton_close_camera->setText(QCoreApplication::translate("MainWindow", "\345\205\263\351\227\255\347\233\270\346\234\272", nullptr));
} // retranslateUi
};
namespace Ui {
class MainWindow: public Ui_MainWindow {};
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_MAINWINDOW_H
main.cpp不用修改:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
接下来我们编写mainwindow的逻辑:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <opencv2/opencv.hpp>
#include <QMainWindow>
#include <QThread>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_open_camera_clicked();
void on_pushButton_close_camera_clicked();
private:
Ui::MainWindow *ui;
cv::VideoCapture cap;
cv::Mat frame;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_open_camera_clicked()
{
cap.open(0);
while (cv::waitKey(1) < 0)
{
cap.read(frame);
QImage image = QImage((const uchar*)frame.data, frame.cols, frame.rows, QImage::Format_RGB888).rgbSwapped();
ui->label->setPixmap(QPixmap::fromImage(image));
ui->label->show();
}
}
void MainWindow::on_pushButton_close_camera_clicked()
{
cap.release();
ui->label->clear();
}
这样,一个单线程Qt相机采图显示界面就做完了。
一个线程采图一个线程显示
修改上面的程序,引入相机采图类Capture并继承QThread类:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <opencv2/opencv.hpp>
#include <QMainWindow>
#include <QThread>
#include <QDebug>
class Capture : public QThread
{
Q_OBJECT
public:
cv::VideoCapture cap;
bool is_opened = false;
private:
void run();
signals:
void send_image(const QImage &qimage);
};
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_open_camera_clicked();
void on_pushButton_close_camera_clicked();
void show_image(const QImage &qimage);
private:
Ui::MainWindow *ui;
Capture *capture;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
capture = new Capture;
connect(capture, &Capture::send_image, this, &MainWindow::show_image);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_open_camera_clicked()
{
capture->cap.open(0);
capture->start();
capture->is_opened = true;
}
void MainWindow::on_pushButton_close_camera_clicked()
{
capture->quit();
capture->cap.release();
ui->label->clear();
capture->is_opened = false;
}
void MainWindow::show_image(const QImage &qimage)
{
std::cout << std::this_thread::get_id() << " show image..." << std::endl;
ui->label->setPixmap(QPixmap::fromImage(qimage));
ui->label->show();
}
void Capture::run()
{
cv::Mat frame;
while (cv::waitKey(1) < 0 && is_opened)
{
std::cout << std::this_thread::get_id() << " grab image..." << std::endl;
cap.read(frame);
QImage qimage = QImage(frame.data, frame.cols, frame.rows, QImage::Format_RGB888).rgbSwapped();
send_image(qimage);
}
}
运行程序,相机采图正常,且打印如下:
14324 grab image...
7456 show image...
14324 grab image...
7456 show image...
14324 grab image...
7456 show image...
14324 grab image...
7456 show image...
可以看到采图和显示图像的线程id是不同的,分别在两个线程中运行。
一个线程采图一个线程运算一个线程显示
在上面的基础上继续增加图像处理类Process 并继承QThread类:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <opencv2/opencv.hpp>
#include <QMainWindow>
#include <QThread>
#include <QDebug>
class Capture : public QThread
{
Q_OBJECT
public:
cv::VideoCapture cap;
bool is_opened = false;
private:
void run();
signals:
void send_image(const QImage &qimage);
};
class Process : public QThread
{
Q_OBJECT
public:
void receive_image(const QImage &qimage);
bool is_opened = false;
private:
void run();
QImage image;
signals:
void send_image(const QImage &qimage);
};
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_open_camera_clicked();
void on_pushButton_close_camera_clicked();
void show_image(const QImage &qimage);
private:
Ui::MainWindow *ui;
Capture *capture;
Process *process;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
capture = new Capture;
process = new Process;
connect(capture, &Capture::send_image, process, &Process::receive_image);
connect(process, &Process::send_image, this, &MainWindow::show_image);
}
MainWindow::~MainWindow()
{
capture->quit();
capture->cap.release();
process->quit();
delete ui;
delete capture;
delete process;
}
void MainWindow::on_pushButton_open_camera_clicked()
{
capture->cap.open(0);
capture->start();
process->start();
capture->is_opened = true;
process->is_opened = true;
}
void MainWindow::on_pushButton_close_camera_clicked()
{
capture->is_opened = false;
process->is_opened = false;
capture->quit();
capture->cap.release();
process->quit();
ui->label->clear();
}
void MainWindow::show_image(const QImage &qimage)
{
if(!capture->is_opened) return;
std::cout << std::this_thread::get_id() << " show image..." << std::endl;
ui->label->setPixmap(QPixmap::fromImage(qimage));
ui->label->show();
}
void Capture::run()
{
cv::Mat frame;
while (is_opened && cv::waitKey(1) < 0)
{
std::cout << std::this_thread::get_id() << " grab image..." << std::endl;
cap.read(frame);
if (frame.empty()) continue;
QImage qimage = QImage(frame.data, frame.cols, frame.rows, QImage::Format_RGB888).rgbSwapped();
send_image(qimage);
}
}
void Process::run()
{
while(is_opened && cv::waitKey(1) < 0)
{
std::cout << std::this_thread::get_id() << " process image..." << std::endl;
QImage qimage_processed = image.convertToFormat(QImage::Format_Grayscale8);
send_image(qimage_processed);
}
}
void Process::receive_image(const QImage &qimage)
{
image = qimage;
}
程序运行时打印如下:
5768 grab image...
13116 process image...
7832 show image...
5768 grab image...
13116 process image...
7832 show image...
5768 grab image...
13116 process image...
7832 show image...