マルチスレッドによるハイスピードカメラを用いた動画の撮影

##構築環境
Visual Studio 2017
OpenCV 3.0

##使用するカメラ
Bauber製VCXU-02M(最大フレームレート：891[fps])
(カラーではなく，モノクロです)

##背景
Webカメラのフレームレートは高くても60[fps]である．
この程度であれば「フレームをキャプチャしてから，動画をファイルに保存する」といった一連の処理をシングルスレッドで行なうことは可能である．
しかし，一般的なフレームレートよりも高いフレームレートのカメラ（＝ハイスピードカメラ）ではプログラムの実行速度に依存し，所望の条件を満たせない可能性がある．

##方法
1. まず一連の処理をプログラムに書き起こし，30[fps]でしっかりとプログラムが正しいかを確認する．
2. カメラからフレームをキャプチャする部分とフレームを動画に書き込む部分を分割して実行してみる．
3. 両者の橋渡しの方法を検討する．
4. 二つのスレッドを同時に実行してみる．

##1. 一連の処理をプログラムに書き起こし，30[fps]での動作検証
<b>まず，実際にカメラで動画を撮影し，保存できるかの確認を行なう．</b>
（ここが出来ないとこれ以降もできるはずがない）
以下，プログラム

```c++:
#以上省略

cv::VideoWriter writer; //ビデオに書き込む変数
writer.open(filename_video, VideoWriter::fourcc('X', 'V', 'I', 'D'), double(fps), cv::Size(640, 480), false); //書きこむビデオファイルの指定
BufferListFilled = pDataStream->GetBufferList(); //空のBufferListの作製
while (1)) {//無限ループ
	pBufferFilled = pDataStream->GetFilledBuffer(1000); //timeout 1000 msec
	if (pBufferFilled == NULL)
	{
		std::cout << "Error: Buffer Timeout after 1000 msec" << std::endl << std::endl;
	}
	else if (pBufferFilled->GetIsIncomplete() == true)
	{
		std::cout << "Error: Image is incomplete" << std::endl << std::endl;
		// queue buffer again
		pBufferFilled->QueueBuffer();
	}
        //ビデオファイルに書き込み
        writer.write(cv::Mat(cv::Size(640, 480), CV_8UC1, (char*)pBufferFilled->GetMemPtr()).clone());
	pBufferFilled->QueueBuffer();
}
writer.release();
#以下省略
```

