人が動いた時などを検知する手法としてフレーム差分というものがあります。
本記事では、フレーム差分を用いて動いた物体のみを表示するプログラムをC++とOpenCVで作成します。
前提知識
フレーム差分とは?
以下の図を用いて説明します。連続する3つの画像(A,B,C)を取得して、「AとBの差分」と「BとCの差分」をとります。その2つの差分の論理積を計算し、動いた物体を検出しています。
実装
では実際に実装していきます。初めにソースコードを示しますが、このソースコードを噛み砕いて説明していきます。(ちなみにこのコードをコピペしてすぐに利用できます。)
ソースコード
#include<opencv2/opencv.hpp> #include <iostream> using namespace std; using namespace cv; int main(int argc, char *argv[]){ Mat gray_img1,gray_img2,gray_img3,diff1,diff2,frame,diff; // カメラからの動画を取り込むためのオブジェクトを宣言する VideoCapture capture; // カメラを起動する capture.open(0); if (capture.isOpened() == false) { // カメラが起動しないときは終了する return 0; } capture >> frame; //1フレームの画像の読み込み(グレー化) cv::cvtColor(frame, gray_img1, cv::COLOR_BGR2GRAY); capture >> frame; //2フレームの画像の読み込み(グレー化) cv::cvtColor(frame, gray_img2, cv::COLOR_BGR2GRAY); capture >> frame; //3フレームの画像の読み込み(グレー化) cv::cvtColor(frame, gray_img3, cv::COLOR_BGR2GRAY); while (1) { //差分1:フレーム1と2の差を求める cv::absdiff(gray_img1, gray_img2,diff1); //差分2:フレーム2と3の差を求める cv::absdiff(gray_img2, gray_img3,diff2); //差分1と差分2の結果を比較(論理積)し、diffに出力 cv::bitwise_and(diff1,diff2,diff); //表示 cv::namedWindow("diff image", cv::WINDOW_NORMAL); cv::imshow("diff image", diff); //画像を1frameずらす gray_img2.copyTo(gray_img1,gray_img2); gray_img3.copyTo(gray_img2,gray_img3); capture >> frame; //画像の読み込み cv::cvtColor(frame, gray_img3, cv::COLOR_BGR2GRAY); if (waitKey(1) == 'q') break; // if (diff.empty() == true) break; // 画像が読み込めなかったとき、無限ループを抜ける } return 0; }
ソースコードの解説1(カメラ取得)
カメラからの取得方法には「【C++/OpenCV】C++/OpenCVを用いた動画の読み込み、表示、書き出しについて解説」で解説してあるので、本記事での説明は省かせていただきます。こちらでは、以下のコードのようにあらかじめ最初の3フレームを取得しグレースケール化しています。ここから、フレーム差分を用いて動体検知を行なっています。
capture >> frame; //1フレームの画像の読み込み(グレー化) cv::cvtColor(frame, gray_img1, cv::COLOR_BGR2GRAY); capture >> frame; //2フレームの画像の読み込み(グレー化) cv::cvtColor(frame, gray_img2, cv::COLOR_BGR2GRAY); capture >> frame; //3フレームの画像の読み込み(グレー化) cv::cvtColor(frame, gray_img3, cv::COLOR_BGR2GRAY);
ソースコードの解説2(フレーム差分&表示)
ここでは実際のフレーム差分を行なって、動いた物体のみ表示することができます。まず、absdiff
を用いて、「フレーム1とフレーム2の差分(diff1
)」と「フレーム2とフレーム3の差分(diff2
)」を求めています。この2つの差分から、bitwise_and
を用いて論理積を計算しdiff
に出力しています。このdiff
をimshow
で表示しています。(実行結果にこの出力結果を示しています。)
//差分1:フレーム1と2の差を求める cv::absdiff(gray_img1, gray_img2,diff1); //差分2:フレーム2と3の差を求める cv::absdiff(gray_img2, gray_img3,diff2); //差分1と差分2の結果を比較(論理積)し、diffに出力 cv::bitwise_and(diff1,diff2,diff); //表示 cv::namedWindow("diff image", cv::WINDOW_NORMAL); cv::imshow("diff image", diff);
ソースコードの解説3(1フレームをずらす)
ここでは新たなフレームの画像を取得し、1フレームづつずらす処理を行なっていきます。copyTo
で「画像2を画像1に」、「画像3を画像2に」ずらしています。(ここで、画像1の画像は消えます)ずらしたのち、画像3に新たな1フレーム画像を代入し、これを動画が終わるまで繰り返しています。
//画像を1frameずらす gray_img2.copyTo(gray_img1,gray_img2); gray_img3.copyTo(gray_img2,gray_img3); capture >> frame; //画像の読み込み cv::cvtColor(frame, gray_img3, cv::COLOR_BGR2GRAY);
出力結果
以下の動画のように実際に動いた時には動いた部分のみが表示され、止まると映像は表示されなくなります。
ここから、泥棒を見つけたり、色々応用することができます。
まとめ
本記事では、フレーム差分という手法を用いて動いた物体のみを表示するプログラムをC++とOpenCVで作成しました。では要点をまとめます。
- 3フレーム分の画像を用いることで動体を検知できる
- 動体検知に必要なライブラリは差分と論理積を計算するものだけ
- 動体検知から色々応用することができる
ぜひ動体検知プログラムを作成して、楽しんで見てください!!