株式会社インデペンデンスシステムズ横浜

システム開発エンジニアの西田五郎が運営しております。Raspberry Pi や Arduino その他新規開発案件のご依頼をお待ちしております。

OpenCV

OpenCVで背景差分とテスト的な動体検知

投稿日:

OpenCVで背景差分を試してみました。あとテスト的な動体検知も試してみました。背景差分とはあらかじめ取得した画像を背景画像として、観測時点の画像とその背景画像との差分を取ることによりその差分を前景領域として取得する手法です。動体検知に有効な手法です。背景差分取得の課題としては、背景の揺れ、影、天候、照明への対応があるようです。ここでのOpenCV(2.4.8)では3種類のアルゴリズムが利用出来ます。今回は、MOGを使ってみます。プログラミングとしては他のアルゴリズムも同様に使えます。

MOGアルゴリズムはOpenCVのヘッダファイルによると、Gaussian Mixture-based Backbround/Foreground Segmentation Algorithm となっています。一般的には、MOGは、混合正規分布と呼ばれているものです。以下はWikipediaからの引用です。

背景差分 – Wikipedia

(※引用ここから)背景物体が揺れる問題に対しては混合正規分布(Mixture of Gaussian Distribution, MoG)を用いた背景のモデル化などによる対処が最も有名である.MoGは新たに観測された画像を用いて逐次的に背景モデルを更新することから,太陽の位置の変化のような,ゆっくりとした照明環境の変化にも対処できる.(※引用ここまで)

私では理論的、数学的なことは説明出来ませんので必要な場合はページ最後の関連リンク、OpenCVのマニュアル、専門書等を参照して頂ければと思います。ここでは以上を簡単な前提としてとりあえず OpenCVを使ってみます。

今回も今までのOpenCVの記事のように以下のプログラムをベースにしています。
OpenCVをWin32ベースで利用する(その1)ダイアログベースアプリ
OpenCVをWin32ベースで利用する(その3)カメラ入力からの画像処理
OpenCVをWin32ベースで利用する(その5)GUIコントロールの追加

以下のようなプログラムを作成しました。
0001

差分画像の取得
差分画像取り込みのボタンでWebカメラからの入力から差分画像を表示します。カメラの前で何かを動かすと画像が表示されます。

テスト的な動体検知
監視フラグ設定ボタンで監視を開始して、画像の白い部分が画面上のしきい値(白の割合)を超えたらメッセージボックスで「動体を検知しました。」と表示して監視フラグをリセットします。処理としては単純に差分画像の白の割合を取得してしきい値と比較しているだけです。厳密に動体検知が出来るものではないので「テスト的に」としています。ご了承ください。あと、ノイズ除去の処理は入っていないです。

この際に保存ファイル設定ボタンでファイル名を指定しておくと、しきい値を超えた時点の画像を保存します。以下その例です。
masksaved

mask

プログラムは以下です。全体を表示すると長くなるのでカメラからの入力を処理しているスレッドの部分のみを表示します。必要な場合は以下からダウンロードをしてご覧頂ければと思います。(※必要な場合は用途に関わらずご利用頂いてかまいませんが、不具合等は弊社では一切の責任を負いかねます。)
プログラムファイル一式(34M程度のサイズです)

