読者です 読者をやめる 読者になる 読者になる

OpenCVで密なオプティカルフローを計算する

Programming OpenCV コンピュータビジョン C++

オプティカルフロー

動画から密なオプティカルフローの計算を行う.SimpleFlowアルゴリズムによるオプティカルフロー,TV‐L1オプティカルフロー,Farnebackのオプティカルフロー,Broxのオプティカルフロー,Lucas-Kanade法によるオプティカルフロー等によって密なオプティカルフローの計算ができる.

ソースコード

cv::calcOpticalFlow○○メソッドを使うと,呼び出すたびにパラメータを渡さなくてはいけなくて見た目がすっきりしないのと,アルゴリズムによって3チャンネルの画像だったり1チャンネルの画像だったりで面倒.

cv::superress::createOptFlow_○○でDenseOpticalFlowExtクラスのオブジェクトを作れば,そこら辺の面倒な処理を中でやってくれるので楽.以下,ソースコード.色符号化によるオプティカルフローの可視化を行っている.オプティカルフローの計算自体は一行で済むため,色符号化のコードが大部分を占めている.

パラメータを確認したり設定したりしたいときは,以下の記事を参考.
OpenCVでcv::Algorithmのパラメータを表示する - whoopsidaisies's diary

#include <opencv2/opencv.hpp>
#include <opencv2/superres/optical_flow.hpp>

using namespace cv;
using namespace cv::superres;

void main()
{
	// 動画ファイルの読み込み
	VideoCapture capture = VideoCapture("movie.mp4");
	// TV-L1アルゴリズムによるオプティカルフロー計算オブジェクトの生成
	Ptr<DenseOpticalFlowExt> opticalFlow = superres::createOptFlow_DualTVL1();

	// 前のフレームを保存しておく
	Mat prev;
	capture >> prev;

	while (waitKey(1) == -1)
	{
		// 現在のフレームを保存
		Mat curr;
		capture >> curr;

		// オプティカルフローの計算
		Mat flowX, flowY;
		opticalFlow->calc(prev, curr, flowX, flowY);

		// オプティカルフローの可視化(色符号化)
		//  オプティカルフローを極座標に変換(角度は[deg])
		Mat magnitude, angle;
		cartToPolar(flowX, flowY, magnitude, angle, true);
		//  色相(H)はオプティカルフローの角度
		//  彩度(S)は0~1に正規化したオプティカルフローの大きさ
		//  明度(V)は1
		Mat hsvPlanes[3];		
		hsvPlanes[0] = angle;
		normalize(magnitude, magnitude, 0, 1, NORM_MINMAX); // 正規化
		hsvPlanes[1] = magnitude;
		hsvPlanes[2] = Mat::ones(magnitude.size(), CV_32F);
		//  HSVを合成して一枚の画像にする
		Mat hsv;
		merge(hsvPlanes, 3, hsv);
		//  HSVからBGRに変換
		Mat flowBgr;
		cvtColor(hsv, flowBgr, cv::COLOR_HSV2BGR);

		// 表示
		cv::imshow("input", curr);
		cv::imshow("optical flow", flowBgr);

		// 前のフレームを保存
		prev = curr;
	}
}

cv::superress::createOptFlow_○○で使えるアルゴリズム

上記ソースの

Ptr<DenseOpticalFlowExt> opticalFlow = superres::createOptFlow_DualTVL1();

のcreateOptFlow_DualTVL1()を置き換えるだけで,他のアルゴリズムでオプティカルフローを計算できる.以下に,指定できるアルゴリズムをまとめる.

CPU OpenCL CUDA
SimpleFlow createOptFlow_Simple なし なし
TV‐L1 createOptFlow_DualTVL1 createOptFlow_DualTVL1_OCL createOptFlow_DualTVL1_GPU
Farneback createOptFlow_Farneback createOptFlow_Farneback_OCL createOptFlow_Farneback_GPU
Brox なし なし createOptFlow_Brox_GPU
LK法 なし createOptFlow_PyrLK_OCL createOptFlow_PyrLK_GPU

OpenCLやCUDAでの計算ができるものもある.OpenCLおよびCUDAでの計算の場合は少し変更が必要.上記ソースの

opticalFlow->calc(prev, curr, flowX, flowY);

の部分を以下のように書き換える.

OpenCLの場合
ocl::oclMat oclCurr(curr), oclPrev(prev), oclFlowX, oclFlowY;
opticalFlow->calc(oclPrev, oclCurr, oclFlowX, oclFlowY);
oclFlowX.download(flowX);
oclFlowY.download(flowY);

インクルードファイルの追加も必要.

#include <opencv2/ocl/ocl.hpp>
CUDAの場合
gpu::GpuMat gpuCurr(curr), gpuPrev(prev), gpuFlowX, gpuFlowY;
opticalFlow->calc(gpuPrev, gpuCurr, gpuFlowX, gpuFlowY);
gpuFlowX.download(flowX);
gpuFlowY.download(flowY);

インクルードファイルの追加も必要.

#include <opencv2/gpu/gpu.hpp>