動体検出を軽くする(AAS, CVPR '12)

はじめに

動体検出を使用して物体の検出を行うことがあります。特に最近のカメラは高画素であり、処理が重くなります。そこで、画像の全画素で処理を行わず、物体がありそうなところだけで処理を行う方法が考えられます。

アルゴリズム

ちょっと前の論文ですが、動体検出(背景除去)の高速化を目指した論文がありました。
Active Attentional Sampling for Speed-up of Background Subtraction, CVPR 2012.
これは過去の検出結果から、物体がありそうな確率マップ(probability map)を生成して、確率が高いところで検出処理を行おうとしたものです。それだと新しい侵入物に対応できないので、ランダムに数割程度の画素に対して検出処理をします。下の図は検出結果、probability map、maskを並べたものです。maskの白い部分が重い動体検出処理を行う画素で、黒の部分は処理しないため、全体としての処理は軽くなります。

f:id:wildpie:20150111201344p:plain

maskの生成にはいくつかのpropertyを考慮します。

temporal property

検出対象の位置は急には変わんないだろう特性です。
検出を行う画素を決めるmaskをM、検出結果をDとします。Mが1に近ければ動体の可能性が高いことを表します。
f:id:wildpie:20150111181213p:plain

spatial property

検出対象は次のフレームでも近くにいる特性です。sがnを中心としたw×wの領域の検出率を表しています。
f:id:wildpie:20150111182139p:plain

frequency property

過去3フレームで2回結果が変わったらおかしいだろ特性です。物体はちかちかしないだろうとする仮説です。
f:id:wildpie:20150111183508p:plain

foreground probability map

以上三つをまとめます。
f:id:wildpie:20150111183805p:plain
この結果が最初の画像まんなかのprobmapです。

randomly scatter

probability mapは確率のmapで、maskではありません。ここから、実際に使用するmaskを生成していきます。
f:id:wildpie:20150111184431p:plain
maskはM_RSとM_SEIとM_SPのORで計算します。
M_RSはrandomly scatterを表し、一定の割合で画像全体に検出点を作ります。
実装はランダムに点を選んでM_RS(i)=1にします。ちょっとしたテクニックですが、前のフレームでrandomly scatterした点が動体だと判定されたら、次のフレームでもM_RS(i)=1にしてます。

spatially expanding importance sampling

randomly scatterだと小さい物体の取りこぼしがあるかもしれないので、検出範囲をprobability mapに応じて広げます。M_RS(i)=1の点を中心に、矩形領域でmaskを1に塗りつぶします。そのとき矩形の大きさは確率に応じて大きくします。矩形の直径は以下で計算します。
f:id:wildpie:20150111190954p:plain
r(i)=P_FG(i)は確率、ω、kは定数で広げ具合を決めます。N_sはランダムの選択数です。

surprise pixel

速く動く物体や急に現れた物体に対応する必要があります。randomly scatter点で、確率が低いのに動体検出された場合にはsurprise pixelとして、それを中心としたω_sの大きさの矩形をmaskとします。
f:id:wildpie:20150111201004p:plain

まとめ

動体検出(背景除去)を高速化するためのアルゴリズムを調べました。全部探索する必要がないタスクは他にもありそうなので、このアルゴリズムを流用できるかもしれません。
OpenCVで実装したものを一部だけ載せておきます。

void AASampling::process(const cv::Mat1b& d_t)
{
	d_ = 0.0f + d_t;

	// temporal property
	m_t_ = (1.0f - ALPHA_T) * m_t_ + ALPHA_T * d_;

	// spatial property
	makeSpatialMatrix(s_);
	m_s_ = (1.0f - ALPHA_S) * m_s_ + ALPHA_S * s_;

	// frequency property
	makeFrequencyMatrix(f_);
	m_f_ = (1.0f - ALPHA_F) * m_f_ + ALPHA_F * f_;

	// foreground probability map
	p_fg_ = m_t_.mul(m_s_).mul(1.0f - m_f_);

	randomlyScattered(m_rs_);
	spatiallyExpanding(m_sei_);
	surprisePixel(m_sp_);

	// active sampling mask generation
	maskGeneration(result_mask_);

	pre_p_fg_ = p_fg_.clone();
	pre_pre_d_ = pre_d_.clone();
	pre_d_ = d_.clone();
}