我正在使用Opencv进行一些实时视频处理。
作为前端,我正在使用QT框架。
在我的GUI上,我有一个输入图像窗口(映射到标签)和一个输出图像窗口(映射到另一个标签)和3个按钮。一个用于开始输入视频捕获,第二个用于处理视频(代码尚未写入),第三个用于退出。
我目前能够流式传输视频并将其显示在前端。但是这会锁定我的GUI并且无法退出。
我尝试使用QTimers(使用来自此和QT论坛的建议),但我的GUI仍然处于锁定状态。
如果有人能指出我正确的方向,我们将不胜感激。
以下是代码:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp> // for cvtColor
#include <iostream>
#include <QTimer>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_buttonCaptureVideo_clicked();
void on_buttonExit_clicked();
public slots:
virtual void doNextFrame() {repaint();}
private:
Ui::MainWindow *ui;
CvCapture *capture; // OpenCV Video Capture Variable
IplImage *frame; // Variable to capture a frame of the input video
cv::Mat source_image; // Variable pointing to the same input frame
cv::Mat dest_image; // Variable to output a frame of the processed video
QTimer *imageTimer;
};
#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;
cvReleaseImage(&frame);
cvReleaseCapture(&capture);
}
void MainWindow::on_buttonCaptureVideo_clicked()
{
// Set to 25 frames per second
const int imagePeriod = 1000/25; // ms
imageTimer = new QTimer(this);
imageTimer->setInterval(imagePeriod);
connect(imageTimer, SIGNAL(timeout()), this, SLOT(doNextFrame()));
// Use the default camera
capture = cvCreateCameraCapture(-1);
while(capture)
{
// Capture a frame
frame = cvQueryFrame(capture);
// Point to the same frame
source_image = frame;
// Resize Image
cv::resize(source_image, source_image, cv::Size(128,128) , 0, 0);
// Change to RGB format
cv::cvtColor(source_image,source_image,CV_BGR2RGB);
// Convert to QImage
QImage qimg = QImage((const unsigned char*) source_image.data, source_image.cols, source_image.rows, QImage::Format_RGB888); // convert to QImage
// Display on Input Label
ui->labelInputVideo->setPixmap(QPixmap::fromImage(qimg));
// Resize the label to fit the image
ui->labelInputVideo->resize(ui->labelInputVideo->pixmap()->size());
}
}
void MainWindow::on_buttonExit_clicked()
{
connect(ui->buttonExit, SIGNAL(clicked()), qApp, SLOT(closeAllWindows()));
}
答案 0 :(得分:5)
单击按钮时,
while(capture) { ... }
循环将永远运行,因为capture
永远不会设置为NULL。
这意味着代码流永远不会离开你的循环,因此主线程不能处理任何其他东西,例如重绘。
QTimer将发出其timeout()信号,但它们将作为事件放在Qt的事件队列中。只要您的on_buttonCaptureVideo_clicked()
方法正在运行,就不会处理这些事件。
以下是我的建议:如何使其发挥作用:
此代码:
// Set to 25 frames per second
const int imagePeriod = 1000/25; // ms
imageTimer = new QTimer(this);
imageTimer->setInterval(imagePeriod);
connect(imageTimer, SIGNAL(timeout()), this, SLOT(doNextFrame()));
// Use the default camera
capture = cvCreateCameraCapture(-1);
属于MainWindow的构造函数,因为您只想将其设置为一次。当用户点击第二,第三等时间按钮时,无需再次执行此操作。
while
循环中的代码应该进入doNextFrame()
广告位(没有while结构)。
然后你的按钮只会
imageTimer->start();
然后例如做
imageTimer->stop();
再次点击它时。
示例代码:
void MainWindow::on_buttonCaptureVideo_clicked()
{
if( imageTimer->isActive() )
{
imageTimer->stop();
}
else
{
imageTimer->start();
}
}
如果你这样做会怎么样?
当您单击按钮时,将从GUI线程调用on_buttonCaptureVideo_clicked()
单击的插槽,计时器将启动,并且该方法几乎立即返回。
现在GUI线程是免费的,能够处理重绘等
从那时起,定时器将每隔40ms发送一次timeout()信号。每当GUI线程空闲时,它将处理此信号并呼叫您的doNextFrame
插槽
此插槽将捕获下一帧并在完成后返回。完成后,GUI线程将能够再次处理其他事件(例如重绘)
只要再次单击该按钮,计时器就会停止,并且不会发送新的timeout()事件。如果在单击按钮后仍然看到几帧,这可能意味着计时器事件的发送速度超过了处理时间。
答案 1 :(得分:2)
前言:我在C ++方面不强,所以我不能提供特定的代码,但我在PyQt中经验
对于Qt的新人来说,这是一个常见的陷阱。您的on_buttonCaptureVideo_clicked
正在做的似乎是在您的主GUI线程中进入循环以执行工作。在QT中,您希望避免在主线程中执行任何繁忙操作。 Qt eventloop需要能够在GUI事件进入时不断处理和清除它们。您正在做的是阻止事件循环。
你可以在这里做两件事。第一种是更基本的方法,但可以让您看到更直接的结果。你可以“抽”eventloop。根据{{1}}循环的迭代速度,您可以调用while
。这将允许Qt处理待处理的GUI事件,并使您的应用程序看起来更具响应性。它基本上在你的while循环和主循环之间共享时间。也许你想在每第n帧调用它。取决于您希望确保GUI刷新的频率。
另一个更优选的选项是将捕获循环放入QThread。当新帧可用时,您可以使用帧数据发出信号。信号将被置于Qt事件循环中,以便与其他所有内容一起处理,并且不会阻止您的GUI。一般来说,这是您想要使用任何重型运算或长时间运行的callables的方法。
修改强>
我刚刚意识到除了在主线程中执行循环外,还启动了QTimer。如果你想使用QTimer,并且你的图像处理不是太重(每个周期不需要很长时间),那么你应该将所有内容移到qApp->processEvents();
并完全删除doNextFrame
循环。如果您的while
是一个繁重的过程,那么您应该使用QThread并发出信号。