차근차근/OpenCV
배경제거
http://opencv.jp/sample/accumulation_of_background.html#background_sub
背景統計量の累積
■ 背景と注目物体の分離
OpenCVには,背景差分を計算する際に便利な背景統計量の累積に関する関数が実装されている.画像中から,変化のない背景領域とそれ以外の領域を分離することは,コンピュータビジョンシステムにおいて多く使用される技術であり,様々な手法が提案されている. OpenCVにおいても,cvauxで2種類の動的背景差分手法が実装されている.ここでは,参考文献[1]で用いられている背景画像の時間的な変化を考慮した動的背景更新によるロバストな注目物体の検出手法を,OpenCVの関数を用いて実装した.
この手法では,背景領域画素の輝度
このモデルにおいて,
背景と判定された領域では輝度平均値
一方,物体領域と判定された領域では,輝度平均値は元の値を保持し,振幅
[1] 森田 真司, 山澤 一誠, 寺沢 征彦, 横矢 直和: "全方位画像センサを用いたネットワーク対応型遠隔監視システム", 電子情報通信学会論文誌(D-II), Vol. J88-D-II, No. 5, pp. 864-875, (2005.5).
サンプル
動的背景更新による物体検出 cvAcc, cvRunningAvg
背景画像の時間的な明度変化を考慮した物体検出.プログラム開始より,ウィンドウが表示されるまでの間は背景統計量を初期化しているためカメラを動かさないように注意する.サンプル内の初期化フレーム数や更新パラメータはテストで使用カメラに関してチューニングされている.また,OpenCV関数の使用例提示を優先したため,処理効率は若干低い.
サンプルコード
#include <cv.h>
#include <highgui.h>
#include <ctype.h>
#include <stdio.h>
int
main (int argc, char **argv)
{
int i, c, counter;
int INIT_TIME = 100;
int w = 0, h = 0;
double B_PARAM = 1.0 / 50.0;
double T_PARAM = 1.0 / 200.0;
double Zeta = 10.0;
CvCapture *capture = 0;
IplImage *frame = 0;
IplImage *av_img, *sgm_img;
IplImage *lower_img, *upper_img, *tmp_img;
IplImage *dst_img, *msk_img;
CvFont font;
char str[64];
// (1)コマンド引数によって指定された番号のカメラに対するキャプチャ構造体を作成する
if (argc == 1 || (argc == 2 && strlen (argv[1]) == 1 && isdigit (argv[1][0])))
capture = cvCreateCameraCapture (argc == 2 ? argv[1][0] - '0' : 0);
// (2)1フレームキャプチャし,キャプチャサイズを取得する.
frame = cvQueryFrame (capture);
w = frame->width;
h = frame->height;
// (3)作業用の領域を生成する
av_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_32F, 3);
sgm_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_32F, 3);
tmp_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_32F, 3);
lower_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_32F, 3);
upper_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_32F, 3);
dst_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_8U, 3);
msk_img = cvCreateImage (cvSize (w, h), IPL_DEPTH_8U, 1);
// (4)背景の輝度平均の初期値を計算する
printf ("Background statistics initialization start\n");
cvSetZero (av_img);
for (i = 0; i < INIT_TIME; i++) {
frame = cvQueryFrame (capture);
cvAcc (frame, av_img);
}
cvConvertScale (av_img, av_img, 1.0 / INIT_TIME);
// (5)背景の輝度振幅の初期値を計算する
cvSetZero (sgm_img);
for (i = 0; i < INIT_TIME; i++) {
frame = cvQueryFrame (capture);
cvConvert (frame, tmp_img);
cvSub (tmp_img, av_img, tmp_img);
cvPow (tmp_img, tmp_img, 2.0);
cvConvertScale (tmp_img, tmp_img, 2.0);
cvPow (tmp_img, tmp_img, 0.5);
cvAcc (tmp_img, sgm_img);
}
cvConvertScale (sgm_img, sgm_img, 1.0 / INIT_TIME);
printf ("Background statistics initialization finish\n");
// (6)表示用ウィンドウを生成する
cvInitFont (&font, CV_FONT_HERSHEY_COMPLEX, 0.7, 0.7);
cvNamedWindow ("Input", CV_WINDOW_AUTOSIZE);
cvNamedWindow ("Substraction", CV_WINDOW_AUTOSIZE);
// (7)取得画像から背景を分離するループ
counter = 0;
while (1) {
frame = cvQueryFrame (capture);
cvConvert (frame, tmp_img);
// (8)背景となりうる画素の輝度値の範囲をチェックする
cvSub (av_img, sgm_img, lower_img);
cvSubS (lower_img, cvScalarAll (Zeta), lower_img);
cvAdd (av_img, sgm_img, upper_img);
cvAddS (upper_img, cvScalarAll (Zeta), upper_img);
cvInRange (tmp_img, lower_img, upper_img, msk_img);
// (9)輝度振幅を再計算する
cvSub (tmp_img, av_img, tmp_img);
cvPow (tmp_img, tmp_img, 2.0);
cvConvertScale (tmp_img, tmp_img, 2.0);
cvPow (tmp_img, tmp_img, 0.5);
// (10)背景と判断された領域の背景の輝度平均と輝度振幅を更新する
cvRunningAvg (frame, av_img, B_PARAM, msk_img);
cvRunningAvg (tmp_img, sgm_img, B_PARAM, msk_img);
// (11)物体領域と判断された領域では輝度振幅のみを(背景領域よりも遅い速度で)更新する
cvNot (msk_img, msk_img);
cvRunningAvg (tmp_img, sgm_img, T_PARAM, msk_img);
// (12)物体領域のみを出力画像にコピーする(背景領域は黒)
cvSetZero (dst_img);
cvCopy (frame, dst_img, msk_img);
// (13)処理結果を表示する
snprintf (str, 64, "%03d[frame]", counter);
cvPutText (dst_img, str, cvPoint (10, 20), &font, CV_RGB (0, 255, 100));
cvShowImage ("Input", frame);
cvShowImage ("Substraction", dst_img);
counter++;
c = cvWaitKey (10);
if (c == '\x1b')
break;
}
cvDestroyWindow ("Input");
cvDestroyWindow ("Substraction");
cvReleaseImage (&frame);
cvReleaseImage (&dst_img);
cvReleaseImage (&av_img);
cvReleaseImage (&sgm_img);
cvReleaseImage (&lower_img);
cvReleaseImage (&upper_img);
cvReleaseImage (&tmp_img);
cvReleaseImage (&msk_img);
return 0;
}
// (1)コマンド引数によって指定された番号のカメラに対するキャプチャ構造体を作成する
関数
cvCreateCameraCapture()を用いて,コマンド引数として与えられた番号のカメラに対するキャプチャ構造体
CvCaptureを作成する.引数が指定されない場合は,デフォルトの値である"0"が利用される.
// (2)1フレームキャプチャし,キャプチャサイズを取得する.
関数
cvQueryFrame() を呼び出し,実際に1フレームキャプチャを行い,取得した画像から,画像の幅wと高さhを取得する.
// (3)
作業用の領域を生成する
輝度平均や輝度振幅計算の為の作業領域を確保する.累積計算や累乗計算を行なうため,デプスは32ビットを指定しておく.マスク画像は8ビット,シングルチャンネルを指定する.
// (4)
背景の輝度平均の初期値を計算する
背景のモデル化の為に,指定されたフレーム数(INIT_TIME)間の各画素値の平均を求める.
cvAcc()関数を用いて,画像の累積値を計算する.
// (5)
背景の輝度振幅の初期値を計算する
(4)で計算された背景画像の平均値を用いて,輝度振幅の初期値を計算する.輝度振幅
// (6)表示用ウィンドウを生成する
入力画像,出力画像を表示するウィンドウを生成する.
//
(7)取得画像から背景を分離するループ
背景画像モデルの初期値を計算した後,取得画像から背景分離を行なうループに入る.
// (8)背景となりうる画素の輝度値の範囲をチェックする
画像の各画素について
// (9)輝度振幅を再計算する
(5)と同様にして各画素の輝度振幅
//
(10)背景と判断された領域の背景の輝度平均と輝度振幅を更新する
(8)で返されるマスク画像を用いて,背景領域のみ
cvRunningAvg()関数を用いて輝度平均と輝度振幅を更新する.
//
(11)物体領域と判断された領域では輝度振幅のみを(背景領域よりも遅い速度で)更新する
物体領域の輝度振幅を更新するために,マスク画像を反転させる.その後(10)と同様にして輝度振幅を更新する.ここで使用する更新速度パラメータは背景領域更新に使用するパラメータよりも大きくなければならない.
//
(12)物体領域のみを出力画像にコピーする(背景領域は黒)
処理結果を示すために,画像dst_imgに対して,frameからマスクにしたがって画素値をコピーする.
//
(13)処理結果を表示する
入力画像と,処理結果を表示する.キャプチャ中に"Esc"キーが押された場合は,終了する.
実行結果例
http://cafe.naver.com/opencv/33769
// 이미지 로딩
Mat image = imread(argv[1],
CV_LOAD_IMAGE_COLOR);
// 이미지의 크기를 가로길이 300 기준으로
축소
// 어떤 이미지가 들어와도 가로길이는 300으로
고정.
int destWidth = 300;
int destHeight = (image.rows * destWidth) / image.cols;
resize(image, image,
Size(destWidth , destHeight) , destWidth / image.cols, destHeight / image.rows,
INTER_LANCZOS4);
// 사각영역 계산...
Rect
rectangle(destWidth / 4,
15, destWidth / 4 * 2, image.rows -
15); // 전경
추출..
Mat
foreground(image.size(), CV_8UC4, Scalar(0, 0, 0, 0));
Mat result, bgModel, fgModel;
grabCut(image, result,
rectangle, bgModel, fgModel, 1,
GC_INIT_WITH_RECT);
compare(result, GC_PR_FGD, result,
CMP_EQ); image.copyTo(foreground,
result);
// 화면에
출력
imshow("Foreground", foreground);
waitKey(0); |
-
사람만으로 한정 지으실거면 human detection을 해야할것 같고 그게 아니라면 ROI검출하는 알고리즘들 보세요(saliency map을 위한 visual attention model이 대표적).
StereoCV님 말씀대로, 사람으로 한정 지으시려면 Haar나 HOG 사용하셔서 사람 검출하시면 되겠습니다. Haar를 사용하면 얼굴만 나온 사진, 상반신 까지 나온 사진, 전신사진 다 가능하기 때문에 비교적 쉽게 원하시는 바를 이루실 수 있겠습니다. 다만 Haar는 오검출률도 신경쓰셔야합니다.
http://cafe.naver.com/opencv/32019
'차근차근 > OpenCV' 카테고리의 다른 글
opencv 배경 제거 알고리즘 (0) | 2014.10.07 |
---|---|
openCV grabcut 배경제거 (0) | 2014.10.07 |
[Visual Studio 2012 ] PDB 파일을 찾거나 열 수 없습니다. (0) | 2014.10.06 |
The function is not implemented (0) | 2014.08.27 |
응용 프로그램 오류 (0xc000007b) (0) | 2014.08.27 |