平均背景法的原理

《Learning OpenCV》中的第9中開篇介紹了一種建立背景模型的方法——平均背景法。 但書上只是簡單地介紹了一下這種方法的思想,更多的原理蘊含在它給出的代碼當中。

平均背景法是一種建立背景模型的方法。簡單地說,就是從視頻或攝像頭中獲取一系列的幀,然後取這些幀中的平均像素值來表徵背景。然後再給這些平均像素值加上一定的閾值範圍,這就構成了背景模型。在新加入的圖像中,如果對應位置的像素超出了這個背景模型中對應位置像素的閾值範圍,就把它當作前景。

應用的例子:舉一個例子來說明背景模型的作用,比如說深夜裏,攝像頭檢測是否有可疑人物進去,一旦建立了背景模型,攝像頭所拍攝的圖像將會與背景模型圖像進行比較,如果圖像中的像素變動很大,則說明前景發生了變化。如果有可疑人物進入攝像頭的範圍,這種變化就可以被捕捉到,可以進行報警等操作。

參考後面附上的代碼,下面介紹平均背景法的建模和分割的步驟:

步驟1: 累積背景

從視頻或攝像頭中連續獲取301幀圖像,把這301幀圖像分別標爲I1I2 ... I300、 I301

 

l  從第2幀圖像開始,把後面的300幀圖像累加起來(這種累加是矩陣加法,下同)I2+…+ I300+I301 (因爲考慮到後面的幀差需要從第2幀開始計算,所以這裏也是從第2幀開始),得到圖像IavgF

 

定義:幀差 = In-In-1

l  把300幀圖像裏面的每一個幀差累加起來[I2-I1+I3-I4+…+I299-I300+(I300-I301)]得到圖像 Iscratch

 

步驟2: 創建基於統計學的背景模型

由步驟1得到了兩個圖像,IavgF Iscratch

l  計算出這300幀中每幀的平均值IavgF * (1.0/300) --> IavgF

l  從這300幀的幀差和中計算出平均幀差IdiffF * (1.0/300) --> IdiffF

 

l  計算閾值的上限:(這裏以7.0作爲一個計算閾值上限的參數)

IavgF + [(IdiffF + 1.0) * 7.0] --> IhiF

IhiF這個3通道圖分割成3個單通道圖Ihi1, Ihi2, Ihi3分別代表BGR通道

 

l  計算閾值的下限:(這裏以6.0作爲一個計算閾值下限的參數)

IavgF - [(IdiffF + 1.0) * 6.0] --> IlowF

IhiF這個3通道圖分割成3個單通道圖Ilow1, Ilow2, Ilow3分別代表BGR通道

 

注意:幀差越大的地方,計算出來的閾值範圍就越大。最直觀的感受是,變化得比較厲害的那些位置,擁有的閾值範圍較大。

步驟3: 利用背景模型分割背景

繼續從視頻或攝像頭中獲取圖像幀,把獲取到的圖像幀標爲I

l  把I這個3通道圖分割成3個單通道圖Igray1, Igray2, Igray3分別代表BGR通道

l  對Igray1, Igray2,Igray3進行閾值處理:

對於Igray1,如果圖像中的像素值在(low1, lhi1)的範圍之內,則把該像素值設置爲255,否則把該像素值設爲0。對Igray2Igray3作同樣的處理。然後合併Igray1, Igray2, Igray33個單通道圖爲13通道圖Imask

l  作最後處理: 255 – Imask à Imask

 

結果是:

l  在建立背景模型Imask的過程中,像素值不常變化的地方,在最後得到的背景模型當中閾值範圍較小,只要圖像幀I中的像素在這些閾值範圍較小的敏感地方,對應於背景模型圖像Imask中的像素稍微出現一些變動,結果圖像中那些變動的位置就會呈現出白色(因爲通過計算所得到的像素值爲255),而沒有變動的地方則呈現黑色(因爲通過計算所得到的像素值爲0)。

l  然而,在建立背景模型Imask的過程中,在像素值經常變化的地方,在最後得到的背景模型當中閾值範圍較大,所以這些區域就不那麼敏感了,需要變化較大時纔會顯現白色。

參考代碼

http://hi.baidu.com/lin65505578/item/3e2faa42d4dd791c896d10d2

這篇博文對《Learning OpenCV》一書當中的代碼進行了一點擴充,而且給出了一些有關背景模型的資料,我在學習這種方法的時候也是參考了這篇博文,通過代碼來理解平均背景法的原理。這裏引用一下博文裏給出的代碼,爲了幫助理解代碼,我同時給出了一個幫助理解這些代碼的僞代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
IplImage *IavgF,*IdiffF,*IprevF,*IhiF,*IlowF;
IplImage *Iscratch,*Iscratch2;
IplImage * Igray1,*Igray2,*Igray3;
IplImage *Ilow1,*Ilow2 ,*Ilow3;
IplImage *Ihi1,*Ihi2,*Ihi3;
IplImage *Imaskt;
IplImage *Imask;
float Icount;
void AllocateImages(IplImage *I);
void accumulateBackground(IplImage * I);
void createModelsfromStats();
void setHighThreshold(float scale);
void setLowThreshold(float scale);
void backgroundDiff(IplImage *I,IplImage * Imask);
void DeallocateImages();
int main(int argc,char ** argv)
{
    CvCapture *capture=cvCreateCameraCapture(0);
    if (NULL==capture)
    {
        printf("沒有找到攝像頭裝置!\n");
        return -1;
    }
    IplImage * Img=cvQueryFrame(capture);
    cvNamedWindow("原圖",0);
    cvNamedWindow("檢測圖",0);
    cvShowImage("原圖",Img);
    AllocateImages(Img);//初始化
    printf("開始統計背景模型\n");
    while (Icount<300)//統計背景模型
    {
        accumulateBackground(Img);
        Img=cvQueryFrame(capture);
        cvWaitKey(100);
        cvShowImage("原圖",Img);
        printf(".");
    }
    createModelsfromStats();//就算每一個像素的均值和方差觀測
    printf("\n統計背景模型結束.....\n");
    printf("按任意鍵開始分割圖像.....\n");
    cvWaitKey(NULL);
    printf("開始分割圖像.....\n");
    while (1)
    {
        Img=cvQueryFrame(capture);
        backgroundDiff(Img,Imask);
        cvShowImage("原圖",Img);
        cvShowImage("檢測圖",Imask);
        if (27==cvWaitKey(100))
            break;         
    }
    cvDestroyAllWindows();
    //釋放內存
    DeallocateImages();
    cvReleaseCapture(&capture);
    return 0;
 
}
 
void AllocateImages(IplImage *I)
{
    CvSize sz=cvGetSize(I);
    IavgF=cvCreateImage(sz,IPL_DEPTH_32F,3);
    IdiffF=cvCreateImage(sz,IPL_DEPTH_32F,3);
    IprevF=cvCreateImage(sz,IPL_DEPTH_32F,3);
    IhiF=cvCreateImage(sz,IPL_DEPTH_32F,3);
    IlowF=cvCreateImage(sz,IPL_DEPTH_32F,3);
    Ilow1=cvCreateImage(sz,IPL_DEPTH_32F,1);
    Ilow2=cvCreateImage(sz,IPL_DEPTH_32F,1);
    Ilow3=cvCreateImage(sz,IPL_DEPTH_32F,1);
    Ihi1=cvCreateImage(sz,IPL_DEPTH_32F,1);
    Ihi2=cvCreateImage(sz,IPL_DEPTH_32F,1);
    Ihi3=cvCreateImage(sz,IPL_DEPTH_32F,1);
    cvZero(IavgF);
    cvZero(IdiffF);
    cvZero(IprevF);
    cvZero(IhiF);
    cvZero(IlowF);
    Icount=0.00001;
    Iscratch=cvCreateImage(sz,IPL_DEPTH_32F,3);
    Iscratch2=cvCreateImage(sz,IPL_DEPTH_32F,3);
    Igray1=cvCreateImage(sz,IPL_DEPTH_32F,1);
    Igray2=cvCreateImage(sz,IPL_DEPTH_32F,1);
    Igray3=cvCreateImage(sz,IPL_DEPTH_32F,1);
    Imaskt=cvCreateImage(sz,IPL_DEPTH_8U,1);
    Imask=cvCreateImage(sz,IPL_DEPTH_8U,1);
    cvZero(Iscratch);
    cvZero(Iscratch2);
    cvZero(Imask);
    cvZero(Imaskt);
}
void accumulateBackground(IplImage * I)
{
    static int first=1;
    cvCvtScale(I,Iscratch,1,0);
    if (!first)
    {
        cvAcc(Iscratch,IavgF);
        cvAbsDiff(Iscratch,IprevF,Iscratch2);
        cvAcc(Iscratch2,IdiffF);
        Icount+=1.0;
    }
    first=0;
    cvCopy(Iscratch,IprevF);
}
void createModelsfromStats()
{
    //通過除以輸入圖像積累的數目計算平均原始圖像和絕對差分圖像
    cvConvertScale(IavgF,IavgF,(double)(1.0/Icount));
    cvConvertScale(IdiffF,IdiffF,(double)(1.0/Icount));
 
    //make sure diff is slways something
    cvAddS(IdiffF,cvScalar(1.0,1.0,1.0),IdiffF);//確保平均差分圖像的值最小是1
    setHighThreshold(7.0);//當計算前景和背景閾值以及避免前景閾值和背景閾值相等而出現的退化情況時,我們要縮放這個因素
    setLowThreshold(6.0);
}
void setHighThreshold(float scale)
{
    cvConvertScale(IdiffF,Iscratch,scale);
    cvAdd(Iscratch,IavgF,IhiF);
    cvSplit(IhiF,Ihi1,Ihi2,Ihi3,0);
}
void setLowThreshold(float scale)
{
    cvConvertScale(IdiffF,Iscratch,scale);
    cvSub(IavgF,Iscratch,IlowF);
    cvSplit(IlowF,Ilow1,Ilow2,Ilow3,0);
}
void backgroundDiff(IplImage *I,IplImage * Imask)
{
    cvConvertScale(I,Iscratch,1,0);
    cvSplit(Iscratch,Igray1,Igray2,Igray3,0);
    cvInRange(Igray1,Ilow1,Ihi1,Imask);
    cvInRange(Igray2,Ilow2,Ihi2,Imaskt);
    cvOr(Imask,Imaskt,Imask);
    cvInRange(Igray3,Ilow3,Ihi3,Imask);
    cvOr(Imask,Imaskt,Imask);
    cvSubRS(Imask,cvScalar(255),Imask);
}
void DeallocateImages()
{
    cvReleaseImage(&IavgF);
    cvReleaseImage(&IdiffF);
    cvReleaseImage(&IprevF);
    cvReleaseImage(&IhiF);
    cvReleaseImage(&IlowF);
    cvReleaseImage(&Ilow1);
    cvReleaseImage(&Ilow2);
    cvReleaseImage(&Ilow3);
    cvReleaseImage(&Ihi1);
    cvReleaseImage(&Ihi2);
    cvReleaseImage(&Ihi3);
    cvReleaseImage(&Iscratch);
    cvReleaseImage(&Iscratch2);
    cvReleaseImage(&Igray1);
    cvReleaseImage(&Igray2);
    cvReleaseImage(&Igray3);
    cvReleaseImage(&Imaskt);
    cvReleaseImage(&Imask);
}


對應的僞代碼

 Step1 : Accumulate background 

 Icount = 300 times 

                  convert to float 
 I(a frame) ------------------>  Iscratch + IavgF-->  IavgF (do the loop for 300 times) 
                                                     - 
                                                  IprevF 
                                                     = 
                                                Iscratch2 

 Iscratch2 + IdiffF --> IdiffF 


Step2: Create model from statistics 

Icount = 300 

IavgF * (1.0/Icount) --> IavgF 
IdiffF * (1.0/Icount) --> IdiffF 

IavgF + [(IdiffF + 1.0) * 7.0] --> IhiF --> Ihi1, Ihi2, Ihi3 
IavgF - [(IdiffF + 1.0) * 6.0] --> IlowF --> Ilow1, Ilow2, Ilow3 


Step3: Segmentation 

                  convert to float 
I(a frame)  ------------------> Iscratch --> Igray1, Igray2, Igray3 

            (Ilow1,Ihi1)-> 255 otherwise -> 0 
Igray1 ------------------------------------> Imask 
     
             (Ilow1,Ihi1)-> 255 otherwise -> 0 
Igray2 ------------------------------------> Imaskt 

Imask | Imaskt --> Imask 

              (Ilow1,Ihi1)-> 255 otherwise -> 0 
Igray3 -------------------------------------> Imask 

Imask | Imaskt --> Imask 
255 - Imask  --> Imask 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章