ページ 11

OpenCVを用いた背景差分について

Posted: 2014年11月19日(水) 00:32
by ももも
背景差分を用いて歩行者を検出したいと思います。

人物のいない道のみの画像を背景画像にして、その場所を人物が歩行している動画と差分を取って人物のみを検出したいのですが、背景差分を取った出力の映像において人物が背景画像と合わさってしまいハッキリと人と認識することができません。
例としては横断歩道を渡っている人物を検出した時に、出力映像の人物が縞々になってしまう感じです。
人物をハッキリと認識できるように切り取るにはどのような方法を取ればよろしいでしょうか。
Windows7でVisual Studio 2010、OpenCV 2.4.9を使用しています。
以下、コードを記載します。宜しくお願いします。

コード:

#include "stdafx.h"
#include <opencv2/opencv.hpp>					// OpenCVヘッダ
#pragma comment(lib,"opencv_imgproc249d.lib")	// OpenCVライブラリ
#pragma comment(lib,"opencv_core249d.lib")		// OpenCVライブラリ
#pragma comment(lib,"opencv_highgui249d.lib")	// OpenCVライブラリ

int main()
{
	//読み込む静止画像名
	IplImage* sourceImage = cvLoadImage("base.jpg", CV_LOAD_IMAGE_ANYDEPTH|CV_LOAD_IMAGE_ANYCOLOR);
	IplImage* differenceImage = NULL;

	//読み込む動画ファイル名
	char* filename="test.mp4";

	//IplImage
	IplImage* src;

	CvCapture* capture = NULL;
	int key; //キー入力

	if(sourceImage == NULL ){
		 //画像が見つからない場合
		printf("画像が見つかりません\n");
		return -1;
	}

	//指定したファイルが見つからない場合
	if(NULL==(capture = cvCaptureFromAVI(filename))){
		fprintf(stderr,"指定のファイルが見つかりませんでした");
		return -1;
	}

	//画像表示ウィンドウの準備
	cvNamedWindow("出力", CV_WINDOW_AUTOSIZE);

	//処理ループの開始
	for(;;){
		//AVIフtァイルからフレーム画像を取り出す
		if(NULL==(src=cvQueryFrame(capture))){
			break;
		}
		
		//処理画像を実体として定義
		differenceImage = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 3);

		//背景差分処理
		cvAbsDiff(src,sourceImage,differenceImage);

		//画像表示
		cvShowImage("出力", differenceImage);

		cvReleaseImage(&differenceImage);

		//キー入力
		key = cvWaitKey(10);

		//ESCキーを押すと終了
		if(key==0x1b){
			break;
		}
	}

	//解放
	cvReleaseCapture(&capture);
	cvDestroyWindow("出力");

	return 0;
} 

Re: OpenCVを用いた背景差分について

Posted: 2014年11月20日(木) 10:24
by usao
>例としては横断歩道を渡っている人物を検出した時に、出力映像の人物が縞々になってしまう感じです。

背景と前景が同じ色だったときに差分が取れない,ということを言っているのでしょうか?
極端な例だと 「あいつら 同化してるぜ!!」 みたいな状況  (←ググると画像わかります)

背景差分法を用いた前景領域の抽出に関する論文はたくさんあるので
検索して使えそうなものを探すのが早い気がします.

Re: OpenCVを用いた背景差分について

Posted: 2014年11月21日(金) 01:53
by ももも
回答ありがとうございます。

>極端な例だと 「あいつら 同化してるぜ!!」 みたいな状況  (←ググると画像わかります)
まさしくこのような状況です。移動物体を抜き取った後に、haar-like特徴を用いて人物を特定したいと考えております。
人の目では人物だと分かるのですが背景画像も混じってしまうため、haar-like特徴では検出されないという理由で質問させていただきました。
背景画像Aと動画Bがあるとき、動画Bにはあって画像Aにないものを出力したいといった感じです。(動画Bにない背景Aの背景部分を出力しない)
このようにできる差分の方法がありましたらご教授いただけませんでしょうか?

