EmguCV Image Process: Process Video Sequences
參考OpenCV 2 Computer Vision Application Programming Cookbook第十章
介紹內容如下:
Reading video sequences
Processing the video frames
Writing video sequences
Tracking feature points in video
Extracting the foreground objects in video
上一篇介紹了透過OpticalFlow來做特徵追蹤
本篇將介紹簡單的方法實作前景偵測
前景偵測所感興趣的物件,就是在畫面中移動的物件、區塊
稱之為前景
反之的部分則為背景
要實作前景偵測就必須要建立背景模型
而最簡單的方式
就是以擷取的第一張畫面作為背景模型
而前景的影像 = 背景模型 與 當前影像 的差值
這裡利用AbsDiff這個方法來實作
計算出的差值差異達到某種程度,就認之為前景
但這方法有一些限制
大環境的變化就會影響前景偵測的效果
例如:光線的變化
光線的變化對於人眼來說算是背景的變化
但若是變化過於劇烈,則會被程式誤判為前景
因此需要一些動態的方式來建立背景模型
若是利用一段時間的影像利用平均值來建立背景模型似乎可行
但這會遇到幾個問題:
這樣勢必得儲存許多張的影像來建立背景
在計算平均值的過程無法做前景偵測
更重要的問題是,到底要多少張影像才夠製作背景模型?
而比較好的方法就是不斷的去更新背景模型
這個方法被稱之為:running average (moving average)
就是不斷的利用新的一張影像來更新背景模型
而每次的計算都給予一定程度的百分比來更新
例:當前的平均值 = (1-a)*前一張平均值 + a*當前的值
a則稱之為learning rate
learning rate的值越大
背景更新的速度就越快
延續前幾篇的實作模式
這裡來實作一個背景/前景切割類別:
public class BGFGSegmentor { //Current gray-level image Image<Gray, Byte> gray; //accumulated background Image<Gray, float> background; //background image Image<Gray, Byte> backImage; //foreground image Image<Gray, Byte> foreground; //learning rate in background accumulation double learningRate; //threshold for foreground extraction int threshold; public BGFGSegmentor() { this.threshold = 10; this.learningRate = 0.01; } public IImage Process(IImage image) { Image<Gray, Byte> output; if (image.NumberOfChannels != 1) { //convert to gray-level image this.gray = ((Image<Bgr, Byte>)image).Convert<Gray, Byte>(); } else { this.gray = ((Image<Gray, Byte>)image).Copy(); } output = this.gray.Copy(); //initialize background to 1st frame if (this.background == null) { this.background = this.gray.Convert<Gray,float>(); } //convert background to 8U this.backImage = this.background.Convert<Gray, Byte>(); //compute difference between image and background this.foreground = this.gray.AbsDiff(this.backImage); //apply threshold to foreground image output = this.foreground.ThresholdBinaryInv(new Gray(this.threshold), new Gray(255)); //accumulate background CvInvoke.cvRunningAvg(this.gray, this.background, this.learningRate, output); return output; } }
此類別需要用到當前的灰階影像 gray
背景模型影像(32bits) background
背景模型影像(8bits) backImage
前景影像 foreground
背景更新率 learningRate
前景擷取閥值 threshold
這幾個變數來實作
一開始先取得傳入影像的灰階影像
並以第一張影像作為背景模型影像
而前景影像就是當前影像與背景影像的差值:
this.foreground = this.gray.AbsDiff(this.backImage);
將前景影像以前景偵測閥值做二值化處理後,則為結果output
output = this.foreground.ThresholdBinaryInv(new Gray(this.threshold), new Gray(255));
最後就是更新背景模型影像
CvInvoke.cvRunningAvg(this.gray, this.background, this.learningRate, output);
利用二值化影像output作為遮罩
將當前影像與背景模型影像做更新率的計算
最後實作的方式如同過去一般:
//Create video processor instance VideoProcessor processor = new VideoProcessor(); //Create background/foreground segmentor BGFGSegmentor segmentor = new BGFGSegmentor(); //Open the video file processor.SetInput(@"tracking.avi"); //Declare a window to display the video processor.DisplayInput("Current frame"); processor.DisplayOutput("Output frame"); //Play the video at the original frame rate processor.SetDelay((int)(1000 / processor.GetFrameRate())); //Set the frame processor callback function processor.SetFrameProcessor(segmentor.Process); //Start the process processor.Run();
執行結果如下:
前景的部分以黑色來呈現
光影變化的部分像是影子也會被誤認為前景
與背景色差異越明顯的部分就會偵測的越明顯
這一張有發現哪裡怪怪的嗎??
與前一張相同的問題,發現了嗎?
最後兩張可以清楚地發現
明明當前影像(上面彩色影像)只有一個人或兩個人
但前景影像(下面黑白影像)在左上方卻有很多人影
這是怎麼回事??
由於此影片的第一張影像
就不是一個乾淨的背景影像
因此在第一張背景模型建立的時候,程式就已經認為那幾個人影是背景
因此當這幾個人離開畫面後
程式就會誤認那個幾個區塊變成了前景
也因為色階的差異過大,在計算cvRunningAvg時所帶入的前景遮罩
使得這個區塊一直無法被更新成背景
因此這幾個黑影就成了永遠的前景
在前景偵測這個領域
就不得不提到混合高斯模型(mixture of Gaussian)
其修正了背景更新的計算方式
使其不容易發生上述的問題
對於光影變化的雜訊控制也比較好
而在EmguCV 2.4板之後提供了新的類別:BackgroundSubstractorMOG2
甚至還提供了陰影偵測
偵測的結果如下:
左上方的人影非但不見了
連人的影子都消去了
是不是很神奇呢!!
而BackgroundSubstractorMOG2這個類別的實作方式非常簡單
在建構式時帶入參數:
this.substractor = new BackgroundSubstractorMOG2(0, 50f, true);
並透過
this.substractor.Update(image as Image<Bgr, byte>);
即可取得 this.substractor.ForgroundMask
自行實作看看嘍~!!
沒有留言:
張貼留言