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 и сигналы.