宜しくお願いします。

Re: OpenCVを用いた背景差分について

Posted: 2014年11月21日(金) 10:13
by usao
処理結果として前景領域を出力するプログラムなのだとして…
・出てきてほしい個所が出ない
・出てきてほしくない箇所が出る

のどちらが問題なのか,よくわからないですね.
前者側の問題(同化してるぜ!)だと思ったのですが,

>背景画像も混じってしまうため

と言われると後者側ですよね.
まぁどちらにしても,画素値に明瞭な差が無いのであれば差分で取れないのは原理上当たり前のことなので,
「背景差分では,そもそも 完全には前景と背景とを分離できない」ことを前提にして考えねばなりません.
とりあえず差分値に対して適用する判定閾値を 厳しくするか,甘くするか のどちらか(後段の処理に都合が良い側)寄りに振ることにして,
後は後段処理側(での工夫)を頑張る方向に注力される方が良いのではないかと思います.

(もちろん 背景差分法自体の精度を上げる という方針もあるとは思いますが,
 「前もって用意した背景画像と色が近い前景物体が出てきた」とかいうこと以外にも,
 照明環境の変化(単に全体の明るさの変化だけではなくて影のでき方等が変わる等も)だとか,
 屋外であれば背景であるべき木の枝や葉が風で揺れたりだとか,
 とにかく,戦わねばならない問題発生要因というのはたくさんありますので
 常に 完璧な=後段処理において都合が良い 結果が出てくる方法 というのを追及するのは至極困難かと)

#あと,直接的に今の問題とは関係ないかもですが,
 とりあえずこの手のことを(特に屋外環境で)行う際に,背景を あらかじめ用意した画像一枚で固定 として扱うのは
 あまりにも乱暴すぎではないかな,とか.

先にも書きましたが,あなたがやろうとしていることと似たようなことをやっている文献等を探して,
どういう工夫がされているかを参考にしてみてはいかがでしょうか?
オフトピック
そして,よい方法が見つかったら教えてください! 私も知りたいので.

Re: OpenCVを用いた背景差分について

Posted: 2014年11月21日(金) 13:48
by softya(ソフト屋)
方法は2つあると思います
1.背景を人物と分離しやすいものに限定する。
2.背景から人物を抽出するために、動画から動くものを抽出する技法に切り替える。ただし、論文を調べたり時間がかかるかも知れません。
検索すれば出てきますので調べてみてください。
申し訳ありませんが詳しくないので解説はできません。
行き詰まっているようなので打開策の提案をさせていただきました。

Re: OpenCVを用いた背景差分について

Posted: 2014年11月26日(水) 23:58
by ももも
回答ありがとうございます。
返信が遅れてしまい申し訳ありません。
調べてみたところ、samples/cpp/bgfg_segm.cppのコードが私が求めていたものに近い結果が出たので、を自分用に少し改良して、Visual Studio 2010上で動作させました。

ここまでは動作したのですが、差分を行った後の画像を用いて同一プログラム上で新たなコードを追加したところ、「'cv::Mat' から 'const CvArr *' に変換できません。」とcv::Mat型との互換性がないといったエラーが出てきてしまいます。
samples/cpp/bgfg_segm.cppのコードの「Mat img, fgmask, fgimg;」の関数の部分を使用し、haar-like特徴を用いて人物を特定したいと考えております。
なので、以下のコードをIplImage型に変換していただけないでしょうか。
どうかよろしくお願いします。

コード:

#include "stdafx.h"
#include <opencv2/opencv.hpp>
#pragma comment(lib,"opencv_imgproc249d.lib")
#pragma comment(lib,"opencv_core249d.lib")
#pragma comment(lib,"opencv_highgui249d.lib")
#pragma comment(lib,"opencv_video249d.lib")
#pragma comment(lib,"opencv_objdetect249d.lib")

using namespace cv; 

