
摘要:人臉檢測與識別是機器視覺領域最熱門的研究方向之一,本文詳細介紹博主自主設計的一款基於深度學習的人臉識別與管理系統。博文給出人臉識別實現原理的同時,給出Python的人臉識別實現代碼以及PyQt設計的UI界面。系統實現了集識別人臉、錄入人臉、管理人臉在內的多項功能:包括通過選擇人臉圖片、視頻、攝像頭進行已錄入人臉的實時識別;可通過圖片和攝像頭檢測人臉並錄入新的人臉;通過系統管理和更新人臉數據等功能,檢測速度快、識別精度較高。博文提供了完整的Python代碼和使用教程,適合新入門的朋友參考,完整代碼資源文件請轉至文末的下載鏈接。本博文目錄如下:
完整資源下載鏈接:https://mbd.pub/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)時,則判定為同一人。
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
完整資源下載鏈接1:https://mbd.pub/o/bread/mbd-YpmYlZtw
完整資源下載鏈接2:博主在CSDN下載頻道的完整資源下載頁
結束語
由於博主能力有限,博文中提及的方法即使經過試驗,也難免會有疏漏之處。希望您能熱心指出其中的錯誤,以便下次修改時能以一個更完美更嚴謹的樣子,呈現在大家面前。同時如果有更好的實現方法也請您不吝賜教。
徐怡彤, 王梅霞, 張培培. Dlib人臉識別在教師考勤中的應用[J]. 電腦編程技巧與維護, 2022(2):3. ↩︎
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. ↩︎
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. ↩︎