From 3f81a8a78454089d720b8f318a12ae1702c4633b Mon Sep 17 00:00:00 2001 From: Henry Ruhs Date: Sun, 5 Jul 2026 16:43:36 +0200 Subject: [PATCH] patch-3.7.1 (#1178) * patch 3.7.1 * fix bug for 2 processors on image to image pipeline (#1177) * reduce default value of target_frame_amount * restore old performance * restore old performance * restore old performance * restore old performance --- facefusion/metadata.py | 2 +- facefusion/program.py | 2 +- facefusion/video_manager.py | 6 ++++++ facefusion/vision.py | 20 ++++++++++---------- facefusion/workflows/image_to_image.py | 5 +++-- 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/facefusion/metadata.py b/facefusion/metadata.py index 4cc03d69..13086cc6 100644 --- a/facefusion/metadata.py +++ b/facefusion/metadata.py @@ -4,7 +4,7 @@ METADATA =\ { 'name': 'FaceFusion', 'description': 'Industry leading face manipulation platform', - 'version': '3.7.0', + 'version': '3.7.1', 'license': 'OpenRAIL-AS', 'author': 'Henry Ruhs', 'url': 'https://facefusion.io' diff --git a/facefusion/program.py b/facefusion/program.py index 540b2862..ae2d9667 100755 --- a/facefusion/program.py +++ b/facefusion/program.py @@ -177,7 +177,7 @@ def create_frame_extraction_program() -> ArgumentParser: def create_frame_distribution_program() -> ArgumentParser: program = ArgumentParser(add_help = False) group_frame_distribution = program.add_argument_group('frame distribution') - group_frame_distribution.add_argument('--target-frame-amount', help = translator.get('help.target_frame_amount'), type = int, default = config.get_int_value('frame_distribution', 'target_frame_amount', '5'), choices = facefusion.choices.target_frame_amount_range, metavar = create_int_metavar(facefusion.choices.target_frame_amount_range)) + group_frame_distribution.add_argument('--target-frame-amount', help = translator.get('help.target_frame_amount'), type = int, default = config.get_int_value('frame_distribution', 'target_frame_amount', '2'), choices = facefusion.choices.target_frame_amount_range, metavar = create_int_metavar(facefusion.choices.target_frame_amount_range)) job_store.register_step_keys([ 'target_frame_amount' ]) return program diff --git a/facefusion/video_manager.py b/facefusion/video_manager.py index 8923afb6..b63203cc 100644 --- a/facefusion/video_manager.py +++ b/facefusion/video_manager.py @@ -19,6 +19,12 @@ def get_video_capture(video_path : str) -> cv2.VideoCapture: return VIDEO_POOL_SET.get('capture').get(video_path) +def conditional_set_video_frame_position(video_capture : cv2.VideoCapture, frame_position : int) -> bool: + if not video_capture.get(cv2.CAP_PROP_POS_FRAMES) == frame_position: + return video_capture.set(cv2.CAP_PROP_POS_FRAMES, frame_position) + return True + + def get_video_writer(video_path : str) -> cv2.VideoWriter: if video_path not in VIDEO_POOL_SET.get('writer'): video_writer = cv2.VideoWriter() diff --git a/facefusion/vision.py b/facefusion/vision.py index 50b2a8d9..79ddb536 100644 --- a/facefusion/vision.py +++ b/facefusion/vision.py @@ -10,7 +10,7 @@ from facefusion.common_helper import is_windows from facefusion.filesystem import get_file_extension, is_image, is_video from facefusion.thread_helper import thread_lock, thread_semaphore from facefusion.types import ColorMode, Duration, Fps, Mask, Orientation, Resolution, Scale, VisionFrame -from facefusion.video_manager import get_video_capture +from facefusion.video_manager import conditional_set_video_frame_position, get_video_capture def read_static_images(image_paths : List[str], color_mode : ColorMode = 'rgb') -> List[VisionFrame]: @@ -80,11 +80,11 @@ def read_video_frame(video_path : str, frame_number : int = 0) -> Optional[Visio video_capture = get_video_capture(video_path) if video_capture and video_capture.isOpened(): - frame_total = video_capture.get(cv2.CAP_PROP_FRAME_COUNT) - frame_position = min(frame_total, frame_number) + video_frame_total = int(video_capture.get(cv2.CAP_PROP_FRAME_COUNT)) + video_frame_position = min(video_frame_total, frame_number) with thread_semaphore(): - video_capture.set(cv2.CAP_PROP_POS_FRAMES, frame_position) + conditional_set_video_frame_position(video_capture, video_frame_position) has_vision_frame, vision_frame = video_capture.read() if has_vision_frame: @@ -105,13 +105,13 @@ def read_video_chunk(video_path : str, chunk_number : int, chunk_size : int) -> video_capture = get_video_capture(video_path) if video_capture and video_capture.isOpened(): - frame_total = int(video_capture.get(cv2.CAP_PROP_FRAME_COUNT)) - frame_position = chunk_number * chunk_size + video_frame_total = int(video_capture.get(cv2.CAP_PROP_FRAME_COUNT)) + video_frame_position = chunk_number * chunk_size with thread_semaphore(): - video_capture.set(cv2.CAP_PROP_POS_FRAMES, frame_position) + conditional_set_video_frame_position(video_capture, video_frame_position) - for frame_number in range(frame_position, min(frame_position + chunk_size, frame_total)): + for frame_number in range(video_frame_position, min(video_frame_position + chunk_size, video_frame_total)): has_vision_frame, vision_frame = video_capture.read() if has_vision_frame: @@ -120,9 +120,9 @@ def read_video_chunk(video_path : str, chunk_number : int, chunk_size : int) -> return video_frame_chunk -def select_video_frames(video_path : str, frame_number : int = 0, frame_offset : int = 5) -> List[VisionFrame]: +def select_video_frames(video_path : str, frame_number : int = 0, frame_offset : int = 2) -> List[VisionFrame]: vision_frames = [] - chunk_size = (frame_offset * 2 + 1) * 4 + chunk_size = frame_offset * 2 + 1 if is_video(video_path): with thread_lock(): diff --git a/facefusion/workflows/image_to_image.py b/facefusion/workflows/image_to_image.py index 766a9dd9..9af47856 100644 --- a/facefusion/workflows/image_to_image.py +++ b/facefusion/workflows/image_to_image.py @@ -63,10 +63,11 @@ def prepare_image() -> ErrorCode: def process_image() -> ErrorCode: temp_image_path = get_temp_file_path(state_manager.get_item('target_path')) - reference_vision_frame = read_static_image(temp_image_path) + reference_vision_frame = read_static_image(state_manager.get_item('target_path')) source_vision_frames = read_static_images(state_manager.get_item('source_paths')) source_audio_frame = create_empty_audio_frame() source_voice_frame = create_empty_audio_frame() + target_vision_frame = read_static_image(state_manager.get_item('target_path')) temp_vision_frame = read_static_image(temp_image_path, 'rgba') temp_vision_mask = extract_vision_mask(temp_vision_frame) @@ -79,7 +80,7 @@ def process_image() -> ErrorCode: 'source_vision_frames': source_vision_frames, 'source_audio_frame': source_audio_frame, 'source_voice_frame': source_voice_frame, - 'target_vision_frames': [ temp_vision_frame[:, :, :3] ], + 'target_vision_frames': [ target_vision_frame ], 'temp_vision_frame': temp_vision_frame[:, :, :3], 'temp_vision_mask': temp_vision_mask })