はじめに
前回と前々回は、タイマを使用してUSBカメラの映像をLabelやGraphicsViewに出力しました。
今回はタイマをスレッドに変更してUSBカメラの映像を取得してLabelへ出力することにします。
前々回と前回を見ておられない方は、
前々回
前回
を参照してくだい。
特にUIの部分に関しては、ほぼ前々回の内容のままです。
今回も前回同様に要点のみ記載したいと思います。
スレッドって何?
wiki
上記のリンクを参照してください。
スレッドの処理
メインスレッドではGUIの処理を、サブスレッドでUSBカメラの取得処理をと言う具合にします。
プログラムが起動している間は、サブスレッドでUSBカメラより映像を取得し続けます。
プロジェクト
今回のプロジェクト名は「OpenCVthread」とします。
UI
前々回
と同様にLabelを配置してください。
今回は、前々回と区別するように、Labelの背景色を「水色」にしました。
実装
クラスの新規作成
USBカメラを取得するクラスを作成します。
左上の「ファイル」→「ファイル/プロジェクトの新規作成」をクリックします。(キャプチャできませんでした)
「C++」、「c++クラス」を選択し、「choose...」をクリックします。
下記の様に設定します。
「完了」をクリックします。
左上に「getUsbCamera.h」と「getUsbCamera.cpp」が追加されていることがわかります。
ソース修正
mainwindow.h、mainwindow.cpp、getUsbCamera.h、getUsbCamera.cppを下記の通りにします。
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMessageBox> // 追加
#include <QThread> // 追加
#include "getUsbCamera.h" // 追加
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
getUsbCamera *getUsbCamT; // 追加
private:
Ui::MainWindow *ui;
QImage qtImage; // 追加
public slots:
void onValueChangedCam(void); // 追加
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 追加(ここから)
getUsbCamT = new getUsbCamera(this);
if(getUsbCamT->initCam()){
connect(getUsbCamT, SIGNAL(valueChangedCam(void)), this, SLOT(onValueChangedCam(void)));
getUsbCamT->start();
} else {
QMessageBox msgBox(this);
msgBox.setText(tr("カメラがオープンできませんでした。"));
msgBox.setWindowTitle(tr("エラー"));
msgBox.setStandardButtons(QMessageBox::Yes);
msgBox.setDefaultButton(QMessageBox::Yes);
msgBox.setIcon(QMessageBox::Warning);
msgBox.exec();
exit(0);
}
// 追加(ここまで)
}
MainWindow::~MainWindow()
{
getUsbCamT->Stop = true; // 追加
delete ui;
}
// 追加(ここから)
void MainWindow::onValueChangedCam(void)
{
cv::Mat dst;
getUsbCamT->getImage(&dst);
qtImage = QImage((const unsigned char*)(dst.data), dst.cols, dst.rows, QImage::Format_RGB888);
ui->label->setPixmap(QPixmap::fromImage(qtImage));
}
// 追加(ここまで)
#ifndef GETUSBCAMERA_H
#define GETUSBCAMERA_H
#include <QThread>
#include <QObject>
// 追加(ここから)
#include <QMutex>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>
// 追加(ここまで)
class getUsbCamera : public QThread
{
Q_OBJECT
public:
// getUsbCamera(); 修正のため削除
// 追加(ここから)
getUsbCamera(QObject *parent = 0, bool b = false);
~getUsbCamera();
bool initCam(void);
void run(void);
void getImage(cv::Mat*);
bool Stop;
signals:
void valueChangedCam(void);
private:
cv::VideoCapture cap;
cv::Mat frame, dst;
// 追加(ここまで)
};
#endif // GETUSBCAMERA_H
#include "getUsbCamera.h"
// 修正のため削除
//getUsbCamera::getUsbCamera()
// 追加(ここから)
getUsbCamera::getUsbCamera(QObject *parent, bool b) : QThread(parent), Stop(b)
{
}
getUsbCamera::~getUsbCamera()
{
Stop = true;
}
bool getUsbCamera::initCam(void)
{
bool ret = true;
cap.open(0);
if(cap.isOpened()){
cap.set(cv::CAP_PROP_FRAME_WIDTH, 640);
cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480);
} else {
ret = false;
}
return ret;
}
void getUsbCamera::run(void)
{
QMutex mutex;
while(1){
mutex.lock();
if(this->Stop) break;
mutex.unlock();
cap >> frame;
cv::cvtColor(frame, dst, cv::COLOR_BGR2RGB);
emit valueChangedCam();
this->msleep(20);
}
}
void getUsbCamera::getImage(cv::Mat *image)
{
*image = dst.clone();
}
// 追加(ここまで)
.proファイル修正
こちら
と同様に追加を行います。
build&run
実装が完了しましたら、▶をクリックして、build & run します。
(映像が暗くてすみません)
スレッドで取得したUSBカメラの映像が表示しております。
まとめ
USBカメラの映像をタイマで取得するのが良いのか、スレッドで取得するのが良いのかは利用用途で異なると思います。
私の所感では、数秒毎に取得する場合にタイマで取得、連続的に取得するときは、スレッドでするのが良いと思います。
今後の予定
OpenCVのネタが少なくなってきました(笑)
画像の回転やフィルタのネタがありますので、今回のスレッドのプロジェクトを基に追記したいと思います。
その他予定しているものは、MySQLへの接続、テーブルへの追加/修正/削除、JoyPadの取得を考えております。