Compare commits

...
2 Commits
Author SHA1 Message Date
Henry RuhsandGitHub 666c15f9da Patch 3.5.2 (#1002)
* Remove old files

* Fix some spacing

* Introduce retry to download

* More testing

* Better installer scripting (#994)

* Better installer scripting

* Add migraphx installer support

* Add migraphx installer support

* Ignore issue

* Ignore issue

* Make --force-install optional
2025-12-13 08:37:15 +01:00
420d738a6b 3.5.1 (#985)
* Fix type for device id

* Fix type for device id

* Fix order

* Remove comma

* Replace Generator typing

* Replace Generator typing

* Conditional kill myself (#984)

* Introduce conditional remove mask

* fix

---------

Co-authored-by: harisreedhar <h4harisreedhar.s.s@gmail.com>

---------

Co-authored-by: harisreedhar <h4harisreedhar.s.s@gmail.com>
2025-11-19 17:39:42 +01:00
25 changed files with 98 additions and 93 deletions
+2 -2
View File
@@ -3,7 +3,7 @@ import os
import statistics import statistics
import tempfile import tempfile
from time import perf_counter from time import perf_counter
from typing import Generator, List from typing import Iterator, List
import facefusion.choices import facefusion.choices
from facefusion import content_analyser, core, state_manager from facefusion import content_analyser, core, state_manager
@@ -31,7 +31,7 @@ def pre_check() -> bool:
return True return True
def run() -> Generator[List[BenchmarkCycleSet], None, None]: def run() -> Iterator[List[BenchmarkCycleSet]]:
benchmark_resolutions = state_manager.get_item('benchmark_resolutions') benchmark_resolutions = state_manager.get_item('benchmark_resolutions')
benchmark_cycle_count = state_manager.get_item('benchmark_cycle_count') benchmark_cycle_count = state_manager.get_item('benchmark_cycle_count')
+5 -1
View File
@@ -16,7 +16,7 @@ def chain(*commands : List[Command]) -> List[Command]:
return list(itertools.chain(*commands)) return list(itertools.chain(*commands))
def head(url : str) -> List[Command]: def ping(url : str) -> List[Command]:
return [ '-I', url ] return [ '-I', url ]
@@ -26,3 +26,7 @@ def download(url : str, download_file_path : str) -> List[Command]:
def set_timeout(timeout : int) -> List[Command]: def set_timeout(timeout : int) -> List[Command]:
return [ '--connect-timeout', str(timeout) ] return [ '--connect-timeout', str(timeout) ]
def set_retry(retry : int) -> List[Command]:
return [ '--retry', str(retry) ]
+4 -3
View File
@@ -29,7 +29,8 @@ def conditional_download(download_directory_path : str, urls : List[str]) -> Non
with tqdm(total = download_size, initial = initial_size, desc = translator.get('downloading'), unit = 'B', unit_scale = True, unit_divisor = 1024, ascii = ' =', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress: with tqdm(total = download_size, initial = initial_size, desc = translator.get('downloading'), unit = 'B', unit_scale = True, unit_divisor = 1024, ascii = ' =', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress:
commands = curl_builder.chain( commands = curl_builder.chain(
curl_builder.download(url, download_file_path), curl_builder.download(url, download_file_path),
curl_builder.set_timeout(5) curl_builder.set_timeout(5),
curl_builder.set_retry(5)
) )
open_curl(commands) open_curl(commands)
current_size = initial_size current_size = initial_size
@@ -44,7 +45,7 @@ def conditional_download(download_directory_path : str, urls : List[str]) -> Non
@lru_cache(maxsize = 64) @lru_cache(maxsize = 64)
def get_static_download_size(url : str) -> int: def get_static_download_size(url : str) -> int:
commands = curl_builder.chain( commands = curl_builder.chain(
curl_builder.head(url), curl_builder.ping(url),
curl_builder.set_timeout(5) curl_builder.set_timeout(5)
) )
process = open_curl(commands) process = open_curl(commands)
@@ -62,7 +63,7 @@ def get_static_download_size(url : str) -> int:
@lru_cache(maxsize = 64) @lru_cache(maxsize = 64)
def ping_static_url(url : str) -> bool: def ping_static_url(url : str) -> bool:
commands = curl_builder.chain( commands = curl_builder.chain(
curl_builder.head(url), curl_builder.ping(url),
curl_builder.set_timeout(5) curl_builder.set_timeout(5)
) )
process = open_curl(commands) process = open_curl(commands)
+4 -4
View File
@@ -28,7 +28,7 @@ def get_available_execution_providers() -> List[ExecutionProvider]:
return available_execution_providers return available_execution_providers
def create_inference_session_providers(execution_device_id : str, execution_providers : List[ExecutionProvider]) -> List[InferenceSessionProvider]: def create_inference_session_providers(execution_device_id : int, execution_providers : List[ExecutionProvider]) -> List[InferenceSessionProvider]:
inference_session_providers : List[InferenceSessionProvider] = [] inference_session_providers : List[InferenceSessionProvider] = []
for execution_provider in execution_providers: for execution_provider in execution_providers:
@@ -89,10 +89,10 @@ def resolve_cudnn_conv_algo_search() -> str:
return 'EXHAUSTIVE' return 'EXHAUSTIVE'
def resolve_openvino_device_type(execution_device_id : str) -> str: def resolve_openvino_device_type(execution_device_id : int) -> str:
if execution_device_id == '0': if execution_device_id == 0:
return 'GPU' return 'GPU'
return 'GPU.' + execution_device_id return 'GPU.' + str(execution_device_id)
def run_nvidia_smi() -> subprocess.Popen[bytes]: def run_nvidia_smi() -> subprocess.Popen[bytes]:
+4 -4
View File
@@ -42,7 +42,7 @@ def get_inference_pool(module_name : str, model_names : List[str], model_source_
return INFERENCE_POOL_SET.get(app_context).get(current_inference_context) return INFERENCE_POOL_SET.get(app_context).get(current_inference_context)
def create_inference_pool(model_source_set : DownloadSet, execution_device_id : str, execution_providers : List[ExecutionProvider]) -> InferencePool: def create_inference_pool(model_source_set : DownloadSet, execution_device_id : int, execution_providers : List[ExecutionProvider]) -> InferencePool:
inference_pool : InferencePool = {} inference_pool : InferencePool = {}
for model_name in model_source_set.keys(): for model_name in model_source_set.keys():
@@ -67,7 +67,7 @@ def clear_inference_pool(module_name : str, model_names : List[str]) -> None:
del INFERENCE_POOL_SET[app_context][inference_context] del INFERENCE_POOL_SET[app_context][inference_context]
def create_inference_session(model_path : str, execution_device_id : str, execution_providers : List[ExecutionProvider]) -> InferenceSession: def create_inference_session(model_path : str, execution_device_id : int, execution_providers : List[ExecutionProvider]) -> InferenceSession:
model_file_name = get_file_name(model_path) model_file_name = get_file_name(model_path)
start_time = time() start_time = time()
@@ -82,8 +82,8 @@ def create_inference_session(model_path : str, execution_device_id : str, execut
fatal_exit(1) fatal_exit(1)
def get_inference_context(module_name : str, model_names : List[str], execution_device_id : str, execution_providers : List[ExecutionProvider]) -> str: def get_inference_context(module_name : str, model_names : List[str], execution_device_id : int, execution_providers : List[ExecutionProvider]) -> str:
inference_context = '.'.join([ module_name ] + model_names + [ execution_device_id ] + list(execution_providers)) inference_context = '.'.join([ module_name ] + model_names + [ str(execution_device_id) ] + list(execution_providers))
return inference_context return inference_context
+17 -8
View File
@@ -13,6 +13,7 @@ from facefusion.common_helper import is_linux, is_windows
LOCALS =\ LOCALS =\
{ {
'install_dependency': 'install the {dependency} package', 'install_dependency': 'install the {dependency} package',
'force_reinstall': 'force reinstall of packages',
'skip_conda': 'skip the conda environment check', 'skip_conda': 'skip the conda environment check',
'conda_not_activated': 'conda is not activated' 'conda_not_activated': 'conda is not activated'
} }
@@ -26,13 +27,15 @@ if is_windows() or is_linux():
if is_windows(): if is_windows():
ONNXRUNTIME_SET['directml'] = ('onnxruntime-directml', '1.23.0') ONNXRUNTIME_SET['directml'] = ('onnxruntime-directml', '1.23.0')
if is_linux(): if is_linux():
ONNXRUNTIME_SET['rocm'] = ('onnxruntime-rocm', '1.21.0') ONNXRUNTIME_SET['migraphx'] = ('onnxruntime-migraphx', '1.23.0')
ONNXRUNTIME_SET['rocm'] = ('onnxruntime_rocm', '1.22.1', '7.0.2') #type:ignore[assignment]
def cli() -> None: def cli() -> None:
signal.signal(signal.SIGINT, signal_exit) signal.signal(signal.SIGINT, signal_exit)
program = ArgumentParser(formatter_class = partial(HelpFormatter, max_help_position = 50)) program = ArgumentParser(formatter_class = partial(HelpFormatter, max_help_position = 50))
program.add_argument('--onnxruntime', help = LOCALS.get('install_dependency').format(dependency = 'onnxruntime'), choices = ONNXRUNTIME_SET.keys(), required = True) program.add_argument('--onnxruntime', help = LOCALS.get('install_dependency').format(dependency = 'onnxruntime'), choices = ONNXRUNTIME_SET.keys(), required = True)
program.add_argument('--force-reinstall', help = LOCALS.get('force_reinstall'), action = 'store_true')
program.add_argument('--skip-conda', help = LOCALS.get('skip_conda'), action = 'store_true') program.add_argument('--skip-conda', help = LOCALS.get('skip_conda'), action = 'store_true')
program.add_argument('-v', '--version', version = metadata.get('name') + ' ' + metadata.get('version'), action = 'version') program.add_argument('-v', '--version', version = metadata.get('name') + ' ' + metadata.get('version'), action = 'version')
run(program) run(program)
@@ -45,7 +48,10 @@ def signal_exit(signum : int, frame : FrameType) -> None:
def run(program : ArgumentParser) -> None: def run(program : ArgumentParser) -> None:
args = program.parse_args() args = program.parse_args()
has_conda = 'CONDA_PREFIX' in os.environ has_conda = 'CONDA_PREFIX' in os.environ
onnxruntime_name, onnxruntime_version = ONNXRUNTIME_SET.get(args.onnxruntime) commands = [ shutil.which('pip'), 'install' ]
if args.force_reinstall:
commands.append('--force-reinstall')
if not args.skip_conda and not has_conda: if not args.skip_conda and not has_conda:
sys.stdout.write(LOCALS.get('conda_not_activated') + os.linesep) sys.stdout.write(LOCALS.get('conda_not_activated') + os.linesep)
@@ -56,17 +62,21 @@ def run(program : ArgumentParser) -> None:
for line in file.readlines(): for line in file.readlines():
__line__ = line.strip() __line__ = line.strip()
if not __line__.startswith('onnxruntime'): if not __line__.startswith('onnxruntime'):
subprocess.call([ shutil.which('pip'), 'install', line, '--force-reinstall' ]) commands.append(__line__)
if args.onnxruntime == 'rocm': if args.onnxruntime == 'rocm':
onnxruntime_name, onnxruntime_version, rocm_version = ONNXRUNTIME_SET.get(args.onnxruntime) #type:ignore[misc]
python_id = 'cp' + str(sys.version_info.major) + str(sys.version_info.minor) python_id = 'cp' + str(sys.version_info.major) + str(sys.version_info.minor)
if python_id in [ 'cp310', 'cp312' ]: if python_id in [ 'cp310', 'cp312' ]:
wheel_name = 'onnxruntime_rocm-' + onnxruntime_version + '-' + python_id + '-' + python_id + '-linux_x86_64.whl' wheel_name = onnxruntime_name + '-' + onnxruntime_version + '-' + python_id + '-' + python_id + '-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl'
wheel_url = 'https://repo.radeon.com/rocm/manylinux/rocm-rel-6.4/' + wheel_name wheel_url = 'https://repo.radeon.com/rocm/manylinux/rocm-rel-' + rocm_version + '/' + wheel_name
subprocess.call([ shutil.which('pip'), 'install', wheel_url, '--force-reinstall' ]) commands.append(wheel_url)
else: else:
subprocess.call([ shutil.which('pip'), 'install', onnxruntime_name + '==' + onnxruntime_version, '--force-reinstall' ]) onnxruntime_name, onnxruntime_version = ONNXRUNTIME_SET.get(args.onnxruntime)
commands.append(onnxruntime_name + '==' + onnxruntime_version)
subprocess.call(commands)
if args.onnxruntime == 'cuda' and has_conda: if args.onnxruntime == 'cuda' and has_conda:
library_paths = [] library_paths = []
@@ -97,4 +107,3 @@ def run(program : ArgumentParser) -> None:
library_paths = list(dict.fromkeys([ library_path for library_path in library_paths if os.path.exists(library_path) ])) library_paths = list(dict.fromkeys([ library_path for library_path in library_paths if os.path.exists(library_path) ]))
subprocess.call([ shutil.which('conda'), 'env', 'config', 'vars', 'set', 'PATH=' + os.pathsep.join(library_paths) ]) subprocess.call([ shutil.which('conda'), 'env', 'config', 'vars', 'set', 'PATH=' + os.pathsep.join(library_paths) ])
+1 -1
View File
@@ -4,7 +4,7 @@ METADATA =\
{ {
'name': 'FaceFusion', 'name': 'FaceFusion',
'description': 'Industry leading face manipulation platform', 'description': 'Industry leading face manipulation platform',
'version': '3.5.0', 'version': '3.5.2',
'license': 'OpenRAIL-AS', 'license': 'OpenRAIL-AS',
'author': 'Henry Ruhs', 'author': 'Henry Ruhs',
'url': 'https://facefusion.io' 'url': 'https://facefusion.io'
-27
View File
@@ -1,27 +0,0 @@
from facefusion.processors.modules.age_modifier.choices import age_modifier_direction_range, age_modifier_models # noqa: F401
from facefusion.processors.modules.background_remover.choices import background_remover_color_range, background_remover_models # noqa: F401
from facefusion.processors.modules.deep_swapper.choices import deep_swapper_models, deep_swapper_morph_range # noqa: F401
from facefusion.processors.modules.expression_restorer.choices import expression_restorer_areas, expression_restorer_factor_range, expression_restorer_models # noqa: F401
from facefusion.processors.modules.face_debugger.choices import face_debugger_items # noqa: F401
from facefusion.processors.modules.face_editor.choices import ( # noqa: F401
face_editor_eye_gaze_horizontal_range,
face_editor_eye_gaze_vertical_range,
face_editor_eye_open_ratio_range,
face_editor_eyebrow_direction_range,
face_editor_head_pitch_range,
face_editor_head_roll_range,
face_editor_head_yaw_range,
face_editor_lip_open_ratio_range,
face_editor_models,
face_editor_mouth_grim_range,
face_editor_mouth_position_horizontal_range,
face_editor_mouth_position_vertical_range,
face_editor_mouth_pout_range,
face_editor_mouth_purse_range,
face_editor_mouth_smile_range,
)
from facefusion.processors.modules.face_enhancer.choices import face_enhancer_blend_range, face_enhancer_models, face_enhancer_weight_range # noqa: F401
from facefusion.processors.modules.face_swapper.choices import face_swapper_models, face_swapper_set, face_swapper_weight_range # noqa: F401
from facefusion.processors.modules.frame_colorizer.choices import frame_colorizer_blend_range, frame_colorizer_models, frame_colorizer_sizes # noqa: F401
from facefusion.processors.modules.frame_enhancer.choices import frame_enhancer_blend_range, frame_enhancer_models # noqa: F401
from facefusion.processors.modules.lip_syncer.choices import lip_syncer_models, lip_syncer_weight_range # noqa: F401
+1 -2
View File
@@ -234,7 +234,7 @@ def create_execution_program() -> ArgumentParser:
program = ArgumentParser(add_help = False) program = ArgumentParser(add_help = False)
available_execution_providers = get_available_execution_providers() available_execution_providers = get_available_execution_providers()
group_execution = program.add_argument_group('execution') group_execution = program.add_argument_group('execution')
group_execution.add_argument('--execution-device-ids', help = translator.get('help.execution_device_ids'), type = int, default = config.get_str_list('execution', 'execution_device_ids', '0'), nargs = '+', metavar = 'EXECUTION_DEVICE_IDS') group_execution.add_argument('--execution-device-ids', help = translator.get('help.execution_device_ids'), type = int, default = config.get_int_list('execution', 'execution_device_ids', '0'), nargs = '+', metavar = 'EXECUTION_DEVICE_IDS')
group_execution.add_argument('--execution-providers', help = translator.get('help.execution_providers').format(choices = ', '.join(available_execution_providers)), default = config.get_str_list('execution', 'execution_providers', get_first(available_execution_providers)), choices = available_execution_providers, nargs = '+', metavar = 'EXECUTION_PROVIDERS') group_execution.add_argument('--execution-providers', help = translator.get('help.execution_providers').format(choices = ', '.join(available_execution_providers)), default = config.get_str_list('execution', 'execution_providers', get_first(available_execution_providers)), choices = available_execution_providers, nargs = '+', metavar = 'EXECUTION_PROVIDERS')
group_execution.add_argument('--execution-thread-count', help = translator.get('help.execution_thread_count'), type = int, default = config.get_int_value('execution', 'execution_thread_count', '8'), choices = facefusion.choices.execution_thread_count_range, metavar = create_int_metavar(facefusion.choices.execution_thread_count_range)) group_execution.add_argument('--execution-thread-count', help = translator.get('help.execution_thread_count'), type = int, default = config.get_int_value('execution', 'execution_thread_count', '8'), choices = facefusion.choices.execution_thread_count_range, metavar = create_int_metavar(facefusion.choices.execution_thread_count_range))
job_store.register_job_keys([ 'execution_device_ids', 'execution_providers', 'execution_thread_count' ]) job_store.register_job_keys([ 'execution_device_ids', 'execution_providers', 'execution_thread_count' ])
@@ -269,7 +269,6 @@ def create_halt_on_error_program() -> ArgumentParser:
def create_job_id_program() -> ArgumentParser: def create_job_id_program() -> ArgumentParser:
program = ArgumentParser(add_help = False) program = ArgumentParser(add_help = False)
program.add_argument('job_id', help = translator.get('help.job_id')) program.add_argument('job_id', help = translator.get('help.job_id'))
job_store.register_job_keys([ 'job_id' ])
return program return program
+2 -2
View File
@@ -2,7 +2,7 @@ import os
import subprocess import subprocess
from collections import deque from collections import deque
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from typing import Deque, Generator from typing import Deque, Iterator
import cv2 import cv2
import numpy import numpy
@@ -18,7 +18,7 @@ from facefusion.types import Fps, StreamMode, VisionFrame
from facefusion.vision import extract_vision_mask, read_static_images from facefusion.vision import extract_vision_mask, read_static_images
def multi_process_capture(camera_capture : cv2.VideoCapture, camera_fps : Fps) -> Generator[VisionFrame, None, None]: def multi_process_capture(camera_capture : cv2.VideoCapture, camera_fps : Fps) -> Iterator[VisionFrame]:
capture_deque : Deque[VisionFrame] = deque() capture_deque : Deque[VisionFrame] = deque()
with tqdm(desc = translator.get('streaming'), unit = 'frame', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress: with tqdm(desc = translator.get('streaming'), unit = 'frame', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress:
+1 -1
View File
@@ -385,7 +385,7 @@ State = TypedDict('State',
'open_browser' : bool, 'open_browser' : bool,
'ui_layouts' : List[str], 'ui_layouts' : List[str],
'ui_workflow' : UiWorkflow, 'ui_workflow' : UiWorkflow,
'execution_device_ids' : List[str], 'execution_device_ids' : List[int],
'execution_providers' : List[ExecutionProvider], 'execution_providers' : List[ExecutionProvider],
'execution_thread_count' : int, 'execution_thread_count' : int,
'video_memory_strategy' : VideoMemoryStrategy, 'video_memory_strategy' : VideoMemoryStrategy,
+2 -2
View File
@@ -1,4 +1,4 @@
from typing import Any, Generator, List, Optional from typing import Any, Iterator, List, Optional
import gradio import gradio
@@ -44,7 +44,7 @@ def listen() -> None:
BENCHMARK_START_BUTTON.click(start, outputs = BENCHMARK_BENCHMARKS_DATAFRAME) BENCHMARK_START_BUTTON.click(start, outputs = BENCHMARK_BENCHMARKS_DATAFRAME)
def start() -> Generator[List[Any], None, None]: def start() -> Iterator[List[Any]]:
state_manager.sync_state() state_manager.sync_state()
for benchmark in benchmarker.run(): for benchmark in benchmarker.run():
+4 -4
View File
@@ -18,7 +18,7 @@ from facefusion.types import AudioFrame, Face, Mask, VisionFrame
from facefusion.uis import choices as uis_choices from facefusion.uis import choices as uis_choices
from facefusion.uis.core import get_ui_component, get_ui_components, register_ui_component from facefusion.uis.core import get_ui_component, get_ui_components, register_ui_component
from facefusion.uis.types import ComponentOptions, PreviewMode from facefusion.uis.types import ComponentOptions, PreviewMode
from facefusion.vision import conditional_merge_vision_mask, detect_frame_orientation, extract_vision_mask, fit_cover_frame, obscure_frame, read_static_image, read_static_images, read_video_frame, restrict_frame, unpack_resolution from facefusion.vision import detect_frame_orientation, extract_vision_mask, fit_cover_frame, merge_vision_mask, obscure_frame, read_static_image, read_static_images, read_video_frame, restrict_frame, unpack_resolution
PREVIEW_IMAGE : Optional[gradio.Image] = None PREVIEW_IMAGE : Optional[gradio.Image] = None
@@ -197,7 +197,7 @@ def update_preview_image(preview_mode : PreviewMode, preview_resolution : str, f
reference_vision_frame = read_static_image(state_manager.get_item('target_path')) reference_vision_frame = read_static_image(state_manager.get_item('target_path'))
target_vision_frame = read_static_image(state_manager.get_item('target_path'), 'rgba') target_vision_frame = read_static_image(state_manager.get_item('target_path'), 'rgba')
target_vision_mask = extract_vision_mask(target_vision_frame) target_vision_mask = extract_vision_mask(target_vision_frame)
target_vision_frame = conditional_merge_vision_mask(target_vision_frame, target_vision_mask) target_vision_frame = merge_vision_mask(target_vision_frame, target_vision_mask)
preview_vision_frame = process_preview_frame(reference_vision_frame, source_vision_frames, source_audio_frame, source_voice_frame, target_vision_frame, preview_mode, preview_resolution) preview_vision_frame = process_preview_frame(reference_vision_frame, source_vision_frames, source_audio_frame, source_voice_frame, target_vision_frame, preview_mode, preview_resolution)
preview_vision_frame = cv2.cvtColor(preview_vision_frame, cv2.COLOR_BGRA2RGBA) preview_vision_frame = cv2.cvtColor(preview_vision_frame, cv2.COLOR_BGRA2RGBA)
return gradio.Image(value = preview_vision_frame, elem_classes = [ 'image-preview', 'is-' + detect_frame_orientation(preview_vision_frame) ]) return gradio.Image(value = preview_vision_frame, elem_classes = [ 'image-preview', 'is-' + detect_frame_orientation(preview_vision_frame) ])
@@ -206,7 +206,7 @@ def update_preview_image(preview_mode : PreviewMode, preview_resolution : str, f
reference_vision_frame = read_video_frame(state_manager.get_item('target_path'), state_manager.get_item('reference_frame_number')) reference_vision_frame = read_video_frame(state_manager.get_item('target_path'), state_manager.get_item('reference_frame_number'))
temp_vision_frame = read_video_frame(state_manager.get_item('target_path'), frame_number) temp_vision_frame = read_video_frame(state_manager.get_item('target_path'), frame_number)
temp_vision_mask = extract_vision_mask(temp_vision_frame) temp_vision_mask = extract_vision_mask(temp_vision_frame)
temp_vision_frame = conditional_merge_vision_mask(temp_vision_frame, temp_vision_mask) temp_vision_frame = merge_vision_mask(temp_vision_frame, temp_vision_mask)
preview_vision_frame = process_preview_frame(reference_vision_frame, source_vision_frames, source_audio_frame, source_voice_frame, temp_vision_frame, preview_mode, preview_resolution) preview_vision_frame = process_preview_frame(reference_vision_frame, source_vision_frames, source_audio_frame, source_voice_frame, temp_vision_frame, preview_mode, preview_resolution)
preview_vision_frame = cv2.cvtColor(preview_vision_frame, cv2.COLOR_BGRA2RGBA) preview_vision_frame = cv2.cvtColor(preview_vision_frame, cv2.COLOR_BGRA2RGBA)
return gradio.Image(value = preview_vision_frame, elem_classes = [ 'image-preview', 'is-' + detect_frame_orientation(preview_vision_frame) ]) return gradio.Image(value = preview_vision_frame, elem_classes = [ 'image-preview', 'is-' + detect_frame_orientation(preview_vision_frame) ])
@@ -297,6 +297,6 @@ def extract_crop_frame(vision_frame : VisionFrame, face : Face) -> Optional[Visi
def prepare_output_frame(target_vision_frame : VisionFrame, temp_vision_frame : VisionFrame, temp_vision_mask : Mask) -> VisionFrame: def prepare_output_frame(target_vision_frame : VisionFrame, temp_vision_frame : VisionFrame, temp_vision_mask : Mask) -> VisionFrame:
temp_vision_mask = temp_vision_mask.clip(state_manager.get_item('background_remover_color')[-1], 255) temp_vision_mask = temp_vision_mask.clip(state_manager.get_item('background_remover_color')[-1], 255)
temp_vision_frame = conditional_merge_vision_mask(temp_vision_frame, temp_vision_mask) temp_vision_frame = merge_vision_mask(temp_vision_frame, temp_vision_mask)
temp_vision_frame = cv2.resize(temp_vision_frame, target_vision_frame.shape[1::-1]) temp_vision_frame = cv2.resize(temp_vision_frame, target_vision_frame.shape[1::-1])
return temp_vision_frame return temp_vision_frame
+2 -2
View File
@@ -1,4 +1,4 @@
from typing import Generator, List, Optional, Tuple from typing import Iterator, List, Optional, Tuple
import cv2 import cv2
import gradio import gradio
@@ -82,7 +82,7 @@ def pre_stop() -> Tuple[gradio.File, gradio.Image, gradio.Button, gradio.Button]
return gradio.File(visible = True), gradio.Image(visible = False), gradio.Button(visible = True), gradio.Button(visible = False) return gradio.File(visible = True), gradio.Image(visible = False), gradio.Button(visible = True), gradio.Button(visible = False)
def start(webcam_device_id : int, webcam_mode : WebcamMode, webcam_resolution : str, webcam_fps : Fps) -> Generator[VisionFrame, None, None]: def start(webcam_device_id : int, webcam_mode : WebcamMode, webcam_resolution : str, webcam_fps : Fps) -> Iterator[VisionFrame]:
state_manager.init_item('face_selector_mode', 'one') state_manager.init_item('face_selector_mode', 'one')
state_manager.sync_state() state_manager.sync_state()
+5 -1
View File
@@ -355,7 +355,11 @@ def extract_vision_mask(vision_frame : VisionFrame) -> Mask:
return numpy.full(vision_frame.shape[:2], 255, dtype = numpy.uint8) return numpy.full(vision_frame.shape[:2], 255, dtype = numpy.uint8)
def merge_vision_mask(vision_frame : VisionFrame, vision_mask : Mask) -> VisionFrame:
return numpy.dstack((vision_frame[:, :, :3], vision_mask))
def conditional_merge_vision_mask(vision_frame : VisionFrame, vision_mask : Mask) -> VisionFrame: def conditional_merge_vision_mask(vision_frame : VisionFrame, vision_mask : Mask) -> VisionFrame:
if numpy.any(vision_mask < 255): if numpy.any(vision_mask < 255):
return numpy.dstack((vision_frame[:, :, :3], vision_mask)) return merge_vision_mask(vision_frame, vision_mask)
return vision_frame return vision_frame
+1 -1
View File
@@ -19,7 +19,7 @@ def process(start_time : float) -> ErrorCode:
setup, setup,
prepare_image, prepare_image,
process_image, process_image,
partial(finalize_image, start_time), partial(finalize_image, start_time)
] ]
process_manager.start() process_manager.start()
+12 -12
View File
@@ -152,18 +152,6 @@ def restore_audio() -> ErrorCode:
return 0 return 0
def finalize_video(start_time : float) -> ErrorCode:
logger.debug(translator.get('clearing_temp'), __name__)
clear_temp_directory(state_manager.get_item('target_path'))
if is_video(state_manager.get_item('output_path')):
logger.info(translator.get('processing_video_succeeded').format(seconds = calculate_end_time(start_time)), __name__)
else:
logger.error(translator.get('processing_video_failed'), __name__)
return 1
return 0
def process_temp_frame(temp_frame_path : str, frame_number : int) -> bool: def process_temp_frame(temp_frame_path : str, frame_number : int) -> bool:
reference_vision_frame = read_static_video_frame(state_manager.get_item('target_path'), state_manager.get_item('reference_frame_number')) reference_vision_frame = read_static_video_frame(state_manager.get_item('target_path'), state_manager.get_item('reference_frame_number'))
source_vision_frames = read_static_images(state_manager.get_item('source_paths')) source_vision_frames = read_static_images(state_manager.get_item('source_paths'))
@@ -195,3 +183,15 @@ def process_temp_frame(temp_frame_path : str, frame_number : int) -> bool:
temp_vision_frame = conditional_merge_vision_mask(temp_vision_frame, temp_vision_mask) temp_vision_frame = conditional_merge_vision_mask(temp_vision_frame, temp_vision_mask)
return write_image(temp_frame_path, temp_vision_frame) return write_image(temp_frame_path, temp_vision_frame)
def finalize_video(start_time : float) -> ErrorCode:
logger.debug(translator.get('clearing_temp'), __name__)
clear_temp_directory(state_manager.get_item('target_path'))
if is_video(state_manager.get_item('output_path')):
logger.info(translator.get('processing_video_succeeded').format(seconds = calculate_end_time(start_time)), __name__)
else:
logger.error(translator.get('processing_video_failed'), __name__)
return 1
return 0
+2 -2
View File
@@ -1,9 +1,9 @@
gradio-rangeslider==0.0.8 gradio-rangeslider==0.0.8
gradio==5.44.1 gradio==5.44.1
numpy==2.3.4 numpy==2.2.6
onnx==1.19.1 onnx==1.19.1
onnxruntime==1.23.2 onnxruntime==1.23.2
opencv-python==4.12.0.88 opencv-python==4.12.0.88
psutil==7.1.2 psutil==7.1.3
tqdm==4.67.1 tqdm==4.67.1
scipy==1.16.3 scipy==1.16.3
+5 -2
View File
@@ -1,7 +1,7 @@
from shutil import which from shutil import which
from facefusion import metadata from facefusion import metadata
from facefusion.curl_builder import chain, head, run from facefusion.curl_builder import chain, ping, run, set_timeout
def test_run() -> None: def test_run() -> None:
@@ -11,4 +11,7 @@ def test_run() -> None:
def test_chain() -> None: def test_chain() -> None:
assert chain(head(metadata.get('url'))) == [ '-I', metadata.get('url') ] assert chain(
ping(metadata.get('url')),
set_timeout(5)
) == [ '-I', metadata.get('url'), '--connect-timeout', '5' ]
+2 -2
View File
@@ -15,10 +15,10 @@ def test_create_inference_session_providers() -> None:
[ [
('CUDAExecutionProvider', ('CUDAExecutionProvider',
{ {
'device_id': '1', 'device_id': 1,
'cudnn_conv_algo_search': 'EXHAUSTIVE' 'cudnn_conv_algo_search': 'EXHAUSTIVE'
}), }),
'CPUExecutionProvider' 'CPUExecutionProvider'
] ]
assert create_inference_session_providers('1', [ 'cpu', 'cuda' ]) == inference_session_providers assert create_inference_session_providers(1, [ 'cpu', 'cuda' ]) == inference_session_providers
+1 -1
View File
@@ -18,7 +18,7 @@ def before_all() -> None:
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('source.jpg'), '-vf', 'crop=iw*0.8:ih*0.8', get_test_example_file('source-80crop.jpg') ]) subprocess.run([ 'ffmpeg', '-i', get_test_example_file('source.jpg'), '-vf', 'crop=iw*0.8:ih*0.8', get_test_example_file('source-80crop.jpg') ])
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('source.jpg'), '-vf', 'crop=iw*0.7:ih*0.7', get_test_example_file('source-70crop.jpg') ]) subprocess.run([ 'ffmpeg', '-i', get_test_example_file('source.jpg'), '-vf', 'crop=iw*0.7:ih*0.7', get_test_example_file('source-70crop.jpg') ])
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('source.jpg'), '-vf', 'crop=iw*0.6:ih*0.6', get_test_example_file('source-60crop.jpg') ]) subprocess.run([ 'ffmpeg', '-i', get_test_example_file('source.jpg'), '-vf', 'crop=iw*0.6:ih*0.6', get_test_example_file('source-60crop.jpg') ])
state_manager.init_item('execution_device_ids', [ '0' ]) state_manager.init_item('execution_device_ids', [ 0 ])
state_manager.init_item('execution_providers', [ 'cpu' ]) state_manager.init_item('execution_providers', [ 'cpu' ])
state_manager.init_item('download_providers', [ 'github' ]) state_manager.init_item('download_providers', [ 'github' ])
state_manager.init_item('face_detector_angles', [ 0 ]) state_manager.init_item('face_detector_angles', [ 0 ])
+1 -1
View File
@@ -9,7 +9,7 @@ from facefusion.inference_manager import INFERENCE_POOL_SET, get_inference_pool
@pytest.fixture(scope = 'module', autouse = True) @pytest.fixture(scope = 'module', autouse = True)
def before_all() -> None: def before_all() -> None:
state_manager.init_item('execution_device_ids', [ '0' ]) state_manager.init_item('execution_device_ids', [ 0 ])
state_manager.init_item('execution_providers', [ 'cpu' ]) state_manager.init_item('execution_providers', [ 'cpu' ])
state_manager.init_item('download_providers', [ 'github' ]) state_manager.init_item('download_providers', [ 'github' ])
content_analyser.pre_check() content_analyser.pre_check()
+18 -6
View File
@@ -1,8 +1,6 @@
from argparse import ArgumentParser from argparse import ArgumentParser
import pytest from facefusion.program_helper import find_argument_group, validate_actions, validate_args
from facefusion.program_helper import find_argument_group, validate_actions
def test_find_argument_group() -> None: def test_find_argument_group() -> None:
@@ -12,12 +10,26 @@ def test_find_argument_group() -> None:
assert find_argument_group(program, 'test-1') assert find_argument_group(program, 'test-1')
assert find_argument_group(program, 'test-2') assert find_argument_group(program, 'test-2')
assert find_argument_group(program, 'invalid') is None assert find_argument_group(program, 'test-3') is None
@pytest.mark.skip()
def test_validate_args() -> None: def test_validate_args() -> None:
pass program = ArgumentParser()
program.add_argument('--test-1', default = 'test_1', choices = [ 'test_1', 'test_2' ])
assert validate_args(program) is True
subparsers = program.add_subparsers()
sub_program = subparsers.add_parser('sub-command')
sub_program.add_argument('--test-2', default = 'test_2', choices = [ 'test_1', 'test_2' ])
assert validate_args(program) is True
for action in sub_program._actions:
if action.dest == 'test_2':
action.default = 'test_3'
assert validate_args(program) is False
def test_validate_actions() -> None: def test_validate_actions() -> None: