如何用OpenCV在Python中實現人臉檢測

選自towardsdatascience

作者:Maël Fabien

機器之心編譯

參與:高璇、張倩、淑婷

本教程將介紹如何使用 OpenCV 和 Dlib 在 Python 中創建和運行人臉檢測算法。同時還將添加一些功能,以同時檢測多個面部的眼睛和嘴巴。本文介紹了人臉檢測的最基本實現,包括級聯分類器、HOG 窗口和深度學習 CNN。

我們將通過以下方法實現人臉檢測:

  • 使用 OpenCV 的 Haar 級聯分類器
  • 使用 Dlib 的方向梯度直方圖
  • 使用 Dlib 的卷積神經網絡

本文代碼的 Github 庫(以及作者其他博客的代碼)鏈接:

https://github.com/maelfabien/Machine_Learning_Tutorials

我們將使用用於計算機視覺的開源庫 OpenCV,它用 C/C++編寫,有 C++、Python 和 Java 接口。同時支持 Windows、Linux、MacOS、iOS 和 Android 系統。同時我們還需要工具包 Dlib,它是一個包含機器學習算法和創建複雜軟件的 C++工具包。

步驟

第一步是安裝 OpenCV 和 Dlib。運行以下命令:

pip install opencv-python
pip install dlib

文件生成的路徑如下(版本不同,路徑會稍有差別):

/usr/local/lib/python3.7/site-packages/cv2

如果在使用 Dlib 時出現問題,請參見文章:https://www.pyimagesearch.com/2018/01/22/install-dlib-easy-complete-guide/

導入工具包和模型路徑

創建一個新的 Jupyter notebook/Python 文件,從以下代碼開始:

import cv2
import matplotlib.pyplot as plt
import dlib
from imutils import face_utils
font = cv2.FONT_HERSHEY_SIMPLEX

級聯分類器

首先研究級聯分類器。

理論

級聯分類器,即使用類 Haar 特徵工作的級聯增強分類器,是集成學習的一種特殊情況,稱為 boost。它通常依賴於 Adaboost 分類器(以及其他模型,如 Real Adaboost、Gentle Adaboost 或 Logitboost)。

級聯分類器在包含檢測目標的幾百個樣本圖像以及不包含檢測目標的其他圖像上進行訓練。

我們如何檢測圖上是否有人臉呢?有一種名為 Viola-Jones 的

目標檢測

框架的算法,包括了實時人臉檢測所需的所有步驟:

  • 提取 Haar 特徵,特徵來自 Haar 小波
  • 創建圖像
  • Adaboost 訓練
  • 級聯分類器

Haar 特徵選擇

人臉上最常見的一些共同特徵如下:

  • 與臉頰相比,眼部顏色較深
  • 與眼睛相比,鼻樑區域較為明亮
  • 眼睛、嘴巴、鼻子的位置較為固定......

這些特徵稱為 Haar 特徵。

特徵提取

過程如下所示:

如何用OpenCV在Python中實現人臉檢測

Haar 特徵

在上圖中,第一個特徵測量眼部和上臉頰之間的強度差異。特徵值計算的方法很簡單,對黑色區域中的像素求和再減去白色區域中的像素即可。

然後,將這個矩形作為卷積核作用到整個圖像。為了不產生遺漏,我們需要用到每個卷積核的所有的維度和位置。簡單的 24 * 24 的圖像可能會產生超過 160000 個特徵,每個特徵由像素值的和/差組成。這樣在計算上無法實現實時人臉檢測。那麼,該如何加快這個過程呢?

一旦通過矩形框識別到有用區域,則在與之完全不同的區域上就無需再做計算了。這一點可以通過 Adaboost 實現。

使用積分圖像原理計算矩形框特徵的方法更快。我們將在下一節介紹這一點。

如何用OpenCV在Python中實現人臉檢測

原始論文中提到幾種可用於 Haar 特徵提取的矩形框:

  • 雙矩形特徵計算的是兩個矩形區域內像素和的差,主要用於檢測邊緣 (a,b)
  • 三矩形特徵計算的是中心矩形和減去兩個外部矩形和的差,主要用於檢測線 (c,d)
  • 四矩形特徵計算的是矩形對角線對之間的差 (e)
如何用OpenCV在Python中實現人臉檢測

Haar 矩形

特徵提取完成後,使用 Adaboost 分類器將它們應用於訓練集,該分類器結合了一組弱分類器來創建準確的集成模型。只需 200 個特徵(最初是 16 萬個),實現了 95%的準確率。該論文的作者提取了 6000 個特徵。

