ViBe提取視頻前景目標

ViBe算法:ViBe - a powerful technique for background detection and subtraction in video sequences

算法官網:http://www2.ulg.ac.be/telecom/research/vibe/

描述:

ViBe是一種像素級視頻背景建模或前景檢測的算法,效果優於所熟知的幾種算法,對硬件內存佔用也少。

Code:

算法執行效率測試程序,windows和linux操作系統下的程序和c/c++文件都可以在作者官網下載,如下:



Windows and Linux users: a benchmarking program to evaluate the time needed by ViBe on your platform and on your own sequences! 
Download an archive zip archive [15 MB] to evaluate the time needed by ViBe on your platform (Windows or Linux [Wine]), and on your own sequences.
A program for Windows and Linux. Download an archive zip archive [16 MB] to use ViBe on Windows (or under Wine in Linux).
The program allows you to: (1) save the result for your own images, (2) change the few parameters of ViBe to experiment with, and (3) reproduce our results.
Linux: link a C/C++ object file to your own code. We provide the object (compiled) code of ViBe for non-commercial applications. Under Linux, download the32 bits zip file, or the 64 bits zip file. Details on this page.


當然,在使用ViBe算法時應該遵循算法官網的License。

算法的主要優勢:

內存佔用少,一個像素需要作一次比較,佔用一個字節的內存;

無參數法;

可直接應用在產品中,軟硬件兼容性好;

性能優於混合高斯,參數化方法,SACON等;

像素級算法,視頻處理中的預處理關鍵步驟;

背景模型及時初始化;

具有較好的抗噪能力。


參考文獻:

Performance

  • An independant evaluation is available in S. Brutzer, B. Hoferlin, and G. Heidemann. Evaluation of background subtraction techniques for video surveillance. In IEEE International Conference on Computer Vision and Pattern Recognition (CVPR), pages 1937-1944, Colorado Spring, USA, June 2011. 
    In their conclusions, they claim: "Considering these aspects, Barnich is a strong favorite, since it is simple and almost parameterless."

背景差方法實現運動物體檢測面臨的挑戰主要有:必須適應環境的變化(比如光照的變化造成圖像色度的變化); 相機抖動引起畫面的抖動(比如手持相機拍照時候的移動);圖像中密集出現的物體(比如樹葉或樹幹等密集出現的物體,要正確的檢測出來);必須能夠正確的檢測出背景物體的改變(比如新停下的車必須及時的歸爲背景物體,而有靜止開始移動的物體也需要及時的檢測出來)。物體檢測中往往會出現Ghost區域,Ghost區域也就是指當一個原本靜止的物體開始運動,背靜差檢測算法可能會將原來該物體所覆蓋的區域錯誤的檢測爲運動的,這塊區域就成爲Ghost,當然原來運動的物體變爲靜止的也會引入Ghost區域,Ghost區域在檢測中必須被儘快的消除。一個Ghost區域的實例如圖

ViBe算法詳解:

ViBe檢測方法

         ViBe是本篇論文中所提出的一個檢測方法,相比於其他方法它有很多的不同和優點。具體的思想就是爲每個像素點存儲了一個樣本集,樣本集中採樣值就是該像素點過去的像素值和其鄰居點的像素值,然後將每一個新的像素值和樣本集進行比較來判斷是否屬於背景點。該模型主要包括三個方面:模型的工作原理;模型的初始化方法;模型的更新策略。

模型的工作原理

背景物體就是指靜止的或是非常緩慢的移動的物體,而前景物體就對應移動的物體。所以我們可以把物體檢測看出一個分類問題,也就是來確定一個像素點是否屬於背景點。在ViBe模型中,背景模型爲每個背景點存儲了一個樣本集,然後將每一個新的像素值和樣本集進行比較來判斷是否屬於背景點。可以知道如果一個新的觀察值屬於背景點那麼它應該和樣本集中的採樣值比較接近。

