介紹
思路介紹
無論是基於視頻或者調用攝像頭來完成人臉識別,其實是一樣,通過使用opencv,來捕獲視頻或者攝像頭傳來的圖像,每隔若干幀取一幀做人臉識別,調用Dlib中的人臉檢測器來檢測人臉,並通過Dlib的人臉關鍵點預測器來獲得人臉的關鍵點,接下來使用Dlib的面部識別模型將獲得的68個關鍵點轉換成128D面部描述符,我們通過計算人臉的128D面部描述符與本地人臉庫(需要自己建立人臉庫)中的人臉128D面部描述符的歐氏距離,來判斷是否爲同一人,當距離小於特定閾值時,認定識別成功,打上標籤。
運行環境介紹
操作系統版本:Windows10
運行環境:python3.6+opencv4.1.2+dlib19.8.1
軟件:PyCharm
(注:這裏下載dlib包最好下載.whl文件,不需要安裝cmake以及boost這些麻煩的東西。因爲dilib包的沒有python3.7版的whl文件,所以建議使用python3.6)
附上opencv和dlib包鏈接:https://pan.baidu.com/s/1Z33r7SoD5Z0faH96wr7Ecw
提取碼:a8gl
模型介紹
這裏的人臉識別使用了Dlib已訓練成功的兩個模型–人臉關鍵點預測器和麪部識別模型。使用時需要加載模型,文件分別爲shape_predictor_68_face_landmarks.dat和dlib_face_recognition_resnet_model_v1.dat
模型文件下載地址 http://dlib.net/files/
人臉關鍵點預測器
Dlib中標記68個特徵點採用的是ERT算法,是一種基於迴歸樹的人臉對齊算法,這種方法通過建立一個級聯的殘差迴歸樹來使人臉形狀從當前形狀一步一步迴歸到真實形狀。每一個GBDT的每一個葉子節點上都存儲着一個殘差迴歸量,當輸入落到一個節點上時,就將殘差加到改輸入上,起到迴歸的目的,最終將所有殘差疊加在一起,就完成了人臉對齊的目的。
用法:
predictor_path = resources_path + "shape_predictor_68_face_landmarks.dat"
#加載人臉關鍵點預測器
predictor= dlib.shape_predictor(predictor_path)
#獲取面部關鍵點,gary爲灰度化的圖片
shape = predictor(gray,value)
人臉識別模型
Dlib中使用的人臉識別模型是基於深度殘差網絡,深度殘差網絡通過殘差塊來構建,它有效的解決了梯度消失以及梯度爆炸問題。當網絡深度很大時,普通網絡的誤差會增加,而深度殘差網絡卻有較小的誤差。這裏的人臉識別通過訓練深度殘差網絡將人臉的68個特徵關鍵點轉換成128D面部描述符,用於人臉的識別。
model_path = resources_path + "dlib_face_recognition_resnet_model_v1.dat"
#生成面部識別器
facerec = dlib.face_recognition_model_v1(model_path)
# 提取特徵-圖像中的68個關鍵點轉換爲128D面部描述符,其中同一人的圖片被映射到彼此附近,並且不同人的圖片被遠離地映射。
face_descriptor = facerec.compute_face_descriptor(frame, shape)
效果展示
識別過程
1、本地人臉庫建立
2 、視頻處理
通過opencv提供的VideoCapture()函數對視頻進行加載,並計算視頻的fps,以方便人臉標記之後的視頻的輸出。
3、加載模型
將已經訓練好的模型加載進來,將人臉關鍵點標記模型和麪部識別模型加載進來,以便後續使用。
4、人臉檢測
對視頻進行讀取,每隔6幀,取一幀進行人臉檢測,先將取得的照片進行灰度處理,然後進行人臉檢測,並繪畫人臉標記框進行展示,然後通過加載的人臉關鍵點標記模型識別圖像中的人臉關鍵點,並且標記。
5、人臉識別
將獲取的人臉關鍵點轉換成128D人臉描述符,將其與人臉庫中的128D面部描述符進行歐氏距離計算,當距離值小於某個閾值時,認爲人物匹配,識別成功,打上標籤。當無一小於該閾值,打上Unknown標籤
6、 保存人臉標記視頻
將整個處理過程進行輸出,將人臉標記過程保存下來。
代碼
建立本地人臉庫
(特別說明,這裏是分別對同一個人的多張圖片進行人臉檢測,通過ERT人臉對齊算法獲得其面部關鍵點,再將關鍵點轉換成128D面部描述符(特徵),將多個128D特徵向量進行取平均值,來降低誤差,最後將所有人的平均128D特徵向量存到一個文件中,作爲人臉特徵庫文件,用於人臉識別)
# 從人臉圖像文件中提取人臉特徵存入 CSV
# Features extraction from images and save into features_all.csv
# return_128d_features() 獲取某張圖像的128D特徵
# compute_the_mean() 計算128D特徵均值
import cv2
import os
import dlib
from skimage import io
import csv
import numpy as np
import pandas as pd
# @author 許翔
# @function 建立本地人臉庫
# @detail 收集每個人物的多張圖片,通過模型計算出人臉的128D面部描述符,計算每個人的特徵平均值,存入人臉特徵總文件
# @time 2020-2-13
# 要讀取人臉圖像文件的路徑
path_images_from_camera= "Resources/faceS/"
path_featureDB= "Resources/featureDB/"
path_featureMean="Resources/featureMean/"
resources_path = os.path.abspath(".")+"\Resources\\"
predictor_path = resources_path + "shape_predictor_68_face_landmarks.dat"
model_path = resources_path + "dlib_face_recognition_resnet_model_v1.dat"
print(model_path)
# Dlib 正向人臉檢測器
detector = dlib.get_frontal_face_detector()
# Dlib 人臉預測器
predictor = dlib.shape_predictor(predictor_path)
# Dlib 人臉識別模型
# Face recognition model, the object maps human faces into 128D vectors
face_rec = dlib.face_recognition_model_v1(model_path)
# 返回單張圖像的 128D 特徵
def return_128d_features(path_img):
img_rd = io.imread(path_img)
img_gray = cv2.cvtColor(img_rd, cv2.COLOR_BGR2RGB)
faces = detector(img_gray, 1)
print("%-40s %-20s" % ("檢測到人臉的圖像 / image with faces detected:", path_img), '\n')
# 刪除無人臉的圖片,只檢測有人臉的圖像
if len(faces) != 0:
shape = predictor(img_gray, faces[0])
face_descriptor = face_rec.compute_face_descriptor(img_gray, shape)
else:
face_descriptor = 0
print("there is no face")
return face_descriptor
# 將文件夾中照片特徵提取出來, 寫入 CSV
def write_into_csv(path_faces_personX, path_csv):
dir_pics = os.listdir(path_faces_personX)
with open(path_csv, "w", newline="") as csvfile:
writer = csv.writer(csvfile)
for i in range(len(dir_pics)):
# 調用return_128d_features()得到128d特徵
print("正在讀的人臉圖像:", path_faces_personX + "/" + dir_pics[i])
features_128d = return_128d_features(path_faces_personX + "/" + dir_pics[i])
# print(features_128d)
# 遇到沒有檢測出人臉的圖片跳過
if features_128d == 0:
i += 1
else:
writer.writerow(features_128d)
#對不同的人的特徵數據進行取均值並將結果存儲到all_feature。csv文件中
def computeMean(feature_path):
head=[]
for i in range(128):
fe="feature_"+str(i+1)
head.append(fe)
#需設置表頭,當表頭缺省時,會將第一行數據當作表頭
rdata = pd.read_csv(feature_path,names=head)
# meanValue=[]
# for fea in range(128):
# fe = "feature_" + str(fea + 1)
# feature=rdata[fe].mean();
# meanValue.append(feature)
meanValue=rdata.mean()
print(len(meanValue))
print(type(meanValue))
print(meanValue)
return meanValue
#讀取所有的人臉圖像的數據,將不同人的數據存在不同的csv文件中,以便取均值進行誤差降低
faces = os.listdir(path_images_from_camera)
i=0;
for person in faces:
i+=1
print(path_featureDB+ person + ".csv")
write_into_csv(path_images_from_camera+person, path_featureDB+ person+".csv")
print(i);
#計算各個特徵文件中的均值,並將值存在feature_all文件中
features=os.listdir(path_featureDB)
i=0;
with open(path_featureMean + "feature_all.csv", "w", newline="") as csvfile:
writer = csv.writer(csvfile)
for fea in features:
i+=1;
meanValue=computeMean(path_featureDB+fea)
writer.writerow(meanValue)
print(i)
人臉識別
(注意人臉匹配的閾值的選取,閾值的選取影響識別的效果)
import dlib,os,glob,time
import cv2
import numpy as np
import csv
import pandas as pd
# @author 許翔
# @function 利用opencv和dlib實現人臉識別
# @time 2020-2-13
# 聲明各個資源路徑
resources_path = os.path.abspath(".")+"\Resources\\"
predictor_path = resources_path + "shape_predictor_68_face_landmarks.dat"
model_path = resources_path + "dlib_face_recognition_resnet_model_v1.dat"
video_path =resources_path + "face_recognition.mp4"
resources_vResult=resources_path+"video\\"
faceDB_path="Resources/featureMean/"
# 加載視頻,加載失敗則退出
video = cv2.VideoCapture(video_path)
# 獲得視頻的fps
fps = video.get(cv2.CAP_PROP_FPS)
if not video.isOpened():
print("video is not opened successfully!")
exit(0)
# # 加載模型
#人臉特徵提取器
detector = dlib.get_frontal_face_detector()
#人臉關鍵點標記
predictor= dlib.shape_predictor(predictor_path)
#生成面部識別器
facerec = dlib.face_recognition_model_v1(model_path)
#定義視頻創建器,用於輸出視頻
video_writer = cv2.VideoWriter(resources_vResult+"result1.avi",
cv2.VideoWriter_fourcc(*'XVID'), int(fps),
(int(video.get(cv2.CAP_PROP_FRAME_WIDTH)), int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))))
#讀取本地人臉庫
head = []
for i in range(128):
fe = "feature_" + str(i + 1)
head.append(fe)
face_path=faceDB_path+"feature_all.csv"
face_feature=pd.read_csv(face_path,names=head)
print(face_feature.shape)
face_feature_array=np.array(face_feature)
print(face_feature_array.shape)
face_list=["Chandler","Joey","Monica","phoebe","Rachel","Ross"]
# 創建窗口
cv2.namedWindow("Face Recognition", cv2.WINDOW_KEEPRATIO)
cv2.resizeWindow("Face Recognition", 720,576)
#計算128D描述符的歐式距離
def compute_dst(feature_1,feature_2):
feature_1 = np.array(feature_1)
feature_2 = np.array(feature_2)
dist = np.linalg.norm(feature_1 - feature_2)
return dist
descriptors = []
faces = []
# 處理視頻,按幀處理
ret,frame = video.read()
flag = True # 標記是否是第一次迭代
i = 0 # 記錄當前迭代到的幀位置
while ret:
if i % 6== 0: # 每6幀截取一幀
# 轉爲灰度圖像處理
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
dets = detector(gray, 1) # 檢測幀圖像中的人臉
# for i in range(len(dets)):
# landmarks = np.matrix([[p.x, p.y] for p in predictor(gray,dets[i]).parts()])
# 處理檢測到的每一張人臉
if len(dets)>0:
for index,value in enumerate(dets):
#獲取面部關鍵點
shape = predictor(gray,value)
#pos = (value[0, 0], value[0, 1])
#標記人臉
cv2.rectangle(frame, (value.left(), value.top()), (value.right(), value.bottom()), (0, 255, 0), 2)
#進行人臉識別並打上姓名標籤
# 提取特徵-圖像中的68個關鍵點轉換爲128D面部描述符,其中同一人的圖片被映射到彼此附近,並且不同人的圖片被遠離地映射。
face_descriptor = facerec.compute_face_descriptor(frame, shape)
v = np.array(face_descriptor)
print(v.shape)
l = len(descriptors)
Flen=len(face_list)
flag=0
# 人臉匹配,距離小於閾值,表示識別成功,打上標籤
for j in range(Flen):
if(compute_dst(v,face_feature_array[j])<0.56):
flag=1
cv2.putText(frame,face_list[j],(value.left(), value.top()),cv2.FONT_HERSHEY_COMPLEX,0.8, (0, 255, 255), 1, cv2.LINE_AA)
break
if(flag==0):
cv2.putText(frame,"Unknonw", (value.left(), value.top()), cv2.FONT_HERSHEY_COMPLEX, 0.8, (0, 255, 255), 1,
cv2.LINE_AA)
#標記關鍵點
for pti,pt in enumerate(shape.parts()):
pos=(pt.x,pt.y)
cv2.circle(frame, pos, 1, color=(0, 255, 0))
faces.append(frame)
cv2.imshow("Face Recognition", frame) # 在窗口中顯示
exitKey= cv2.waitKey(1)
if exitKey == 27:
video.release()
video_writer.release()
cv2.destroyWindow("Face Recognition")
break
#逐幀輸出圖像
video_writer.write(frame)
ret,frame = video.read()
i += 1
print(len(descriptors)) # 輸出不同的人臉數
print(len(faces)) #輸出的照片數
# 將不同的比較清晰的人臉照片輸出到本地
j = 1
for fc in faces:
cv2.imwrite(resources_path + "\pictures\\" + str(j) +".jpg", fc)
j += 1