積分圖像

以卷積核的形式計算特徵需要花費很長時間。出於這個原因,作者 Viola 和 Jones 提出了圖像的中間表示:積分圖像。積分圖像的作用是僅使用四個值簡單地計算矩形和。我們來看看它是如何工作的!

假設我們想要確定一個座標為 (x,y) 的給定像素的矩形特徵。然後,像素的積分圖像是給定像素的上方和左側的像素之和。

如何用OpenCV在Python中實現人臉檢測

其中 ii(x,y) 是積分圖像,i(x,y) 是原始圖像。

當計算整個積分圖像時,有一種只需要遍歷一次原始圖像的遞歸方法。實際上,我們可以定義以下一對遞歸形式:

如何用OpenCV在Python中實現人臉檢測

其中 s(x,y) 是累積行和,而 s(x−1)=0, ii(−1,y)=0。

這是怎麼實現的呢?假設我們想要估算區域 D 的像素總和。我們已經定義了 3 個其他區域:A,B 和 C。

  • 點 1 處的積分圖像的值是矩形 A 中的像素的總和。
  • 點 2 處的值為 A + B。
  • 點 3 處的值為 A + C。
  • 點 4 處的值是 A + B + C + D。

因此,區域 D 中的像素之和可以簡單地計算為: 4+1−(2+3)。

這樣我們僅使用 4 個數組值就計算出了矩形 D 的值。

如何用OpenCV在Python中實現人臉檢測

人們應該知道矩形在實際中是非常簡單的特徵,但對於人臉檢測已經足夠了。當涉及複雜問題時,可調濾波器往往更靈活多變。

如何用OpenCV在Python中實現人臉檢測

可調濾波器

使用 Adaboost 學習分類函數

給定一組帶標籤的訓練圖像(正負樣本均有),Adaboost 用於:

  • 提取一小部分特徵
  • 訓練分類器

由於 16 萬個特徵中的大多數特徵與之極不相關,因此我們設計一個增強模型的弱學習算法,用來提取單個矩形特徵,將最好的正負樣本區分開。

級聯分類器

雖然上述過程非常有效,但仍存在一個重大問題。在圖像中,大部分圖像為非面部區域。對圖像的每個區域給予等同的注意力是沒有意義的,因為我們應該主要關注最有可能包含人臉的區域。Viola 和 Jone 使用級聯分類器在減少了計算時間的同時,實現了更高的檢測率。

關鍵思想是在識別人臉區域時排除不含人臉的子窗口。由於任務是正確識別人臉,我們希望假陰率最小,即包含人臉卻未被識別的子窗口最少。

每個子窗口都使用一系列分類器。這些分類器是簡單的決策樹:

  • 如果第一個分類器檢測為正樣本,繼續用第二個
  • 如果第二個分類器檢測是正樣本,繼續用第三個
  • 以此類推

雖然有時可能包含人臉的圖被認成負樣本被子窗口漏檢。但初級分類器以較低的計算成本篩除了大多數負樣本,下圖的分類器可額外消除更多的負樣本,但需要更多的計算量。

如何用OpenCV在Python中實現人臉檢測

使用 Adaboost 訓練分類器,並調整閾值使錯誤率降到最低。在訓練該模型時,變量如下:

  • 每個階段分類器數量
  • 每個階段的特徵數量
  • 每個階段的閾值

幸運的是,在 OpenCV 中,整個模型已經經過預訓練,可直接用於人臉檢測。

如果想了解有關 Boosting 技術的更多信息,歡迎查看作者關於 Adaboost 的文章:

https://maelfabien.github.io/machinelearning/adaboost

輸入

下一步是找到預訓練的權重。我們將使用默認的預訓練模型來檢測人臉、眼睛和嘴巴。文件應位於此路徑(python 版本不同,路徑略有不同):

/usr/local/lib/python3.7/site-packages/cv2/data

確定路徑後,以此方式聲明級聯分類器:

cascPath = "/usr/local/lib/python3.7/site-packages/cv2/data/haarcascade_frontalface_default.xml"
eyePath = "/usr/local/lib/python3.7/site-packages/cv2/data/haarcascade_eye.xml"
smilePath = "/usr/local/lib/python3.7/site-packages/cv2/data/haarcascade_smile.xml"
faceCascade = cv2.CascadeClassifier(cascPath)
eyeCascade = cv2.CascadeClassifier(eyePath)
smileCascade = cv2.CascadeClassifier(smilePath)

檢測圖像中的人臉

在實現實時人臉檢測算法之前,讓我們先嚐試在圖像上簡單檢測一下。從加載測試圖像開始:

# Load the image
gray = cv2.imread('face_detect_test.jpeg', 0)
plt.figure(figsize=(12,8))
plt.imshow(gray, cmap='gray')
plt.show()
如何用OpenCV在Python中實現人臉檢測

測試圖像

然後開始檢測人臉,並將檢測到的人臉框起來。

# Detect faces
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.1,
minNeighbors=5,
flags=cv2.CASCADE_SCALE_IMAGE
)
# For each face
for (x, y, w, h) in faces:
# Draw rectangle around the face
cv2.rectangle(gray, (x, y), (x+w, y+h), (255, 255, 255), 3)

以下是 detectMultiScale 函數常見的參數列表:

  • scaleFactor:確定每個圖像縮放比例大小。
  • minNeighbors:確定每個候選矩形應保留多少個相鄰框。
  • minSize:最小目標的大小。小於該值的目標將被忽略。
  • maxSize:最大目標的大小。大於該值的目標將被忽略。

最後,顯示結果:

plt.figure(figsize=(12,8))
plt.imshow(gray, cmap='gray')
plt.show()
如何用OpenCV在Python中實現人臉檢測

在測試圖像上成功檢測到人臉。現在開始實時檢測!

實時人臉檢測

下面繼續進行實時人臉檢測的 Python 實現。第一步是啟動攝像頭,並拍攝視頻。然後,將圖像轉換為灰度圖。這用於減小輸入圖像的維數。實際上,我們應用了一個簡單的線性變換,而不是每個像素用三個點來描述紅、綠、藍。

如何用OpenCV在Python中實現人臉檢測

這在 OpenCV 中是默認實現的。

video_capture = cv2.VideoCapture(0)
while True:
# Capture frame-by-frame
ret, frame = video_capture.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

現在我們使用上述定義的 faceCascade 變量,它包含一個預訓練算法,現在將其用於灰度圖。

faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30),
flags=cv2.CASCADE_SCALE_IMAGE
)

對於檢測到的每個人臉,都加上一個矩形框:

for (x, y, w, h) in faces:
if w > 250 :
cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 3)
roi_gray = gray[y:y+h, x:x+w]
roi_color = frame[y:y+h, x:x+w]

對於檢測到的每張嘴,都加上一個矩形框:

smile = smileCascade.detectMultiScale(
roi_gray,
scaleFactor= 1.16,
minNeighbors=35,
minSize=(25, 25),
flags=cv2.CASCADE_SCALE_IMAGE
)
for (sx, sy, sw, sh) in smile:
cv2.rectangle(roi_color, (sh, sy), (sx+sw, sy+sh), (255, 0, 0), 2)
cv2.putText(frame,'Smile',(x + sx,y + sy), 1, 1, (0, 255, 0), 1)

對於檢測到的每雙眼睛,都加上一個矩形框:

eyes = eyeCascade.detectMultiScale(roi_gray)
for (ex,ey,ew,eh) in eyes:
cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)
cv2.putText(frame,'Eye',(x + ex,y + ey), 1, 1, (0, 255, 0), 1)

然後計算人臉總數,顯示整體圖像:

cv2.putText(frame,'Number of Faces : ' + str(len(faces)),(40, 40), font, 1,(255,0,0),2) 
# Display the resulting frame
cv2.imshow('Video', frame)

當按下 q 鍵時,執行退出選項。

if cv2.waitKey(1) & 0xFF == ord('q'):
break

最後當所有操作完成後,關閉所有窗口。在 Mac 上關閉窗口存在一些問題,可能需要通過活動管理器退出 Python。

video_capture.release()
cv2.destroyAllWindows()

封裝

