About BBS

背景統計量の累積

作成者: 上田悦子, 最終変更者: 怡土順一, 最終変更リビジョン: 357, 最終変更日時: 2007-12-26 14:22:07 +0900 (水, 26 12月 2007)
This is a local copy taken at 2010/09/20. CLICK HERE to the original page at opencv.jp.

■ 背景と注目物体の分離

OpenCVには,背景差分を計算する際に便利な背景統計量の累積に関する関数が実装されている.
画像中から,変化のない背景領域とそれ以外の領域を分離することは,コンピュータビジョンシステムにおいて多く使用される技術であり,様々な手法が提案されている. OpenCVにおいても,cvauxで2種類の動的背景差分手法が実装されている. ここでは,参考文献[1]で用いられている背景画像の時間的な変化を考慮した動的背景更新によるロバストな注目物体の検出手法を,OpenCVの関数を用いて実装した.
この手法では,背景領域画素の輝度 I を以下のようにモデル化している.
I = \overline{I} + \sigma\sin(2 \pi \omega t) + k \zeta
\overline{I} は輝度値の時間平均, \sigma は輝度の振幅, \omega は輝度の周波数, t は時間, k ( -1 \leq k \leq 1 )は係数, \zeta はカメラのみに依存するノイズの最大値をそれぞれ表す.
このモデルにおいて, I が, \overline{I} - \sigma - \zeta \leq I \leq \overline{I} + \sigma + \zeta の場合に,その画素は背景領域に存在する画素であると判断する.

背景と判定された領域では輝度平均値 \overline{I}と振幅 \sigma を以下の式を用いて更新する.
\overline{I}' = (n-1) / n \times \overline{I} + 1 / n \times I
\sigma' = (n-1)/n \times \sigma + 1/n \times \sqrt{2 \times ( I - \overline{I})^2}
ここで, nは更新速度パラメータである.

一方,物体領域と判定された領域では,輝度平均値は元の値を保持し,振幅 \sigma のみを以下の式を 用いて更新する.
\overline{I}' = \overline{I}
\sigma' = (m-1)/m \times \sigma + 1/m \times \sqrt{2 \times ( I - \overline{I})^2}
ここで, m ( m \geq n )は物体領域更新速度パラメータである.

[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)で計算された背景画像の平均値を用いて,輝度振幅の初期値を計算する. 輝度振幅 \sigma \sqrt{2 \times ( I - \overline{I})^2}として計算する. 輝度振幅についても指定されたフレーム数(INIT_TIME)間の平均を求める.

// (6)表示用ウィンドウを生成する
入力画像,出力画像を表示するウィンドウを生成する.

// (7)取得画像から背景を分離するループ
背景画像モデルの初期値を計算した後,取得画像から背景分離を行なうループに入る.

// (8)背景となりうる画素の輝度値の範囲をチェックする
画像の各画素について \overline{I} - \sigma - \zeta \leq I \leq \overline{I} + \sigma + \zeta を計算し,cvInRange()で値が範囲内かどうかを比較し,msk_imgに結果を保存して返す. 範囲内の画素に関してはmsk_imgの対応する画素値が0xFFとしてセットされる.

// (9)輝度振幅を再計算する
(5)と同様にして各画素の輝度振幅 \sigma を再計算する.

// (10)背景と判断された領域の背景の輝度平均と輝度振幅を更新する
(8)で返されるマスク画像を用いて,背景領域のみ cvRunningAvg()関数を用いて輝度平均と輝度振幅を更新する.

// (11)物体領域と判断された領域では輝度振幅のみを(背景領域よりも遅い速度で)更新する
物体領域の輝度振幅を更新するために,マスク画像を反転させる.その後(10)と同様にして輝度振幅を更新する. ここで使用する更新速度パラメータは背景領域更新に使用するパラメータよりも大きくなければならない.

// (12)物体領域のみを出力画像にコピーする(背景領域は黒)
処理結果を示すために,画像dst_imgに対して,frameからマスクにしたがって画素値をコピーする.

// (13)処理結果を表示する
入力画像と,処理結果を表示する.キャプチャ中に"Esc"キーが押された場合は,終了する.

実行結果例

You need to upgrade your Flash Player
(左)入力画像,(右)物体検出結果

OpenCV-1.0 リファレンス マニュアル
OpenCV-1.1pre リファレンス マニュアル
OpenCVサンプルコード


画素値の直接操作
部分画像のシャッフル
画像の連結
画像のコピー
画像形状の変形
タイリング
画像の反転
逆行列(擬似逆行列)の計算
色空間の写像
離散フーリエ変換
階層構造を持つ輪郭の座標取得
図形の描画
ポリゴンの描画
凸ポリゴンの描画
テキストの描画
IplImage構造体情報の保存
マップのシーケンスを保存
IplImage構造体情報の読み込み
マップのシーケンスを読み込む
K-means法によるクラスタリング
クラスタリングによる減色処理
エッジの検出
コーナーの検出
並進移動のためのピクセルサンプリング
回転移動のためのピクセルサンプリング
画像のサイズ変更
画像のアフィン変換(1)
画像のアフィン変換(2)
画像の透視投影変換
全方位画像の透視投影変換
モルフォロジー変換
平滑化
ユーザ定義フィルタ
境界線の作成
画像の二値化
画像の二値化(大津の手法)
画像ピラミッドの作成
画像ピラミッドを用いた画像の領域分割
平均値シフト法による画像のセグメント化
Watershedアルゴリズムによる画像の領域分割
輪郭の検出と描画
画像のモーメントを計算
ハフ変換による直線検出
ハフ変換による円検出
距離変換とその可視化
不要オブジェクトの除去
ヒストグラムの描画
ヒストグラム間の距離
二次元のヒストグラム
バックプロジェクションパッチ
ヒストグラムの均一化
テンプレートマッチング
形状のマッチング
点列を包含する矩形
輪郭領域の面積と輪郭の長さ
二つの矩形を包含する矩形
楕円のフィッティング
点列を包含する図形
動的背景更新による物体検出
snakeによる輪郭追跡(静止画)
オプティカルフロー1
オプティカルフロー2
オプティカルフロー3
Condensation
顔の検出
カメラキャリブレーション
歪み補正
マップを利用した歪み補正
サポートベクターマシン
画像の各ピクセル値を特徴ベクトルとしたSVMの学習
画像の各ピクセル値を特徴ベクトルとしたSVMによる物体検出
マウスイベントの取得
トラックバーの利用
カメラからの画像キャプチャ
動画としてファイルへ書き出す
ラベリング