Add --workflow-mode (disk|stream), drop --keep-temp, ffmpeg video writer

Two selectable workflows share one swap loop (process_target_frame):
- disk (default): extract -> process -> merge; temp frames on disk, supports
  fps resampling, robust/inspectable.
- stream: read -> swap -> encode over pipes; no temp frames, ~2x faster wall.

Remove --keep-temp entirely (arg, config, state, ui, locale). Replace the dead
cv2.VideoWriter in video_manager with an ffmpeg rawvideo encoder held in the
pool (symmetric to the reader): get_video_writer / write_video_writer_frame /
close_video_writer, with fix_video_encoder relocated to ffmpeg_builder.

The encoder must be closed before processor post_process(), which clears the
video pool and would otherwise SIGTERM the still-open writer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
henryruhs
2026-07-22 12:23:03 +02:00
parent 0e9f1b40b9
commit d88c1f6060
13 changed files with 165 additions and 91 deletions
+1 -1
View File
@@ -51,7 +51,7 @@ voice_extractor_model =
trim_frame_start =
trim_frame_end =
temp_frame_format =
keep_temp =
workflow_mode =
[frame_distribution]
target_frame_amount =
+1 -1
View File
@@ -45,7 +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('keep_temp', args.get('keep_temp'))
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'))
apply_state_item('output_image_scale', args.get('output_image_scale'))
+2 -1
View File
@@ -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
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
face_detector_set : FaceDetectorSet =\
{
@@ -82,6 +82,7 @@ video_formats : List[VideoFormat] = list(get_args(VideoFormat))
temp_frame_formats : List[TempFrameFormat] = list(get_args(TempFrameFormat))
output_audio_encoders : List[AudioEncoder] = list(get_args(AudioEncoder))
workflow_modes : List[WorkflowMode] = list(get_args(WorkflowMode))
output_video_encoders : List[VideoEncoder] = list(get_args(VideoEncoder))
output_encoder_set : EncoderSet =\
{
+2 -39
View File
@@ -10,7 +10,7 @@ import facefusion.choices
from facefusion import ffmpeg_builder, logger, process_manager, state_manager, translator
from facefusion.filesystem import get_file_format, remove_file
from facefusion.temp_helper import get_temp_file_path, get_temp_frame_pattern
from facefusion.types import AudioBuffer, AudioEncoder, Command, EncoderSet, Fps, Resolution, UpdateProgress, VideoEncoder, VideoFormat
from facefusion.types import AudioBuffer, AudioEncoder, Command, EncoderSet, Fps, Resolution, UpdateProgress, VideoFormat
from facefusion.vision import detect_video_duration, detect_video_fps, pack_resolution, predict_video_frame_total
@@ -70,33 +70,6 @@ def open_ffmpeg(commands : List[Command]) -> subprocess.Popen[bytes]:
return subprocess.Popen(commands, stdin = subprocess.PIPE, stdout = subprocess.PIPE)
def open_video_encoder(target_path : str, temp_video_fps : Fps, temp_video_resolution : Resolution, output_video_resolution : Resolution, output_video_fps : Fps) -> subprocess.Popen[bytes]:
output_video_encoder = state_manager.get_item('output_video_encoder')
output_video_quality = state_manager.get_item('output_video_quality')
output_video_preset = state_manager.get_item('output_video_preset')
temp_video_path = get_temp_file_path(target_path)
temp_video_format = cast(VideoFormat, get_file_format(temp_video_path))
output_video_encoder = fix_video_encoder(temp_video_format, output_video_encoder)
commands = ffmpeg_builder.chain(
ffmpeg_builder.set_output_format('rawvideo'),
ffmpeg_builder.enforce_pixel_format('bgr24'),
ffmpeg_builder.set_media_resolution(pack_resolution(temp_video_resolution)),
ffmpeg_builder.set_input_fps(temp_video_fps),
ffmpeg_builder.set_input('pipe:0'),
ffmpeg_builder.set_media_resolution(pack_resolution(output_video_resolution)),
ffmpeg_builder.set_video_encoder(output_video_encoder),
ffmpeg_builder.set_video_tag(output_video_encoder, temp_video_format),
ffmpeg_builder.set_video_quality(output_video_encoder, output_video_quality),
ffmpeg_builder.set_video_preset(output_video_encoder, output_video_preset),
ffmpeg_builder.set_video_fps(output_video_fps),
ffmpeg_builder.set_pixel_format(output_video_encoder),
ffmpeg_builder.force_output(temp_video_path)
)
commands = ffmpeg_builder.run(commands)
return subprocess.Popen(commands, stdin = subprocess.PIPE, stdout = subprocess.DEVNULL)
def log_debug(process : subprocess.Popen[bytes]) -> None:
_, stderr = process.communicate()
errors = stderr.decode().split(os.linesep)
@@ -254,7 +227,7 @@ def merge_video(target_path : str, temp_video_fps : Fps, output_video_resolution
temp_video_format = cast(VideoFormat, get_file_format(temp_video_path))
temp_frame_pattern = get_temp_frame_pattern(target_path, '%08d')
output_video_encoder = fix_video_encoder(temp_video_format, output_video_encoder)
output_video_encoder = ffmpeg_builder.fix_video_encoder(temp_video_format, output_video_encoder)
commands = ffmpeg_builder.chain(
ffmpeg_builder.set_input_fps(temp_video_fps),
ffmpeg_builder.set_start_number(trim_frame_start),
@@ -317,13 +290,3 @@ def fix_audio_encoder(video_format : VideoFormat, audio_encoder : AudioEncoder)
return audio_encoder
def fix_video_encoder(video_format : VideoFormat, video_encoder : VideoEncoder) -> VideoEncoder:
if video_format in [ 'm4v', 'mpeg', 'mxf', 'wmv' ]:
return 'libx264'
if video_format in [ 'mkv', 'mp4' ] and video_encoder == 'rawvideo':
return 'libx264'
if video_format == 'mov' and video_encoder == 'libvpx-vp9':
return 'libx264'
if video_format == 'webm':
return 'libvpx-vp9'
return video_encoder
+12
View File
@@ -199,6 +199,18 @@ def copy_video_encoder() -> List[Command]:
return set_video_encoder('copy')
def fix_video_encoder(video_format : VideoFormat, video_encoder : VideoEncoder) -> VideoEncoder:
if video_format in [ 'm4v', 'mpeg', 'mxf', 'wmv' ]:
return 'libx264'
if video_format in [ 'mkv', 'mp4' ] and video_encoder == 'rawvideo':
return 'libx264'
if video_format == 'mov' and video_encoder == 'libvpx-vp9':
return 'libx264'
if video_format == 'webm':
return 'libvpx-vp9'
return video_encoder
def set_faststart(video_format : VideoFormat) -> List[Command]:
if video_format in [ 'm4v', 'mov', 'mp4' ]:
return [ '-movflags', '+faststart' ]
+1 -1
View File
@@ -136,7 +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',
'keep_temp': 'keep the temporary resources after processing',
'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',
'output_image_quality': 'specify the image quality which translates to the image compression',
'output_image_scale': 'specify the image scale based on the target image',
+2 -2
View File
@@ -169,8 +169,8 @@ 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)
group_frame_extraction.add_argument('--keep-temp', help = translator.get('help.keep_temp'), action = 'store_true', default = config.get_bool_value('frame_extraction', 'keep_temp'))
job_store.register_step_keys([ 'trim_frame_start', 'trim_frame_end', 'temp_frame_format', 'keep_temp' ])
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' ])
return program
+2 -4
View File
@@ -43,7 +43,5 @@ def create_temp_directory(file_path : str) -> bool:
def clear_temp_directory(file_path : str) -> bool:
if not state_manager.get_item('keep_temp'):
temp_directory_path = get_temp_directory_path(file_path)
return remove_directory(temp_directory_path)
return True
temp_directory_path = get_temp_directory_path(file_path)
return remove_directory(temp_directory_path)
+4 -3
View File
@@ -65,7 +65,7 @@ Locales : TypeAlias = Dict[Language, Dict[str, Any]]
LocalePoolSet : TypeAlias = Dict[str, Locales]
VideoCaptureSet : TypeAlias = Dict[str, cv2.VideoCapture]
VideoWriterSet : TypeAlias = Dict[str, cv2.VideoWriter]
VideoWriterSet : TypeAlias = Dict[str, subprocess.Popen]
CameraCaptureSet : TypeAlias = Dict[str, cv2.VideoCapture]
CameraPoolSet = TypedDict('CameraPoolSet',
{
@@ -169,6 +169,7 @@ 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']
WorkflowMode = Literal['disk', 'stream']
AudioTypeSet : TypeAlias = Dict[AudioFormat, str]
ImageTypeSet : TypeAlias = Dict[ImageFormat, str]
VideoTypeSet : TypeAlias = Dict[VideoFormat, str]
@@ -339,7 +340,7 @@ StateKey = Literal\
'trim_frame_start',
'trim_frame_end',
'temp_frame_format',
'keep_temp',
'workflow_mode',
'target_frame_amount',
'output_image_quality',
'output_image_scale',
@@ -410,7 +411,7 @@ State = TypedDict('State',
'trim_frame_start' : int,
'trim_frame_end' : int,
'temp_frame_format' : TempFrameFormat,
'keep_temp' : bool,
'workflow_mode' : WorkflowMode,
'target_frame_amount' : int,
'output_image_quality' : int,
'output_image_scale' : Scale,
+1 -1
View File
@@ -6,7 +6,7 @@ from facefusion.uis.types import JobManagerAction, JobRunnerAction, PreviewMode
job_manager_actions : List[JobManagerAction] = [ 'job-create', 'job-submit', 'job-delete', 'job-add-step', 'job-remix-step', 'job-insert-step', 'job-remove-step' ]
job_runner_actions : List[JobRunnerAction] = [ 'job-run', 'job-run-all', 'job-retry', 'job-retry-all' ]
common_options : List[str] = [ 'keep-temp' ]
common_options : List[str] = []
preview_modes : List[PreviewMode] = [ 'default', 'frame-by-frame', 'face-by-face' ]
preview_resolutions : List[str] = [ '512x512', '768x768', '1024x1024' ]
+2 -6
View File
@@ -2,7 +2,7 @@ from typing import List, Optional
import gradio
from facefusion import state_manager, translator
from facefusion import translator
from facefusion.uis import choices as uis_choices
COMMON_OPTIONS_CHECKBOX_GROUP : Optional[gradio.Checkboxgroup] = None
@@ -13,9 +13,6 @@ def render() -> None:
common_options = []
if state_manager.get_item('keep_temp'):
common_options.append('keep-temp')
COMMON_OPTIONS_CHECKBOX_GROUP = gradio.Checkboxgroup(
label = translator.get('uis.common_options_checkbox_group'),
choices = uis_choices.common_options,
@@ -28,5 +25,4 @@ def listen() -> None:
def update(common_options : List[str]) -> None:
keep_temp = 'keep-temp' in common_options
state_manager.set_item('keep_temp', keep_temp)
pass
+50 -10
View File
@@ -1,12 +1,14 @@
import subprocess
from typing import Optional, Tuple
from typing import Optional, Tuple, cast
import cv2
import numpy
from facefusion import ffmpeg_builder
from facefusion import ffmpeg_builder, state_manager
from facefusion.ffprobe import extract_video_metadata
from facefusion.types import Fps, VideoPoolSet, VideoReader, VideoReaderBuffer, VisionFrame
from facefusion.filesystem import get_file_format
from facefusion.temp_helper import get_temp_file_path
from facefusion.types import Fps, Resolution, VideoFormat, VideoPoolSet, VideoReader, VideoReaderBuffer, VisionFrame
VIDEO_POOL_SET : VideoPoolSet =\
{
@@ -119,14 +121,52 @@ def read_video_reader_window(video_reader : VideoReader, frame_start : int, fram
return frame_buffer
def get_video_writer(video_path : str) -> cv2.VideoWriter:
if video_path not in VIDEO_POOL_SET.get('writer'):
video_writer = cv2.VideoWriter()
def pack_video_resolution(resolution : Resolution) -> str:
width, height = resolution
return str(round(width / 2) * 2) + 'x' + str(round(height / 2) * 2)
if video_writer.isOpened():
VIDEO_POOL_SET['writer'][video_path] = video_writer
return VIDEO_POOL_SET.get('writer').get(video_path)
def create_video_writer_process(target_path : str, temp_video_fps : Fps, temp_video_resolution : Resolution, output_video_resolution : Resolution, output_video_fps : Fps) -> subprocess.Popen[bytes]:
output_video_encoder = state_manager.get_item('output_video_encoder')
output_video_quality = state_manager.get_item('output_video_quality')
output_video_preset = state_manager.get_item('output_video_preset')
temp_video_path = get_temp_file_path(target_path)
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)
commands = ffmpeg_builder.chain(
ffmpeg_builder.set_output_format('rawvideo'),
ffmpeg_builder.enforce_pixel_format('bgr24'),
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'),
ffmpeg_builder.set_media_resolution(pack_video_resolution(output_video_resolution)),
ffmpeg_builder.set_video_encoder(output_video_encoder),
ffmpeg_builder.set_video_tag(output_video_encoder, temp_video_format),
ffmpeg_builder.set_video_quality(output_video_encoder, output_video_quality),
ffmpeg_builder.set_video_preset(output_video_encoder, output_video_preset),
ffmpeg_builder.set_video_fps(output_video_fps),
ffmpeg_builder.set_pixel_format(output_video_encoder),
ffmpeg_builder.force_output(temp_video_path)
)
commands = ffmpeg_builder.run(commands)
return subprocess.Popen(commands, stdin = subprocess.PIPE, stdout = subprocess.DEVNULL)
def get_video_writer(target_path : str, temp_video_fps : Fps, temp_video_resolution : Resolution, output_video_resolution : Resolution, output_video_fps : Fps) -> subprocess.Popen[bytes]:
if target_path not in VIDEO_POOL_SET.get('writer'):
VIDEO_POOL_SET['writer'][target_path] = create_video_writer_process(target_path, temp_video_fps, temp_video_resolution, output_video_resolution, output_video_fps)
return VIDEO_POOL_SET.get('writer').get(target_path)
def write_video_writer_frame(video_writer : subprocess.Popen[bytes], vision_frame : VisionFrame) -> None:
video_writer.stdin.write(vision_frame.tobytes())
def close_video_writer(video_writer : subprocess.Popen[bytes]) -> bool:
video_writer.stdin.close()
video_writer.wait()
return video_writer.returncode == 0
def clear_video_pool() -> None:
@@ -134,7 +174,7 @@ def clear_video_pool() -> None:
video_capture.release()
for video_writer in VIDEO_POOL_SET.get('writer').values():
video_writer.release()
video_writer.terminate()
for video_reader in VIDEO_POOL_SET.get('reader').values():
video_reader.get('process').terminate()
+85 -22
View File
@@ -1,6 +1,6 @@
from concurrent.futures import ThreadPoolExecutor
from concurrent.futures import ThreadPoolExecutor, as_completed
from functools import partial
from typing import Tuple
from typing import List, Tuple
import cv2
import numpy
@@ -13,10 +13,10 @@ from facefusion.common_helper import get_first, get_middle
from facefusion.content_analyser import analyse_video
from facefusion.filesystem import filter_audio_paths, is_video
from facefusion.processors.core import get_processors_modules
from facefusion.temp_helper import clear_temp_directory, create_temp_directory, move_temp_file
from facefusion.temp_helper import clear_temp_directory, create_temp_directory, move_temp_file, resolve_temp_frame_set
from facefusion.time_helper import calculate_end_time
from facefusion.types import ErrorCode, Resolution, VisionFrame
from facefusion.vision import conditional_merge_vision_mask, detect_video_resolution, extract_vision_mask, pack_resolution, read_static_images, read_static_video_frame, restrict_trim_frame, restrict_video_fps, restrict_video_resolution, scale_resolution, select_video_frames
from facefusion.types import ErrorCode, FrameSet, Resolution, VisionFrame
from facefusion.vision import conditional_merge_vision_mask, detect_video_resolution, extract_vision_mask, pack_resolution, read_static_image, read_static_images, read_static_video_frame, restrict_trim_frame, restrict_video_fps, restrict_video_resolution, scale_resolution, select_video_frames, write_image
from facefusion.workflows.core import is_process_stopping
@@ -24,10 +24,21 @@ def process(start_time : float) -> ErrorCode:
tasks =\
[
setup,
process_video,
extract_frames,
process_disk_frames,
merge_frames,
restore_audio,
partial(finalize_video, start_time)
]
if state_manager.get_item('workflow_mode') == 'stream':
tasks =\
[
setup,
process_stream_frames,
restore_audio,
partial(finalize_video, start_time)
]
process_manager.start()
for task in tasks:
@@ -73,7 +84,7 @@ def extract_frames() -> ErrorCode:
return 0
def process_video() -> ErrorCode:
def process_stream_frames() -> ErrorCode:
trim_frame_start, trim_frame_end = restrict_trim_frame(state_manager.get_item('target_path'), state_manager.get_item('trim_frame_start'), state_manager.get_item('trim_frame_end'))
output_video_resolution = scale_resolution(detect_video_resolution(state_manager.get_item('target_path')), state_manager.get_item('output_video_scale'))
temp_video_resolution = restrict_video_resolution(state_manager.get_item('target_path'), output_video_resolution)
@@ -81,7 +92,7 @@ def process_video() -> ErrorCode:
frame_range = range(trim_frame_start, trim_frame_end)
if frame_range:
video_encoder = ffmpeg.open_video_encoder(state_manager.get_item('target_path'), temp_video_fps, temp_video_resolution, output_video_resolution, state_manager.get_item('output_video_fps'))
video_writer = video_manager.get_video_writer(state_manager.get_item('target_path'), temp_video_fps, temp_video_resolution, output_video_resolution, state_manager.get_item('output_video_fps'))
with tqdm(total = len(frame_range), desc = translator.get('processing'), unit = 'frame', ascii = ' =', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress:
progress.set_postfix(execution_providers = state_manager.get_item('execution_providers'))
@@ -95,24 +106,23 @@ def process_video() -> ErrorCode:
if len(futures) >= frame_look_ahead and is_process_stopping() is False:
_, vision_frame = futures.pop(0).result()
video_encoder.stdin.write(vision_frame.tobytes())
video_manager.write_video_writer_frame(video_writer, vision_frame)
progress.update()
for future in futures:
if is_process_stopping() is False:
_, vision_frame = future.result()
video_encoder.stdin.write(vision_frame.tobytes())
video_manager.write_video_writer_frame(video_writer, vision_frame)
progress.update()
close_succeeded = video_manager.close_video_writer(video_writer)
for processor_module in get_processors_modules(state_manager.get_item('processors')):
processor_module.post_process()
video_encoder.stdin.close()
video_encoder.wait()
if is_process_stopping():
return 4
if not video_encoder.returncode == 0:
if not close_succeeded:
return 1
else:
logger.error(translator.get('temp_frames_not_found'), __name__)
@@ -167,18 +177,12 @@ def restore_audio() -> ErrorCode:
return 0
def process_stream_frame(frame_number : int, temp_video_resolution : Resolution) -> Tuple[int, VisionFrame]:
def process_target_frame(frame_number : int, target_vision_frames : List[VisionFrame], temp_vision_frame : VisionFrame) -> VisionFrame:
trim_frame_start, _ = restrict_trim_frame(state_manager.get_item('target_path'), state_manager.get_item('trim_frame_start'), state_manager.get_item('trim_frame_end'))
reference_vision_frame = read_static_video_frame(state_manager.get_item('target_path'), state_manager.get_item('reference_frame_number'))
source_vision_frames = read_static_images(state_manager.get_item('source_paths'))
source_audio_path = get_first(filter_audio_paths(state_manager.get_item('source_paths')))
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)
temp_video_fps = restrict_video_fps(state_manager.get_item('target_path'), state_manager.get_item('output_video_fps'))
temp_vision_frame = target_vision_frame.copy()
if not (target_vision_frame.shape[1], target_vision_frame.shape[0]) == temp_video_resolution:
temp_vision_frame = cv2.resize(target_vision_frame, temp_video_resolution)
temp_vision_mask = extract_vision_mask(temp_vision_frame)
source_audio_frame = get_audio_frame(source_audio_path, temp_video_fps, frame_number - trim_frame_start)
@@ -201,7 +205,66 @@ def process_stream_frame(frame_number : int, temp_video_resolution : Resolution)
'temp_vision_mask': temp_vision_mask
})
temp_vision_frame = conditional_merge_vision_mask(temp_vision_frame, temp_vision_mask)
return conditional_merge_vision_mask(temp_vision_frame, temp_vision_mask)
def resolve_disk_target_frames(frame_number : int, frame_amount : int, temp_frame_set : FrameSet) -> List[VisionFrame]:
target_vision_frames = []
for current_number in range(frame_number - frame_amount, frame_number + frame_amount + 1):
target_vision_frames.append(read_static_image(temp_frame_set.get(current_number)))
return target_vision_frames
def process_disk_frame(temp_frame_path : str, frame_number : int, temp_frame_set : FrameSet) -> bool:
target_vision_frames = resolve_disk_target_frames(frame_number, state_manager.get_item('target_frame_amount'), temp_frame_set)
temp_vision_frame = read_static_image(temp_frame_path, 'rgba')
temp_vision_frame = process_target_frame(frame_number, target_vision_frames, temp_vision_frame)
return write_image(temp_frame_path, temp_vision_frame)
def process_disk_frames() -> ErrorCode:
temp_frame_set = resolve_temp_frame_set(state_manager.get_item('target_path'))
if temp_frame_set:
with tqdm(total = len(temp_frame_set), desc = translator.get('processing'), unit = 'frame', ascii = ' =', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress:
progress.set_postfix(execution_providers = state_manager.get_item('execution_providers'))
with ThreadPoolExecutor(max_workers = state_manager.get_item('execution_thread_count')) as executor:
futures = []
for frame_number, temp_frame_path in temp_frame_set.items():
futures.append(executor.submit(process_disk_frame, temp_frame_path, frame_number, temp_frame_set))
for future in as_completed(futures):
if is_process_stopping():
for __future__ in futures:
__future__.cancel()
if not future.cancelled():
future.result()
progress.update()
for processor_module in get_processors_modules(state_manager.get_item('processors')):
processor_module.post_process()
if is_process_stopping():
return 4
else:
logger.error(translator.get('temp_frames_not_found'), __name__)
return 1
return 0
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)
temp_vision_frame = target_vision_frame.copy()
if not (target_vision_frame.shape[1], target_vision_frame.shape[0]) == temp_video_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)
return frame_number, numpy.ascontiguousarray(temp_vision_frame[:, :, :3])