import cv2
cascPath = "/usr/local/lib/python3.7/site-packages/cv2/data/haarcascade_frontalface_default.xml"
eyePath = "/usr/local/lib/python3.7/site-packages/cv2/data/haarcascade_eye.xml"
smilePath = "/usr/local/lib/python3.7/site-packages/cv2/data/haarcascade_smile.xml"
faceCascade = cv2.CascadeClassifier(cascPath)
eyeCascade = cv2.CascadeClassifier(eyePath)
smileCascade = cv2.CascadeClassifier(smilePath)
font = cv2.FONT_HERSHEY_SIMPLEX
video_capture = cv2.VideoCapture(0)
while True:
# Capture frame-by-frame
ret, frame = video_capture.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(200, 200),
flags=cv2.CASCADE_SCALE_IMAGE
)
# Draw a rectangle around the faces
for (x, y, w, h) in faces:
cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 3)
roi_gray = gray[y:y+h, x:x+w]
roi_color = frame[y:y+h, x:x+w]
cv2.putText(frame,'Face',(x, y), font, 2,(255,0,0),5)
smile = smileCascade.detectMultiScale(
roi_gray,
scaleFactor= 1.16,
minNeighbors=35,
minSize=(25, 25),
flags=cv2.CASCADE_SCALE_IMAGE
)
for (sx, sy, sw, sh) in smile:
cv2.rectangle(roi_color, (sh, sy), (sx+sw, sy+sh), (255, 0, 0), 2)
cv2.putText(frame,'Smile',(x + sx,y + sy), 1, 1, (0, 255, 0), 1)
eyes = eyeCascade.detectMultiScale(roi_gray)
for (ex,ey,ew,eh) in eyes:
cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)
cv2.putText(frame,'Eye',(x + ex,y + ey), 1, 1, (0, 255, 0), 1)
cv2.putText(frame,'Number of Faces : ' + str(len(faces)),(40, 40), font, 1,(255,0,0),2)
# Display the resulting frame
cv2.imshow('Video', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# When everything is done, release the capture
video_capture.release()
cv2.destroyAllWindows()

結果

我已經制作了人臉檢測算法的 YouTube 視頻演示:

Dlib 的方向梯度直方圖(HOG)

第二種常用的人臉檢測工具由 Dlib 提供,它使用了方向梯度直方圖(HOG)的概念。論文《Histograms of Oriented Gradients for Human Detection》實現這一方案。

理論

HOG 背後的想法是將特徵提取到一個向量中,並將其輸入到分類算法中,例如支持向量機,它將評估人臉(或實際想識別的任何對象)是否存在於某個區域中。

提取的特徵是圖像梯度(方向梯度)方向的分佈(直方圖)。梯度通常在邊緣和角落周圍較大,並允許我們檢測這些區域。

在原始論文中,該算法用於人體檢測,檢測過程如下:

預處理

首先,輸入圖像必須尺寸相同(可通過裁剪和縮放)。圖像長寬比要求為 1:2,因此輸入圖像的尺寸可能為 64x128 或 100x200。

計算梯度圖像

第一步是通過以下卷積核計算圖像的水平梯度和垂直梯度:

如何用OpenCV在Python中實現人臉檢測

計算梯度的卷積核

圖像的梯度通常會消除非必要信息。

上面圖像的梯度可以通過下面的 python 語句找到:

gray = cv2.imread('images/face_detect_test.jpeg', 0)
im = np.float32(gray) / 255.0
# Calculate gradient
gx = cv2.Sobel(im, cv2.CV_32F, 1, 0, ksize=1)
gy = cv2.Sobel(im, cv2.CV_32F, 0, 1, ksize=1)
mag, angle = cv2.cartToPolar(gx, gy, angleInDegrees=True)

繪製圖片:

plt.figure(figsize=(12,8))
plt.imshow(mag)
plt.show()
如何用OpenCV在Python中實現人臉檢測

我們之前沒有預處理圖像。

計算 HOG

首先將圖像分成 8x8 個單元來提供緊湊表示,使 HOG 對噪聲更魯棒。然後,計算每個單元的 HOG。

為了估計區域內的梯度方向,我們只需在每個區域內的 64 個梯度方向值(8x8)及其大小(另外 64 個值)之間構建直方圖。直方圖的類別對應梯度的角度,從 0 到 180°。總共 9 類:0°,20°,40°...... 160°。

上面的代碼給了我們 2 個信息:

  • 梯度方向
  • 梯度大小

當我們構建 HOG 時,有 3 種情況:

  • 角度小於 160°,且不介於兩類之間。在這種情況下,角度將添加到 HOG 的正確類中。
  • 角度小於 160°,恰好在兩類之間。在這種情況下,像素被均分到左右兩側類中。
  • 角度大於 160°。在這種情況下,我們認為像素與 160°和 0°成比例。
如何用OpenCV在Python中實現人臉檢測

如何用OpenCV在Python中實現人臉檢測

每個 8x8 單元的 HOG 如下所示:

如何用OpenCV在Python中實現人臉檢測

HOG

模塊歸一化

最後,可以用 16×16 的模塊對圖像進行歸一化,並使其對光照不變。這可以通過將大小為 8x8 的 HOG 的每個值除以包含它的 16x16 模塊的 HOG 的 L2 範數來實現,這個模塊實際上是長度為 9*4 = 36 的簡單向量。

模塊歸一化

最後,將所有 36x1 向量連接成一個大向量。OK!現在有了特徵向量,我們可以在上面訓練一個軟 SVM 分類器(C=0.01)。

檢測圖像上的人臉

實現非常簡單:

face_detect = dlib.get_frontal_face_detector()
rects = face_detect(gray, 1)
for (i, rect) in enumerate(rects):
(x, y, w, h) = face_utils.rect_to_bb(rect)
cv2.rectangle(gray, (x, y), (x + w, y + h), (255, 255, 255), 3)
plt.figure(figsize=(12,8))
plt.imshow(gray, cmap='gray')
plt.show()
如何用OpenCV在Python中實現人臉檢測

實時人臉檢測

如前所述,該算法非常容易實現。我們還實現了一個更輕量的版本,只用來識別人臉。Dlib 讓人臉關鍵點的檢測更加容易,但這是另一個話題。

video_capture = cv2.VideoCapture(0)
flag = 0
while True:
ret, frame = video_capture.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
rects = face_detect(gray, 1)
for (i, rect) in enumerate(rects):
(x, y, w, h) = face_utils.rect_to_bb(rect)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow('Video', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
video_capture.release()
cv2.destroyAllWindows()

Dlib 中的卷積神經網絡

最後一種方法基於卷積神經網絡。為了增強結果,它還實現了最大邊緣目標檢測(MMOD)。

理論

卷積神經網絡是主要用於計算機視覺的前饋神經網絡。它們提供自動圖像預處理以及密集的神經網絡部分。CNN 還是用來處理帶有網格狀拓撲的數據的特殊神經網絡。它的架構靈感來自動物視覺皮層。

以前的方法中,很大一部分工作是選擇濾波器來創建特徵,以便盡從圖像中可能多地提取信息。隨著深度學習和計算能力的提高,這項工作現在可以實現自動化。CNN 的名稱就來自我們用一組濾波器卷積初始圖像輸入的事實。需要選擇的參數仍是需要應用的濾波器數量以及尺寸。濾波器的尺寸稱為步幅。一般步幅設置在 2 到 5 之間。

如何用OpenCV在Python中實現人臉檢測

在這種特定情況下,CNN 的輸出是二分類,如果有人臉,則取值 1,否則取 0。

檢測圖像上的人臉

一些元素在實現中會發生變化。

第一步是下載預訓練模型:https://github.com/davisking/dlib-models/blob/master/mmod_human_face_detector.dat.bz2

將下載後的權重放到文件夾中,並定義 dnnDaceDetector:

dnnFaceDetector = dlib.cnn_face_detection_model_v1("mmod_human_face_detector.dat")

然後,與之前做的相同:

rects = dnnFaceDetector(gray, 1)
for (i, rect) in enumerate(rects):
x1 = rect.rect.left()
y1 = rect.rect.top()
x2 = rect.rect.right()
y2 = rect.rect.bottom()
# Rectangle around the face
cv2.rectangle(gray, (x1, y1), (x2, y2), (255, 255, 255), 3)
plt.figure(figsize=(12,8))
plt.imshow(gray, cmap='gray')
plt.show()
如何用OpenCV在Python中實現人臉檢測

實時人臉檢測

最後,實現實時 CNN 人臉檢測:

video_capture = cv2.VideoCapture(0)
flag = 0
while True:
# Capture frame-by-frame
ret, frame = video_capture.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
rects = dnnFaceDetector(gray, 1)
for (i, rect) in enumerate(rects):
x1 = rect.rect.left()
y1 = rect.rect.top()
x2 = rect.rect.right()
y2 = rect.rect.bottom()
# Rectangle around the face
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
# Display the video output
cv2.imshow('Video', frame)
# Quit video by typing Q
if cv2.waitKey(1) & 0xFF == ord('q'):
break
video_capture.release()
cv2.destroyAllWindows()

如何選擇模型

這是一個很難回答的問題,但我們只討論兩個重要指標:

  • 計算時間
  • 準確率

在速度方面,HOG 是最快的算法,其次是 Haar 級聯分類器和 CNN。

但是,Dlib 中的 CNN 是準確率最高的算法。HOG 表現也很好,但在識別較小的人臉時會有一些問題。Haar 級聯分類器的整體表現與 HOG 相似。

考慮到實時人臉檢測的速度,我在個人項目中使用了 HOG。

希望這個關於 OpenCV 和 Dlib 的人臉檢測的快速教程能對你有所幫助。

原文鏈接:https://towardsdatascience.com/a-guide-to-face-detection-in-python-3eab0f6b9fc1

相關推薦

推薦中...