/*------------------------------------------------
 カメラキャプチャースレッド
-------------------------------------------------*/
unsigned int __stdcall CameraCaptureThread(PVOID pv)  {

	cv::VideoCapture cap(0);
	if (!cap.isOpened()){
		return -1;
	}

	//メインウィンドウ
	HWND wnd = (HWND)pv;

	//各画像定義
	cv::Mat frame; //カメラフレーム

	cv::Mat fgMaskMOG; //MOGでの前景マスク  
	//cv::Mat fgMaskMOG2; //MOG2での前景マスク
	//cv::Mat fgMaskGMG; //GMGでの前景マスク  

	//各背景差分処理
	cv::Ptr< cv::BackgroundSubtractor> pMOG; //MOGでの差分処理  
	//cv::Ptr< cv::BackgroundSubtractor> pMOG2; //MOG2での差分処理  
	//cv::Ptr< cv::BackgroundSubtractorGMG> pGMG; //GMGでの差分処理  

	pMOG = new cv::BackgroundSubtractorMOG();
	////pMOG2 = new cv::BackgroundSubtractorMOG2();
	//pGMG = new cv::BackgroundSubtractorGMG();

	while (true) {
		if (endFlag){
			break;
		}

		//カメラ画像取り込み
		cv::Mat cameraFrame;
		if (!cap.read(frame)){
			break;
		}

		//マスク処理
		pMOG->operator()(frame, fgMaskMOG);
		///pMOG2->operator()(frame, fgMaskMOG2);
		//pGMG->operator()(frame, fgMaskGMG);

		//マスク画像での動体部分(白い部分)の割合の取得
		cv::Mat binImage;

		//二値化画像の作成
		cv::threshold(fgMaskMOG, binImage, 0, 255.0, CV_THRESH_OTSU);

		//白と黒の割合の取得
		int TotalNumberOfPixels = binImage.rows * binImage.cols;
		int ZeroPixels = TotalNumberOfPixels - cv::countNonZero(binImage);

		double detectRate = (double)Pos / 100.0; //画面上で指定しているパーセントを取得
		double rate = ((double)(TotalNumberOfPixels - ZeroPixels) / (double)TotalNumberOfPixels); //白の割合

		//動体検出と判定した場合
		if (rate > detectRate){
			if (bDetectCheck){

				//ファイル名が指定されていればカメラ画像を保存
				if (strlen(szSaveFileName) != 0){
					cv::imwrite((const std::string&)szSaveFileName, frame);

					//テスト用にマスク画像を保存
					cv::imwrite("c:\\tmp\\mask.png", fgMaskMOG);
				}

				SendMessage(wnd, ID_DETECTED, 0, 0);   //メインウィンドウへ通知
				bDetectCheck = FALSE;
			}
		}

		//マスク画像の表示
		cv::cvtColor(fgMaskMOG, imgMatRead, CV_GRAY2BGR);
	
		img = imgMatRead;
		imgFromOpenCV = &img;

		//メモリを確保
		if (bmpData != NULL){
			HeapFree(GetProcessHeap(), 0, bmpData);
		}

		bmpData = (LPDWORD)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, imgFromOpenCV->width * imgFromOpenCV->height * 4);

		//iplImageからDIBへ変換
		iplTobmp(imgFromOpenCV, bmpInfo, bmpData);

		//ここで描画
		StretchDIBits(hDC,
			0, 0, imgFromOpenCV->width, imgFromOpenCV->height,
			0, 0, imgFromOpenCV->width, imgFromOpenCV->height,
			bmpData, &bmpInfo, DIB_RGB_COLORS, SRCCOPY);
	}

	return 0;
}

プログラムの説明です。
cv::Ptr pMOG で宣言して、new で MOGでの差分処理を生成しています。コメントになっていますが、他のアルゴリズムの場合も同様に出来ます。cv::Ptrは、OpenCVのクラスで参照カウントを利用するクラスです。operatorでマスク画像を取得して表示しています。

アルゴリズムをGMGにした場合、画像が表示されるまでしばらくかかりました。

テスト的な動体検知では、単純に白い部分の割合を取得して画面で設定したしきい値を超えたらメッセージボックスを表示するようにしています。

いろいろと課題はあると思いますが今回はここまでです。

関連リンク
【Python/OpenCV】背景差分法とUSBカメラで移動物体を検出する
【Python/OpenCV3.0】USBカメラで動体検出(背景差分法2)
【Python/OpenCV3.0】USBカメラで動体検出(背景差分法3)

AdSense

AdSense

-OpenCV

執筆者:

関連記事

OpenCVをWin32ベースで利用する(その4)カメラ入力からの動画処理

OpenCVをWin32ベースで利用するの4回目です。前回はカメラ入力からの画像処理(静止画)を扱いましたが、今回は動画を処理します。動画と言っても特に変わることはなくキャプチャしたフレームごとに処理 …

OpenCVで輪郭抽出から隣接領域の切り出し(その3)凸包の取得

OpenCVで画像内の輪郭抽出からその輪郭の隣接領域(四角形)を求めてその領域を切り出すという処理を作ってみました。前回までのプログラムとほぼ同じですが、前回までは輪郭抽出 → 直線近似 → 隣接領域 …

OpenCVをWin32ベースで利用する(その2)OpenCVの組み込み

OpenCVをWin32ベースで利用するの2回目です。前回はベースになるダイアログベースのアプリケーションを作成しました。今回はこのアプリケーションにOpenCVを組み込んで実際に動かしてみます。 ( …

NuGetでOpenCVを導入する

Visual Studio Express 2013 for Windows Desktopの開発環境でNuGetを使ってOpenCVを導入してみました。その方法についてです。 まずは、OpenCV( …

Tesseract-OCRの導入(その3)OpenCVの出力を認識する

2019/07/20追記 この記事を書いてから随分と経過しました。完全に情報が古くなっています。外部のサイトですが必要な場合は以下等を参照して下さい。 PythonとTesseract OCRで文字認 …