From 6e67d7bff694d544c046915e91e0b8e2c2ec10ba Mon Sep 17 00:00:00 2001 From: Henry Ruhs Date: Wed, 20 Mar 2024 10:02:08 +0100 Subject: [PATCH] 2.4.1 (#442) * Update official url for cuda 12-2 wheels * Fix preview for audio to image * Prevent download loop when remote is unreachable * Prevent download loop when remote is unreachable * changes (#444) * Tidy up monkey patch * Use cpu core count for concurrency count * Dynamic concurrency_count for ideal Gradio performance * Conditional download face analyser models * Fix testing via pre_check() * Introduce checking to process manager for blocking the UI * Introduce checking to process manager for blocking the UI * Introduce checking to process manager for blocking the UI * Introduce checking to process manager for blocking the UI * Move the blocking while model download to the correct position * Remove unused imports --------- Co-authored-by: Harisreedhar <46858047+harisreedhar@users.noreply.github.com> --- facefusion/content_analyser.py | 7 +++- facefusion/download.py | 20 +++++------ facefusion/face_analyser.py | 33 ++++++++++++++----- facefusion/face_masker.py | 6 ++++ facefusion/installer.py | 3 +- facefusion/metadata.py | 2 +- facefusion/process_manager.py | 8 +++++ .../processors/frame/modules/face_enhancer.py | 5 +++ .../processors/frame/modules/face_swapper.py | 7 ++++ .../frame/modules/frame_enhancer.py | 5 +++ .../processors/frame/modules/lip_syncer.py | 5 +++ facefusion/typing.py | 2 +- facefusion/uis/components/face_analyser.py | 18 +++++----- .../components/frame_processors_options.py | 4 +-- facefusion/uis/components/preview.py | 25 +++++++------- facefusion/uis/core.py | 4 +++ facefusion/uis/layouts/benchmark.py | 4 ++- facefusion/uis/layouts/default.py | 4 ++- facefusion/uis/layouts/webcam.py | 4 ++- facefusion/uis/overrides.py | 13 ++++++++ tests/test_face_analyser.py | 6 +++- 21 files changed, 133 insertions(+), 52 deletions(-) create mode 100644 facefusion/uis/overrides.py diff --git a/facefusion/content_analyser.py b/facefusion/content_analyser.py index d7f5b375..7025977b 100644 --- a/facefusion/content_analyser.py +++ b/facefusion/content_analyser.py @@ -1,5 +1,6 @@ from typing import Any, Dict from functools import lru_cache +from time import sleep import threading import cv2 import numpy @@ -7,7 +8,7 @@ import onnxruntime from tqdm import tqdm import facefusion.globals -from facefusion import wording +from facefusion import process_manager, wording from facefusion.typing import VisionFrame, ModelValue, Fps from facefusion.execution import apply_execution_provider_options from facefusion.vision import get_video_frame, count_video_frame_total, read_image, detect_video_fps @@ -33,6 +34,8 @@ def get_content_analyser() -> Any: global CONTENT_ANALYSER with THREAD_LOCK: + while process_manager.is_checking(): + sleep(0.5) if CONTENT_ANALYSER is None: model_path = MODELS.get('open_nsfw').get('path') CONTENT_ANALYSER = onnxruntime.InferenceSession(model_path, providers = apply_execution_provider_options(facefusion.globals.execution_providers)) @@ -49,7 +52,9 @@ def pre_check() -> bool: if not facefusion.globals.skip_download: download_directory_path = resolve_relative_path('../.assets/models') model_url = MODELS.get('open_nsfw').get('url') + process_manager.check() conditional_download(download_directory_path, [ model_url ]) + process_manager.end() return True diff --git a/facefusion/download.py b/facefusion/download.py index 01d883b9..d5f12cfe 100644 --- a/facefusion/download.py +++ b/facefusion/download.py @@ -22,19 +22,19 @@ def conditional_download(download_directory_path : str, urls : List[str]) -> Non executor.submit(get_download_size, url) for url in urls: download_file_path = os.path.join(download_directory_path, os.path.basename(url)) - initial = os.path.getsize(download_file_path) if is_file(download_file_path) else 0 - total = get_download_size(url) - if initial < total: - with tqdm(total = total, initial = initial, desc = wording.get('downloading'), unit = 'B', unit_scale = True, unit_divisor = 1024, ascii = ' =', disable = facefusion.globals.log_level in [ 'warn', 'error' ]) as progress: + initial_size = os.path.getsize(download_file_path) if is_file(download_file_path) else 0 + download_size = get_download_size(url) + if initial_size < download_size: + with tqdm(total = download_size, initial = initial_size, desc = wording.get('downloading'), unit = 'B', unit_scale = True, unit_divisor = 1024, ascii = ' =', disable = facefusion.globals.log_level in [ 'warn', 'error' ]) as progress: subprocess.Popen([ 'curl', '--create-dirs', '--silent', '--insecure', '--location', '--continue-at', '-', '--output', download_file_path, url ]) - current = initial - while current < total: + current_size = initial_size + while current_size < download_size: if is_file(download_file_path): - current = os.path.getsize(download_file_path) - progress.update(current - progress.n) - if not is_download_done(url, download_file_path): + current_size = os.path.getsize(download_file_path) + progress.update(current_size - progress.n) + if download_size and not is_download_done(url, download_file_path): os.remove(download_file_path) - conditional_download(download_directory_path, [ url] ) + conditional_download(download_directory_path, [ url ]) @lru_cache(maxsize = None) diff --git a/facefusion/face_analyser.py b/facefusion/face_analyser.py index 6c1811db..75faffc0 100644 --- a/facefusion/face_analyser.py +++ b/facefusion/face_analyser.py @@ -1,10 +1,12 @@ from typing import Any, Optional, List, Tuple +from time import sleep import threading import cv2 import numpy import onnxruntime import facefusion.globals +from facefusion import process_manager from facefusion.common_helper import get_first from facefusion.face_helper import warp_face_by_face_landmark_5, warp_face_by_translation, create_static_anchors, distance_to_face_landmark_5, distance_to_bounding_box, convert_face_landmark_68_to_5, apply_nms, categorize_age, categorize_gender from facefusion.face_store import get_static_faces, set_static_faces @@ -77,6 +79,8 @@ def get_face_analyser() -> Any: face_detectors = {} with THREAD_LOCK: + while process_manager.is_checking(): + sleep(0.5) if FACE_ANALYSER is None: if facefusion.globals.face_detector_model in [ 'many', 'retinaface' ]: face_detector = onnxruntime.InferenceSession(MODELS.get('face_detector_retinaface').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_providers)) @@ -121,18 +125,29 @@ def pre_check() -> bool: download_directory_path = resolve_relative_path('../.assets/models') model_urls =\ [ - MODELS.get('face_detector_retinaface').get('url'), - MODELS.get('face_detector_scrfd').get('url'), - MODELS.get('face_detector_yoloface').get('url'), - MODELS.get('face_detector_yunet').get('url'), - MODELS.get('face_recognizer_arcface_blendswap').get('url'), - MODELS.get('face_recognizer_arcface_inswapper').get('url'), - MODELS.get('face_recognizer_arcface_simswap').get('url'), - MODELS.get('face_recognizer_arcface_uniface').get('url'), MODELS.get('face_landmarker').get('url'), - MODELS.get('gender_age').get('url'), + MODELS.get('gender_age').get('url') ] + + if facefusion.globals.face_detector_model in [ 'many', 'retinaface' ]: + model_urls.append(MODELS.get('face_detector_retinaface').get('url')) + if facefusion.globals.face_detector_model in [ 'many', 'scrfd' ]: + model_urls.append(MODELS.get('face_detector_scrfd').get('url')) + if facefusion.globals.face_detector_model in [ 'many', 'yoloface' ]: + model_urls.append(MODELS.get('face_detector_yoloface').get('url')) + if facefusion.globals.face_detector_model in [ 'yunet' ]: + model_urls.append(MODELS.get('face_detector_yunet').get('url')) + if facefusion.globals.face_recognizer_model == 'arcface_blendswap': + model_urls.append(MODELS.get('face_recognizer_arcface_blendswap').get('url')) + if facefusion.globals.face_recognizer_model == 'arcface_inswapper': + model_urls.append(MODELS.get('face_recognizer_arcface_inswapper').get('url')) + if facefusion.globals.face_recognizer_model == 'arcface_simswap': + model_urls.append(MODELS.get('face_recognizer_arcface_simswap').get('url')) + if facefusion.globals.face_recognizer_model == 'arcface_uniface': + model_urls.append(MODELS.get('face_recognizer_arcface_uniface').get('url')) + process_manager.check() conditional_download(download_directory_path, model_urls) + process_manager.end() return True diff --git a/facefusion/face_masker.py b/facefusion/face_masker.py index e71b2b09..062bc9db 100755 --- a/facefusion/face_masker.py +++ b/facefusion/face_masker.py @@ -1,12 +1,14 @@ from typing import Any, Dict, List from cv2.typing import Size from functools import lru_cache +from time import sleep import threading import cv2 import numpy import onnxruntime import facefusion.globals +from facefusion import process_manager from facefusion.typing import FaceLandmark68, VisionFrame, Mask, Padding, FaceMaskRegion, ModelSet from facefusion.execution import apply_execution_provider_options from facefusion.filesystem import resolve_relative_path @@ -57,6 +59,8 @@ def get_face_parser() -> Any: global FACE_PARSER with THREAD_LOCK: + while process_manager.is_checking(): + sleep(0.5) if FACE_PARSER is None: model_path = MODELS.get('face_parser').get('path') FACE_PARSER = onnxruntime.InferenceSession(model_path, providers = apply_execution_provider_options(facefusion.globals.execution_providers)) @@ -83,7 +87,9 @@ def pre_check() -> bool: MODELS.get('face_occluder').get('url'), MODELS.get('face_parser').get('url'), ] + process_manager.check() conditional_download(download_directory_path, model_urls) + process_manager.end() return True diff --git a/facefusion/installer.py b/facefusion/installer.py index 56ffbcde..1b5ec7b3 100644 --- a/facefusion/installer.py +++ b/facefusion/installer.py @@ -15,7 +15,6 @@ if platform.system().lower() == 'darwin': ONNXRUNTIMES['default'] = ('onnxruntime', '1.17.1') else: ONNXRUNTIMES['default'] = ('onnxruntime', '1.16.3') -if platform.system().lower() == 'linux' or platform.system().lower() == 'windows': ONNXRUNTIMES['cuda-12.2'] = ('onnxruntime-gpu', '1.17.1') ONNXRUNTIMES['cuda-11.8'] = ('onnxruntime-gpu', '1.16.3') ONNXRUNTIMES['openvino'] = ('onnxruntime-openvino', '1.16.0') @@ -71,6 +70,6 @@ def run(program : ArgumentParser) -> None: else: subprocess.call([ 'pip', 'uninstall', 'onnxruntime', onnxruntime_name, '-y', '-q' ]) if onnxruntime == 'cuda-12.2': - subprocess.call([ 'pip', 'install', onnxruntime_name + '==' + onnxruntime_version, '--extra-index-url', 'https://pkgs.dev.azure.com/onnxruntime/onnxruntime/_packaging/onnxruntime-cuda-12/pypi/simple', '--force-reinstall' ]) + subprocess.call([ 'pip', 'install', onnxruntime_name + '==' + onnxruntime_version, '--extra-index-url', 'https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple', '--force-reinstall' ]) else: subprocess.call([ 'pip', 'install', onnxruntime_name + '==' + onnxruntime_version, '--force-reinstall' ]) diff --git a/facefusion/metadata.py b/facefusion/metadata.py index d204858c..13673056 100644 --- a/facefusion/metadata.py +++ b/facefusion/metadata.py @@ -2,7 +2,7 @@ METADATA =\ { 'name': 'FaceFusion', 'description': 'Next generation face swapper and enhancer', - 'version': '2.4.0', + 'version': '2.4.1', 'license': 'MIT', 'author': 'Henry Ruhs', 'url': 'https://facefusion.io' diff --git a/facefusion/process_manager.py b/facefusion/process_manager.py index 1983528a..3d5cce0f 100644 --- a/facefusion/process_manager.py +++ b/facefusion/process_manager.py @@ -15,6 +15,10 @@ def set_process_state(process_state : ProcessState) -> None: PROCESS_STATE = process_state +def is_checking() -> bool: + return get_process_state() == 'checking' + + def is_processing() -> bool: return get_process_state() == 'processing' @@ -27,6 +31,10 @@ def is_pending() -> bool: return get_process_state() == 'pending' +def check() -> None: + set_process_state('checking') + + def start() -> None: set_process_state('processing') diff --git a/facefusion/processors/frame/modules/face_enhancer.py b/facefusion/processors/frame/modules/face_enhancer.py index a469a4bf..6e034f40 100755 --- a/facefusion/processors/frame/modules/face_enhancer.py +++ b/facefusion/processors/frame/modules/face_enhancer.py @@ -1,5 +1,6 @@ from typing import Any, List, Literal, Optional from argparse import ArgumentParser +from time import sleep import cv2 import threading import numpy @@ -87,6 +88,8 @@ def get_frame_processor() -> Any: global FRAME_PROCESSOR with THREAD_LOCK: + while process_manager.is_checking(): + sleep(0.5) if FRAME_PROCESSOR is None: model_path = get_options('model').get('path') FRAME_PROCESSOR = onnxruntime.InferenceSession(model_path, providers = apply_execution_provider_options(facefusion.globals.execution_providers)) @@ -131,7 +134,9 @@ def pre_check() -> bool: if not facefusion.globals.skip_download: download_directory_path = resolve_relative_path('../.assets/models') model_url = get_options('model').get('url') + process_manager.check() conditional_download(download_directory_path, [ model_url ]) + process_manager.end() return True diff --git a/facefusion/processors/frame/modules/face_swapper.py b/facefusion/processors/frame/modules/face_swapper.py index a890cdde..c7da87af 100755 --- a/facefusion/processors/frame/modules/face_swapper.py +++ b/facefusion/processors/frame/modules/face_swapper.py @@ -1,5 +1,6 @@ from typing import Any, List, Literal, Optional from argparse import ArgumentParser +from time import sleep import threading import numpy import onnx @@ -99,6 +100,8 @@ def get_frame_processor() -> Any: global FRAME_PROCESSOR with THREAD_LOCK: + while process_manager.is_checking(): + sleep(0.5) if FRAME_PROCESSOR is None: model_path = get_options('model').get('path') FRAME_PROCESSOR = onnxruntime.InferenceSession(model_path, providers = apply_execution_provider_options(facefusion.globals.execution_providers)) @@ -115,6 +118,8 @@ def get_model_matrix() -> Any: global MODEL_MATRIX with THREAD_LOCK: + while process_manager.is_checking(): + sleep(0.5) if MODEL_MATRIX is None: model_path = get_options('model').get('path') model = onnx.load(model_path) @@ -171,7 +176,9 @@ def pre_check() -> bool: if not facefusion.globals.skip_download: download_directory_path = resolve_relative_path('../.assets/models') model_url = get_options('model').get('url') + process_manager.check() conditional_download(download_directory_path, [ model_url ]) + process_manager.end() return True diff --git a/facefusion/processors/frame/modules/frame_enhancer.py b/facefusion/processors/frame/modules/frame_enhancer.py index 7209207e..e29ae84b 100644 --- a/facefusion/processors/frame/modules/frame_enhancer.py +++ b/facefusion/processors/frame/modules/frame_enhancer.py @@ -1,5 +1,6 @@ from typing import Any, List, Literal, Optional from argparse import ArgumentParser +from time import sleep import threading import cv2 import numpy @@ -69,6 +70,8 @@ def get_frame_processor() -> Any: global FRAME_PROCESSOR with THREAD_LOCK: + while process_manager.is_checking(): + sleep(0.5) if FRAME_PROCESSOR is None: model_path = get_options('model').get('path') FRAME_PROCESSOR = onnxruntime.InferenceSession(model_path, providers = apply_execution_provider_options(facefusion.globals.execution_providers)) @@ -113,7 +116,9 @@ def pre_check() -> bool: if not facefusion.globals.skip_download: download_directory_path = resolve_relative_path('../.assets/models') model_url = get_options('model').get('url') + process_manager.check() conditional_download(download_directory_path, [ model_url ]) + process_manager.end() return True diff --git a/facefusion/processors/frame/modules/lip_syncer.py b/facefusion/processors/frame/modules/lip_syncer.py index 33c27af8..25a4439b 100755 --- a/facefusion/processors/frame/modules/lip_syncer.py +++ b/facefusion/processors/frame/modules/lip_syncer.py @@ -1,5 +1,6 @@ from typing import Any, List, Literal, Optional from argparse import ArgumentParser +from time import sleep import threading import cv2 import numpy @@ -45,6 +46,8 @@ def get_frame_processor() -> Any: global FRAME_PROCESSOR with THREAD_LOCK: + while process_manager.is_checking(): + sleep(0.5) if FRAME_PROCESSOR is None: model_path = get_options('model').get('path') FRAME_PROCESSOR = onnxruntime.InferenceSession(model_path, providers = apply_execution_provider_options(facefusion.globals.execution_providers)) @@ -87,7 +90,9 @@ def pre_check() -> bool: if not facefusion.globals.skip_download: download_directory_path = resolve_relative_path('../.assets/models') model_url = get_options('model').get('url') + process_manager.check() conditional_download(download_directory_path, [ model_url ]) + process_manager.end() return True diff --git a/facefusion/typing.py b/facefusion/typing.py index e6aefc22..e16bcb1a 100755 --- a/facefusion/typing.py +++ b/facefusion/typing.py @@ -49,7 +49,7 @@ Fps = float Padding = Tuple[int, int, int, int] Resolution = Tuple[int, int] -ProcessState = Literal['processing', 'stopping', 'pending'] +ProcessState = Literal['checking', 'processing', 'stopping', 'pending'] QueuePayload = TypedDict('QueuePayload', { 'frame_number' : int, diff --git a/facefusion/uis/components/face_analyser.py b/facefusion/uis/components/face_analyser.py index b6f17b37..aed04182 100644 --- a/facefusion/uis/components/face_analyser.py +++ b/facefusion/uis/components/face_analyser.py @@ -1,10 +1,10 @@ -from typing import Optional, Dict, Any +from typing import Optional, Dict, Any, Tuple import gradio import facefusion.globals import facefusion.choices -from facefusion import wording +from facefusion import face_analyser, wording from facefusion.typing import FaceAnalyserOrder, FaceAnalyserAge, FaceAnalyserGender, FaceDetectorModel from facefusion.uis.core import register_ui_component @@ -83,7 +83,7 @@ def listen() -> None: FACE_ANALYSER_ORDER_DROPDOWN.change(update_face_analyser_order, inputs = FACE_ANALYSER_ORDER_DROPDOWN) FACE_ANALYSER_AGE_DROPDOWN.change(update_face_analyser_age, inputs = FACE_ANALYSER_AGE_DROPDOWN) FACE_ANALYSER_GENDER_DROPDOWN.change(update_face_analyser_gender, inputs = FACE_ANALYSER_GENDER_DROPDOWN) - FACE_DETECTOR_MODEL_DROPDOWN.change(update_face_detector_model, inputs = FACE_DETECTOR_MODEL_DROPDOWN, outputs = FACE_DETECTOR_SIZE_DROPDOWN) + FACE_DETECTOR_MODEL_DROPDOWN.change(update_face_detector_model, inputs = FACE_DETECTOR_MODEL_DROPDOWN, outputs = [ FACE_DETECTOR_MODEL_DROPDOWN, FACE_DETECTOR_SIZE_DROPDOWN ]) FACE_DETECTOR_SIZE_DROPDOWN.change(update_face_detector_size, inputs = FACE_DETECTOR_SIZE_DROPDOWN) FACE_DETECTOR_SCORE_SLIDER.release(update_face_detector_score, inputs = FACE_DETECTOR_SCORE_SLIDER) FACE_LANDMARKER_SCORE_SLIDER.release(update_face_landmarker_score, inputs = FACE_LANDMARKER_SCORE_SLIDER) @@ -101,12 +101,14 @@ def update_face_analyser_gender(face_analyser_gender : FaceAnalyserGender) -> No facefusion.globals.face_analyser_gender = face_analyser_gender if face_analyser_gender != 'none' else None -def update_face_detector_model(face_detector_model : FaceDetectorModel) -> gradio.Dropdown: +def update_face_detector_model(face_detector_model : FaceDetectorModel) -> Tuple[gradio.Dropdown, gradio.Dropdown]: facefusion.globals.face_detector_model = face_detector_model - facefusion.globals.face_detector_size = '640x640' - if facefusion.globals.face_detector_size in facefusion.choices.face_detector_set[face_detector_model]: - return gradio.Dropdown(value = facefusion.globals.face_detector_size, choices = facefusion.choices.face_detector_set[face_detector_model]) - return gradio.Dropdown(value = facefusion.globals.face_detector_size, choices = [ facefusion.globals.face_detector_size ]) + update_face_detector_size('640x640') + if face_analyser.pre_check(): + if facefusion.globals.face_detector_size in facefusion.choices.face_detector_set[face_detector_model]: + return gradio.Dropdown(value = facefusion.globals.face_detector_model), gradio.Dropdown(value = facefusion.globals.face_detector_size, choices = facefusion.choices.face_detector_set[face_detector_model]) + return gradio.Dropdown(value = facefusion.globals.face_detector_model), gradio.Dropdown(value = facefusion.globals.face_detector_size, choices = [ facefusion.globals.face_detector_size ]) + return gradio.Dropdown(), gradio.Dropdown() def update_face_detector_size(face_detector_size : str) -> None: diff --git a/facefusion/uis/components/frame_processors_options.py b/facefusion/uis/components/frame_processors_options.py index 85e0e8c7..74dcdf7d 100755 --- a/facefusion/uis/components/frame_processors_options.py +++ b/facefusion/uis/components/frame_processors_options.py @@ -2,7 +2,7 @@ from typing import List, Optional, Tuple import gradio import facefusion.globals -from facefusion import wording +from facefusion import face_analyser, wording from facefusion.processors.frame.core import load_frame_processor_module from facefusion.processors.frame import globals as frame_processors_globals, choices as frame_processors_choices from facefusion.processors.frame.typings import FaceDebuggerItem, FaceEnhancerModel, FaceSwapperModel, FrameEnhancerModel, LipSyncerModel @@ -134,7 +134,7 @@ def update_face_swapper_model(face_swapper_model : FaceSwapperModel) -> gradio.D face_swapper_module = load_frame_processor_module('face_swapper') face_swapper_module.clear_frame_processor() face_swapper_module.set_options('model', face_swapper_module.MODELS[face_swapper_model]) - if face_swapper_module.pre_check(): + if face_analyser.pre_check() and face_swapper_module.pre_check(): return gradio.Dropdown(value = frame_processors_globals.face_swapper_model) return gradio.Dropdown() diff --git a/facefusion/uis/components/preview.py b/facefusion/uis/components/preview.py index 7b0d8e73..5b6468ab 100755 --- a/facefusion/uis/components/preview.py +++ b/facefusion/uis/components/preview.py @@ -5,7 +5,7 @@ import gradio import numpy import facefusion.globals -from facefusion import wording, logger +from facefusion import logger, wording from facefusion.audio import get_audio_frame, create_empty_audio_frame from facefusion.common_helper import get_first from facefusion.core import conditional_append_reference_faces @@ -45,12 +45,11 @@ def render() -> None: source_frames = read_static_images(facefusion.globals.source_paths) source_face = get_average_face(source_frames) source_audio_path = get_first(filter_audio_paths(facefusion.globals.source_paths)) - if source_audio_path and facefusion.globals.output_video_fps: - source_audio_frame = get_audio_frame(source_audio_path, facefusion.globals.output_video_fps, facefusion.globals.reference_frame_number) - if not numpy.any(source_audio_frame): - source_audio_frame = create_empty_audio_frame() - else: - source_audio_frame = None + source_audio_frame = create_empty_audio_frame() + if source_audio_path and facefusion.globals.output_video_fps and facefusion.globals.reference_frame_number: + temp_audio_frame = get_audio_frame(source_audio_path, facefusion.globals.output_video_fps, facefusion.globals.reference_frame_number) + if numpy.any(temp_audio_frame): + source_audio_frame = temp_audio_frame if is_image(facefusion.globals.target_path): target_vision_frame = read_static_image(facefusion.globals.target_path) preview_vision_frame = process_preview_frame(reference_faces, source_face, source_audio_frame, target_vision_frame) @@ -142,7 +141,6 @@ def clear_and_update_preview_image(frame_number : int = 0) -> gradio.Image: clear_face_analyser() clear_reference_faces() clear_static_faces() - sleep(0.5) return update_preview_image(frame_number) @@ -158,15 +156,14 @@ def update_preview_image(frame_number : int = 0) -> gradio.Image: source_frames = read_static_images(facefusion.globals.source_paths) source_face = get_average_face(source_frames) source_audio_path = get_first(filter_audio_paths(facefusion.globals.source_paths)) - if source_audio_path and facefusion.globals.output_video_fps: + source_audio_frame = create_empty_audio_frame() + if source_audio_path and facefusion.globals.output_video_fps and facefusion.globals.reference_frame_number: reference_audio_frame_number = facefusion.globals.reference_frame_number if facefusion.globals.trim_frame_start: reference_audio_frame_number -= facefusion.globals.trim_frame_start - source_audio_frame = get_audio_frame(source_audio_path, facefusion.globals.output_video_fps, reference_audio_frame_number) - if not numpy.any(source_audio_frame): - source_audio_frame = create_empty_audio_frame() - else: - source_audio_frame = None + temp_audio_frame = get_audio_frame(source_audio_path, facefusion.globals.output_video_fps, reference_audio_frame_number) + if numpy.any(temp_audio_frame): + source_audio_frame = temp_audio_frame if is_image(facefusion.globals.target_path): target_vision_frame = read_static_image(facefusion.globals.target_path) preview_vision_frame = process_preview_frame(reference_faces, source_face, source_audio_frame, target_vision_frame) diff --git a/facefusion/uis/core.py b/facefusion/uis/core.py index 9ce0ee53..68cdd86e 100644 --- a/facefusion/uis/core.py +++ b/facefusion/uis/core.py @@ -5,10 +5,14 @@ import sys import gradio import facefusion.globals +from facefusion.uis import overrides from facefusion import metadata, logger, wording from facefusion.uis.typing import Component, ComponentName from facefusion.filesystem import resolve_relative_path +gradio.processing_utils.encode_array_to_base64 = overrides.encode_array_to_base64 +gradio.processing_utils.encode_pil_to_base64 = overrides.encode_pil_to_base64 + UI_COMPONENTS: Dict[ComponentName, Component] = {} UI_LAYOUT_MODULES : List[ModuleType] = [] UI_LAYOUT_METHODS =\ diff --git a/facefusion/uis/layouts/benchmark.py b/facefusion/uis/layouts/benchmark.py index 2943fe2f..8168bf1d 100644 --- a/facefusion/uis/layouts/benchmark.py +++ b/facefusion/uis/layouts/benchmark.py @@ -1,3 +1,4 @@ +import multiprocessing import gradio import facefusion.globals @@ -61,4 +62,5 @@ def listen() -> None: def run(ui : gradio.Blocks) -> None: - ui.queue(concurrency_count = 2).launch(show_api = False, quiet = True) + concurrency_count = min(2, multiprocessing.cpu_count()) + ui.queue(concurrency_count = concurrency_count).launch(show_api = False, quiet = True) diff --git a/facefusion/uis/layouts/default.py b/facefusion/uis/layouts/default.py index 3c6fd99d..eb3f07dc 100755 --- a/facefusion/uis/layouts/default.py +++ b/facefusion/uis/layouts/default.py @@ -1,3 +1,4 @@ +import multiprocessing import gradio from facefusion.uis.components import about, frame_processors, frame_processors_options, execution, execution_thread_count, execution_queue_count, memory, temp_frame, output_options, common_options, source, target, output, preview, trim_frame, face_analyser, face_selector, face_masker @@ -75,4 +76,5 @@ def listen() -> None: def run(ui : gradio.Blocks) -> None: - ui.queue(concurrency_count = 4).launch(show_api = False, quiet = True) + concurrency_count = min(8, multiprocessing.cpu_count()) + ui.queue(concurrency_count = concurrency_count).launch(show_api = False, quiet = True) diff --git a/facefusion/uis/layouts/webcam.py b/facefusion/uis/layouts/webcam.py index 9d23190d..016a0486 100644 --- a/facefusion/uis/layouts/webcam.py +++ b/facefusion/uis/layouts/webcam.py @@ -1,3 +1,4 @@ +import multiprocessing import gradio from facefusion.uis.components import about, frame_processors, frame_processors_options, execution, execution_thread_count, webcam_options, source, webcam @@ -44,4 +45,5 @@ def listen() -> None: def run(ui : gradio.Blocks) -> None: - ui.queue(concurrency_count = 2).launch(show_api = False, quiet = True) + concurrency_count = min(2, multiprocessing.cpu_count()) + ui.queue(concurrency_count = concurrency_count).launch(show_api = False, quiet = True) diff --git a/facefusion/uis/overrides.py b/facefusion/uis/overrides.py new file mode 100644 index 00000000..8fff726a --- /dev/null +++ b/facefusion/uis/overrides.py @@ -0,0 +1,13 @@ +from typing import Any +import cv2 +import numpy +import base64 + + +def encode_array_to_base64(array : numpy.ndarray[Any, Any]) -> str: + buffer = cv2.imencode('.jpg', array[:, :, ::-1])[1] + return 'data:image/jpg;base64,' + base64.b64encode(buffer.tobytes()).decode('utf-8') + + +def encode_pil_to_base64(image : Any) -> str: + return encode_array_to_base64(numpy.asarray(image)[:, :, ::-1]) diff --git a/tests/test_face_analyser.py b/tests/test_face_analyser.py index f4fa0dae..957dfc8d 100644 --- a/tests/test_face_analyser.py +++ b/tests/test_face_analyser.py @@ -3,7 +3,7 @@ import pytest import facefusion.globals from facefusion.download import conditional_download -from facefusion.face_analyser import clear_face_analyser, get_one_face +from facefusion.face_analyser import pre_check, clear_face_analyser, get_one_face from facefusion.typing import Face from facefusion.vision import read_static_image @@ -31,6 +31,7 @@ def test_get_one_face_with_retinaface() -> None: facefusion.globals.face_detector_model = 'retinaface' facefusion.globals.face_detector_size = '320x320' + pre_check() source_paths =\ [ '.assets/examples/source.jpg', @@ -49,6 +50,7 @@ def test_get_one_face_with_scrfd() -> None: facefusion.globals.face_detector_model = 'scrfd' facefusion.globals.face_detector_size = '640x640' + pre_check() source_paths =\ [ '.assets/examples/source.jpg', @@ -67,6 +69,7 @@ def test_get_one_face_with_yoloface() -> None: facefusion.globals.face_detector_model = 'yoloface' facefusion.globals.face_detector_size = '640x640' + pre_check() source_paths =\ [ '.assets/examples/source.jpg', @@ -85,6 +88,7 @@ def test_get_one_face_with_yunet() -> None: facefusion.globals.face_detector_model = 'yunet' facefusion.globals.face_detector_size = '640x640' + pre_check() source_paths =\ [ '.assets/examples/source.jpg',