幀差法是背景減圖法中的一種,只不過是幀差法不需要建模,因為它的背景模型就是上一幀的圖,所以速度非常快,另外幀差法對緩慢變換的光照不是很敏感,所以其用途還是有的,有不少學者對其做出了出色的改進。
其基本原理可以用下面公式看出:
|i(t)-i(t-1)|<T 背景
|i(t)-i(t-1)|>=T 前景
其中i(t),i(t-1)分別為t,t-1時刻對應像素點的像素值,T為閾值。
當然其缺點也不少,容易出現”雙影”和”空洞”現象。
用opencv2.3.1+vs2010做了個簡單的實驗,其實驗代碼如下:
1 // frame_diff.cpp : 定義控制台應用程序的入口點。 2 // 3 4 #include "stdafx.h" 5 #include <opencv2/highgui/highgui.hpp> 6 #include <opencv2/imgproc/imgproc.hpp> 7 #include <opencv2/core/core.hpp> 8 9 #define threshold_diff 20 //設置簡單幀差法閾值 10 11 using namespace cv; 12 using namespace std; 13 14 int main(int argc,unsigned char* argv[]) 15 { 16 Mat img_src1,img_src2,img_dst,gray1,gray2,gray_diff; 17 bool pause=false; 18 19 VideoCapture vido_file("IndoorGTTest1.avi");//在這里改相應的文件名 20 namedWindow("foreground",0); 21 for (;;) 22 { 23 if(!pause) 24 { 25 vido_file >>img_src1; //因為視頻文件幀數已經固定了,所以每次到這句語句都是讀取相鄰的幀數,沒到時間視頻並不向前走 26 cvtColor(img_src1,gray1,CV_BGR2GRAY); 27 imshow("video_src",img_src1);//可以事先不用新建一個窗口 28 waitKey(5); 29 30 vido_file >>img_src2; 31 cvtColor(img_src2,gray2,CV_BGR2GRAY); 32 imshow("video_src",img_src2);//可以事先不用新建一個窗口 33 34 waitKey(5); 35 subtract(gray1,gray2,gray_diff); 36 for(int i=0;i<gray_diff.rows;i++) 37 for(int j=0;j<gray_diff.cols;j++) 38 if(abs(gray_diff.at<unsigned char>(i,j))>=threshold_diff)//這里模板參數一定要用unsigned char,否則就一直報錯 39 gray_diff.at<unsigned char>(i,j)=255; 40 else gray_diff.at<unsigned char>(i,j)=0; 41 42 imshow("foreground",gray_diff); 43 } 44 char c=(char)waitKey(10); 45 if (c==27) 46 { 47 break; 48 } 49 if(c==' ') 50 pause=!pause; 51 } 52 return 0; 53 }
實驗結果如下:
可以看出其“雙影”和”空洞”比較明顯。雙影是由於幀差法有2個影子,在該試驗中就是輪廓變得很粗,”空洞”是由於物體內部顏色相近,檢測不出來。當然幀差法還有個致命的缺點那就是閾值T需要人工設定。
對於幀差法的”雙影”現象,有人提出來了三幀差法。其原理如下所示:
1. 由i(t)-i(t-1)得到前景圖 F1
2. 由i(t+1)-i(t)得到前景圖 F2
3. F1 ∩ F2得到前景圖 F3
4. 形態學處理
也就是利用2次相鄰幀的差,然后去與操作,就得到了真正的那個影子了。
這個在一定程度上可以解決”雙影”現象。
同樣做了個簡單的實驗,代碼如下:
1 // frame_3diff.cpp : 定義控制台應用程序的入口點。 2 // 3 4 #include "stdafx.h" 5 #include <opencv2/highgui/highgui.hpp> 6 #include <opencv2/imgproc/imgproc.hpp> 7 #include <opencv2/core/core.hpp> 8 9 #define threshold_diff1 10 //設置簡單幀差法閾值 10 #define threshold_diff2 10 //設置簡單幀差法閾值 11 12 using namespace cv; 13 using namespace std; 14 15 int main(int argc,unsigned char* argv[]) 16 { 17 Mat img_src1,img_src2,img_src3;//3幀法需要3幀圖片 18 Mat img_dst,gray1,gray2,gray3; 19 Mat gray_diff1,gray_diff2;//存儲2次相減的圖片 20 Mat gray;//用來顯示前景的 21 bool pause=false; 22 23 VideoCapture vido_file("IndoorGTTest1.avi");//在這里改相應的文件名 24 namedWindow("foreground",0); 25 for (;;) 26 { 27 if(!false) 28 { 29 vido_file >>img_src1; 30 cvtColor(img_src1,gray1,CV_BGR2GRAY); 31 32 waitKey(5); 33 vido_file >>img_src2; 34 cvtColor(img_src2,gray2,CV_BGR2GRAY); 35 imshow("video_src",img_src2);// 36 37 waitKey(5); 38 vido_file >>img_src3; 39 cvtColor(img_src3,gray3,CV_BGR2GRAY); 40 41 subtract(gray2,gray1,gray_diff1);//第二幀減第一幀 42 subtract(gray3,gray2,gray_diff2);//第三幀減第二幀 43 44 for(int i=0;i<gray_diff1.rows;i++) 45 for(int j=0;j<gray_diff1.cols;j++) 46 { 47 if(abs(gray_diff1.at<unsigned char>(i,j))>=threshold_diff1)//這里模板參數一定要用unsigned char,否則就一直報錯 48 gray_diff1.at<unsigned char>(i,j)=255; //第一次相減閾值處理 49 else gray_diff1.at<unsigned char>(i,j)=0; 50 51 if(abs(gray_diff2.at<unsigned char>(i,j))>=threshold_diff2)//第二次相減閾值處理 52 gray_diff2.at<unsigned char>(i,j)=255; 53 else gray_diff2.at<unsigned char>(i,j)=0; 54 } 55 bitwise_and(gray_diff1,gray_diff2,gray); 56 imshow("foreground",gray); 57 } 58 char c=(char)waitKey(10); 59 if (c==27) 60 { 61 break; 62 } 63 if(c==' ') 64 pause=!pause;//為什么暫停不了?? 65 } 66 return 0; 67 }
實驗結果如下:
可以看出,效果並沒怎么變好,只是影子輪廓確實變細了,”空洞”現象也沒有改善,當然這只是個簡單的實驗,也沒有優化或改進,用的是最原始的思想,沒有使用形態學做后期處理。