使用Python+OpenCV+dlib爲人臉生成口罩



來源:深度學習與計算機視覺

本文約4800字,建議閱讀6分鐘本文試圖用OpenCV和dlib庫來實現這個過程,在這裏我們綜合生成5種類型的口罩來繪製人臉圖像。


本文使用OpenCV dlib庫生成口罩


口罩已經被證明是防止COVID-19傳播的最好的防禦措施之一,然而,這也導致了基於面部特徵(包括鼻子、嘴和下巴線)的面部識別算法的失效。

在全球有傳染病之前,面部識別系統通過對檢測到的不同面部特徵進行比較測量來驗證兩幅圖像中的人臉。當一個人的鼻子、嘴和臉頰上戴上口罩,大大減少了通常用來識別他/她的身份的信息。

將需要重新訓練或重新設計有效的識別系統,以識別受管制地區的口罩臉。爲了做到這一點,需要一個大的口罩數據集來訓練深度學習模型,以檢測戴口罩的人和不戴口罩的人。

目前,可用於訓練和評估人臉識別系統的圖像數據集是有限的。據報道,美國國家標準與技術研究所(NIST)的研究通過將口罩(各種顏色、大小和位置)疊加在沒有帶口罩人臉的圖像上來解決這個問題。

https://www.cnet.com/news/face-masks-are-thwarting-even-the-best-facial-recognition-algorithms-study-finds/?ftag=COS-05-10aaa0b&TheTime=2020-07-27T22%3A23%3A21&PostType=link&UniqueID=C7330AC8-D057-11EA-8580-DE063A982C1E&ServiceType=twitter

這篇文章試圖用OpenCV和dlib庫來實現這個過程,在這裏我們綜合生成5種類型的口罩來繪製人臉圖像。圖1顯示了生成的5種口罩類型。

安裝所需的軟件包

使用Python3.7創建一個新的虛擬環境並安裝依賴項。所需的庫如下:

#requirements_facemask.txt
numpy == 1.18.5
pip == 20.2.2
imutils == 0.5.3
python >=3.7
dlib == 19.21.0
cmake == 3.18.0
opencv-python == 4.4.0

由於此腳本需要dlib庫,因此在開始運行該腳本之前需要安裝dlib,你可以通過以下鏈接瞭解如何使用Python綁定安裝dlib:https://www.pyimagesearch.com/2017/03/27/how-to-install-dlib/

Dlib是一個高級的機器學習庫,它是爲解決複雜的現實世界問題而創建的。這個庫是用C++編程語言創建的,它可以使用C/C++、Python和java等語言。

導入庫

我們從導入所需的庫開始:OpenCV、dlib、numpy、os和imutils。

# 必要的導入
import cv2
import dlib
import numpy as np
import os
import imutils

下一步是設置口罩的顏色,並設置要從中導入圖像的目錄和路徑,OpenCV的顏色空間按BGR順序而不是RGB。

# 設置目錄
os.chdir('PATH_TO_DIR')
path = 'IMAGE_PATH'

# 初始化顏色 [color_type] = (Blue, Green, Red)
color_blue = (239,207,137)
color_cyan = (255,200,0)
color_black = (0, 0, 0)

下面的鏈接讓你可以立即從視覺上探索顏色,它可以用於將顏色從十六進制轉換爲RGB,反之亦然:https://www.rgbtohex.net/rgb/

圖像預處理

接下來,我們通過OpenCV加載我們的輸入圖像,然後通過調整大小使其具有500像素的寬度並將其轉換爲灰度來預處理圖像。

# 加載圖像並調整大小,將其轉換爲灰度
img= cv2.imread('image_path')
img = imutils.resize(img, width = 500)
gray=cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)


使用dlib、OpenCV和Python檢測和提取人臉關鍵點

爲了覆蓋口罩,我們需要進行人臉檢測,有許多方法可用於執行此任務,我們可以使用OpenCV內置的Haar Cascade XML文件,甚至TensorFlow或使用Keras。在這篇文章中,我們使用的是dlib的人臉檢測器。

dlib中的正面人臉檢測器是基於方向梯度直方圖(HOG)和線性SVM的。


我們使用dlib的正面人臉檢測來首先檢測人臉,然後使用面部標誌點預測器dlib.shape_predictor檢測人臉關鍵點。

