QtでOpenCV(Threadによる取得)

# はじめに
前回と前々回は、タイマを使用してUSBカメラの映像をLabelやGraphicsViewに出力しました。
今回はタイマをスレッドに変更してUSBカメラの映像を取得してLabelへ出力することにします。
前々回と前回を見ておられない方は、
[前々回](https://qiita.com/tom_tom_tom/items/0490d930a90d94f4df02)
[前回](https://qiita.com/tom_tom_tom/items/50e9f179e6f68685f7f6)
を参照してくだい。
特にUIの部分に関しては、ほぼ前々回の内容のままです。
今回も前回同様に要点のみ記載したいと思います。

# スレッドって何？
[wiki](https://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89)
上記のリンクを参照してください。
# スレッドの処理
メインスレッドではGUIの処理を、サブスレッドでUSBカメラの取得処理をと言う具合にします。
プログラムが起動している間は、サブスレッドでUSBカメラより映像を取得し続けます。
# プロジェクト
今回のプロジェクト名は「OpenCVthread」とします。
# UI
[前々回](https://qiita.com/tom_tom_tom/items/0490d930a90d94f4df02)
と同様にLabelを配置してください。
今回は、前々回と区別するように、Labelの背景色を「水色」にしました。
![Screenshot from 2022-12-16 17-07-42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/d7e26e30-eb50-f402-46b9-0e9acae7dd4d.png)
# 実装
## クラスの新規作成
USBカメラを取得するクラスを作成します。
左上の「ファイル」→「ファイル/プロジェクトの新規作成」をクリックします。(キャプチャできませんでした)
「C++」、「c++クラス」を選択し、「choose...」をクリックします。
![Screenshot from 2022-12-17 08-33-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/bfb2447c-87e9-faa9-7580-23f22976e44c.png)
下記の様に設定します。
![Screenshot from 2022-12-17 10-38-56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/ba76c3da-9d43-3862-1322-7605f144e9eb.png)
「完了」をクリックします。
![Screenshot from 2022-12-17 08-40-17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/f270c767-48d8-9675-385f-a16504510ae6.png)
左上に「getUsbCamera.h」と「getUsbCamera.cpp」が追加されていることがわかります。
![Screenshot from 2022-12-17 08-41-44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/82012cd7-5235-5c5a-9133-749d4f43e636.png)
## ソース修正
mainwindow.h、mainwindow.cpp、getUsbCamera.h、getUsbCamera.cppを下記の通りにします。
```mainwindow.h
#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
```
```mainwindow.cpp
#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));
}
// 追加(ここまで)
```
```getUsbCamera.h
#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
```
```getUsbCamera.cpp
#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ファイル修正
[こちら](https://qiita.com/tom_tom_tom/items/0490d930a90d94f4df02)
と同様に追加を行います。
# build&run
実装が完了しましたら、▶をクリックして、build & run します。
![Screenshot from 2022-12-17 10-51-08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/347e7a33-c7fd-b1ff-3204-57cf75a95a50.png)
(映像が暗くてすみません)
スレッドで取得したUSBカメラの映像が表示しております。
# まとめ
USBカメラの映像をタイマで取得するのが良いのか、スレッドで取得するのが良いのかは利用用途で異なると思います。
私の所感では、数秒毎に取得する場合にタイマで取得、連続的に取得するときは、スレッドでするのが良いと思います。
# 今後の予定
OpenCVのネタが少なくなってきました(笑)
画像の回転やフィルタのネタがありますので、今回のスレッドのプロジェクトを基に追記したいと思います。
その他予定しているものは、MySQLへの接続、テーブルへの追加/修正/削除、JoyPadの取得を考えております。