From 589c317f1916cb56c0c980912aacd7eb46dbb124 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Fri, 13 Jun 2025 23:15:34 +0200 Subject: [PATCH 01/15] Vibe coded benchmark command --- README.md | 1 + facefusion/args.py | 3 + facefusion/benchmarker.py | 115 ++++++++++++++++++ facefusion/core.py | 10 ++ facefusion/program.py | 11 ++ facefusion/types.py | 4 + facefusion/uis/components/benchmark.py | 79 ++---------- .../uis/components/benchmark_options.py | 2 +- facefusion/uis/layouts/benchmark.py | 21 +--- facefusion/wording.py | 4 + 10 files changed, 166 insertions(+), 84 deletions(-) create mode 100644 facefusion/benchmarker.py diff --git a/README.md b/README.md index 0012238..15e9de4 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ commands: run run the program headless-run run the program in headless mode batch-run run the program in batch mode + benchmark run performance benchmarks and exit force-download force automate downloads and exit job-list list jobs by status job-create create a drafted job diff --git a/facefusion/args.py b/facefusion/args.py index 929713d..4acae27 100644 --- a/facefusion/args.py +++ b/facefusion/args.py @@ -125,6 +125,9 @@ def apply_args(args : Args, apply_state_item : ApplyStateItem) -> None: # download apply_state_item('download_providers', args.get('download_providers')) apply_state_item('download_scope', args.get('download_scope')) + # benchmark + apply_state_item('benchmark_runs', args.get('benchmark_runs')) + apply_state_item('benchmark_cycles', args.get('benchmark_cycles')) # memory apply_state_item('video_memory_strategy', args.get('video_memory_strategy')) apply_state_item('system_memory_limit', args.get('system_memory_limit')) diff --git a/facefusion/benchmarker.py b/facefusion/benchmarker.py new file mode 100644 index 0000000..3bfc2d7 --- /dev/null +++ b/facefusion/benchmarker.py @@ -0,0 +1,115 @@ +import hashlib +import os +import statistics +import tempfile +from time import perf_counter +from typing import Any, Dict, List + +from facefusion import state_manager +from facefusion.cli_helper import render_table +from facefusion.download import conditional_download, resolve_download_url +from facefusion.filesystem import get_file_extension, is_video +from facefusion.memory import limit_system_memory +from facefusion.vision import count_video_frame_total, detect_video_fps, detect_video_resolution, pack_resolution + + +BENCHMARKS : Dict[str, str] = { + '240p': '.assets/examples/target-240p.mp4', + '360p': '.assets/examples/target-360p.mp4', + '540p': '.assets/examples/target-540p.mp4', + '720p': '.assets/examples/target-720p.mp4', + '1080p': '.assets/examples/target-1080p.mp4', + '1440p': '.assets/examples/target-1440p.mp4', + '2160p': '.assets/examples/target-2160p.mp4' +} + + +def pre_check() -> bool: + conditional_download('.assets/examples', + [ + resolve_download_url('examples-3.0.0', 'source.jpg'), + resolve_download_url('examples-3.0.0', 'source.mp3'), + resolve_download_url('examples-3.0.0', 'target-240p.mp4'), + resolve_download_url('examples-3.0.0', 'target-360p.mp4'), + resolve_download_url('examples-3.0.0', 'target-540p.mp4'), + resolve_download_url('examples-3.0.0', 'target-720p.mp4'), + resolve_download_url('examples-3.0.0', 'target-1080p.mp4'), + resolve_download_url('examples-3.0.0', 'target-1440p.mp4'), + resolve_download_url('examples-3.0.0', 'target-2160p.mp4') + ]) + return True + + +def suggest_output_path(target_path : str) -> str: + if is_video(target_path): + target_file_extension = get_file_extension(target_path) + return os.path.join(tempfile.gettempdir(), hashlib.sha1().hexdigest()[:8] + target_file_extension) + return '' + + +def pre_process() -> None: + system_memory_limit = state_manager.get_item('system_memory_limit') + if system_memory_limit and system_memory_limit > 0: + limit_system_memory(system_memory_limit) + + +def benchmark_target(benchmark_cycles : int) -> List[Any]: + from facefusion.core import conditional_process + + process_times = [] + video_frame_total = count_video_frame_total(state_manager.get_item('target_path')) + output_video_resolution = detect_video_resolution(state_manager.get_item('target_path')) + state_manager.set_item('output_video_resolution', pack_resolution(output_video_resolution)) + state_manager.set_item('output_video_fps', detect_video_fps(state_manager.get_item('target_path'))) + + conditional_process() + for index in range(benchmark_cycles): + start_time = perf_counter() + conditional_process() + end_time = perf_counter() + process_times.append(end_time - start_time) + average_run = round(statistics.mean(process_times), 2) + fastest_run = round(min(process_times), 2) + slowest_run = round(max(process_times), 2) + relative_fps = round(video_frame_total * benchmark_cycles / sum(process_times), 2) + + return [ + state_manager.get_item('target_path'), + benchmark_cycles, + average_run, + fastest_run, + slowest_run, + relative_fps + ] + + +def run() -> None: + benchmark_runs = state_manager.get_item('benchmark_runs') + benchmark_cycles = state_manager.get_item('benchmark_cycles') + + state_manager.set_item('source_paths', [ '.assets/examples/source.jpg', '.assets/examples/source.mp3' ]) + state_manager.set_item('face_landmarker_score', 0) + state_manager.set_item('temp_frame_format', 'bmp') + state_manager.set_item('output_audio_volume', 0) + state_manager.set_item('output_video_preset', 'ultrafast') + state_manager.set_item('video_memory_strategy', 'tolerant') + + benchmark_results = [] + target_paths = [ BENCHMARKS[benchmark_run] for benchmark_run in benchmark_runs if benchmark_run in BENCHMARKS ] + + if target_paths: + pre_process() + for target_path in target_paths: + state_manager.set_item('target_path', target_path) + state_manager.set_item('output_path', suggest_output_path(state_manager.get_item('target_path'))) + benchmark_results.append(benchmark_target(benchmark_cycles)) + + headers = [ + 'Target Path', + 'Cycles', + 'Average (s)', + 'Fastest (s)', + 'Slowest (s)', + 'Relative FPS' + ] + render_table(headers, benchmark_results) diff --git a/facefusion/core.py b/facefusion/core.py index cf3a0b8..a8c3b77 100755 --- a/facefusion/core.py +++ b/facefusion/core.py @@ -59,6 +59,16 @@ def route(args : Args) -> None: error_code = force_download() return hard_exit(error_code) + if state_manager.get_item('command') == 'benchmark': + import facefusion.benchmarker as benchmarker + + if not common_pre_check() or not processors_pre_check(): + return hard_exit(2) + if not benchmarker.pre_check(): + return hard_exit(2) + benchmarker.run() + return + if state_manager.get_item('command') in [ 'job-list', 'job-create', 'job-submit', 'job-submit-all', 'job-delete', 'job-delete-all', 'job-add-step', 'job-remix-step', 'job-insert-step', 'job-remove-step' ]: if not job_manager.init_jobs(state_manager.get_item('jobs_path')): hard_exit(1) diff --git a/facefusion/program.py b/facefusion/program.py index 72146c1..335cac7 100755 --- a/facefusion/program.py +++ b/facefusion/program.py @@ -214,6 +214,16 @@ def create_download_providers_program() -> ArgumentParser: return program +def create_benchmark_program() -> ArgumentParser: + from facefusion.benchmarker import BENCHMARKS + program = ArgumentParser(add_help = False) + group_benchmark = program.add_argument_group('benchmark') + group_benchmark.add_argument('--benchmark-runs', help = wording.get('help.benchmark_runs'), default = [ '240p' ], choices = list(BENCHMARKS.keys()), nargs = '+') + group_benchmark.add_argument('--benchmark-cycles', help = wording.get('help.benchmark_cycles'), type = int, default = 5, choices = range(1, 11)) + job_store.register_job_keys([ 'benchmark_runs', 'benchmark_cycles' ]) + return program + + def create_download_scope_program() -> ArgumentParser: program = ArgumentParser(add_help = False) group_download = program.add_argument_group('download') @@ -283,6 +293,7 @@ def create_program() -> ArgumentParser: sub_program.add_parser('run', help = wording.get('help.run'), parents = [ create_config_path_program(), create_temp_path_program(), create_jobs_path_program(), create_source_paths_program(), create_target_path_program(), create_output_path_program(), collect_step_program(), create_uis_program(), collect_job_program() ], formatter_class = create_help_formatter_large) sub_program.add_parser('headless-run', help = wording.get('help.headless_run'), parents = [ create_config_path_program(), create_temp_path_program(), create_jobs_path_program(), create_source_paths_program(), create_target_path_program(), create_output_path_program(), collect_step_program(), collect_job_program() ], formatter_class = create_help_formatter_large) sub_program.add_parser('batch-run', help = wording.get('help.batch_run'), parents = [ create_config_path_program(), create_temp_path_program(), create_jobs_path_program(), create_source_pattern_program(), create_target_pattern_program(), create_output_pattern_program(), collect_step_program(), collect_job_program() ], formatter_class = create_help_formatter_large) + sub_program.add_parser('benchmark', help = wording.get('help.benchmark'), parents = [ create_config_path_program(), create_temp_path_program(), create_jobs_path_program(), create_benchmark_program(), collect_step_program(), collect_job_program() ], formatter_class = create_help_formatter_large) sub_program.add_parser('force-download', help = wording.get('help.force_download'), parents = [ create_download_providers_program(), create_download_scope_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) # job manager sub_program.add_parser('job-list', help = wording.get('help.job_list'), parents = [ create_job_status_program(), create_jobs_path_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) diff --git a/facefusion/types.py b/facefusion/types.py index 38187f1..44a4b6b 100755 --- a/facefusion/types.py +++ b/facefusion/types.py @@ -283,6 +283,8 @@ StateKey = Literal\ 'execution_thread_count', 'execution_queue_count', 'download_providers', + 'benchmark_runs', + 'benchmark_cycles', 'download_scope', 'video_memory_strategy', 'system_memory_limit', @@ -349,6 +351,8 @@ State = TypedDict('State', 'execution_thread_count' : int, 'execution_queue_count' : int, 'download_providers' : List[DownloadProvider], + 'benchmark_runs' : List[str], + 'benchmark_cycles' : int, 'download_scope' : DownloadScope, 'video_memory_strategy' : VideoMemoryStrategy, 'system_memory_limit' : int, diff --git a/facefusion/uis/components/benchmark.py b/facefusion/uis/components/benchmark.py index 1fa50a7..123fa49 100644 --- a/facefusion/uis/components/benchmark.py +++ b/facefusion/uis/components/benchmark.py @@ -1,31 +1,13 @@ -import hashlib -import os -import statistics -import tempfile -from time import perf_counter -from typing import Any, Dict, Generator, List, Optional +from typing import Any, Generator, List, Optional import gradio from facefusion import state_manager, wording -from facefusion.core import conditional_process -from facefusion.filesystem import get_file_extension, is_video -from facefusion.memory import limit_system_memory +from facefusion.benchmarker import BENCHMARKS, benchmark_target, pre_process, suggest_output_path from facefusion.uis.core import get_ui_component -from facefusion.vision import count_video_frame_total, detect_video_fps, detect_video_resolution, pack_resolution BENCHMARK_BENCHMARKS_DATAFRAME : Optional[gradio.Dataframe] = None BENCHMARK_START_BUTTON : Optional[gradio.Button] = None -BENCHMARKS : Dict[str, str] =\ -{ - '240p': '.assets/examples/target-240p.mp4', - '360p': '.assets/examples/target-360p.mp4', - '540p': '.assets/examples/target-540p.mp4', - '720p': '.assets/examples/target-720p.mp4', - '1080p': '.assets/examples/target-1080p.mp4', - '1440p': '.assets/examples/target-1440p.mp4', - '2160p': '.assets/examples/target-2160p.mp4' -} def render() -> None: @@ -68,19 +50,13 @@ def listen() -> None: BENCHMARK_START_BUTTON.click(start, inputs = [ benchmark_runs_checkbox_group, benchmark_cycles_slider ], outputs = BENCHMARK_BENCHMARKS_DATAFRAME) -def suggest_output_path(target_path : str) -> Optional[str]: - if is_video(target_path): - target_file_extension = get_file_extension(target_path) - return os.path.join(tempfile.gettempdir(), hashlib.sha1().hexdigest()[:8] + target_file_extension) - return None - - def start(benchmark_runs : List[str], benchmark_cycles : int) -> Generator[List[Any], None, None]: - state_manager.init_item('source_paths', [ '.assets/examples/source.jpg', '.assets/examples/source.mp3' ]) - state_manager.init_item('face_landmarker_score', 0) - state_manager.init_item('temp_frame_format', 'bmp') - state_manager.init_item('output_audio_volume', 0) - state_manager.init_item('output_video_preset', 'ultrafast') + state_manager.set_item('source_paths', [ '.assets/examples/source.jpg', '.assets/examples/source.mp3' ]) + state_manager.set_item('face_landmarker_score', 0) + state_manager.set_item('temp_frame_format', 'bmp') + state_manager.set_item('output_audio_volume', 0) + state_manager.set_item('output_video_preset', 'ultrafast') + state_manager.set_item('video_memory_strategy', 'tolerant') state_manager.sync_item('execution_providers') state_manager.sync_item('execution_thread_count') state_manager.sync_item('execution_queue_count') @@ -91,42 +67,9 @@ def start(benchmark_runs : List[str], benchmark_cycles : int) -> Generator[List[ if target_paths: pre_process() for target_path in target_paths: - state_manager.init_item('target_path', target_path) - state_manager.init_item('output_path', suggest_output_path(state_manager.get_item('target_path'))) - benchmark_results.append(benchmark(benchmark_cycles)) + state_manager.set_item('target_path', target_path) + state_manager.set_item('output_path', suggest_output_path(state_manager.get_item('target_path'))) + benchmark_results.append(benchmark_target(benchmark_cycles)) yield benchmark_results -def pre_process() -> None: - system_memory_limit = state_manager.get_item('system_memory_limit') - if system_memory_limit and system_memory_limit > 0: - limit_system_memory(system_memory_limit) - - -def benchmark(benchmark_cycles : int) -> List[Any]: - process_times = [] - video_frame_total = count_video_frame_total(state_manager.get_item('target_path')) - output_video_resolution = detect_video_resolution(state_manager.get_item('target_path')) - state_manager.init_item('output_video_resolution', pack_resolution(output_video_resolution)) - state_manager.init_item('output_video_fps', detect_video_fps(state_manager.get_item('target_path'))) - - conditional_process() - for index in range(benchmark_cycles): - start_time = perf_counter() - conditional_process() - end_time = perf_counter() - process_times.append(end_time - start_time) - average_run = round(statistics.mean(process_times), 2) - fastest_run = round(min(process_times), 2) - slowest_run = round(max(process_times), 2) - relative_fps = round(video_frame_total * benchmark_cycles / sum(process_times), 2) - - return\ - [ - state_manager.get_item('target_path'), - benchmark_cycles, - average_run, - fastest_run, - slowest_run, - relative_fps - ] diff --git a/facefusion/uis/components/benchmark_options.py b/facefusion/uis/components/benchmark_options.py index c087487..4e981cf 100644 --- a/facefusion/uis/components/benchmark_options.py +++ b/facefusion/uis/components/benchmark_options.py @@ -3,7 +3,7 @@ from typing import Optional import gradio from facefusion import wording -from facefusion.uis.components.benchmark import BENCHMARKS +from facefusion.benchmarker import BENCHMARKS from facefusion.uis.core import register_ui_component BENCHMARK_RUNS_CHECKBOX_GROUP : Optional[gradio.CheckboxGroup] = None diff --git a/facefusion/uis/layouts/benchmark.py b/facefusion/uis/layouts/benchmark.py index b119a1f..79f3670 100644 --- a/facefusion/uis/layouts/benchmark.py +++ b/facefusion/uis/layouts/benchmark.py @@ -1,24 +1,15 @@ import gradio from facefusion import state_manager -from facefusion.download import conditional_download, resolve_download_url -from facefusion.uis.components import about, age_modifier_options, benchmark, benchmark_options, deep_swapper_options, download, execution, execution_queue_count, execution_thread_count, expression_restorer_options, face_debugger_options, face_editor_options, face_enhancer_options, face_swapper_options, frame_colorizer_options, frame_enhancer_options, lip_syncer_options, memory, processors +from facefusion.benchmarker import pre_check as benchmarker_pre_check +from facefusion.uis.components import (about, age_modifier_options, benchmark, benchmark_options, deep_swapper_options, + download, execution, execution_queue_count, execution_thread_count, expression_restorer_options, + face_debugger_options, face_editor_options, face_enhancer_options, face_swapper_options, + frame_colorizer_options, frame_enhancer_options, lip_syncer_options, memory, processors) def pre_check() -> bool: - conditional_download('.assets/examples', - [ - resolve_download_url('examples-3.0.0', 'source.jpg'), - resolve_download_url('examples-3.0.0', 'source.mp3'), - resolve_download_url('examples-3.0.0', 'target-240p.mp4'), - resolve_download_url('examples-3.0.0', 'target-360p.mp4'), - resolve_download_url('examples-3.0.0', 'target-540p.mp4'), - resolve_download_url('examples-3.0.0', 'target-720p.mp4'), - resolve_download_url('examples-3.0.0', 'target-1080p.mp4'), - resolve_download_url('examples-3.0.0', 'target-1440p.mp4'), - resolve_download_url('examples-3.0.0', 'target-2160p.mp4') - ]) - return True + return benchmarker_pre_check() def render() -> gradio.Blocks: diff --git a/facefusion/wording.py b/facefusion/wording.py index c2282ee..c176a23 100755 --- a/facefusion/wording.py +++ b/facefusion/wording.py @@ -207,7 +207,11 @@ WORDING : Dict[str, Any] =\ 'run': 'run the program', 'headless_run': 'run the program in headless mode', 'batch_run': 'run the program in batch mode', + 'benchmark': 'run performance benchmarks and exit', 'force_download': 'force automate downloads and exit', + # benchmark + 'benchmark_runs': 'choose the resolution for the benchmark runs (choices: {choices}, ...)', + 'benchmark_cycles': 'specify the number of benchmark cycles', # jobs 'job_id': 'specify the job id', 'job_status': 'specify the job status', From 19400a92bd38d01e3ffacad0f06d8eb4fd708aa1 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 14 Jun 2025 11:00:52 +0200 Subject: [PATCH 02/15] Vibe coded benchmark command part2 --- README.md | 2 +- facefusion.ini | 4 ++++ facefusion/args.py | 2 +- facefusion/benchmarker.py | 4 ++-- facefusion/program.py | 7 +++---- facefusion/types.py | 4 ++-- facefusion/uis/components/benchmark.py | 10 +++++----- facefusion/uis/components/benchmark_options.py | 10 +++++----- facefusion/uis/types.py | 2 +- facefusion/wording.py | 6 +++--- 10 files changed, 27 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 15e9de4..d16935e 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,8 @@ commands: run run the program headless-run run the program in headless mode batch-run run the program in batch mode - benchmark run performance benchmarks and exit force-download force automate downloads and exit + benchmark run performance benchmarks and exit job-list list jobs by status job-create create a drafted job job-submit submit a drafted job to become a queued job diff --git a/facefusion.ini b/facefusion.ini index 172ecaf..42c6e94 100644 --- a/facefusion.ini +++ b/facefusion.ini @@ -110,6 +110,10 @@ execution_queue_count = download_providers = download_scope = +[benchmark] +benchmark_resolutions = +benchmark_cycles = + [memory] video_memory_strategy = system_memory_limit = diff --git a/facefusion/args.py b/facefusion/args.py index 4acae27..560b6ac 100644 --- a/facefusion/args.py +++ b/facefusion/args.py @@ -126,7 +126,7 @@ def apply_args(args : Args, apply_state_item : ApplyStateItem) -> None: apply_state_item('download_providers', args.get('download_providers')) apply_state_item('download_scope', args.get('download_scope')) # benchmark - apply_state_item('benchmark_runs', args.get('benchmark_runs')) + apply_state_item('benchmark_resolutions', args.get('benchmark_resolutions')) apply_state_item('benchmark_cycles', args.get('benchmark_cycles')) # memory apply_state_item('video_memory_strategy', args.get('video_memory_strategy')) diff --git a/facefusion/benchmarker.py b/facefusion/benchmarker.py index 3bfc2d7..99f75fd 100644 --- a/facefusion/benchmarker.py +++ b/facefusion/benchmarker.py @@ -84,7 +84,7 @@ def benchmark_target(benchmark_cycles : int) -> List[Any]: def run() -> None: - benchmark_runs = state_manager.get_item('benchmark_runs') + benchmark_resolutions = state_manager.get_item('benchmark_resolutions') benchmark_cycles = state_manager.get_item('benchmark_cycles') state_manager.set_item('source_paths', [ '.assets/examples/source.jpg', '.assets/examples/source.mp3' ]) @@ -95,7 +95,7 @@ def run() -> None: state_manager.set_item('video_memory_strategy', 'tolerant') benchmark_results = [] - target_paths = [ BENCHMARKS[benchmark_run] for benchmark_run in benchmark_runs if benchmark_run in BENCHMARKS ] + target_paths = [ BENCHMARKS[benchmark_resolution] for benchmark_resolution in benchmark_resolutions if benchmark_resolution in BENCHMARKS ] if target_paths: pre_process() diff --git a/facefusion/program.py b/facefusion/program.py index 335cac7..6f0d487 100755 --- a/facefusion/program.py +++ b/facefusion/program.py @@ -218,9 +218,8 @@ def create_benchmark_program() -> ArgumentParser: from facefusion.benchmarker import BENCHMARKS program = ArgumentParser(add_help = False) group_benchmark = program.add_argument_group('benchmark') - group_benchmark.add_argument('--benchmark-runs', help = wording.get('help.benchmark_runs'), default = [ '240p' ], choices = list(BENCHMARKS.keys()), nargs = '+') - group_benchmark.add_argument('--benchmark-cycles', help = wording.get('help.benchmark_cycles'), type = int, default = 5, choices = range(1, 11)) - job_store.register_job_keys([ 'benchmark_runs', 'benchmark_cycles' ]) + group_benchmark.add_argument('--benchmark-resolutions', help = wording.get('help.benchmark_resolutions'), default = config.get_str_list('benchmark', 'benchmark_resolutions', '240p'), choices = list(BENCHMARKS.keys()), nargs = '+') + group_benchmark.add_argument('--benchmark-cycles', help = wording.get('help.benchmark_cycles'), type = int, default = config.get_int_value('benchmark', 'benchmark_cycles', '5'), choices = range(1, 11)) return program @@ -293,8 +292,8 @@ def create_program() -> ArgumentParser: sub_program.add_parser('run', help = wording.get('help.run'), parents = [ create_config_path_program(), create_temp_path_program(), create_jobs_path_program(), create_source_paths_program(), create_target_path_program(), create_output_path_program(), collect_step_program(), create_uis_program(), collect_job_program() ], formatter_class = create_help_formatter_large) sub_program.add_parser('headless-run', help = wording.get('help.headless_run'), parents = [ create_config_path_program(), create_temp_path_program(), create_jobs_path_program(), create_source_paths_program(), create_target_path_program(), create_output_path_program(), collect_step_program(), collect_job_program() ], formatter_class = create_help_formatter_large) sub_program.add_parser('batch-run', help = wording.get('help.batch_run'), parents = [ create_config_path_program(), create_temp_path_program(), create_jobs_path_program(), create_source_pattern_program(), create_target_pattern_program(), create_output_pattern_program(), collect_step_program(), collect_job_program() ], formatter_class = create_help_formatter_large) - sub_program.add_parser('benchmark', help = wording.get('help.benchmark'), parents = [ create_config_path_program(), create_temp_path_program(), create_jobs_path_program(), create_benchmark_program(), collect_step_program(), collect_job_program() ], formatter_class = create_help_formatter_large) sub_program.add_parser('force-download', help = wording.get('help.force_download'), parents = [ create_download_providers_program(), create_download_scope_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) + sub_program.add_parser('benchmark', help = wording.get('help.benchmark'), parents = [ create_config_path_program(), create_temp_path_program(), create_jobs_path_program(), create_benchmark_program(), collect_step_program(), collect_job_program() ], formatter_class = create_help_formatter_large) # job manager sub_program.add_parser('job-list', help = wording.get('help.job_list'), parents = [ create_job_status_program(), create_jobs_path_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) sub_program.add_parser('job-create', help = wording.get('help.job_create'), parents = [ create_job_id_program(), create_jobs_path_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) diff --git a/facefusion/types.py b/facefusion/types.py index 44a4b6b..15978ce 100755 --- a/facefusion/types.py +++ b/facefusion/types.py @@ -283,7 +283,7 @@ StateKey = Literal\ 'execution_thread_count', 'execution_queue_count', 'download_providers', - 'benchmark_runs', + 'benchmark_resolutions', 'benchmark_cycles', 'download_scope', 'video_memory_strategy', @@ -351,7 +351,7 @@ State = TypedDict('State', 'execution_thread_count' : int, 'execution_queue_count' : int, 'download_providers' : List[DownloadProvider], - 'benchmark_runs' : List[str], + 'benchmark_resolutions' : List[str], 'benchmark_cycles' : int, 'download_scope' : DownloadScope, 'video_memory_strategy' : VideoMemoryStrategy, diff --git a/facefusion/uis/components/benchmark.py b/facefusion/uis/components/benchmark.py index 123fa49..ed17710 100644 --- a/facefusion/uis/components/benchmark.py +++ b/facefusion/uis/components/benchmark.py @@ -43,14 +43,14 @@ def render() -> None: def listen() -> None: - benchmark_runs_checkbox_group = get_ui_component('benchmark_runs_checkbox_group') + benchmark_resolutions_checkbox_group = get_ui_component('benchmark_resolutions_checkbox_group') benchmark_cycles_slider = get_ui_component('benchmark_cycles_slider') - if benchmark_runs_checkbox_group and benchmark_cycles_slider: - BENCHMARK_START_BUTTON.click(start, inputs = [ benchmark_runs_checkbox_group, benchmark_cycles_slider ], outputs = BENCHMARK_BENCHMARKS_DATAFRAME) + if benchmark_resolutions_checkbox_group and benchmark_cycles_slider: + BENCHMARK_START_BUTTON.click(start, inputs = [ benchmark_resolutions_checkbox_group, benchmark_cycles_slider ], outputs = BENCHMARK_BENCHMARKS_DATAFRAME) -def start(benchmark_runs : List[str], benchmark_cycles : int) -> Generator[List[Any], None, None]: +def start(benchmark_resolutions : List[str], benchmark_cycles : int) -> Generator[List[Any], None, None]: state_manager.set_item('source_paths', [ '.assets/examples/source.jpg', '.assets/examples/source.mp3' ]) state_manager.set_item('face_landmarker_score', 0) state_manager.set_item('temp_frame_format', 'bmp') @@ -62,7 +62,7 @@ def start(benchmark_runs : List[str], benchmark_cycles : int) -> Generator[List[ state_manager.sync_item('execution_queue_count') state_manager.sync_item('system_memory_limit') benchmark_results = [] - target_paths = [ BENCHMARKS[benchmark_run] for benchmark_run in benchmark_runs if benchmark_run in BENCHMARKS ] + target_paths = [ BENCHMARKS[benchmark_resolution] for benchmark_resolution in benchmark_resolutions if benchmark_resolution in BENCHMARKS ] if target_paths: pre_process() diff --git a/facefusion/uis/components/benchmark_options.py b/facefusion/uis/components/benchmark_options.py index 4e981cf..a7f07cf 100644 --- a/facefusion/uis/components/benchmark_options.py +++ b/facefusion/uis/components/benchmark_options.py @@ -6,16 +6,16 @@ from facefusion import wording from facefusion.benchmarker import BENCHMARKS from facefusion.uis.core import register_ui_component -BENCHMARK_RUNS_CHECKBOX_GROUP : Optional[gradio.CheckboxGroup] = None +BENCHMARK_RESOLUTIONS_CHECKBOX_GROUP : Optional[gradio.CheckboxGroup] = None BENCHMARK_CYCLES_SLIDER : Optional[gradio.Button] = None def render() -> None: - global BENCHMARK_RUNS_CHECKBOX_GROUP + global BENCHMARK_RESOLUTIONS_CHECKBOX_GROUP global BENCHMARK_CYCLES_SLIDER - BENCHMARK_RUNS_CHECKBOX_GROUP = gradio.CheckboxGroup( - label = wording.get('uis.benchmark_runs_checkbox_group'), + BENCHMARK_RESOLUTIONS_CHECKBOX_GROUP = gradio.CheckboxGroup( + label = wording.get('uis.benchmark_resolutions_checkbox_group'), choices = list(BENCHMARKS.keys()), value = list(BENCHMARKS.keys()) ) @@ -26,5 +26,5 @@ def render() -> None: minimum = 1, maximum = 10 ) - register_ui_component('benchmark_runs_checkbox_group', BENCHMARK_RUNS_CHECKBOX_GROUP) + register_ui_component('benchmark_resolutions_checkbox_group', BENCHMARK_RESOLUTIONS_CHECKBOX_GROUP) register_ui_component('benchmark_cycles_slider', BENCHMARK_CYCLES_SLIDER) diff --git a/facefusion/uis/types.py b/facefusion/uis/types.py index 52c5015..32a6080 100644 --- a/facefusion/uis/types.py +++ b/facefusion/uis/types.py @@ -6,7 +6,7 @@ ComponentName = Literal\ 'age_modifier_direction_slider', 'age_modifier_model_dropdown', 'benchmark_cycles_slider', - 'benchmark_runs_checkbox_group', + 'benchmark_resolutions_checkbox_group', 'deep_swapper_model_dropdown', 'deep_swapper_morph_slider', 'expression_restorer_factor_slider', diff --git a/facefusion/wording.py b/facefusion/wording.py index c176a23..fde5ae8 100755 --- a/facefusion/wording.py +++ b/facefusion/wording.py @@ -207,10 +207,10 @@ WORDING : Dict[str, Any] =\ 'run': 'run the program', 'headless_run': 'run the program in headless mode', 'batch_run': 'run the program in batch mode', - 'benchmark': 'run performance benchmarks and exit', 'force_download': 'force automate downloads and exit', + 'benchmark': 'run performance benchmarks and exit', # benchmark - 'benchmark_runs': 'choose the resolution for the benchmark runs (choices: {choices}, ...)', + 'benchmark_resolutions': 'choose the resolution for the benchmark runs (choices: {choices}, ...)', 'benchmark_cycles': 'specify the number of benchmark cycles', # jobs 'job_id': 'specify the job id', @@ -245,7 +245,7 @@ WORDING : Dict[str, Any] =\ 'age_modifier_model_dropdown': 'AGE MODIFIER MODEL', 'apply_button': 'APPLY', 'benchmark_cycles_slider': 'BENCHMARK CYCLES', - 'benchmark_runs_checkbox_group': 'BENCHMARK RUNS', + 'benchmark_resolutions_checkbox_group': 'BENCHMARK RESOLUTIONS', 'clear_button': 'CLEAR', 'common_options_checkbox_group': 'OPTIONS', 'download_providers_checkbox_group': 'DOWNLOAD PROVIDERS', From 703c64bed58d8926d47e63e51a5face20ec5f7e4 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sun, 15 Jun 2025 19:53:12 +0200 Subject: [PATCH 03/15] Vibe coded benchmark command part3 --- facefusion/benchmarker.py | 126 ++++++++++++------------- facefusion/core.py | 11 +-- facefusion/uis/components/benchmark.py | 24 +---- facefusion/wording.py | 8 +- 4 files changed, 74 insertions(+), 95 deletions(-) diff --git a/facefusion/benchmarker.py b/facefusion/benchmarker.py index 99f75fd..3308bf4 100644 --- a/facefusion/benchmarker.py +++ b/facefusion/benchmarker.py @@ -3,17 +3,17 @@ import os import statistics import tempfile from time import perf_counter -from typing import Any, Dict, List +from typing import Any, Dict, Generator, List -from facefusion import state_manager +from facefusion import core, state_manager from facefusion.cli_helper import render_table from facefusion.download import conditional_download, resolve_download_url -from facefusion.filesystem import get_file_extension, is_video -from facefusion.memory import limit_system_memory +from facefusion.filesystem import get_file_extension from facefusion.vision import count_video_frame_total, detect_video_fps, detect_video_resolution, pack_resolution -BENCHMARKS : Dict[str, str] = { +BENCHMARKS : Dict[str, str] =\ +{ '240p': '.assets/examples/target-240p.mp4', '360p': '.assets/examples/target-360p.mp4', '540p': '.assets/examples/target-540p.mp4', @@ -40,50 +40,7 @@ def pre_check() -> bool: return True -def suggest_output_path(target_path : str) -> str: - if is_video(target_path): - target_file_extension = get_file_extension(target_path) - return os.path.join(tempfile.gettempdir(), hashlib.sha1().hexdigest()[:8] + target_file_extension) - return '' - - -def pre_process() -> None: - system_memory_limit = state_manager.get_item('system_memory_limit') - if system_memory_limit and system_memory_limit > 0: - limit_system_memory(system_memory_limit) - - -def benchmark_target(benchmark_cycles : int) -> List[Any]: - from facefusion.core import conditional_process - - process_times = [] - video_frame_total = count_video_frame_total(state_manager.get_item('target_path')) - output_video_resolution = detect_video_resolution(state_manager.get_item('target_path')) - state_manager.set_item('output_video_resolution', pack_resolution(output_video_resolution)) - state_manager.set_item('output_video_fps', detect_video_fps(state_manager.get_item('target_path'))) - - conditional_process() - for index in range(benchmark_cycles): - start_time = perf_counter() - conditional_process() - end_time = perf_counter() - process_times.append(end_time - start_time) - average_run = round(statistics.mean(process_times), 2) - fastest_run = round(min(process_times), 2) - slowest_run = round(max(process_times), 2) - relative_fps = round(video_frame_total * benchmark_cycles / sum(process_times), 2) - - return [ - state_manager.get_item('target_path'), - benchmark_cycles, - average_run, - fastest_run, - slowest_run, - relative_fps - ] - - -def run() -> None: +def run() -> Generator[List[Any], None, None]: benchmark_resolutions = state_manager.get_item('benchmark_resolutions') benchmark_cycles = state_manager.get_item('benchmark_cycles') @@ -95,21 +52,64 @@ def run() -> None: state_manager.set_item('video_memory_strategy', 'tolerant') benchmark_results = [] - target_paths = [ BENCHMARKS[benchmark_resolution] for benchmark_resolution in benchmark_resolutions if benchmark_resolution in BENCHMARKS ] + target_paths = [ BENCHMARKS.get(benchmark_resolution) for benchmark_resolution in benchmark_resolutions if benchmark_resolution in BENCHMARKS ] - if target_paths: - pre_process() - for target_path in target_paths: - state_manager.set_item('target_path', target_path) - state_manager.set_item('output_path', suggest_output_path(state_manager.get_item('target_path'))) - benchmark_results.append(benchmark_target(benchmark_cycles)) + for target_path in target_paths: + state_manager.set_item('target_path', target_path) + state_manager.set_item('output_path', suggest_output_path(state_manager.get_item('target_path'))) + benchmark_results.append(cycle(benchmark_cycles)) + yield benchmark_results - headers = [ - 'Target Path', - 'Cycles', - 'Average (s)', - 'Fastest (s)', - 'Slowest (s)', - 'Relative FPS' + +def cycle(benchmark_cycles : int) -> List[Any]: + process_times = [] + video_frame_total = count_video_frame_total(state_manager.get_item('target_path')) + output_video_resolution = detect_video_resolution(state_manager.get_item('target_path')) + state_manager.set_item('output_video_resolution', pack_resolution(output_video_resolution)) + state_manager.set_item('output_video_fps', detect_video_fps(state_manager.get_item('target_path'))) + + core.conditional_process() + + for index in range(benchmark_cycles): + start_time = perf_counter() + core.conditional_process() + end_time = perf_counter() + process_times.append(end_time - start_time) + + average_run = round(statistics.mean(process_times), 2) + fastest_run = round(min(process_times), 2) + slowest_run = round(max(process_times), 2) + relative_fps = round(video_frame_total * benchmark_cycles / sum(process_times), 2) + + return\ + [ + state_manager.get_item('target_path'), + benchmark_cycles, + average_run, + fastest_run, + slowest_run, + relative_fps ] + + +def suggest_output_path(target_path : str) -> str: + target_file_extension = get_file_extension(target_path) + return os.path.join(tempfile.gettempdir(), hashlib.sha1().hexdigest()[:8] + target_file_extension) + + +def render() -> None: + benchmark_results = [] + headers = \ + [ + 'target_path', + 'benchmark_cycles', + 'average_run', + 'fastest_run', + 'slowest_run', + 'relative_fps' + ] + + for cycle_result in run(): + benchmark_results = cycle_result + render_table(headers, benchmark_results) diff --git a/facefusion/core.py b/facefusion/core.py index a8c3b77..be6e8b0 100755 --- a/facefusion/core.py +++ b/facefusion/core.py @@ -6,7 +6,7 @@ from time import time import numpy -from facefusion import cli_helper, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, logger, process_manager, state_manager, video_manager, voice_extractor, wording +from facefusion import benchmarker, cli_helper, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, logger, process_manager, state_manager, video_manager, voice_extractor, wording from facefusion.args import apply_args, collect_job_args, reduce_job_args, reduce_step_args from facefusion.common_helper import get_first from facefusion.content_analyser import analyse_image, analyse_video @@ -60,14 +60,9 @@ def route(args : Args) -> None: return hard_exit(error_code) if state_manager.get_item('command') == 'benchmark': - import facefusion.benchmarker as benchmarker - - if not common_pre_check() or not processors_pre_check(): + if not common_pre_check() or not processors_pre_check() or not benchmarker.pre_check(): return hard_exit(2) - if not benchmarker.pre_check(): - return hard_exit(2) - benchmarker.run() - return + benchmarker.render() if state_manager.get_item('command') in [ 'job-list', 'job-create', 'job-submit', 'job-submit-all', 'job-delete', 'job-delete-all', 'job-add-step', 'job-remix-step', 'job-insert-step', 'job-remove-step' ]: if not job_manager.init_jobs(state_manager.get_item('jobs_path')): diff --git a/facefusion/uis/components/benchmark.py b/facefusion/uis/components/benchmark.py index ed17710..55e1577 100644 --- a/facefusion/uis/components/benchmark.py +++ b/facefusion/uis/components/benchmark.py @@ -2,8 +2,7 @@ from typing import Any, Generator, List, Optional import gradio -from facefusion import state_manager, wording -from facefusion.benchmarker import BENCHMARKS, benchmark_target, pre_process, suggest_output_path +from facefusion import benchmarker, state_manager, wording from facefusion.uis.core import get_ui_component BENCHMARK_BENCHMARKS_DATAFRAME : Optional[gradio.Dataframe] = None @@ -51,25 +50,10 @@ def listen() -> None: def start(benchmark_resolutions : List[str], benchmark_cycles : int) -> Generator[List[Any], None, None]: - state_manager.set_item('source_paths', [ '.assets/examples/source.jpg', '.assets/examples/source.mp3' ]) - state_manager.set_item('face_landmarker_score', 0) - state_manager.set_item('temp_frame_format', 'bmp') - state_manager.set_item('output_audio_volume', 0) - state_manager.set_item('output_video_preset', 'ultrafast') - state_manager.set_item('video_memory_strategy', 'tolerant') + state_manager.set_item('benchmark_resolutions', benchmark_resolutions) + state_manager.set_item('benchmark_cycles', benchmark_cycles) state_manager.sync_item('execution_providers') state_manager.sync_item('execution_thread_count') state_manager.sync_item('execution_queue_count') - state_manager.sync_item('system_memory_limit') - benchmark_results = [] - target_paths = [ BENCHMARKS[benchmark_resolution] for benchmark_resolution in benchmark_resolutions if benchmark_resolution in BENCHMARKS ] - - if target_paths: - pre_process() - for target_path in target_paths: - state_manager.set_item('target_path', target_path) - state_manager.set_item('output_path', suggest_output_path(state_manager.get_item('target_path'))) - benchmark_results.append(benchmark_target(benchmark_cycles)) - yield benchmark_results - + yield from benchmarker.run() diff --git a/facefusion/wording.py b/facefusion/wording.py index fde5ae8..89f4f04 100755 --- a/facefusion/wording.py +++ b/facefusion/wording.py @@ -197,6 +197,9 @@ WORDING : Dict[str, Any] =\ # download 'download_providers': 'download using different providers (choices: {choices}, ...)', 'download_scope': 'specify the download scope', + # benchmark + 'benchmark_resolutions': 'choose the resolution for the benchmarks (choices: {choices}, ...)', + 'benchmark_cycles': 'specify the number of benchmark cycles', # memory 'video_memory_strategy': 'balance fast processing and low VRAM usage', 'system_memory_limit': 'limit the available RAM that can be used while processing', @@ -208,10 +211,7 @@ WORDING : Dict[str, Any] =\ 'headless_run': 'run the program in headless mode', 'batch_run': 'run the program in batch mode', 'force_download': 'force automate downloads and exit', - 'benchmark': 'run performance benchmarks and exit', - # benchmark - 'benchmark_resolutions': 'choose the resolution for the benchmark runs (choices: {choices}, ...)', - 'benchmark_cycles': 'specify the number of benchmark cycles', + 'benchmark': 'run performance benchmarks', # jobs 'job_id': 'specify the job id', 'job_status': 'specify the job status', From 45fbac02203ecb02deebf8e4ebeffbc43a998de7 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sun, 15 Jun 2025 20:23:52 +0200 Subject: [PATCH 04/15] Vibe coded benchmark command part3 --- facefusion/types.py | 10 ++++++++++ facefusion/uis/components/benchmark.py | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/facefusion/types.py b/facefusion/types.py index 15978ce..4afc91d 100755 --- a/facefusion/types.py +++ b/facefusion/types.py @@ -100,6 +100,16 @@ LogLevelSet : TypeAlias = Dict[LogLevel, int] TableHeaders = List[str] TableContents = List[List[Any]] +BenchmarkSet = TypedDict('BenchmarkSet', +{ + 'target_path' : str, + 'benchmark_cycles' : int, + 'average_run' : float, + 'fastest_run' : float, + 'slowest_run' : float, + 'relative_fps' : float +}) + FaceDetectorModel = Literal['many', 'retinaface', 'scrfd', 'yolo_face'] FaceLandmarkerModel = Literal['many', '2dfan4', 'peppa_wutz'] FaceDetectorSet : TypeAlias = Dict[FaceDetectorModel, List[str]] diff --git a/facefusion/uis/components/benchmark.py b/facefusion/uis/components/benchmark.py index 55e1577..5f53b7a 100644 --- a/facefusion/uis/components/benchmark.py +++ b/facefusion/uis/components/benchmark.py @@ -56,4 +56,5 @@ def start(benchmark_resolutions : List[str], benchmark_cycles : int) -> Generato state_manager.sync_item('execution_thread_count') state_manager.sync_item('execution_queue_count') - yield from benchmarker.run() + for benchmarks in benchmarker.run_with_progress(): + yield [ list(benchmark.values()) for benchmark in benchmarks ] From 0f92cdfab16dc1db24b23ff0e0b7fba0d581b2b6 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sun, 15 Jun 2025 20:26:20 +0200 Subject: [PATCH 05/15] Vibe coded benchmark command part3 --- facefusion/uis/components/benchmark.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/facefusion/uis/components/benchmark.py b/facefusion/uis/components/benchmark.py index 5f53b7a..55e1577 100644 --- a/facefusion/uis/components/benchmark.py +++ b/facefusion/uis/components/benchmark.py @@ -56,5 +56,4 @@ def start(benchmark_resolutions : List[str], benchmark_cycles : int) -> Generato state_manager.sync_item('execution_thread_count') state_manager.sync_item('execution_queue_count') - for benchmarks in benchmarker.run_with_progress(): - yield [ list(benchmark.values()) for benchmark in benchmarks ] + yield from benchmarker.run() From 293dff56ed63bc49af2517df72958fc246c8c99d Mon Sep 17 00:00:00 2001 From: henryruhs Date: Mon, 16 Jun 2025 09:54:37 +0200 Subject: [PATCH 06/15] Vibe coded benchmark command part4 --- facefusion/benchmarker.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/facefusion/benchmarker.py b/facefusion/benchmarker.py index 3308bf4..3123176 100644 --- a/facefusion/benchmarker.py +++ b/facefusion/benchmarker.py @@ -3,9 +3,10 @@ import os import statistics import tempfile from time import perf_counter -from typing import Any, Dict, Generator, List +from typing import Dict, Generator, List from facefusion import core, state_manager +from facefusion.types import BenchmarkSet from facefusion.cli_helper import render_table from facefusion.download import conditional_download, resolve_download_url from facefusion.filesystem import get_file_extension @@ -40,7 +41,7 @@ def pre_check() -> bool: return True -def run() -> Generator[List[Any], None, None]: +def run() -> Generator[List[BenchmarkSet], None, None]: benchmark_resolutions = state_manager.get_item('benchmark_resolutions') benchmark_cycles = state_manager.get_item('benchmark_cycles') @@ -61,7 +62,7 @@ def run() -> Generator[List[Any], None, None]: yield benchmark_results -def cycle(benchmark_cycles : int) -> List[Any]: +def cycle(benchmark_cycles : int) -> BenchmarkSet: process_times = [] video_frame_total = count_video_frame_total(state_manager.get_item('target_path')) output_video_resolution = detect_video_resolution(state_manager.get_item('target_path')) @@ -82,14 +83,14 @@ def cycle(benchmark_cycles : int) -> List[Any]: relative_fps = round(video_frame_total * benchmark_cycles / sum(process_times), 2) return\ - [ - state_manager.get_item('target_path'), - benchmark_cycles, - average_run, - fastest_run, - slowest_run, - relative_fps - ] + { + 'target_path': state_manager.get_item('target_path'), + 'benchmark_cycles': benchmark_cycles, + 'average_run': average_run, + 'fastest_run': fastest_run, + 'slowest_run': slowest_run, + 'relative_fps': relative_fps + } def suggest_output_path(target_path : str) -> str: From 26d0a5b9bb5b16c149833adefca4a9e5e6d571a7 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Mon, 16 Jun 2025 10:32:31 +0200 Subject: [PATCH 07/15] Vibe coded benchmark command part5 --- facefusion/benchmarker.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/facefusion/benchmarker.py b/facefusion/benchmarker.py index 3123176..0244913 100644 --- a/facefusion/benchmarker.py +++ b/facefusion/benchmarker.py @@ -6,13 +6,12 @@ from time import perf_counter from typing import Dict, Generator, List from facefusion import core, state_manager -from facefusion.types import BenchmarkSet from facefusion.cli_helper import render_table from facefusion.download import conditional_download, resolve_download_url from facefusion.filesystem import get_file_extension +from facefusion.types import BenchmarkSet from facefusion.vision import count_video_frame_total, detect_video_fps, detect_video_resolution, pack_resolution - BENCHMARKS : Dict[str, str] =\ { '240p': '.assets/examples/target-240p.mp4', @@ -52,14 +51,14 @@ def run() -> Generator[List[BenchmarkSet], None, None]: state_manager.set_item('output_video_preset', 'ultrafast') state_manager.set_item('video_memory_strategy', 'tolerant') - benchmark_results = [] + benchmarks = [] target_paths = [ BENCHMARKS.get(benchmark_resolution) for benchmark_resolution in benchmark_resolutions if benchmark_resolution in BENCHMARKS ] for target_path in target_paths: state_manager.set_item('target_path', target_path) state_manager.set_item('output_path', suggest_output_path(state_manager.get_item('target_path'))) - benchmark_results.append(cycle(benchmark_cycles)) - yield benchmark_results + benchmarks.append(cycle(benchmark_cycles)) + yield benchmarks def cycle(benchmark_cycles : int) -> BenchmarkSet: @@ -83,7 +82,7 @@ def cycle(benchmark_cycles : int) -> BenchmarkSet: relative_fps = round(video_frame_total * benchmark_cycles / sum(process_times), 2) return\ - { + { 'target_path': state_manager.get_item('target_path'), 'benchmark_cycles': benchmark_cycles, 'average_run': average_run, @@ -99,8 +98,8 @@ def suggest_output_path(target_path : str) -> str: def render() -> None: - benchmark_results = [] - headers = \ + benchmarks = [] + headers =\ [ 'target_path', 'benchmark_cycles', @@ -110,7 +109,7 @@ def render() -> None: 'relative_fps' ] - for cycle_result in run(): - benchmark_results = cycle_result + for benchmark in run(): + benchmarks = benchmark - render_table(headers, benchmark_results) + render_table(headers, benchmarks) From 0feecfd9d77f99da07f7ea63609be6cfd7c05ee6 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Mon, 16 Jun 2025 11:21:48 +0200 Subject: [PATCH 08/15] Finalize choices and types --- facefusion/benchmarker.py | 25 ++++++++----------------- facefusion/choices.py | 15 ++++++++++++++- facefusion/program.py | 5 ++--- facefusion/types.py | 22 ++++++++++++---------- 4 files changed, 36 insertions(+), 31 deletions(-) diff --git a/facefusion/benchmarker.py b/facefusion/benchmarker.py index 0244913..072c5b3 100644 --- a/facefusion/benchmarker.py +++ b/facefusion/benchmarker.py @@ -3,26 +3,16 @@ import os import statistics import tempfile from time import perf_counter -from typing import Dict, Generator, List +from typing import Generator, List +import facefusion.choices from facefusion import core, state_manager from facefusion.cli_helper import render_table from facefusion.download import conditional_download, resolve_download_url from facefusion.filesystem import get_file_extension -from facefusion.types import BenchmarkSet +from facefusion.types import BenchmarkCycleSet from facefusion.vision import count_video_frame_total, detect_video_fps, detect_video_resolution, pack_resolution -BENCHMARKS : Dict[str, str] =\ -{ - '240p': '.assets/examples/target-240p.mp4', - '360p': '.assets/examples/target-360p.mp4', - '540p': '.assets/examples/target-540p.mp4', - '720p': '.assets/examples/target-720p.mp4', - '1080p': '.assets/examples/target-1080p.mp4', - '1440p': '.assets/examples/target-1440p.mp4', - '2160p': '.assets/examples/target-2160p.mp4' -} - def pre_check() -> bool: conditional_download('.assets/examples', @@ -40,7 +30,7 @@ def pre_check() -> bool: return True -def run() -> Generator[List[BenchmarkSet], None, None]: +def run() -> Generator[List[BenchmarkCycleSet], None, None]: benchmark_resolutions = state_manager.get_item('benchmark_resolutions') benchmark_cycles = state_manager.get_item('benchmark_cycles') @@ -52,7 +42,7 @@ def run() -> Generator[List[BenchmarkSet], None, None]: state_manager.set_item('video_memory_strategy', 'tolerant') benchmarks = [] - target_paths = [ BENCHMARKS.get(benchmark_resolution) for benchmark_resolution in benchmark_resolutions if benchmark_resolution in BENCHMARKS ] + target_paths = [facefusion.choices.benchmark_set.get(benchmark_resolution) for benchmark_resolution in benchmark_resolutions if benchmark_resolution in facefusion.choices.benchmark_set] for target_path in target_paths: state_manager.set_item('target_path', target_path) @@ -61,7 +51,7 @@ def run() -> Generator[List[BenchmarkSet], None, None]: yield benchmarks -def cycle(benchmark_cycles : int) -> BenchmarkSet: +def cycle(benchmark_cycles : int) -> BenchmarkCycleSet: process_times = [] video_frame_total = count_video_frame_total(state_manager.get_item('target_path')) output_video_resolution = detect_video_resolution(state_manager.get_item('target_path')) @@ -112,4 +102,5 @@ def render() -> None: for benchmark in run(): benchmarks = benchmark - render_table(headers, benchmarks) + contents = [ list(benchmark_set.values()) for benchmark_set in benchmarks ] + render_table(headers, contents) diff --git a/facefusion/choices.py b/facefusion/choices.py index 8c44c45..7e6f8e7 100755 --- a/facefusion/choices.py +++ b/facefusion/choices.py @@ -2,7 +2,7 @@ import logging from typing import List, Sequence from facefusion.common_helper import create_float_range, create_int_range -from facefusion.types import Angle, AudioEncoder, AudioFormat, AudioTypeSet, DownloadProvider, DownloadProviderSet, DownloadScope, EncoderSet, ExecutionProvider, ExecutionProviderSet, FaceDetectorModel, FaceDetectorSet, FaceLandmarkerModel, FaceMaskArea, FaceMaskAreaSet, FaceMaskRegion, FaceMaskRegionSet, FaceMaskType, FaceOccluderModel, FaceParserModel, FaceSelectorMode, FaceSelectorOrder, Gender, ImageFormat, ImageTypeSet, JobStatus, LogLevel, LogLevelSet, Race, Score, TempFrameFormat, UiWorkflow, VideoEncoder, VideoFormat, VideoMemoryStrategy, VideoPreset, VideoTypeSet, WebcamMode +from facefusion.types import Angle, AudioEncoder, AudioFormat, AudioTypeSet, BenchmarkResolution, BenchmarkSet, DownloadProvider, DownloadProviderSet, DownloadScope, EncoderSet, ExecutionProvider, ExecutionProviderSet, FaceDetectorModel, FaceDetectorSet, FaceLandmarkerModel, FaceMaskArea, FaceMaskAreaSet, FaceMaskRegion, FaceMaskRegionSet, FaceMaskType, FaceOccluderModel, FaceParserModel, FaceSelectorMode, FaceSelectorOrder, Gender, ImageFormat, ImageTypeSet, JobStatus, LogLevel, LogLevelSet, Race, Score, TempFrameFormat, UiWorkflow, VideoEncoder, VideoFormat, VideoMemoryStrategy, VideoPreset, VideoTypeSet, WebcamMode face_detector_set : FaceDetectorSet =\ { @@ -85,6 +85,18 @@ output_video_presets : List[VideoPreset] = [ 'ultrafast', 'superfast', 'veryfast image_template_sizes : List[float] = [ 0.25, 0.5, 0.75, 1, 1.5, 2, 2.5, 3, 3.5, 4 ] video_template_sizes : List[int] = [ 240, 360, 480, 540, 720, 1080, 1440, 2160, 4320 ] +benchmark_set : BenchmarkSet =\ +{ + '240p': '.assets/examples/target-240p.mp4', + '360p': '.assets/examples/target-360p.mp4', + '540p': '.assets/examples/target-540p.mp4', + '720p': '.assets/examples/target-720p.mp4', + '1080p': '.assets/examples/target-1080p.mp4', + '1440p': '.assets/examples/target-1440p.mp4', + '2160p': '.assets/examples/target-2160p.mp4' +} +benchmark_resolutions : List[BenchmarkResolution] = list(benchmark_set.keys()) + webcam_modes : List[WebcamMode] = [ 'inline', 'udp', 'v4l2' ] webcam_resolutions : List[str] = [ '320x240', '640x480', '800x600', '1024x768', '1280x720', '1280x960', '1920x1080', '2560x1440', '3840x2160' ] @@ -136,6 +148,7 @@ log_levels : List[LogLevel] = list(log_level_set.keys()) ui_workflows : List[UiWorkflow] = [ 'instant_runner', 'job_runner', 'job_manager' ] job_statuses : List[JobStatus] = [ 'drafted', 'queued', 'completed', 'failed' ] +benchmark_cycles_range : Sequence[int] = create_int_range(1, 10, 1) execution_thread_count_range : Sequence[int] = create_int_range(1, 32, 1) execution_queue_count_range : Sequence[int] = create_int_range(1, 4, 1) system_memory_limit_range : Sequence[int] = create_int_range(0, 128, 4) diff --git a/facefusion/program.py b/facefusion/program.py index 6f0d487..ae35562 100755 --- a/facefusion/program.py +++ b/facefusion/program.py @@ -215,11 +215,10 @@ def create_download_providers_program() -> ArgumentParser: def create_benchmark_program() -> ArgumentParser: - from facefusion.benchmarker import BENCHMARKS program = ArgumentParser(add_help = False) group_benchmark = program.add_argument_group('benchmark') - group_benchmark.add_argument('--benchmark-resolutions', help = wording.get('help.benchmark_resolutions'), default = config.get_str_list('benchmark', 'benchmark_resolutions', '240p'), choices = list(BENCHMARKS.keys()), nargs = '+') - group_benchmark.add_argument('--benchmark-cycles', help = wording.get('help.benchmark_cycles'), type = int, default = config.get_int_value('benchmark', 'benchmark_cycles', '5'), choices = range(1, 11)) + group_benchmark.add_argument('--benchmark-resolutions', help = wording.get('help.benchmark_resolutions'), default = config.get_str_list('benchmark', 'benchmark_resolutions', get_first(facefusion.choices.benchmark_resolutions)), choices = facefusion.choices.benchmark_resolutions, nargs = '+') + group_benchmark.add_argument('--benchmark-cycles', help = wording.get('help.benchmark_cycles'), type = int, default = config.get_int_value('benchmark', 'benchmark_cycles', '5'), choices = facefusion.choices.benchmark_cycles_range) return program diff --git a/facefusion/types.py b/facefusion/types.py index 4afc91d..b7d5d19 100755 --- a/facefusion/types.py +++ b/facefusion/types.py @@ -100,16 +100,6 @@ LogLevelSet : TypeAlias = Dict[LogLevel, int] TableHeaders = List[str] TableContents = List[List[Any]] -BenchmarkSet = TypedDict('BenchmarkSet', -{ - 'target_path' : str, - 'benchmark_cycles' : int, - 'average_run' : float, - 'fastest_run' : float, - 'slowest_run' : float, - 'relative_fps' : float -}) - FaceDetectorModel = Literal['many', 'retinaface', 'scrfd', 'yolo_face'] FaceLandmarkerModel = Literal['many', '2dfan4', 'peppa_wutz'] FaceDetectorSet : TypeAlias = Dict[FaceDetectorModel, List[str]] @@ -140,6 +130,18 @@ EncoderSet = TypedDict('EncoderSet', }) VideoPreset = Literal['ultrafast', 'superfast', 'veryfast', 'faster', 'fast', 'medium', 'slow', 'slower', 'veryslow'] +BenchmarkResolution = Literal['240p', '360p', '540p', '720p', '1080p', '1440p', '2160p'] +BenchmarkSet : TypeAlias = Dict[BenchmarkResolution, str] +BenchmarkCycleSet = TypedDict('BenchmarkCycleSet', +{ + 'target_path' : str, + 'benchmark_cycles' : int, + 'average_run' : float, + 'fastest_run' : float, + 'slowest_run' : float, + 'relative_fps' : float +}) + WebcamMode = Literal['inline', 'udp', 'v4l2'] StreamMode = Literal['udp', 'v4l2'] From 56d714368ad7b9de11219061e8e49d7d4dab9ce3 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Mon, 16 Jun 2025 11:44:22 +0200 Subject: [PATCH 09/15] Fix UI --- facefusion/uis/components/benchmark.py | 3 ++- facefusion/uis/components/benchmark_options.py | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/facefusion/uis/components/benchmark.py b/facefusion/uis/components/benchmark.py index 55e1577..89939a6 100644 --- a/facefusion/uis/components/benchmark.py +++ b/facefusion/uis/components/benchmark.py @@ -56,4 +56,5 @@ def start(benchmark_resolutions : List[str], benchmark_cycles : int) -> Generato state_manager.sync_item('execution_thread_count') state_manager.sync_item('execution_queue_count') - yield from benchmarker.run() + for benchmark in benchmarker.run(): + yield [ list(benchmark_set.values()) for benchmark_set in benchmark ] diff --git a/facefusion/uis/components/benchmark_options.py b/facefusion/uis/components/benchmark_options.py index a7f07cf..60322af 100644 --- a/facefusion/uis/components/benchmark_options.py +++ b/facefusion/uis/components/benchmark_options.py @@ -3,7 +3,7 @@ from typing import Optional import gradio from facefusion import wording -from facefusion.benchmarker import BENCHMARKS +import facefusion.choices from facefusion.uis.core import register_ui_component BENCHMARK_RESOLUTIONS_CHECKBOX_GROUP : Optional[gradio.CheckboxGroup] = None @@ -16,15 +16,15 @@ def render() -> None: BENCHMARK_RESOLUTIONS_CHECKBOX_GROUP = gradio.CheckboxGroup( label = wording.get('uis.benchmark_resolutions_checkbox_group'), - choices = list(BENCHMARKS.keys()), - value = list(BENCHMARKS.keys()) + choices = facefusion.choices.benchmark_resolutions, + value = facefusion.choices.benchmark_resolutions ) BENCHMARK_CYCLES_SLIDER = gradio.Slider( label = wording.get('uis.benchmark_cycles_slider'), value = 5, step = 1, - minimum = 1, - maximum = 10 + minimum = min(facefusion.choices.benchmark_cycles_range), + maximum = max(facefusion.choices.benchmark_cycles_range) ) register_ui_component('benchmark_resolutions_checkbox_group', BENCHMARK_RESOLUTIONS_CHECKBOX_GROUP) register_ui_component('benchmark_cycles_slider', BENCHMARK_CYCLES_SLIDER) From 9e1d60fd076dbdbc732bd185141a0a8f90774bdc Mon Sep 17 00:00:00 2001 From: henryruhs Date: Mon, 16 Jun 2025 11:52:52 +0200 Subject: [PATCH 10/15] Fix UI --- facefusion/program.py | 2 +- facefusion/uis/components/benchmark_options.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/facefusion/program.py b/facefusion/program.py index ae35562..5f415f0 100755 --- a/facefusion/program.py +++ b/facefusion/program.py @@ -292,7 +292,7 @@ def create_program() -> ArgumentParser: sub_program.add_parser('headless-run', help = wording.get('help.headless_run'), parents = [ create_config_path_program(), create_temp_path_program(), create_jobs_path_program(), create_source_paths_program(), create_target_path_program(), create_output_path_program(), collect_step_program(), collect_job_program() ], formatter_class = create_help_formatter_large) sub_program.add_parser('batch-run', help = wording.get('help.batch_run'), parents = [ create_config_path_program(), create_temp_path_program(), create_jobs_path_program(), create_source_pattern_program(), create_target_pattern_program(), create_output_pattern_program(), collect_step_program(), collect_job_program() ], formatter_class = create_help_formatter_large) sub_program.add_parser('force-download', help = wording.get('help.force_download'), parents = [ create_download_providers_program(), create_download_scope_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) - sub_program.add_parser('benchmark', help = wording.get('help.benchmark'), parents = [ create_config_path_program(), create_temp_path_program(), create_jobs_path_program(), create_benchmark_program(), collect_step_program(), collect_job_program() ], formatter_class = create_help_formatter_large) + sub_program.add_parser('benchmark', help = wording.get('help.benchmark'), parents = [ create_temp_path_program(), create_benchmark_program(), collect_step_program(), collect_job_program() ], formatter_class = create_help_formatter_large) # job manager sub_program.add_parser('job-list', help = wording.get('help.job_list'), parents = [ create_job_status_program(), create_jobs_path_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) sub_program.add_parser('job-create', help = wording.get('help.job_create'), parents = [ create_job_id_program(), create_jobs_path_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) diff --git a/facefusion/uis/components/benchmark_options.py b/facefusion/uis/components/benchmark_options.py index 60322af..1b52ed3 100644 --- a/facefusion/uis/components/benchmark_options.py +++ b/facefusion/uis/components/benchmark_options.py @@ -2,8 +2,8 @@ from typing import Optional import gradio -from facefusion import wording import facefusion.choices +from facefusion import wording from facefusion.uis.core import register_ui_component BENCHMARK_RESOLUTIONS_CHECKBOX_GROUP : Optional[gradio.CheckboxGroup] = None From 4f204194846318469ee97840acb77962fd115cf8 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Mon, 16 Jun 2025 12:01:11 +0200 Subject: [PATCH 11/15] Minor wording --- README.md | 2 +- facefusion/wording.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d16935e..5eb32f4 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ commands: headless-run run the program in headless mode batch-run run the program in batch mode force-download force automate downloads and exit - benchmark run performance benchmarks and exit + benchmark benchmark the program job-list list jobs by status job-create create a drafted job job-submit submit a drafted job to become a queued job diff --git a/facefusion/wording.py b/facefusion/wording.py index 89f4f04..49854e8 100755 --- a/facefusion/wording.py +++ b/facefusion/wording.py @@ -211,7 +211,7 @@ WORDING : Dict[str, Any] =\ 'headless_run': 'run the program in headless mode', 'batch_run': 'run the program in batch mode', 'force_download': 'force automate downloads and exit', - 'benchmark': 'run performance benchmarks', + 'benchmark': 'benchmark the program', # jobs 'job_id': 'specify the job id', 'job_status': 'specify the job status', From bf651f5420a4bc1bcfc3c2500af40ad7c0cb672d Mon Sep 17 00:00:00 2001 From: henryruhs Date: Mon, 16 Jun 2025 12:03:20 +0200 Subject: [PATCH 12/15] Fix import --- facefusion/uis/layouts/benchmark.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/facefusion/uis/layouts/benchmark.py b/facefusion/uis/layouts/benchmark.py index 79f3670..b6d6686 100644 --- a/facefusion/uis/layouts/benchmark.py +++ b/facefusion/uis/layouts/benchmark.py @@ -2,10 +2,7 @@ import gradio from facefusion import state_manager from facefusion.benchmarker import pre_check as benchmarker_pre_check -from facefusion.uis.components import (about, age_modifier_options, benchmark, benchmark_options, deep_swapper_options, - download, execution, execution_queue_count, execution_thread_count, expression_restorer_options, - face_debugger_options, face_editor_options, face_enhancer_options, face_swapper_options, - frame_colorizer_options, frame_enhancer_options, lip_syncer_options, memory, processors) +from facefusion.uis.components import about, age_modifier_options, benchmark, benchmark_options, deep_swapper_options, download, execution, execution_queue_count, execution_thread_count, expression_restorer_options, face_debugger_options, face_editor_options, face_enhancer_options, face_swapper_options, frame_colorizer_options, frame_enhancer_options, lip_syncer_options, memory, processors def pre_check() -> bool: From 13772f8400499b24f220ca0514f26f08861e9f16 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Mon, 16 Jun 2025 12:04:31 +0200 Subject: [PATCH 13/15] This types are never needed --- facefusion/uis/types.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/facefusion/uis/types.py b/facefusion/uis/types.py index 32a6080..aeb606e 100644 --- a/facefusion/uis/types.py +++ b/facefusion/uis/types.py @@ -5,8 +5,6 @@ ComponentName = Literal\ [ 'age_modifier_direction_slider', 'age_modifier_model_dropdown', - 'benchmark_cycles_slider', - 'benchmark_resolutions_checkbox_group', 'deep_swapper_model_dropdown', 'deep_swapper_morph_slider', 'expression_restorer_factor_slider', From 505432cfdea1901151357fc23a50bdb0287e82d9 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Mon, 16 Jun 2025 12:08:18 +0200 Subject: [PATCH 14/15] This types are needed --- facefusion/uis/types.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/facefusion/uis/types.py b/facefusion/uis/types.py index aeb606e..32a6080 100644 --- a/facefusion/uis/types.py +++ b/facefusion/uis/types.py @@ -5,6 +5,8 @@ ComponentName = Literal\ [ 'age_modifier_direction_slider', 'age_modifier_model_dropdown', + 'benchmark_cycles_slider', + 'benchmark_resolutions_checkbox_group', 'deep_swapper_model_dropdown', 'deep_swapper_morph_slider', 'expression_restorer_factor_slider', From 6340d67c9645fae7021a7b97b67b06e3ce79fee0 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Mon, 16 Jun 2025 12:27:15 +0200 Subject: [PATCH 15/15] Polish types --- facefusion/types.py | 16 ++++++++-------- facefusion/uis/components/benchmark.py | 3 ++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/facefusion/types.py b/facefusion/types.py index b7d5d19..4078741 100755 --- a/facefusion/types.py +++ b/facefusion/types.py @@ -250,6 +250,10 @@ StateKey = Literal\ 'source_pattern', 'target_pattern', 'output_pattern', + 'download_providers', + 'download_scope', + 'benchmark_resolutions', + 'benchmark_cycles', 'face_detector_model', 'face_detector_size', 'face_detector_angles', @@ -294,10 +298,6 @@ StateKey = Literal\ 'execution_providers', 'execution_thread_count', 'execution_queue_count', - 'download_providers', - 'benchmark_resolutions', - 'benchmark_cycles', - 'download_scope', 'video_memory_strategy', 'system_memory_limit', 'log_level', @@ -318,6 +318,10 @@ State = TypedDict('State', 'source_pattern' : str, 'target_pattern' : str, 'output_pattern' : str, + 'download_providers': List[DownloadProvider], + 'download_scope': DownloadScope, + 'benchmark_resolutions': List[BenchmarkResolution], + 'benchmark_cycles': int, 'face_detector_model' : FaceDetectorModel, 'face_detector_size' : str, 'face_detector_angles' : List[Angle], @@ -362,10 +366,6 @@ State = TypedDict('State', 'execution_providers' : List[ExecutionProvider], 'execution_thread_count' : int, 'execution_queue_count' : int, - 'download_providers' : List[DownloadProvider], - 'benchmark_resolutions' : List[str], - 'benchmark_cycles' : int, - 'download_scope' : DownloadScope, 'video_memory_strategy' : VideoMemoryStrategy, 'system_memory_limit' : int, 'log_level' : LogLevel, diff --git a/facefusion/uis/components/benchmark.py b/facefusion/uis/components/benchmark.py index 89939a6..038b289 100644 --- a/facefusion/uis/components/benchmark.py +++ b/facefusion/uis/components/benchmark.py @@ -3,6 +3,7 @@ from typing import Any, Generator, List, Optional import gradio from facefusion import benchmarker, state_manager, wording +from facefusion.types import BenchmarkResolution from facefusion.uis.core import get_ui_component BENCHMARK_BENCHMARKS_DATAFRAME : Optional[gradio.Dataframe] = None @@ -49,7 +50,7 @@ def listen() -> None: BENCHMARK_START_BUTTON.click(start, inputs = [ benchmark_resolutions_checkbox_group, benchmark_cycles_slider ], outputs = BENCHMARK_BENCHMARKS_DATAFRAME) -def start(benchmark_resolutions : List[str], benchmark_cycles : int) -> Generator[List[Any], None, None]: +def start(benchmark_resolutions : List[BenchmarkResolution], benchmark_cycles : int) -> Generator[List[Any], None, None]: state_manager.set_item('benchmark_resolutions', benchmark_resolutions) state_manager.set_item('benchmark_cycles', benchmark_cycles) state_manager.sync_item('execution_providers')