2012-07-23 70 views
6

Estoy usando Opencv para un procesamiento de video en tiempo real.La GUI de QT se congela al capturar video desde la cámara web usando OpenCV

Como front-end estoy usando QT framework.

En mi GUI, tengo una ventana de imagen de entrada (asignada a una etiqueta) y una ventana de imagen de salida (asignada a otra etiqueta) y 3 botones. Una para iniciar la captura de video de entrada, la segunda para procesar el video (código no escrito aún) y la tercera para Salir.

Actualmente puedo transmitir mi video y mostrarlo en el Front-end. Pero esto bloquea mi GUI y no puedo salir.

Intenté usar QTimers (usando sugerencias de este y del foro de QT), pero mi GUI aún permanece bloqueada.

Agradecería que alguien me señale la dirección correcta.

A continuación se muestra el código:

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

¿Cuánto dura la evaluación de 'capture' en true? ¿Cuánto tiempo dura ese bucle while? – jdi

+0

@jdi: Mientras el usuario desee transmitir el video de entrada, la captura debería ser verdadera. He recibido la GUI para que responda ahora usando QTimer, pero me doy cuenta de que si dejo activa la transmisión durante demasiado tiempo, recibo los mensajes "Camera dropped frame!", Así que supongo que con el tiempo tendré que moverme a QThreads. Gracias por su respuesta. – Sid

+0

@Sid, ¿podría publicar el código funcionando, por favor? Gracias –

Respuesta

5

Al hacer clic en el botón, el bucle

while(capture) { ... } 

se ejecutará siempre, como nunca habrá capture establecer a NULL Esto significa que el flujo de código nunca abandona el ciclo y, por lo tanto, el hilo principal no puede procesar nada más, como p. Ej. repintar

El QTimer emitirá sus señales de tiempo de espera(), pero se colocarán como Eventos en la cola de eventos de Qt. Siempre que su método on_buttonCaptureVideo_clicked() se esté ejecutando, esos Eventos no serán procesados.

Aquí están mis sugerencias de cómo hacer que funcione:


este código:

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

pertenece al constructor de MainWindow como usted quiere poner esto en marcha sólo una vez. No es necesario volver a hacerlo cuando el usuario hace clic en el botón la segunda, la tercera, etc.

El código que está dentro de su lazo while debe ir a la ranura doNextFrame() (sin la construcción while).

A continuación, el botón sólo hará

imageTimer->start(); 

y luego, por ejemplo, do

imageTimer->stop(); 

cuando se hace clic de nuevo.

código Ejemplo:

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

¿Qué pasará si se hace eso?

Al hacer clic en el botón, se llamará a su ranura on_buttonCaptureVideo_clicked() que hizo clic desde la secuencia de la GUI, se iniciará el temporizador y el método volverá casi al instante.
Ahora el hilo de la GUI es libre y es capaz de manejar repintados, etc.
A partir de ese momento, el temporizador enviará señales de tiempo de espera() cada 40 ms. Siempre que el hilo de la GUI esté libre, manejará esta señal y llamará a su ranura doNextFrame.
Esta ranura capturará el siguiente cuadro y lo devolverá cuando lo haya hecho. Cuando esté listo, el hilo de la GUI podrá procesar otros Eventos (por ejemplo, volver a pintar) nuevamente.
Tan pronto como vuelva a hacer clic en el botón, el temporizador se detendrá y no se enviarán nuevos eventos de timeout(). Si aún ve un par de fotogramas después de hacer clic en el botón, esto podría significar que los eventos del temporizador se enviaron más rápido de lo que podrían procesarse.

+0

Gracias por su respuesta. Pude hacer que la GUI respondiera en función de su sugerencia. – Sid

2

Prefacio: No soy fuerte en C++ por lo que puede no ofrece código específico, pero tengo experiencia en PyQt

Este es un error común para los recién llegados a Qt. Lo que parece que su on_buttonCaptureVideo_clicked está haciendo es ingresar un bucle en su hilo principal de GUI para hacer el trabajo. En QT, quieres evitar hacer algo ocupado en tu hilo principal. Qt eventloop necesita poder procesar y eliminar constantemente los eventos de tu GUI a medida que entran. Lo que estás haciendo es bloquear el ciclo de eventos.

Hay dos cosas diferentes que puede hacer aquí. El primero es el enfoque más básico, pero le permite ver resultados más inmediatos. Puedes "bombear" el evento en loop. Dependiendo de la velocidad con la que itera el ciclo while, puede llamar al qApp->processEvents();. Esto permitirá a Qt procesar los eventos GUI pendientes y hacer que su aplicación parezca más receptiva. Básicamente, comparte tiempo entre tu ciclo while y el ciclo principal. Quizás quieras llamar esto en cada enésimo fotograma. Depende de la frecuencia con la que desee asegurarse de que la GUI se actualice.

La otra opción, que es más preferible, es colocar su bucle de captura en un QThread. Cuando hay un nuevo fotograma disponible, puede emitir una señal con los datos del fotograma. La señal se colocará en el ciclo de eventos Qt para ser procesada con todo lo demás y no mantendrá su GUI. En general, este es el enfoque que desea tomar con cualquier crujido pesado o callables de larga ejecución.

Editar

me he dado cuenta de que se inicia un QTimer además de hacer un bucle en el hilo principal. Si desea utilizar un QTimer y su procesamiento de imagen no es demasiado pesado (no requiere mucho tiempo por ciclo), debe mover todo al doNextFrame y eliminar por completo el bucle while. Si su doNextFrame es un proceso pesado, entonces debe usar un QThread y señales.

Cuestiones relacionadas