人臉關鍵點檢測被定義爲檢測臉上的關鍵標誌點並跟蹤它們(對由於頭部運動和麪部表情而導致的剛性和非剛性面部變形具有魯棒性)[來源]


什麼是人臉關鍵點?

人臉關鍵點是用於定位和表示面部的顯著區域,如眼睛、眉毛、鼻子、下顎線、嘴巴等,應用於人臉對齊、頭部姿態估計、換臉、眨眼檢測、睏倦檢測等領域。

在人臉關鍵點下,利用形狀預測方法對人臉上重要的面部結構進行檢測是非常必要的。面部標誌點檢測包括兩個步驟:

  1. 定位圖像中檢測到的人臉。

  2. 面部關鍵點的檢測。

如前所述,我們可以通過多種方式執行人臉檢測,但每種方法都試圖定位和標記以下面部區域:

  • 鼻子

  • 下顎線

  • 左眼和右眼

  • 左右眉

在這篇文章中,我們使用了基於深度學習的人臉定位算法,該算法還用於圖像中人臉的檢測。我們將通過某種方法獲得面邊界框,其中我們分別使用圖像中人臉的(x,y)座標。一旦人臉區域被檢測到並被限定,我們進入下一步檢測臉部區域中的關鍵點。

我們正在使用的dlib庫中包含的預訓練人臉關鍵點探測器,這是Kazemi和Sullivan(2014)用迴歸樹集合論文實現的1毫秒人臉對齊算法,其中估計了映射到人臉結構的68個(x,y)座標的位置。我們可以使用下圖顯示68個座標或點的索引:


從圖3可以通過不同的點集[起點,終點]來評估面部特徵的位置:

  • 左眼:點[42,47]

  • 嘴:點[48,67]

  • 左眉:點[22,26]

  • 鼻子:點[27,34]

  • 右眉:點[17,21]

  • 右眼:點[36,41]

  • 下顎線:點[0,16]

請注意,標誌點從0開始

dlib人臉關鍵點檢測器就是在這個數據集上訓練的:https://ibug.doc.ic.ac.uk/resources/facial-point-annotations/

人臉檢測與人臉關鍵點檢測

