Viola-Jones人臉檢測詳解

在人臉檢測中,Viola-Jones算法是一種非常經典的算法,該算法在2001年的CVPR上提出,因其高效快速的檢測而被廣泛使用。

這個算法用來檢測正面的人臉圖像,對於側臉圖像的檢測不是很穩健。

算法可以被分爲以下幾個部分:

  • 利用Haar特徵描述人臉特徵
  • 建立積分圖像,利用該圖像快速獲取幾種不同的矩形特徵
  • 利用Adaboost算法進行訓練
  • 建立層級分類器
  • 非極大值抑制

1 利用Haar特徵描述人臉特徵

人臉有一些特徵,一張正臉圖像中,人眼睛區域會比臉頰區域暗,嘴脣區域也會比四周的區域暗,但鼻子區域會比兩邊臉頰要亮。

基於這些特徵,VJ使用了四種矩形特徵,如下圖所示

這裏寫圖片描述

其中A,B爲邊界特徵,C爲細線特徵,D爲對角線特徵

那麼,Haar特徵是如何作用於正臉圖像的呢?

這裏寫圖片描述

如上圖所示,Haar特徵分別對白色區域和黑色區域的像素求和,然後求這兩種和的差;

這可以通過圖像卷積實現。

 

2 積分圖像

對於積分圖像中的任何一點,該點的積分圖像值等於位於該點左上角所有像素之和

表達式如下:

s(x,y)=\sum_{x'\leq x}\sum_{y'\leq y}f(x',y')

積分圖像滿足如下公式:

s(x,y)=f(x,y)+s(x-1,y)+s(x,y-1)-s(x-1,y-1)

積分圖像同時還滿足:

上圖爲一張原始圖像,其標示了四個區域:A, B , C ,D

1 處像素點對應的在積分圖像中的值爲:sum(A);

2 處像素點對應的在積分圖像中的值爲:sum(A+B);

3 處像素點對應的在積分圖像中的值爲:sum(A+C);

4 處像素點對應的在積分圖像中的值爲:sum(A+B+C+D);

則:

區域D所有的像素點灰度值之和爲:

sum(A+B+C+D) - sum(A+C) - sum(A+B) + sum(A)
 

3 獲取圖像特徵

VJ在論文中提到,24*24大小的圖像可以產生約160000個矩形特徵,那麼160000是怎麼得到的呢?

VJ使用的矩形特徵可以歸爲三類:

二鄰接矩形,橫豎兩種情況,如矩形特徵A,B,最少需要2個像素點表示

三鄰接矩形,如矩形特徵C,最少需要3個像素點表示,也有橫豎兩種情況

四鄰接矩形,如矩形特徵D,最少需要4個像素點表示,只有一種情況

對於24*24大小的圖像,每種鄰接矩形可能的大小爲:

二鄰接矩形(最小1*2):長度每次加2,寬度加1

1*2,1*4,1*6,...1*24

2*2,2*4,2*6,...,2*24

...

24*24

三鄰接矩形(最小1*3):長度加3,寬度加1

1*3,1*6,1*9,...1*24

2*3,2*6,2*9,...,2*24

...

24*24

四鄰接矩形(最小2*2):長度加2,寬度加2

2*2,2*4,2*6,...1*24

4*2,4*4,4*6,...4*24

...

24*24

根據圖像卷積,一個W*H的圖像與m*n的filter卷積,得到的圖像大小爲:(W-m+1)*(H-n+1)(默認stride爲1)

新圖像的每一個像素點的值就是原圖一個m*n的local patch 與m*n的filter的乘積和。

新圖像有多少個像素點,原圖就有多少個m*n的矩形。

這麼多矩形,可以通過編程算出,借用未雨綢繆的代碼。

這段代碼中,橫豎矩形窗口的數量是一樣的,代碼裏只計算一種,然後乘以2就行了。

import numpy as np

a = np.zeros((3, 2), dtype=int) 
Count = np.zeros(3, dtype=int) 
a[0, :] = [1, 2]
a[1, :] = [1, 3]
a[2, :] = [2, 2]
Img_size = 24

for ii in range(3):
    rec_h = a[ii, 0]
    rec_w = a[ii, 1]
    for xx in range(rec_h, Img_size+1, rec_h):
        for yy in range(rec_w, Img_size+1, rec_w):
            Count[ii] = Count[ii]+(Img_size-xx+1)*(Img_size-yy+1)
    print Count[ii]

Total = Count[0]*2+Count[1]*2+Count[2]
print ("Total: ", Total)

最後可以得到:

二鄰接矩形:43200

三鄰接矩形:27600

四鄰接矩形:20736

最終總的矩形特徵爲:43200×2+27600×2+20736=162336

所以一個24*24的圖像最終可以產生162336個矩形特徵。

