From ca206f1c99aea49ed92d4108abf7dc49f320289d Mon Sep 17 00:00:00 2001 From: henryruhs Date: Mon, 21 Aug 2023 18:56:44 +0200 Subject: [PATCH 1/6] First set for extract_frames() --- tests/test_cli.py | 4 +++- tests/test_utilities.py | 49 +++++++++++++++++++++++++++++++++-------- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 138177e..8a48a02 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -6,7 +6,7 @@ from facefusion.utilities import conditional_download @pytest.fixture(scope = 'module', autouse = True) -def setup() -> None: +def before_all() -> None: conditional_download('.assets/examples', [ 'https://github.com/facefusion/facefusion-assets/releases/download/examples/source.jpg', @@ -18,6 +18,7 @@ def setup() -> None: def test_image_to_image() -> None: commands = [ 'python', 'run.py', '-s', '.assets/examples/source.jpg', '-t', '.assets/examples/target-1080p.jpg', '-o', '.assets/examples' ] run = subprocess.run(commands, stdout = subprocess.PIPE) + assert run.returncode == 0 assert wording.get('processing_image_succeed') in run.stdout.decode() @@ -25,5 +26,6 @@ def test_image_to_image() -> None: def test_image_to_video() -> None: commands = [ 'python', 'run.py', '-s', '.assets/examples/source.jpg', '-t', '.assets/examples/target-1080p.mp4', '-o', '.assets/examples', '--trim-frame-end', '10' ] run = subprocess.run(commands, stdout = subprocess.PIPE) + assert run.returncode == 0 assert wording.get('processing_video_succeed') in run.stdout.decode() diff --git a/tests/test_utilities.py b/tests/test_utilities.py index 251d57d..8acba7d 100644 --- a/tests/test_utilities.py +++ b/tests/test_utilities.py @@ -1,21 +1,52 @@ +import glob import subprocess import pytest -from facefusion.utilities import conditional_download, detect_fps +import facefusion.globals +from facefusion.utilities import conditional_download, detect_fps, extract_frames, create_temp, get_temp_directory_path, clear_temp @pytest.fixture(scope = 'module', autouse = True) -def setup() -> None: +def before_all() -> None: + facefusion.globals.temp_frame_quality = 100 + facefusion.globals.trim_frame_start = None + facefusion.globals.trim_frame_end = None + facefusion.globals.temp_frame_format = 'png' conditional_download('.assets/examples', [ - 'https://github.com/facefusion/facefusion-assets/releases/download/examples/target-1080p.mp4' + 'https://github.com/facefusion/facefusion-assets/releases/download/examples/target-240p.mp4' ]) - subprocess.run([ 'ffmpeg', '-i', '.assets/examples/target-1080p.mp4', '-vf', 'fps=25', '.assets/examples/target-1080p-25fps.mp4' ]) - subprocess.run([ 'ffmpeg', '-i', '.assets/examples/target-1080p.mp4', '-vf', 'fps=30', '.assets/examples/target-1080p-30fps.mp4' ]) - subprocess.run([ 'ffmpeg', '-i', '.assets/examples/target-1080p.mp4', '-vf', 'fps=60', '.assets/examples/target-1080p-60fps.mp4' ]) + subprocess.run([ 'ffmpeg', '-i', '.assets/examples/target-240p.mp4', '-vf', 'fps=25', '.assets/examples/target-240p-25fps.mp4' ]) + subprocess.run([ 'ffmpeg', '-i', '.assets/examples/target-240p.mp4', '-vf', 'fps=30', '.assets/examples/target-240p-30fps.mp4' ]) + subprocess.run([ 'ffmpeg', '-i', '.assets/examples/target-240p.mp4', '-vf', 'fps=60', '.assets/examples/target-240p-60fps.mp4' ]) + + +@pytest.fixture(scope = 'function', autouse = True) +def before_each() -> None: + facefusion.globals.trim_frame_start = None + facefusion.globals.trim_frame_end = None + facefusion.globals.temp_frame_quality = 90 + facefusion.globals.temp_frame_format = 'jpg' def test_detect_fps() -> None: - assert detect_fps('.assets/examples/target-1080p-25fps.mp4') == 25.0 - assert detect_fps('.assets/examples/target-1080p-30fps.mp4') == 30.0 - assert detect_fps('.assets/examples/target-1080p-60fps.mp4') == 60.0 + assert detect_fps('.assets/examples/target-240p-25fps.mp4') == 25.0 + assert detect_fps('.assets/examples/target-240p-30fps.mp4') == 30.0 + assert detect_fps('.assets/examples/target-240p-60fps.mp4') == 60.0 + + +def test_extract_frames() -> None: + target_paths =\ + [ + '.assets/examples/target-240p-25fps.mp4', + '.assets/examples/target-240p-30fps.mp4', + '.assets/examples/target-240p-60fps.mp4' + ] + for target_path in target_paths: + temp_directory_path = get_temp_directory_path(target_path) + create_temp(target_path) + + assert extract_frames(target_path, 30) is True + assert len(glob.glob1(temp_directory_path, '*.jpg')) == 324 + + clear_temp(target_path) From 49061f133d0eabec7426c612d78d5c814464e83c Mon Sep 17 00:00:00 2001 From: henryruhs Date: Mon, 21 Aug 2023 23:51:26 +0200 Subject: [PATCH 2/6] Finish tests for extract_frames() --- facefusion/utilities.py | 18 +++++++------- tests/test_utilities.py | 55 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/facefusion/utilities.py b/facefusion/utilities.py index 41d2d32..1e77c1f 100644 --- a/facefusion/utilities.py +++ b/facefusion/utilities.py @@ -34,30 +34,30 @@ def run_ffmpeg(args : List[str]) -> bool: return False -def detect_fps(target_path : str) -> float: +def detect_fps(target_path : str) -> Optional[float]: commands = [ 'ffprobe', '-v', 'error', '-select_streams', 'v:0', '-show_entries', 'stream=r_frame_rate', '-of', 'default=noprint_wrappers = 1:nokey = 1', target_path ] output = subprocess.check_output(commands).decode().strip().split('/') try: numerator, denominator = map(int, output) return numerator / denominator except (ValueError, ZeroDivisionError): - return 30 + return None -def extract_frames(target_path : str, fps : float = 30) -> bool: +def extract_frames(target_path : str, fps : float = 30.0) -> bool: temp_directory_path = get_temp_directory_path(target_path) temp_frame_quality = round(31 - (facefusion.globals.temp_frame_quality * 0.31)) trim_frame_start = facefusion.globals.trim_frame_start trim_frame_end = facefusion.globals.trim_frame_end - commands = [ '-hwaccel', 'auto', '-i', target_path, '-q:v', str(temp_frame_quality), '-pix_fmt', 'rgb24' ] + commands = [ '-hwaccel', 'auto', '-i', target_path, '-q:v', str(temp_frame_quality), '-pix_fmt', 'rgb24', ] if trim_frame_start is not None and trim_frame_end is not None: - commands.extend(['-vf', 'trim=start_frame=' + str(trim_frame_start) + ':end_frame=' + str(trim_frame_end) + ',fps=' + str(fps)]) + commands.extend([ '-vf', 'trim=start_frame=' + str(trim_frame_start) + ':end_frame=' + str(trim_frame_end) + ',fps=' + str(fps) ]) elif trim_frame_start is not None: - commands.extend(['-vf', 'trim=start_frame=' + str(trim_frame_start) + ',fps=' + str(fps)]) + commands.extend([ '-vf', 'trim=start_frame=' + str(trim_frame_start) + ',fps=' + str(fps) ]) elif trim_frame_end is not None: - commands.extend(['-vf', 'trim=end_frame=' + str(trim_frame_end) + ',fps=' + str(fps)]) + commands.extend([ '-vf', 'trim=end_frame=' + str(trim_frame_end) + ',fps=' + str(fps) ]) else: - commands.extend(['-vf', 'fps=' + str(fps)]) + commands.extend([ '-vf', 'fps=' + str(fps) ]) commands.extend([os.path.join(temp_directory_path, '%04d.' + facefusion.globals.temp_frame_format)]) return run_ffmpeg(commands) @@ -68,7 +68,7 @@ def create_video(target_path : str, fps : float = 30) -> bool: output_video_quality = round(51 - (facefusion.globals.output_video_quality * 0.5)) commands = [ '-hwaccel', 'auto', '-r', str(fps), '-i', os.path.join(temp_directory_path, '%04d.' + facefusion.globals.temp_frame_format), '-c:v', facefusion.globals.output_video_encoder ] if facefusion.globals.output_video_encoder in [ 'libx264', 'libx265', 'libvpx' ]: - commands.extend(['-crf', str(output_video_quality)]) + commands.extend([ '-crf', str(output_video_quality) ]) if facefusion.globals.output_video_encoder in [ 'h264_nvenc', 'hevc_nvenc' ]: commands.extend([ '-cq', str(output_video_quality) ]) commands.extend([ '-pix_fmt', 'yuv420p', '-vf', 'colorspace=bt709:iall=bt601-6-625', '-y', temp_output_path ]) diff --git a/tests/test_utilities.py b/tests/test_utilities.py index 8acba7d..b233a7c 100644 --- a/tests/test_utilities.py +++ b/tests/test_utilities.py @@ -50,3 +50,58 @@ def test_extract_frames() -> None: assert len(glob.glob1(temp_directory_path, '*.jpg')) == 324 clear_temp(target_path) + + +def test_extract_frames_with_trim_start() -> None: + facefusion.globals.trim_frame_start = 224 + data_provider =\ + [ + ('.assets/examples/target-240p-25fps.mp4', 55), + ('.assets/examples/target-240p-30fps.mp4', 100), + ('.assets/examples/target-240p-60fps.mp4', 212) + ] + for target_path, frame_total in data_provider: + temp_directory_path = get_temp_directory_path(target_path) + create_temp(target_path) + + assert extract_frames(target_path, 30) is True + assert len(glob.glob1(temp_directory_path, '*.jpg')) == frame_total + + clear_temp(target_path) + + +def test_extract_frames_with_trim_start_and_trim_end() -> None: + facefusion.globals.trim_frame_start = 224 + facefusion.globals.trim_frame_end = 324 + data_provider =\ + [ + ('.assets/examples/target-240p-25fps.mp4', 55), + ('.assets/examples/target-240p-30fps.mp4', 100), + ('.assets/examples/target-240p-60fps.mp4', 50) + ] + for target_path, frame_total in data_provider: + temp_directory_path = get_temp_directory_path(target_path) + create_temp(target_path) + + assert extract_frames(target_path, 30) is True + assert len(glob.glob1(temp_directory_path, '*.jpg')) == frame_total + + clear_temp(target_path) + + +def test_extract_frames_with_trim_end() -> None: + facefusion.globals.trim_frame_end = 100 + data_provider =\ + [ + ('.assets/examples/target-240p-25fps.mp4', 120), + ('.assets/examples/target-240p-30fps.mp4', 100), + ('.assets/examples/target-240p-60fps.mp4', 50) + ] + for target_path, frame_total in data_provider: + temp_directory_path = get_temp_directory_path(target_path) + create_temp(target_path) + + assert extract_frames(target_path, 30) is True + assert len(glob.glob1(temp_directory_path, '*.jpg')) == frame_total + + clear_temp(target_path) From 3c059cfb0e61918782efc046d2f161a37efa23c8 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Tue, 22 Aug 2023 01:30:32 +0200 Subject: [PATCH 3/6] Fix restore audio --- facefusion/utilities.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/facefusion/utilities.py b/facefusion/utilities.py index 1e77c1f..9184069 100644 --- a/facefusion/utilities.py +++ b/facefusion/utilities.py @@ -76,17 +76,24 @@ def create_video(target_path : str, fps : float = 30) -> bool: def restore_audio(target_path : str, output_path : str) -> None: + fps = detect_fps(target_path) trim_frame_start = facefusion.globals.trim_frame_start trim_frame_end = facefusion.globals.trim_frame_end temp_output_path = get_temp_output_path(target_path) commands = [ '-hwaccel', 'auto', '-i', temp_output_path, '-i', target_path ] - if trim_frame_start is not None and trim_frame_end is not None: - commands.extend([ '-filter:v', 'select=between(n,' + str(trim_frame_start) + ',' + str(trim_frame_end) + ')' ]) - elif trim_frame_start is not None: - commands.extend([ '-filter:v', 'select=gt(n,' + str(trim_frame_start) + ')' ]) - elif trim_frame_end is not None: - commands.extend([ '-filter:v', 'select=lt(n,' + str(trim_frame_end) + ')' ]) - commands.extend([ '-c:a', 'copy', '-map', '0:v:0', '-map', '1:a:0', '-y', output_path ]) + if trim_frame_start is None and trim_frame_end is None: + commands.extend([ '-c:a', 'copy' ]) + else: + if trim_frame_start > 0: + start_time = trim_frame_start / fps + commands.extend([ '-ss', str(start_time) ]) + else: + commands.extend([ '-ss', '0' ]) + if trim_frame_end > 0: + end_time = trim_frame_end / fps + commands.extend([ '-to', str(end_time) ]) + commands.extend([ '-c:a', 'aac' ]) + commands.extend([ '-map', '0:v:0', '-map', '1:a:0', '-y', output_path ]) done = run_ffmpeg(commands) if not done: move_temp(target_path, output_path) From 24c6903158eaa1d3692ff3fa0d19c11de2eecd04 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Tue, 22 Aug 2023 01:37:10 +0200 Subject: [PATCH 4/6] Fix restore audio --- facefusion/utilities.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/facefusion/utilities.py b/facefusion/utilities.py index 9184069..dcbee1c 100644 --- a/facefusion/utilities.py +++ b/facefusion/utilities.py @@ -44,7 +44,7 @@ def detect_fps(target_path : str) -> Optional[float]: return None -def extract_frames(target_path : str, fps : float = 30.0) -> bool: +def extract_frames(target_path : str, fps : float) -> bool: temp_directory_path = get_temp_directory_path(target_path) temp_frame_quality = round(31 - (facefusion.globals.temp_frame_quality * 0.31)) trim_frame_start = facefusion.globals.trim_frame_start @@ -62,7 +62,7 @@ def extract_frames(target_path : str, fps : float = 30.0) -> bool: return run_ffmpeg(commands) -def create_video(target_path : str, fps : float = 30) -> bool: +def create_video(target_path : str, fps : float) -> bool: temp_output_path = get_temp_output_path(target_path) temp_directory_path = get_temp_directory_path(target_path) output_video_quality = round(51 - (facefusion.globals.output_video_quality * 0.5)) From 17aac228ce8f05e2f0ea50f391bafe273f410b64 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Tue, 22 Aug 2023 01:41:46 +0200 Subject: [PATCH 5/6] Fix restore audio --- facefusion/utilities.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/facefusion/utilities.py b/facefusion/utilities.py index dcbee1c..ac61800 100644 --- a/facefusion/utilities.py +++ b/facefusion/utilities.py @@ -44,7 +44,7 @@ def detect_fps(target_path : str) -> Optional[float]: return None -def extract_frames(target_path : str, fps : float) -> bool: +def extract_frames(target_path : str, fps : float = 30) -> bool: temp_directory_path = get_temp_directory_path(target_path) temp_frame_quality = round(31 - (facefusion.globals.temp_frame_quality * 0.31)) trim_frame_start = facefusion.globals.trim_frame_start @@ -62,7 +62,7 @@ def extract_frames(target_path : str, fps : float) -> bool: return run_ffmpeg(commands) -def create_video(target_path : str, fps : float) -> bool: +def create_video(target_path : str, fps : float = 30) -> bool: temp_output_path = get_temp_output_path(target_path) temp_directory_path = get_temp_directory_path(target_path) output_video_quality = round(51 - (facefusion.globals.output_video_quality * 0.5)) From b48240d3ec5019dca3f79b8f459b3e49121a52d8 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Tue, 22 Aug 2023 01:47:56 +0200 Subject: [PATCH 6/6] Fix restore audio --- facefusion/utilities.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/facefusion/utilities.py b/facefusion/utilities.py index ac61800..300b76a 100644 --- a/facefusion/utilities.py +++ b/facefusion/utilities.py @@ -84,12 +84,12 @@ def restore_audio(target_path : str, output_path : str) -> None: if trim_frame_start is None and trim_frame_end is None: commands.extend([ '-c:a', 'copy' ]) else: - if trim_frame_start > 0: + if trim_frame_start is not None: start_time = trim_frame_start / fps commands.extend([ '-ss', str(start_time) ]) else: commands.extend([ '-ss', '0' ]) - if trim_frame_end > 0: + if trim_frame_end is not None: end_time = trim_frame_end / fps commands.extend([ '-to', str(end_time) ]) commands.extend([ '-c:a', 'aac' ])