2012-07-23 94 views
6

我正在使用Opencv进行一些实时视频处理。使用OpenCV从网络摄像头捕捉视频时,QT GUI冻结

作为前端,我正在使用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

“捕获”评估为真的时间有多长?那个while循环运行多久了? – jdi 2012-07-23 04:58:37

+0

@jdi:只要用户希望流式传输输入视频,捕获应该评估为真。我已经得到了使用QTimer进行响应的GUI,但是我注意到,如果我将流动激活时间过长,我会收到消息“Camera dropped frame!”,所以我的猜测是最终我需要移动到QThreads。感谢您的回复。 – Sid 2012-07-24 02:58:43

+0

@Sid,你可以发布代码吗?谢谢 – 2013-03-27 17:50:33

回答

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); 

属于到主窗口的构造函数:


此代码。当用户在第二,第三等时间点击按钮时,不需要再次执行该操作。

您的while循环内的代码应该进入doNextFrame()插槽(不含while结构)。

然后你的按钮只会做

imageTimer->start(); 

,然后如做

imageTimer->stop(); 

当它再次点击。

示例代码:

void MainWindow::on_buttonCaptureVideo_clicked() 
{ 
    if(imageTimer->isActive()) 
    { 
     imageTimer->stop(); 
    } 
    else 
    { 
     imageTimer->start(); 
    } 
} 

如果你这样做,会发生什么?

当你点击按钮,你的on_buttonCaptureVideo_clicked()被点击的槽将从GUI线程被调用,定时器将被启动,并且该方法将几乎立即返回。
现在,GUI线程是空闲的,并且能够处理重新绘制等。
从那时开始,定时器将每隔40ms发送一次timeout()信号。每当GUI线程空闲时,它将处理这个信号并调用你的doNextFrame插槽。
此插槽将捕获下一帧,并在完成时返回。完成后,GUI线程将能够再次处理其他事件(例如重绘)。
只要再次单击该按钮,计时器将停止,并且不会发送新的超时()事件。如果点击按钮后仍然看到两帧,这可能意味着计时器事件的发送速度比它们可以处理的速度快。

+0

感谢您的回复。根据您的建议,我可以让GUI做出回应。 – Sid 2012-07-24 03:00:07

2

前言:我不坚强在C++中,所以我不能提供具体的代码,但我在PyQt的

经历这对于初来乍到的Qt的一个常见问题。看起来你的on_buttonCaptureVideo_clicked正在做的是在你的主GUI线程中进入一个循环来完成工作。在QT中,你希望避免在主线程中忙于任何事情。 Qt eventloop需要能够不断处理和刷新你的GUI事件。你正在做的是阻止事件循环。

你可以在这里做两件不同的事情。第一种是更基本的方法,但可以让你看到更直接的结果。您可以“抽取”事件回复。根据您的while循环迭代的速度,您可以拨打qApp->processEvents();。这将允许Qt处理未决的GUI事件,并使您的应用看起来更具响应性。它基本上共享while循环和主循环之间的时间。也许你想在每第n帧调用一次。取决于你想确保GUI刷新的频率。

另一种选择,更可取的方法是将捕获循环放入QThread中。当一个新的帧可用时,你可以发出一个信号与帧数据。该信号将被放置到Qt事件循环中并与其他所有事件一起处理,并且不会妨碍您的GUI。一般来说,这是你想要采取的任何重型嘎吱嘎吱或长时间运行的可卡因的方法。

编辑

我才意识到,你除了在主线程做一个循环开始QTimer。如果你想使用QTimer,并且你的图像处理不是太重(每个周期不需要很长的时间),那么你应该将所有东西都移动到doNextFrame并完全移除while循环。如果你的doNextFrame是一个沉重的过程,那么你应该使用QThread和信号。

相关问题