##2. プログラムの分割，関数化
<b>次に上記のプログラムを分割し，各部分を関数化する．</b>
（ここでは両者で必要な変数はグローバル変数にしておるぞ）
![無題.png](https://qiita-image-store.s3.amazonaws.com/0/266417/66525fb9-96aa-4a6f-4283-db9b0af3801e.png)

```c++:
//ビデオファイルの書き込み
cv::VideoWriter writer;
writer.open(filename_video, VideoWriter::fourcc('X', 'V', 'I', 'D'), double(fps), cv::Size(640, 480), false);
if (!writer.isOpened()) {
	std::cout << "open error." << std::endl;
}
else {
	std::cout << "open success." << std::endl;
}

//main function
void main(void) {
	BufferListFilled = pDataStream->GetBufferList();
	while (count_t <= (frame_count + 1)) {//取得フレーム数が目的値以下ならループ
		pBufferFilled = pDataStream->GetFilledBuffer(1000); //timeout 1000 msec
		if (pBufferFilled == NULL)
		{
			std::cout << "Error: Buffer Timeout after 1000 msec" << std::endl << std::endl;
		}
		else if (pBufferFilled->GetIsIncomplete() == true)
		{
			std::cout << "Error: Image is incomplete" << std::endl << std::endl;
			// queue buffer again
			pBufferFilled->QueueBuffer();
		}
		//push_back frame image
		video_write(cv::Mat(cv::Size(640, 480), CV_8UC1, (char*)pBufferFilled->GetMemPtr()).clone());
		pBufferFilled->QueueBuffer();
	}
        video_finish();
}

//write function
void video_write(cv::Mat temp) {
	writer.write(temp);
}

//VideoWriter close function
void video_finish(void){
        writer.release();
}
```

##3.データの渡し方を検討
<b>カメラからフレームをキャプチャする部分とフレームを動画に書き込む部分の橋渡しを考える.</b>
これまではキャプチャした画像をビデオファイルに書き込んでから次のフレームをキャプチャしていたが，
そんなんでは日が暮れてしまうどころか朝日が昇ってきてしまう．
そこで橋渡しの方法としてFIFOであるキューを使う．
（キューを知らないという方は適当にググっておくれやす）

```C++:
//キューの作製
class myvactor {
	std::mutex mtx;
	std::vector<cv::Mat> src;
public:
	void push_back(cv::Mat x) {
			std::lock_guard<std::mutex> lock(mtx);
			src.push_back(x);
			std::lock_guard<std::mutex> unlock(mtx);
	}
};
std::vector<cv::Mat> src;

```

##4.スレッドに分割
<b>3.で作製したキューを利用して2.のプログラムをスレッドに分割する．</b>
最後にマルチスレッドで実行できるようにするで

```C++:
int main(int argc, char *argv[])
{
	try
	{
		double frame_count = 4.0 / (pDevice->GetRemoteNode("ExposureTime")->GetDouble()*std::pow(10, -6.0));
		std::thread t1(camera_capture, frame_count);
                t1.join();
               
                std::thread t3(video_write, pTransformImage);
		t3.join();

	}
	catch (BGAPI2::Exceptions::IException& ex)
	{
		returncode = 0 == returncode ? 1 : returncode;
		std::cout << "ExceptionType:    " << ex.GetType() << std::endl;
		std::cout << "ErrorDescription: " << ex.GetErrorDescription() << std::endl;
		std::cout << "in function:      " << ex.GetFunctionName() << std::endl;
	}

}

void camera_capture(double frame_count) {
	BufferListFilled = pDataStream->GetBufferList();
	while (count_t <= (frame_count + 1)) {//取得フレーム数が目的値以下ならループ
		//std::cout << count_t << std::endl;
		pBufferFilled = pDataStream->GetFilledBuffer(1000); //timeout 1000 msec
		if (pBufferFilled == NULL)
		{
			std::cout << "Error: Buffer Timeout after 1000 msec" << std::endl << std::endl;
		}
		else if (pBufferFilled->GetIsIncomplete() == true)
		{
			std::cout << "Error: Image is incomplete" << std::endl << std::endl;
			// queue buffer again
			pBufferFilled->QueueBuffer();
		}
		//push_back frame image
		src.push_back(cv::Mat(cv::Size(640, 480), CV_8UC1, (char*)pBufferFilled->GetMemPtr()).clone());
		pBufferFilled->QueueBuffer();
		count_t++;
	}
}

void video_write(BGAPI2::Image * pTransformImage) {
	cv::VideoWriter writer;
	writer.open(filename_video, VideoWriter::fourcc('X', 'V', 'I', 'D'), double(fps), cv::Size(640, 480), false);
	if (!writer.isOpened()) {
		std::cout << "open error." << std::endl;
	}
	else {
		std::cout << "open success." << std::endl;
	}

	while (start_flag <1) {}
	for (int i = 0; i < src.size(); i++) {
		writer.write(src[i]);
	}
	src.clear();
	writer.release();
}
```

##終わりに
「あれ，これマルチスレッドでやる意味ある？」と思った方，さすがです．
上のプログラムではマルチスレッドで行なう意味は<b>全くありません！！</b>

ではなぜマルチスレッドで行なっているのか？
そう，それはフレームをキャプチャしながら，動画への書き込みを行なうためです．

以下に，スレッドを同時に実行した際とばらばらに実行した際の処理時間を示します．

そう，このようにカメラのフレームレートが500[fps]であるため，同時に実行しても意味がないのです．
（なんじゃそら…）

今後はキャプチャしながら，動画への書き込みが同時に行えるようプログラムを見直す必要がありそうです．

ではまた！！

以下，GithubのURLです．
https://github.com/minna30waiwai/High-Speeding-camera-capturing