下一步是對dlib的預訓練人臉檢測器進行初始化,該檢測器是基於Histogram of Oriented Gradients + Linear SVM method](https://pyimagesearch.com/2014/11/10/histogram-oriented-gradients-object-detection/) 。此檢測器會進行圖像中人臉邊界框的檢測。

檢測器的第一個參數是我們的灰度圖像。(此方法也適用於彩色圖像)。

第二個參數是在應用檢測器之前對圖像進行放大時要應用的圖像金字塔層的數量。在人臉檢測之前增加輸入圖像的分辨率的好處是可以讓我們在圖像中檢測到更多的人臉,但其缺點是,輸入圖像越大,計算開銷越大,檢測速度越慢。

我們還打印出邊界框的座標以及檢測到的人臉數。我們也可以使用cv2在檢測到的面部周圍使用for循環繪製邊界框。

# 初始化dlib的人臉檢測器
detector = dlib.get_frontal_face_detector()

"""
在灰度圖像中檢測人臉並創建一個對象-存儲邊界矩形的座標列表
第二個參數中的“1”表示應該向上採樣圖像1次。 
這會使圖像變得更大,並允許我們檢測更多的面孔
"""

faces = detector(gray, 1)

# 打印邊界矩形的座標
print(faces)
print("Number of faces detected: ", len(faces))

"""
# 使用for循環來提取特定座標(x1,x2,y1,y2)
for face in faces:
  x1 = face.left()
  y1 = face.top()
  x2 = face.right()
  y2 = face.bottom()
  # 在檢測到的臉部周圍畫一個矩形
  cv2.rectangle(img, (x1,y1), (x2,y2),(0,255,0),3)
cv2.imshow("image", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
"""

爲了檢測人臉關鍵點,我們需要從dlib庫下載人臉關鍵點預測器dlib.shape_predictor。

我們的預測方法需要一個名爲“shape_predictor_68_face_landmarks.dat”的文件,可從以下鏈接下載文件:http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2

需要強調的是,這個模型文件是專爲dlib的HOG人臉檢測器設計的,不應該用於dlib的基於CNN的人臉檢測器,原因是它期望人臉檢測器的邊界框按照dlib的HOG人臉檢測器的方式對齊。

當與另一個產生不同對齊框的人臉檢測器(如基於CNN的mmod_human_face_detector.dat )一起使用時,結果不會很好。

# 文件路徑
p = "shape_predictor_68_face_landmarks.dat"
# 初始化dlib的預測器
predictor = dlib.shape_predictor(p)

# 使用預測器獲取外形

for face in faces:
    landmarks = predictor(gray, face)

    # for n in range(0,68):
    #     x = landmarks.part(n).x
    #     y = landmarks.part(n).y
    #     img_landmark = cv2.circle(img, (x, y), 4, (0, 0, 255), -1)

下載檢測器後,我們可以初始化檢測器,以便在輸入圖像中檢測到的每個人臉上檢測到人臉關鍵點。

一旦檢測到人臉關鍵點,我們就可以開始“繪圖”了,通過使用OpenCV中的繪圖功能連接所需的點,將口罩覆蓋在臉上:https://docs.opencv.org/master/dc/da5/tutorial_py_drawing_functions.html

Dlib口罩方法

下面的步驟包括識別繪製不同類型口罩所需的點。我們複製的口罩類型由NIST研究報告附錄A中提到的不同點集定義:https://doi.org/10.6028/NIST.IR.8311。視覺效果見圖。


我們將通過連接附錄A中定義的標誌點來定義口罩的形狀。例如,爲了形成寬覆蓋和中覆蓋口罩,我們將用29點的標誌點座標連接(繪製)下顎線[0,16]的標誌點。

可以使用OpenCV中橢圓和其他三種規則形狀函數繪製口罩輪廓。然後我們可以使用cv2.fillpoly函數將繪製的口罩填充顏色。

points = []
    for i in range(1, 16):
        point = [landmarks.part(i).x, landmarks.part(i).y]
        points.append(point)
    # print(points)

    # 寬,高覆蓋口罩
    mask_a = [((landmarks.part(42).x), (landmarks.part(15).y)),
              ((landmarks.part(27).x), (landmarks.part(27).y)),
              ((landmarks.part(39).x), (landmarks.part(1).y))]

    # 寬,中覆蓋口罩
    mask_c = [((landmarks.part(29).x), (landmarks.part(29).y))]

    # 寬、低覆蓋口罩
    mask_e = [((landmarks.part(35).x), (landmarks.part(35).y)),
              ((landmarks.part(34).x), (landmarks.part(34).y)),
              ((landmarks.part(33).x), (landmarks.part(33).y)),
              ((landmarks.part(32).x), (landmarks.part(32).y)),
              ((landmarks.part(31).x), (landmarks.part(31).y))]

    fmask_a = points + mask_a
    fmask_c = points + mask_c
    fmask_e = points + mask_e

    # mask_type = {1: fmask_a, 2: fmask_c, 3: fmask_e}
    # mask_type[choice2]


    # 使用Python OpenCV - cv2.polylines()方法爲[mask_type]繪製口罩輪廓:
    # fmask_a = wide, high coverage mask,
    # fmask_c = wide, medium coverage mask,
    # fmask_e  = wide, low coverage mask

    fmask_a = np.array(fmask_a, dtype=np.int32)
    fmask_c = np.array(fmask_c, dtype=np.int32)
    fmask_e = np.array(fmask_e, dtype=np.int32)

    mask_type = {1: fmask_a, 2: fmask_c, 3: fmask_e}
    mask_type[choice2]


    # 更改參數[mask_type]和color_type用於各種組合
    img2 = cv2.polylines(img, [mask_type[choice2]], True, choice1, thickness=2, lineType=cv2.LINE_8)

    # 使用Python OpenCV - cv2.fillPoly()方法填充口罩
    # 更改參數[mask_type]和color_type用於各種組合
    img3 = cv2.fillPoly(img2, [mask_type[choice2]], choice1, lineType=cv2.LINE_AA)

# cv2.imshow("image with mask outline", img2)
cv2.imshow("image with mask", img3)

# 爲測試保存輸出文件
outputNameofImage = "output/imagetest.jpg"
print("Saving output image to", outputNameofImage)
cv2.imwrite(outputNameofImage, img3)

    points = []
    for i in range(1, 16):
        point = [landmarks.part(i).x, landmarks.part(i).y]
        points.append(point)
    # print(points)

    # 橢圓參數爲高,圓形是覆蓋口罩
    top_ellipse = landmarks.part(27).y + (landmarks.part(28).y - landmarks.part(27).y) / 2
    centre_x = landmarks.part(28).x
    centre_y = landmarks.part(8).y - ((landmarks.part(8).y - (top_ellipse)) / 2)
    # 橢圓高度
    axis_major = (landmarks.part(8).y - top_ellipse) / 2
    # 橢圓寬度
    axis_minor = ((landmarks.part(13).x - landmarks.part(3).x) * 0.8) / 2

    centre_x = int(round(centre_x))
    centre_y = int(round(centre_y))
    axis_major = int(round(axis_major))
    axis_minor = int(round(axis_minor))

    centre = (centre_x, centre_y)
    axes = (axis_major, axis_minor)

    # 使用Python OpenCV - cv2.ellipse()方法繪製口罩輪廓
    # 更改最後一個參數- line thickness和color_type爲各種組合
    img_2 = cv2.ellipse(img, centre, axes, 0, 0, 360, color_type, thickness=2)



    # 使用Python OpenCV - cv2.ellipse()方法繪製口罩輪廓
    # 更改最後一個參數-line thickness爲負數用於填充,color_type用於各種組合
    img_3 = cv2.ellipse(img, centre, axes, 0, 0, 360, color_type, thickness=-1)

# cv2.imshow("image with mask outline", img_2)
cv2.imshow("image with mask", img_3)

在圖像檢測開始之前,用戶可以選擇預先確定口罩的顏色和類型。我們預先選擇了兩種顏色的口罩:藍色和黑色。

# 使用input()函數根據用戶需求獲取口罩類型和口罩顏色
choice1 = input("Please select the choice of mask color\nEnter 1 for blue\nEnter 2 for black:\n")
choice1 = int(choice1)

if choice1 == 1:
    choice1 = color_blue
    print('You selected mask color = blue')
elif choice1 == 2:
    choice1 = color_black
    print('You selected mask color = black')
else:
    print("invalid selection, please select again.")
    input("Please select the choice of mask color\nEnter 1 for blue\nEnter 2 for black :\n")


choice2 = input("Please enter choice of mask type coverage \nEnter 1 for high \nEnter 2 for medium \nEnter 3 for low :\n")
choice2 = int(choice2)

if choice2 == 1:
    # choice2 = fmask_a
    print(f'You chosen wide, high coverage mask')
elif choice2 == 2:
    # choice2 = fmask_c
    print(f'You chosen wide, medium coverage mask')
elif choice2 == 3:
    # choice2 = fmask_e
    print(f'You chosen wide, low coverage mask')
else:
    print("invalid selection, please select again.")
    input("Please enter choice of mask type coverage \nEnter 1 for high \nEnter 2 for medium \nEnter 3 for low :\n")

# print(choice2)


結果

圖5顯示了原始輸入圖像(Barack Obama的圖像)與使用腳本生成了口罩的輸出圖像之間的比較。我們也可以在人羣鏡頭使用這個腳本。如圖6所示,在著名的Ellen's wefie拍攝中,在檢測到的人臉上疊加口罩的結果。

我們能夠成功地複製生成5種不同類型的口罩過程(詳見附錄A),這些口罩可以使用dlib和OpenCV疊加在未帶口罩的人臉的圖像上。

圖7到圖9顯示了在不直接看相機的臉上的更多示例。


結論

該腳本能夠在檢測到的人臉上生成合成口罩臉,輸出圖像可用於測試或驗證其他面向應用的ML網絡,如室內考勤系統的人臉識別、口罩檢測等。

你可以在這裏下載完整代碼:https://github.com/xictus77/face-mask-overlay-with-OpenCV-Dlib.git

參考文獻:

Facial landmarks with dlib, OpenCV, and Python(https://www.pyimagesearch.com/2017/04/03/facial-landmarks-dlib-opencv-python/)

Facial point annotations(https://ibug.doc.ic.ac.uk/resources/facial-point-annotations/)

Real-Time Face Pose Estimation(http://blog.dlib.net/2014/08/real-time-face-pose-estimation.html)

Drawing Functions in OpenCV(https://docs.opencv.org/master/dc/da5/tutorial_py_drawing_functions.html)

Sources of images — open source and https://www.kaggle.com/dansbecker/5-celebrity-faces-dataset

編輯:王菁

校對:王欣

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