人脸位置检测
face_locations = face_recognition.face_locations(frame)
参数说明
返回值
face_locations:[(x1,y1,x2,y2)] 表示所有检测到人脸的位置坐标数组,每个数组元素代表一个人脸的位置(左上角和右下角坐标)
人脸数据编码
face_encodings = face_recognition.face_encodings(frame, face_locations)
参数说明
frame:按照RGB排列的彩色图像
face_locations:检测到的人脸位置
返回值
face_encodings:人脸编码数据形成的数组,数组大小与传入的face_locations一致
人脸比对
face_distances = face_recognition.face_distance(known_face_encodings, face_encoding)
参数说明
known_face_encodings:已有的人脸编码数组
face_encoding:待比对的人脸编码
返回值
face_distances:和已有人脸编码的距离,距离越小则说明越接近,低于某个阈值则可认为是相同的人脸
使用示例:打开摄像头,识别画面中的人脸
把人脸图片存放在~/Lepi_Data/ros/face_recognizer/known_face目录下,运行下面的程序就能识别到对应的人脸
#!coding:utf-8
import cv2
from PIL import Image, ImageFont, ImageDraw
import os
import time
import face_recognition
import numpy as np
Key_Esc = 27
defaultFont = ImageFont.truetype(
'/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc', 48)
def putText(frame, text, pos, color, font=defaultFont):
"""
将文本显示在图片上
Keyword arguments:
frame: image 原图
text: str 想要显示的文本
pos: (x,y) 指定显示的初始坐标(左上角顶点)
color: [r,g,b] 指定文本颜色的r、g、b值
Returns:
cv_img: image 叠加了文本的新图片(不改变原图)
"""
if hasattr(text, "decode"):
text = text.decode('utf-8')
pil_image = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(pil_image)
draw.text(pos, text, font=font, fill=color)
cv_img = cv2.cvtColor(np.asarray(pil_image), cv2.COLOR_RGB2BGR)
return cv_img
class FaceRecognizer(object):
"""
FaceRecognizer类, 用来检测和识别人脸
Attributes:
data_dir: str 标记人脸图片的保存目录
scale: int 缩放倍数,加快检测速度但会降低检测精度
font: ImageFont 中文字体
known_faces: dict 存放已标记人脸的字典
threshold: float 检测阈值,小于该值才会进行相似度比较
"""
def __init__(self, scale=5, threshold=0.45, fontSize=18):
super(FaceRecognizer, self).__init__()
self.data_dir = os.path.expanduser(
'~') + "/Lepi_Data/ros/face_recognizer/known_face"
self.scale = scale
# self.font = ImageFont.truetype(
# '/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf', fontSize)
self.known_faces = {}
self.threshold = threshold
self.face_locations = []
self.face_names = []
self.face_data = []
self.add_label_success = False
self.load_faces()
def detect(self, frame, scale=None):
"""
人脸检测函数
Keyword arguments:
frame: image 原图
scale: float 缩放倍数
Returns:
face_locations: [(x1,y1,x2,y2)] 表示所有检测到人脸的位置坐标数组,每个数组元素代表一个人脸的位置
"""
if scale is None:
scale = self.scale
# Resize frame of video to 1/self.scale size for faster face recognition processing
small_frame = cv2.resize(frame, (0, 0), fx=1.0/scale, fy=1.0/scale)
# Convert the image from BGR color (which OpenCV uses) to RGB color (which face_recognition uses)
rgb_small_frame = cv2.cvtColor(small_frame, cv2.COLOR_BGR2RGB)
self.face_locations = face_recognition.face_locations(rgb_small_frame)
self.face_data = self.getFaceData(0)
return self.rect_faces(frame, self.face_locations)
def recognize(self, frame, scale=None):
"""
人脸识别函数
Keyword arguments:
frame: image 原图
scale: float 缩放倍数
Returns:
face_locations: [(x1,y1,x2,y2)] 表示所有检测到人脸的位置坐标数组,每个数组元素代表一个人脸的位置
face_names: ['未知'] 对应每个人脸的标签,未检测到用'未知'表示
"""
if scale is None:
scale = self.scale
small_frame = cv2.resize(frame, (0, 0), fx=1.0/scale, fy=1.0/scale)
rgb_small_frame = cv2.cvtColor(small_frame, cv2.COLOR_BGR2RGB)
face_locations = face_recognition.face_locations(rgb_small_frame)
start = time.time()
face_encodings = face_recognition.face_encodings(
rgb_small_frame, face_locations)
face_names = []
# python 3.9 默认生成dict_values 手动转成list进行比较
known_face_encodings = list(self.known_faces.values())
known_face_names = list(self.known_faces.keys())
for face_encoding in face_encodings:
name = "未知"
if len(known_face_encodings) > 0:
# matches = face_recognition.compare_faces(known_face_encodings, face_encoding)
# print(known_face_encodings, face_encoding)
face_distances = face_recognition.face_distance(
known_face_encodings, face_encoding)
best_match_index = np.argmin(face_distances)
# print(matches)
print(face_distances)
if face_distances[best_match_index] < self.threshold:
name = known_face_names[best_match_index]
face_names.append(name)
end = time.time()
print("recognized %d faces in %.2f ms" %
(len(face_locations), (end - start)*1000))
self.face_locations = face_locations
self.face_names = face_names
self.face_data = self.getFaceData(0)
return self.label_faces(frame, self.face_locations, self.face_names)
# return face_locations, face_names
def getFaceData(self, index):
if len(self.face_locations) > index:
(top, right, bottom, left) = self.face_locations[index]
top *= self.scale
right *= self.scale
bottom *= self.scale
left *= self.scale
return [int((right+left)/2), int((top+bottom)/2), int(right-left), int(bottom-top)]
else:
return [0, 0, 0, 0]
def detectedFaceLabel(self, name):
if name in self.face_names:
index = self.face_names.index(name)
self.face_data = self.getFaceData(index)
return True
else:
self.face_data = []
return False
def rect_faces(self, frame, face_locations, scale=None):
"""
把检测到的人脸用矩形框出来
Keyword arguments:
frame: image 原图
face_locations: [(x1,y1,x2,y2)] 表示所有检测到人脸的位置坐标数组,每个数组元素代表一个人脸的位置
scale: float 缩放倍数
Returns:
frame: image 框出了人脸的新图像(改变原图)
"""
if scale is None:
scale = self.scale
# Display the results
for (top, right, bottom, left) in face_locations:
# Scale back up face locations since the frame we detected in was scaled to 1/4 size
top *= scale
right *= scale
bottom *= scale
left *= scale
# Draw a box around the face
cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 4)
# Draw a label with a name below the face
return frame
def label_faces(self, frame, face_locations, face_names, scale=None):
"""
给识别到的人脸添加标签
Keyword arguments:
frame: image 原图
face_locations: [(x1,y1,x2,y2)] 表示所有检测到人脸的位置坐标数组,每个数组元素代表一个人脸的位置
scale: float 缩放倍数,需要和检测时的一致,否则比例会出错
Returns:
frame: image 标记了人脸的新图像(不改变原图)
"""
if scale is None:
scale = self.scale
frame = self.rect_faces(frame, face_locations, scale)
# Display the results
for (top, right, bottom, left), name in zip(face_locations, face_names):
# Scale back up face locations since the frame we detected in was scaled to 1/4 size
top *= scale
right *= scale
bottom *= scale
left *= scale
# Draw a box around the face
# cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2)
# Draw a label with a name below the face
color = (0, 0, 255)
frame = putText(frame, name, (left + 6, bottom ), color)
return frame
def load_faces(self):
"""
加载本地的人脸标签(默认存放在data_dir下)
Keyword arguments:
无
Returns:
无
"""
if not os.path.exists(self.data_dir):
os.makedirs(self.data_dir)
files = os.listdir(self.data_dir)
for file in files:
try:
name = file.split('.')[0]
file_path = os.path.join(self.data_dir, file)
print(self.add_face_label(cv2.imread(file_path), name, scale=1))
except Exception as e:
print(e)
def add_face_label(self, frame, name, scale=None, save=False):
"""
动态添加人脸标签
Keyword arguments:
frame: image 原图
name: str 标签名称
scale: float 缩放倍数
save: bool 是否保存,True则将保存图片至本地标签目录,每次启动会重新读取,False只在本次运行生效
Returns:
无
"""
if scale is None:
scale = self.scale
frame = cv2.resize(frame, (0, 0), fx=1.0/scale, fy=1.0/scale)
rgb_frame = frame[:, :, ::-1]
start = time.time()
self.detect(frame, scale=1)
face_locations = self.face_locations
end = time.time()
print("Found %d faces in %.2f ms" %
(len(face_locations), (end - start)*1000))
if len(face_locations) == 1:
face_encoding = face_recognition.face_encodings(
frame, face_locations)[0]
self.known_faces[name] = face_encoding
if save:
file_path = os.path.join(self.data_dir, name+'.png')
print(file_path)
cv2.imwrite(file_path, frame)
print('成功添加"%s"的标记' % (name))
self.add_label_success = True
return '标记成功'
elif len(face_locations) > 1:
self.add_label_success = False
return '标记"%s"失败,检测到多余人脸' % (name)
else:
self.add_label_success = False
return '标记"%s"失败,未检测到人脸' % (name)
def remove_face_label(self, name):
"""
删除人脸标签
Keyword arguments:
name: str 标签名称
Returns:
无
"""
if self.known_faces.__contains__(name):
self.known_faces.pop(name)
try:
os.system('rm '+os.path.join(self.data_dir, name+".*"))
return "已删除"
except Exception as e:
print(e)
return "删除出错"
def test_recognize():
"""
测试函数,打开摄像头实时检测人脸, 按q退出
Keyword arguments:
无
Returns:
无
"""
fr = FaceRecognizer()
cap = cv2.VideoCapture(0)
while True:
# Grab a single frame of video
ret, frame = cap.read()
start = time.time()
nframe = fr.recognize(frame)
end = time.time()
print("labeled %d faces in %.2f ms" %
(len(fr.face_locations), (end - start)*1000))
cv2.imshow("images", np.rot90(cv2.resize(nframe, (320, 240))))
c = cv2.waitKey(1)
if c == Key_Esc:
break
cap.release()
cv2.destroyAllWindows()
if __name__ == '__main__':
test_recognize()