第九節、人臉檢測之Haar分類器


人臉檢測屬於計算機視覺的范疇,早期人們的主要研究方向是人臉識別,即根據人臉來識別人物的身份,后來在復雜背景下的人臉檢測需求越來越大,人臉檢測也逐漸作為一個單獨的研究方向發展起來。

目前人臉檢測的方法主要有兩大類:基於知識和基於統計。

基於知識的方法:主要利用先驗知識將人臉看作器官特征的組合,根據眼睛、眉毛、嘴巴、鼻子等器官的特征以及相互之間的幾何位置關系來檢測人臉。主要包括模板匹配、人臉特征、形狀與邊緣、紋理特性、顏色特征等方法。

基於統計的方法:將人臉看作一個整體的模式——二維像素矩陣,從統計的觀點通過大量人臉圖像樣本構造人臉模式空間,根據相似度量來判斷人臉是否存在。主要包括主成分分析與特征臉、神經網絡方法、支持向量機、隱馬爾可夫模型、Adaboost算法等。

本文中介紹的Haar分類器方法,包含了Adaboost算法,稍候會對這一算法做詳細介紹。所謂分類器,在這里就是指對人臉和非人臉進行分類的算法,在機器學習領域,很多算法都是對事物進行分類、聚類的過程

我們要探討的Haar分類器實際上是Boosting算法的一個應用,Haar分類器用到了Boosting算法中的AdaBoost算法,只是把AdaBoost算法訓練出的強分類器進行了級聯,並且在底層的特征提取中采用了高效率的矩形特征和積分圖方法,這里涉及到的幾個名詞接下來會具體討論。

2001年,Viola和Jones兩位大牛發表了經典的《Rapid Object Detection using a Boosted Cascade of Simple Features》和《Robust Real-Time Face Detection》,在AdaBoost算法的基礎上,使用Haar-like小波特征和積分圖方法進行人臉檢測,他倆不是最早使用提出小波特征的,但是他們設計了針對人臉檢測更有效的特征,並對AdaBoost訓練出的強分類器進行級聯。這可以說是人臉檢測史上里程碑式的一筆了,也因此當時提出的這個算法被稱為Viola-Jones檢測器。又過了一段時間,Rainer Lienhart和Jochen Maydt兩位大牛將這個檢測器進行了擴展,最終形成了OpenCV現在的Haar分類器。

AdaBoost是Freund和Schapire在1995年提出的算法,是對傳統Boosting算法的一大提升。Boosting算法的核心思想,是將弱學習方法提升成強學習算法,也就是“三個臭皮匠頂一個諸葛亮”。

Haar分類器 = Haar-like特征 + 積分圖方法 + AdaBoost +級聯;

Haar分類器算法的要點如下:

  1. 使用Haar-like特征做檢測。
  2. 使用積分圖(Integral Image)對Haar-like特征求值進行加速。
  3. 使用AdaBoost算法訓練區分人臉和非人臉的強分類器。
  4. 使用篩選式級聯把強分類器級聯到一起,提高准確率。

一 Haar-like特征

Haar(哈爾)特征分為三類:邊緣特征、線性特征、中心特征和對角線特征,組合成特征模板。特征模板內有白色和黑色兩種矩形,並定義該模板的特征值為白色矩形像素和減去黑色矩形像素和Haar特征值反映了圖像的灰度變化情況。例如:臉部的一些特征能由矩形特征簡單的描述,如:眼睛要比臉頰顏色要深,鼻梁兩側比鼻梁顏色要深,嘴巴比周圍顏色要深等。但矩形特征只對一些簡單的圖形結構,如邊緣、線段較敏感,所以只能描述特定走向(水平、垂直、對角)的結構。

對於圖中的A, B和D這類特征,特征數值計算公式為:v=Σ白-Σ黑,而對於C來說,計算公式如下:v=Σ白-2*Σ黑;之所以將黑色區域像素和乘以2,是為了使兩種矩形區域中像素數目一致。我們希望當把矩形放到人臉區域計算出來的特征值和放到非人臉區域計算出來的特征值差別越大越好,這樣就可以用來區分人臉和非人臉。

