1. 程式人生 > 其它 >基於深度學習的人臉識別與管理系統(UI介面增強版,Python程式碼)

基於深度學習的人臉識別與管理系統(UI介面增強版,Python程式碼)

摘要:人臉檢測與識別是機器視覺領域最熱門的研究方向之一,本文詳細介紹博主自主設計的一款基於深度學習的人臉識別與管理系統。博文給出人臉識別實現原理的同時,給出Python的人臉識別實現程式碼以及PyQt設計的UI介面。系統實現了集識別人臉、錄入人臉、管理人臉在內的多項功能:包括通過選擇人臉圖片、視訊、攝像頭進行已錄入人臉的實時識別;可通過圖片和攝像頭檢測人臉並錄入新的人臉;通過系統管理和更新人臉資料等功能,檢測速度快、識別精度較高。博文提供了完整的Python程式碼和使用教程,適合新入門的朋友參考,完整程式碼資原始檔請轉至文末的下載連結。本博文目錄如下:

目錄

➷點選跳轉至文末所有涉及的完整程式碼檔案下載頁☇

完整資源下載連結https://mianbaoduo.com/o/bread/mbd-YpmYlZtw

程式碼介紹及演示視訊連結:https://www.bilibili.com/video/BV1XB4y1U73S(正在更新中,歡迎關注博主B站視訊)


前言

        近年來,人臉識別的技術愈發成熟,在大型資料集上的訓練測試結果已超過人類,其應用也日益廣泛,譬如刷臉支付、安防偵破、出入口控制、網際網路服務等。人臉識別(Face Recognition)是一種通過獲取人面部的特徵資訊進行身份確認的技術,類似已用於身份識別的人體的其他生物特徵(如虹膜、指紋等),人臉具備唯一性、一致性和高度的不可複製性,為身份識別提供了穩定的條件。人臉識別系統是博主一直想做的一個專案,通過人臉面部資訊識別可以進行很多有趣的設計,如面部解鎖、考勤打卡等。

        前面博主撰寫了人臉性別識別系統表情識別系統等,其實是人臉屬性識別的一種,即根據人臉面部影象中的相關特徵判斷其性別或表情屬性,該任務本身也同樣具有較強的現實意義。這篇博文則回到人臉識別的任務本身,採用深度學習的方法對人臉特徵進行提取,計算其與已存在的人臉特徵的相似度,判讀其是否屬於庫中的某一人臉,達到身份識別的目的。不過我希望在實現的基礎上,能多增加一些可操作性,因此儘可能設計一個功能完善的人臉識別系統。

        查閱網上資料發現,研究和分享人臉識別技術和程式碼的大有人在,主要是做一些簡單易行的小Demo等,大家可自行查閱參考。這裡博主分享一個自主設計的人臉識別專案,包括識別人臉、錄入人臉、管理人臉在內的多項功能,以下是介面的截圖,供大家參考學習了:

        檢測識別人臉時的介面截圖(點選圖片可放大)如下圖,可識別畫面中存在的多個人臉,也可開啟攝像頭或視訊檢測,以及人臉錄入管理等功能:

         詳細的功能演示效果參見博主的B站視訊或下一節的動圖演示,覺得不錯的朋友敬請點贊、關注加收藏!系統UI介面的設計工作量較大,介面美化更需仔細雕琢,大家有任何建議或意見和可在下方評論交流。


1. 效果演示

(一)選擇人臉圖片識別

        在系統的功能選項按鈕中選擇“識別人臉”,點選下方的圖片選擇按鈕圖示選擇圖片後,在主顯區域標記所有人臉識別的結果,並被逐條記錄在表格中。本功能的介面展示如下圖所示:

(二)人臉視訊識別效果展示

        很多時候我們需要識別一段視訊中的人臉資訊,這裡設計了視訊選擇功能。同樣的在“識別人臉”功能選項下,點選視訊按鈕可選擇待檢測的視訊,系統會自動解析視訊逐幀識別人臉,並將結果記錄在右下角表格中,效果如下圖所示:
        

(三)攝像頭檢測效果展示

        在真實場景中,我們往往利用裝置攝像頭獲取實時畫面,同時需要對畫面中的人臉進行識別,同樣可以在“識別人臉”功能選項下選擇此項功能。如下圖所示,點選攝像頭按鈕後系統進入準備狀態,系統顯示實時畫面並開始檢測畫面中的人臉,識別結果展示如下圖:

(四)錄入人臉效果展示

        當出現一個新的人臉需要錄入時,點選“錄入人臉”功能選項按鈕,此時底部功能介面切換至錄入功能,首先輸入人臉名字點選“新建”後可通過選擇人臉圖片或開啟攝像頭進行畫面捕捉,系統檢測到人臉後可選擇“取圖”,系統得到捕獲的人臉區域圖片,最後點選“錄入”按鈕,則提取所有人臉圖片特徵並寫入系統庫中,演示效果如下:

(五)管理人臉效果展示

        對於已經存在的人臉資料可選擇“管理人臉”功能選項按鈕,切換至管理介面,選擇表格中要刪除或更新的人臉資料欄,點選確定後系統自動更新人臉資料庫,該功能展示如下圖:

         在識別某張特定人臉前,應該先在系統庫中錄入人臉資訊,即送入一張人臉影象供系統提取特徵,此過程可選擇圖片也可開啟攝像頭實時獲取。至此係統的演示完畢,其實除了動圖中演示的功能,當然還有許多細節功能無法一一演示,讀者可以自行測試。


2. 人臉識別原理

        如今機器學習、神經網路方法廣泛應用於人臉識別領域,而後深度學習廣泛應用於各種目標檢測領域,2015年,Google團隊的FaceNet在LFW資料集上得平均準確率達到了99.63%,基於深度學習的人臉識別的準確率已經高於人類本身,深度學習在人臉識別領域基本佔據了統治地位。

        Dlib是一個包含機器學習演算法的C++開源工具包,目前已經被廣泛的用在行業和學術領域,包括機器人,嵌入式裝置,行動電話和大型高效能運算環境。作為人臉識別工具之一,Dlib在影象處理及人臉面部特徵處理、分類、識別等方面具有計算簡單、較容易實現的優點。

        Dlib在人臉識別上的應用:(1)接受影象並將其載入到一個畫素陣列中進行處理;(2)使用區域性二進位制模式的人臉描述生成新的影象;(3)根據Dlib庫中的scan_image_boxes等函式寫入讀取到的圖片,進而計算人臉之間的特徵向量;(4)與人臉資料庫中的特徵向量進行對比並利用全域性函式threshold_image計算閾值,完成人臉識別[1]

        Dlib可通過Python呼叫,實現對影象預處理、提取特徵向量、與人臉資料庫中資料進行校驗進而判別人物身份的流程。這裡我們的人臉識別的過程有人臉檢測(Face Detection)、人臉對齊(Face Alignment)、人臉表示(Face Representation)和人臉匹配(Face Matching),示意圖如下圖所示:

        (1)人臉檢測(Face Detection):首先利用Dlib的get_frontal_face_detector方法檢測人臉區域並輸出人臉矩形的四個座標點。呼叫get_frontal_face_detector會返回 dlib 庫中包含的預訓練方向梯度直方圖 (HOG)結合線性支援向量機(SVM)的人臉檢測器,該檢測器快速高效。由於方向梯度直方圖 (HOG) 描述符的工作原理,它對影象幾何的和光學的形變都能保持很好的不變性。

        (2)人臉對齊(Face Alignment):這是人臉識別系統中的一種標準操作,即從人臉區域中檢測到人臉特徵點,並以特徵點為依據對人臉進行歸一化操作,使人臉區域的尺度和角度一致,方便特徵提取與人臉匹配。一般通過旋轉、平移與縮放將目標人臉區域放置在影象特定位置。這樣做可以減小需要處理的人臉影象在空間分佈上的差異。這裡我們使用的是基於迴歸樹的人臉對齊演算法[2],該演算法是Vahid Kazemi 和 Josephine Sullivan在CVPR2014上發表的One Millisecond Face Alignment with an Ensemble of Regression Trees演算法(以下簡稱GBDT),這種方法通過建立一個級聯的殘差迴歸樹(GBDT)來使人臉形狀從當前形狀一步一步迴歸到真實形狀。每一個GBDT的每一個葉子節點上都儲存著一個殘差迴歸量,當輸入落到一個節點上時,就將殘差加到改輸入上,起到迴歸的目的,最終將所有殘差疊加在一起,就完成了人臉對齊的目的。此處我們使用shape_predictor方法載入shape_predictor_68_face_landmarks.dat模型實現。

        (3)人臉表示(Face Representation):這一步我們從歸一化的人臉區域中進行面部特徵提取,採用深度神經網路方法得到具有128個特徵的特徵向量。這裡利用Dlib中的殘差學習深度神經網路(ResNet)[3]為待識別人臉建立128維特徵向量。人臉的特徵表示,最理想的情況是不同人臉的照片提取出的特徵向量差異較大,而同一人臉在不同照片中可以提取出相似度高的特徵向量。此處我們使用的是dlib庫中的face_recognition_model_v1方法,使用預訓練的dlib_face_recognition_resnet_model_v1.dat模型。

        (4)人臉匹配(Face Matching):將待識別圖片中提取的特徵向量與比對圖中的進行對比,通過評估方法計算兩幅照片的相似度。可以根據相似得分,將得分高的判斷為同一人,得分低的判斷為不同人。這裡我們使用歐式距離計算,兩個人臉特徵向量的歐式距離越小,則兩張人臉越相似,若人臉影象與待識別人像之間的歐式距離小於設定的閾值(這裡我設定為0.4)時,則判定為同一人。

\[Dis = \sqrt{\left(x_{1}-y_{1}\right)^{2}+\left(x_{2}-y_{2}\right)^{2}+\ldots+\left(x_{n}-y_{n}\right)^{2}} \]

3. 程式碼實現

        原理介紹完畢,我們開始按照以上的步驟實現人臉識別過程。首先是匯入幾個需要用到的Python依賴包,我們使用的Python版本是3.8,其程式碼如下:

import dlib
import csv
import os
import cv2
import numpy as np

        這裡面值得要說的dlib這個依賴,是後面人臉識別演算法需要用到的工具庫,它的安裝其實很簡單,並不需要像網上說的安裝Visual Studio 2015等軟體(網上安裝問題主要是dlib沒有編譯的安裝包)。這裡我將所有需要用到的依賴包都打包在了mylib資料夾中,並將依賴的版本號寫入了requirements.txt檔案中,如下圖所示:

        在安裝有Python3.8的情況下,首先切換cmd的目錄到mylib所在資料夾(requirements.txt也放在資料夾下),然後輸入以下程式碼就可以完成依賴安裝了。這樣省去了版本不一致帶來的出錯或諸多麻煩,dlib也是優雅地安裝好了。安裝過程也可參考博主的B站視訊,完整資料夾中有一鍵安裝的bat檔案可以幫助安裝。

pip install -r requirements.txt --no-index --find-links=./mylib/

        配置好環境和匯入依賴後,可以正式開始程式碼的介紹了。首先我們載入dlib中的幾個模型方法,其實現程式碼如下:

path_face_dir = "./data/database_faces/"
person_list = os.listdir(path_face_dir)
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('./data/data_dlib/shape_predictor_68_face_landmarks.dat')
face_reco_model = dlib.face_recognition_model_v1("./data/data_dlib"
                                                 "/dlib_face_recognition_resnet_model_v1.dat")

        如上一章節介紹的,detector、predictor、face_reco_model分別是人臉檢測器、人臉對齊方法、人臉表示(特徵提取)模型。接下來我們寫一個從圖片中提取人臉特徵的函式extract_features,該函式讀取圖片利用人臉檢測器獲取人臉位置,通過深度卷積神經網路Resnet進行特徵提取,最終得到128維的特徵向量。其程式碼如下:

def extract_features(path_img):
    img_rd = cv2.imdecode(np.fromfile(path_img, dtype=np.uint8), -1)
    faces = detector(img_rd, 1)
    if len(faces) != 0:
        shape = predictor(img_rd, faces[0])
        face_descriptor = face_reco_model.compute_face_descriptor(img_rd, shape)
    else:
        face_descriptor = 0
    return face_descriptor

        我們將每個人臉的影象各建一個資料夾儲存,將資料夾的名字作為該人臉的命名標識,如下圖所示。每個資料夾下可放置一張或多張同類人臉影象,用以後面進行人臉特徵提取,可自行收集人臉影象放置在對應資料夾下:

        接下來就可以進行特徵提取了,這樣遍歷上面目錄中的每類人臉資料夾下的所有影象並提取特徵,然後取均值儲存在csv檔案中即可完成特徵提取並記錄,該程式碼實現如下:

with open("./features_all_test.csv", "w", newline="") as csvfile:
    writer = csv.writer(csvfile)
    for person in person_list:
        features_list = []
        photos_list = os.listdir(path_face_dir + "/" + person)
        if photos_list:
            for photo in photos_list:
                features_128D = extract_features(path_face_dir + "/" + person + "/" + photo)
                print("圖片" + photo + "已錄入!")

                if features_128D == 0:
                    continue
                else:
                    features_list.append(features_128D)
        if features_list:
            features_mean = np.array(features_list).mean(axis=0)
        else:
            features_mean = np.zeros(128, dtype=int, order='C')
        str_face = [person]
        str_face.extend(list(features_mean))
        writer.writerow(str_face)
        print("已完成人臉錄入!")

        以上程式碼執行下來,提取到的人臉特徵資訊被寫入csv檔案中,部分資訊如下圖所示,每行的128個特徵表示一個人臉資訊,第一列為該人臉的名字:

        接下來利用提取到的特徵進行人臉匹配,讀取一張新的人臉圖片然後判斷其屬於庫中的那張人臉。同樣的新建一個py檔案,首先匯入需要的依賴包,程式碼如下:

import os
import time
import warnings
import cv2
import dlib
import numpy as np
import pandas as pd
from PIL import Image, ImageDraw, ImageFont

        由於後面需要在影象中顯示中文,所以這裡先利用PIL匯入中文字型記為fontC,然後還是匯入需要用到的三個模型,程式碼如下:

fontC = ImageFont.truetype("./FaceRecUI/Font/platech.ttf", 14, 0)
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('./data/data_dlib/shape_predictor_68_face_landmarks.dat')
face_reco_model = dlib.face_recognition_model_v1("./data/data_dlib"
                                                 "/dlib_face_recognition_resnet_model_v1.dat")

        在匹配的過程中需要計算兩個人臉特徵向量間的距離(相似度),這裡先定義一個計算特徵向量的歐氏距離函式euclidean_distance,返回兩個向量的距離值:

def euclidean_distance(feature_1, feature_2):
    # 計算兩個128D向量間的歐式距離
    feature_1 = np.array(feature_1)
    feature_2 = np.array(feature_2)
    dist = np.sqrt(np.sum(np.square(feature_1 - feature_2)))
    return dist

        另外,檢測和識別出的人臉結果需要標記在影象中,所以這裡定義一個新增標記的函式drawRectBox,即利用OpenCV和PIL在人臉位置處繪製標記框和文字,其程式碼如下:

def drawRectBox(img, rect_pos, addText):
    cv2.rectangle(img, (int(round(rect_pos[0])), int(round(rect_pos[1]))),
                  (int(round(rect_pos[2])), int(round(rect_pos[3]))),
                  (0, 0, 255), 2)
    cv2.rectangle(img, (int(rect_pos[0] - 1), int(rect_pos[1]) - 16), (int(rect_pos[0] + 75), int(rect_pos[1])), (0, 0, 255), -1,
                  cv2.LINE_AA)
    img = Image.fromarray(img)
    draw = ImageDraw.Draw(img)
    draw.text((int(rect_pos[0] + 1), int(rect_pos[1] - 16)), addText, (255, 255, 255), font=fontC)
    image_x = np.array(img)
    return image_x

        函式準備就緒,開始主函式部分,首先讀取一張人臉圖片:

if __name__ == '__main__':
    filePath = "./FaceRecUI/test_img/樸信惠-1.jpeg"
    img_rd = cv2.imdecode(np.fromfile(filePath, dtype=np.uint8), -1)

        從csv檔案讀取所有的人臉特徵,將其儲存在變數face_feature_exist中,用於後面的特徵計算。這裡讀取時採用逐行遍歷csv檔案的方式,將每行的特徵名和128維特徵向量儲存出來,空資料的標記為未知人臉。該部分程式碼如下:

face_feature_exist = []
    face_name_exist = []
    flag = False

    # 讀取已存入的人臉特徵資訊
    if os.path.exists("./features_all_test.csv"):
        path_features_known_csv = "./features_all_test.csv"
        csv_rd = pd.read_csv(path_features_known_csv, header=None, encoding='gb2312')
        for i in range(csv_rd.shape[0]):
            features_someone_arr = []
            for j in range(1, 129):
                if csv_rd.iloc[i][j] == '':
                    features_someone_arr.append('0')
                else:
                    features_someone_arr.append(csv_rd.iloc[i][j])
            face_feature_exist.append(features_someone_arr)

            if csv_rd.iloc[i][0] == '':
                face_name_exist.append("未知人臉")
            else:
                face_name_exist.append(csv_rd.iloc[i][0])

        exist_flag = True
    else:
        exist_flag = False

        我們使用人臉檢測器獲取人臉位置,剪切出人臉區域;然後對人臉區域進行特徵提取並將其與庫中的特徵進行比較,逐個計算歐幾里得聚類,找出與之距離最小的庫人臉;將最小距離與設定的閾值(0.4)進行比較,若小於0.4表示與該庫人臉匹配,否則視為未知人臉。此流程的程式碼如下:

# 使用人臉檢測器進行人臉檢測
    image = img_rd.copy()
    faces = detector(image, 0)

    if len(faces) > 0:
        # 矩形框 / Show the ROI of faces
        face_feature_list = []
        face_name_list = []
        face_position_list = []
        start_time = time.time()

        for k, d in enumerate(faces):
            # 計算矩形框大小 / Compute the size of rectangle box
            height = (d.bottom() - d.top())
            width = (d.right() - d.left())
            hh = int(height / 2)
            ww = int(width / 2)

            y2 = d.right() + ww
            x2 = d.bottom() + hh
            y1 = d.left() - ww
            x1 = d.top() - hh
            # 判斷人臉區域是否超出畫面範圍
            if y2 > img_rd.shape[1]:
                y2 = img_rd.shape[1]
            elif x2 > img_rd.shape[0]:
                x2 = img_rd.shape[0]
            elif y1 < 0:
                y1 = 0
            elif x1 < 0:
                x1 = 0

            # 剪切出人臉
            crop_face = img_rd[x1: x2, y1: y2]
            # 獲取人臉特徵
            shape = predictor(img_rd, d)
            face_feature_list.append(face_reco_model.compute_face_descriptor(img_rd, shape))

            current_face = crop_face

        if exist_flag:  # 獲取已存在人臉的特徵
            for k in range(len(faces)):
                # 初始化
                face_name_list.append("未知人臉")

                # 每個捕獲人臉的名字座標
                face_position_list.append(tuple(
                    [faces[k].left(), int(faces[k].bottom() + (faces[k].bottom() - faces[k].top()) / 4)]))

                # 對於某張人臉,遍歷所有儲存的人臉特徵
                current_distance_list = []
                for i in range(len(face_feature_exist)):
                    # 如果 person_X 資料不為空
                    if str(face_feature_exist[i][0]) != '0.0':
                        e_distance_tmp = euclidean_distance(face_feature_list[k],
                                                            face_feature_exist[i])
                        current_distance_list.append(e_distance_tmp)
                    else:
                        # 空資料 person_X
                        current_distance_list.append(999999999)

                # 尋找出最小的歐式距離匹配
                min_dis = min(current_distance_list)
                similar_person_num = current_distance_list.index(min_dis)
                if min_dis < 0.4:
                    face_name_list[k] = face_name_exist[similar_person_num]

        end_time = time.time()
        fps_rec = int(1.0 / round((end_time - start_time), 3))

        for k, d in enumerate(faces):
            # 計算矩形框大小 / Compute the size of rectangle box
            height = (d.bottom() - d.top())
            width = (d.right() - d.left())
            hh = int(height / 2)
            ww = int(width / 2)
            rect = (d.left(), d.top(), d.right(), d.bottom())
            image = drawRectBox(image, rect, face_name_list[k])

        cv2.imshow('Stream', image)
        c = cv2.waitKey(0) & 0xff

        除了以上介紹的人臉匹配流程,這部分程式碼中還給出了識別出人臉後的標記過程。如果檢測出人臉,則根據人臉的座標未知繪製矩形框,根據識別結果在矩形框上方新增識別結果的文字,最後顯示標記影象在視窗中。其執行結果如下圖所示:

        有了以上實現的基礎,我們可以把這部分功能進行改進,新增進UI介面中方便我們選擇影象和管理人臉庫。開啟QtDesigner軟體,拖動以下控制元件至主視窗中,調整介面樣式和控制元件放置,人臉識別系統的介面設計如下圖所示:

        控制元件介面部分設計好,接下來利用PyUIC工具將.ui檔案轉化為.py程式碼檔案,通過呼叫介面部分的程式碼同時加入對應的邏輯處理程式碼。博主對其中的UI功能進行了詳細測試,最終開發出一版流暢得到清新介面,就是博文演示部分的展示,完整的UI介面、測試圖片視訊、程式碼檔案,以及Python離線依賴包(方便安裝執行,也可自行配置環境),均已打包上傳,感興趣的朋友可以通過下載連結獲取。


