本教程的人臉識別是使用的是insightface庫進行開發的,該庫使用的框架爲ONNX,使用的是Anaconda環境。
代碼地址:點擊下載
安裝環境¶
- 安裝insightface ,安裝命令如下。
python -m pip install Cython insightface==0.6.2 -i https://mirror.baidu.com/pypi/simple
- 安裝onnxruntime-gpu,命令如下。
python -m pip install onnxruntime-gpu -i https://mirror.baidu.com/pypi/simple
- 安裝cudatoolkit,命令如下。
conda install cudatoolkit=11.3 cudnn --channel https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/Paddle/
人臉識別和人臉註冊¶
爲了方便,寫一個類完成所有的識別流程,開始編寫人臉識別和人臉註冊工具類,使用insightface.app.FaceAnalysis()可以獲取模型對象,這裏包含了三個模型,首先是人臉檢測模型,然後是人臉特徵提取模型,和最後的性別年齡識別模型。使用model.prepare()可以配置ctx_id指定使用哪一塊GPU,如果是負數則是使用CPU執行預測,det_thresh配置的是人臉檢測的閾值。load_faces()函數是加載人臉庫中的人臉,用於之後的人臉識別對比。
import os
import cv2
import insightface
import numpy as np
from sklearn import preprocessing
class FaceRecognition:
def __init__(self, gpu_id=0, face_db='face_db', threshold=1.24, det_thresh=0.50, det_size=(640, 640)):
"""
人臉識別工具類
:param gpu_id: 正數爲GPU的ID,負數爲使用CPU
:param face_db: 人臉庫文件夾
:param threshold: 人臉識別閾值
:param det_thresh: 檢測閾值
:param det_size: 檢測模型圖片大小
"""
self.gpu_id = gpu_id
self.face_db = face_db
self.threshold = threshold
self.det_thresh = det_thresh
self.det_size = det_size
# 加載人臉識別模型,當allowed_modules=['detection', 'recognition']時,只單純檢測和識別
self.model = insightface.app.FaceAnalysis(root='./',
allowed_modules=None,
providers=['CUDAExecutionProvider'])
self.model.prepare(ctx_id=self.gpu_id, det_thresh=self.det_thresh, det_size=self.det_size)
# 人臉庫的人臉特徵
self.faces_embedding = list()
# 加載人臉庫中的人臉
self.load_faces(self.face_db)
# 加載人臉庫中的人臉
def load_faces(self, face_db_path):
if not os.path.exists(face_db_path):
os.makedirs(face_db_path)
for root, dirs, files in os.walk(face_db_path):
for file in files:
input_image = cv2.imdecode(np.fromfile(os.path.join(root, file), dtype=np.uint8), 1)
user_name = file.split(".")[0]
face = self.model.get(input_image)[0]
embedding = np.array(face.embedding).reshape((1, -1))
embedding = preprocessing.normalize(embedding)
self.faces_embedding.append({
"user_name": user_name,
"feature": embedding
})
接下來編寫recognition()函數實現人臉識別,首先獲取每張人臉的特徵embedding,其中使用人臉識別的就是通過歐氏距離來對比人臉庫中的人臉特徵,默認如何它們的歐氏距離小於1.24,我們就可以認爲他們是同一個人。
def recognition(self, image):
faces = self.model.get(image)
results = list()
for face in faces:
# 開始人臉識別
embedding = np.array(face.embedding).reshape((1, -1))
embedding = preprocessing.normalize(embedding)
user_name = "unknown"
for com_face in self.faces_embedding:
r = self.feature_compare(embedding, com_face["feature"], self.threshold)
if r:
user_name = com_face["user_name"]
results.append(user_name)
return results
上面使用到的歐氏距離計算方式如下。
@staticmethod
def feature_compare(feature1, feature2, threshold):
diff = np.subtract(feature1, feature2)
dist = np.sum(np.square(diff), 1)
if dist < threshold:
return True
else:
return False
人臉註冊方式如下,通過傳入一張照片,首先要判斷照片中的人臉只有一張,然後開始提取該人臉的特徵值,再次比較要註冊的人臉是否已經存在人臉庫中了,否之就包人臉特徵添加到人臉庫中並保存圖片到本地。通過命名包只包含一個人臉的圖片放在face_db文件夾中也可以實現。
def register(self, image, user_name):
faces = self.model.get(image)
if len(faces) != 1:
return '圖片檢測不到人臉'
# 判斷人臉是否存在
embedding = np.array(faces[0].embedding).reshape((1, -1))
embedding = preprocessing.normalize(embedding)
is_exits = False
for com_face in self.faces_embedding:
r = self.feature_compare(embedding, com_face["feature"], self.threshold)
if r:
is_exits = True
if is_exits:
return '該用戶已存在'
# 符合註冊條件保存圖片,同時把特徵添加到人臉特徵庫中
cv2.imencode('.png', image)[1].tofile(os.path.join(self.face_db, '%s.png' % user_name))
self.faces_embedding.append({
"user_name": user_name,
"feature": embedding
})
return "success"
這裏還提供了通用的人臉檢測函數,通過調用detect()函數可以獲取圖像中每張人臉框座標bbox,人臉五個關鍵點kps,人臉3D關鍵點landmark_3d_68,人臉2D關鍵點landmark_2d_106,人臉姿態pose,性別gender,年齡age,人臉特徵embedding。
def detect(self, image):
faces = self.model.get(image)
results = list()
for face in faces:
result = dict()
# 獲取人臉屬性
result["bbox"] = np.array(face.bbox).astype(np.int32).tolist()
result["kps"] = np.array(face.kps).astype(np.int32).tolist()
result["landmark_3d_68"] = np.array(face.landmark_3d_68).astype(np.int32).tolist()
result["landmark_2d_106"] = np.array(face.landmark_2d_106).astype(np.int32).tolist()
result["pose"] = np.array(face.pose).astype(np.int32).tolist()
result["age"] = face.age
gender = '男'
if face.gender == 0:
gender = '女'
result["gender"] = gender
# 開始人臉識別
embedding = np.array(face.embedding).reshape((1, -1))
embedding = preprocessing.normalize(embedding)
result["embedding"] = embedding
results.append(result)
return results
使用¶
全部功能都實現了,使用如下,首先是進行人臉註冊。
if __name__ == '__main__':
img = cv2.imdecode(np.fromfile('迪麗熱巴.jpg', dtype=np.uint8), -1)
face_recognitio = FaceRecognition()
# 人臉註冊
result = face_recognitio.register(img, user_name='迪麗熱巴')
print(result)
人臉識別,通過傳入一張圖片,可以輸出每張人臉對應的用戶名。
if __name__ == '__main__':
img = cv2.imdecode(np.fromfile('迪麗熱巴.jpg', dtype=np.uint8), -1)
face_recognitio = FaceRecognition()
results = face_recognitio.recognition(img)
for result in results:
print("識別結果:{}".format(result))
人臉通用檢測,可以獲取每張人臉的各種屬性。
if __name__ == '__main__':
img = cv2.imdecode(np.fromfile('迪麗熱巴.jpg', dtype=np.uint8), -1)
face_recognitio = FaceRecognition()
results = face_recognitio.detect(img)
for result in results:
print('人臉框座標:{}'.format(result["bbox"]))
print('人臉五個關鍵點:{}'.format(result["kps"]))
print('人臉3D關鍵點:{}'.format(result["landmark_3d_68"]))
print('人臉2D關鍵點:{}'.format(result["landmark_2d_106"]))
print('人臉姿態:{}'.format(result["pose"]))
print('年齡:{}'.format(result["age"]))
print('性別:{}'.format(result["gender"]))
全部代碼¶
import os
import cv2
import insightface
import numpy as np
from sklearn import preprocessing
class FaceRecognition:
def __init__(self, gpu_id=0, face_db='face_db', threshold=1.24, det_thresh=0.50, det_size=(640, 640)):
"""
人臉識別工具類
:param gpu_id: 正數爲GPU的ID,負數爲使用CPU
:param face_db: 人臉庫文件夾
:param threshold: 人臉識別閾值
:param det_thresh: 檢測閾值
:param det_size: 檢測模型圖片大小
"""
self.gpu_id = gpu_id
self.face_db = face_db
self.threshold = threshold
self.det_thresh = det_thresh
self.det_size = det_size
# 加載人臉識別模型,當allowed_modules=['detection', 'recognition']時,只單純檢測和識別
self.model = insightface.app.FaceAnalysis(root='./',
allowed_modules=None,
providers=['CUDAExecutionProvider'])
self.model.prepare(ctx_id=self.gpu_id, det_thresh=self.det_thresh, det_size=self.det_size)
# 人臉庫的人臉特徵
self.faces_embedding = list()
# 加載人臉庫中的人臉
self.load_faces(self.face_db)
# 加載人臉庫中的人臉
def load_faces(self, face_db_path):
if not os.path.exists(face_db_path):
os.makedirs(face_db_path)
for root, dirs, files in os.walk(face_db_path):
for file in files:
input_image = cv2.imdecode(np.fromfile(os.path.join(root, file), dtype=np.uint8), 1)
user_name = file.split(".")[0]
face = self.model.get(input_image)[0]
embedding = np.array(face.embedding).reshape((1, -1))
embedding = preprocessing.normalize(embedding)
self.faces_embedding.append({
"user_name": user_name,
"feature": embedding
})
# 人臉識別
def recognition(self, image):
faces = self.model.get(image)
results = list()
for face in faces:
# 開始人臉識別
embedding = np.array(face.embedding).reshape((1, -1))
embedding = preprocessing.normalize(embedding)
user_name = "unknown"
for com_face in self.faces_embedding:
r = self.feature_compare(embedding, com_face["feature"], self.threshold)
if r:
user_name = com_face["user_name"]
results.append(user_name)
return results
@staticmethod
def feature_compare(feature1, feature2, threshold):
diff = np.subtract(feature1, feature2)
dist = np.sum(np.square(diff), 1)
if dist < threshold:
return True
else:
return False
def register(self, image, user_name):
faces = self.model.get(image)
if len(faces) != 1:
return '圖片檢測不到人臉'
# 判斷人臉是否存在
embedding = np.array(faces[0].embedding).reshape((1, -1))
embedding = preprocessing.normalize(embedding)
is_exits = False
for com_face in self.faces_embedding:
r = self.feature_compare(embedding, com_face["feature"], self.threshold)
if r:
is_exits = True
if is_exits:
return '該用戶已存在'
# 符合註冊條件保存圖片,同時把特徵添加到人臉特徵庫中
cv2.imencode('.png', image)[1].tofile(os.path.join(self.face_db, '%s.png' % user_name))
self.faces_embedding.append({
"user_name": user_name,
"feature": embedding
})
return "success"
# 檢測人臉
def detect(self, image):
faces = self.model.get(image)
results = list()
for face in faces:
result = dict()
# 獲取人臉屬性
result["bbox"] = np.array(face.bbox).astype(np.int32).tolist()
result["kps"] = np.array(face.kps).astype(np.int32).tolist()
result["landmark_3d_68"] = np.array(face.landmark_3d_68).astype(np.int32).tolist()
result["landmark_2d_106"] = np.array(face.landmark_2d_106).astype(np.int32).tolist()
result["pose"] = np.array(face.pose).astype(np.int32).tolist()
result["age"] = face.age
gender = '男'
if face.gender == 0:
gender = '女'
result["gender"] = gender
# 開始人臉識別
embedding = np.array(face.embedding).reshape((1, -1))
embedding = preprocessing.normalize(embedding)
result["embedding"] = embedding
results.append(result)
return results
if __name__ == '__main__':
img = cv2.imdecode(np.fromfile('迪麗熱巴.jpg', dtype=np.uint8), -1)
face_recognitio = FaceRecognition()
# 人臉註冊
result = face_recognitio.register(img, user_name='迪麗熱巴')
print(result)
# 人臉識別
results = face_recognitio.recognition(img)
for result in results:
print("識別結果:{}".format(result))
results = face_recognitio.detect(img)
for result in results:
print('人臉框座標:{}'.format(result["bbox"]))
print('人臉五個關鍵點:{}'.format(result["kps"]))
print('人臉3D關鍵點:{}'.format(result["landmark_3d_68"]))
print('人臉2D關鍵點:{}'.format(result["landmark_2d_106"]))
print('人臉姿態:{}'.format(result["pose"]))
print('年齡:{}'.format(result["age"]))
print('性別:{}'.format(result["gender"]))