具體的講,我們記v(x):x點處的像素值;M(x)={V1,V2,…VN}爲x處的背景樣本集(樣本集大小爲N);SR(v(x)):以x爲中心R爲半徑的區域,如果M(x) [{SR(v(x))∩ {v1,v2, . . . , vN}}]大於一個給定的閾值#min,那麼就認爲x點屬於背景點。

模型的初始化方法

初始化就是建立背景模型的過程。通用的檢測算法的初始化需要一定長度的視頻序列來完成,通常要耗費數秒的時間,這極大的影戲的檢測的實時性,對於手持相機實時拍照來講並不合適。ViBe的初始化僅僅通過一幀圖像即可完成。ViBe初始化就是填充像素的樣本集的過程但是由於在一幀圖像中不可能包含像素點的時空分佈信息,我們利用了相近像素點擁有相近的時空分佈特性,具體來講就是:對於一個像素點,隨機的選擇它的鄰居點的像素值作爲它的模型樣本值。M0(x) = {v0(y | y ∈NG(x))},t=0初始時刻,NG(x)即爲鄰居點 。這種初始化方法優點是對於噪聲的反應比較靈敏,計算量小速度快,可以很快的進行運動物體的檢測,缺點是容易引入Ghost區域。

模型的更新策略

背景模型的更新就是使得背景模型能夠適應背景的不斷變化,比如光照的變化,背景物體的變更等等。保守的更新策略:前景點永遠不會被用來填充背景模型,會引起死鎖,比如初始化的時候如果一塊靜止的區域被錯誤的檢測爲運動的,那麼在這種策略下它永遠會被當做運動的物體來對待;Blind策略:對死鎖不敏感,前景背景都可以來更新背景模型,缺點是緩慢移動的物體會融入背景中無法被檢測出來。在本方法中採用的更新策略是保守的更新策略+前景點計數方法。前景點計數:對像素點進行統計,如果某個像素點連續N次被檢測爲前景,則將其更新爲背景點。

隨機的子採樣:在每一個新的視頻幀中都去更新背景模型中的每一個像素點的樣本值是沒有必要的,當一個像素點被分類爲背景點時,它有1/ φ的概率去更新背景模型。

具體的更新方法:每一個背景點有1/ φ的概率去更新自己的模型樣本值,同時也有1/ φ的概率去更新它的鄰居點的模型樣本值。更新鄰居的樣本值利用了像素值的空間傳播特性,背景模型逐漸向外擴 散,這也有利於Ghost區域的更快的識別。同時當前景點計數達到臨界值時將其變爲背景,並有1/ φ的概率去更新自己的模型樣本值。

在選擇要替換的樣本集中的樣本值時候,我們是隨機選取一個樣本值進行更新,這樣可以保證樣本值的平滑的生命週期由於是隨機的更新,這樣一個樣本值在時刻t不被更新的概率是 (N-1)/N,假設時間是連續的,那麼在dt的時間過去後,樣本值仍然保留的概率是

也可以寫作, 

這就表明一個樣本值在模型中是否被替換與時間t無關 ,隨機策略是合適的。

ViBe實驗結果

         在實驗中,我們和其他的一些檢測算法在檢測準確率和算法的計算量方面都進行了比較,實驗表明我們的方法檢測效果明顯要好很多,對於光照的變化和相機抖動等的效果都十分穩定,而且計算量非常小,內存佔用較少,這就使得該方法能夠用於嵌入手持照相機中。一些具體的實驗效果和數據如下

總結

在這片文章中,我們提出了一個新的背靜差算法-ViBe,和以前相比它具有三個不同點。首先,我們提出了一個新的分類模型。其次,我們介紹了ViBe如何初始化,它只需要一幀圖像即可完成初始化,而其他的算法通常需要等待數秒去完成初始化,這對於嵌入照相機中的要求實時性比較高的和一些比較短的視頻序列很有幫助。 最後,我們提出了自己的更新策略,相比於其他算法將樣本值在模型中保存一個固定的時間,我們採用隨機的替換更新樣本值,經過證明這樣可以保證樣本值的一個指數衰減的平滑生命週期,並且可以使得背景模型很好的適應視頻場景的變化,從而達到更好的檢測效果。

