【人臉辨識】使用OpenVINO預訓練模型進行人臉辨識

作者:曾成訓(CH.Tseng)

OpenVINO 套件包提供多種訓練好的模型供使用者直接進行專案,本文介紹如何使用 OpenVINO 所提供的三種 pre-trained models:face detection、facial landmarks 及 facial embedded features 來進行人臉識別。

Model 介紹

Face Detection Models

針對 face detection,OpenVINO 提供三種 pre-trained model,分別是:

本文將使用其中的 face–detection–adas–0001 作為示範。

Facial Landmarks Models

OpenVINO 提供了二種 facial landmarks models,分別為 5 點的 landmarks–regression–retail–0009 以及35 點的 facial–landmarks–35–adas–0002。

(圖片來源:曾成訓)

本文將使用五點的 landmarks–regression–retail–0009 取得雙眼的兩個中間點,進行人臉的校正示範。

Facial Embedded Features

針對 facial embedded features,本文使用 face–reidentification–retail–0095 model,該 pre–trained model 輸出 256 個 facial embedded features,LFW 正確率為 0.9947,接受的圖片 shape 為[1x3x128x128],輸出的 shape 為 [1, 256, 1, 1]。

下載 Models

1. 下載 Open Model Zoo

git clone:https://github.com/opencv/open_model_zoo

2. 使用 downloader 下載需要的 model:

cd open_model_zoo/model_downloader

本文需要下載下列三個模型:

./downloader.py –all –c

或者,您也可以下載全部的 pre-trained models:

./downloader.py –all

下圖為下載全部模型的畫面:

(圖片來源:曾成訓)

(註:model_downloader.py 需要安裝 pyyaml 及 requests 這兩個模型,可使用 pip install pyyaml requests 來安裝)

最後,請確定您已經下載了下列三個 pre-trained 模型檔案(如果您要在樹莓派執行,請下載後接有 fp16 的檔案,其它如 PC 端,則下載沒有 fp16 結尾的檔案)。

(圖片來源:曾成訓)

face–detection–adas–0001 臉部識別模型

為了方便使用,我寫了一個 class for OpenVINO Face detection,import 進來後,只要三行程式便可傳回圖片中的所有人臉。測試結果在樹莓派使用 NCS2 搭配 OpenVINO,使用此 CNN model 進行 Face detection 的速度為 0.2289 秒。

from libFaceDoor import ov_FaceDect
FACE_Detect = ov_FaceDect("models/face-detection-adas-0001.bin", \
"models/face-detection-adas-0001.xml")
#傳入image檔及給予threshold值,就可得到face bbox及scores。
face_boxes, face_scores = FACE_Detect.detect_face(image, score=0.5, \
target_device=cv2.dnn.DNN_TARGET_MYRIAD)

landmarks–regression–retail–0009 臉部特徵點模型

本 model 會傳回一組十個數值的陣列 [1, 10],分別代表 [x0, y0, x1, y1, …, x5, y5] 共五個 facial landmarks。

同樣為了方便使用,我也寫了一個 class for OpenVINO Facial landmarks,import 進來後,只需三行程式,便可取得人臉的五點 landmarks 以及應校正的角度值。測試結果在樹莓派使用 NCS2 搭配OpenVINO,使用此 CNN model 取得 landmarks 的速度為 0.0097 秒。

from libFaceDoor import ov_Face5Landmarks
faceLandmarks = ov_Face5Landmarks(bin_path= \
"models/landmarks-regression-retail-0009.bin",  \
xml_path="models/landmarks-regression-retail-0009.xml")

#face為經由face detection所取得的臉部區域
points = faceLandmarks.getLandmarks(face, \
target_device=cv2.dnn.DNN_TARGET_MYRIAD)

所取得的 landmark points 值如下((x,y)共五組):

[[[[0.19104004]]  [[0.3540039 ]]  [[0.65234375]]  [[0.39941406]]  [[0.3803711 ]]  [[0.5830078 ]]  [[0.21154785]]  [[0.7446289 ]]  [[0.5371094 ]]  [[0.77246094]]]]

face–reidentification–retail–0095 臉部辨識模型

有了臉部的 landmarks 後,我們便可以校正該臉部的傾斜度並取得臉部的 embedded features。下方為取得 facial embedded features 的 python class 使用方法:

from libFaceDoor import ov_FaceRecognize
FACE_RECOG = ov_FaceRecognize( \
"models/face-reidentification-retail-0095.bin", \
"models/face-reidentification-retail-0095.xml")

#下方輸入的參數face為校正過的臉部區域圖檔
face_embs = FACE_RECOG.detect_face(face, \
target_device=cv2.dnn.DNN_TARGET_MYRIAD)

整合應用示範

最後,我們把下方列出的五個步驟整合在一起,組成一個完整的人臉辨識系統。記得嗎?步驟 4~5 已在先前的知識文件中介紹過,而 1~3 步驟的使用可參考本文的說明。

  1. face detect
  2. facial landmarks detect
  3. face alignment
  4. facial embedded features
  5. calculate distance of 2 embedded features

下方的影片示範了上述五個執行步驟:

取得人臉 –> 臉部特徵點定位 –> 臉部對齊(顯示於畫面中左上角)–> 臉部特徵向量取得 –> 計算人臉差異,取最小者。

左上角顯示的人臉為即時校正後、用以計算 facial embedded features 的人臉預覽,可以看出,手機上的人臉雖然隨時都在轉動,但經過臉部水平校正後,畫面左上角臉孔的雙眼角度仍然維持水平。

libFaceDoor 程式碼

import time, datetime
import imutils
import math
import cv2
import numpy as np

#--------------------------------------------------------#
# 1. wget --no-check-certificate https://download.01.org/opencv/2019/open_model_zoo/R1/models_bin/face-detection-adas-0001/FP16/face-detection-adas-0001.bin
# 2. wget --no-check-certificate https://download.01.org/opencv/2019/open_model_zoo/R1/models_bin/face-detection-adas-0001/FP16/face-detection-adas-0001.xml
#
class ov_FaceDect:
    def __init__(self, bin_path, xml_path):
        #load the model
        net = cv2.dnn.readNet(xml_path, bin_path)
        self.net = net

    def detect_face(self, frame, score=0.5, target_device=cv2.dnn.DNN_TARGET_MYRIAD):
        net = self.net
        # Specify target device.
        net.setPreferableTarget(target_device)

        #Prepare input blob and perform an inference.
        blob = cv2.dnn.blobFromImage(frame, size=(672, 384), ddepth=cv2.CV_8U)
        net.setInput(blob)
        out = net.forward()

        faces, scores = [], []
        for detection in out.reshape(-1, 7):
            confidence = float(detection[2])
            xmin = int(detection[3] * frame.shape[1])
            ymin = int(detection[4] * frame.shape[0])
            xmax = int(detection[5] * frame.shape[1])
            ymax = int(detection[6] * frame.shape[0])

            if confidence > score:
                faces.append([xmin,ymin,xmax-xmin,ymax-ymin])
                scores.append(confidence)

        return faces, scores

class ov_FaceRecognize:
    def __init__(self, bin_path, xml_path):
        #load the model
        net = cv2.dnn.readNet(xml_path, bin_path)
        self.net = net

    def detect_face(self, face_img, target_device=cv2.dnn.DNN_TARGET_MYRIAD):
        net = self.net
        # Specify target device.
        net.setPreferableTarget(target_device)

        #Prepare input blob and perform an inference.
        blob = cv2.dnn.blobFromImage(face_img, size=(128, 128), ddepth=cv2.CV_8U)
        net.setInput(blob)
        out = net.forward()

        return out

class ov_Face5Landmarks:
    def __init__(self, bin_path, xml_path):
        #load the model
        net = cv2.dnn.readNet(xml_path, bin_path)
        self.net = net

    def getLandmarks(self, face_img, target_device=cv2.dnn.DNN_TARGET_MYRIAD):
        net = self.net
        # Specify target device.
        net.setPreferableTarget(target_device)

        #Prepare input blob and perform an inference.
        try:
            blob = cv2.dnn.blobFromImage(face_img, size=(48, 48), ddepth=cv2.CV_8U)
            net.setInput(blob)
            out = net.forward()
        except:
            out = None

        return out

    def renderFace(self, im, landmarks, color=(0, 255, 0), radius=5):
        for p in landmarks:
            cv2.circle(im, (p[0], p[1]), radius, color, -1)

        return im

    def angle_2_points(self, p1, p2):
        r_angle = math.atan2(p1[1] - p2[1], p1[0] - p2[0])
        rotate_angle = r_angle * 180 / math.pi

        return rotate_angle

(本文經作者同意轉載自 CH.TSENG 部落格、原文連結;責任編輯:賴佩萱)

曾 成訓

Author: 曾 成訓

人到中年就像沒對準的描圖紙,一點一點的錯開,我只能當個Maker來使它復位。

Share This Post On

Submit a Comment

Your email address will not be published. Required fields are marked *