1. 程式人生 > >學習OpenCV範例(二十三)—GMM前景檢測(一)

學習OpenCV範例(二十三)—GMM前景檢測(一)

前一篇部落格中有談論到混合高斯模型GMM,但是隻是在上面的一個小應用,可能沒有很徹底的分析,大部分讀者看起來有點吃力,那麼在這篇微博中就給大家分析一下GMM在前景檢測的原理以及在OpenCV中的運用,當然長篇大論的原理我還是不全部寫出來的,依舊會貼出其他高手的部落格,他們寫的個人覺得已經夠詳細了,再總結已經沒什麼意思了,也總結不出太多的新意,在這裡也是作為記錄,談談自己的小感悟以及執行的結果和函式的使用。

1、原理

如果大家想了解最原汁原味的原理,那還是看原作者的論文吧,如果想了解濃縮版的,和基本的實現步驟,那可以訪問以下幾篇部落格

當然,看完了論文,還會覺得有點雲裡霧裡的感覺,不知道怎麼做到的,怎麼實現,那這時候就得看看原始碼了

有了原理,又提供了函式庫,有了函式介面,那肯定得試一下效果如何

本人也是根據上面提供的程式碼測試了效果,加強了一點註解,感覺不錯。

GMM演算法不同於其他的背景相減方法,因為有時背景也存在部分割槽域的震盪變化,那如果按照一般的前景檢測方法,則會頻繁的檢測到錯誤的前景,GMM演算法則有效的克服了這一點,那是因為GMM演算法有效的做到了以下幾點:

(1)、對每個畫素建立多個高斯模型(即存在多個滑動平均值),那麼背景畫素就可以在多個均值之間波動,而不會被誤判,如果有新的畫素值不屬於其中的一個高斯模型,則認為是前景。

(2)、不僅僅儲存滑動平均值,還儲存了滑動方差,由方差和均值產生了一個高斯模型,於是我們可以獲知某個畫素值屬於哪個高斯模型的概率,如果新畫素不屬於其中的一個高斯模型,則認為是前景。

(3)、增加了學習因子,如果某個模型被擊中的頻率不夠頻繁,那麼權值就會減少,減少到最後把該模型移除,如果一個畫素是前景,那麼新的高斯模型會被建立,剛開始權值較小,但是如果該前景一直不動,不離開,則權重加大,慢慢的和背景融為一體,成為了新的背景。

2、程式碼實現

#include "opencv2/core/core.hpp"
#include "opencv2/video/background_segm.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <stdio.h>

using namespace std;
using namespace cv;

//this is a sample for foreground detection functions
string src_img_name="WavingTrees/b00";
const char *src_img_name1;
Mat img, fgmask, fgimg;
int i=-1;
char chari[500];
bool pause=false;

//第一種gmm,用的是KaewTraKulPong, P. and R. Bowden (2001).
//An improved adaptive background mixture model for real-time tracking with shadow detection.
BackgroundSubtractorMOG bg_model(200,5,0.7,10);

void refineSegments(const Mat& img, Mat& mask, Mat& dst)
{
	int niters = 3;

	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;

	Mat temp;

	dilate(mask, temp, Mat(), Point(-1,-1), niters);//膨脹,3*3的element,迭代次數為niters
	erode(temp, temp, Mat(), Point(-1,-1), niters*2);//腐蝕
	dilate(temp, temp, Mat(), Point(-1,-1), niters);

	findContours( temp, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );//找輪廓

	dst = Mat::zeros(img.size(), CV_8UC3);

	if( contours.size() == 0 )
		return;

	// iterate through all the top-level contours,
	// draw each connected component with its own random color
	int idx = 0, largestComp = 0;
	double maxArea = 0;
	//輪廓模式為CV_RETR_CCOMP,第一層為連通域的外圍邊界,因為我們前景顏色值為255,背景為0
	//所以得到的輪廓為外圍邊界,所以我們只需要第一層,hierarchy[idx][0]表示為下一個輪廓的索引
	//如果到達最後一個輪廓,則hierarchy[idx][0]=-1;
	//如需詳細瞭解可訪問網址:http://blog.csdn.net/chenjiazhou12/article/details/22304099
	for( ; idx >= 0; idx = hierarchy[idx][0] )
	{
		const vector<Point>& c = contours[idx];
		double area = fabs(contourArea(Mat(c)));
		if( area > maxArea )
		{
			maxArea = area;
			largestComp = idx;//找出包含面積最大的輪廓
		}
	}
	Scalar color( 0, 255, 0 );
	drawContours( dst, contours, largestComp, color, CV_FILLED, 8, hierarchy );
}

int main(int argc, const char** argv)
{
	img=imread("WavingTrees/b00000.bmp");
	if(img.empty())
	{
		namedWindow("image",1);
		namedWindow("foreground image",1);
		namedWindow("mean background image", 1);
	}
	for(;;)
	{
		if(!pause)
		{
			//讀取圖片檔案
			i++;
			itoa(i,chari,10);
			if(i<10)
			{
				src_img_name+="00";
			}
			else if(i<100)
			{
				src_img_name+="0";
			}
			else if(i>283)
			{
				i=-1;
			}

			src_img_name+=chari;
			src_img_name+=".bmp";

			img=imread(src_img_name);
			if( img.empty() )
				break;

			//update the model
			bg_model(img, fgmask,0.005 );//計算前景mask影象,其中輸出fgmask為8-bit二進位制影象,第3個引數為學習速率,如果學習速率為0,則為背景相減法
			refineSegments(img, fgmask, fgimg);

			imshow("image", img);
			imshow("foreground image", fgimg);

			src_img_name="WavingTrees/b00";

		}
		char k = (char)waitKey(80);
		if( k == 27 ) break;

		if( k == ' ' )
		{
			pause=!pause;
		}        
	}

	return 0;
}

3、實現結果


                                      圖1、開始背景建模


                                     圖2、前景檢測


                                   圖3、前景檢測


                                    圖4、前景檢測


                                  圖5、後期背景檢測

總結:剛開始時,樹葉都是在搖晃的,所以把樹當成前景,如圖1所示,但當訓練很多副圖片之後,模型慢慢適應了樹葉的晃動,於是能把樹葉也當成背景,如圖5所示,由於人物是不時的插進來,於是一直被認為是前景來處理,所以GMM對背景的震盪變化有較好的處理效果。

4、用到的類或函式

BackgroundSubtractorMOG

建構函式

BackgroundSubtractorMOG::BackgroundSubtractorMOG(int history, int nmixtures, double backgroundRatio, double noiseSigma=0)

history :歷史幀數的長度,如果是預設建構函式,則值為200
nmixtures :高斯模型個數,如果是預設建構函式,則值為5
backgroundRatio :背景門限,如果是預設建構函式,則值為0.7
noiseSigma :噪聲方差,預設為0,如果是預設建構函式,則值為15
void BackgroundSubtractorMOG::operator()(InputArray image, OutputArray fgmask, double learningRate=0)

功能:更新背景模型,返回前景mask

image:輸入圖片

fgmask:輸出前景mask

learningRate:學習速率,如果為0,則為背景相減法