From 5d2536cabe67300d18b37cf62dedb588b0f15390 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Wed, 22 Jul 2026 22:15:29 +0200 Subject: [PATCH] support for rgba via stream --- facefusion/args.py | 1 + facefusion/choices.py | 4 +++- facefusion/locales.py | 1 + facefusion/program.py | 4 +++- facefusion/types.py | 4 ++++ facefusion/video_manager.py | 8 ++++++-- facefusion/workflows/image_to_video.py | 6 ++++-- 7 files changed, 22 insertions(+), 6 deletions(-) diff --git a/facefusion/args.py b/facefusion/args.py index bc786a4b..ee2083df 100644 --- a/facefusion/args.py +++ b/facefusion/args.py @@ -45,6 +45,7 @@ def apply_args(args : Args, apply_state_item : ApplyStateItem) -> None: apply_state_item('trim_frame_start', args.get('trim_frame_start')) apply_state_item('trim_frame_end', args.get('trim_frame_end')) apply_state_item('temp_frame_format', args.get('temp_frame_format')) + apply_state_item('temp_pixel_format', args.get('temp_pixel_format')) apply_state_item('workflow_mode', args.get('workflow_mode')) apply_state_item('target_frame_amount', args.get('target_frame_amount')) apply_state_item('output_image_quality', args.get('output_image_quality')) diff --git a/facefusion/choices.py b/facefusion/choices.py index 02f6653e..e6063ec7 100755 --- a/facefusion/choices.py +++ b/facefusion/choices.py @@ -2,7 +2,7 @@ import logging from typing import List, Sequence, get_args from facefusion.common_helper import create_float_range, create_int_range -from facefusion.types import Angle, AudioEncoder, AudioFormat, AudioTypeSet, BenchmarkMode, BenchmarkResolution, BenchmarkSet, DownloadProvider, DownloadProviderSet, DownloadScope, EncoderSet, ExecutionProvider, ExecutionProviderSet, FaceDetectorModel, FaceDetectorSet, FaceLandmarkerModel, FaceMaskArea, FaceMaskAreaSet, FaceMaskRegion, FaceMaskRegionSet, FaceMaskType, FaceOccluderModel, FaceParserModel, FaceSelectorGender, FaceSelectorMode, FaceSelectorOrder, FaceSelectorRace, Gender, ImageFormat, ImageTypeSet, JobStatus, LogLevel, LogLevelSet, Race, Score, TempFrameFormat, UiWorkflow, VideoEncoder, VideoFormat, VideoMemoryStrategy, VideoPreset, VideoTypeSet, VoiceExtractorModel, WorkflowMode +from facefusion.types import Angle, AudioEncoder, AudioFormat, AudioTypeSet, BenchmarkMode, BenchmarkResolution, BenchmarkSet, DownloadProvider, DownloadProviderSet, DownloadScope, EncoderSet, ExecutionProvider, ExecutionProviderSet, FaceDetectorModel, FaceDetectorSet, FaceLandmarkerModel, FaceMaskArea, FaceMaskAreaSet, FaceMaskRegion, FaceMaskRegionSet, FaceMaskType, FaceOccluderModel, FaceParserModel, FaceSelectorGender, FaceSelectorMode, FaceSelectorOrder, FaceSelectorRace, Gender, ImageFormat, ImageTypeSet, JobStatus, LogLevel, LogLevelSet, Race, Score, TempFrameFormat, TempPixelFormat, UiWorkflow, VideoEncoder, VideoFormat, VideoMemoryStrategy, VideoPreset, VideoTypeSet, VoiceExtractorModel, WorkflowMode face_detector_set : FaceDetectorSet =\ { @@ -80,6 +80,8 @@ audio_formats : List[AudioFormat] = list(get_args(AudioFormat)) image_formats : List[ImageFormat] = list(get_args(ImageFormat)) video_formats : List[VideoFormat] = list(get_args(VideoFormat)) temp_frame_formats : List[TempFrameFormat] = list(get_args(TempFrameFormat)) +#todo: needs review - [config] [critical: low] temp pixel format choices for the stream pipe +temp_pixel_formats : List[TempPixelFormat] = list(get_args(TempPixelFormat)) output_audio_encoders : List[AudioEncoder] = list(get_args(AudioEncoder)) #todo: needs review - [config] [critical: low] workflow mode choices for disk and stream diff --git a/facefusion/locales.py b/facefusion/locales.py index b0a00ce0..3e6c9204 100644 --- a/facefusion/locales.py +++ b/facefusion/locales.py @@ -136,6 +136,7 @@ LOCALES : Locales =\ 'trim_frame_start': 'specify the starting frame of the target video', 'trim_frame_end': 'specify the ending frame of the target video', 'temp_frame_format': 'specify the temporary resources format', + 'temp_pixel_format': 'specify the pixel format for the stream pipe (rgba keeps the alpha channel)', #todo: needs review - [ui] [critical: low] workflow_mode help text replaces keep_temp 'workflow_mode': 'specify how frames flow through processing (disk extracts and merges temp frames, stream pipes frames through ffmpeg)', 'target_frame_amount': 'specify the amount of target frames forwarded to the processor', diff --git a/facefusion/program.py b/facefusion/program.py index 901c480f..a9ea2d5f 100755 --- a/facefusion/program.py +++ b/facefusion/program.py @@ -169,9 +169,11 @@ def create_frame_extraction_program() -> ArgumentParser: group_frame_extraction.add_argument('--trim-frame-start', help = translator.get('help.trim_frame_start'), type = int, default = facefusion.config.get_int_value('frame_extraction', 'trim_frame_start')) group_frame_extraction.add_argument('--trim-frame-end', help = translator.get('help.trim_frame_end'), type = int, default = facefusion.config.get_int_value('frame_extraction', 'trim_frame_end')) group_frame_extraction.add_argument('--temp-frame-format', help = translator.get('help.temp_frame_format'), default = config.get_str_value('frame_extraction', 'temp_frame_format', 'png'), choices = facefusion.choices.temp_frame_formats) + #todo: needs review - [streaming] [critical: low] new temp pixel format option, rgba enables alpha through the stream pipe + group_frame_extraction.add_argument('--temp-pixel-format', help = translator.get('help.temp_pixel_format'), default = config.get_str_value('frame_extraction', 'temp_pixel_format', 'rgb'), choices = facefusion.choices.temp_pixel_formats) #todo: needs review - [workflow] [critical: medium] keep_temp cli option replaced by workflow_mode, breaks existing configs and jobs group_frame_extraction.add_argument('--workflow-mode', help = translator.get('help.workflow_mode'), default = config.get_str_value('frame_extraction', 'workflow_mode', 'disk'), choices = facefusion.choices.workflow_modes) - job_store.register_step_keys([ 'trim_frame_start', 'trim_frame_end', 'temp_frame_format', 'workflow_mode' ]) + job_store.register_step_keys([ 'trim_frame_start', 'trim_frame_end', 'temp_frame_format', 'temp_pixel_format', 'workflow_mode' ]) return program diff --git a/facefusion/types.py b/facefusion/types.py index 71dfa001..9e5ef968 100755 --- a/facefusion/types.py +++ b/facefusion/types.py @@ -172,6 +172,8 @@ AudioFormat = Literal['flac', 'm4a', 'mp3', 'ogg', 'opus', 'wav'] ImageFormat = Literal['bmp', 'jpeg', 'png', 'tiff', 'webp'] VideoFormat = Literal['avi', 'm4v', 'mkv', 'mov', 'mp4', 'mpeg', 'mxf', 'webm', 'wmv'] TempFrameFormat = Literal['bmp', 'jpeg', 'png', 'tiff'] +#todo: needs review - [streaming] [critical: low] new temp pixel format controls the stream pipe channels +TempPixelFormat = Literal['rgb', 'rgba'] #todo: needs review - [workflow] [critical: medium] new workflow mode replaces keep_temp in state and jobs WorkflowMode = Literal['disk', 'stream'] AudioTypeSet : TypeAlias = Dict[AudioFormat, str] @@ -344,6 +346,7 @@ StateKey = Literal\ 'trim_frame_start', 'trim_frame_end', 'temp_frame_format', + 'temp_pixel_format', 'workflow_mode', 'target_frame_amount', 'output_image_quality', @@ -415,6 +418,7 @@ State = TypedDict('State', 'trim_frame_start' : int, 'trim_frame_end' : int, 'temp_frame_format' : TempFrameFormat, + 'temp_pixel_format' : TempPixelFormat, 'workflow_mode' : WorkflowMode, 'target_frame_amount' : int, 'output_image_quality' : int, diff --git a/facefusion/video_manager.py b/facefusion/video_manager.py index c7089d8d..a929a6ec 100644 --- a/facefusion/video_manager.py +++ b/facefusion/video_manager.py @@ -167,10 +167,14 @@ def create_video_writer_process(target_path : str, temp_video_fps : Fps, temp_vi temp_video_format = cast(VideoFormat, get_file_format(temp_video_path)) output_video_encoder = ffmpeg_builder.fix_video_encoder(temp_video_format, output_video_encoder) encoder_thread_count = 16 + writer_pixel_format = 'bgr24' + + if state_manager.get_item('temp_pixel_format') == 'rgba': + writer_pixel_format = 'bgra' + commands = ffmpeg_builder.chain( ffmpeg_builder.set_output_format('rawvideo'), - #todo: consider to introduce a argument to pass --output-pixel-format - ffmpeg_builder.enforce_pixel_format('bgra'), + ffmpeg_builder.enforce_pixel_format(writer_pixel_format), ffmpeg_builder.set_media_resolution(pack_video_resolution(temp_video_resolution)), ffmpeg_builder.set_input_fps(temp_video_fps), ffmpeg_builder.set_input('pipe:0'), diff --git a/facefusion/workflows/image_to_video.py b/facefusion/workflows/image_to_video.py index 2ddb0547..afcb4f25 100644 --- a/facefusion/workflows/image_to_video.py +++ b/facefusion/workflows/image_to_video.py @@ -279,7 +279,7 @@ def process_disk_frames() -> ErrorCode: return 0 -#todo: needs review - [streaming] [critical: high] middle frame resized to temp resolution before processing, returns contiguous bgra matching the writer pipe +#todo: needs review - [streaming] [critical: high] middle frame resized to temp resolution before processing, channels follow temp_pixel_format to match the writer pipe def process_stream_frame(frame_number : int, temp_video_resolution : Resolution) -> Tuple[int, VisionFrame]: target_vision_frames = select_video_frames(state_manager.get_item('target_path'), frame_number, state_manager.get_item('target_frame_amount')) target_vision_frame = get_middle(target_vision_frames) @@ -289,8 +289,10 @@ def process_stream_frame(frame_number : int, temp_video_resolution : Resolution) temp_vision_frame = cv2.resize(target_vision_frame, temp_video_resolution) temp_vision_frame = process_target_frame(frame_number, target_vision_frames, temp_vision_frame) - if temp_vision_frame.shape[2] == 3: + if state_manager.get_item('temp_pixel_format') == 'rgba': temp_vision_frame = cv2.cvtColor(temp_vision_frame, cv2.COLOR_BGR2BGRA) + if state_manager.get_item('temp_pixel_format') == 'rgb': + temp_vision_frame = temp_vision_frame[:, :, :3] return frame_number, numpy.ascontiguousarray(temp_vision_frame)