在人臉檢測中,Viola-Jones算法是一種非常經典的算法,該算法在2001年的CVPR上提出,因其高效快速的檢測而被廣泛使用。
這個算法用來檢測正面的人臉圖像,對於側臉圖像的檢測不是很穩健。
算法可以被分爲以下幾個部分:
- 利用Haar特徵描述人臉特徵
- 建立積分圖像,利用該圖像快速獲取幾種不同的矩形特徵
- 利用Adaboost算法進行訓練
- 建立層級分類器
- 非極大值抑制
1 利用Haar特徵描述人臉特徵
人臉有一些特徵,一張正臉圖像中,人眼睛區域會比臉頰區域暗,嘴脣區域也會比四周的區域暗,但鼻子區域會比兩邊臉頰要亮。
基於這些特徵,VJ使用了四種矩形特徵,如下圖所示
其中A,B爲邊界特徵,C爲細線特徵,D爲對角線特徵
那麼,Haar特徵是如何作用於正臉圖像的呢?
如上圖所示,Haar特徵分別對白色區域和黑色區域的像素求和,然後求這兩種和的差;
這可以通過圖像卷積實現。
2 積分圖像
對於積分圖像中的任何一點,該點的積分圖像值等於位於該點左上角所有像素之和
表達式如下:
積分圖像滿足如下公式:
積分圖像同時還滿足:
上圖爲一張原始圖像,其標示了四個區域: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 將一系列的弱分類器通過線性組合,構成一個強分類器,如下所示:
是一個強分類器,是一個弱分類器,其爲一個簡單的閾值函數:
爲閾值,,爲係數。
4.2 訓練弱分類器
計算所有訓練樣本的特徵值,並將其從小到大排序,隨機選取一個特徵值作爲閾值,
把所有元素分爲兩部分,小於閾值的一部分分類爲人臉,大於閾值的一部分分類爲非人臉。
如下圖所示,紅色表示人臉,藍色表示非人臉。
假如有5個樣本,前兩個爲人臉,後三個爲非人臉,用11000表示。
如果閾值在第一個之前,通過弱分類器判定爲:00000,有兩個誤差,
如果閾值在第一個和第二個之間,通過弱分類器判定爲:10000,有1個誤差,
如果閾值在第二個和第三個之間,通過弱分類器判定爲:11000,有0個誤差,
依次類推,這樣共有6個誤差,然後從中找到一個誤差最小的當成閾值,
這樣就訓練好了一個最優的弱分類器。
4.3 訓練強分類器
假設有N個訓練樣本,其中有個正樣本,個負樣本,如果是人臉圖像,
則, 否則
其步驟如下:
每一級分類器使用的訓練集中的負樣本,都是上一級被錯分的,即false positive,誤檢率或假陽性。
這使得下一級分類器更加關注那些更難的(容易被錯分的)樣本。
5 級聯分類器(cascade of classifiers)
在正常的圖像中,人臉區域只是佔了很小的一部分,如果使用所有的特徵進行訓練的話,運算量非常大。
級爲了簡化任務,把若干個adaboost 分類器級聯起來,一開始使用少量的特徵將大部分的非人臉區域剔除掉,後面再利用更復雜的特徵將更復雜的非人臉區域剔除掉。
如果級聯分類器的識別率(true positive rate)爲D,誤識率(false positive rate)爲F,
第 層的分類器的識別率爲, 誤識率爲,
則:
其中: K 爲分類器的個數
假如每一級的分類器,都具有非常高的檢測率(99.9%),
同時誤檢率也保持相當高(50%)。
那麼,如果級聯20個這樣的小adaboost分類器,
人臉的識別率有:
但是誤檢率有:
5.1 級聯分類器的訓練
論文中給出了一種很有效的方法
- 設定每一層最大的可接受誤檢率 f, 和每一層最小的檢測率 d.
- 設定級聯分類器的總體誤檢率
- 初始化總體誤檢率爲,識別率,循環計數器 i=0
- 循環,如果當前 F 大於 時,繼續增加一層adaboost分類器
- 在訓練每一層分類器時,如果目前該層的特徵沒有達到該層的 標準,繼續添加新的特徵。添加新特徵時,持續降低該特徵的閾值(一般而言,高閾值的分類器的檢測率和誤檢率都會比較低),直到該層分類器的檢測率,然後更新
在論文中,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算法的理解,可能會有些偏差,請讀者指正。