diff --git a/facefusion/benchmarker.py b/facefusion/benchmarker.py index 07d88181..4ba16bd4 100644 --- a/facefusion/benchmarker.py +++ b/facefusion/benchmarker.py @@ -9,6 +9,7 @@ import facefusion.choices from facefusion import content_analyser, core, state_manager from facefusion.cli_helper import render_table from facefusion.download import conditional_download, resolve_download_url +from facefusion.face_store import clear_faces from facefusion.filesystem import get_file_extension from facefusion.types import BenchmarkCycleSet from facefusion.vision import count_video_frame_total, detect_video_fps @@ -63,6 +64,7 @@ def cycle(cycle_count : int) -> BenchmarkCycleSet: if state_manager.get_item('benchmark_mode') == 'cold': content_analyser.analyse_image.cache_clear() content_analyser.analyse_video.cache_clear() + clear_faces() start_time = perf_counter() core.conditional_process() diff --git a/facefusion/face_analyser.py b/facefusion/face_analyser.py index 135261ed..ea0acf9c 100644 --- a/facefusion/face_analyser.py +++ b/facefusion/face_analyser.py @@ -2,7 +2,7 @@ from typing import List, Optional import numpy -from facefusion import state_manager +from facefusion import state_manager, face_store from facefusion.common_helper import get_first from facefusion.face_classifier import classify_face from facefusion.face_detector import detect_faces, detect_faces_by_angle @@ -92,6 +92,21 @@ def get_average_face(faces : List[Face]) -> Optional[Face]: return None +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: + faces = get_many_faces([ vision_frame ]) + face_store.set_faces(vision_frame, faces) + + many_faces.extend(faces) + + return many_faces + + def get_many_faces(vision_frames : List[VisionFrame]) -> List[Face]: many_faces : List[Face] = [] @@ -115,6 +130,7 @@ def get_many_faces(vision_frames : List[VisionFrame]) -> List[Face]: if faces: many_faces.extend(faces) + return many_faces diff --git a/facefusion/face_selector.py b/facefusion/face_selector.py index 00459e9a..6b6545d3 100644 --- a/facefusion/face_selector.py +++ b/facefusion/face_selector.py @@ -3,7 +3,7 @@ from typing import List import numpy from facefusion import state_manager -from facefusion.face_analyser import get_many_faces, get_one_face +from facefusion.face_analyser import get_many_faces, get_one_face, get_static_faces from facefusion.types import Face, FaceSelectorOrder, Gender, Race, Score, VisionFrame @@ -19,7 +19,7 @@ def select_faces(reference_vision_frame : VisionFrame, target_vision_frame : Vis return [ target_face ] if state_manager.get_item('face_selector_mode') == 'reference': - reference_faces = get_many_faces([ reference_vision_frame ]) + reference_faces = get_static_faces([ reference_vision_frame ]) reference_faces = sort_and_filter_faces(reference_faces) reference_face = get_one_face(reference_faces, state_manager.get_item('reference_face_position')) if reference_face: diff --git a/facefusion/face_store.py b/facefusion/face_store.py new file mode 100644 index 00000000..0356378e --- /dev/null +++ b/facefusion/face_store.py @@ -0,0 +1,21 @@ +from typing import List, Optional + +from facefusion.hash_helper import create_hash +from facefusion.types import Face, FaceStore, VisionFrame + +FACE_STORE : FaceStore = {} + + +def get_faces(vision_frame : VisionFrame) -> Optional[List[Face]]: + vision_hash = create_hash(vision_frame.tobytes()) + return FACE_STORE.get(vision_hash) + + +def set_faces(vision_frame : VisionFrame, faces : List[Face]) -> None: + vision_hash = create_hash(vision_frame.tobytes()) + if vision_hash: + FACE_STORE[vision_hash] = faces + + +def clear_faces() -> None: + FACE_STORE.clear() diff --git a/facefusion/processors/modules/face_swapper/core.py b/facefusion/processors/modules/face_swapper/core.py index 4122a0af..368c1a63 100755 --- a/facefusion/processors/modules/face_swapper/core.py +++ b/facefusion/processors/modules/face_swapper/core.py @@ -12,7 +12,7 @@ from facefusion import config, content_analyser, face_classifier, face_detector, from facefusion.common_helper import get_first, is_macos from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url from facefusion.execution import has_execution_provider -from facefusion.face_analyser import get_average_face, get_many_faces, get_one_face, scale_face +from facefusion.face_analyser import get_average_face, get_one_face, get_static_faces, scale_face from facefusion.face_helper import paste_back, warp_face_by_face_landmark_5 from facefusion.face_masker import create_area_mask, create_box_mask, create_occlusion_mask, create_region_mask from facefusion.face_selector import select_faces, sort_faces_by_order @@ -566,7 +566,7 @@ def pre_process(mode : ProcessMode) -> bool: source_image_paths = filter_image_paths(state_manager.get_item('source_paths')) source_vision_frames = read_static_images(source_image_paths) - source_faces = get_many_faces(source_vision_frames) + source_faces = get_static_faces(source_vision_frames) if not get_one_face(source_faces): logger.error(translator.get('no_source_face_detected') + translator.get('exclamation_mark'), __name__) @@ -770,7 +770,7 @@ def extract_source_face(source_vision_frames : List[VisionFrame]) -> Optional[Fa if source_vision_frames: for source_vision_frame in source_vision_frames: - temp_faces = get_many_faces([source_vision_frame]) + temp_faces = get_static_faces([ source_vision_frame ]) temp_faces = sort_faces_by_order(temp_faces, 'large-small') if temp_faces: diff --git a/facefusion/types.py b/facefusion/types.py index 6d4949cd..afd4bf7e 100755 --- a/facefusion/types.py +++ b/facefusion/types.py @@ -46,6 +46,8 @@ Face = namedtuple('Face', 'age', 'race' ]) +FaceStore : TypeAlias = Dict[str, List[Face]] + Language = Literal['en'] Locales : TypeAlias = Dict[Language, Dict[str, Any]] LocalePoolSet : TypeAlias = Dict[str, Locales] diff --git a/tests/test_face_analyser.py b/tests/test_face_analyser.py index 52e395d2..4c8eb54d 100644 --- a/tests/test_face_analyser.py +++ b/tests/test_face_analyser.py @@ -5,6 +5,7 @@ import pytest from facefusion import face_classifier, face_detector, face_landmarker, face_recognizer, state_manager from facefusion.download import conditional_download from facefusion.face_analyser import get_many_faces +from facefusion.face_store import clear_faces from facefusion.vision import read_static_image from .assert_helper import get_test_example_file, get_test_examples_directory @@ -40,6 +41,7 @@ def before_each() -> None: face_detector.clear_inference_pool() face_landmarker.clear_inference_pool() face_recognizer.clear_inference_pool() + clear_faces() @pytest.mark.parametrize('face_detector_model, face_detector_size',