基于Python_opencv人脸录入、识别系统(应用dlib机器学习库)(上)+https://developer.aliyun.com/article/1627339
调用摄像头进行实时人脸识别(face_reco_from_camera.py):
- 将捕获到的人脸数据和之前存的人脸数据进行对比计算欧式距离, 由此判断是否是同一个人;
class Face_Recognizer: def __init__(self): 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) # 从 "features_all.csv" 读取录入人脸特征 / Read known faces from "features_all.csv" def get_face_database(self): if os.path.exists("data/features_all.csv"): path_features_known_csv = "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 # 更新 FPS / Update FPS of Video stream def update_fps(self): now = time.time() # 每秒刷新 fps / Refresh fps per second if str(self.start_time).split(".")[0] != str(now).split(".")[0]: self.fps_show = self.fps self.start_time = now self.frame_time = now - self.frame_start_time self.fps = 1.0 / self.frame_time self.frame_start_time = now # 生成的 cv2 window 上面添加说明文字 / PutText on cv2 window def draw_note(self, img_rd): cv2.putText(img_rd, "Face Recognizer", (20, 40), self.font, 1, (255, 255, 255), 1, cv2.LINE_AA) cv2.putText(img_rd, "Frame: " + str(self.frame_cnt), (20, 100), self.font, 0.8, (0, 255, 0), 1, cv2.LINE_AA) cv2.putText(img_rd, "FPS: " + str(self.fps_show.__round__(2)), (20, 130), self.font, 0.8, (0, 255, 0), 1, cv2.LINE_AA) cv2.putText(img_rd, "Faces: " + str(self.current_frame_face_cnt), (20, 160), self.font, 0.8, (0, 255, 0), 1, cv2.LINE_AA) cv2.putText(img_rd, "Q: Quit", (20, 450), self.font, 0.8, (255, 255, 255), 1, cv2.LINE_AA) def draw_name(self, img_rd): # 在人脸框下面写人脸名字 / Write names under rectangle img = Image.fromarray(cv2.cvtColor(img_rd, cv2.COLOR_BGR2RGB)) draw = ImageDraw.Draw(img) for i in range(self.current_frame_face_cnt): # 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=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 # 修改显示人名 / Show names in chinese def show_chinese_name(self): # Default known name: person_1, person_2, person_3 if self.current_frame_face_cnt >= 1: # 修改录入的人脸姓名 / Modify names in face_name_known_list to chinese name self.face_name_known_list[0] = '张三'.encode('utf-8').decode() # self.face_name_known_list[1] = '张四'.encode('utf-8').decode() # 处理获取的视频流,进行人脸识别 / Face detection and recognition from input video stream def process(self, stream): # 1. 读取存放所有人脸特征的 csv / Read known faces from "features.all.csv" if self.get_face_database(): while stream.isOpened(): self.frame_cnt += 1 logging.debug("Frame %d starts", self.frame_cnt) flag, img_rd = stream.read() faces = detector(img_rd, 0) kk = cv2.waitKey(1) # 按下 q 键退出 / Press 'q' to quit if kk == ord('q'): break else: 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.debug("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.debug(" 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.debug("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.debug("Face recognition result: %s", self.face_name_known_list[similar_person_num]) else: logging.debug("Face recognition result: Unknown person") logging.debug("\n") # 矩形框 / Draw rectangle for kk, d in enumerate(faces): # 绘制矩形框 cv2.rectangle(img_rd, tuple([d.left(), d.top()]), tuple([d.right(), d.bottom()]), (255, 255, 255), 2) self.current_frame_face_cnt = len(faces) # 7. 在这里更改显示的人名 / Modify name if needed # self.show_chinese_name() # 8. 写名字 / Draw name img_with_name = self.draw_name(img_rd) else: img_with_name = img_rd logging.debug("Faces in camera now: %s", self.current_frame_face_name_list) cv2.imshow("camera", img_with_name) # 9. 更新 FPS / Update stream FPS self.update_fps() logging.debug("Frame ends\n\n") # OpenCV 调用摄像头并进行 process def run(self): # cap = cv2.VideoCapture("video.mp4") # Get video stream from video file cap = cv2.VideoCapture(0) # Get video stream from camera cap.set(3, 480) # 640x480 self.process(cap) cap.release() cv2.destroyAllWindows() def main(): # logging.basicConfig(level=logging.DEBUG) # Set log level to 'logging.DEBUG' to print debug info of every frame logging.basicConfig(level=logging.INFO) Face_Recognizer_con = Face_Recognizer() Face_Recognizer_con.run() if __name__ == '__main__': main()
对每一帧都进行检测+识别(face_reco_from_camera_single_face.py):
- 针对于人脸数 <=1 的场景, 区别于
face_reco_from_camera.py
(对每一帧都进行检测+识别), 只有人脸出现的时候进行识别
class Face_Recognizer: def __init__(self): self.font = cv2.FONT_ITALIC self.font_chinese = ImageFont.truetype("simsun.ttc", 30) # 统计 FPS / For FPS self.frame_time = 0 self.frame_start_time = 0 self.fps = 0 self.fps_show = 0 self.start_time = time.time() # 统计帧数 / cnt for frame self.frame_cnt = 0 # 用来存储所有录入人脸特征的数组 / Save the features of faces in the database self.features_known_list = [] # 用来存储录入人脸名字 / Save the name of faces in the database self.face_name_known_list = [] # 用来存储上一帧和当前帧 ROI 的质心坐标 / List to save centroid positions of ROI in frame N-1 and N self.last_frame_centroid_list = [] self.current_frame_centroid_list = [] # 用来存储当前帧检测出目标的名字 / List to save names of objects in current frame self.current_frame_name_list = [] # 上一帧和当前帧中人脸数的计数器 / cnt for faces in frame N-1 and N self.last_frame_faces_cnt = 0 self.current_frame_face_cnt = 0 # 用来存放进行识别时候对比的欧氏距离 / Save the e-distance for faceX when recognizing self.current_frame_face_X_e_distance_list = [] # 存储当前摄像头中捕获到的所有人脸的坐标名字 / Save the positions and names of current faces captured self.current_frame_face_position_list = [] # 存储当前摄像头中捕获到的人脸特征 / Save the features of people in current frame self.current_frame_face_feature_list = [] # 控制再识别的后续帧数 / Reclassify after 'reclassify_interval' frames # 如果识别出 "unknown" 的脸, 将在 reclassify_interval_cnt 计数到 reclassify_interval 后, 对于人脸进行重新识别 self.reclassify_interval_cnt = 0 self.reclassify_interval = 10 # 从 "features_all.csv" 读取录入人脸特征 / Get known faces from "features_all.csv" def get_face_database(self): if os.path.exists("data/features_all.csv"): path_features_known_csv = "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.features_known_list.append(features_someone_arr) logging.info("Faces in Database: %d", len(self.features_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 # 获取处理之后 stream 的帧数 / Update FPS of video stream def update_fps(self): now = time.time() # 每秒刷新 fps / Refresh fps per second if str(self.start_time).split(".")[0] != str(now).split(".")[0]: self.fps_show = self.fps self.start_time = now self.frame_time = now - self.frame_start_time self.fps = 1.0 / self.frame_time self.frame_start_time = now # 计算两个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 # 生成的 cv2 window 上面添加说明文字 / putText on cv2 window def draw_note(self, img_rd): # 添加说明 (Add some statements cv2.putText(img_rd, "Face Recognizer for single face", (20, 40), self.font, 1, (255, 255, 255), 1, cv2.LINE_AA) cv2.putText(img_rd, "Frame: " + str(self.frame_cnt), (20, 100), self.font, 0.8, (0, 255, 0), 1, cv2.LINE_AA) cv2.putText(img_rd, "FPS: " + str(self.fps_show.__round__(2)), (20, 130), self.font, 0.8, (0, 255, 0), 1, cv2.LINE_AA) cv2.putText(img_rd, "Faces: " + str(self.current_frame_face_cnt), (20, 160), self.font, 0.8, (0, 255, 0), 1, cv2.LINE_AA) cv2.putText(img_rd, "Q: Quit", (20, 450), self.font, 0.8, (255, 255, 255), 1, cv2.LINE_AA) def draw_name(self, img_rd): # 在人脸框下面写人脸名字 / Write names under ROI logging.debug(self.current_frame_name_list) img = Image.fromarray(cv2.cvtColor(img_rd, cv2.COLOR_BGR2RGB)) draw = ImageDraw.Draw(img) draw.text(xy=self.current_frame_face_position_list[0], text=self.current_frame_name_list[0], font=self.font_chinese, fill=(255, 255, 0)) img_rd = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) return img_rd def show_chinese_name(self): if self.current_frame_face_cnt >= 1: logging.debug(self.face_name_known_list) # 修改录入的人脸姓名 / Modify names in face_name_known_list to chinese name self.face_name_known_list[0] = '张三'.encode('utf-8').decode() # self.face_name_known_list[1] = '张四'.encode('utf-8').decode() # 处理获取的视频流, 进行人脸识别 / Face detection and recognition wit OT from input video stream def process(self, stream): # 1. 读取存放所有人脸特征的 csv / Get faces known from "features.all.csv" if self.get_face_database(): while stream.isOpened(): self.frame_cnt += 1 logging.debug("Frame " + str(self.frame_cnt) + " starts") flag, img_rd = stream.read() kk = cv2.waitKey(1) # 2. 检测人脸 / Detect faces for frame X faces = detector(img_rd, 0) # 3. 更新帧中的人脸数 / Update cnt for faces in frames self.last_frame_faces_cnt = self.current_frame_face_cnt self.current_frame_face_cnt = len(faces) # 4.1 当前帧和上一帧相比没有发生人脸数变化 / If cnt not changes, 1->1 or 0->0 if self.current_frame_face_cnt == self.last_frame_faces_cnt: logging.debug("scene 1: 当前帧和上一帧相比没有发生人脸数变化 / No face cnt changes in this frame!!!") if "unknown" in self.current_frame_name_list: logging.debug(" >>> 有未知人脸, 开始进行 reclassify_interval_cnt 计数") self.reclassify_interval_cnt += 1 # 4.1.1 当前帧一张人脸 / One face in this frame if self.current_frame_face_cnt == 1: if self.reclassify_interval_cnt == self.reclassify_interval: logging.debug(" scene 1.1 需要对于当前帧重新进行人脸识别 / Re-classify for current frame") self.reclassify_interval_cnt = 0 self.current_frame_face_feature_list = [] self.current_frame_face_X_e_distance_list = [] self.current_frame_name_list = [] 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)) # a. 遍历捕获到的图像中所有的人脸 / Traversal all the faces in the database for k in range(len(faces)): self.current_frame_name_list.append("unknown") # b. 每个捕获人脸的名字坐标 / Positions of faces captured self.current_frame_face_position_list.append(tuple( [faces[k].left(), int(faces[k].bottom() + (faces[k].bottom() - faces[k].top()) / 4)])) # c. 对于某张人脸, 遍历所有存储的人脸特征 / For every face detected, compare it with all the faces in the database for i in range(len(self.features_known_list)): # 如果 person_X 数据不为空 / If the data of person_X is not empty if str(self.features_known_list[i][0]) != '0.0': e_distance_tmp = self.return_euclidean_distance( self.current_frame_face_feature_list[k], self.features_known_list[i]) logging.debug(" with person %d, the e-distance: %f", i + 1, e_distance_tmp) self.current_frame_face_X_e_distance_list.append(e_distance_tmp) else: # 空数据 person_X / For empty data self.current_frame_face_X_e_distance_list.append(999999999) # d. 寻找出最小的欧式距离匹配 / Find the one with minimum e distance similar_person_num = self.current_frame_face_X_e_distance_list.index( min(self.current_frame_face_X_e_distance_list)) if min(self.current_frame_face_X_e_distance_list) < 0.4: # 在这里更改显示的人名 / Modify name if needed self.show_chinese_name() self.current_frame_name_list[k] = self.face_name_known_list[similar_person_num] logging.debug(" recognition result for face %d: %s", k + 1, self.face_name_known_list[similar_person_num]) else: logging.debug(" recognition result for face %d: %s", k + 1, "unknown") else: logging.debug( " scene 1.2 不需要对于当前帧重新进行人脸识别 / No re-classification needed for current frame") # 获取特征框坐标 / Get ROI positions for k, d in enumerate(faces): cv2.rectangle(img_rd, tuple([d.left(), d.top()]), tuple([d.right(), d.bottom()]), (255, 255, 255), 2) self.current_frame_face_position_list[k] = tuple( [faces[k].left(), int(faces[k].bottom() + (faces[k].bottom() - faces[k].top()) / 4)]) img_rd = self.draw_name(img_rd) # 4.2 当前帧和上一帧相比发生人脸数变化 / If face cnt changes, 1->0 or 0->1 else: logging.debug("scene 2: 当前帧和上一帧相比人脸数发生变化 / Faces cnt changes in this frame") self.current_frame_face_position_list = [] self.current_frame_face_X_e_distance_list = [] self.current_frame_face_feature_list = [] # 4.2.1 人脸数从 0->1 / Face cnt 0->1 if self.current_frame_face_cnt == 1: logging.debug(" scene 2.1 出现人脸, 进行人脸识别 / Get faces in this frame and do face recognition") self.current_frame_name_list = [] 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)) # a. 遍历捕获到的图像中所有的人脸 / Traversal all the faces in the database for k in range(len(faces)): self.current_frame_name_list.append("unknown") # b. 每个捕获人脸的名字坐标 / Positions of faces captured self.current_frame_face_position_list.append(tuple( [faces[k].left(), int(faces[k].bottom() + (faces[k].bottom() - faces[k].top()) / 4)])) # c. 对于某张人脸, 遍历所有存储的人脸特征 / For every face detected, compare it with all the faces in database for i in range(len(self.features_known_list)): # 如果 person_X 数据不为空 / If data of person_X is not empty if str(self.features_known_list[i][0]) != '0.0': e_distance_tmp = self.return_euclidean_distance( self.current_frame_face_feature_list[k], self.features_known_list[i]) logging.debug(" with person %d, the e-distance: %f", i + 1, e_distance_tmp) self.current_frame_face_X_e_distance_list.append(e_distance_tmp) else: # 空数据 person_X / Empty data for person_X self.current_frame_face_X_e_distance_list.append(999999999) # d. 寻找出最小的欧式距离匹配 / Find the one with minimum e distance similar_person_num = self.current_frame_face_X_e_distance_list.index( min(self.current_frame_face_X_e_distance_list)) if min(self.current_frame_face_X_e_distance_list) < 0.4: # 在这里更改显示的人名 / Modify name if needed self.show_chinese_name() self.current_frame_name_list[k] = self.face_name_known_list[similar_person_num] logging.debug(" recognition result for face %d: %s", k + 1, self.face_name_known_list[similar_person_num]) else: logging.debug(" recognition result for face %d: %s", k + 1, "unknown") if "unknown" in self.current_frame_name_list: self.reclassify_interval_cnt += 1 # 4.2.1 人脸数从 1->0 / Face cnt 1->0 elif self.current_frame_face_cnt == 0: logging.debug(" scene 2.2 人脸消失, 当前帧中没有人脸 / No face in this frame!!!") self.reclassify_interval_cnt = 0 self.current_frame_name_list = [] self.current_frame_face_feature_list = [] # 5. 生成的窗口添加说明文字 / Add note on cv2 window self.draw_note(img_rd) if kk == ord('q'): break self.update_fps() cv2.namedWindow("camera", 1) cv2.imshow("camera", img_rd) logging.debug("Frame ends\n\n") def run(self): # cap = cv2.VideoCapture("video.mp4") # Get video stream from video file cap = cv2.VideoCapture(0) # Get video stream from camera self.process(cap) cap.release() cv2.destroyAllWindows() def main(): # logging.basicConfig(level=logging.DEBUG) # Set log level to 'logging.DEBUG' to print debug info of every frame logging.basicConfig(level=logging.INFO) Face_Recognizer_con = Face_Recognizer() Face_Recognizer_con.run() if __name__ == '__main__': main()
对初始帧做检测+识别, 对后续帧做检测+质心跟踪(face_reco_from_camera_ot.py):
class Face_Recognizer: def __init__(self): self.font = cv2.FONT_ITALIC # FPS self.frame_time = 0 self.frame_start_time = 0 self.fps = 0 self.fps_show = 0 self.start_time = time.time() # cnt for frame self.frame_cnt = 0 # 用来存放所有录入人脸特征的数组 / Save the features of faces in the database self.face_features_known_list = [] # 存储录入人脸名字 / Save the name of faces in the database self.face_name_known_list = [] # 用来存储上一帧和当前帧 ROI 的质心坐标 / List to save centroid positions of ROI in frame N-1 and N self.last_frame_face_centroid_list = [] self.current_frame_face_centroid_list = [] # 用来存储上一帧和当前帧检测出目标的名字 / List to save names of objects in frame N-1 and N self.last_frame_face_name_list = [] self.current_frame_face_name_list = [] # 上一帧和当前帧中人脸数的计数器 / cnt for faces in frame N-1 and N self.last_frame_face_cnt = 0 self.current_frame_face_cnt = 0 # 用来存放进行识别时候对比的欧氏距离 / Save the e-distance for faceX when recognizing self.current_frame_face_X_e_distance_list = [] # 存储当前摄像头中捕获到的所有人脸的坐标名字 / Save the positions and names of current faces captured self.current_frame_face_position_list = [] # 存储当前摄像头中捕获到的人脸特征 / Save the features of people in current frame self.current_frame_face_feature_list = [] # e distance between centroid of ROI in last and current frame self.last_current_frame_centroid_e_distance = 0 # 控制再识别的后续帧数 / Reclassify after 'reclassify_interval' frames # 如果识别出 "unknown" 的脸, 将在 reclassify_interval_cnt 计数到 reclassify_interval 后, 对于人脸进行重新识别 self.reclassify_interval_cnt = 0 self.reclassify_interval = 10 # 从 "features_all.csv" 读取录入人脸特征 / Get known faces from "features_all.csv" def get_face_database(self): if os.path.exists("data/features_all.csv"): path_features_known_csv = "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_features_known_list.append(features_someone_arr) logging.info("Faces in Database: %d", len(self.face_features_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 def update_fps(self): now = time.time() # 每秒刷新 fps / Refresh fps per second if str(self.start_time).split(".")[0] != str(now).split(".")[0]: self.fps_show = self.fps self.start_time = now self.frame_time = now - self.frame_start_time self.fps = 1.0 / self.frame_time self.frame_start_time = now @staticmethod # 计算两个128D向量间的欧式距离 / Compute the e-distance between two 128D features 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 # 使用质心追踪来识别人脸 / Use centroid tracker to link face_x in current frame with person_x in last frame def centroid_tracker(self): for i in range(len(self.current_frame_face_centroid_list)): e_distance_current_frame_person_x_list = [] # 对于当前帧中的人脸1, 和上一帧中的 人脸1/2/3/4/.. 进行欧氏距离计算 / For object 1 in current_frame, compute e-distance with object 1/2/3/4/... in last frame for j in range(len(self.last_frame_face_centroid_list)): self.last_current_frame_centroid_e_distance = self.return_euclidean_distance( self.current_frame_face_centroid_list[i], self.last_frame_face_centroid_list[j]) e_distance_current_frame_person_x_list.append( self.last_current_frame_centroid_e_distance) last_frame_num = e_distance_current_frame_person_x_list.index( min(e_distance_current_frame_person_x_list)) self.current_frame_face_name_list[i] = self.last_frame_face_name_list[last_frame_num] # 生成的 cv2 window 上面添加说明文字 / putText on cv2 window def draw_note(self, img_rd): # 添加说明 / Add some info on windows cv2.putText(img_rd, "Face Recognizer with OT", (20, 40), self.font, 1, (255, 255, 255), 1, cv2.LINE_AA) cv2.putText(img_rd, "Frame: " + str(self.frame_cnt), (20, 100), self.font, 0.8, (0, 255, 0), 1, cv2.LINE_AA) cv2.putText(img_rd, "FPS: " + str(self.fps.__round__(2)), (20, 130), self.font, 0.8, (0, 255, 0), 1, cv2.LINE_AA) cv2.putText(img_rd, "Faces: " + str(self.current_frame_face_cnt), (20, 160), self.font, 0.8, (0, 255, 0), 1, cv2.LINE_AA) cv2.putText(img_rd, "Q: Quit", (20, 450), self.font, 0.8, (255, 255, 255), 1, cv2.LINE_AA) for i in range(len(self.current_frame_face_name_list)): img_rd = cv2.putText(img_rd, "Face_" + str(i + 1), tuple( [int(self.current_frame_face_centroid_list[i][0]), int(self.current_frame_face_centroid_list[i][1])]), self.font, 0.8, (255, 190, 0), 1, cv2.LINE_AA) # 处理获取的视频流, 进行人脸识别 / Face detection and recognition wit OT from input video stream def process(self, stream): # 1. 读取存放所有人脸特征的 csv / Get faces known from "features.all.csv" if self.get_face_database(): while stream.isOpened(): self.frame_cnt += 1 logging.debug("Frame " + str(self.frame_cnt) + " starts") flag, img_rd = stream.read() kk = cv2.waitKey(1) # 2. 检测人脸 / Detect faces for frame X faces = detector(img_rd, 0) # 3. 更新人脸计数器 / Update cnt for faces in frames self.last_frame_face_cnt = self.current_frame_face_cnt self.current_frame_face_cnt = len(faces) # 4. 更新上一帧中的人脸列表 / Update the face name list in last frame self.last_frame_face_name_list = self.current_frame_face_name_list[:] # 5. 更新上一帧和当前帧的质心列表 / update frame centroid list self.last_frame_face_centroid_list = self.current_frame_face_centroid_list self.current_frame_face_centroid_list = [] # 6.1 如果当前帧和上一帧人脸数没有变化 / if cnt not changes if (self.current_frame_face_cnt == self.last_frame_face_cnt) and ( self.reclassify_interval_cnt != self.reclassify_interval): logging.debug("scene 1: 当前帧和上一帧相比没有发生人脸数变化 / No face cnt changes in this frame!!!") self.current_frame_face_position_list = [] if "unknown" in self.current_frame_face_name_list: logging.debug(" 有未知人脸, 开始进行 reclassify_interval_cnt 计数") self.reclassify_interval_cnt += 1 if self.current_frame_face_cnt != 0: for k, d in enumerate(faces): self.current_frame_face_position_list.append(tuple( [faces[k].left(), int(faces[k].bottom() + (faces[k].bottom() - faces[k].top()) / 4)])) self.current_frame_face_centroid_list.append( [int(faces[k].left() + faces[k].right()) / 2, int(faces[k].top() + faces[k].bottom()) / 2]) img_rd = cv2.rectangle(img_rd, tuple([d.left(), d.top()]), tuple([d.right(), d.bottom()]), (255, 255, 255), 2) # 如果当前帧中有多个人脸, 使用质心追踪 / Multi-faces in current frame, use centroid-tracker to track if self.current_frame_face_cnt != 1: self.centroid_tracker() for i in range(self.current_frame_face_cnt): # 6.2 Write names under ROI img_rd = cv2.putText(img_rd, self.current_frame_face_name_list[i], self.current_frame_face_position_list[i], self.font, 0.8, (0, 255, 255), 1, cv2.LINE_AA) self.draw_note(img_rd) # 6.2 如果当前帧和上一帧人脸数发生变化 / If cnt of faces changes, 0->1 or 1->0 or ... else: logging.debug("scene 2: 当前帧和上一帧相比人脸数发生变化 / Faces cnt changes in this frame") self.current_frame_face_position_list = [] self.current_frame_face_X_e_distance_list = [] self.current_frame_face_feature_list = [] self.reclassify_interval_cnt = 0 # 6.2.1 人脸数减少 / Face cnt decreases: 1->0, 2->1, ... if self.current_frame_face_cnt == 0: logging.debug(" scene 2.1 人脸消失, 当前帧中没有人脸 / No faces in this frame!!!") # clear list of names and features self.current_frame_face_name_list = [] # 6.2.2 人脸数增加 / Face cnt increase: 0->1, 0->2, ..., 1->2, ... else: logging.debug(" scene 2.2 出现人脸, 进行人脸识别 / Get faces in this frame and do face recognition") self.current_frame_face_name_list = [] 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)) self.current_frame_face_name_list.append("unknown") # 6.2.2.1 遍历捕获到的图像中所有的人脸 / Traversal all the faces in the database for k in range(len(faces)): logging.debug(" For face %d in current frame:", k + 1) self.current_frame_face_centroid_list.append( [int(faces[k].left() + faces[k].right()) / 2, int(faces[k].top() + faces[k].bottom()) / 2]) self.current_frame_face_X_e_distance_list = [] # 6.2.2.2 每个捕获人脸的名字坐标 / Positions of faces captured self.current_frame_face_position_list.append(tuple( [faces[k].left(), int(faces[k].bottom() + (faces[k].bottom() - faces[k].top()) / 4)])) # 6.2.2.3 对于某张人脸, 遍历所有存储的人脸特征 # For every faces detected, compare the faces in the database for i in range(len(self.face_features_known_list)): # 如果 q 数据不为空 if str(self.face_features_known_list[i][0]) != '0.0': e_distance_tmp = self.return_euclidean_distance( self.current_frame_face_feature_list[k], self.face_features_known_list[i]) logging.debug(" with person %d, the e-distance: %f", i + 1, e_distance_tmp) self.current_frame_face_X_e_distance_list.append(e_distance_tmp) else: # 空数据 person_X self.current_frame_face_X_e_distance_list.append(999999999) # 6.2.2.4 寻找出最小的欧式距离匹配 / Find the one with minimum e distance similar_person_num = self.current_frame_face_X_e_distance_list.index( min(self.current_frame_face_X_e_distance_list)) if min(self.current_frame_face_X_e_distance_list) < 0.4: self.current_frame_face_name_list[k] = self.face_name_known_list[similar_person_num] logging.debug(" Face recognition result: %s", self.face_name_known_list[similar_person_num]) else: logging.debug(" Face recognition result: Unknown person") # 7. 生成的窗口添加说明文字 / Add note on cv2 window self.draw_note(img_rd) # cv2.imwrite("debug/debug_" + str(self.frame_cnt) + ".png", img_rd) # Dump current frame image if needed # 8. 按下 'q' 键退出 / Press 'q' to exit if kk == ord('q'): break self.update_fps() cv2.namedWindow("camera", 1) cv2.imshow("camera", img_rd) logging.debug("Frame ends\n\n") def run(self): # cap = cv2.VideoCapture("video.mp4") # Get video stream from video file cap = cv2.VideoCapture(0) # Get video stream from camera self.process(cap) cap.release() cv2.destroyAllWindows() def main(): # logging.basicConfig(level=logging.DEBUG) # Set log level to 'logging.DEBUG' to print debug info of every frame logging.basicConfig(level=logging.INFO) Face_Recognizer_con = Face_Recognizer() Face_Recognizer_con.run() if __name__ == '__main__': main()
调用摄像头进行实时特征描述子计算(face_descriptor_from_camera.py):
class Face_Descriptor: def __init__(self): self.frame_time = 0 self.frame_start_time = 0 self.fps = 0 self.frame_cnt = 0 def update_fps(self): now = time.time() self.frame_time = now - self.frame_start_time self.fps = 1.0 / self.frame_time self.frame_start_time = now def run(self): cap = cv2.VideoCapture(0) cap.set(3, 480) self.process(cap) cap.release() cv2.destroyAllWindows() def process(self, stream): while stream.isOpened(): flag, img_rd = stream.read() self.frame_cnt+=1 k = cv2.waitKey(1) print('- Frame ', self.frame_cnt, " starts:") timestamp1 = time.time() faces = detector(img_rd, 0) timestamp2 = time.time() print("--- Time used to `detector`: %s seconds ---" % (timestamp2 - timestamp1)) font = cv2.FONT_HERSHEY_SIMPLEX # 检测到人脸 if len(faces) != 0: for face in faces: timestamp3 = time.time() face_shape = predictor(img_rd, face) timestamp4 = time.time() print("--- Time used to `predictor`: %s seconds ---" % (timestamp4 - timestamp3)) timestamp5 = time.time() face_desc = face_reco_model.compute_face_descriptor(img_rd, face_shape) timestamp6 = time.time() print("--- Time used to `compute_face_descriptor:` %s seconds ---" % (timestamp6 - timestamp5)) # 添加说明 cv2.putText(img_rd, "Face descriptor", (20, 40), font, 1, (255, 255, 255), 1, cv2.LINE_AA) cv2.putText(img_rd, "FPS: " + str(self.fps.__round__(2)), (20, 100), font, 0.8, (0, 255, 0), 1, cv2.LINE_AA) cv2.putText(img_rd, "Faces: " + str(len(faces)), (20, 140), font, 0.8, (0, 255, 0), 1, cv2.LINE_AA) cv2.putText(img_rd, "S: Save current face", (20, 400), font, 0.8, (255, 255, 255), 1, cv2.LINE_AA) cv2.putText(img_rd, "Q: Quit", (20, 450), font, 0.8, (255, 255, 255), 1, cv2.LINE_AA) # 按下 'q' 键退出 if k == ord('q'): break self.update_fps() cv2.namedWindow("camera", 1) cv2.imshow("camera", img_rd) print('\n') def main(): Face_Descriptor_con = Face_Descriptor() Face_Descriptor_con.run() if __name__ == '__main__': main()
至此模块化代码已经介绍完毕。
5、总结
本项目的核心是dlib机器学习库函数的运用,如果能熟练运用此库,就能掌握此项目。
- 如果希望详细了解 dlib 的用法, 请参考 Dlib 官方 Python api 的网站
- 代码最好不要有中文路径;
- 人脸录入的时候先建文件夹再保存图片, 先
N
再S
/ PressN
beforeS
- 关于
face_reco_from_camera.py
人脸识别卡顿 FPS 低问题, 原因是特征描述子提取很费时间; 光跑face_descriptor_from_camera.py
中face_reco_model.compute_face_descriptor
在我的机器上得到的平均 FPS 在 5 左右 (检测在0.03s
, 特征描述子提取在0.158s
, 和已知人脸进行遍历对比在0.003s
左右); 所以主要提取特征时候耗资源, 可以用 OT 去做追踪 (使用face_reco_from_camera_ot.py
), 而不是对每一帧都做检测+识别, 识别的性能从 20 FPS -> 200 FPS