通過改變特征模板的大小和位置,可在圖像子窗口中窮舉出大量的特征。上圖的特征模板稱為“特征原型”;特征原型在圖像子窗口中擴展(平移伸縮)得到的特征稱為“矩形特征”;矩形特征的值稱為“特征值”。

上圖中兩個矩形特征,表示出人臉的某些特征。比如中間一幅表示眼睛區域的顏色比臉頰區域的顏色深,右邊一幅表示鼻梁兩側比鼻梁的顏色要深。同樣,其他目標,如眼睛等,也可以用一些矩形特征來表示。使用特征比單純地使用像素點具有很大的優越性,並且速度更快。

矩形特征可位於圖像任意位置,大小也可以任意改變,所以矩形特征值是矩形模版類別、矩形位置和矩形大小這三個因素的函數。故類別、大小和位置的變化,使得很小的檢測窗口含有非常多的矩形特征,如:在24*24像素大小的檢測窗口內矩形特征數量可以達到16萬個。這樣就有兩個問題需要解決了:(1)如何快速計算那么多的特征?---積分圖大顯神通;(2)哪些矩形特征才是對分類器分類最有效的?---如通過AdaBoost算法來訓練。

二、Haar-like特征的計算—積分圖

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

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

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

積分圖構建算法:

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的像素和可以表示位$$D_{sum}=ii(α)+ii(β)-(ii(γ)+ii(δ))$$

而Haar-like特征值無非就是兩個矩陣像素和的差,同樣可以在常數時間內完成。

 三 計算Haar特征值

上面已經知道,一個區域的像素值的和,可以由該區域的端點的積分圖來計算。由前面特征模板的特征值的定義可以推出,矩形特征的特征值可以由特征端點的積分圖計算出來。以A矩形特征為例,如下圖,使用積分圖計算其特征值:

 

該矩形特征的特征值,由定義,為區域A的像素值減去區域B的像素值。

區域A的像素值:$$ii(5)+ii(1)-ii(2)-ii(4)$$

區域B的像素值:$$ii(6)+ii(2)-ii(5)-ii(3)$$

所以:該矩形特征的特征值$$ii(5)+ii(1)-ii(2)-ii(4)-[ii(6)+ii(2)-ii(5)-ii(3)]$$$$=[ii(5)-ii(4)]+[ii(3)-ii(2)]-[ii(2)-ii(1)]-[ii(6)-ii(5)]$$

所以,矩形特征的特征值,只與特征矩形的端點的積分圖有關,而與圖像的坐標無關。通過計算特征矩形的端點的積分圖,再進行簡單的加減運算,就可以得到特征值,正因為如此,特征的計算速度大大提高,也提高了目標的檢測速度。

了解了特征值的計算之后,我們來看看不同的特征值的含義是什么。我們選取MIT人臉庫中2706個大小為20*20的人臉正樣本圖像,計算如下圖所示的Haar特征:

左邊對應的人眼區域,右邊無具體意義。

可以看到,圖中2個不同Haar特征在同一組樣本中具有不同的特征值分布,左邊特征計算出的特征值基本都大於0(對樣本的區分度大),而右邊特征的特征值基本均勻分布於0兩側(對樣本的區分度小)。所以,正是由於樣本中Haar特征值分布不均勻,導致了不同Haar特征分類效果不同。顯而易見,對正負樣本區分度越大的特征分類效果越好,即紅色曲線對應圖中的的左邊Haar特征分類效果好於右邊Haar特征。

那么看到這里,應該理解了下面2個問題:

(1)在檢測窗口通過平移+縮放可以產生一系列Haar特征,這些特征由於位置和大小不同,分類效果也不同;

(2)通過計算Haar特征的特征值,可以有將圖像矩陣映射為1維特征值,有效實現了降維。

 四 Haar特征值歸一化(也可以采用標准歸一化)

從上圖我們可以發現,僅僅一個12*8大小的Haar特征計算出的特征值變化范圍從-2000~+6000,跨度非常大。這種跨度大的特性不利於量化評定特征值,所以需要進行“歸一化”,壓縮特征值范圍。假設當前檢測窗口中的圖像像素為$i(x,y)$,當前檢測窗口為$w*h$大小(例如上圖中為20*20大小),OpenCV采用如下方式“歸一化”:

1、計算檢測窗口中圖像的灰度值和灰度值平方和:

$$sum=\sum i(x,y)$$

$$sq_{sum}=\sum i^2(x,y)$$

 2、計算平均值:

$$mean = \frac{sum}{w*h}$$

$$sq_{mean}=\frac{sq_{sum}}{w*h}$$

3、計算歸一化因子:

$$varNormFactor=\sqrt{sq_{mean}-mean^2}$$

4、歸一化特征值:

$$normValue=\frac{featureValue}{varNormFactor}$$

之后使用歸一化的特征值$normValue$與閾值對比。

 五 Adaboost級聯分類器

前面幾塊內容我們分析了Haar特征,積分圖、特征值計算。這里則主要分析一下2個內容:

(1)OpenCV中的Adaboost級聯分類器的結構,包括強分類器和弱分類器的形式;

(2)OpenCV自帶的XML分類器中各項參數,如internalNodes和leafValues標簽里面的一大堆數字的意義。

1、級聯分類器

集成學習值Adaboost算法原理和代碼小結(轉載)小節中我們已經介紹過了Adboost分類器,這里我們會介紹一下Adaboost級聯分類器。級聯分類模型是樹狀結構可以用下圖表示:

其中每一個stage都代表一級強分類器。當檢測窗口通過所有的強分類器時才被認為是正樣本,否則拒絕。實際上,不僅強分類器是樹狀結構,強分類器中的每一個弱分類器也是樹狀結構。由於每一個強分類器對負樣本的判別准確度非常高,所以一旦發現檢測到的目標位負樣本,就不在繼續調用下面的強分類器,減少了很多的檢測時間。因為一幅圖像中待檢測的區域很多都是負樣本,這樣由級聯分類器在分類器的初期就拋棄了很多負樣本的復雜檢測,所以級聯分類器的速度是非常快的;只有正樣本才會送到下一個強分類器進行再次檢驗,這樣就保證了最后輸出的正樣本的偽正(false positive)的可能性非常低。

2、級聯分類器的訓練

級聯分類器是如何訓練的呢?首先需要訓練出每一個弱分類器,然后把每個弱分類器按照一定的組合策略,得到一個強分類器,我們訓練出多個強分類器,然后按照級聯的方式把它們組合在一塊,就會得到我們最終想要的Haar分類器。

一個弱分類器就是一個基本和上圖類似的決策樹,最基本的弱分類器只包含一個Haar-like特征,也就是它的決策樹只有一層,被稱為樹樁(stump)。

以20*20圖像為例,78,460個特征,如果直接利用AdaBoost訓練,那么工作量是極其極其巨大的。

所以必須有個篩選的過程,篩選出T個優秀的特征值(即最優弱分類器),然后把這個T個最優弱分類器傳給AdaBoost進行訓練。

現在有人臉樣本2000張,非人臉樣本4000張,這些樣本都經過了歸一化,大小都是20x20的圖像。那么,對於78,460中的任一特征fi,我們計算該特征在這2000人臉樣本、4000非人臉樣本上的值,這樣就得到6000個特征值。將這些特征值排序,然后選取一個最佳的特征值,在該特征值下,對於特征$f_i$來說,樣本的加權錯誤率最低。

在確定了訓練子窗口中(20x20的圖像)的矩形特征數量(78,460)和特征值后,需要對每一個特征$f$,訓練一個弱分類器$h(x,f,ρ,Θ)$。$$h(x,f,ρ,Θ)=\begin{cases}1,   {ρf(x)<ρΘ}\\0,  {other}\end{cases}$$

其中$f$為特征,$Θ$為閾值,$ρ$指示不等號的方向,$x$代表一個檢測子窗口。對每個特征$f$,訓練一個弱分類器$h(x,f,ρ,Θ)$,就是確定$f$的最優閾值,使得這個弱分類器對所有的訓練樣本分類誤差最低。

在弱分類器訓練的過程中,訓練采用的照片一般都是20*20左右的小圖片,弱分類器訓練的具體步驟

1、對於每個特征 $f$,計算所有訓練樣本的特征值,並將其排序:

2、掃描一遍排好序的特征值,對排好序的表中的每個元素,計算下面四個值:

計算全部正例的權重和$T^+$;

計算全部負例的權重和$T^-$;

計算該元素前之前的正例的權重和$S^+$;

計算該元素前之前的負例的權重和$S^-$;

3、選取當前元素的特征值$F_{k_j}$和它前面的一個特征值$F_{k_{j-1}}$之間的數作為閾值,所得到的弱分類器就在當前元素處把樣本分開 —— 也就是說這個閾值對應的弱分類器將當前元素前的所有元素分為人臉(或非人臉),而把當前元素后(含)的所有元素分為非人臉(或人臉)。該閾值的分類誤差為:

$$e=min(S^++(T^--S^-),S^-+(T^+-S^+))$$

於是,通過把這個排序表從頭到尾掃描一遍就可以為弱分類器選擇使分類誤差最小的閾值(最優閾值),也就是選取了一個最佳弱分類器。

這里寫圖片描述

 

由於一共有78,460個特征、因此會得到78,460個最優弱分類器,在78,460個特征中,我們選取錯誤率最低的特征,用來判斷人臉,同時用此分類器對樣本進行分類,並更新樣本的權重。

強分類器的訓練步驟:

1.、給定訓練樣本集($x_i$,$y_i$),$i=1,2,3,...N$,共N個樣本,$y_i$取值為0(負樣本)或者1(正樣本);設人臉正樣本的數量為$n_1$,負樣本數量為$n_2$; T為訓練的最大循環次數;

2.、初始化樣本權重為$\frac{1}{n_1+n_2}$,即為訓練樣本的初始概率分布;

3、$for  t=1,...T$:
①權重歸一化$$ω_{t,i}=\frac{ω_{t,i}}{\sum\limits_{j-1}^{n}ω_{t,j}}$$

②對每個(種)特征$f_j$,訓練一個弱分類器$h_j$(如上),每個分類器只使用一種Haar特征進行訓練。分類誤差為:$$ε_j=\sum\limits_{i}ω_i|h_j(x_i)-y_i|$$
③從②確定的弱分類器中,找出一個具有最小分類誤差的弱分類器$h_t$;
④更新每個樣本對應的權重:

這里,如果樣本$x_i$被正確分類,則$e_i=0$,否則$e_i=1$,而$$\beta_t=\frac{ε_t}{1-ε_t}$$

4、最終形成的強分類器組成為:

其中:$$\alpha_t=log\frac{1}{\beta_t}$$
在使用Adaboost算法訓練分類器之前,需要准備好正、負樣本,根據樣本特點選擇和構造特征集。由算法的訓練過程可知,當弱分類器對樣本分類正確,樣本的權重會減小;而分類錯誤時,樣本的權重會增加。這樣,后面的分類器會加強對錯分樣本的訓練。最后,組合所有的弱分類器形成強分類器,通過比較這些弱分類器投票的加權和與平均投票結果來檢測圖像。

 3、級聯分類器的檢測

訓練級聯分類器的目的就是為了檢測的時候,更加准確,這涉及到Haar分類器的另一個體系,檢測體系,檢測體系是以現實中的一幅大圖片作為輸入,然后對圖片中進行多區域,多尺度的檢測,所謂多區域,是要對圖片划分多塊,對每個塊進行檢測,由於訓練的時候用的照片一般都是20*20左右的小圖片,所以對於大的人臉,還需要進行多尺度的檢測,多尺度檢測機制一般有兩種策略:

  • 一種是不改變搜索窗口的大小,而不斷縮放圖片,這種方法顯然需要對每個縮放后的圖片進行區域特征值的運算,效率不高;
  • 另一種方法,不斷擴大搜索窗口,進行搜索,解決了第一種方法的弱勢。

在區域放大的過程中會出現同一個人臉被多次檢測,這需要進行區域的合並,這里不作探討。

無論哪一種搜索方法,都會為輸入圖片輸出大量的子窗口圖像,這些子窗口圖像經過篩選式級聯分類器會不斷地被每一個節點篩選,拋棄或通過。

 4、總結

從上面所述內容我們可以總結Haar分類器訓練的五大步驟:

1、准備人臉、非人臉樣本集;

2、計算特征值和積分圖;

3、篩選出T個優秀的特征值(即最優弱分類器);

4、把這個T個最優弱分類器傳給AdaBoost進行訓練。

5、級聯,也就是強分類器的強強聯手。

在開始前,一定要記住,以20*20窗口為例,就有78,460的特征數量,篩選出T個優秀的特征值(即最優弱分類器),然后把這個T個最優弱分類器傳給AdaBoost進行訓練得到一個強分類器,最后將強分類器進行級聯。

5、XML文件

OpenCV 自帶了訓練器和檢測器。如果你想自己訓練一個分類器來檢測汽車,飛機等的話,可以使用 OpenCV 構建。其中的細節參考這里:Cascade Classifier Training。這里我們介紹的XML文件,就是OpenCV自帶的檢測器,在OpenCV 3的庫文件中會包含一個文件夾haarcascades,在我的電腦上路徑為D:\Anaconda\pkgs\opencv-3.3.1-py36h20b85fd_1\Library\etc\haarcascades。在這個文件夾下包含了OpenCV的人臉檢測的XML文件,這些文件可用於檢測靜止圖像,視頻和攝像頭所得到圖像中的人臉。除此之外還有一個文件夾是lbpcascades,它不是通過Haar特征進行人臉檢測,而是采用的LBP特征。

從這些文件名可以知道這些級聯適用於檢測人臉、眼睛、鼻子和嘴等部位的跟蹤,這些文件需要正面、直立的人體圖像。

 xml文件主要保存相關的特征矩陣,以及各個弱分類器相關的信息,關於各個節點的具體含義可以參考文章haar+adaboost結合講解(偏重實際),這里不做過多的介紹。

六 人臉檢測

在這里我們將會學習到使用級聯分類器進行人臉檢測。在靜態圖像或視頻中檢測人臉的操作非常相似。視頻人臉檢測知識從攝像頭讀出毎幀圖像,然后采用靜態圖像中的人臉檢測方法進行檢測,當然,視頻人臉檢測還涉及其他概念,例如跟蹤,而靜態圖像中人臉檢測就沒有這樣的概念,但是它們的基本理論是一致的。

1、靜態圖像中的人臉檢測

我們首先把haarcascades文件夾復制到當前項目路徑下,然后創建.py文件,代碼如下:

# -*- coding: utf-8 -*-
"""
Created on Thu Aug 16 10:32:55 2018

@author: lenovo
"""

'''
人臉檢測
'''
import cv2
import numpy as np


#1.靜態圖像中的人臉檢測
def StaticDetect(filename):
    #創建一個級聯分類器 加載一個 .xml 分類器文件. 它既可以是Haar特征也可以是LBP特征的分類器.
    face_cascade = cv2.CascadeClassifier('./haarcascades/haarcascade_frontalface_default.xml')
    
    #加載圖像
    img = cv2.imread(filename)
    #轉換為灰度圖
    gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    #進行人臉檢測,傳入scaleFactor,minNegihbors,分別表示人臉檢測過程中每次迭代時圖像的壓縮率以及
    #每個人臉矩形保留近似數目的最小值
    #返回人臉矩形數組
    faces = face_cascade.detectMultiScale(gray_img,1.3,5)
    for (x,y,w,h) in faces:
        #在原圖像上繪制矩形
        img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
    cv2.namedWindow('Face Detected!')
    cv2.imshow('Face Detected!',img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

#2、視頻中的人臉檢測
def DynamicDetect():
    '''
    打開攝像頭,讀取幀,檢測幀中的人臉,掃描檢測到的人臉中的眼睛,對人臉繪制藍色的矩形框,對人眼繪制綠色的矩形框
    '''
    #創建一個級聯分類器 加載一個 .xml 分類器文件. 它既可以是Haar特征也可以是LBP特征的分類器.
    face_cascade = cv2.CascadeClassifier('./haarcascades/haarcascade_frontalface_default.xml')
    eye_cascade = cv2.CascadeClassifier('./haarcascades/haarcascade_eye.xml')
    
    #打開攝像頭    
    camera = cv2.VideoCapture(0)
    cv2.namedWindow('Dynamic')
    
    while(True):
        #讀取一幀圖像
        ret,frame = camera.read()
        #判斷圖片讀取成功?
        if ret:
            gray_img = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
            #人臉檢測
            faces = face_cascade.detectMultiScale(gray_img,1.3,5)
            for (x,y,w,h) in faces:
                #在原圖像上繪制矩形
                cv2.rectangle(frame,(x,y),(x+w,y+h),(255,0,0),2)
                roi_gray = gray_img[y:y+h,x:x+w]
                #眼睛檢測
                eyes = eye_cascade.detectMultiScale(roi_gray,1.03,5,0,(40,40))
                for (ex,ey,ew,eh) in eyes:
                    cv2.rectangle(frame,(ex+x,ey+y),(x+ex+ew,y+ey+eh),(0,255,0),2)
                    
            cv2.imshow('Dynamic',frame)            
            #如果按下q鍵則退出
            if cv2.waitKey(100) & 0xff == ord('q') :
                break
            
    camera.release()
    cv2.destroyAllWindows()
            

if __name__=='__main__':
    #filename = './image/img23.jpg'
    #StaticDetect(filename)
    DynamicDetect()

我們來分析一下StaticDetect函數,首先創建一個級聯分類器對象,然后加載xml檢測器,用來進行人臉檢測。

    #創建一個級聯分類器 加載一個 .xml 分類器文件. 它既可以是Haar特征也可以是LBP特征的分類器.
    face_cascade = cv2.CascadeClassifier('./haarcascades/haarcascade_frontalface_default.xml')

然后加載圖片文件,並將其轉換為灰度圖像,因為人臉檢測需要這樣的色彩空間。

    #加載圖像
    img = cv2.imread(filename)
    #轉換為灰度圖
    gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

接下來進行人臉檢測,需要傳入scaleFactor和minNeighbors參數,它們分別表示人臉檢測過程中每次迭代時圖像的壓縮率以及每個人臉矩形保留近似數目的最小值。然后函數返回人臉矩陣數組。我們利用cv2.rectangle函數在原圖中把矩形繪制出來。

    #進行人臉檢測,傳入scaleFactor,minNegihbors,分別表示人臉檢測過程中每次迭代時圖像的壓縮率以及
    #每個人臉矩形保留近似數目的最小值
    #返回人臉矩形數組
    faces = face_cascade.detectMultiScale(gray_img,1.3,5)
    for (x,y,w,h) in faces:
        #在原圖像上繪制矩形
        img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
    cv2.namedWindow('Face Detected!')
    cv2.imshow('Face Detected!',img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

運行結果如下,我們可以看到所有的人臉都檢測出來了,但是其中還有一個誤檢測:

2、視頻中的人臉檢測

上面已經介紹了在靜態圖像上進行人臉檢測,在視頻幀上重復進行這個過程就能完成視頻中的人臉檢測。DynamicDetect函數主要包括:打開攝像頭、讀取幀、檢測人臉、掃描檢測到的人臉中的眼睛,並使用不同顏色繪制出矩形框。

#2、視頻中的人臉檢測
def DynamicDetect():
    '''
    打開攝像頭,讀取幀,檢測幀中的人臉,掃描檢測到的人臉中的眼睛,對人臉繪制藍色的矩形框,對人眼繪制綠色的矩形框
    '''
    #創建一個級聯分類器 加載一個 .xml 分類器文件. 它既可以是Haar特征也可以是LBP特征的分類器.
    face_cascade = cv2.CascadeClassifier('./haarcascades/haarcascade_frontalface_default.xml')
    eye_cascade = cv2.CascadeClassifier('./haarcascades/haarcascade_eye.xml')
    
    #打開攝像頭    
    camera = cv2.VideoCapture(0)
    cv2.namedWindow('Dynamic')
    
    while(True):
        #讀取一幀圖像
        ret,frame = camera.read()
        #判斷圖片讀取成功?
        if ret:
            gray_img = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
            #人臉檢測
            faces = face_cascade.detectMultiScale(gray_img,1.3,5)
            for (x,y,w,h) in faces:
                #在原圖像上繪制矩形
                cv2.rectangle(frame,(x,y),(x+w,y+h),(255,0,0),2)
                roi_gray = gray_img[y:y+h,x:x+w]
                #眼睛檢測
                eyes = eye_cascade.detectMultiScale(roi_gray,1.03,5,0,(40,40))
                for (ex,ey,ew,eh) in eyes:
                    cv2.rectangle(frame,(ex+x,ey+y),(x+ex+ew,y+ey+eh),(0,255,0),2)
                    
            cv2.imshow('Dynamic',frame)            
            #如果按下q鍵則退出
            if cv2.waitKey(100) & 0xff == ord('q') :
                break
            
    camera.release()
    cv2.destroyAllWindows()

這里和上面有些類似,只是在進行眼睛檢測的時候多了幾個參數。detectMultiScale有許多可選參數;在人臉檢測時,默認選項足以檢測人臉,但是眼睛是一個比較小的人臉特征,並且胡子或者鼻子的本身陰影以及幀的隨機陰影都會產生假陽性。通過限制對眼睛搜索的最小尺寸為40x40像素,可以去掉假陽性。然后測試這些參數,直至應用程序可以滿足預期(例如可以嘗試指定特征的最大尺寸,或增加比例因子以及近鄰的數量)。

下面我們來總結一下detectMultiScale函數:

detectMultiScale(image[,scaleFactor[,minNeighbors[,flags[,minSize[,maxSize]]]]])
  • image:表示的是要檢測的輸入圖像
  • scaleFactor:為每一個圖像尺度中的尺度參數,默認值為1.1。scaleFactor參數可以決定兩個不同大小的窗口掃描之間有多大的跳躍,這個參數設置的大,則意味着計算會變快,但如果窗口錯過了某個大小的人臉,則可能丟失物體。
  • minNeighbors:參數為每一個級聯矩形應該保留的鄰近個數,默認為3。minNeighbors控制着誤檢測,默認值為3表明至少有3次重疊檢測,我們才認為人臉確實存。
  • flags:對於新的分類器沒有用(但目前的haar分類器都是舊版的,CV_HAAR_DO_CANNY_PRUNING,這個值告訴分類器跳過平滑(無邊緣區域)。利用Canny邊緣檢測器來排除一些邊緣很少或者很多的圖像區域;CV_HAAR_SCALE_IMAGE,這個值告訴分類器不要縮放分類器。而是縮放圖像(處理好內存和緩存的使用問題,這可以提高性能。)就是按比例正常檢測;CV_HAAR_FIND_BIGGEST_OBJECTS,告訴分類器只返回最大的目標(這樣返回的物體個數只可能是0或1)只檢測最大的物,CV_HAAR_DO_ROUGH_SEARCH,他只可與CV_HAAR_FIND_BIGGEST_OBJECTS一起使用,這個標志告訴分類器在任何窗口,只要第一個候選者被發現則結束尋找(當然需要足夠的相鄰的區域來說明真正找到了。),只做初略檢測.
  • minSize:為目標的最小尺寸
  • maxSize:為目標的最大尺寸

參考文獻:

[1]淺析人臉檢測之Haar分類器方法:Haar特征、積分圖、 AdaBoost 、級聯

[2]淺析人臉檢測之Haar分類器方法

[3]Haar+cascade AdaBoost分類器學習訓練總結

[4]OpenCV中的Haar+Adaboost(一):Haar特征詳細介紹

[5]Adaboost算法結合Haar-like特征

[6]ADABOOST做人臉檢測程序與原理

[7]AdaBoost 人臉檢測介紹(3) : AdaBoost算法流程

[8]Haar特征與積分圖(推薦)

[9]haar+adaboost結合講解(偏重原理)

[10]haar+adaboost結合講解(偏重實際)


免責聲明!

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



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