【Visual Studio】OpenCVで動画出力ファイル作成

#はじめに
######※これは、VC++ Visual Studioを利用する初心者向けの学習用記事です。
今回はVisual Studioを利用し、画像処理ライブラリOpenCVを用いて、出力として動画(.avi, .mp4, etc)を作成する方法説明する。
OpenCV 4.0.0を利用しているが、本記事で説明する関数は、2.4.xx～4.xまで含まれているので利用できると思います。

#OpenCVでの動画出力の関数
OpenCVで動画として出力する方法は、以下の関数を利用する。

```cpp:
cv::VideoWriter::VideoWriter();
```

```cpp:動画出力ファイルの宣言
cv::VideoWriter writer("Video.mp4", VideoWriter::fourcc('m', 'p', '4', 'v'), 30.0, Size(640, 480), true);
//宣言時に出力生成ファイル名を「Video.mp4」として宣言。
//fourccにより、動画ファイルのコーデックを指定。
//動画ファイルのfpsを30.0に指定。
//動画ファイルの画像サイズを「横幅:640, 縦幅:480」に指定。
//動画ファイルのチャンネル数　カラー画像(３チャンネル)を保存する場合はTrue、
//グレースケール画像(１チャンネル)を保存する場合は、Falseを指定する。

cv::VideoWriter writer; 		//先に変数のみの宣言を行う。
writer.open("Video.mp4", VideoWriter::fourcc('m', 'p', '4', 'v'), 30.0, Size(640, 480), true);
//後から、ファイル名やコーデック、fps、画像サイズを指定して作成することも可能。
```

作成した動画ファイルに画像を書き込む場合は、以下の関数を利用する。

```cpp:動画出力ファイルへの書き込み

writer.write(frame); 	//write関数を用いた方法

writer << frame;		//左シフト演算子を用いた書き込み方法
```
書き込む画像をMat型かUMat型でframeとして宣言した場合です。
どちらも結果は同様になります。

この書き込み関数により１枚ずつフレームを動画ファイルに追加していきます
fpsを30と設定した場合は、30フレーム代入することで、１秒の動画を生成することができます。
動画を生成するためには、for文やwhile文などを利用し、いくつかのフレームを代入してください。

注意点として、
動画出力ファイルの宣言時に設定した、画像サイズや画像のチャンネル数に合わせて書き込みをする必要があります。
「Size(640, 480)、true」で宣言した場合は、Mat型の画像は「Size(640, 480)、３チャンネル（カラー画像）」
「Size(1920, 1080)、false」で宣言した場合は、Mat型の画像は「Size(1920, 1080)、１チャンネル（グレースケール画像）」
となります。

##動画出力のテストプログラム

```cpp:Video_Test1.cpp
#include <opencv2/opencv.hpp>

#ifdef _DEBUG
#pragma comment(lib, "opencv_world400d.lib")
#else
#pragma comment(lib, "opencv_world400.lib")
#endif

using namespace std;
using namespace cv;

int main()
{
	VideoWriter writer("Video.mov", VideoWriter::fourcc('m', 'p', '4', 'v'), 30.0, Size(640, 480), true);				//動画ファイル生成
	VideoWriter writer_gray("Video_gray.mov", VideoWriter::fourcc('m', 'p', '4', 'v'), 30.0, Size(1920, 1080), false);	//動画ファイル生成
	
	Mat frame(Size(640, 480), CV_8UC3);			//frameをサイズ(640, 480)、CV_8UC3にて３チャンネルで宣言
	Mat frame_gray(Size(1920, 1080), CV_8UC1);	//frame_grayをサイズ(1920, 1080)、CV_8UC1にて１チャンネルで宣言

	while (1) {
		imshow("frame", frame);					//frameに格納されている画像を表示
		imshow("frame_gray", frame_gray);		//frame_grayに格納されている画像を表示

		writer << frame;						//frameに格納されている画像をwriterに書き込む
		writer_gray << frame_gray;				//frame_grayに格納されている画像をwriter_grayに書き込む
		if (waitKey(1) == 27) break;			//ESCキーが入力されるまで実行
	}

	return 0;
}
```
Mat型で大きさとチャンネル数を宣言しているだけなので、灰色の画面が表示される動画が生成される。

#動画ファイルの生成確認方法
動画ファイル生成した後、正しく生成ができているか確認を行う関数がある。

```cpp:動画ファイルの生成確認
bool cv::VideoWriter::isOpened();
```
isOpened()は、返戻値がbool型であり、Trueであれば生成成功状態。Falseでは生成に不備があったと確認することが可能である。
画像が書き込めない状態での処理はAssertion failedの原因となるため、必ず生成確認を行ったほうが無難である。
また、VideoWriterでは一切エラーを吐かずにプログラムを正常終了するが、動画ファイルが0~6KBなどの破損データになっていることがあるため、必ず確認を行うようにしてください。

##動画ファイル生成確認のプログラム

```cpp:Video_Test2.cpp
#include <opencv2/opencv.hpp>

#ifdef _DEBUG
#pragma comment(lib, "opencv_world400d.lib")
#else
#pragma comment(lib, "opencv_world400.lib")
#endif

using namespace std;
using namespace cv;

int main()
{
	VideoWriter writer;

	writer.open("Video.mov", VideoWriter::fourcc('m', 'p', '4', 'v'), 30.0, Size(640, 480), true);	//動画ファイル生成

	if (!writer.isOpened()) {																		//動画ファイル生成の確認
		cout << "Writer Not Opened!" << endl;														//生成できてない場合はエラー
		return -1;																					//プログラム終了
	}

	return 0;
}
```
#コーデックについて
コーデックとは、動画を圧縮や復元する際の、符号化方式のことです。
拡張子によって、コーデックは異なり、動画にした後の動画ファイルのサイズなども変わってきます。

以下はOpenCVで利用できるコーデックの一覧の抜粋である。

|fourccの引数|拡張子|
|:-----------------|:------------------|
| ('M', 'J', 'P', 'G')             |              .avi |
| ('m', 'p', '4', 'v')             |              .mov |
大文字小文字まで判別しますので正しく入力する必要があるのに注意が必要です。
OpenCV4.0.0、Windows 8.1~10では、以上のコーデックのみ利用可能であった。
一覧にあるコーデックを利用するとファイルが破損してしまうので、そのうち原因を見つけたいと思う。

#注意点
ファイル拡張子「.avi」コーデック「fourcc('M', 'J', 'P', 'G')」にした場合は、カラー画像(３チャンネル画像)のみ書き込みが可能であり、グレースケール画像(１チャンネル)などを保存する場合は、「cvtColor」などを利用して３チャンネルに変更する必要がある。
コーデックによって保存できる画像の形式が変わることに注意する必要がある。

#さいごに
今回はVisual Studioを利用し、画像処理ライブラリOpenCVを用いて動画出力する方法の説明を行った。
作成した動画などを保存したり結果を動画として見やすく把握するために利用されると思う。