通過一系列實驗表明ViBe方法相比於其他的一些檢測算法具有計算量小、內存佔用少、處理速度快、檢測效果好、有更快的Ghost區域消融速度和應對噪聲穩定可靠的特點,並且非常適合嵌入照相機等要求計算量小和內存佔用少的情境中。

vibe.h

#include "ViBe.h"
#include "opencv2/opencv.hpp"
int main(int argc, char *argv[])
{
	Mat frame, gray, mask;
	VideoCapture capture;
	capture.open("C:\\Users\\si\\Desktop\\1.avi");//campus sequence

	if (!capture.isOpened())
	{
		cout << "No camera or video input!\n" << endl;
		return -1;
	}    

	ViBe_BGS Vibe_Bgs;
	int count = 0;

	while (1)
	{

		count++;
		capture >> frame;
		if (frame.empty())//直到幀結束
			break;
		cvtColor(frame, gray, CV_RGB2GRAY);//色彩空間轉換

		if (count == 1)//處理第一幀
		{
			Vibe_Bgs.init(gray);
			Vibe_Bgs.processFirstFrame(gray);
			cout << " Training GMM complete!" << endl;
		}
		else//正常更新
		{
			Vibe_Bgs.testAndUpdate(gray);
			mask = Vibe_Bgs.getMask();
			morphologyEx(mask, mask, MORPH_OPEN, Mat());
			imshow("mask", mask);
		}
		imshow("input", frame);
		if (cvWaitKey(20) == 'q')
			break;
	}
	return 0;
}
Vibe.cpp

#include "ViBe.h"
#include <iostream>

int c_xoff[9] = { -1, 0, 1, 1, 1, 0, -1, -1, 0 };  //x的鄰居點
int c_yoff[9] = { -1, -1, -1, 0, 1, 1, 1, 0, 0 };  //y的鄰居點
ViBe_BGS::ViBe_BGS(void)
{

}
ViBe_BGS::~ViBe_BGS(void)
{

}

void ViBe_BGS::init(const Mat _image)
{
	for (int i = 0; i < NUM_SAMPLES; i++)
	{
		m_samples[i] = Mat::zeros(_image.size(), CV_8UC1);//剛開始都給0值初始化
	}
	m_mask = Mat::zeros(_image.size(), CV_8UC1);
	m_foregroundMatchCount = Mat::zeros(_image.size(), CV_8UC1);//前景匹配圖像
}

void ViBe_BGS::processFirstFrame(const Mat _image)
{
	RNG rng;//RNG:隨機數生成器
	int row, col;

	for (int i = 0; i < _image.rows; i++)//逐像素處理
	{
		for (int j = 0; j < _image.cols; j++)
		{
			for (int k = 0; k < NUM_SAMPLES; k++)//取NUM_SAMPLES個採樣點
			{
				// Random pick up NUM_SAMPLES pixel in neighbourhood to construct the model
				int random = rng.uniform(0, 9);//產生一個0-9的數字

				row = i + c_yoff[random]; //這裏表示產生的隨機數會在8領域範圍內選擇點作爲採樣點
				if (row < 0)
					row = 0;
				if (row >= _image.rows)
					row = _image.rows - 1;

				col = j + c_xoff[random];
				if (col < 0)
					col = 0;
				if (col >= _image.cols)
					col = _image.cols - 1;

				m_samples[k].at<uchar>(i, j) = _image.at<uchar>(row, col);
			}
		}
	}
}

