【问题标题】:QT GUI freezes when capturing video from webcam using OpenCV使用 OpenCV 从网络摄像头捕获视频时 QT GUI 冻结
【发布时间】:2012-07-21 08:12:39
【问题描述】:

我正在使用 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

主窗口.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()));
}

【问题讨论】:

  • capture 评估为真需要多长时间? while 循环运行多长时间?
  • @jdi :只要用户希望流式传输输入视频,捕获应该评估为真。我已经让 GUI 现在使用 QTimer 进行响应,但我注意到如果我让流式传输活动时间过长,我会收到消息“相机丢帧!”,所以我的猜测是最终我需要转移到 QThreads。感谢您的回复。
  • @Sid,你能发布代码吗?谢谢

标签: qt user-interface opencv webcam


【解决方案1】:

前言:我不擅长 C++,所以无法提供具体代码,但我在 PyQt 方面经验丰富

对于 Qt 新手来说,这是一个常见的陷阱。看起来你的on_buttonCaptureVideo_clicked 正在做的是在你的主 GUI 线程中进入一个循环来完成工作。在 QT 中,您希望避免在主线程中做任何忙碌的事情。 Qt 事件循环需要能够在它们进入时不断处理和刷新您的 GUI 事件。您正在做的是阻塞事件循环。

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

另一个更可取的选项是将捕获循环放入 QThread。当新帧可用时,您可以使用帧数据发出信号。该信号将被放置到 Qt 事件循环中以与其他所有内容一起处理,并且不会阻止您的 GUI。通常,这是您希望对任何繁重的处理或长时间运行的可调用对象采取的方法。

编辑

我刚刚意识到除了在主线程中执行循环之外,您还启动了 QTimer。如果您想使用 QTimer,并且您的图像处理不是太重(每个周期不需要很长时间),那么您应该将所有内容移入 doNextFrame 并完全删除 while 循环。如果您的 doNextFrame 是一个繁重的进程,那么您应该使用 QThread 和信号。

【讨论】:

    【解决方案2】:

    当您点击按钮时,

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

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

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

    【讨论】:

    • 感谢您的回复。我能够根据您的建议让 GUI 做出响应。
    猜你喜欢
    • 1970-01-01
    • 2017-04-27
    • 2015-08-18
    • 1970-01-01
    • 2015-07-17
    • 1970-01-01
    • 2014-06-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多