int main(int argc, char** argv) 
{ 
	CvCapture *src;
	src = cvCaptureFromFile("file.mp4");
	if(src == NULL){printf("映像が取得できません。\n"); cvWaitKey(0); return -1;}
	bool update_bg_model = true;

    namedWindow("image"); 
    namedWindow("foreground mask"); 
        namedWindow("foreground image"); 
    namedWindow("mean background image"); 
    BackgroundSubtractorMOG2 bg_model; 
    Mat img, fgmask, fgimg; 

    for(;;) 
    { 
        img = cvQueryFrame(src);
        if( img.empty() ) 
            break; 

        if( fgimg.empty() ) 
          fgimg.create(img.size(), img.type()); 
        //update the model 
        bg_model(img, fgmask, update_bg_model ? -1 : 0); 

        fgimg = Scalar::all(0); 
        img.copyTo(fgimg, fgmask); 
        Mat bgimg; 
        bg_model.getBackgroundImage(bgimg); 

        imshow("image", img); 
        imshow("foreground mask", fgmask); 
        imshow("foreground image", fgimg); 
        if(!bgimg.empty()) 
          imshow("mean background image", bgimg ); 
        char k = (char)waitKey(30); 
        if( k == 27 ) break; 
        if( k == ' ' ) 
        { 
            update_bg_model = !update_bg_model; 
            if(update_bg_model) 
                printf("Background update is on\n"); 
            else 
                printf("Background update is off\n"); 
        } 
    } 
    return 0; 
} 

Re: OpenCVを用いた背景差分について

Posted: 2014年11月27日(木) 09:46
by usao
>以下のコードをIplImage型に変換していただけないでしょうか

そのくらいの「作業」は自分でやりましょうよ…

あと,全部書き換えなくても
どうしてもIplImage型が必要になったときだけ cv::Mat から変換して使う という形ではダメなのですか?

Re: OpenCVを用いた背景差分について

Posted: 2014年11月27日(木) 12:52
by ももも
返信ありがとうございます。
申し訳ありませんが、その変換の方法が分からなかったので質問させていただきました。
samples/cpp/bgfg_segm.cppのコードの「Mat img, fgmask, fgimg;」の関数の部分をIplImage型で使用したいので、そこだけでもご教授していただけないでしょうか。
宜しくお願いします。

Re: OpenCVを用いた背景差分について

Posted: 2014年11月27日(木) 13:25
by usao
>「Mat img, fgmask, fgimg;」の関数の部分

というのが,何かおかしな文章で,正確な意味が取れませんが…

例えば,あなたが cv::Mat 型として用意して使っているデータがあって,それを
void f( const IplImage *);
みたいな形の関数に渡したいとかそういう単純な話なんですよね? きっと.
cv::Mat の operator IplImage() とかを使えばいいのではないかと想像します.

「cv::Mat IplImage 変換」とかで検索でもして方法を調べてみてはいかがでしょうか.

Re: OpenCVを用いた背景差分について

Posted: 2014年11月27日(木) 13:44
by ももも
返信ありがとうございます。
>「Mat img, fgmask, fgimg;」の関数の部分
というのは、No: 6で記載したコードの41~45行目で使用されている、img, fgmask, fgimg, bgimgをimshowのようにMat 型で出力するのではなく、差分した結果を、cvHaarDetectObjectsに渡したいといったものです。
単純に結果を渡すと、「'cv::Mat' から 'const CvArr *' に変換できません。」というメッセージが出てきてしまうので、cv::MatからCvArrに変換するようなワンクッションを置く方法を探しています。

>「cv::Mat IplImage 変換」とかで検索
ありがとうございます。こちらで検索して解決方法を探してみたいと思います。

Re: OpenCVを用いた背景差分について

Posted: 2014年12月11日(木) 23:44
by ももも
返信が遅れてしまい大変申し訳ありません。
BackgroundSubtractorMOG2を使用してみたところ、理想的な背景差分が出来ました。
ありがとうございました。