# Copyright (C) 2018-2021 coneypo # SPDX-License-Identifier: MIT # Author: coneypo # Blog: http://www.cnblogs.com/AdaminXie # GitHub: https://github.com/coneypo/Dlib_face_recognition_from_camera # Mail: coneypo@foxmail.com # 摄像头实时人脸识别 / Real-time face detection and recognition import dlib import numpy as np import cv2 import pandas as pd import os import time import logging from PIL import Image, ImageDraw, ImageFont from read_data import LoadImages, LoadStreams import torch.backends.cudnn as cudnn # Dlib 人脸 landmark 特征点检测器 / Get face landmarks predictor = dlib.shape_predictor('algorithm/face_recognition/data/data_dlib/shape_predictor_68_face_landmarks.dat') # Dlib Resnet 人脸识别模型,提取 128D 的特征矢量 / Use Dlib resnet50 model to get 128D face descriptor face_reco_model = dlib.face_recognition_model_v1("algorithm/face_recognition/data/data_dlib/dlib_face_recognition_resnet_model_v1.dat") class Face_Recognizer(): def __init__(self, video_path = None, model=None): self.face_feature_known_list = [] # 用来存放所有录入人脸特征的数组 / Save the features of faces in database self.face_name_known_list = [] # 存储录入人脸名字 / Save the name of faces in database self.current_frame_face_cnt = 0 # 存储当前摄像头中捕获到的人脸数 / Counter for faces in current frame self.current_frame_face_feature_list = [] # 存储当前摄像头中捕获到的人脸特征 / Features of faces in current frame self.current_frame_face_name_list = [] # 存储当前摄像头中捕获到的所有人脸的名字 / Names of faces in current frame self.current_frame_face_name_position_list = [] # 存储当前摄像头中捕获到的所有人脸的名字坐标 / Positions of faces in current frame # Update FPS self.fps = 0 # FPS of current frame self.fps_show = 0 # FPS per second self.frame_start_time = 0 self.frame_cnt = 0 self.start_time = time.time() self.font = cv2.FONT_ITALIC self.font_chinese = ImageFont.truetype("simsun.ttc", 30) if video_path is not None: self.video_name = video_path else: self.video_name = 'vid2.mp4' # A default video file self.dataset = LoadImages(self.video_name) self.face_detector = model self.get_face_database() def use_webcam(self, source): source = source cudnn.benchmark = True self.dataset = LoadStreams(source) # 从 "features_all.csv" 读取录入人脸特征 / Read known faces from "features_all.csv" def get_face_database(self): if os.path.exists("algorithm/face_recognition/data/features_all.csv"): path_features_known_csv = "algorithm/face_recognition/data/features_all.csv" csv_rd = pd.read_csv(path_features_known_csv, header=None) for i in range(csv_rd.shape[0]): features_someone_arr = [] self.face_name_known_list.append(csv_rd.iloc[i][0]) 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]) self.face_feature_known_list.append(features_someone_arr) logging.info("Faces in Database:%d", len(self.face_feature_known_list)) return 1 else: logging.warning("'features_all.csv' not found!") logging.warning("Please run 'get_faces_from_camera.py' " "and 'features_extraction_to_csv.py' before 'face_reco_from_camera.py'") return 0 # 计算两个128D向量间的欧式距离 / Compute the e-distance between two 128D features @staticmethod def return_euclidean_distance(feature_1, feature_2): 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 def draw_name(self, img_rd): # 在人脸框下面写人脸名字 / Write names under rectangle img = Image.fromarray(cv2.cvtColor(img_rd, cv2.COLOR_BGR2RGB)) draw = ImageDraw.Draw(img) name = [] for i in range(self.current_frame_face_cnt): name.append(self.current_frame_face_name_list[i]) # cv2.putText(img_rd, self.current_frame_face_name_list[i], self.current_frame_face_name_position_list[i], self.font, 0.8, (0, 255, 255), 1, cv2.LINE_AA) draw.text(xy=self.current_frame_face_name_position_list[i], text=str(self.current_frame_face_name_list[i]), font=self.font_chinese, fill=(255, 255, 0)) img_rd = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) return img_rd, name def detect_faces(self, img): results = self.face_detector(img) face_results = dlib.rectangles() for obj in results.xyxy[0]: if obj[-1] == 0: # 0 is the class ID for 'person' # Draw bounding boxes around people xmin, ymin, xmax, ymax = map(int, obj[:4]) xmin, ymin, xmax, ymax = convert_to_square(xmin, ymin, xmax, ymax) if xmax <= xmin or ymax <= ymin: continue rectangle = dlib.rectangle(xmin, ymin, xmax, ymax) face_results.append(rectangle) return face_results # 处理获取的视频流,进行人脸识别 / Face detection and recognition from input video stream def get_frame(self): # 1. 读取存放所有人脸特征的 csv / Read known faces from "features.all.csv" # logging.debug("Frame %d starts", self.frame_cnt) for im0s in self.dataset: self.frame_cnt += 1 if self.dataset.mode == 'stream': img_rd = im0s[0].copy() else: img_rd = im0s.copy() faces = self.detect_faces(img_rd) # if self.frame_cnt % 1 == 0: # self.draw_note(img_rd) self.current_frame_face_feature_list = [] self.current_frame_face_cnt = 0 self.current_frame_face_name_position_list = [] self.current_frame_face_name_list = [] # 2. 检测到人脸 / Face detected in current frame if len(faces) != 0: # 3. 获取当前捕获到的图像的所有人脸的特征 / Compute the face descriptors for faces in current frame for i in range(len(faces)): shape = predictor(img_rd, faces[i]) self.current_frame_face_feature_list.append(face_reco_model.compute_face_descriptor(img_rd, shape)) # 4. 遍历捕获到的图像中所有的人脸 / Traversal all the faces in the database for k in range(len(faces)): logging.info("For face %d in camera:", k+1) # 先默认所有人不认识,是 unknown / Set the default names of faces with "unknown" self.current_frame_face_name_list.append("unknown") # 每个捕获人脸的名字坐标 / Positions of faces captured self.current_frame_face_name_position_list.append(tuple( [faces[k].left(), int(faces[k].bottom() + (faces[k].bottom() - faces[k].top()) / 4)])) # 5. 对于某张人脸,遍历所有存储的人脸特征 # For every faces detected, compare the faces in the database current_frame_e_distance_list = [] for i in range(len(self.face_feature_known_list)): # 如果 person_X 数据不为空 if str(self.face_feature_known_list[i][0]) != '0.0': e_distance_tmp = self.return_euclidean_distance(self.current_frame_face_feature_list[k], self.face_feature_known_list[i]) logging.info(" With person %s, the e-distance is %f", str(i + 1), e_distance_tmp) current_frame_e_distance_list.append(e_distance_tmp) else: # 空数据 person_X current_frame_e_distance_list.append(999999999) # 6. 寻找出最小的欧式距离匹配 / Find the one with minimum e-distance similar_person_num = current_frame_e_distance_list.index(min(current_frame_e_distance_list)) logging.info("Minimum e-distance with %s: %f", self.face_name_known_list[similar_person_num], min(current_frame_e_distance_list)) if min(current_frame_e_distance_list) < 0.4: self.current_frame_face_name_list[k] = self.face_name_known_list[similar_person_num] logging.info("Face recognition result: %s", self.face_name_known_list[similar_person_num]) else: logging.info("Face recognition result: Unknown person") logging.info("\n") # 矩形框 / Draw rectangle for kk, d in enumerate(faces): # # 绘制矩形框 # print(kk,d) cv2.rectangle(img_rd, tuple([d.left(), d.top()]), tuple([d.right(), d.bottom()]), (0, 255, 0), 2) # self.update_fps() self.current_frame_face_cnt = len(faces) img_rd, name = self.draw_name(img_rd) else: img_with_name = img_rd ret, jpeg = cv2.imencode(".jpg", img_rd) resText=f'人脸识别到了{name}' return jpeg.tobytes(), resText # else: # ret, jpeg = cv2.imencode(".jpg", img_rd) # return jpeg.tobytes() def convert_to_square(xmin, ymin, xmax, ymax): # convert to square location center_x = (xmin + xmax) // 2 center_y = (ymin + ymax) // 2 square_length = ((xmax - xmin) + (ymax - ymin)) // 2 // 2 square_length *= 1.1 xmin = int(center_x - square_length) ymin = int(center_y - square_length) xmax = int(center_x + square_length) ymax = int(center_y + square_length) return xmin, ymin, xmax, ymax