void ViBe_BGS::testAndUpdate(const Mat _image)
{
	RNG rng;
	for (int i = 0; i < _image.rows; i++)
	{
		for (int j = 0; j < _image.cols; j++)
		{
			int matches(0), count(0);
			float dist;

			while (matches < MIN_MATCHES && count < NUM_SAMPLES) //#min指數,最小交集
			{
				dist = abs(m_samples[count].at<uchar>(i, j) - _image.at<uchar>(i, j));//這先計算里歐氏距離
				if (dist < RADIUS) //如果在我們設定的採樣半徑之內,匹配計數+1
					matches++;
				count++;
			}

			if (matches >= MIN_MATCHES)//#min 最小交集符合要求
			{
				// It is a background pixel
				m_foregroundMatchCount.at<uchar>(i, j) = 0;
				//說明該點與周圍點融合得比較好,可以作爲背景處理
				//某個像素點連續N次被檢測爲前景,則認爲一塊靜止區域被誤判爲運動,將其更新爲背景點。這裏需及時清零.

				// Set background pixel to 0
				m_mask.at<uchar>(i, j) = 0; //作爲圖像背景點

											// 如果一個像素是背景點,那麼它有 1 / defaultSubsamplingFactor 的概率去更新自己的模型樣本值
				int random = rng.uniform(0, SUBSAMPLE_FACTOR);//
				if (random == 0) // 1/SUBSAMPLE_FACTOR的概率去更新自己的樣本。
				{
					random = rng.uniform(0, NUM_SAMPLES);
					m_samples[random].at<uchar>(i, j) = _image.at<uchar>(i, j);
				}

				// 同時也有 1 / defaultSubsamplingFactor 的概率去更新它的鄰居點的模型樣本值
				random = rng.uniform(0, SUBSAMPLE_FACTOR);
				if (random == 0) // 1/SUBSAMPLE_FACTOR的概率去更新8領域範圍內的樣本。
				{
					int row, col;
					random = rng.uniform(0, 9);
					row = i + c_yoff[random];
					if (row < 0)
						row = 0;
					if (row >= _image.rows)
						row = _image.rows - 1;

					random = rng.uniform(0, 9);
					col = j + c_xoff[random];
					if (col < 0)
						col = 0;
					if (col >= _image.cols)
						col = _image.cols - 1;

					random = rng.uniform(0, NUM_SAMPLES);
					m_samples[random].at<uchar>(row, col) = _image.at<uchar>(i, j);
				}
			}
			else //距離太遠,色差太大。當前景用。
			{
				// It is a foreground pixel
				m_foregroundMatchCount.at<uchar>(i, j)++;

				// Set background pixel to 255
				m_mask.at<uchar>(i, j) = 255;

				//如果某個像素點連續N次被檢測爲前景,則認爲一塊靜止區域被誤判爲運動,將其更新爲背景點
				if (m_foregroundMatchCount.at<uchar>(i, j) > 50)
				{
					int random = rng.uniform(0, NUM_SAMPLES);
					if (random == 0)
					{
						random = rng.uniform(0, NUM_SAMPLES);
						m_samples[random].at<uchar>(i, j) = _image.at<uchar>(i, j);
					}
				}
			}
		}
	}
}

main.cpp

#include "ViBe.h"
#include "opencv2/opencv.hpp"
int main(int argc, char *argv[])
{
	Mat frame, gray, mask;
	VideoCapture capture;
	capture.open("C:\\Users\\si\\Desktop\\1.avi");//campus sequence

	if (!capture.isOpened())
	{
		cout << "No camera or video input!\n" << endl;
		return -1;
	}    

	ViBe_BGS Vibe_Bgs;
	int count = 0;

	while (1)
	{

		count++;
		capture >> frame;
		if (frame.empty())//直到幀結束
			break;
		cvtColor(frame, gray, CV_RGB2GRAY);//色彩空間轉換

		if (count == 1)//處理第一幀
		{
			Vibe_Bgs.init(gray);
			Vibe_Bgs.processFirstFrame(gray);
			cout << " Training GMM complete!" << endl;
		}
		else//正常更新
		{
			Vibe_Bgs.testAndUpdate(gray);
			mask = Vibe_Bgs.getMask();
			morphologyEx(mask, mask, MORPH_OPEN, Mat());
			imshow("mask", mask);
		}
		imshow("input", frame);
		if (cvWaitKey(20) == 'q')
			break;
	}
	return 0;
}


發佈了56 篇原創文章 · 獲贊 72 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章