使用 while 循环结合waitKey和使用 Qt 的 QTimerEvent进行定时任务处理,在GUI应用程序中是两种截然不同的方法。
while循环结合 waitKey
原理:
这种方法通常用于简单的CV应用,特别是在不涉及复杂UI界面的情况下。while 循环连续读取视频帧,waitKey 函数用于捕获键盘输入,并允许一定程度的UI响应性,相当于在帧中间的延迟。
缺点:
阻塞UI:
在GUI应用中,如果使用 while 循环,尤其是在Qt的主线程中运行,会阻塞事件循环,有可能导致UI无响应或延迟响应用户操作。
CPU资源消耗:
持续运行的 while 循环可能导致CPU使用率较高,尤其是循环体内延迟较低时。
QTimerEvent
原理:
信号与槽机制,事件通知机制
优点:
非阻塞:
使用 QTimer 不会阻塞Qt的事件循环,保持应用的响应性和流畅性。
资源高效:
定时器仅在指定的时间间隔激活,可以更有效地管理CPU资源,降低CPU使用率。
下面是一个无bug+考虑了大多数边缘情况的while(1)+waitKey的代码实现
界面:
一个button + 一个label
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.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QDebug>
#include <QMainWindow>
#include <QPixmap>
#include <opencv.hpp>
#include <QMessageBox>
using namespace cv;
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_clicked();
private:
Ui::MainWindow *ui;
};
#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_clicked()
{
VideoCapture vc;
if (!vc.open(0)) {
QMessageBox::warning(this, "open failed !", "open failed !");
return;
}
Mat frame;
while (1) {
vc.read(frame);
if (frame.empty()) {
break;
}
rectangle(frame,
Rect(frame.cols / 2 - 100, frame.rows / 2 - 100, 200, 200),
Scalar(0, 0, 255));
cvtColor(frame, frame, COLOR_BGR2RGB);
QImage image(frame.data, frame.cols, frame.rows, frame.step, QImage::Format_RGB888);
ui->label->setPixmap(QPixmap(QPixmap::fromImage(image)));
waitKey(1);
}
vc.release();
destroyAllWindows();
}
改成Qtimerevent:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QDebug>
#include <QMainWindow>
#include <QMessageBox>
#include <QPixmap>
#include <QTimerEvent>
#include <opencv.hpp>
using namespace cv;
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_clicked();
private:
Ui::MainWindow *ui;
VideoCapture vc;
int timerId;
// QObject interface
protected:
void timerEvent(QTimerEvent *event);
};
#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);
if (!vc.open(0)) {
QMessageBox::warning(this, "open failed !", "open failed !");
return;
}
timerId = startTimer(33);
}
MainWindow::~MainWindow()
{
if (vc.isOpened()) {
vc.release();
}
delete ui;
}
void MainWindow::on_pushButton_clicked() {}
void MainWindow::timerEvent(QTimerEvent *event)
{
//区分多个计时器
if (event->timerId() == timerId) {
Mat frame;
vc.read(frame);
if (frame.empty()) {
QMessageBox::warning(this, "no signals !", "no signals !");
return;
}
rectangle(frame,
Rect(frame.cols / 2 - 100, frame.rows / 2 - 100, 200, 200),
Scalar(0, 0, 255));
cvtColor(frame, frame, COLOR_BGR2RGB);
QImage image(frame.data, frame.cols, frame.rows, frame.step, QImage::Format_RGB888);
ui->label->setPixmap(QPixmap::fromImage(image));
}
}