下載連結

        若您想獲得博文中涉及的實現完整全部程式檔案(包括測試圖片、視訊,py, UI檔案等,如下圖),這裡已打包上傳至博主的麵包多平臺和CSDN下載資源。本資源已上傳至麵包多網站和CSDN下載資源頻道,可以點選以下連結獲取,已將所有涉及的檔案同時打包到裡面,點選即可執行,完整檔案截圖如下:

    在資料夾下的資源顯示如下,其中包含了Python的離線依賴包,讀者可在正確安裝Anaconda和Pycharm軟體後,點選bat檔案進行安裝,詳細演示也可見本人B站視訊。

注意:本資源已經過除錯通過,下載後可通過Pycharm執行;執行介面的主程式為runMain.py,測試圖片指令碼可執行testFaceDemo.py,測試人臉特徵提取可執行testGetFeatures.py。為確保程式順利執行,請配置Python版本:3.8,請勿使用其他版本,詳見requirements.txt檔案,如下:➷➷➷

blurhash == 1.1.4
boost == 0.1
certifi == 2021.10.8
charset-normalizer == 2.0.12
cmake == 3.22.2
decorator == 5.1.1
dlib == 19.19.0
greenlet == 1.1.2
idna == 3.3
joblib == 1.1.0
Mastodon.py == 1.5.1
numpy == 1.19.5
opencv-python == 4.1.2.30
pandas == 1.2.5
Pillow == 8.3.0
pyqt5 == 5.15.5
python-dateutil == 2.8.2
python-magic == 0.4.25
pytz == 2021.3
requests == 2.27.1
scikit-learn == 1.0.2
scipy == 1.8.0
six == 1.16.0
SQLAlchemy == 1.4.31
threadpoolctl == 3.1.0
urllib3 == 1.26.8
wincertstore == 0.2

完整資源下載連結1https://mianbaoduo.com/o/bread/mbd-YpmYlZtw

完整資源下載連結2博主在CSDN下載頻道的完整資源下載頁


結束語

        由於博主能力有限,博文中提及的方法即使經過試驗,也難免會有疏漏之處。希望您能熱心指出其中的錯誤,以便下次修改時能以一個更完美更嚴謹的樣子,呈現在大家面前。同時如果有更好的實現方法也請您不吝賜教。


  1. 徐怡彤, 王梅霞, 張培培. Dlib人臉識別在教師考勤中的應用[J]. 電腦程式設計技巧與維護, 2022(2):3. ↩︎

  2. Kazemi V, Sullivan J. One millisecond face alignment with an ensemble of regression trees[C]//Proceedings of the IEEE conference on computer vision and pattern recognition. 2014: 1867-1874. ↩︎

  3. He K, Zhang X, Ren S, et al. Deep residual learning for image recognition[C]//Proceedings of the IEEE conference on computer vision and pattern recognition. 2016: 770-778. ↩︎