Qt GUI зависает при захвате видео с веб-камеры с помощью OpenCV

Я использую Opencv для обработки видео в реальном времени.

в качестве интерфейса я использую Qt framework.

на моем 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()));
}

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

и затем например, do

imageTimer->stop();

при повторном нажатии.

пример кода:

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

что произойдет, если вы это сделаете?

когда вы нажимаете кнопку, ваш on_buttonCaptureVideo_clicked() щелчок слот будет вызван из потока GUI, таймер будет запущен, и метод вернется почти мгновенно.
Теперь поток GUI свободен и может обрабатывать перекраски и т. д.
С этого момента таймер будет посылать сигналы timeout () каждые 40 мс. Всякий раз, когда поток GUI свободен, он будет обрабатывать этот сигнал и вызывать ваш doNextFrame слот.
Этот слот захватит следующий кадр и вернется, когда это будет сделано. Когда это будет сделано, поток GUI сможет снова обработать другие события (например, перекрасить).
Как только вы снова нажмете кнопку, таймер остановится, и никакие новые события timeout() не будут отправлены. Если вы все еще видите пару кадров после нажатия кнопки, это может означать, что события таймера были отправлены быстрее, чем их можно обработать.


предисловие: я не силен в C++, поэтому я не могу предложить конкретный код, но у меня есть опыт в PyQt

это обычная ловушка для новичков в Qt. Какой она кажется вашей on_buttonCaptureVideo_clicked делает это ввод в цикл в основной поток GUI для выполнения работы. В QT вы хотите избежать чего-либо занятого в своем основном потоке. Qt eventloop должен иметь возможность постоянно обрабатывать и очищать ваши события GUI по мере их поступления. То, что вы делаете блокирует цикл обработки событий.

есть две разные вещи, которые вы можете сделать здесь. Первый-более базовый подход, но позволяет увидеть более непосредственные результаты. Вы можете "прокачать" eventloop. В зависимости от того, как быстро ваш while цикл повторяется, вы можете вызвать qApp->processEvents();. Это позволит Qt обрабатывать ожидающие события GUI и сделать ваше приложение более отзывчивым. Его в основном совместное время между вашим циклом while и основным циклом. Может быть, вы хотите назвать это на каждом N-м кадре. Зависит от того, как часто вы хотите убедитесь, что GUI обновляется.

другой вариант, который более предпочтителен, - поместить цикл захвата в QThread. Когда новый кадр, можно излучать сигнал с данными. Сигнал будет помещен в цикл событий Qt для обработки со всем остальным и не будет удерживать ваш графический интерфейс. Как правило, это подход, который вы хотите использовать с любыми тяжелыми хрустящими или длительными вызовами.

редактировать

Я просто понял, что вы запускаете QTimer в дополнение к выполнению цикла в основном потоке. Если вы хотите использовать QTimer, и ваша обработка изображений не слишком тяжелая (это не займет много времени за цикл), то вы должны переместить все в doNextFrame и полностью удалить while петли. Если doNextFrame тяжелый процесс, тогда вы должны использовать QThread и сигналы.