並不是所有特徵都是有用的,那麼如何提取出有用的特徵呢?

AdaBoost特徵分類器具有特徵選擇的能力。

 

4 利用AdaBoost算法進行訓練

4.1 AdaBoost分類器

AdaBoost 將一系列的弱分類器通過線性組合,構成一個強分類器,如下所示:

h(x)是一個強分類器,h_j(x)是一個弱分類器,其爲一個簡單的閾值函數:

\theta _j爲閾值,s_j\in \left\{-1,1 \right\}\alpha _j爲係數。

4.2 訓練弱分類器

計算所有訓練樣本的特徵值,並將其從小到大排序,隨機選取一個特徵值作爲閾值,

把所有元素分爲兩部分,小於閾值的一部分分類爲人臉,大於閾值的一部分分類爲非人臉。

如下圖所示,紅色表示人臉,藍色表示非人臉。

假如有5個樣本,前兩個爲人臉,後三個爲非人臉,用11000表示。

如果閾值在第一個之前,通過弱分類器判定爲:00000,有兩個誤差,

如果閾值在第一個和第二個之間,通過弱分類器判定爲:10000,有1個誤差,

如果閾值在第二個和第三個之間,通過弱分類器判定爲:11000,有0個誤差,

依次類推,這樣共有6個誤差,然後從中找到一個誤差最小的當成閾值,

這樣就訓練好了一個最優的弱分類器。

4.3 訓練強分類器

假設有N個訓練樣本(x^i,y^i),其中有m個正樣本,l個負樣本,如果x^i是人臉圖像,

y^i=1, 否則y^i=-1

其步驟如下:

每一級分類器使用的訓練集中的負樣本,都是上一級被錯分的,即false positive,誤檢率或假陽性。

這使得下一級分類器更加關注那些更難的(容易被錯分的)樣本。

 

5 級聯分類器(cascade of classifiers)

在正常的圖像中,人臉區域只是佔了很小的一部分,如果使用所有的特徵進行訓練的話,運算量非常大。

級爲了簡化任務,把若干個adaboost 分類器級聯起來,一開始使用少量的特徵將大部分的非人臉區域剔除掉,後面再利用更復雜的特徵將更復雜的非人臉區域剔除掉。

這裏寫圖片描述

如果級聯分類器的識別率(true positive rate)爲D,誤識率(false positive rate)爲F, 

第 i 層的分類器的識別率爲d_i, 誤識率爲f_i,

則:

D=\prod_{i=1}^{K}d_i

F=\prod_{i=1}^{K}f_i

其中: K 爲分類器的個數

假如每一級的分類器,都具有非常高的檢測率(99.9%),

同時誤檢率也保持相當高(50%)。

那麼,如果級聯20個這樣的小adaboost分類器,

人臉的識別率有:

但是誤檢率有:

5.1 級聯分類器的訓練

論文中給出了一種很有效的方法

  • 設定每一層最大的可接受誤檢率 f, 和每一層最小的檢測率 d.
  • 設定級聯分類器的總體誤檢率F_{target}
  • 初始化總體誤檢率爲F_{target}=1,識別率D=1,循環計數器 i=0
  • 循環,如果當前 F 大於 F_{target} 時,繼續增加一層adaboost分類器
  • 在訓練每一層分類器時,如果目前該層的特徵沒有達到該層的 f_i 標準,繼續添加新的特徵。添加新特徵時,持續降低該特徵的閾值(一般而言,高閾值的分類器的檢測率和誤檢率都會比較低),直到該層分類器的檢測率d_i>d,然後更新D_i=d_i*D_{i-1}

在論文中,VJ分類器一共有38層,含有6060個特徵,前7層的特徵數爲:2->10->25->25->50->50->50

 

6 非極大值抑制(NMS)

在人臉識別中,一張臉會出現非常多的窗口,如下圖所示:

假設有N個窗口,根據分類器的分類概率從小到大排序,概率最大的框記爲Z

非極大值抑制的工作步驟如下:

  • 從最大概率矩形框Z開始,分別判斷其它框與X框的重疊度是否大於設定的閾值
  • 假設其中的B,C框超過了閾值,就扔掉B,C,並保留Z框
  • 從剩下的矩形框中,選擇概率最大的(假設爲Y),然後判斷其它框與X框的重疊度是否大於設定的閾值,大於扔掉,並保留框Y
  • 一直重複這個過程,直到最後一個框

論文中有些地方不夠詳細,可能比較符合微軟研究院的風格吧。

相關鏈接

論文傳送門:Viola-Jones人臉檢測

AdaBoost算法:集成算法-AdaBoost

 

本文參考了論文原文和網上的資料,是筆者自己對Viola-Jones算法的理解,可能會有些偏差,請讀者指正。

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