QtでOpenCV(映像の回転)

# はじめに
前回はスレッドでUSBカメラから映像を取得して表示をしました。
スレッドの処理の章で、
>メインスレッドではGUIの処理を、サブスレッドでUSBカメラの取得処理をと言う具合にします。
プログラムが起動している間は、サブスレッドでUSBカメラより映像を取得し続けます。

と記載しましたが、GUIの処理を付加しておりませんでしたので、「PushButton」をクリックして映像を反転させる処理を追加したいと思います。
今まで、前回や前々回と記載してややこしくなっていますので、今回から投稿したタイトルで表記します。
[QtでOpenCV(Threadによる取得)](https://qiita.com/tom_tom_tom/items/49c0229671f0e55d22e3)
がベースとなっております。
こちらで作成したプロジェクトに機能を追加します。
# 仕様
上PushButton：回転しないで映像を表示
下PushButton：180度回転した映像を表示
左PushButton：270度回転した映像を表示
右PushButton：90度回転した映像を表示
とします。
# UI
## PushButtonの追加と名称やオブジェクト名修正
仕様の通り、4つの「PushButton」を追加します。mainwindow.uiを開きます。
![Screenshot from 2022-12-17 13-18-18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/506ee99a-1dea-9753-4381-4c96df67be1e.png)
「PushButton」を配置するのに、スペースが足りませんので、「Label」を左に移動します。
![Screenshot from 2022-12-17 13-19-27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/da484cde-3d10-446d-9361-9e8f24d0992f.png)
これで右側にスペースができましたので、「PushButton」を配置します。
[QtでHello world!](https://qiita.com/tom_tom_tom/items/5cc7b3334b9fda9c7dc7)
を参考に、「PushButton」を4つ配置してください。
![Screenshot from 2022-12-17 13-21-50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/ff51fb5e-0603-ecbb-3e77-1fd61051efdb.png)
配置した「PushButton」のテキストがすべて「PushButton」の表記で、操作する人が分かりませんので変更します。
先ず、1番上の「PushButton」をクリックして、左下にある「Text」の「PushButton」を「rotateUp」にします。変更すると「PushButton」の表記か「rotateUp」に変わります。
![Screenshot from 2022-12-17 13-26-22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/a904eefa-13d9-8d75-c7bc-acc27f3331b1.png)
「objectName」も変更します。
「pushButton」、「pushButton_2」、「pushButton_3」、「pushButton_4」となっておりますので、実装するときに分かり易い名称へ変更します。
「pushButton」を「pushButtonUp」へ変更します。
![Screenshot from 2022-12-17 13-30-35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/6407e2d3-5635-9a82-b37a-1b29b399d0d6.png)
「pushButton_2」、「pushButton_3」、「pushButton_4」も同様に変更します。
全て修正すると、下記のようになると思います。
![Screenshot from 2022-12-17 13-36-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/9139ffb2-bd93-8895-c25e-442a34908c77.png)
## イベントを発生させる
「rotateUp」、「rotateDown」、「rotateLeft」、「rotateRight」にクリックのイベントを発生させるようにします。詳しくは、
[QtでHello world!](https://qiita.com/tom_tom_tom/items/5cc7b3334b9fda9c7dc7)
の「イベントを発生させる」を参照してください。
今回の操作で、mainwindo.hとmainwindow.cppに下記が追加されます。
```mainwindow.h
private slots:
	void on_pushButtonUp_clicked();
	void on_pushButtonDown_clicked();
	void on_pushButtonLeft_clicked();
	void on_pushButtonRight_clicked();
```
```mainwindow.cpp
void MainWindow::on_pushButtonUp_clicked()
{

}


void MainWindow::on_pushButtonDown_clicked()
{

}


void MainWindow::on_pushButtonLeft_clicked()
{

}


void MainWindow::on_pushButtonRight_clicked()
{

}
```
Ctrl+sで保存し、mainwindow.uiを閉じます。
# 実装
追加は、「追加rotate」と記載しております。
修正は、「修正rotate」と記載しております。
```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;									// 追加
	unsigned char btnRotate;						// 追加rotate
public slots:
	void onValueChangedCam(void);					// 追加
private slots:
	void on_pushButtonUp_clicked();
	void on_pushButtonDown_clicked();
	void on_pushButtonLeft_clicked();
	void on_pushButtonRight_clicked();
};
#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()){
		btnRotate = POSITION_UP;					// 追加rotate
		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);
	getUsbCamT->getImage(&dst, btnRotate);			// 修正rotate
	qtImage = QImage((const unsigned char*)(dst.data), dst.cols, dst.rows, QImage::Format_RGB888);
	ui->label->setPixmap(QPixmap::fromImage(qtImage));
}
// 追加(ここまで)

void MainWindow::on_pushButtonUp_clicked()
{
	btnRotate = POSITION_UP;						// 追加rotate
}


void MainWindow::on_pushButtonDown_clicked()
{
	btnRotate = POSITION_DOWN;						// 追加rotate
}


void MainWindow::on_pushButtonLeft_clicked()
{
	btnRotate = POSITION_LEFT;						// 追加rotate
}


void MainWindow::on_pushButtonRight_clicked()
{
	btnRotate = POSITION_RIGHT;						// 追加rotate
}
```
```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>
// 追加(ここまで)

#define POSITION_UP		0							// 追加rotate
#define POSITION_DOWN	1							// 追加rotate
#define POSITION_LEFT	2							// 追加rotate
#define POSITION_RIGHT	3							// 追加rotate

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*);						修正rotateのため削除
	void getImage(cv::Mat*, unsigned char);			// 修正rotate
	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);
    }
}

/* 修正rotateのため削除
void getUsbCamera::getImage(cv::Mat *image)
{
	*image = dst.clone();
}
*/
void getUsbCamera::getImage(cv::Mat *image, unsigned char mode)	// 修正rotate
{
	// 追加rotate(ここから)
	switch(mode){
		case POSITION_UP:
			*image = dst.clone();
			break;
		case POSITION_DOWN:
			cv::rotate(dst, *image, cv::ROTATE_180);
			break;
		case POSITION_LEFT:
			cv::rotate(dst, *image, cv::ROTATE_90_COUNTERCLOCKWISE);
			break;
		case POSITION_RIGHT:
			cv::rotate(dst, *image, cv::ROTATE_90_CLOCKWISE);
			break;
	}
	// 追加rotate(ここまで)
}
// 追加(ここまで)
```
# build&run
実装が完了しましたら、▶をクリックして、build & run します。
USBカメラの映像(上)
![Screenshot from 2022-12-17 14-26-10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/d2c4c1cf-8b31-0c4e-a4dd-c136d628cae2.png)
USBカメラの映像(下)
![Screenshot from 2022-12-17 14-26-33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/9aae00ef-e713-e6ce-5bdc-1de3989e25a7.png)
USBカメラの映像(左)
![Screenshot from 2022-12-17 14-26-42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/b9f40ae4-ecde-0084-c568-28374b817a31.png)
USBカメラの映像(右)
![Screenshot from 2022-12-17 14-26-49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/c238b2cc-a2ce-f041-311d-452f60ccab58.png)
あれっ？って気が付かれている方もおられると思います。
左右のPushButtonをクリックしたときに、Labelの背景が表示しています。
また、上下のときと比べて左右の映像が切れております。
更に映像が左に寄っています。
UIでLabelのgeometryは下記に設定しました。
```幅.
640
```
```高さ.
480
```
映像を左右にすると、Labelの高さが映像の幅になりますので、映像が左右切れてしまいました。
修正してみましょう。
# UI修正
windowの高さを下記のように変更
```高さ.
600 → 700
```
labelの高さを下記のように変更
```高さ.
480 → 640
```
labelのalignmentを下記のように変更
```alignment.
左揃え → 中央揃え(横方向)
```
Labelの位置も適切な位置にします。
![Screenshot from 2022-12-17 14-35-56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/cdb5a8e3-95a3-ebad-27bc-a7235346cbf2.png)
![Screenshot from 2022-12-17 14-36-29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/141e9211-52ec-e145-2c18-e48fe988b21d.png)
![Screenshot from 2022-12-17 14-44-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/c2b36683-9a9b-5ca9-81c6-20301c9d8076.png)Ctrl+sで保存し、mainwindow.uiを閉じます。
# 再度build&run
USBカメラの映像(上)
![Screenshot from 2022-12-17 14-42-09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/9cb5269d-7fe2-0f74-b586-3ce1d042ec52.png)
USBカメラの映像(下)
![Screenshot from 2022-12-17 14-42-15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/e857dba9-9b58-0e33-ba9c-0bbce69412b4.png)
USBカメラの映像(左)
![Screenshot from 2022-12-17 14-42-20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/87f241f7-607e-869f-3696-965d94825c82.png)
USBカメラの映像(右)
![Screenshot from 2022-12-17 14-42-24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1473918/6a4fd6de-8860-8e18-2ad9-771d263ad18f.png)
映像が切れることなく表示しました。
また映像もLabelの中央になっています。
# まとめ
映像を回転させるときは、Labelの背景色は無い方が無難だと思います。