6

リアルタイムのビデオ処理に Opencv を使用しています。

フロントエンドとして QT フレームワークを使用しています。

私の GUI には、入力画像ウィンドウ (ラベルにマップ) と出力画像ウィンドウ (別のラベルにマップ) と 3 つのプッシュ ボタンがあります。1 つは入力ビデオ キャプチャを開始し、2 番目はビデオを処理し (コードはまだ記述されていません)、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()));
}
4

2 に答える 2

5

ボタンをクリックすると、

while(capture) { ... }

loop はcaptureNULL に設定されないため、永久に実行されます。これは、コード フローがループを離れることはないため、メイン スレッドは再描画などの他の処理を実行できないことを意味します。

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 のコンストラクターに属します。ユーザーがボタンを 2 回目、3 回目などとクリックしたときに再度行う必要はありません。

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() シグナルを送信します。doNextFrameGUI スレッドが空いているときはいつでも、このシグナルを処理してスロットを呼び出します。
このスロットは次のフレームをキャプチャし、完了すると戻ります。それが完了すると、GUI スレッドは他のイベント (再描画など) を再び処理できるようになります。
ボタンをもう一度クリックするとすぐにタイマーが停止し、新しい timeout() イベントは送信されません。ボタンがクリックされた後も数フレームが表示される場合は、タイマー イベントが処理されるよりも速く送信されたことを意味している可能性があります。

于 2012-07-23T05:07:53.860 に答える
2

序文: 私は C++ が苦手なので特定のコードを提供することはできませんが、PyQt の経験はあります

これは、Qt の初心者にとってよくある落とし穴です。あなたon_buttonCaptureVideo_clickedがしているように見えるのは、メインの GUI スレッドでループに入って作業を行うことです。QT では、メイン スレッドでビジー状態になることは避けたいと考えています。Qt のイベントループは、GUI イベントが発生したときに常に処理してフラッシュできる必要があります。あなたがしていることは、イベント ループをブロックすることです。

ここでできることは 2 つあります。1 つ目はより基本的なアプローチですが、より迅速な結果を確認できます。イベントループを「ポンピング」できます。ループの反復速度に応じてwhile、 を呼び出すことができますqApp->processEvents();。これにより、Qt が保留中の GUI イベントを処理できるようになり、アプリの応答性が向上します。基本的に、while ループとメイン ループの間で時間を共有します。たぶん、n 番目のフレームごとにこれを呼び出したいと思うでしょう。GUI を確実に更新する頻度によって異なります。

より望ましいもう 1 つのオプションは、キャプチャ ループを QThread に配置することです。新しいフレームが利用可能になったら、フレーム データで信号を送信できます。信号は Qt イベント ループに配置され、他のすべてで処理され、GUI を保持しません。一般に、これは、負荷の高いクランチまたは長時間実行される callable で採用するアプローチです。

編集

メインスレッドでループを実行するだけでなく、QTimer を開始することに気付きました。QTimer を使用する必要があり、画像処理が重すぎない (1 サイクルあたりの時間がかからない) 場合は、すべてを に移動し、ループdoNextFrameを完全に削除する必要があります。while重いプロセスの場合doNextFrameは、QThread とシグナルを使用する必要があります。

于 2012-07-23T05:09:33.473 に答える