diff --git a/facefusion/face_analyser.py b/facefusion/face_analyser.py index 8ed0941b..1078931a 100644 --- a/facefusion/face_analyser.py +++ b/facefusion/face_analyser.py @@ -8,7 +8,7 @@ import facefusion.globals from facefusion.download import conditional_download from facefusion.face_store import get_static_faces, set_static_faces from facefusion.execution_helper import apply_execution_provider_options -from facefusion.face_helper import warp_face_by_kps, create_static_anchors, distance_to_kps, distance_to_bbox, apply_nms +from facefusion.face_helper import warp_face_by_kps, create_static_anchors, distance_to_kps, distance_to_bbox, apply_nms, categorize_age, categorize_gender from facefusion.filesystem import resolve_relative_path from facefusion.typing import VisionFrame, Face, FaceSet, FaceAnalyserOrder, FaceAnalyserAge, FaceAnalyserGender, ModelSet, Bbox, Kps, Score, Embedding from facefusion.vision import resize_frame_resolution, unpack_resolution @@ -389,13 +389,7 @@ def sort_by_order(faces : List[Face], order : FaceAnalyserOrder) -> List[Face]: def filter_by_age(faces : List[Face], age : FaceAnalyserAge) -> List[Face]: filter_faces = [] for face in faces: - if face.age < 13 and age == 'child': - filter_faces.append(face) - elif face.age < 19 and age == 'teen': - filter_faces.append(face) - elif face.age < 60 and age == 'adult': - filter_faces.append(face) - elif face.age > 59 and age == 'senior': + if categorize_age(face.age) == age: filter_faces.append(face) return filter_faces @@ -403,8 +397,6 @@ def filter_by_age(faces : List[Face], age : FaceAnalyserAge) -> List[Face]: def filter_by_gender(faces : List[Face], gender : FaceAnalyserGender) -> List[Face]: filter_faces = [] for face in faces: - if face.gender == 0 and gender == 'female': - filter_faces.append(face) - if face.gender == 1 and gender == 'male': + if categorize_gender(face.gender) == gender: filter_faces.append(face) return filter_faces diff --git a/facefusion/face_helper.py b/facefusion/face_helper.py index 72151972..6a24e2de 100644 --- a/facefusion/face_helper.py +++ b/facefusion/face_helper.py @@ -4,7 +4,7 @@ from functools import lru_cache import cv2 import numpy -from facefusion.typing import Bbox, Kps, VisionFrame, Mask, Matrix, Template +from facefusion.typing import Bbox, Kps, VisionFrame, Mask, Matrix, Template, FaceAnalyserAge, FaceAnalyserGender TEMPLATES : Dict[Template, numpy.ndarray[Any, Any]] =\ { @@ -121,3 +121,19 @@ def apply_nms(bbox_list : List[Bbox], iou_threshold : float) -> List[int]: iou = width * height / (areas[index] + areas[remain_indices] - width * height) indices = indices[numpy.where(iou <= iou_threshold)[0] + 1] return keep_indices + + +def categorize_age(age : int) -> FaceAnalyserAge: + if age < 13: + return 'child' + elif age < 19: + return 'teen' + elif age < 60: + return 'adult' + return 'senior' + + +def categorize_gender(gender : int) -> FaceAnalyserGender: + if gender == 0: + return 'female' + return 'male' diff --git a/facefusion/processors/frame/choices.py b/facefusion/processors/frame/choices.py index 5c2e440d..50261ec3 100755 --- a/facefusion/processors/frame/choices.py +++ b/facefusion/processors/frame/choices.py @@ -3,11 +3,10 @@ from typing import List from facefusion.common_helper import create_int_range from facefusion.processors.frame.typings import FaceDebuggerItem, FaceEnhancerModel, FaceSwapperModel, FrameEnhancerModel -face_debugger_items : List[FaceDebuggerItem] = [ 'bbox', 'kps', 'face-mask', 'score' ] +face_debugger_items : List[FaceDebuggerItem] = [ 'bbox', 'kps', 'face-mask', 'score', 'age', 'gender' ] face_enhancer_models : List[FaceEnhancerModel] = [ 'codeformer', 'gfpgan_1.2', 'gfpgan_1.3', 'gfpgan_1.4', 'gpen_bfr_256', 'gpen_bfr_512', 'restoreformer_plus_plus' ] face_swapper_models : List[FaceSwapperModel] = [ 'blendswap_256', 'inswapper_128', 'inswapper_128_fp16', 'simswap_256', 'simswap_512_unofficial' ] frame_enhancer_models : List[FrameEnhancerModel] = [ 'real_esrgan_x2plus', 'real_esrgan_x4plus', 'real_esrnet_x4plus' ] face_enhancer_blend_range : List[int] = create_int_range(0, 100, 1) frame_enhancer_blend_range : List[int] = create_int_range(0, 100, 1) - diff --git a/facefusion/processors/frame/modules/face_debugger.py b/facefusion/processors/frame/modules/face_debugger.py index 6cc1a14b..8a335905 100755 --- a/facefusion/processors/frame/modules/face_debugger.py +++ b/facefusion/processors/frame/modules/face_debugger.py @@ -11,7 +11,7 @@ from facefusion.face_store import get_reference_faces from facefusion.content_analyser import clear_content_analyser from facefusion.typing import Face, FaceSet, VisionFrame, Update_Process, ProcessMode from facefusion.vision import read_image, read_static_image, read_static_images, write_image -from facefusion.face_helper import warp_face_by_kps +from facefusion.face_helper import warp_face_by_kps, categorize_age, categorize_gender from facefusion.face_masker import create_static_box_mask, create_occlusion_mask, create_region_mask, clear_face_occluder, clear_face_parser from facefusion.processors.frame import globals as frame_processors_globals, choices as frame_processors_choices @@ -71,6 +71,7 @@ def debug_face(source_face : Face, target_face : Face, reference_faces : FaceSet secondary_color = (0, 255, 0) bounding_box = target_face.bbox.astype(numpy.int32) temp_frame = temp_frame.copy() + if 'bbox' in frame_processors_globals.face_debugger_items: cv2.rectangle(temp_frame, (bounding_box[0], bounding_box[1]), (bounding_box[2], bounding_box[3]), secondary_color, 2) if 'face-mask' in frame_processors_globals.face_debugger_items: @@ -92,14 +93,24 @@ def debug_face(source_face : Face, target_face : Face, reference_faces : FaceSet inverse_mask_contours = cv2.findContours(inverse_mask_frame, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)[0] cv2.drawContours(temp_frame, inverse_mask_contours, -1, primary_color, 2) if bounding_box[3] - bounding_box[1] > 60 and bounding_box[2] - bounding_box[0] > 60: + top = bounding_box[1] + left = bounding_box[0] + 20 if 'kps' in frame_processors_globals.face_debugger_items: kps = target_face.kps.astype(numpy.int32) for index in range(kps.shape[0]): cv2.circle(temp_frame, (kps[index][0], kps[index][1]), 3, primary_color, -1) if 'score' in frame_processors_globals.face_debugger_items: face_score_text = str(round(target_face.score, 2)) - face_score_position = (bounding_box[0] + 20, bounding_box[1] + 20) - cv2.putText(temp_frame, face_score_text, face_score_position, cv2.FONT_HERSHEY_SIMPLEX, 0.5, secondary_color, 2) + top = top + 20 + cv2.putText(temp_frame, face_score_text, (left, top), cv2.FONT_HERSHEY_SIMPLEX, 0.5, secondary_color, 2) + if 'age' in frame_processors_globals.face_debugger_items: + face_age_text = categorize_age(target_face.age) + top = top + 20 + cv2.putText(temp_frame, face_age_text, (left, top), cv2.FONT_HERSHEY_SIMPLEX, 0.5, secondary_color, 2) + if 'gender' in frame_processors_globals.face_debugger_items: + face_gender_text = categorize_gender(target_face.gender) + top = top + 20 + cv2.putText(temp_frame, face_gender_text, (left, top), cv2.FONT_HERSHEY_SIMPLEX, 0.5, secondary_color, 2) return temp_frame @@ -129,6 +140,7 @@ def process_frames(source_paths : List[str], temp_frame_paths : List[str], updat source_frames = read_static_images(source_paths) source_face = get_average_face(source_frames) reference_faces = get_reference_faces() if 'reference' in facefusion.globals.face_selector_mode else None + for temp_frame_path in temp_frame_paths: temp_frame = read_image(temp_frame_path) result_frame = process_frame(source_face, reference_faces, temp_frame) diff --git a/facefusion/processors/frame/typings.py b/facefusion/processors/frame/typings.py index d4193a0f..95bdd2bf 100644 --- a/facefusion/processors/frame/typings.py +++ b/facefusion/processors/frame/typings.py @@ -1,6 +1,6 @@ from typing import Literal -FaceDebuggerItem = Literal['bbox', 'kps', 'face-mask', 'score'] +FaceDebuggerItem = Literal['bbox', 'kps', 'face-mask', 'score', 'age', 'gender'] FaceEnhancerModel = Literal['codeformer', 'gfpgan_1.2', 'gfpgan_1.3', 'gfpgan_1.4', 'gpen_bfr_256', 'gpen_bfr_512', 'restoreformer_plus_plus'] FaceSwapperModel = Literal['blendswap_256', 'inswapper_128', 'inswapper_128_fp16', 'simswap_256', 'simswap_512_unofficial'] FrameEnhancerModel = Literal['real_esrgan_x2plus', 'real_esrgan_x4plus', 'real_esrnet_x4plus']