人脸位置检测
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-8import cv2from PIL import Image, ImageFont, ImageDrawimport osimport timeimport face_recognitionimport numpy as npKey_Esc = 27defaultFont = 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_imgclass 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 = thresholdself.face_locations = []self.face_names = []self.face_data = []self.add_label_success = Falseself.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 processingsmall_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.scalesmall_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_locationsself.face_names = face_namesself.face_data = self.getFaceData(0)return self.label_faces(frame, self.face_locations, self.face_names)# return face_locations, face_namesdef getFaceData(self, index):if len(self.face_locations) > index:(top, right, bottom, left) = self.face_locations[index]top *= self.scaleright *= self.scalebottom *= self.scaleleft *= self.scalereturn [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 Trueelse:self.face_data = []return Falsedef 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 resultsfor (top, right, bottom, left) in face_locations:# Scale back up face locations since the frame we detected in was scaled to 1/4 sizetop *= scaleright *= scalebottom *= scaleleft *= scale# Draw a box around the facecv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 4)# Draw a label with a name below the facereturn framedef 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.scaleframe = self.rect_faces(frame, face_locations, scale)# Display the resultsfor (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 sizetop *= scaleright *= scalebottom *= scaleleft *= 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 facecolor = (0, 0, 255)frame = putText(frame, name, (left + 6, bottom ), color)return framedef 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.scaleframe = 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_locationsend = 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_encodingif 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 = Truereturn '标记成功'elif len(face_locations) > 1:self.add_label_success = Falsereturn '标记"%s"失败,检测到多余人脸' % (name)else:self.add_label_success = Falsereturn '标记"%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 videoret, 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:breakcap.release()cv2.destroyAllWindows()if __name__ == '__main__':test_recognize()
