基於Haar特征的Adaboost級聯人臉檢測分類器


        基於Haar特征的Adaboost級聯人臉檢測分類器
基於Haar特征的Adaboost級聯人臉檢測分類器,簡稱haar分類器。通過這個算法的名字,我們可以看到這個算法其實包含了幾個關鍵點:Haar特征、Adaboost、級聯。理解了這三個詞對該算法基本就掌握了。
1        算法要點
Haar分類器 = Haar-like特征 + 積分圖方法 + AdaBoost +級聯;
Haar分類器算法的要點如下:
a)        使用Haar-like特征做檢測。
b)       使用積分圖(IntegralImage)對Haar-like特征求值進行加速。
c)        使用AdaBoost算法訓練區分人臉和非人臉的強分類器。
d)       使用篩選式級聯把分類器級聯到一起,提高准確率。
2        歷史
  在2001年,Viola和Jones兩位大牛發表了經典的《Rapid Object Detectionusing a Boosted Cascade of Simple Features》和《Robust Real-Time Face Detection》,在AdaBoost算法的基礎上,使用Haar-like小波特征和積分圖方法進行人臉檢測,他倆不是最早使用提出小波特征的,但是他們設計了針對人臉檢測更有效的特征,並對AdaBoost訓練出的強分類器進行級聯。這可以說是人臉檢測史上里程碑式的一筆了,也因此當時提出的這個算法被稱為Viola-Jones檢測器。又過了一段時間,RainerLienhart和Jochen
