1. 程式人生 > >HAAR與DLib的實時人臉檢測之實現與對比

HAAR與DLib的實時人臉檢測之實現與對比

人臉檢測方法有許多,比如opencv自帶的人臉Haar特徵分類器和dlib人臉檢測方法等。

對於opencv的人臉檢測方法,優點是簡單,快速;存在的問題是人臉檢測效果不好。正面/垂直/光線較好的人臉,該方法可以檢測出來,而側面/歪斜/光線不好的人臉,無法檢測。因此,該方法不適合現場應用。而對於dlib人臉檢測方法採用64個特徵點檢測,效果會好於opencv的方法識別率會更高,本文會分別採用這幾種方法來實現人臉識別。那個演算法更好,跑跑程式碼就知道。

實時影象捕獲

首先在進行人臉識別之前需要先來學點OpenCV的基礎,起碼知道如何從攝像頭獲取當前拍到的影象吧。OpenCV其實很簡單,接下來的程式碼就是最基本的起步點。

第一步:開啟本機上的攝像頭,例項化VideoCapture

camera = cv2.VideoCapture(0)

開始第一幀影象的捕獲,這個方法用來測試當前的攝像頭是否可用

success, frame = camera.read()

success返回真時表示開始捕捉影象,反則表示攝像頭開啟失敗,接下來就用最少的程式碼來開啟攝像頭並將當前的影象直接顯示到一個視窗上,具體程式碼結構如下:

# coding=utf-8
# ~/learn_face/cv_base.py
from __future__ import print_function

import cv2

cameraCapture = cv2.VideoCapture(0)
success, frame = cameraCapture.read()

while success and cv2.waitKey(1) == -1:
    success, frame = cameraCapture.read()
    #TODO:在此處可放置各種對當前每一幀影象的處理
    cv2.imshow("Camera", frame)

cameraCapture.release()
cv2.destroyAllWindows()

將上述程式碼存為opencv_base.py然後在命令列直接執行檢視效果:

python opencv_base.py

效果如下:

HAAR 分類器

基於Haar特徵的cascade分類器(classifiers) 是Paul Viola和 Michael Jone在2001年,論文”Rapid Object Detection using a Boosted Cascade of Simple Features”中提出的一種有效的物品檢測(object detect)方法。它是一種機器學習方法,通過許多正負樣例中訓練得到cascade方程,然後將其應用於其他圖片。

在OpenCV3的原始碼的data

目錄中就可以找到已訓練好的HAAR演算法模型,至HAAR演算法的各種細節與理論有興趣的直接去Google或者百度吧,一搜一大堆。花時間看一堆理論不如直接上程式碼,由程式碼直接理解這些複雜理論的應用更適合開發人員,畢竟我們不是數學家。

使用HAAR模型識別影象中的人臉其實只要三步走,即使你對深度網路一點不懂也沒關係,再複雜的理論到最終不過是一個方法呼叫罷了,瞭解清楚其中的原理就好。

第一步:初始化分類器並載入已訓練好的HARR模型:

face_cascade = cv2.CascadeClassifier(r'haarcascade_frontalface_default.xml')

第二步: 通過cv2.cvtColor方法將當前的影象進行灰度化處理,簡化影象的資訊:

gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

第三步:然後將灰度化後的影象輸入到分類器進行預判:

faces = face_cascade.detectMultiScale(gray, 1.3, 5) #識別人臉

只要faces陣列的長度大於一就表示檢測到當前畫面中檢測到人臉,反之亦然。簡單來說其實人臉檢測已經完成,

最後,為了我們可以知道識別出來的結果,我們可以將臉用方框給圈出來,這裡寫個方法來圈臉:

def mark_face(img,x,y,w,h):
    return cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)

以下為本例的全部程式碼:

# coding=utf-8
# ~/learn_face/cv_haar.py
from __future__ import print_function
import cv2

def mark_face(img, x, y, w, h):
    return cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)

cameraCapture = cv2.VideoCapture(0)
success, frame = cameraCapture.read()
face_cascade = cv2.CascadeClassifier(r'haarcascade_frontalface_default.xml') # 1.載入模型

while success and cv2.waitKey(1) == -1:
    success, frame = cameraCapture.read()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #  2.生成灰度圖
    faces = face_cascade.detectMultiScale(
        gray,
        scaleFactor=1.1,
        minNeighbors=5
    ) # 3.進行識別
    [mark_face(frame, *args) for args in faces] #畫出識別的結果
    cv2.imshow("Camera", frame)
cameraCapture.release()
cv2.destroyAllWindows()

以下是執行效果:

執行起來就會覺得HAAR的識別效果不怎麼樣,稍微動一下就很會識別不了。

Dlib

接下來我們試試用DLib這個老牌的專做人臉識別起家的C++庫來試試,Dlib是一個跨平臺的C++公共庫,除了執行緒支援,網路支援,提供測試以及大量工具等等優點,Dlib還是一個強大的機器學習的C++庫,包含了許多機器學習常用的演算法。同時支援大量的數值演算法如矩陣、大整數、隨機數運算等等。Dlib同時還包含了大量的圖形模型演算法。最重要的是Dlib的文件和例子都非常詳細。

與HAAR分類器的檢測方法相比dLib就簡單得多了,只需要用dlib自帶的人臉檢測器detector就夠了,連模型都省了!之前的程式碼兩步就能完成

第一步:例項化 detector:

detector = dlib.get_frontal_face_detector()

第二步:進行人臉檢測

faces = detector(frame, 1)

That's all! 是不是很簡單?

以下是本例的全部程式碼:

# coding=utf-8
# ~/learn_face/cv_dlib.py
from __future__ import print_function
import cv2
import dlib

cameraCapture = cv2.VideoCapture(0)
success, frame = cameraCapture.read()
detector = dlib.get_frontal_face_detector()

while success and cv2.waitKey(1) == -1:
    success, frame = cameraCapture.read()
    faces = detector(frame, 1)
    for k, d in enumerate(faces):
        frame = cv2.rectangle(frame, (d.left(), d.top()),
                              (d.right(), d.bottom()), (255, 0, 0), 2)

    cv2.imshow("Camera", frame)

cameraCapture.release()
cv2.destroyAllWindows()

執行上述程式碼後會發現dlib的效果真的比HAAR的檢測效果要好很多!不管頭怎麼轉都能瞬間識別到,畫出來的矩形框都不帶閃的!

特徵點檢測

接下來我們用DLib的特徵點提取器detector所識別出來的人臉輪廓點給標記出來。關鍵點(landmarks)提取需要一個特徵提取器predictor,為了構建特徵提取器,預訓練模型必不可少。除了自行進行訓練外,可以使用官方提供的一個模型。該模型可從dlib sourceforge 庫下載,此模型是從人臉中提出64個特徵點進行檢測,其準確度相當高。

具體實現思路如下:

  • 第一步:生成灰度圖
  • 第二步:生成直方圖
  • 第三步:進行檢測

以下為全部程式碼

# coding=utf-8
# ~/learn_face/landmark.py
import cv2
import dlib

cameraCapture = cv2.VideoCapture(0)
success, frame = cameraCapture.read()
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(
    "shape_predictor_68_face_landmarks.dat")  

while success and cv2.waitKey(1) == -1:
    success, frame = cameraCapture.read()

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)            #生成灰度圖
    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))  #生成直方圖
    clahe_image = clahe.apply(gray)
    detections = detector(clahe_image, 1)

    for k, d in enumerate(detections): 
        shape = predictor(clahe_image, d)  # 獲取座標
        for i in range(1, 68):  # 每張臉都有68個識別點
            cv2.circle(frame, (shape.part(i).x, shape.part(i).y), 1, (0, 0, 255),
                       thickness=2)

    cv2.imshow("Camera", frame)

cameraCapture.release()
cv2.destroyAllWindows()

執行效果:

小結

我在macBookPro上跑以上的程式碼在速度是上沒有什麼很大區別的,至少不會產生卡頓。但如果換將程式碼植到樹莓3和樹莓Zero上區別就明顯了,HAAR分類器在樹梅Zero上的執行時間平均在1.2s左右,而dlib則需要8s。至於準確率Dlib又明顯會優於HAAR。

參考閱讀

  • HAAR分類器 - 這篇知乎上的文章對HAAR分類器的原理分析得很詳盡,有興趣可以讀一讀
  • 本文程式碼可到我的碼雲上下載