from typing import List, Optional import numpy from facefusion import face_store, state_manager from facefusion.common_helper import get_first, get_middle from facefusion.face_classifier import classify_face from facefusion.face_detector import detect_faces, detect_faces_by_angle from facefusion.face_helper import apply_nms, average_points, convert_to_face_landmark_5, estimate_face_angle, get_nms_threshold from facefusion.face_landmarker import detect_face_landmark, estimate_face_landmark_68_5 from facefusion.face_recognizer import calculate_face_embedding from facefusion.types import BoundingBox, Face, FaceLandmark5, FaceLandmarkSet, FaceScoreSet, Score, VisionFrame from facefusion.vision import is_vision_frame def create_faces(vision_frame : VisionFrame, bounding_boxes : List[BoundingBox], face_scores : List[Score], face_landmarks_5 : List[FaceLandmark5]) -> List[Face]: faces = [] nms_threshold = get_nms_threshold(state_manager.get_item('face_detector_model'), state_manager.get_item('face_detector_angles')) keep_indices = apply_nms(bounding_boxes, face_scores, state_manager.get_item('face_detector_score'), nms_threshold) for index in keep_indices: bounding_box = bounding_boxes[index] face_score = face_scores[index] face_landmark_5 = face_landmarks_5[index] face_landmark_5_68 = face_landmark_5 face_landmark_68_5 = estimate_face_landmark_68_5(face_landmark_5_68) face_landmark_68 = face_landmark_68_5 face_landmark_score_68 = 0.0 face_angle = estimate_face_angle(face_landmark_68_5) if state_manager.get_item('face_landmarker_score') > 0: face_landmark_68, face_landmark_score_68 = detect_face_landmark(vision_frame, bounding_box, face_angle) if face_landmark_score_68 > state_manager.get_item('face_landmarker_score'): face_landmark_5_68 = convert_to_face_landmark_5(face_landmark_68) face_landmark_set : FaceLandmarkSet =\ { '5': face_landmark_5, '5/68': face_landmark_5_68, '68': face_landmark_68, '68/5': face_landmark_68_5 } face_score_set : FaceScoreSet =\ { 'detector': face_score, 'landmarker': face_landmark_score_68 } face_embedding, face_embedding_norm = calculate_face_embedding(vision_frame, face_landmark_set.get('5/68')) gender, age, race = classify_face(vision_frame, face_landmark_set.get('5/68')) faces.append(Face( origin = 'detect', bounding_box = bounding_box, score_set = face_score_set, landmark_set = face_landmark_set, angle = face_angle, embedding = face_embedding, embedding_norm = face_embedding_norm, gender = gender, age = age, race = race )) return faces def get_one_face(faces : List[Face], position : int = 0) -> Optional[Face]: if faces: position = min(position, len(faces) - 1) return faces[position] return None def get_many_faces(vision_frames : List[VisionFrame]) -> List[Face]: many_faces : List[Face] = [] for vision_frame in vision_frames: if is_vision_frame(vision_frame): all_bounding_boxes = [] all_face_scores = [] all_face_landmarks_5 = [] for face_detector_angle in state_manager.get_item('face_detector_angles'): if face_detector_angle == 0: bounding_boxes, face_scores, face_landmarks_5 = detect_faces(vision_frame) else: bounding_boxes, face_scores, face_landmarks_5 = detect_faces_by_angle(vision_frame, face_detector_angle) all_bounding_boxes.extend(bounding_boxes) all_face_scores.extend(face_scores) all_face_landmarks_5.extend(face_landmarks_5) if all_bounding_boxes and all_face_scores and all_face_landmarks_5 and state_manager.get_item('face_detector_score') > 0: faces = create_faces(vision_frame, all_bounding_boxes, all_face_scores, all_face_landmarks_5) if faces: many_faces.extend(faces) return many_faces def get_static_faces(vision_frames : List[VisionFrame]) -> List[Face]: many_faces : List[Face] = [] for vision_frame in vision_frames: faces = face_store.get_faces(vision_frame) if not faces: with face_store.resolve_lock(vision_frame): faces = face_store.get_faces(vision_frame) if not faces: faces = get_many_faces([ vision_frame ]) if faces: face_store.set_faces(vision_frame, faces) many_faces.extend(faces) return many_faces def refill_faces(faces : List[Optional[Face]]) -> List[Face]: fill_faces = [] anchor_index_previous = -1 for index, face in enumerate(faces): if face: for gap_index in range(anchor_index_previous + 1, index): average_factor = (gap_index - anchor_index_previous) / (index - anchor_index_previous) average_face = average_face_geometry([faces[anchor_index_previous], face], average_factor) fill_faces.append(average_face) fill_faces.append(face) anchor_index_previous = index return fill_faces def average_face_geometry(faces : List[Face], average_factor : float) -> Face: face_first = get_first(faces) face_middle = get_middle(faces) face_anchor = face_middle if average_factor < 0.5: face_anchor = face_first landmark_set : FaceLandmarkSet =\ { '5': average_points(face_first.landmark_set.get('5'), face_middle.landmark_set.get('5'), average_factor), '5/68': average_points(face_first.landmark_set.get('5/68'), face_middle.landmark_set.get('5/68'), average_factor), '68': average_points(face_first.landmark_set.get('68'), face_middle.landmark_set.get('68'), average_factor), '68/5': average_points(face_first.landmark_set.get('68/5'), face_middle.landmark_set.get('68/5'), average_factor) } return Face( origin = 'refill', bounding_box = average_points(face_first.bounding_box, face_middle.bounding_box, average_factor), score_set = face_anchor.score_set, landmark_set = landmark_set, angle = estimate_face_angle(landmark_set.get('68/5')), embedding = face_anchor.embedding, embedding_norm = face_anchor.embedding_norm, gender = face_anchor.gender, age = face_anchor.age, race = face_anchor.race ) def average_face_identity(faces : List[Face]) -> Optional[Face]: face_embeddings = [] face_embeddings_norm = [] if faces: first_face = get_first(faces) for face in faces: face_embeddings.append(face.embedding) face_embeddings_norm.append(face.embedding_norm) return Face( origin = first_face.origin, bounding_box = first_face.bounding_box, score_set = first_face.score_set, landmark_set = first_face.landmark_set, angle = first_face.angle, embedding = numpy.mean(face_embeddings, axis = 0), embedding_norm = numpy.mean(face_embeddings_norm, axis = 0), gender = first_face.gender, age = first_face.age, race = first_face.race ) return None def scale_face(target_face : Face, target_vision_frame : VisionFrame, temp_vision_frame : VisionFrame) -> Face: scale_x = temp_vision_frame.shape[1] / target_vision_frame.shape[1] scale_y = temp_vision_frame.shape[0] / target_vision_frame.shape[0] bounding_box = target_face.bounding_box * [ scale_x, scale_y, scale_x, scale_y ] landmark_set =\ { '5': target_face.landmark_set.get('5') * numpy.array([ scale_x, scale_y ]), '5/68': target_face.landmark_set.get('5/68') * numpy.array([ scale_x, scale_y ]), '68': target_face.landmark_set.get('68') * numpy.array([ scale_x, scale_y ]), '68/5': target_face.landmark_set.get('68/5') * numpy.array([ scale_x, scale_y ]) } return target_face._replace( bounding_box = bounding_box, landmark_set = landmark_set )