Maydt兩位大牛將這個檢測器進行了擴展,最終形成了OpenCV現在的Haar分類器。
AdaBoost是Freund和Schapire在1995年提出的算法,是對傳統Boosting算法的一大提升。Boosting算法的核心思想,是將弱學習方法提升成強學習算法,也就是“三個臭皮匠頂一個諸葛亮”
3        Haar特征
  什么是特征,特征就是分類器的輸入。把它放在下面的情景中來描述,假設在人臉檢測時我們需要有這么一個子窗口在待檢測的圖片窗口中不斷的移位滑動,子窗口每到一個位置,就會計算出該區域的特征,然后用我們訓練好的級聯分類器對該特征進行篩選,一旦該特征通過了所有強分類器的篩選,則判定該區域為人臉。
  1 clc;
  2 clear; 
  3 close all;
  4 
  5 % Haar-like特征矩形計算
  6 
  7 board = 24                                              % 檢測窗口寬度
  8 num = 24                                                % 檢測窗口分划數
  9 
 10 show = 1;                                               % 1為作圖
 11 time = 0.001;                                           % 作圖間隔
 12 
 13 %%
 14 
 15 if mod(board,num)~=0
 16     error('檢測窗口寬度必須是分划數的整數倍')
 17 else
 18     delta = board/num                                   % 滑動步進值 
 19 end
 20 
 21 % Haar特征1:左白,右黑,(s,t)=(1,2)
 22 
 23 s = 1;
 24 t = 2;
 25 R = s:s:floor(num/s)*s;                                 % Haar窗口高
 26 C = t:t:floor(num/t)*t;                                 % Haar窗口寬
 27 NUM = 0;                                                % Haar特征總數
 28 
 29 % '---- Haar特征1:左白,右黑,(s,t)=(1,2) ---'
 30 for I = 1:length(R)
 31     for J = 1:length(C)
 32        
 33         r = R(I)*delta;                                   % Haar窗口高
 34         c = C(J)*delta;                                  % Haar窗口寬
 35         nr = num-R(I)+1;                                 % 行方向移動個數
 36         nc = num-C(J)+1;                                 % 列方向移動個數
 37        
 38         Px0 = [0 r];                                     % 矩形坐標初始化
 39         Py0 = [0 c/2 c];
 40         for i = 1:nr
 41             for j = 1:nc
 42                 Px = Px0+(i-1)*delta;                    % 滑動取點
 43                 Py = Py0+(j-1)*delta;
 44                 NUM = NUM+1;
 45                
 46                 if show
 47                     plot([0 board],repmat((0:delta:board)',1,2),'k'); hold on;
 48                     plot(repmat((0:delta:board)',1,2),[0 board],'k'); axis tight; axis square;
 49                     title('Haar矩形遍歷演示');xlabel('x');ylabel('y');
 50                    
 51                     plot(Px,repmat(Py',1,2),'r','LineWidth',5)
 52                     plot(repmat(Px,2,1),repmat([Py(1) Py(end)]',1,2),'r','LineWidth',5); hold off
 53                     pause(time)
 54                 end
 55                
 56             end
 57         end
 58        
 59     end
 60 end
 61 NUM
 62 %% Haar特征2:上白,下黑,(s,t)=(2,1)
 63 s = 2;
 64 t = 1;
 65 R = s:s:floor(num/s)*s;                                 % Haar窗口高
 66 C = t:t:floor(num/t)*t;                                 % Haar窗口寬
 67 NUM = 0;                                                % Haar特征總數
 68 '---- Haar特征2:上白,下黑,(s,t)=(2,1) ---'
 69 for I = 1:length(R)
 70     for J = 1:length(C)
 71        
 72         r = R(I)*delta;                                  % Haar窗口高
 73         c = C(J)*delta;                                  % Haar窗口寬
 74         nr = num-R(I)+1;                                 % 行方向移動個數
 75         nc = num-C(J)+1;                                 % 列方向移動個數
 76        
 77         Px0 = [0 r/2 r];                                 % 矩形坐標初始化
 78         Py0 = [0 c];
 79         for i = 1:nr
 80             for j = 1:nc
 81                 Px = Px0+(i-1)*delta;                    % 滑動取點
 82                 Py = Py0+(j-1)*delta;
 83                 NUM = NUM+1;
 84                 if show
 85                     plot([0 board],repmat((0:delta:board)',1,2),'k'); hold on;
 86                     plot(repmat((0:delta:board)',1,2),[0 board],'k'); axis tight; axis square;
 87                     title('Haar矩形遍歷演示');xlabel('x');ylabel('y');
 88                    
 89                     plot(repmat(Px,2,1),repmat(Py',1,length(Px)),'r','LineWidth',3);
 90                     plot(repmat([Px(1) Px(end)]',1,2),repmat(Py,2,1),'r','LineWidth',3); hold off
 91                     pause(time)
 92                 end
 93                
 94             end
 95         end
 96        
 97     end
 98 end
 99 NUM
100 %% Haar特征3:左右白,中間黑,(s,t)=(1,3)
101 s = 1;
102 t = 3;
103 R = s:s:floor(num/s)*s;                                 % Haar窗口高
104 C = t:t:floor(num/t)*t;                                 % Haar窗口寬
105 NUM = 0;                                                % Haar特征總數
106 '---- Haar特征3:左右白,中間黑,(s,t)=(1,3) ---'
107 for I = 1:length(R)
108     for J = 1:length(C)
109        
110         r = R(I)*delta;                                  % Haar窗口高
111         c = C(J)*delta;                                  % Haar窗口寬
112         nr = num-R(I)+1;                                 % 行方向移動個數
113         nc = num-C(J)+1;                                 % 列方向移動個數
114        
115         Px0 = [0 r];                                     % 矩形坐標初始化
116         Py0 = [0 c/3 c*2/3 c];
117         for i = 1:nr
118             for j = 1:nc
119                 Px = Px0+(i-1)*delta;                    % 滑動取點
120                 Py = Py0+(j-1)*delta;
121                 NUM = NUM+1;
122                
123                 if show
124                     plot([0 board],repmat((0:delta:board)',1,2),'k'); hold on;
125                     plot(repmat((0:delta:board)',1,2),[0 board],'k'); axis tight; axis square;
126                     title('Haar矩形遍歷演示');xlabel('x');ylabel('y');
127                    
128                     plot(Px,repmat(Py',1,2),'r','LineWidth',5)
129                     plot(repmat(Px,2,1),repmat([Py(1) Py(end)]',1,2),'r','LineWidth',5); hold off
130                     pause(time)
131                 end
132             end
133         end
134        
135     end
136 end
137 NUM
138 %% Haar特征4:左右白,中間黑(2倍寬度),(s,t)=(1,4)
139 s = 1;
140 t = 4;
141 R = s:s:floor(num/s)*s;                                 % Haar窗口高
142 C = t:t:floor(num/t)*t;                                 % Haar窗口寬
143 NUM = 0;                                                % Haar特征總數
144 '---- Haar特征4:左右白,中間黑(2倍寬度),(s,t)=(1,4) ---'
145 for I = 1:length(R)
146     for J = 1:length(C)
147        
148         r = R(I)*delta;                                  % Haar窗口高
149         c = C(J)*delta;                                  % Haar窗口寬
150         nr = num-R(I)+1;                                 % 行方向移動個數
151         nc = num-C(J)+1;                                 % 列方向移動個數
152        
153         Px0 = [0 r];                                     % 矩形坐標初始化
154         Py0 = [0 c/4 c*3/4 c];
155         for i = 1:nr
156             for j = 1:nc
157                 Px = Px0+(i-1)*delta;                    % 滑動取點
158                 Py = Py0+(j-1)*delta;
159                 NUM = NUM+1;
160        
161                 if show
162                     plot([0 board],repmat((0:delta:board)',1,2),'k'); hold on;
163                     plot(repmat((0:delta:board)',1,2),[0 board],'k'); axis tight; axis square;
164                     title('Haar矩形遍歷演示');xlabel('x');ylabel('y');
165                    
166                     plot(Px,repmat(Py',1,2),'r','LineWidth',5)
167                     plot(repmat(Px,2,1),repmat([Py(1) Py(end)]',1,2),'r','LineWidth',5); hold off
168                     pause(time)
169                 end
170                
171             end
172         end
173        
174     end
175 end
176 NUM
177 %% Haar特征5:上下白,中間黑,(s,t)=(3,1)
178 s = 3;
179 t = 1;
180 R = s:s:floor(num/s)*s;                                 % Haar窗口高
181 C = t:t:floor(num/t)*t;                                 % Haar窗口寬
182 NUM = 0;                                                % Haar特征總數
183 '---- Haar特征5:上下白,中間黑,(s,t)=(3,1) ---'
184 for I = 1:length(R)
185     for J = 1:length(C)
186        
187         r = R(I)*delta;                                  % Haar窗口高
188         c = C(J)*delta;                                  % Haar窗口寬
189         nr = num-R(I)+1;                                 % 行方向移動個數
190         nc = num-C(J)+1;                                 % 列方向移動個數
191        
192         Px0 = [0 r/3 r*2/3 r];                           % 矩形坐標初始化
193         Py0 = [0 c];
194         for i = 1:nr
195             for j = 1:nc
196                 Px = Px0+(i-1)*delta;                    % 滑動取點
197                 Py = Py0+(j-1)*delta;
198                 NUM = NUM+1;
199                
200                 if show
201                     plot([0 board],repmat((0:delta:board)',1,2),'k'); hold on;
202                     plot(repmat((0:delta:board)',1,2),[0 board],'k'); axis tight; axis square;
203                     title('Haar矩形遍歷演示');xlabel('x');ylabel('y');
204                    
205                     plot(repmat(Px,2,1),repmat(Py',1,length(Px)),'r','LineWidth',3);
206                     plot(repmat([Px(1) Px(end)]',1,2),repmat(Py,2,1),'r','LineWidth',3); hold off
207                     pause(time)
208                 end
209                
210             end
211         end
212        
213     end
214 end
215 NUM
216 %% Haar特征6:上下白,中間黑(2倍寬度),(s,t)=(4,1)
217 s = 4;
218 t = 1;
219 R = s:s:floor(num/s)*s;                                 % Haar窗口高
220 C = t:t:floor(num/t)*t;                                 % Haar窗口寬
221 NUM = 0;                                                % Haar特征總數
222 '---- Haar特征6:上下白,中間黑(2倍寬度),(s,t)=(4,1) ---'
223 for I = 1:length(R)
224     for J = 1:length(C)
225        
226         r = R(I)*delta;                                  % Haar窗口高
227         c = C(J)*delta;                                 % Haar窗口寬
228         nr = num-R(I)+1;                                 % 行方向移動個數
229         nc = num-C(J)+1;                                 % 列方向移動個數
230        
231         Px0 = [0 r/4 r*3/4 r];                           % 矩形坐標初始化
232         Py0 = [0 c];
233         for i = 1:nr
234             for j = 1:nc
235                 Px = Px0+(i-1)*delta;                    % 滑動取點
236                 Py = Py0+(j-1)*delta;
237                 NUM = NUM+1;
238                 if show
239                     plot([0 board],repmat((0:delta:board)',1,2),'k'); hold on;
240                     plot(repmat((0:delta:board)',1,2),[0 board],'k'); axis tight; axis square;
241                     title('Haar矩形遍歷演示');xlabel('x');ylabel('y');
242                    
243                     plot(repmat(Px,2,1),repmat(Py',1,length(Px)),'r','LineWidth',3);
244                     plot(repmat([Px(1) Px(end)]',1,2),repmat(Py,2,1),'r','LineWidth',3); hold off
245                     pause(time)
246                 end
247                
248             end
249         end
250        
251     end
252 end
253 NUM
254 %% Haar特征7:左上右下白,其它黑,(s,s)=(2,2)
255 
256 s = 2;
257 t = 2;
258 R = s:s:floor(num/s)*s;                                 % Haar窗口高
259 C = t:t:floor(num/t)*t;                                 % Haar窗口寬
260 NUM = 0;                                                % Haar特征總數
261 '---- Haar特征7:左上右下白,其它黑,(s,s)=(2,2) ---'
262 for I = 1:length(R)
263     for J = 1:length(C)
264        
265         r = R(I)*delta;                                  % Haar窗口高
266         c = C(J)*delta;                                  % Haar窗口高
267         nr = num-R(I)+1;                                 % 行方向移動個數
268         nc = num-C(J)+1;                                 % 行方向移動個數
269        
270         Px0 = [0 r/2 r];                           % 矩形坐標初始化
271         Py0 = [0 c/2 c];                           % 矩形坐標初始化
272         for i = 1:nr
273             for j = 1:nc
274                 Px = Px0+(i-1)*delta;                    % 滑動取點
275                 Py = Py0+(j-1)*delta;
276                 NUM = NUM+1;
277                
278                 if show
279                     plot([0 board],repmat((0:delta:board)',1,2),'k'); hold on;
280                     plot(repmat((0:delta:board)',1,2),[0 board],'k'); axis tight; axis square;
281                     title('Haar矩形遍歷演示');xlabel('x');ylabel('y');
282                    
283                     plot(repmat(Px,3,1),repmat(Py',1,length(Px)),'r','LineWidth',3);
284                     plot(repmat([Px(1) Px(end)]',1,3),repmat(Py,2,1),'r','LineWidth',3); hold off
285                     pause(time)
286                 end
287                
288             end
289         end
290        
291     end
292 end
293 NUM
294 %% Haar特征8:四周白,中間黑,(s,s)=(3,3)
295 s = 3;
296 t = 3;
297 R = s:s:floor(num/s)*s;                                 % Haar窗口高
298 C = t:t:floor(num/t)*t;                                 % Haar窗口寬
299 NUM = 0;                                                % Haar特征總數
300 '---- Haar特征8:四周白,中間黑,(s,s)=(3,3) ---'
301 for I = 1:length(R)
302     for J = 1:length(C)
303        
304         r = R(I)*delta;                                  % Haar窗口高
305         c = C(J)*delta;                                  % Haar窗口高
306         nr = num-R(I)+1;                                 % 行方向移動個數
307         nc = num-C(J)+1;                                 % 行方向移動個數
308        
309         Px0 = [0 r/3 r*2/3 r];                           % 矩形坐標初始化
310         Py0 = [0 c/3 c*2/3 c];                           % 矩形坐標初始化
311         for i = 1:nr
312             for j = 1:nc
313                 Px = Px0+(i-1)*delta;                    % 滑動取點
314                 Py = Py0+(j-1)*delta;
315                 NUM = NUM+1;
316                
317                 if show
318                     plot([0 board],repmat((0:delta:board)',1,2),'k'); hold on;
319                     plot(repmat((0:delta:board)',1,2),[0 board],'k'); axis tight; axis square;
320                     title('Haar矩形遍歷演示');xlabel('x');ylabel('y');
321                    
322                     plot(repmat(Px,4,1),repmat(Py',1,length(Px)),'r','LineWidth',3);
323                     plot(repmat([Px(1) Px(end)]',1,4),repmat(Py,2,1),'r','LineWidth',3); hold off
324                     pause(time)
325                 end
326                
327             end
328         end
329        
330     end
331 end
332 NUM
View Code

運行效果部分動態圖如下

 


  那么這個特征如何表示呢?好了,這就是大牛們干的好事了。后人稱這他們搞出來的這些東西叫Haar-Like特征。
Viola大牛在[1]中提出的haar特征如下:

 


Rainer大牛改進了這些特征,提出了更多的haar特征。如下圖所示:

 


  這些所謂的特征不就是一堆堆帶條紋的矩形么,到底是干什么用的?我這樣給出解釋,將上面的任意一個矩形放到人臉區域上,然后,將白色區域的像素和減去黑色區域的像素和,得到的值我們暫且稱之為人臉特征值,如果你把這個矩形放到一個非人臉區域,那么計算出的特征值應該和人臉特征值是不一樣的,而且越不一樣越好,所以這些方塊的目的就是把人臉特征量化,以區分人臉和非人臉。
haar-like特征的特點
  Haar特征值反映了圖像的灰度變化情況。例如:臉部的一些特征能由矩形特征簡單的描述,如:眼睛要比臉頰顏色要深,鼻梁兩側比鼻梁顏色要深,嘴巴比周圍顏色要深等。但矩形特征只對一些簡單的圖形結構,如邊緣、線段較敏感,所以只能描述特定走向(水平、垂直、對角)的結構。
    通過改變特征模板的大小和位置,可在圖像子窗口中窮舉出大量的特征。上圖的特征模板稱為“特征原型”;特征原型在圖像子窗口中擴展(平移伸縮)得到的特征稱為“矩形特征”;矩形特征的值稱為“特征值”。
4       積分圖

   積分圖就是只遍歷一次圖像就可以求出圖像中所有區域像素和的快速算法,大大的提高了圖像特征值計算的效率。

       積分圖主要的思想是將圖像從起點開始到各個點所形成的矩形區域像素之和作為一個數組的元素保存在內存中,當要計算某個區域的像素和時可以直接索引數組的元素,不用重新計算這個區域的像素和,從而加快了計算(這有個相應的稱呼,叫做動態規划算法)。積分圖能夠在多種尺度下,使用相同的時間(常數時間)來計算不同的特征,因此大大提高了檢測速度。

       我們來看看它是怎么做到的。

       積分圖是一種能夠描述全局信息的矩陣表示方法。積分圖的構造方式是位置(i,j)處的值ii(i,j)是原圖像(i,j)左上角方向所有像素的和:

  

        

 

積分圖構建算法:

1)用s(i,j)表示行方向的累加和,初始化s(i,-1)=0;

2)用ii(i,j)表示一個積分圖像,初始化ii(-1,i)=0;

3)逐行掃描圖像,遞歸計算每個像素(i,j)行方向的累加和s(i,j)和積分圖像ii(i,j)的值

s(i,j)=s(i,j-1)+f(i,j)

ii(i,j)=ii(i-1,j)+s(i,j)

4)掃描圖像一遍,當到達圖像右下角像素時,積分圖像ii就構造好了。

積分圖構造好之后,圖像中任何矩陣區域的像素累加和都可以通過簡單運算得到如圖所示。

          

 

設D的四個頂點分別為α、β、γ、δ,則D的像素和可以表示為

Dsum = ii( α )+ii( β)-(ii( γ)+ii( δ ));

        而Haar-like特征值無非就是兩個矩陣像素和的差,同樣可以在常數時間內完成。所以矩形特征的特征值計算,只與此特征矩形的端點的積分圖有關,所以不管此特征矩形的尺度變換如何,特征值的計算所消耗的時間都是常量。這樣只要遍歷圖像一次,就可以求得所有子窗口的特征值。



5       Adaboost算法
本節旨在介紹AdaBoost在Haar分類器中的應用,所以只是描述了它在Haar分類器中的特性,而實際上AdaBoost是一種具有一般性的分類器提升算法,它使用的分類器並不局限某一特定算法。
[1]中給出的Adaboost算法流程如下圖。
 

 

由adaboost在haar特征上構建分類器的流程可知,adaboost算法就是構建多個簡單的分類器,每個簡單的分類器都建立在之前分類器的基礎上(對之前分類器分錯了的樣例提高其權重),然后將這些分類器加權,得到一個強大的分類器。
Adaboost的每一步訓練出的分類器,如下圖所示。其中,f表示特征的值,theta表示閾值,p則表示不等式的方向。這樣的一個分類器就是基於一個特征的弱分類器。

 

 


更進一步,adaboost的一般算法框架如下。可以看到,Discrete Adaboost和GentleAdaboost在分類器的計算上和權重的更新上是有差別的。還有一種是RealAdaboost,即分類器輸出的是一個概率而不只是+1與-1。[3]中就比較了這三種Adaboost的變種的效果。
 

 有關adaboost算法的實現可以參考這篇博客:https://blog.csdn.net/u012679707/article/details/80369772


6        級聯
什么是級聯分類器?級聯分類器就是如下圖所示的一種退化了的決策樹。為什么說是退化了的決策樹呢?是因為一般決策樹中,判斷后的兩個分支都會有新的分支出現,而級聯分類器中,圖像被拒絕后就直接被拋棄,不會再有判斷了。

 


級聯強分類器的策略是,將若干個強分類器由簡單到復雜排列,希望經過訓練使每個強分類器都有較高檢測率,而誤識率可以放低,比如幾乎99%的人臉可以通過,但50%的非人臉也可以通過,這樣如果有20個強分類器級聯,那么他們的總識別率為0.99^20約等於98%,錯誤接受率也僅為0.5^20約等於0.0001%。這樣的效果就可以滿足現實的需要了。文獻[1]中給出了一種由簡單到復雜設計級聯分類器的方法,那就是添加特征法,對於第一個分類器,只用少數幾個特征,之后的每個分類器都在上一個的基礎上添加特征,直到滿足該級的要求。
訓練級聯分類器的目的就是為了檢測的時候,更加准確,這涉及到Haar分類器的另一個體系,檢測體系,檢測體系是以現實中的一幅大圖片作為輸入,然后對圖片中進行多區域,多尺度的檢測,所謂多區域,是要對圖片划分多塊,對每個塊進行檢測,由於訓練的時候用的照片一般都是20*20左右的小圖片,所以對於大的人臉,還需要進行多尺度的檢測,多尺度檢測機制一般有兩種策略,一種是不改變搜索窗口的大小,而不斷縮放圖片,這種方法顯然需要對每個縮放后的圖片進行區域特征值的運算,效率不高,而另一種方法,是不斷初始化搜索窗口size為訓練時的圖片大小,不斷擴大搜索窗口,進行搜索,解決了第一種方法的弱勢。

7        總結
基於Haar特征的Adaboost級聯分類器,在人臉的識別效果上並沒有比其他算法高,其亮點在於檢測速度。而速度的提升,有如下幾方面的因素。第一:使用的特征簡單,haar特征只需計算像素的和就可以了。第二:即便是如此簡單的特征,還添加了積分圖進行加速。第三,級聯分類器的設定,使得大量的沒有人臉的子窗口被拋棄
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM