mirror of
https://github.com/facefusion/facefusion.git
synced 2026-07-28 12:59:03 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a394d48f34 | ||
|
|
5e8a37110d | ||
|
|
66d4cad568 | ||
|
|
7990faa38c | ||
|
|
aba04180ed | ||
|
|
3e7645e65c | ||
|
|
0fc547a975 | ||
|
|
ec4cba0947 | ||
|
|
33ed68679d | ||
|
|
585a7d75da | ||
|
|
4a8bfec297 | ||
|
|
e25efafef0 | ||
|
|
c5f892e76c | ||
|
|
d249733feb | ||
|
|
a14d688869 | ||
|
|
8c92608569 | ||
|
|
4ee27234f8 | ||
|
|
3602f69207 | ||
|
|
be77bd6572 | ||
|
|
76c1f79f29 | ||
|
|
1b151c5c3e | ||
|
|
a515d7fc3f | ||
|
|
4d92e8b5b7 | ||
|
|
74bfb8c297 | ||
|
|
a982ed9f61 | ||
|
|
fe4aedec4d | ||
|
|
a1e9a4180b | ||
|
|
3702f2ec99 | ||
|
|
fb4c11983d |
+5
-1
@@ -1,3 +1,7 @@
|
|||||||
|
[workflow]
|
||||||
|
workflow_mode =
|
||||||
|
workflow_strategy =
|
||||||
|
|
||||||
[paths]
|
[paths]
|
||||||
temp_path =
|
temp_path =
|
||||||
jobs_path =
|
jobs_path =
|
||||||
@@ -51,7 +55,7 @@ voice_extractor_model =
|
|||||||
trim_frame_start =
|
trim_frame_start =
|
||||||
trim_frame_end =
|
trim_frame_end =
|
||||||
temp_frame_format =
|
temp_frame_format =
|
||||||
keep_temp =
|
temp_pixel_format =
|
||||||
|
|
||||||
[frame_distribution]
|
[frame_distribution]
|
||||||
target_frame_amount =
|
target_frame_amount =
|
||||||
|
|||||||
+3
-1
@@ -9,6 +9,8 @@ from facefusion.vision import detect_video_fps
|
|||||||
|
|
||||||
def apply_args(args : Args, apply_state_item : ApplyStateItem) -> None:
|
def apply_args(args : Args, apply_state_item : ApplyStateItem) -> None:
|
||||||
apply_state_item('command', args.get('command'))
|
apply_state_item('command', args.get('command'))
|
||||||
|
apply_state_item('workflow_mode', args.get('workflow_mode'))
|
||||||
|
apply_state_item('workflow_strategy', args.get('workflow_strategy'))
|
||||||
apply_state_item('temp_path', args.get('temp_path'))
|
apply_state_item('temp_path', args.get('temp_path'))
|
||||||
apply_state_item('jobs_path', args.get('jobs_path'))
|
apply_state_item('jobs_path', args.get('jobs_path'))
|
||||||
apply_state_item('source_paths', args.get('source_paths'))
|
apply_state_item('source_paths', args.get('source_paths'))
|
||||||
@@ -45,7 +47,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_start', args.get('trim_frame_start'))
|
||||||
apply_state_item('trim_frame_end', args.get('trim_frame_end'))
|
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_frame_format', args.get('temp_frame_format'))
|
||||||
apply_state_item('keep_temp', args.get('keep_temp'))
|
apply_state_item('temp_pixel_format', args.get('temp_pixel_format'))
|
||||||
apply_state_item('target_frame_amount', args.get('target_frame_amount'))
|
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_quality', args.get('output_image_quality'))
|
||||||
apply_state_item('output_image_scale', args.get('output_image_scale'))
|
apply_state_item('output_image_scale', args.get('output_image_scale'))
|
||||||
|
|||||||
+3
-3
@@ -5,7 +5,7 @@ import numpy
|
|||||||
import scipy
|
import scipy
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
|
|
||||||
from facefusion.ffmpeg import read_audio_buffer
|
from facefusion import ffmpeg
|
||||||
from facefusion.filesystem import is_audio
|
from facefusion.filesystem import is_audio
|
||||||
from facefusion.types import Audio, AudioFrame, Fps, Mel, MelFilterBank, Spectrogram
|
from facefusion.types import Audio, AudioFrame, Fps, Mel, MelFilterBank, Spectrogram
|
||||||
from facefusion.voice_extractor import batch_extract_voice
|
from facefusion.voice_extractor import batch_extract_voice
|
||||||
@@ -22,7 +22,7 @@ def read_audio(audio_path : str, fps : Fps) -> Optional[List[AudioFrame]]:
|
|||||||
audio_channel_total = 2
|
audio_channel_total = 2
|
||||||
|
|
||||||
if is_audio(audio_path):
|
if is_audio(audio_path):
|
||||||
audio_buffer = read_audio_buffer(audio_path, audio_sample_rate, audio_sample_size, audio_channel_total)
|
audio_buffer = ffmpeg.read_audio_buffer(audio_path, audio_sample_rate, audio_sample_size, audio_channel_total)
|
||||||
audio = numpy.frombuffer(audio_buffer, dtype = numpy.int16).reshape(-1, 2)
|
audio = numpy.frombuffer(audio_buffer, dtype = numpy.int16).reshape(-1, 2)
|
||||||
audio = prepare_audio(audio)
|
audio = prepare_audio(audio)
|
||||||
spectrogram = create_spectrogram(audio)
|
spectrogram = create_spectrogram(audio)
|
||||||
@@ -44,7 +44,7 @@ def read_voice(audio_path : str, fps : Fps) -> Optional[List[AudioFrame]]:
|
|||||||
voice_step_size = 180 * 1024
|
voice_step_size = 180 * 1024
|
||||||
|
|
||||||
if is_audio(audio_path):
|
if is_audio(audio_path):
|
||||||
audio_buffer = read_audio_buffer(audio_path, voice_sample_rate, voice_sample_size, voice_channel_total)
|
audio_buffer = ffmpeg.read_audio_buffer(audio_path, voice_sample_rate, voice_sample_size, voice_channel_total)
|
||||||
audio = numpy.frombuffer(audio_buffer, dtype = numpy.int16).reshape(-1, 2)
|
audio = numpy.frombuffer(audio_buffer, dtype = numpy.int16).reshape(-1, 2)
|
||||||
audio = batch_extract_voice(audio, voice_chunk_size, voice_step_size)
|
audio = batch_extract_voice(audio, voice_chunk_size, voice_step_size)
|
||||||
audio = prepare_voice(audio)
|
audio = prepare_voice(audio)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import logging
|
|||||||
from typing import List, Sequence, get_args
|
from typing import List, Sequence, get_args
|
||||||
|
|
||||||
from facefusion.common_helper import create_float_range, create_int_range
|
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, TempPixelFormat, UiWorkflow, VideoEncoder, VideoFormat, VideoMemoryStrategy, VideoPreset, VideoTypeSet, VoiceExtractorModel, WorkflowMode, WorkflowStrategy
|
||||||
|
|
||||||
face_detector_set : FaceDetectorSet =\
|
face_detector_set : FaceDetectorSet =\
|
||||||
{
|
{
|
||||||
@@ -76,10 +76,14 @@ video_type_set : VideoTypeSet =\
|
|||||||
'webm': 'video/webm',
|
'webm': 'video/webm',
|
||||||
'wmv': 'video/x-ms-wmv'
|
'wmv': 'video/x-ms-wmv'
|
||||||
}
|
}
|
||||||
|
workflow_modes : List[WorkflowMode] = list(get_args(WorkflowMode))
|
||||||
|
workflow_strategies : List[WorkflowStrategy] = list(get_args(WorkflowStrategy))
|
||||||
|
|
||||||
audio_formats : List[AudioFormat] = list(get_args(AudioFormat))
|
audio_formats : List[AudioFormat] = list(get_args(AudioFormat))
|
||||||
image_formats : List[ImageFormat] = list(get_args(ImageFormat))
|
image_formats : List[ImageFormat] = list(get_args(ImageFormat))
|
||||||
video_formats : List[VideoFormat] = list(get_args(VideoFormat))
|
video_formats : List[VideoFormat] = list(get_args(VideoFormat))
|
||||||
temp_frame_formats : List[TempFrameFormat] = list(get_args(TempFrameFormat))
|
temp_frame_formats : List[TempFrameFormat] = list(get_args(TempFrameFormat))
|
||||||
|
temp_pixel_formats : List[TempPixelFormat] = list(get_args(TempPixelFormat))
|
||||||
|
|
||||||
output_audio_encoders : List[AudioEncoder] = list(get_args(AudioEncoder))
|
output_audio_encoders : List[AudioEncoder] = list(get_args(AudioEncoder))
|
||||||
output_video_encoders : List[VideoEncoder] = list(get_args(VideoEncoder))
|
output_video_encoders : List[VideoEncoder] = list(get_args(VideoEncoder))
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ from typing import Tuple
|
|||||||
import numpy
|
import numpy
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
from facefusion import inference_manager, state_manager, translator
|
from facefusion import inference_manager, state_manager, translator, video_manager
|
||||||
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
|
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
|
||||||
from facefusion.filesystem import resolve_relative_path
|
from facefusion.filesystem import resolve_relative_path
|
||||||
from facefusion.thread_helper import conditional_thread_semaphore
|
from facefusion.thread_helper import conditional_thread_semaphore
|
||||||
from facefusion.types import Detection, DownloadScope, DownloadSet, Fps, InferencePool, ModelSet, VisionFrame
|
from facefusion.types import Detection, DownloadScope, DownloadSet, Fps, InferencePool, ModelSet, VisionFrame
|
||||||
from facefusion.vision import detect_video_fps, fit_contain_frame, read_image, read_video_frame
|
from facefusion.vision import detect_video_fps, fit_contain_frame, read_image
|
||||||
|
|
||||||
STREAM_COUNTER = 0
|
STREAM_COUNTER = 0
|
||||||
|
|
||||||
@@ -158,16 +158,20 @@ def analyse_image(image_path : str) -> bool:
|
|||||||
def analyse_video(video_path : str, trim_frame_start : int, trim_frame_end : int) -> bool:
|
def analyse_video(video_path : str, trim_frame_start : int, trim_frame_end : int) -> bool:
|
||||||
video_fps = detect_video_fps(video_path)
|
video_fps = detect_video_fps(video_path)
|
||||||
frame_range = range(trim_frame_start, trim_frame_end)
|
frame_range = range(trim_frame_start, trim_frame_end)
|
||||||
|
video_reader = video_manager.get_reader(video_path, 'analyse_video')
|
||||||
rate = 0.0
|
rate = 0.0
|
||||||
total = 0
|
total = 0
|
||||||
counter = 0
|
counter = 0
|
||||||
|
|
||||||
|
if trim_frame_start > 0:
|
||||||
|
video_manager.seek_video_reader(video_reader, trim_frame_start)
|
||||||
|
|
||||||
with tqdm(total = len(frame_range), desc = translator.get('analysing'), unit = 'frame', ascii = ' =', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress:
|
with tqdm(total = len(frame_range), desc = translator.get('analysing'), unit = 'frame', ascii = ' =', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress:
|
||||||
|
|
||||||
for frame_number in frame_range:
|
for frame_number in frame_range:
|
||||||
if frame_number % int(video_fps) == 0:
|
vision_frame = video_manager.read_video_frame(video_reader)
|
||||||
vision_frame = read_video_frame(video_path, frame_number)
|
|
||||||
|
|
||||||
|
if frame_number % int(video_fps) == 0:
|
||||||
if numpy.any(vision_frame):
|
if numpy.any(vision_frame):
|
||||||
total += 1
|
total += 1
|
||||||
|
|
||||||
|
|||||||
+19
-12
@@ -9,13 +9,13 @@ from facefusion import benchmarker, cli_helper, content_analyser, hash_helper, l
|
|||||||
from facefusion.args import apply_args, collect_job_args, reduce_job_args, reduce_step_args
|
from facefusion.args import apply_args, collect_job_args, reduce_job_args, reduce_step_args
|
||||||
from facefusion.download import conditional_download_hashes, conditional_download_sources
|
from facefusion.download import conditional_download_hashes, conditional_download_sources
|
||||||
from facefusion.exit_helper import hard_exit, signal_exit
|
from facefusion.exit_helper import hard_exit, signal_exit
|
||||||
from facefusion.filesystem import get_file_extension, get_file_name, is_image, is_video, resolve_file_paths, resolve_file_pattern
|
from facefusion.filesystem import get_file_extension, get_file_name, is_video, resolve_file_paths, resolve_file_pattern
|
||||||
from facefusion.jobs import job_helper, job_manager, job_runner
|
from facefusion.jobs import job_helper, job_manager, job_runner
|
||||||
from facefusion.jobs.job_list import compose_job_list
|
from facefusion.jobs.job_list import compose_job_list
|
||||||
from facefusion.processors.core import get_processors_modules
|
from facefusion.processors.core import get_processors_modules
|
||||||
from facefusion.program import create_program
|
from facefusion.program import create_program
|
||||||
from facefusion.program_helper import validate_args
|
from facefusion.program_helper import validate_args
|
||||||
from facefusion.types import Args, ErrorCode
|
from facefusion.types import Args, ErrorCode, WorkflowMode
|
||||||
from facefusion.workflows import image_to_image, image_to_video
|
from facefusion.workflows import image_to_image, image_to_video
|
||||||
|
|
||||||
|
|
||||||
@@ -90,20 +90,17 @@ def pre_check() -> bool:
|
|||||||
logger.error(translator.get('python_not_supported').format(version = '3.10'), __name__)
|
logger.error(translator.get('python_not_supported').format(version = '3.10'), __name__)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not shutil.which('curl'):
|
for dependency in [ 'curl', 'ffmpeg', 'ffprobe' ]:
|
||||||
logger.error(translator.get('curl_not_installed'), __name__)
|
if not shutil.which(dependency):
|
||||||
return False
|
logger.error(translator.get('dependency_not_installed').format(dependency = dependency), __name__)
|
||||||
|
return False
|
||||||
if not shutil.which('ffmpeg'):
|
|
||||||
logger.error(translator.get('ffmpeg_not_installed'), __name__)
|
|
||||||
return False
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def common_pre_check() -> bool:
|
def common_pre_check() -> bool:
|
||||||
content_analyser_content = inspect.getsource(content_analyser).encode()
|
content_analyser_content = inspect.getsource(content_analyser).encode()
|
||||||
|
|
||||||
return hash_helper.create_hash(content_analyser_content) == '975d67d6'
|
return hash_helper.create_hash(content_analyser_content) == '0922c180'
|
||||||
|
|
||||||
|
|
||||||
def processors_pre_check() -> bool:
|
def processors_pre_check() -> bool:
|
||||||
@@ -315,15 +312,25 @@ def process_step(job_id : str, step_index : int, step_args : Args) -> bool:
|
|||||||
def conditional_process() -> ErrorCode:
|
def conditional_process() -> ErrorCode:
|
||||||
start_time = time()
|
start_time = time()
|
||||||
|
|
||||||
|
if state_manager.get_item('workflow_mode') == 'auto':
|
||||||
|
state_manager.set_item('workflow_mode', detect_workflow_mode())
|
||||||
|
|
||||||
for processor_module in get_processors_modules(state_manager.get_item('processors')):
|
for processor_module in get_processors_modules(state_manager.get_item('processors')):
|
||||||
if not processor_module.pre_process('output'):
|
if not processor_module.pre_process('output'):
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
if is_image(state_manager.get_item('target_path')):
|
if state_manager.get_item('workflow_mode') == 'image-to-image':
|
||||||
return image_to_image.process(start_time)
|
return image_to_image.process(start_time)
|
||||||
if is_video(state_manager.get_item('target_path')):
|
if state_manager.get_item('workflow_mode') == 'image-to-video':
|
||||||
return image_to_video.process(start_time)
|
return image_to_video.process(start_time)
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def detect_workflow_mode() -> WorkflowMode:
|
||||||
|
if is_video(state_manager.get_item('target_path')):
|
||||||
|
return 'image-to-video'
|
||||||
|
|
||||||
|
return 'image-to-image'
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ def conditional_download(download_directory_path : str, urls : List[str]) -> Non
|
|||||||
curl_builder.set_timeout(5),
|
curl_builder.set_timeout(5),
|
||||||
curl_builder.set_retry(5)
|
curl_builder.set_retry(5)
|
||||||
)
|
)
|
||||||
|
|
||||||
open_curl(commands)
|
open_curl(commands)
|
||||||
current_size = initial_size
|
current_size = initial_size
|
||||||
progress.set_postfix(download_providers = state_manager.get_item('download_providers'), file_name = download_file_name)
|
progress.set_postfix(download_providers = state_manager.get_item('download_providers'), file_name = download_file_name)
|
||||||
@@ -48,6 +49,7 @@ def get_static_download_size(url : str) -> int:
|
|||||||
curl_builder.ping(url),
|
curl_builder.ping(url),
|
||||||
curl_builder.set_timeout(5)
|
curl_builder.set_timeout(5)
|
||||||
)
|
)
|
||||||
|
|
||||||
process = open_curl(commands)
|
process = open_curl(commands)
|
||||||
lines = reversed(process.stdout.readlines())
|
lines = reversed(process.stdout.readlines())
|
||||||
|
|
||||||
@@ -66,6 +68,7 @@ def ping_static_url(url : str) -> bool:
|
|||||||
curl_builder.ping(url),
|
curl_builder.ping(url),
|
||||||
curl_builder.set_timeout(5)
|
curl_builder.set_timeout(5)
|
||||||
)
|
)
|
||||||
|
|
||||||
process = open_curl(commands)
|
process = open_curl(commands)
|
||||||
process.communicate()
|
process.communicate()
|
||||||
return process.returncode == 0
|
return process.returncode == 0
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ FACE_STORE : FaceStore = {}
|
|||||||
|
|
||||||
def get_faces(vision_frame : VisionFrame) -> Optional[List[Face]]:
|
def get_faces(vision_frame : VisionFrame) -> Optional[List[Face]]:
|
||||||
if numpy.any(vision_frame):
|
if numpy.any(vision_frame):
|
||||||
vision_hash = create_hash(vision_frame.tobytes())
|
vision_hash = create_hash(vision_frame.data)
|
||||||
|
|
||||||
if FACE_STORE.get(vision_hash):
|
if FACE_STORE.get(vision_hash):
|
||||||
return FACE_STORE.get(vision_hash).get('faces')
|
return FACE_STORE.get(vision_hash).get('faces')
|
||||||
@@ -21,7 +21,7 @@ def get_faces(vision_frame : VisionFrame) -> Optional[List[Face]]:
|
|||||||
|
|
||||||
def set_faces(vision_frame : VisionFrame, faces : List[Face]) -> None:
|
def set_faces(vision_frame : VisionFrame, faces : List[Face]) -> None:
|
||||||
if numpy.any(vision_frame):
|
if numpy.any(vision_frame):
|
||||||
vision_hash = create_hash(vision_frame.tobytes())
|
vision_hash = create_hash(vision_frame.data)
|
||||||
FACE_STORE.setdefault(vision_hash,
|
FACE_STORE.setdefault(vision_hash,
|
||||||
{
|
{
|
||||||
'lock': threading.Lock()
|
'lock': threading.Lock()
|
||||||
@@ -30,7 +30,7 @@ def set_faces(vision_frame : VisionFrame, faces : List[Face]) -> None:
|
|||||||
|
|
||||||
def resolve_lock(vision_frame : VisionFrame) -> threading.Lock:
|
def resolve_lock(vision_frame : VisionFrame) -> threading.Lock:
|
||||||
if numpy.any(vision_frame):
|
if numpy.any(vision_frame):
|
||||||
vision_hash = create_hash(vision_frame.tobytes())
|
vision_hash = create_hash(vision_frame.data)
|
||||||
return FACE_STORE.setdefault(vision_hash,
|
return FACE_STORE.setdefault(vision_hash,
|
||||||
{
|
{
|
||||||
'lock': threading.Lock()
|
'lock': threading.Lock()
|
||||||
|
|||||||
+75
-17
@@ -7,11 +7,10 @@ from typing import List, Optional, cast
|
|||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
import facefusion.choices
|
import facefusion.choices
|
||||||
from facefusion import ffmpeg_builder, logger, process_manager, state_manager, translator
|
from facefusion import ffmpeg_builder, ffprobe, logger, process_manager, state_manager, translator, vision
|
||||||
from facefusion.filesystem import get_file_format, remove_file
|
from facefusion.filesystem import get_file_format, remove_file
|
||||||
from facefusion.temp_helper import get_temp_file_path, get_temp_frame_pattern
|
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, VideoEncoder, VideoFormat, VideoReaderMetadata
|
||||||
from facefusion.vision import detect_video_duration, detect_video_fps, pack_resolution, predict_video_frame_total
|
|
||||||
|
|
||||||
|
|
||||||
def run_ffmpeg_with_progress(commands : List[Command], update_progress : UpdateProgress) -> subprocess.Popen[bytes]:
|
def run_ffmpeg_with_progress(commands : List[Command], update_progress : UpdateProgress) -> subprocess.Popen[bytes]:
|
||||||
@@ -70,6 +69,51 @@ def open_ffmpeg(commands : List[Command]) -> subprocess.Popen[bytes]:
|
|||||||
return subprocess.Popen(commands, stdin = subprocess.PIPE, stdout = subprocess.PIPE)
|
return subprocess.Popen(commands, stdin = subprocess.PIPE, stdout = subprocess.PIPE)
|
||||||
|
|
||||||
|
|
||||||
|
def create_video_reader(video_path : str, frame_number : int, video_metadata : VideoReaderMetadata) -> subprocess.Popen[bytes]:
|
||||||
|
commands = ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.seek_to(frame_number / video_metadata.get('fps')),
|
||||||
|
ffmpeg_builder.set_input(video_path),
|
||||||
|
ffmpeg_builder.restrict_color_transfer(video_metadata.get('color_transfer')),
|
||||||
|
ffmpeg_builder.prevent_frame_drop(),
|
||||||
|
ffmpeg_builder.enforce_pixel_format('bgr24'),
|
||||||
|
ffmpeg_builder.set_output_format('rawvideo'),
|
||||||
|
ffmpeg_builder.cast_stream()
|
||||||
|
)
|
||||||
|
|
||||||
|
return open_ffmpeg(commands)
|
||||||
|
|
||||||
|
|
||||||
|
def create_video_writer(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(state_manager.get_item('temp_pixel_format')),
|
||||||
|
ffmpeg_builder.set_media_resolution(vision.pack_resolution(temp_video_resolution)),
|
||||||
|
ffmpeg_builder.set_input_fps(temp_video_fps),
|
||||||
|
ffmpeg_builder.set_input('pipe:0'),
|
||||||
|
ffmpeg_builder.set_media_resolution(vision.pack_resolution(output_video_resolution)),
|
||||||
|
ffmpeg_builder.set_video_encoder(output_video_encoder),
|
||||||
|
ffmpeg_builder.set_thread_count(16),
|
||||||
|
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.concat(
|
||||||
|
ffmpeg_builder.set_video_fps(output_video_fps),
|
||||||
|
ffmpeg_builder.convert_color_space('bt709')
|
||||||
|
),
|
||||||
|
ffmpeg_builder.set_pixel_format(output_video_encoder),
|
||||||
|
ffmpeg_builder.force_output(temp_video_path)
|
||||||
|
)
|
||||||
|
|
||||||
|
return open_ffmpeg(commands)
|
||||||
|
|
||||||
|
|
||||||
def log_debug(process : subprocess.Popen[bytes]) -> None:
|
def log_debug(process : subprocess.Popen[bytes]) -> None:
|
||||||
_, stderr = process.communicate()
|
_, stderr = process.communicate()
|
||||||
errors = stderr.decode().split(os.linesep)
|
errors = stderr.decode().split(os.linesep)
|
||||||
@@ -108,14 +152,19 @@ def get_available_encoder_set() -> EncoderSet:
|
|||||||
|
|
||||||
|
|
||||||
def extract_frames(target_path : str, temp_video_resolution : Resolution, temp_video_fps : Fps, trim_frame_start : int, trim_frame_end : int) -> bool:
|
def extract_frames(target_path : str, temp_video_resolution : Resolution, temp_video_fps : Fps, trim_frame_start : int, trim_frame_end : int) -> bool:
|
||||||
extract_frame_total = predict_video_frame_total(target_path, temp_video_fps, trim_frame_start, trim_frame_end)
|
color_transfer = ffprobe.extract_static_video_metadata(target_path).get('color_transfer')
|
||||||
|
extract_frame_total = vision.predict_video_frame_total(target_path, temp_video_fps, trim_frame_start, trim_frame_end)
|
||||||
temp_frame_pattern = get_temp_frame_pattern(target_path, '%08d')
|
temp_frame_pattern = get_temp_frame_pattern(target_path, '%08d')
|
||||||
|
|
||||||
commands = ffmpeg_builder.chain(
|
commands = ffmpeg_builder.chain(
|
||||||
ffmpeg_builder.set_input(target_path),
|
ffmpeg_builder.set_input(target_path),
|
||||||
ffmpeg_builder.set_media_resolution(pack_resolution(temp_video_resolution)),
|
ffmpeg_builder.set_media_resolution(vision.pack_resolution(temp_video_resolution)),
|
||||||
ffmpeg_builder.set_frame_quality(0),
|
ffmpeg_builder.set_frame_quality(0),
|
||||||
ffmpeg_builder.enforce_pixel_format('rgb24'),
|
ffmpeg_builder.enforce_pixel_format('rgb24'),
|
||||||
ffmpeg_builder.select_frame_range(trim_frame_start, trim_frame_end, temp_video_fps),
|
ffmpeg_builder.concat(
|
||||||
|
ffmpeg_builder.select_frame_range(trim_frame_start, trim_frame_end, temp_video_fps),
|
||||||
|
ffmpeg_builder.restrict_color_transfer(color_transfer)
|
||||||
|
),
|
||||||
ffmpeg_builder.prevent_frame_drop(),
|
ffmpeg_builder.prevent_frame_drop(),
|
||||||
ffmpeg_builder.set_start_number(trim_frame_start),
|
ffmpeg_builder.set_start_number(trim_frame_start),
|
||||||
ffmpeg_builder.set_output(temp_frame_pattern)
|
ffmpeg_builder.set_output(temp_frame_pattern)
|
||||||
@@ -128,24 +177,28 @@ def extract_frames(target_path : str, temp_video_resolution : Resolution, temp_v
|
|||||||
|
|
||||||
def copy_image(target_path : str, temp_image_resolution : Resolution) -> bool:
|
def copy_image(target_path : str, temp_image_resolution : Resolution) -> bool:
|
||||||
temp_image_path = get_temp_file_path(target_path)
|
temp_image_path = get_temp_file_path(target_path)
|
||||||
|
|
||||||
commands = ffmpeg_builder.chain(
|
commands = ffmpeg_builder.chain(
|
||||||
ffmpeg_builder.set_input(target_path),
|
ffmpeg_builder.set_input(target_path),
|
||||||
ffmpeg_builder.set_media_resolution(pack_resolution(temp_image_resolution)),
|
ffmpeg_builder.set_media_resolution(vision.pack_resolution(temp_image_resolution)),
|
||||||
ffmpeg_builder.set_image_quality(target_path, 100),
|
ffmpeg_builder.set_image_quality(target_path, 100),
|
||||||
ffmpeg_builder.force_output(temp_image_path)
|
ffmpeg_builder.force_output(temp_image_path)
|
||||||
)
|
)
|
||||||
|
|
||||||
return run_ffmpeg(commands).returncode == 0
|
return run_ffmpeg(commands).returncode == 0
|
||||||
|
|
||||||
|
|
||||||
def finalize_image(target_path : str, output_path : str, output_image_resolution : Resolution) -> bool:
|
def finalize_image(target_path : str, output_path : str, output_image_resolution : Resolution) -> bool:
|
||||||
output_image_quality = state_manager.get_item('output_image_quality')
|
output_image_quality = state_manager.get_item('output_image_quality')
|
||||||
temp_image_path = get_temp_file_path(target_path)
|
temp_image_path = get_temp_file_path(target_path)
|
||||||
|
|
||||||
commands = ffmpeg_builder.chain(
|
commands = ffmpeg_builder.chain(
|
||||||
ffmpeg_builder.set_input(temp_image_path),
|
ffmpeg_builder.set_input(temp_image_path),
|
||||||
ffmpeg_builder.set_media_resolution(pack_resolution(output_image_resolution)),
|
ffmpeg_builder.set_media_resolution(vision.pack_resolution(output_image_resolution)),
|
||||||
ffmpeg_builder.set_image_quality(target_path, output_image_quality),
|
ffmpeg_builder.set_image_quality(target_path, output_image_quality),
|
||||||
ffmpeg_builder.force_output(output_path)
|
ffmpeg_builder.force_output(output_path)
|
||||||
)
|
)
|
||||||
|
|
||||||
return run_ffmpeg(commands).returncode == 0
|
return run_ffmpeg(commands).returncode == 0
|
||||||
|
|
||||||
|
|
||||||
@@ -170,13 +223,13 @@ def restore_audio(target_path : str, output_path : str, trim_frame_start : int,
|
|||||||
output_audio_encoder = state_manager.get_item('output_audio_encoder')
|
output_audio_encoder = state_manager.get_item('output_audio_encoder')
|
||||||
output_audio_quality = state_manager.get_item('output_audio_quality')
|
output_audio_quality = state_manager.get_item('output_audio_quality')
|
||||||
output_audio_volume = state_manager.get_item('output_audio_volume')
|
output_audio_volume = state_manager.get_item('output_audio_volume')
|
||||||
target_video_fps = detect_video_fps(target_path)
|
target_video_fps = vision.detect_video_fps(target_path)
|
||||||
temp_video_path = get_temp_file_path(target_path)
|
temp_video_path = get_temp_file_path(target_path)
|
||||||
temp_video_format = cast(VideoFormat, get_file_format(temp_video_path))
|
temp_video_format = cast(VideoFormat, get_file_format(temp_video_path))
|
||||||
temp_video_duration = detect_video_duration(temp_video_path)
|
temp_video_duration = vision.detect_video_duration(temp_video_path)
|
||||||
output_video_format = cast(VideoFormat, get_file_format(output_path))
|
output_video_format = cast(VideoFormat, get_file_format(output_path))
|
||||||
|
|
||||||
output_audio_encoder = fix_audio_encoder(temp_video_format, output_audio_encoder)
|
output_audio_encoder = fix_audio_encoder(temp_video_format, output_audio_encoder)
|
||||||
|
|
||||||
commands = ffmpeg_builder.chain(
|
commands = ffmpeg_builder.chain(
|
||||||
ffmpeg_builder.set_input(temp_video_path),
|
ffmpeg_builder.set_input(temp_video_path),
|
||||||
ffmpeg_builder.select_media_range(trim_frame_start, trim_frame_end, target_video_fps),
|
ffmpeg_builder.select_media_range(trim_frame_start, trim_frame_end, target_video_fps),
|
||||||
@@ -191,6 +244,7 @@ def restore_audio(target_path : str, output_path : str, trim_frame_start : int,
|
|||||||
ffmpeg_builder.set_faststart(output_video_format),
|
ffmpeg_builder.set_faststart(output_video_format),
|
||||||
ffmpeg_builder.force_output(output_path)
|
ffmpeg_builder.force_output(output_path)
|
||||||
)
|
)
|
||||||
|
|
||||||
return run_ffmpeg(commands).returncode == 0
|
return run_ffmpeg(commands).returncode == 0
|
||||||
|
|
||||||
|
|
||||||
@@ -200,10 +254,10 @@ def replace_audio(target_path : str, audio_path : str, output_path : str) -> boo
|
|||||||
output_audio_volume = state_manager.get_item('output_audio_volume')
|
output_audio_volume = state_manager.get_item('output_audio_volume')
|
||||||
temp_video_path = get_temp_file_path(target_path)
|
temp_video_path = get_temp_file_path(target_path)
|
||||||
temp_video_format = cast(VideoFormat, get_file_format(temp_video_path))
|
temp_video_format = cast(VideoFormat, get_file_format(temp_video_path))
|
||||||
temp_video_duration = detect_video_duration(temp_video_path)
|
temp_video_duration = vision.detect_video_duration(temp_video_path)
|
||||||
output_video_format = cast(VideoFormat, get_file_format(output_path))
|
output_video_format = cast(VideoFormat, get_file_format(output_path))
|
||||||
|
|
||||||
output_audio_encoder = fix_audio_encoder(temp_video_format, output_audio_encoder)
|
output_audio_encoder = fix_audio_encoder(temp_video_format, output_audio_encoder)
|
||||||
|
|
||||||
commands = ffmpeg_builder.chain(
|
commands = ffmpeg_builder.chain(
|
||||||
ffmpeg_builder.set_input(temp_video_path),
|
ffmpeg_builder.set_input(temp_video_path),
|
||||||
ffmpeg_builder.set_input(audio_path),
|
ffmpeg_builder.set_input(audio_path),
|
||||||
@@ -215,6 +269,7 @@ def replace_audio(target_path : str, audio_path : str, output_path : str) -> boo
|
|||||||
ffmpeg_builder.set_faststart(output_video_format),
|
ffmpeg_builder.set_faststart(output_video_format),
|
||||||
ffmpeg_builder.force_output(output_path)
|
ffmpeg_builder.force_output(output_path)
|
||||||
)
|
)
|
||||||
|
|
||||||
return run_ffmpeg(commands).returncode == 0
|
return run_ffmpeg(commands).returncode == 0
|
||||||
|
|
||||||
|
|
||||||
@@ -222,24 +277,25 @@ def merge_video(target_path : str, temp_video_fps : Fps, output_video_resolution
|
|||||||
output_video_encoder = state_manager.get_item('output_video_encoder')
|
output_video_encoder = state_manager.get_item('output_video_encoder')
|
||||||
output_video_quality = state_manager.get_item('output_video_quality')
|
output_video_quality = state_manager.get_item('output_video_quality')
|
||||||
output_video_preset = state_manager.get_item('output_video_preset')
|
output_video_preset = state_manager.get_item('output_video_preset')
|
||||||
merge_frame_total = predict_video_frame_total(target_path, output_video_fps, trim_frame_start, trim_frame_end)
|
merge_frame_total = vision.predict_video_frame_total(target_path, output_video_fps, trim_frame_start, trim_frame_end)
|
||||||
temp_video_path = get_temp_file_path(target_path)
|
temp_video_path = get_temp_file_path(target_path)
|
||||||
temp_video_format = cast(VideoFormat, get_file_format(temp_video_path))
|
temp_video_format = cast(VideoFormat, get_file_format(temp_video_path))
|
||||||
temp_frame_pattern = get_temp_frame_pattern(target_path, '%08d')
|
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 = fix_video_encoder(temp_video_format, output_video_encoder)
|
||||||
|
|
||||||
commands = ffmpeg_builder.chain(
|
commands = ffmpeg_builder.chain(
|
||||||
ffmpeg_builder.set_input_fps(temp_video_fps),
|
ffmpeg_builder.set_input_fps(temp_video_fps),
|
||||||
ffmpeg_builder.set_start_number(trim_frame_start),
|
ffmpeg_builder.set_start_number(trim_frame_start),
|
||||||
ffmpeg_builder.set_input(temp_frame_pattern),
|
ffmpeg_builder.set_input(temp_frame_pattern),
|
||||||
ffmpeg_builder.set_media_resolution(pack_resolution(output_video_resolution)),
|
ffmpeg_builder.set_media_resolution(vision.pack_resolution(output_video_resolution)),
|
||||||
ffmpeg_builder.set_video_encoder(output_video_encoder),
|
ffmpeg_builder.set_video_encoder(output_video_encoder),
|
||||||
ffmpeg_builder.set_video_tag(output_video_encoder, temp_video_format),
|
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_quality(output_video_encoder, output_video_quality),
|
||||||
ffmpeg_builder.set_video_preset(output_video_encoder, output_video_preset),
|
ffmpeg_builder.set_video_preset(output_video_encoder, output_video_preset),
|
||||||
ffmpeg_builder.concat(
|
ffmpeg_builder.concat(
|
||||||
ffmpeg_builder.set_video_fps(output_video_fps),
|
ffmpeg_builder.set_video_fps(output_video_fps),
|
||||||
ffmpeg_builder.keep_video_alpha(output_video_encoder)
|
ffmpeg_builder.keep_video_alpha(output_video_encoder),
|
||||||
|
ffmpeg_builder.convert_color_space('bt709')
|
||||||
),
|
),
|
||||||
ffmpeg_builder.set_pixel_format(output_video_encoder),
|
ffmpeg_builder.set_pixel_format(output_video_encoder),
|
||||||
ffmpeg_builder.force_output(temp_video_path)
|
ffmpeg_builder.force_output(temp_video_path)
|
||||||
@@ -262,6 +318,7 @@ def concat_video(output_path : str, temp_output_paths : List[str]) -> bool:
|
|||||||
|
|
||||||
output_path = os.path.abspath(output_path)
|
output_path = os.path.abspath(output_path)
|
||||||
output_video_format = cast(VideoFormat, get_file_format(output_path))
|
output_video_format = cast(VideoFormat, get_file_format(output_path))
|
||||||
|
|
||||||
commands = ffmpeg_builder.chain(
|
commands = ffmpeg_builder.chain(
|
||||||
ffmpeg_builder.unsafe_concat(),
|
ffmpeg_builder.unsafe_concat(),
|
||||||
ffmpeg_builder.set_input(concat_video_file.name),
|
ffmpeg_builder.set_input(concat_video_file.name),
|
||||||
@@ -270,6 +327,7 @@ def concat_video(output_path : str, temp_output_paths : List[str]) -> bool:
|
|||||||
ffmpeg_builder.set_faststart(output_video_format),
|
ffmpeg_builder.set_faststart(output_video_format),
|
||||||
ffmpeg_builder.force_output(output_path)
|
ffmpeg_builder.force_output(output_path)
|
||||||
)
|
)
|
||||||
|
|
||||||
process = run_ffmpeg(commands)
|
process = run_ffmpeg(commands)
|
||||||
process.communicate()
|
process.communicate()
|
||||||
remove_file(concat_video_path)
|
remove_file(concat_video_path)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from typing import List, Optional
|
|||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
from facefusion.filesystem import get_file_format
|
from facefusion.filesystem import get_file_format
|
||||||
from facefusion.types import AudioEncoder, Command, CommandSet, Duration, Fps, StreamMode, VideoEncoder, VideoFormat, VideoPreset
|
from facefusion.types import AudioEncoder, ColorSpace, ColorTransfer, Command, CommandSet, Duration, Fps, StreamMode, VideoEncoder, VideoFormat, VideoPreset
|
||||||
|
|
||||||
|
|
||||||
def run(commands : List[Command]) -> List[Command]:
|
def run(commands : List[Command]) -> List[Command]:
|
||||||
@@ -83,6 +83,14 @@ def unsafe_concat() -> List[Command]:
|
|||||||
return [ '-f', 'concat', '-safe', '0' ]
|
return [ '-f', 'concat', '-safe', '0' ]
|
||||||
|
|
||||||
|
|
||||||
|
def seek_to(time : float) -> List[Command]:
|
||||||
|
return [ '-ss', str(time)]
|
||||||
|
|
||||||
|
|
||||||
|
def set_output_format(output_format : str) -> List[Command]:
|
||||||
|
return [ '-f', output_format ]
|
||||||
|
|
||||||
|
|
||||||
def enforce_pixel_format(pixel_format : str) -> List[Command]:
|
def enforce_pixel_format(pixel_format : str) -> List[Command]:
|
||||||
return [ '-pix_fmt', pixel_format ]
|
return [ '-pix_fmt', pixel_format ]
|
||||||
|
|
||||||
@@ -113,6 +121,16 @@ def prevent_frame_drop() -> List[Command]:
|
|||||||
return [ '-vsync', '0' ]
|
return [ '-vsync', '0' ]
|
||||||
|
|
||||||
|
|
||||||
|
def restrict_color_transfer(color_transfer : ColorTransfer) -> List[Command]:
|
||||||
|
if color_transfer in [ 'smpte2084', 'arib-std-b67' ]:
|
||||||
|
return [ '-vf', 'scale=out_primaries=bt709:out_transfer=bt709:intent=perceptual' ]
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def convert_color_space(color_space : ColorSpace) -> List[Command]:
|
||||||
|
return [ '-vf', 'scale=out_color_matrix=' + color_space + ':out_range=tv,setparams=colorspace=' + color_space + ':color_primaries=' + color_space + ':color_trc=' + color_space ]
|
||||||
|
|
||||||
|
|
||||||
def select_media_range(frame_start : int, frame_end : int, media_fps : Fps) -> List[Command]:
|
def select_media_range(frame_start : int, frame_end : int, media_fps : Fps) -> List[Command]:
|
||||||
commands = []
|
commands = []
|
||||||
|
|
||||||
@@ -183,6 +201,10 @@ def set_audio_volume(audio_volume : int) -> List[Command]:
|
|||||||
return [ '-filter:a', 'volume=' + str(audio_volume / 100) ]
|
return [ '-filter:a', 'volume=' + str(audio_volume / 100) ]
|
||||||
|
|
||||||
|
|
||||||
|
def set_thread_count(thread_count : int) -> List[Command]:
|
||||||
|
return [ '-threads', str(thread_count) ]
|
||||||
|
|
||||||
|
|
||||||
def set_video_encoder(video_encoder : str) -> List[Command]:
|
def set_video_encoder(video_encoder : str) -> List[Command]:
|
||||||
return [ '-c:v', video_encoder ]
|
return [ '-c:v', video_encoder ]
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
import subprocess
|
||||||
|
from functools import lru_cache
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
|
from facefusion import ffprobe_builder
|
||||||
|
from facefusion.types import AudioMetadata, Buffer, Command, Fps, VideoMetadata
|
||||||
|
|
||||||
|
|
||||||
|
def run_ffprobe(commands : List[Command]) -> subprocess.Popen[Buffer]:
|
||||||
|
commands = ffprobe_builder.run(commands)
|
||||||
|
return subprocess.Popen(commands, stderr = subprocess.PIPE, stdout = subprocess.PIPE)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_entries(output : Buffer) -> Dict[str, str]:
|
||||||
|
media_entries = {}
|
||||||
|
|
||||||
|
if output:
|
||||||
|
lines = output.decode().strip().splitlines()
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
if '=' in line:
|
||||||
|
key, value = line.split('=', 1)
|
||||||
|
media_entries[key] = value
|
||||||
|
|
||||||
|
return media_entries
|
||||||
|
|
||||||
|
|
||||||
|
def probe_audio_entries(audio_path : str, entries : List[str]) -> Dict[str, str]:
|
||||||
|
commands = ffprobe_builder.chain(
|
||||||
|
ffprobe_builder.select_stream('a:0'),
|
||||||
|
ffprobe_builder.show_stream_entries(entries),
|
||||||
|
ffprobe_builder.format_to_key_value(),
|
||||||
|
ffprobe_builder.set_input(audio_path)
|
||||||
|
)
|
||||||
|
|
||||||
|
output, _ = run_ffprobe(commands).communicate()
|
||||||
|
|
||||||
|
return parse_entries(output)
|
||||||
|
|
||||||
|
|
||||||
|
def probe_video_entries(video_path : str, entries : List[str]) -> Dict[str, str]:
|
||||||
|
commands = ffprobe_builder.chain(
|
||||||
|
ffprobe_builder.select_stream('v:0'),
|
||||||
|
ffprobe_builder.show_stream_entries(entries),
|
||||||
|
ffprobe_builder.format_to_key_value(),
|
||||||
|
ffprobe_builder.set_input(video_path)
|
||||||
|
)
|
||||||
|
|
||||||
|
output, _ = run_ffprobe(commands).communicate()
|
||||||
|
|
||||||
|
return parse_entries(output)
|
||||||
|
|
||||||
|
|
||||||
|
def probe_format_entries(media_path : str, entries : List[str]) -> Dict[str, str]:
|
||||||
|
commands = ffprobe_builder.chain(
|
||||||
|
ffprobe_builder.show_format_entries(entries),
|
||||||
|
ffprobe_builder.format_to_key_value(),
|
||||||
|
ffprobe_builder.set_input(media_path)
|
||||||
|
)
|
||||||
|
|
||||||
|
output, _ = run_ffprobe(commands).communicate()
|
||||||
|
|
||||||
|
return parse_entries(output)
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize = 128)
|
||||||
|
def extract_static_audio_metadata(audio_path : str) -> AudioMetadata:
|
||||||
|
return extract_audio_metadata(audio_path)
|
||||||
|
|
||||||
|
|
||||||
|
def extract_audio_metadata(audio_path : str) -> AudioMetadata:
|
||||||
|
audio_entries = probe_audio_entries(audio_path, [ 'sample_rate', 'channels' ])
|
||||||
|
format_entries = probe_format_entries(audio_path, [ 'duration', 'bit_rate' ])
|
||||||
|
|
||||||
|
duration = float(format_entries.get('duration'))
|
||||||
|
sample_rate = int(audio_entries.get('sample_rate'))
|
||||||
|
frame_total = round(duration * sample_rate)
|
||||||
|
channel_total = int(audio_entries.get('channels'))
|
||||||
|
bit_rate = int(format_entries.get('bit_rate'))
|
||||||
|
|
||||||
|
audio_metadata : AudioMetadata =\
|
||||||
|
{
|
||||||
|
'duration' : duration,
|
||||||
|
'frame_total' : frame_total,
|
||||||
|
'channel_total' : channel_total,
|
||||||
|
'sample_rate' : sample_rate,
|
||||||
|
'bit_rate' : bit_rate
|
||||||
|
}
|
||||||
|
|
||||||
|
return audio_metadata
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize = 128)
|
||||||
|
def extract_static_video_metadata(video_path : str) -> VideoMetadata:
|
||||||
|
return extract_video_metadata(video_path)
|
||||||
|
|
||||||
|
|
||||||
|
def extract_video_metadata(video_path : str) -> VideoMetadata:
|
||||||
|
video_entries = probe_video_entries(video_path, [ 'width', 'height', 'r_frame_rate', 'color_transfer' ])
|
||||||
|
format_entries = probe_format_entries(video_path, [ 'duration', 'bit_rate' ])
|
||||||
|
|
||||||
|
duration = float(format_entries.get('duration'))
|
||||||
|
fps = extract_video_fps(video_entries.get('r_frame_rate'))
|
||||||
|
frame_total = round(duration * fps)
|
||||||
|
width = int(video_entries.get('width'))
|
||||||
|
height = int(video_entries.get('height'))
|
||||||
|
bit_rate = int(format_entries.get('bit_rate'))
|
||||||
|
color_transfer = video_entries.get('color_transfer', 'unknown')
|
||||||
|
|
||||||
|
video_metadata : VideoMetadata =\
|
||||||
|
{
|
||||||
|
'duration' : duration,
|
||||||
|
'frame_total' : frame_total,
|
||||||
|
'fps' : fps,
|
||||||
|
'resolution' : (width, height),
|
||||||
|
'bit_rate' : bit_rate,
|
||||||
|
'color_transfer' : color_transfer
|
||||||
|
}
|
||||||
|
|
||||||
|
return video_metadata
|
||||||
|
|
||||||
|
|
||||||
|
def extract_video_fps(frame_rate : str) -> Fps:
|
||||||
|
if frame_rate and '/' in frame_rate:
|
||||||
|
numerator, denominator = frame_rate.split('/')
|
||||||
|
|
||||||
|
if int(numerator) and int(denominator):
|
||||||
|
return int(numerator) / int(denominator)
|
||||||
|
|
||||||
|
return 0.0
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import itertools
|
||||||
|
import shutil
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from facefusion.types import Command
|
||||||
|
|
||||||
|
|
||||||
|
def run(commands : List[Command]) -> List[Command]:
|
||||||
|
return [ shutil.which('ffprobe'), '-loglevel', 'error' ] + commands
|
||||||
|
|
||||||
|
|
||||||
|
def chain(*commands : List[Command]) -> List[Command]:
|
||||||
|
return list(itertools.chain(*commands))
|
||||||
|
|
||||||
|
|
||||||
|
def select_stream(stream : str) -> List[Command]:
|
||||||
|
return [ '-select_streams', stream ]
|
||||||
|
|
||||||
|
|
||||||
|
def show_stream_entries(entries : List[str]) -> List[Command]:
|
||||||
|
return [ '-show_entries', 'stream=' + ','.join(entries) ]
|
||||||
|
|
||||||
|
|
||||||
|
def show_format_entries(entries : List[str]) -> List[Command]:
|
||||||
|
return [ '-show_entries', 'format=' + ','.join(entries) ]
|
||||||
|
|
||||||
|
|
||||||
|
def format_to_key_value() -> List[Command]:
|
||||||
|
return [ '-of', 'default=noprint_wrappers=1' ]
|
||||||
|
|
||||||
|
|
||||||
|
def set_input(input_path : str) -> List[Command]:
|
||||||
|
return [ '-i', input_path ]
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
from facefusion.types import FrameStoreSet, VisionFrame, VisionFrameSet
|
||||||
|
|
||||||
|
FRAME_STORE_SET : FrameStoreSet = {}
|
||||||
|
|
||||||
|
|
||||||
|
def get_frame_store(id : str) -> VisionFrameSet:
|
||||||
|
if id not in FRAME_STORE_SET:
|
||||||
|
FRAME_STORE_SET[id] = {}
|
||||||
|
|
||||||
|
return FRAME_STORE_SET.get(id)
|
||||||
|
|
||||||
|
|
||||||
|
def set_frame(id : str, frame_number : int, vision_frame : VisionFrame) -> None:
|
||||||
|
frame_store = get_frame_store(id)
|
||||||
|
frame_store[frame_number] = vision_frame
|
||||||
|
|
||||||
|
|
||||||
|
def select_frame_set(id : str, frame_start : int, frame_end : int) -> VisionFrameSet:
|
||||||
|
frame_store = get_frame_store(id)
|
||||||
|
frame_set = {}
|
||||||
|
|
||||||
|
for frame_number in range(frame_start, frame_end + 1):
|
||||||
|
if frame_number in frame_store:
|
||||||
|
frame_set[frame_number] = frame_store.get(frame_number)
|
||||||
|
|
||||||
|
return frame_set
|
||||||
|
|
||||||
|
|
||||||
|
def reduce_frames(id : str, frame_min : int, frame_max : int) -> None:
|
||||||
|
FRAME_STORE_SET[id] = select_frame_set(id, frame_min, frame_max)
|
||||||
|
|
||||||
|
|
||||||
|
def clear_frames(id : str) -> None:
|
||||||
|
if id in FRAME_STORE_SET:
|
||||||
|
del FRAME_STORE_SET[id]
|
||||||
@@ -93,10 +93,23 @@ def resolve_static_inference_providers(module_name : str, execution_device_id :
|
|||||||
module = importlib.import_module(module_name)
|
module = importlib.import_module(module_name)
|
||||||
execution_providers = state_manager.get_item('execution_providers')
|
execution_providers = state_manager.get_item('execution_providers')
|
||||||
|
|
||||||
if hasattr(module, 'resolve_inference_providers'):
|
if hasattr(module, 'override_inference_providers'):
|
||||||
inference_providers = getattr(module, 'resolve_inference_providers')()
|
override_inference_providers = getattr(module, 'override_inference_providers')()
|
||||||
|
|
||||||
|
if override_inference_providers:
|
||||||
|
return override_inference_providers
|
||||||
|
|
||||||
|
if hasattr(module, 'adjust_inference_providers'):
|
||||||
|
adjust_inference_providers = getattr(module, 'adjust_inference_providers')()
|
||||||
|
|
||||||
|
if adjust_inference_providers:
|
||||||
|
inference_providers = create_inference_providers(execution_device_id, execution_providers)
|
||||||
|
|
||||||
|
for adjust_inference_provider in adjust_inference_providers:
|
||||||
|
for inference_provider in inference_providers:
|
||||||
|
if inference_provider[0] == adjust_inference_provider[0] and inference_provider[1]:
|
||||||
|
inference_provider[1].update(adjust_inference_provider[1])
|
||||||
|
|
||||||
if inference_providers:
|
|
||||||
return inference_providers
|
return inference_providers
|
||||||
|
|
||||||
return create_inference_providers(execution_device_id, execution_providers)
|
return create_inference_providers(execution_device_id, execution_providers)
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ LOCALES : Locales =\
|
|||||||
{
|
{
|
||||||
'conda_not_activated': 'conda is not activated',
|
'conda_not_activated': 'conda is not activated',
|
||||||
'python_not_supported': 'python version is not supported, upgrade to {version} or higher',
|
'python_not_supported': 'python version is not supported, upgrade to {version} or higher',
|
||||||
'curl_not_installed': 'curl is not installed',
|
'dependency_not_installed': '{dependency} is not installed',
|
||||||
'ffmpeg_not_installed': 'ffmpeg is not installed',
|
|
||||||
'creating_temp': 'creating temporary resources',
|
'creating_temp': 'creating temporary resources',
|
||||||
'extracting_frames': 'extracting frames with a resolution of {resolution} and {fps} frames per second',
|
'extracting_frames': 'extracting frames with a resolution of {resolution} and {fps} frames per second',
|
||||||
'extracting_frames_succeeded': 'extracting frames succeeded',
|
'extracting_frames_succeeded': 'extracting frames succeeded',
|
||||||
@@ -99,6 +98,8 @@ LOCALES : Locales =\
|
|||||||
{
|
{
|
||||||
'install_dependency': 'choose the variant of {dependency} to install',
|
'install_dependency': 'choose the variant of {dependency} to install',
|
||||||
'skip_conda': 'skip the conda environment check',
|
'skip_conda': 'skip the conda environment check',
|
||||||
|
'workflow_mode': 'choose the workflow mode',
|
||||||
|
'workflow_strategy': 'choose the workflow strategy',
|
||||||
'config_path': 'choose the config file to override defaults',
|
'config_path': 'choose the config file to override defaults',
|
||||||
'temp_path': 'specify the directory for the temporary resources',
|
'temp_path': 'specify the directory for the temporary resources',
|
||||||
'jobs_path': 'specify the directory to store jobs',
|
'jobs_path': 'specify the directory to store jobs',
|
||||||
@@ -136,7 +137,7 @@ LOCALES : Locales =\
|
|||||||
'trim_frame_start': 'specify the starting frame of the target video',
|
'trim_frame_start': 'specify the starting frame of the target video',
|
||||||
'trim_frame_end': 'specify the ending 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_frame_format': 'specify the temporary resources format',
|
||||||
'keep_temp': 'keep the temporary resources after processing',
|
'temp_pixel_format': 'specify the temporary pixel format',
|
||||||
'target_frame_amount': 'specify the amount of target frames forwarded to the processor',
|
'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_quality': 'specify the image quality which translates to the image compression',
|
||||||
'output_image_scale': 'specify the image scale based on the target image',
|
'output_image_scale': 'specify the image scale based on the target image',
|
||||||
@@ -201,9 +202,10 @@ LOCALES : Locales =\
|
|||||||
'benchmark_cycle_count_slider': 'BENCHMARK CYCLE COUNT',
|
'benchmark_cycle_count_slider': 'BENCHMARK CYCLE COUNT',
|
||||||
'benchmark_resolutions_checkbox_group': 'BENCHMARK RESOLUTIONS',
|
'benchmark_resolutions_checkbox_group': 'BENCHMARK RESOLUTIONS',
|
||||||
'clear_button': 'CLEAR',
|
'clear_button': 'CLEAR',
|
||||||
'common_options_checkbox_group': 'OPTIONS',
|
|
||||||
'download_providers_checkbox_group': 'DOWNLOAD PROVIDERS',
|
'download_providers_checkbox_group': 'DOWNLOAD PROVIDERS',
|
||||||
'execution_providers_checkbox_group': 'EXECUTION PROVIDERS',
|
'execution_providers_checkbox_group': 'EXECUTION PROVIDERS',
|
||||||
|
'workflow_mode_dropdown': 'WORKFLOW MODE',
|
||||||
|
'workflow_strategy_dropdown': 'WORKFLOW STRATEGY',
|
||||||
'execution_thread_count_slider': 'EXECUTION THREAD COUNT',
|
'execution_thread_count_slider': 'EXECUTION THREAD COUNT',
|
||||||
'face_detector_angles_checkbox_group': 'FACE DETECTOR ANGLES',
|
'face_detector_angles_checkbox_group': 'FACE DETECTOR ANGLES',
|
||||||
'face_detector_model_dropdown': 'FACE DETECTOR MODEL',
|
'face_detector_model_dropdown': 'FACE DETECTOR MODEL',
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ METADATA =\
|
|||||||
{
|
{
|
||||||
'name': 'FaceFusion',
|
'name': 'FaceFusion',
|
||||||
'description': 'Industry leading face manipulation platform',
|
'description': 'Industry leading face manipulation platform',
|
||||||
'version': '3.7.1',
|
'version': 'NEXT',
|
||||||
'license': 'OpenRAIL-AS',
|
'license': 'OpenRAIL-AS',
|
||||||
'author': 'Henry Ruhs',
|
'author': 'Henry Ruhs',
|
||||||
'url': 'https://facefusion.io'
|
'url': 'https://facefusion.io'
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ from facefusion.processors.types import ProcessorOutputs
|
|||||||
from facefusion.program_helper import find_argument_group
|
from facefusion.program_helper import find_argument_group
|
||||||
from facefusion.thread_helper import thread_semaphore
|
from facefusion.thread_helper import thread_semaphore
|
||||||
from facefusion.types import ApplyStateItem, Args, DownloadScope, Face, InferencePool, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
from facefusion.types import ApplyStateItem, Args, DownloadScope, Face, InferencePool, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
||||||
from facefusion.vision import match_frame_color, read_static_image, read_static_video_chunk, read_static_video_frame
|
from facefusion.vision import match_frame_color, read_static_image, read_static_video_frame
|
||||||
|
|
||||||
|
|
||||||
@lru_cache()
|
@lru_cache()
|
||||||
@@ -166,7 +166,6 @@ def pre_process(mode : ProcessMode) -> bool:
|
|||||||
def post_process() -> None:
|
def post_process() -> None:
|
||||||
read_static_image.cache_clear()
|
read_static_image.cache_clear()
|
||||||
read_static_video_frame.cache_clear()
|
read_static_video_frame.cache_clear()
|
||||||
read_static_video_chunk.cache_clear()
|
|
||||||
video_manager.clear_video_pool()
|
video_manager.clear_video_pool()
|
||||||
|
|
||||||
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ from facefusion.program_helper import find_argument_group
|
|||||||
from facefusion.sanitizer import sanitize_int_range
|
from facefusion.sanitizer import sanitize_int_range
|
||||||
from facefusion.thread_helper import thread_semaphore
|
from facefusion.thread_helper import thread_semaphore
|
||||||
from facefusion.types import ApplyStateItem, Args, DownloadScope, InferencePool, InferenceProvider, Mask, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
from facefusion.types import ApplyStateItem, Args, DownloadScope, InferencePool, InferenceProvider, Mask, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
||||||
from facefusion.vision import read_static_image, read_static_video_chunk, read_static_video_frame
|
from facefusion.vision import read_static_image, read_static_video_frame
|
||||||
|
|
||||||
|
|
||||||
@lru_cache()
|
@lru_cache()
|
||||||
@@ -479,7 +479,7 @@ def clear_inference_pool() -> None:
|
|||||||
inference_manager.clear_inference_pool(__name__, model_names)
|
inference_manager.clear_inference_pool(__name__, model_names)
|
||||||
|
|
||||||
|
|
||||||
def resolve_inference_providers() -> List[InferenceProvider]:
|
def override_inference_providers() -> List[InferenceProvider]:
|
||||||
model_type = get_model_options().get('type')
|
model_type = get_model_options().get('type')
|
||||||
|
|
||||||
if is_macos() and has_execution_provider('coreml') or is_windows() and has_execution_provider('directml') and model_type == 'corridor_key':
|
if is_macos() and has_execution_provider('coreml') or is_windows() and has_execution_provider('directml') and model_type == 'corridor_key':
|
||||||
@@ -539,7 +539,6 @@ def pre_process(mode : ProcessMode) -> bool:
|
|||||||
def post_process() -> None:
|
def post_process() -> None:
|
||||||
read_static_image.cache_clear()
|
read_static_image.cache_clear()
|
||||||
read_static_video_frame.cache_clear()
|
read_static_video_frame.cache_clear()
|
||||||
read_static_video_chunk.cache_clear()
|
|
||||||
video_manager.clear_video_pool()
|
video_manager.clear_video_pool()
|
||||||
|
|
||||||
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ from facefusion.processors.types import ProcessorOutputs
|
|||||||
from facefusion.program_helper import find_argument_group
|
from facefusion.program_helper import find_argument_group
|
||||||
from facefusion.thread_helper import thread_semaphore
|
from facefusion.thread_helper import thread_semaphore
|
||||||
from facefusion.types import ApplyStateItem, Args, DownloadScope, Face, InferencePool, Mask, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
from facefusion.types import ApplyStateItem, Args, DownloadScope, Face, InferencePool, Mask, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
||||||
from facefusion.vision import conditional_match_frame_color, read_static_image, read_static_video_chunk, read_static_video_frame
|
from facefusion.vision import conditional_match_frame_color, read_static_image, read_static_video_frame
|
||||||
|
|
||||||
|
|
||||||
@lru_cache()
|
@lru_cache()
|
||||||
@@ -320,7 +320,6 @@ def pre_process(mode : ProcessMode) -> bool:
|
|||||||
def post_process() -> None:
|
def post_process() -> None:
|
||||||
read_static_image.cache_clear()
|
read_static_image.cache_clear()
|
||||||
read_static_video_frame.cache_clear()
|
read_static_video_frame.cache_clear()
|
||||||
read_static_video_chunk.cache_clear()
|
|
||||||
video_manager.clear_video_pool()
|
video_manager.clear_video_pool()
|
||||||
|
|
||||||
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ from facefusion.processors.types import LivePortraitExpression, LivePortraitFeat
|
|||||||
from facefusion.program_helper import find_argument_group
|
from facefusion.program_helper import find_argument_group
|
||||||
from facefusion.thread_helper import conditional_thread_semaphore, thread_semaphore
|
from facefusion.thread_helper import conditional_thread_semaphore, thread_semaphore
|
||||||
from facefusion.types import ApplyStateItem, Args, DownloadScope, Face, InferencePool, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
from facefusion.types import ApplyStateItem, Args, DownloadScope, Face, InferencePool, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
||||||
from facefusion.vision import read_static_image, read_static_video_chunk, read_static_video_frame
|
from facefusion.vision import read_static_image, read_static_video_frame
|
||||||
|
|
||||||
|
|
||||||
@lru_cache()
|
@lru_cache()
|
||||||
@@ -146,7 +146,6 @@ def pre_process(mode : ProcessMode) -> bool:
|
|||||||
def post_process() -> None:
|
def post_process() -> None:
|
||||||
read_static_image.cache_clear()
|
read_static_image.cache_clear()
|
||||||
read_static_video_frame.cache_clear()
|
read_static_video_frame.cache_clear()
|
||||||
read_static_video_chunk.cache_clear()
|
|
||||||
video_manager.clear_video_pool()
|
video_manager.clear_video_pool()
|
||||||
|
|
||||||
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ from facefusion.processors.modules.face_debugger.types import FaceDebuggerInputs
|
|||||||
from facefusion.processors.types import ProcessorOutputs
|
from facefusion.processors.types import ProcessorOutputs
|
||||||
from facefusion.program_helper import find_argument_group
|
from facefusion.program_helper import find_argument_group
|
||||||
from facefusion.types import ApplyStateItem, Args, Face, InferencePool, ProcessMode, VisionFrame
|
from facefusion.types import ApplyStateItem, Args, Face, InferencePool, ProcessMode, VisionFrame
|
||||||
from facefusion.vision import read_static_image, read_static_video_chunk, read_static_video_frame
|
from facefusion.vision import read_static_image, read_static_video_frame
|
||||||
|
|
||||||
|
|
||||||
def get_inference_pool() -> InferencePool:
|
def get_inference_pool() -> InferencePool:
|
||||||
@@ -68,7 +68,6 @@ def pre_process(mode : ProcessMode) -> bool:
|
|||||||
def post_process() -> None:
|
def post_process() -> None:
|
||||||
read_static_image.cache_clear()
|
read_static_image.cache_clear()
|
||||||
read_static_video_frame.cache_clear()
|
read_static_video_frame.cache_clear()
|
||||||
read_static_video_chunk.cache_clear()
|
|
||||||
video_manager.clear_video_pool()
|
video_manager.clear_video_pool()
|
||||||
|
|
||||||
if state_manager.get_item('video_memory_strategy') == 'strict':
|
if state_manager.get_item('video_memory_strategy') == 'strict':
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ from facefusion.processors.types import LivePortraitExpression, LivePortraitFeat
|
|||||||
from facefusion.program_helper import find_argument_group
|
from facefusion.program_helper import find_argument_group
|
||||||
from facefusion.thread_helper import conditional_thread_semaphore, thread_semaphore
|
from facefusion.thread_helper import conditional_thread_semaphore, thread_semaphore
|
||||||
from facefusion.types import ApplyStateItem, Args, DownloadScope, Face, FaceLandmark68, InferencePool, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
from facefusion.types import ApplyStateItem, Args, DownloadScope, Face, FaceLandmark68, InferencePool, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
||||||
from facefusion.vision import read_static_image, read_static_video_chunk, read_static_video_frame
|
from facefusion.vision import read_static_image, read_static_video_frame
|
||||||
|
|
||||||
|
|
||||||
@lru_cache()
|
@lru_cache()
|
||||||
@@ -197,7 +197,6 @@ def pre_process(mode : ProcessMode) -> bool:
|
|||||||
def post_process() -> None:
|
def post_process() -> None:
|
||||||
read_static_image.cache_clear()
|
read_static_image.cache_clear()
|
||||||
read_static_video_frame.cache_clear()
|
read_static_video_frame.cache_clear()
|
||||||
read_static_video_chunk.cache_clear()
|
|
||||||
video_manager.clear_video_pool()
|
video_manager.clear_video_pool()
|
||||||
|
|
||||||
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ from facefusion.processors.types import ProcessorOutputs
|
|||||||
from facefusion.program_helper import find_argument_group
|
from facefusion.program_helper import find_argument_group
|
||||||
from facefusion.thread_helper import thread_semaphore
|
from facefusion.thread_helper import thread_semaphore
|
||||||
from facefusion.types import ApplyStateItem, Args, DownloadScope, Face, InferencePool, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
from facefusion.types import ApplyStateItem, Args, DownloadScope, Face, InferencePool, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
||||||
from facefusion.vision import blend_frame, read_static_image, read_static_video_chunk, read_static_video_frame
|
from facefusion.vision import blend_frame, read_static_image, read_static_video_frame
|
||||||
|
|
||||||
|
|
||||||
@lru_cache()
|
@lru_cache()
|
||||||
@@ -337,7 +337,6 @@ def pre_process(mode : ProcessMode) -> bool:
|
|||||||
def post_process() -> None:
|
def post_process() -> None:
|
||||||
read_static_image.cache_clear()
|
read_static_image.cache_clear()
|
||||||
read_static_video_frame.cache_clear()
|
read_static_video_frame.cache_clear()
|
||||||
read_static_video_chunk.cache_clear()
|
|
||||||
video_manager.clear_video_pool()
|
video_manager.clear_video_pool()
|
||||||
|
|
||||||
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ from facefusion.processors.types import ProcessorOutputs
|
|||||||
from facefusion.program_helper import find_argument_group
|
from facefusion.program_helper import find_argument_group
|
||||||
from facefusion.thread_helper import conditional_thread_semaphore
|
from facefusion.thread_helper import conditional_thread_semaphore
|
||||||
from facefusion.types import ApplyStateItem, Args, DownloadScope, Embedding, Face, InferencePool, InferenceProvider, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
from facefusion.types import ApplyStateItem, Args, DownloadScope, Embedding, Face, InferencePool, InferenceProvider, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
||||||
from facefusion.vision import read_static_image, read_static_images, read_static_video_chunk, read_static_video_frame, unpack_resolution
|
from facefusion.vision import read_static_image, read_static_images, read_static_video_frame, unpack_resolution
|
||||||
|
|
||||||
|
|
||||||
@lru_cache()
|
@lru_cache()
|
||||||
@@ -502,7 +502,7 @@ def clear_inference_pool() -> None:
|
|||||||
inference_manager.clear_inference_pool(__name__, model_names)
|
inference_manager.clear_inference_pool(__name__, model_names)
|
||||||
|
|
||||||
|
|
||||||
def resolve_inference_providers() -> List[InferenceProvider]:
|
def adjust_inference_providers() -> List[InferenceProvider]:
|
||||||
model_precision = get_model_options().get('precision')
|
model_precision = get_model_options().get('precision')
|
||||||
model_type = get_model_options().get('type')
|
model_type = get_model_options().get('type')
|
||||||
|
|
||||||
@@ -512,8 +512,7 @@ def resolve_inference_providers() -> List[InferenceProvider]:
|
|||||||
[
|
[
|
||||||
(facefusion.choices.execution_provider_set.get('coreml'),
|
(facefusion.choices.execution_provider_set.get('coreml'),
|
||||||
{
|
{
|
||||||
'ModelFormat': 'MLProgram',
|
'ModelFormat': 'MLProgram'
|
||||||
'SpecializationStrategy': 'FastPrediction'
|
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -588,7 +587,6 @@ def pre_process(mode : ProcessMode) -> bool:
|
|||||||
def post_process() -> None:
|
def post_process() -> None:
|
||||||
read_static_image.cache_clear()
|
read_static_image.cache_clear()
|
||||||
read_static_video_frame.cache_clear()
|
read_static_video_frame.cache_clear()
|
||||||
read_static_video_chunk.cache_clear()
|
|
||||||
video_manager.clear_video_pool()
|
video_manager.clear_video_pool()
|
||||||
|
|
||||||
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from facefusion.processors.types import ProcessorOutputs
|
|||||||
from facefusion.program_helper import find_argument_group
|
from facefusion.program_helper import find_argument_group
|
||||||
from facefusion.thread_helper import thread_semaphore
|
from facefusion.thread_helper import thread_semaphore
|
||||||
from facefusion.types import ApplyStateItem, Args, DownloadScope, InferencePool, InferenceProvider, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
from facefusion.types import ApplyStateItem, Args, DownloadScope, InferencePool, InferenceProvider, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
||||||
from facefusion.vision import blend_frame, read_static_image, read_static_video_chunk, read_static_video_frame, unpack_resolution
|
from facefusion.vision import blend_frame, read_static_image, read_static_video_frame, unpack_resolution
|
||||||
|
|
||||||
|
|
||||||
@lru_cache()
|
@lru_cache()
|
||||||
@@ -172,7 +172,7 @@ def clear_inference_pool() -> None:
|
|||||||
inference_manager.clear_inference_pool(__name__, model_names)
|
inference_manager.clear_inference_pool(__name__, model_names)
|
||||||
|
|
||||||
|
|
||||||
def resolve_inference_providers() -> List[InferenceProvider]:
|
def override_inference_providers() -> List[InferenceProvider]:
|
||||||
if is_macos() and has_execution_provider('coreml'):
|
if is_macos() and has_execution_provider('coreml'):
|
||||||
return [ facefusion.choices.execution_provider_set.get('cpu') ]
|
return [ facefusion.choices.execution_provider_set.get('cpu') ]
|
||||||
|
|
||||||
@@ -230,7 +230,6 @@ def pre_process(mode : ProcessMode) -> bool:
|
|||||||
def post_process() -> None:
|
def post_process() -> None:
|
||||||
read_static_image.cache_clear()
|
read_static_image.cache_clear()
|
||||||
read_static_video_frame.cache_clear()
|
read_static_video_frame.cache_clear()
|
||||||
read_static_video_chunk.cache_clear()
|
|
||||||
video_manager.clear_video_pool()
|
video_manager.clear_video_pool()
|
||||||
|
|
||||||
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from facefusion.processors.types import ProcessorOutputs
|
|||||||
from facefusion.program_helper import find_argument_group
|
from facefusion.program_helper import find_argument_group
|
||||||
from facefusion.thread_helper import conditional_thread_semaphore
|
from facefusion.thread_helper import conditional_thread_semaphore
|
||||||
from facefusion.types import ApplyStateItem, Args, DownloadScope, InferencePool, InferenceProvider, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
from facefusion.types import ApplyStateItem, Args, DownloadScope, InferencePool, InferenceProvider, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
||||||
from facefusion.vision import blend_frame, create_tile_frames, merge_tile_frames, read_static_image, read_static_video_chunk, read_static_video_frame
|
from facefusion.vision import blend_frame, create_tile_frames, merge_tile_frames, read_static_image, read_static_video_frame
|
||||||
|
|
||||||
|
|
||||||
@lru_cache()
|
@lru_cache()
|
||||||
@@ -558,7 +558,7 @@ def clear_inference_pool() -> None:
|
|||||||
inference_manager.clear_inference_pool(__name__, model_names)
|
inference_manager.clear_inference_pool(__name__, model_names)
|
||||||
|
|
||||||
|
|
||||||
def resolve_inference_providers() -> List[InferenceProvider]:
|
def adjust_inference_providers() -> List[InferenceProvider]:
|
||||||
model_precision = get_model_options().get('precision')
|
model_precision = get_model_options().get('precision')
|
||||||
|
|
||||||
if is_macos() and has_execution_provider('coreml') and model_precision == 'fp16':
|
if is_macos() and has_execution_provider('coreml') and model_precision == 'fp16':
|
||||||
@@ -566,8 +566,7 @@ def resolve_inference_providers() -> List[InferenceProvider]:
|
|||||||
[
|
[
|
||||||
(facefusion.choices.execution_provider_set.get('coreml'),
|
(facefusion.choices.execution_provider_set.get('coreml'),
|
||||||
{
|
{
|
||||||
'ModelFormat': 'MLProgram',
|
'ModelFormat': 'MLProgram'
|
||||||
'SpecializationStrategy': 'FastPrediction'
|
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -623,7 +622,6 @@ def pre_process(mode : ProcessMode) -> bool:
|
|||||||
def post_process() -> None:
|
def post_process() -> None:
|
||||||
read_static_image.cache_clear()
|
read_static_image.cache_clear()
|
||||||
read_static_video_frame.cache_clear()
|
read_static_video_frame.cache_clear()
|
||||||
read_static_video_chunk.cache_clear()
|
|
||||||
video_manager.clear_video_pool()
|
video_manager.clear_video_pool()
|
||||||
|
|
||||||
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ from facefusion.processors.types import ProcessorOutputs
|
|||||||
from facefusion.program_helper import find_argument_group
|
from facefusion.program_helper import find_argument_group
|
||||||
from facefusion.thread_helper import conditional_thread_semaphore
|
from facefusion.thread_helper import conditional_thread_semaphore
|
||||||
from facefusion.types import ApplyStateItem, Args, AudioFrame, DownloadScope, Face, InferencePool, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
from facefusion.types import ApplyStateItem, Args, AudioFrame, DownloadScope, Face, InferencePool, ModelOptions, ModelSet, ProcessMode, VisionFrame
|
||||||
from facefusion.vision import read_static_image, read_static_video_chunk, read_static_video_frame
|
from facefusion.vision import read_static_image, read_static_video_frame
|
||||||
|
|
||||||
|
|
||||||
@lru_cache()
|
@lru_cache()
|
||||||
@@ -169,7 +169,6 @@ def pre_process(mode : ProcessMode) -> bool:
|
|||||||
def post_process() -> None:
|
def post_process() -> None:
|
||||||
read_static_image.cache_clear()
|
read_static_image.cache_clear()
|
||||||
read_static_video_frame.cache_clear()
|
read_static_video_frame.cache_clear()
|
||||||
read_static_video_chunk.cache_clear()
|
|
||||||
read_static_voice.cache_clear()
|
read_static_voice.cache_clear()
|
||||||
video_manager.clear_video_pool()
|
video_manager.clear_video_pool()
|
||||||
|
|
||||||
|
|||||||
+12
-3
@@ -21,6 +21,15 @@ def create_help_formatter_large(prog : str) -> HelpFormatter:
|
|||||||
return HelpFormatter(prog, max_help_position = 300)
|
return HelpFormatter(prog, max_help_position = 300)
|
||||||
|
|
||||||
|
|
||||||
|
def create_workflow_program() -> ArgumentParser:
|
||||||
|
program = ArgumentParser(add_help = False)
|
||||||
|
group_workflow = program.add_argument_group('workflow')
|
||||||
|
group_workflow.add_argument('--workflow-mode', help = translator.get('help.workflow_mode'), default = config.get_str_value('workflow', 'workflow_mode', 'auto'), choices = facefusion.choices.workflow_modes)
|
||||||
|
group_workflow.add_argument('--workflow-strategy', help = translator.get('help.workflow_strategy'), default = config.get_str_value('workflow', 'workflow_strategy', 'stream'), choices = facefusion.choices.workflow_strategies)
|
||||||
|
job_store.register_step_keys([ 'workflow_mode', 'workflow_strategy' ])
|
||||||
|
return program
|
||||||
|
|
||||||
|
|
||||||
def create_config_path_program() -> ArgumentParser:
|
def create_config_path_program() -> ArgumentParser:
|
||||||
program = ArgumentParser(add_help = False)
|
program = ArgumentParser(add_help = False)
|
||||||
group_paths = program.add_argument_group('paths')
|
group_paths = program.add_argument_group('paths')
|
||||||
@@ -169,8 +178,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-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('--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('--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'))
|
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', 'bgr24'), choices = facefusion.choices.temp_pixel_formats)
|
||||||
job_store.register_step_keys([ 'trim_frame_start', 'trim_frame_end', 'temp_frame_format', 'keep_temp' ])
|
job_store.register_step_keys([ 'trim_frame_start', 'trim_frame_end', 'temp_frame_format', 'temp_pixel_format' ])
|
||||||
return program
|
return program
|
||||||
|
|
||||||
|
|
||||||
@@ -300,7 +309,7 @@ def create_step_index_program() -> ArgumentParser:
|
|||||||
|
|
||||||
|
|
||||||
def collect_step_program() -> ArgumentParser:
|
def collect_step_program() -> ArgumentParser:
|
||||||
return ArgumentParser(parents = [ create_face_detector_program(), create_face_landmarker_program(), create_face_selector_program(), create_face_tracker_program(), create_face_masker_program(), create_voice_extractor_program(), create_frame_extraction_program(), create_frame_distribution_program(), create_output_creation_program(), create_processors_program() ], add_help = False)
|
return ArgumentParser(parents = [ create_workflow_program(), create_face_detector_program(), create_face_landmarker_program(), create_face_selector_program(), create_face_tracker_program(), create_face_masker_program(), create_voice_extractor_program(), create_frame_extraction_program(), create_frame_distribution_program(), create_output_creation_program(), create_processors_program() ], add_help = False)
|
||||||
|
|
||||||
|
|
||||||
def collect_job_program() -> ArgumentParser:
|
def collect_job_program() -> ArgumentParser:
|
||||||
|
|||||||
@@ -43,7 +43,5 @@ def create_temp_directory(file_path : str) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def clear_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)
|
||||||
temp_directory_path = get_temp_directory_path(file_path)
|
return remove_directory(temp_directory_path)
|
||||||
return remove_directory(temp_directory_path)
|
|
||||||
return True
|
|
||||||
|
|||||||
+64
-9
@@ -1,3 +1,4 @@
|
|||||||
|
import subprocess
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from typing import Any, Callable, Dict, List, Literal, NotRequired, Optional, Tuple, TypeAlias, TypedDict
|
from typing import Any, Callable, Dict, List, Literal, NotRequired, Optional, Tuple, TypeAlias, TypedDict
|
||||||
@@ -63,20 +64,18 @@ Language = Literal['en']
|
|||||||
Locales : TypeAlias = Dict[Language, Dict[str, Any]]
|
Locales : TypeAlias = Dict[Language, Dict[str, Any]]
|
||||||
LocalePoolSet : TypeAlias = Dict[str, Locales]
|
LocalePoolSet : TypeAlias = Dict[str, Locales]
|
||||||
|
|
||||||
VideoCaptureSet : TypeAlias = Dict[str, cv2.VideoCapture]
|
WorkflowMode = Literal['auto', 'image-to-image', 'image-to-video']
|
||||||
VideoWriterSet : TypeAlias = Dict[str, cv2.VideoWriter]
|
WorkflowStrategy = Literal['disk', 'stream']
|
||||||
|
|
||||||
CameraCaptureSet : TypeAlias = Dict[str, cv2.VideoCapture]
|
CameraCaptureSet : TypeAlias = Dict[str, cv2.VideoCapture]
|
||||||
VideoPoolSet = TypedDict('VideoPoolSet',
|
|
||||||
{
|
|
||||||
'capture' : VideoCaptureSet,
|
|
||||||
'writer' : VideoWriterSet
|
|
||||||
})
|
|
||||||
CameraPoolSet = TypedDict('CameraPoolSet',
|
CameraPoolSet = TypedDict('CameraPoolSet',
|
||||||
{
|
{
|
||||||
'capture' : CameraCaptureSet
|
'capture' : CameraCaptureSet
|
||||||
})
|
})
|
||||||
|
|
||||||
ColorMode = Literal['rgb', 'rgba']
|
ColorMode = Literal['rgb', 'rgba']
|
||||||
|
ColorSpace = Literal['bt601', 'bt709', 'bt2020']
|
||||||
|
ColorTransfer : TypeAlias = str
|
||||||
VisionFrame : TypeAlias = NDArray[Any]
|
VisionFrame : TypeAlias = NDArray[Any]
|
||||||
Mask : TypeAlias = NDArray[Any]
|
Mask : TypeAlias = NDArray[Any]
|
||||||
Points : TypeAlias = NDArray[Any]
|
Points : TypeAlias = NDArray[Any]
|
||||||
@@ -95,13 +94,64 @@ MelFilterBank : TypeAlias = NDArray[Any]
|
|||||||
Voice : TypeAlias = NDArray[Any]
|
Voice : TypeAlias = NDArray[Any]
|
||||||
VoiceChunk : TypeAlias = NDArray[Any]
|
VoiceChunk : TypeAlias = NDArray[Any]
|
||||||
|
|
||||||
|
BitRate : TypeAlias = int
|
||||||
|
SampleRate : TypeAlias = int
|
||||||
Fps : TypeAlias = float
|
Fps : TypeAlias = float
|
||||||
Duration : TypeAlias = float
|
Duration : TypeAlias = float
|
||||||
|
|
||||||
|
Buffer : TypeAlias = bytes
|
||||||
|
VisionFrameSet : TypeAlias = Dict[int, VisionFrame]
|
||||||
Color : TypeAlias = Tuple[int, int, int, int]
|
Color : TypeAlias = Tuple[int, int, int, int]
|
||||||
Padding : TypeAlias = Tuple[int, int, int, int]
|
Padding : TypeAlias = Tuple[int, int, int, int]
|
||||||
Margin : TypeAlias = Tuple[int, int, int, int]
|
Margin : TypeAlias = Tuple[int, int, int, int]
|
||||||
Orientation = Literal['landscape', 'portrait']
|
Orientation = Literal['landscape', 'portrait']
|
||||||
Resolution : TypeAlias = Tuple[int, int]
|
Resolution : TypeAlias = Tuple[int, int]
|
||||||
|
AudioMetadata = TypedDict('AudioMetadata',
|
||||||
|
{
|
||||||
|
'duration' : Duration,
|
||||||
|
'frame_total' : int,
|
||||||
|
'channel_total' : int,
|
||||||
|
'sample_rate' : SampleRate,
|
||||||
|
'bit_rate' : BitRate
|
||||||
|
})
|
||||||
|
VideoMetadata = TypedDict('VideoMetadata',
|
||||||
|
{
|
||||||
|
'duration' : Duration,
|
||||||
|
'frame_total' : int,
|
||||||
|
'fps' : Fps,
|
||||||
|
'resolution' : Resolution,
|
||||||
|
'bit_rate' : BitRate,
|
||||||
|
'color_transfer' : ColorTransfer
|
||||||
|
})
|
||||||
|
VideoReaderMetadata : TypeAlias = VideoMetadata
|
||||||
|
VideoWriterMetadata = TypedDict('VideoWriterMetadata',
|
||||||
|
{
|
||||||
|
'fps' : Fps,
|
||||||
|
'resolution' : Resolution
|
||||||
|
})
|
||||||
|
VideoReader = TypedDict('VideoReader',
|
||||||
|
{
|
||||||
|
'id' : str,
|
||||||
|
'file_path' : str,
|
||||||
|
'process' : subprocess.Popen[bytes],
|
||||||
|
'metadata' : VideoReaderMetadata,
|
||||||
|
'frame_number' : int
|
||||||
|
})
|
||||||
|
VideoReaderSet : TypeAlias = Dict[str, VideoReader]
|
||||||
|
VideoWriter = TypedDict('VideoWriter',
|
||||||
|
{
|
||||||
|
'id' : str,
|
||||||
|
'file_path' : str,
|
||||||
|
'process' : subprocess.Popen[bytes],
|
||||||
|
'metadata' : VideoWriterMetadata
|
||||||
|
})
|
||||||
|
VideoWriterSet : TypeAlias = Dict[str, VideoWriter]
|
||||||
|
VideoPoolSet = TypedDict('VideoPoolSet',
|
||||||
|
{
|
||||||
|
'reader' : VideoReaderSet,
|
||||||
|
'writer' : VideoWriterSet
|
||||||
|
})
|
||||||
|
FrameStoreSet : TypeAlias = Dict[str, VisionFrameSet]
|
||||||
|
|
||||||
ProcessState = Literal['checking', 'processing', 'stopping', 'pending']
|
ProcessState = Literal['checking', 'processing', 'stopping', 'pending']
|
||||||
Args : TypeAlias = Dict[str, Any]
|
Args : TypeAlias = Dict[str, Any]
|
||||||
@@ -143,6 +193,7 @@ AudioFormat = Literal['flac', 'm4a', 'mp3', 'ogg', 'opus', 'wav']
|
|||||||
ImageFormat = Literal['bmp', 'jpeg', 'png', 'tiff', 'webp']
|
ImageFormat = Literal['bmp', 'jpeg', 'png', 'tiff', 'webp']
|
||||||
VideoFormat = Literal['avi', 'm4v', 'mkv', 'mov', 'mp4', 'mpeg', 'mxf', 'webm', 'wmv']
|
VideoFormat = Literal['avi', 'm4v', 'mkv', 'mov', 'mp4', 'mpeg', 'mxf', 'webm', 'wmv']
|
||||||
TempFrameFormat = Literal['bmp', 'jpeg', 'png', 'tiff']
|
TempFrameFormat = Literal['bmp', 'jpeg', 'png', 'tiff']
|
||||||
|
TempPixelFormat = Literal['bgr24', 'bgra']
|
||||||
AudioTypeSet : TypeAlias = Dict[AudioFormat, str]
|
AudioTypeSet : TypeAlias = Dict[AudioFormat, str]
|
||||||
ImageTypeSet : TypeAlias = Dict[ImageFormat, str]
|
ImageTypeSet : TypeAlias = Dict[ImageFormat, str]
|
||||||
VideoTypeSet : TypeAlias = Dict[VideoFormat, str]
|
VideoTypeSet : TypeAlias = Dict[VideoFormat, str]
|
||||||
@@ -271,6 +322,8 @@ JobSet : TypeAlias = Dict[str, Job]
|
|||||||
StateKey = Literal\
|
StateKey = Literal\
|
||||||
[
|
[
|
||||||
'command',
|
'command',
|
||||||
|
'workflow_mode',
|
||||||
|
'workflow_strategy',
|
||||||
'config_path',
|
'config_path',
|
||||||
'temp_path',
|
'temp_path',
|
||||||
'jobs_path',
|
'jobs_path',
|
||||||
@@ -313,7 +366,7 @@ StateKey = Literal\
|
|||||||
'trim_frame_start',
|
'trim_frame_start',
|
||||||
'trim_frame_end',
|
'trim_frame_end',
|
||||||
'temp_frame_format',
|
'temp_frame_format',
|
||||||
'keep_temp',
|
'temp_pixel_format',
|
||||||
'target_frame_amount',
|
'target_frame_amount',
|
||||||
'output_image_quality',
|
'output_image_quality',
|
||||||
'output_image_scale',
|
'output_image_scale',
|
||||||
@@ -342,6 +395,8 @@ StateKey = Literal\
|
|||||||
State = TypedDict('State',
|
State = TypedDict('State',
|
||||||
{
|
{
|
||||||
'command' : str,
|
'command' : str,
|
||||||
|
'workflow_mode' : WorkflowMode,
|
||||||
|
'workflow_strategy' : WorkflowStrategy,
|
||||||
'config_path' : str,
|
'config_path' : str,
|
||||||
'temp_path' : str,
|
'temp_path' : str,
|
||||||
'jobs_path' : str,
|
'jobs_path' : str,
|
||||||
@@ -384,7 +439,7 @@ State = TypedDict('State',
|
|||||||
'trim_frame_start' : int,
|
'trim_frame_start' : int,
|
||||||
'trim_frame_end' : int,
|
'trim_frame_end' : int,
|
||||||
'temp_frame_format' : TempFrameFormat,
|
'temp_frame_format' : TempFrameFormat,
|
||||||
'keep_temp' : bool,
|
'temp_pixel_format' : TempPixelFormat,
|
||||||
'target_frame_amount' : int,
|
'target_frame_amount' : int,
|
||||||
'output_image_quality' : int,
|
'output_image_quality' : int,
|
||||||
'output_image_scale' : Scale,
|
'output_image_scale' : Scale,
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ 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_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' ]
|
job_runner_actions : List[JobRunnerAction] = [ 'job-run', 'job-run-all', 'job-retry', 'job-retry-all' ]
|
||||||
|
|
||||||
common_options : List[str] = [ 'keep-temp' ]
|
|
||||||
|
|
||||||
preview_modes : List[PreviewMode] = [ 'default', 'frame-by-frame', 'face-by-face' ]
|
preview_modes : List[PreviewMode] = [ 'default', 'frame-by-frame', 'face-by-face' ]
|
||||||
preview_resolutions : List[str] = [ '512x512', '768x768', '1024x1024' ]
|
preview_resolutions : List[str] = [ '512x512', '768x768', '1024x1024' ]
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
from typing import List, Optional
|
|
||||||
|
|
||||||
import gradio
|
|
||||||
|
|
||||||
from facefusion import state_manager, translator
|
|
||||||
from facefusion.uis import choices as uis_choices
|
|
||||||
|
|
||||||
COMMON_OPTIONS_CHECKBOX_GROUP : Optional[gradio.Checkboxgroup] = None
|
|
||||||
|
|
||||||
|
|
||||||
def render() -> None:
|
|
||||||
global COMMON_OPTIONS_CHECKBOX_GROUP
|
|
||||||
|
|
||||||
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,
|
|
||||||
value = common_options
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def listen() -> None:
|
|
||||||
COMMON_OPTIONS_CHECKBOX_GROUP.change(update, inputs = COMMON_OPTIONS_CHECKBOX_GROUP)
|
|
||||||
|
|
||||||
|
|
||||||
def update(common_options : List[str]) -> None:
|
|
||||||
keep_temp = 'keep-temp' in common_options
|
|
||||||
state_manager.set_item('keep_temp', keep_temp)
|
|
||||||
@@ -26,8 +26,9 @@ def render() -> None:
|
|||||||
'visible': False
|
'visible': False
|
||||||
}
|
}
|
||||||
if is_video(state_manager.get_item('target_path')):
|
if is_video(state_manager.get_item('target_path')):
|
||||||
|
video_frame_total = count_video_frame_total(state_manager.get_item('target_path'))
|
||||||
preview_frame_slider_options['value'] = state_manager.get_item('reference_frame_number')
|
preview_frame_slider_options['value'] = state_manager.get_item('reference_frame_number')
|
||||||
preview_frame_slider_options['maximum'] = count_video_frame_total(state_manager.get_item('target_path'))
|
preview_frame_slider_options['maximum'] = video_frame_total - 1
|
||||||
preview_frame_slider_options['visible'] = True
|
preview_frame_slider_options['visible'] = True
|
||||||
PREVIEW_FRAME_SLIDER = gradio.Slider(**preview_frame_slider_options)
|
PREVIEW_FRAME_SLIDER = gradio.Slider(**preview_frame_slider_options)
|
||||||
with gradio.Row():
|
with gradio.Row():
|
||||||
@@ -57,5 +58,5 @@ def listen() -> None:
|
|||||||
def update_preview_frame_slider() -> gradio.Slider:
|
def update_preview_frame_slider() -> gradio.Slider:
|
||||||
if is_video(state_manager.get_item('target_path')):
|
if is_video(state_manager.get_item('target_path')):
|
||||||
video_frame_total = count_video_frame_total(state_manager.get_item('target_path'))
|
video_frame_total = count_video_frame_total(state_manager.get_item('target_path'))
|
||||||
return gradio.Slider(maximum = video_frame_total, visible = True)
|
return gradio.Slider(maximum = video_frame_total - 1, visible = True)
|
||||||
return gradio.Slider(value = 0, visible = False)
|
return gradio.Slider(value = 0, visible = False)
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ def start(webcam_device_id : int, webcam_mode : WebcamMode, webcam_resolution :
|
|||||||
yield capture_vision_frame
|
yield capture_vision_frame
|
||||||
if webcam_mode in [ 'udp', 'v4l2' ]:
|
if webcam_mode in [ 'udp', 'v4l2' ]:
|
||||||
try:
|
try:
|
||||||
stream.stdin.write(capture_vision_frame.tobytes())
|
stream.stdin.write(capture_vision_frame.data)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import gradio
|
||||||
|
|
||||||
|
import facefusion.choices
|
||||||
|
from facefusion import state_manager, translator
|
||||||
|
from facefusion.types import WorkflowMode, WorkflowStrategy
|
||||||
|
|
||||||
|
WORKFLOW_MODE_DROPDOWN : Optional[gradio.Dropdown] = None
|
||||||
|
WORKFLOW_STRATEGY_DROPDOWN : Optional[gradio.Dropdown] = None
|
||||||
|
|
||||||
|
|
||||||
|
def render() -> None:
|
||||||
|
global WORKFLOW_MODE_DROPDOWN
|
||||||
|
global WORKFLOW_STRATEGY_DROPDOWN
|
||||||
|
|
||||||
|
WORKFLOW_MODE_DROPDOWN = gradio.Dropdown(
|
||||||
|
label = translator.get('uis.workflow_mode_dropdown'),
|
||||||
|
choices = facefusion.choices.workflow_modes,
|
||||||
|
value = state_manager.get_item('workflow_mode')
|
||||||
|
)
|
||||||
|
WORKFLOW_STRATEGY_DROPDOWN = gradio.Dropdown(
|
||||||
|
label = translator.get('uis.workflow_strategy_dropdown'),
|
||||||
|
choices = facefusion.choices.workflow_strategies,
|
||||||
|
value = state_manager.get_item('workflow_strategy')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def listen() -> None:
|
||||||
|
WORKFLOW_MODE_DROPDOWN.change(update_workflow_mode, inputs = WORKFLOW_MODE_DROPDOWN)
|
||||||
|
WORKFLOW_STRATEGY_DROPDOWN.change(update_workflow_strategy, inputs = WORKFLOW_STRATEGY_DROPDOWN)
|
||||||
|
|
||||||
|
|
||||||
|
def update_workflow_mode(workflow_mode : WorkflowMode) -> None:
|
||||||
|
state_manager.set_item('workflow_mode', workflow_mode)
|
||||||
|
|
||||||
|
|
||||||
|
def update_workflow_strategy(workflow_strategy : WorkflowStrategy) -> None:
|
||||||
|
state_manager.set_item('workflow_strategy', workflow_strategy)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import gradio
|
import gradio
|
||||||
|
|
||||||
from facefusion import state_manager
|
from facefusion import state_manager
|
||||||
from facefusion.uis.components import about, age_modifier_options, background_remover_options, common_options, deep_swapper_options, download, execution, execution_thread_count, expression_restorer_options, face_debugger_options, face_detector, face_editor_options, face_enhancer_options, face_landmarker, face_masker, face_selector, face_swapper_options, face_tracker, frame_colorizer_options, frame_enhancer_options, instant_runner, job_manager, job_runner, lip_syncer_options, memory, output, output_options, preview, preview_options, processors, source, target, temp_frame, terminal, trim_frame, ui_workflow, voice_extractor
|
from facefusion.uis.components import about, age_modifier_options, background_remover_options, deep_swapper_options, download, execution, execution_thread_count, expression_restorer_options, face_debugger_options, face_detector, face_editor_options, face_enhancer_options, face_landmarker, face_masker, face_selector, face_swapper_options, face_tracker, frame_colorizer_options, frame_enhancer_options, instant_runner, job_manager, job_runner, lip_syncer_options, memory, output, output_options, preview, preview_options, processors, source, target, temp_frame, terminal, trim_frame, ui_workflow, voice_extractor, workflow
|
||||||
|
|
||||||
|
|
||||||
def pre_check() -> bool:
|
def pre_check() -> bool:
|
||||||
@@ -40,6 +40,8 @@ def render() -> gradio.Blocks:
|
|||||||
lip_syncer_options.render()
|
lip_syncer_options.render()
|
||||||
with gradio.Blocks():
|
with gradio.Blocks():
|
||||||
voice_extractor.render()
|
voice_extractor.render()
|
||||||
|
with gradio.Blocks():
|
||||||
|
workflow.render()
|
||||||
with gradio.Blocks():
|
with gradio.Blocks():
|
||||||
execution.render()
|
execution.render()
|
||||||
execution_thread_count.render()
|
execution_thread_count.render()
|
||||||
@@ -81,8 +83,6 @@ def render() -> gradio.Blocks:
|
|||||||
face_detector.render()
|
face_detector.render()
|
||||||
with gradio.Blocks():
|
with gradio.Blocks():
|
||||||
face_landmarker.render()
|
face_landmarker.render()
|
||||||
with gradio.Blocks():
|
|
||||||
common_options.render()
|
|
||||||
return layout
|
return layout
|
||||||
|
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ def listen() -> None:
|
|||||||
face_detector.listen()
|
face_detector.listen()
|
||||||
face_landmarker.listen()
|
face_landmarker.listen()
|
||||||
voice_extractor.listen()
|
voice_extractor.listen()
|
||||||
common_options.listen()
|
workflow.listen()
|
||||||
|
|
||||||
|
|
||||||
def run(ui : gradio.Blocks) -> None:
|
def run(ui : gradio.Blocks) -> None:
|
||||||
|
|||||||
+131
-22
@@ -1,46 +1,155 @@
|
|||||||
import cv2
|
import hashlib
|
||||||
|
import uuid
|
||||||
|
from io import BufferedReader
|
||||||
|
from typing import Optional, cast
|
||||||
|
|
||||||
from facefusion.types import VideoPoolSet
|
import numpy
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffprobe, frame_store, vision
|
||||||
|
from facefusion.common_helper import get_first, get_last
|
||||||
|
from facefusion.types import Fps, Resolution, VideoPoolSet, VideoReader, VideoWriter, VisionFrame, VisionFrameSet
|
||||||
|
|
||||||
VIDEO_POOL_SET : VideoPoolSet =\
|
VIDEO_POOL_SET : VideoPoolSet =\
|
||||||
{
|
{
|
||||||
'capture': {},
|
'reader': {},
|
||||||
'writer': {}
|
'writer': {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_video_capture(video_path : str) -> cv2.VideoCapture:
|
def get_reader(video_path : str, context : str) -> VideoReader:
|
||||||
if video_path not in VIDEO_POOL_SET.get('capture'):
|
reader_id = hashlib.sha1((video_path + '_' + context).encode()).hexdigest()
|
||||||
video_capture = cv2.VideoCapture(video_path)
|
|
||||||
|
|
||||||
if video_capture.isOpened():
|
if reader_id not in VIDEO_POOL_SET.get('reader'):
|
||||||
VIDEO_POOL_SET['capture'][video_path] = video_capture
|
video_metadata = ffprobe.extract_static_video_metadata(video_path)
|
||||||
|
|
||||||
return VIDEO_POOL_SET.get('capture').get(video_path)
|
VIDEO_POOL_SET['reader'][reader_id] =\
|
||||||
|
{
|
||||||
|
'id': reader_id,
|
||||||
|
'file_path': video_path,
|
||||||
|
'process': ffmpeg.create_video_reader(video_path, 0, video_metadata),
|
||||||
|
'metadata': video_metadata,
|
||||||
|
'frame_number': 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return VIDEO_POOL_SET.get('reader').get(reader_id)
|
||||||
|
|
||||||
|
|
||||||
def conditional_set_video_frame_position(video_capture : cv2.VideoCapture, frame_position : int) -> bool:
|
def conditional_seek_video_reader(video_reader : VideoReader, frame_number : int = 0) -> None:
|
||||||
if not video_capture.get(cv2.CAP_PROP_POS_FRAMES) == frame_position:
|
frame_total = video_reader.get('metadata').get('frame_total')
|
||||||
return video_capture.set(cv2.CAP_PROP_POS_FRAMES, frame_position)
|
frame_number = min(frame_total - 1, frame_number)
|
||||||
return True
|
skip_total = frame_number - video_reader.get('frame_number')
|
||||||
|
skip_margin = 128
|
||||||
|
|
||||||
|
if 0 < skip_total <= skip_margin:
|
||||||
|
drain_video_reader(video_reader, skip_total)
|
||||||
|
|
||||||
|
if not video_reader.get('frame_number') == frame_number:
|
||||||
|
seek_video_reader(video_reader, frame_number)
|
||||||
|
|
||||||
|
|
||||||
def get_video_writer(video_path : str) -> cv2.VideoWriter:
|
def seek_video_reader(video_reader : VideoReader, frame_number : int = 0) -> None:
|
||||||
|
close_video_reader(video_reader)
|
||||||
|
|
||||||
|
video_reader['process'] = ffmpeg.create_video_reader(video_reader.get('file_path'), frame_number, video_reader.get('metadata'))
|
||||||
|
video_reader['frame_number'] = frame_number
|
||||||
|
|
||||||
|
|
||||||
|
def drain_video_reader(video_reader : VideoReader, skip_total : int) -> None:
|
||||||
|
width, height = video_reader.get('metadata').get('resolution')
|
||||||
|
channel_total = 3
|
||||||
|
frame_size = width * height * channel_total
|
||||||
|
|
||||||
|
for _ in range(skip_total):
|
||||||
|
video_reader.get('process').stdout.read(frame_size)
|
||||||
|
|
||||||
|
video_reader['frame_number'] = video_reader.get('frame_number') + skip_total
|
||||||
|
|
||||||
|
|
||||||
|
def read_video_frame(video_reader : VideoReader) -> Optional[VisionFrame]:
|
||||||
|
width, height = video_reader.get('metadata').get('resolution')
|
||||||
|
channel_total = 3
|
||||||
|
video_stream = cast(BufferedReader, video_reader.get('process').stdout)
|
||||||
|
vision_frame = numpy.empty(width * height * channel_total, numpy.uint8)
|
||||||
|
|
||||||
|
if video_stream.readinto(vision_frame) == vision_frame.size:
|
||||||
|
video_reader['frame_number'] = video_reader.get('frame_number') + 1
|
||||||
|
return vision_frame.reshape(height, width, channel_total)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def read_video_frames(video_reader : VideoReader, frame_start : int, frame_end : int) -> VisionFrameSet:
|
||||||
|
reader_id = video_reader.get('id')
|
||||||
|
frame_set = frame_store.get_frame_store(reader_id)
|
||||||
|
keep_margin = 4
|
||||||
|
frame_gaps = []
|
||||||
|
|
||||||
|
for frame_number in range(frame_start, frame_end + 1):
|
||||||
|
if frame_number not in frame_set:
|
||||||
|
frame_gaps.append(frame_number)
|
||||||
|
|
||||||
|
if frame_gaps:
|
||||||
|
collect_video_frames(video_reader, get_first(frame_gaps), get_last(frame_gaps))
|
||||||
|
|
||||||
|
frame_store.reduce_frames(reader_id, frame_start - keep_margin, frame_end + keep_margin)
|
||||||
|
return frame_store.select_frame_set(reader_id, frame_start, frame_end)
|
||||||
|
|
||||||
|
|
||||||
|
def collect_video_frames(video_reader : VideoReader, frame_start : int, frame_end : int) -> None:
|
||||||
|
reader_id = video_reader.get('id')
|
||||||
|
skip_total = frame_start - video_reader.get('frame_number')
|
||||||
|
skip_margin = 16
|
||||||
|
|
||||||
|
if skip_total < 0 or skip_total > skip_margin:
|
||||||
|
seek_video_reader(video_reader, frame_start)
|
||||||
|
|
||||||
|
for frame_number in range(video_reader.get('frame_number'), frame_end + 1):
|
||||||
|
vision_frame = read_video_frame(video_reader)
|
||||||
|
|
||||||
|
if vision.is_vision_frame(vision_frame):
|
||||||
|
frame_store.set_frame(reader_id, frame_number, vision_frame)
|
||||||
|
|
||||||
|
|
||||||
|
def close_video_reader(video_reader : VideoReader) -> None:
|
||||||
|
video_reader.get('process').kill()
|
||||||
|
video_reader.get('process').wait()
|
||||||
|
|
||||||
|
|
||||||
|
def get_writer(video_path : str, temp_video_fps : Fps, temp_video_resolution : Resolution, output_video_resolution : Resolution, output_video_fps : Fps) -> VideoWriter:
|
||||||
if video_path not in VIDEO_POOL_SET.get('writer'):
|
if video_path not in VIDEO_POOL_SET.get('writer'):
|
||||||
video_writer = cv2.VideoWriter()
|
VIDEO_POOL_SET['writer'][video_path] =\
|
||||||
|
{
|
||||||
if video_writer.isOpened():
|
'id': uuid.uuid4().hex,
|
||||||
VIDEO_POOL_SET['writer'][video_path] = video_writer
|
'file_path': video_path,
|
||||||
|
'process': ffmpeg.create_video_writer(video_path, temp_video_fps, temp_video_resolution, output_video_resolution, output_video_fps),
|
||||||
|
'metadata':
|
||||||
|
{
|
||||||
|
'fps': output_video_fps,
|
||||||
|
'resolution': output_video_resolution
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return VIDEO_POOL_SET.get('writer').get(video_path)
|
return VIDEO_POOL_SET.get('writer').get(video_path)
|
||||||
|
|
||||||
|
|
||||||
|
def write_video_frame(video_writer : VideoWriter, vision_frame : VisionFrame) -> None:
|
||||||
|
video_writer.get('process').stdin.write(vision_frame.data)
|
||||||
|
|
||||||
|
|
||||||
|
def close_video_writer(video_writer : VideoWriter) -> bool:
|
||||||
|
video_writer.get('process').stdin.close()
|
||||||
|
video_writer.get('process').wait()
|
||||||
|
|
||||||
|
return video_writer.get('process').returncode == 0
|
||||||
|
|
||||||
|
|
||||||
def clear_video_pool() -> None:
|
def clear_video_pool() -> None:
|
||||||
for video_capture in VIDEO_POOL_SET.get('capture').values():
|
for video_reader in VIDEO_POOL_SET.get('reader').values():
|
||||||
video_capture.release()
|
close_video_reader(video_reader)
|
||||||
|
frame_store.clear_frames(video_reader.get('id'))
|
||||||
|
|
||||||
for video_writer in VIDEO_POOL_SET.get('writer').values():
|
for video_writer in VIDEO_POOL_SET.get('writer').values():
|
||||||
video_writer.release()
|
close_video_writer(video_writer)
|
||||||
|
|
||||||
VIDEO_POOL_SET['capture'].clear()
|
VIDEO_POOL_SET['reader'].clear()
|
||||||
VIDEO_POOL_SET['writer'].clear()
|
VIDEO_POOL_SET['writer'].clear()
|
||||||
|
|||||||
+21
-64
@@ -1,16 +1,16 @@
|
|||||||
import math
|
import math
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from typing import Dict, List, Optional, Tuple
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
import numpy
|
import numpy
|
||||||
from cv2.typing import Size
|
from cv2.typing import Size
|
||||||
|
|
||||||
|
from facefusion import ffprobe, video_manager
|
||||||
from facefusion.common_helper import is_windows
|
from facefusion.common_helper import is_windows
|
||||||
from facefusion.filesystem import get_file_extension, is_image, is_video
|
from facefusion.filesystem import get_file_extension, is_image, is_video
|
||||||
from facefusion.thread_helper import thread_lock, thread_semaphore
|
from facefusion.thread_helper import thread_lock, thread_semaphore
|
||||||
from facefusion.types import ColorMode, Duration, Fps, Mask, Orientation, Resolution, Scale, VisionFrame
|
from facefusion.types import ColorMode, Duration, Fps, Mask, Orientation, Resolution, Scale, VisionFrame
|
||||||
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]:
|
def read_static_images(image_paths : List[str], color_mode : ColorMode = 'rgb') -> List[VisionFrame]:
|
||||||
@@ -77,61 +77,30 @@ def read_static_video_frame(video_path : str, frame_number : int = 0) -> Optiona
|
|||||||
|
|
||||||
def read_video_frame(video_path : str, frame_number : int = 0) -> Optional[VisionFrame]:
|
def read_video_frame(video_path : str, frame_number : int = 0) -> Optional[VisionFrame]:
|
||||||
if is_video(video_path):
|
if is_video(video_path):
|
||||||
video_capture = get_video_capture(video_path)
|
video_reader = video_manager.get_reader(video_path, 'read_video_frame')
|
||||||
|
|
||||||
if video_capture and video_capture.isOpened():
|
with thread_semaphore():
|
||||||
video_frame_total = int(video_capture.get(cv2.CAP_PROP_FRAME_COUNT))
|
video_manager.conditional_seek_video_reader(video_reader, frame_number)
|
||||||
video_frame_position = min(video_frame_total, frame_number)
|
return video_manager.read_video_frame(video_reader)
|
||||||
|
|
||||||
with thread_semaphore():
|
|
||||||
conditional_set_video_frame_position(video_capture, video_frame_position)
|
|
||||||
has_vision_frame, vision_frame = video_capture.read()
|
|
||||||
|
|
||||||
if has_vision_frame:
|
|
||||||
return vision_frame
|
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize = 2)
|
|
||||||
def read_static_video_chunk(video_path : str, chunk_number : int, chunk_size : int) -> Dict[int, VisionFrame]:
|
|
||||||
return read_video_chunk(video_path, chunk_number, chunk_size)
|
|
||||||
|
|
||||||
|
|
||||||
def read_video_chunk(video_path : str, chunk_number : int, chunk_size : int) -> Dict[int, VisionFrame]:
|
|
||||||
video_frame_chunk = {}
|
|
||||||
|
|
||||||
if is_video(video_path) and chunk_number > -1:
|
|
||||||
video_capture = get_video_capture(video_path)
|
|
||||||
|
|
||||||
if video_capture and video_capture.isOpened():
|
|
||||||
video_frame_total = int(video_capture.get(cv2.CAP_PROP_FRAME_COUNT))
|
|
||||||
video_frame_position = chunk_number * chunk_size
|
|
||||||
|
|
||||||
with thread_semaphore():
|
|
||||||
conditional_set_video_frame_position(video_capture, video_frame_position)
|
|
||||||
|
|
||||||
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:
|
|
||||||
video_frame_chunk[frame_number] = vision_frame
|
|
||||||
|
|
||||||
return video_frame_chunk
|
|
||||||
|
|
||||||
|
|
||||||
def select_video_frames(video_path : str, frame_number : int = 0, frame_offset : int = 2) -> List[VisionFrame]:
|
def select_video_frames(video_path : str, frame_number : int = 0, frame_offset : int = 2) -> List[VisionFrame]:
|
||||||
vision_frames = []
|
vision_frames = []
|
||||||
chunk_size = frame_offset * 2 + 1
|
frame_start = frame_number - frame_offset
|
||||||
|
frame_end = frame_number + frame_offset
|
||||||
|
|
||||||
if is_video(video_path):
|
if is_video(video_path):
|
||||||
with thread_lock():
|
with thread_lock():
|
||||||
for current_number in range(frame_number - frame_offset, frame_number + frame_offset + 1):
|
video_reader = video_manager.get_reader(video_path, 'select_video_frames')
|
||||||
video_frame_chunk = read_static_video_chunk(video_path, current_number // chunk_size, chunk_size)
|
frame_set = video_manager.read_video_frames(video_reader, max(frame_start, 0), frame_end)
|
||||||
|
|
||||||
|
for frame_number in range(frame_start, frame_end + 1):
|
||||||
vision_frame = create_empty_vision_frame()
|
vision_frame = create_empty_vision_frame()
|
||||||
|
|
||||||
if current_number in video_frame_chunk:
|
if frame_number in frame_set:
|
||||||
vision_frame = video_frame_chunk.get(current_number)
|
vision_frame = frame_set.get(frame_number)
|
||||||
|
|
||||||
vision_frames.append(vision_frame)
|
vision_frames.append(vision_frame)
|
||||||
|
|
||||||
@@ -140,12 +109,7 @@ def select_video_frames(video_path : str, frame_number : int = 0, frame_offset :
|
|||||||
|
|
||||||
def count_video_frame_total(video_path : str) -> int:
|
def count_video_frame_total(video_path : str) -> int:
|
||||||
if is_video(video_path):
|
if is_video(video_path):
|
||||||
video_capture = get_video_capture(video_path)
|
return ffprobe.extract_static_video_metadata(video_path).get('frame_total')
|
||||||
|
|
||||||
if video_capture and video_capture.isOpened():
|
|
||||||
with thread_semaphore():
|
|
||||||
video_frame_total = int(video_capture.get(cv2.CAP_PROP_FRAME_COUNT))
|
|
||||||
return video_frame_total
|
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@@ -160,12 +124,7 @@ def predict_video_frame_total(video_path : str, fps : Fps, trim_frame_start : in
|
|||||||
|
|
||||||
def detect_video_fps(video_path : str) -> Optional[float]:
|
def detect_video_fps(video_path : str) -> Optional[float]:
|
||||||
if is_video(video_path):
|
if is_video(video_path):
|
||||||
video_capture = get_video_capture(video_path)
|
return ffprobe.extract_static_video_metadata(video_path).get('fps')
|
||||||
|
|
||||||
if video_capture and video_capture.isOpened():
|
|
||||||
with thread_semaphore():
|
|
||||||
video_fps = video_capture.get(cv2.CAP_PROP_FPS)
|
|
||||||
return video_fps
|
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -213,13 +172,7 @@ def restrict_trim_frame(video_path : str, trim_frame_start : Optional[int], trim
|
|||||||
|
|
||||||
def detect_video_resolution(video_path : str) -> Optional[Resolution]:
|
def detect_video_resolution(video_path : str) -> Optional[Resolution]:
|
||||||
if is_video(video_path):
|
if is_video(video_path):
|
||||||
video_capture = get_video_capture(video_path)
|
return ffprobe.extract_static_video_metadata(video_path).get('resolution')
|
||||||
|
|
||||||
if video_capture and video_capture.isOpened():
|
|
||||||
with thread_semaphore():
|
|
||||||
width = video_capture.get(cv2.CAP_PROP_FRAME_WIDTH)
|
|
||||||
height = video_capture.get(cv2.CAP_PROP_FRAME_HEIGHT)
|
|
||||||
return int(width), int(height)
|
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -357,6 +310,10 @@ def create_empty_vision_frame() -> VisionFrame:
|
|||||||
return numpy.zeros((1, 1, 3)).astype(numpy.uint8)
|
return numpy.zeros((1, 1, 3)).astype(numpy.uint8)
|
||||||
|
|
||||||
|
|
||||||
|
def is_vision_frame(vision_frame : VisionFrame) -> bool:
|
||||||
|
return numpy.ndim(vision_frame) == 3
|
||||||
|
|
||||||
|
|
||||||
def create_tile_frames(vision_frame : VisionFrame, size : Size) -> Tuple[List[VisionFrame], int, int]:
|
def create_tile_frames(vision_frame : VisionFrame, size : Size) -> Tuple[List[VisionFrame], int, int]:
|
||||||
tile_width = size[0] - 2 * size[2]
|
tile_width = size[0] - 2 * size[2]
|
||||||
pad_size_top = size[1] + size[2]
|
pad_size_top = size[1] + size[2]
|
||||||
|
|||||||
@@ -1,4 +1,15 @@
|
|||||||
from facefusion import logger, process_manager, translator
|
from typing import List
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
|
||||||
|
from facefusion import logger, process_manager, state_manager, translator
|
||||||
|
from facefusion.audio import create_empty_audio_frame, get_audio_frame, get_voice_frame
|
||||||
|
from facefusion.common_helper import get_first
|
||||||
|
from facefusion.filesystem import filter_audio_paths
|
||||||
|
from facefusion.processors.core import get_processors_modules
|
||||||
|
from facefusion.temp_helper import clear_temp_directory, create_temp_directory
|
||||||
|
from facefusion.types import AudioFrame, ErrorCode, VisionFrame
|
||||||
|
from facefusion.vision import conditional_merge_vision_mask, extract_vision_mask, read_static_image, read_static_images, read_static_video_frame, restrict_trim_frame, restrict_video_fps, select_video_frames
|
||||||
|
|
||||||
|
|
||||||
def is_process_stopping() -> bool:
|
def is_process_stopping() -> bool:
|
||||||
@@ -6,3 +17,75 @@ def is_process_stopping() -> bool:
|
|||||||
process_manager.end()
|
process_manager.end()
|
||||||
logger.info(translator.get('processing_stopped'), __name__)
|
logger.info(translator.get('processing_stopped'), __name__)
|
||||||
return process_manager.is_pending()
|
return process_manager.is_pending()
|
||||||
|
|
||||||
|
|
||||||
|
def setup() -> ErrorCode:
|
||||||
|
if create_temp_directory(state_manager.get_item('target_path')):
|
||||||
|
logger.debug(translator.get('creating_temp'), __name__)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def clear() -> ErrorCode:
|
||||||
|
if clear_temp_directory(state_manager.get_item('target_path')):
|
||||||
|
logger.debug(translator.get('clearing_temp'), __name__)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def conditional_get_reference_vision_frame() -> VisionFrame:
|
||||||
|
if state_manager.get_item('workflow_mode') == 'image-to-video':
|
||||||
|
return read_static_video_frame(state_manager.get_item('target_path'), state_manager.get_item('reference_frame_number'))
|
||||||
|
return read_static_image(state_manager.get_item('target_path'))
|
||||||
|
|
||||||
|
|
||||||
|
def conditional_get_source_audio_frame(frame_number : int) -> AudioFrame:
|
||||||
|
if state_manager.get_item('workflow_mode') == 'image-to-video':
|
||||||
|
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'))
|
||||||
|
temp_video_fps = restrict_video_fps(state_manager.get_item('target_path'), state_manager.get_item('output_video_fps'))
|
||||||
|
source_audio_path = get_first(filter_audio_paths(state_manager.get_item('source_paths')))
|
||||||
|
source_audio_frame = get_audio_frame(source_audio_path, temp_video_fps, frame_number - trim_frame_start)
|
||||||
|
|
||||||
|
if numpy.any(source_audio_frame):
|
||||||
|
return source_audio_frame
|
||||||
|
|
||||||
|
return create_empty_audio_frame()
|
||||||
|
|
||||||
|
|
||||||
|
def conditional_get_source_voice_frame(frame_number : int) -> AudioFrame:
|
||||||
|
if state_manager.get_item('workflow_mode') == 'image-to-video':
|
||||||
|
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'))
|
||||||
|
temp_video_fps = restrict_video_fps(state_manager.get_item('target_path'), state_manager.get_item('output_video_fps'))
|
||||||
|
source_audio_path = get_first(filter_audio_paths(state_manager.get_item('source_paths')))
|
||||||
|
source_voice_frame = get_voice_frame(source_audio_path, temp_video_fps, frame_number - trim_frame_start)
|
||||||
|
|
||||||
|
if numpy.any(source_voice_frame):
|
||||||
|
return source_voice_frame
|
||||||
|
|
||||||
|
return create_empty_audio_frame()
|
||||||
|
|
||||||
|
|
||||||
|
def conditional_get_target_vision_frames(frame_number : int) -> List[VisionFrame]:
|
||||||
|
if state_manager.get_item('workflow_mode') == 'image-to-video':
|
||||||
|
return select_video_frames(state_manager.get_item('target_path'), frame_number, state_manager.get_item('target_frame_amount'))
|
||||||
|
return [ read_static_image(state_manager.get_item('target_path')) ]
|
||||||
|
|
||||||
|
|
||||||
|
def process_temp_frame(target_vision_frames : List[VisionFrame], temp_vision_frame : VisionFrame, frame_number : int) -> VisionFrame:
|
||||||
|
reference_vision_frame = conditional_get_reference_vision_frame()
|
||||||
|
source_vision_frames = read_static_images(state_manager.get_item('source_paths'))
|
||||||
|
source_audio_frame = conditional_get_source_audio_frame(frame_number)
|
||||||
|
source_voice_frame = conditional_get_source_voice_frame(frame_number)
|
||||||
|
temp_vision_mask = extract_vision_mask(temp_vision_frame)
|
||||||
|
|
||||||
|
for processor_module in get_processors_modules(state_manager.get_item('processors')):
|
||||||
|
temp_vision_frame, temp_vision_mask = processor_module.process_frame(
|
||||||
|
{
|
||||||
|
'reference_vision_frame': reference_vision_frame,
|
||||||
|
'source_vision_frames': source_vision_frames,
|
||||||
|
'source_audio_frame': source_audio_frame,
|
||||||
|
'source_voice_frame': source_voice_frame,
|
||||||
|
'target_vision_frames': target_vision_frames,
|
||||||
|
'temp_vision_frame': temp_vision_frame[:, :, :3],
|
||||||
|
'temp_vision_mask': temp_vision_mask
|
||||||
|
})
|
||||||
|
|
||||||
|
return conditional_merge_vision_mask(temp_vision_frame, temp_vision_mask)
|
||||||
|
|||||||
@@ -1,25 +1,21 @@
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from facefusion import ffmpeg
|
from facefusion import process_manager
|
||||||
from facefusion import logger, process_manager, state_manager, translator
|
|
||||||
from facefusion.audio import create_empty_audio_frame
|
|
||||||
from facefusion.content_analyser import analyse_image
|
|
||||||
from facefusion.filesystem import is_image
|
|
||||||
from facefusion.processors.core import get_processors_modules
|
|
||||||
from facefusion.temp_helper import clear_temp_directory, create_temp_directory, get_temp_file_path
|
|
||||||
from facefusion.time_helper import calculate_end_time
|
|
||||||
from facefusion.types import ErrorCode
|
from facefusion.types import ErrorCode
|
||||||
from facefusion.vision import conditional_merge_vision_mask, detect_image_resolution, extract_vision_mask, pack_resolution, read_static_image, read_static_images, restrict_image_resolution, scale_resolution, write_image
|
from facefusion.workflows.core import clear, setup
|
||||||
from facefusion.workflows.core import is_process_stopping
|
from facefusion.workflows.to_image import analyse_image, finalize_image, prepare_image, process_image
|
||||||
|
|
||||||
|
|
||||||
def process(start_time : float) -> ErrorCode:
|
def process(start_time : float) -> ErrorCode:
|
||||||
tasks =\
|
tasks =\
|
||||||
[
|
[
|
||||||
|
analyse_image,
|
||||||
|
clear,
|
||||||
setup,
|
setup,
|
||||||
prepare_image,
|
prepare_image,
|
||||||
process_image,
|
process_image,
|
||||||
partial(finalize_image, start_time)
|
partial(finalize_image, start_time),
|
||||||
|
clear
|
||||||
]
|
]
|
||||||
process_manager.start()
|
process_manager.start()
|
||||||
|
|
||||||
@@ -32,84 +28,3 @@ def process(start_time : float) -> ErrorCode:
|
|||||||
|
|
||||||
process_manager.end()
|
process_manager.end()
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def setup() -> ErrorCode:
|
|
||||||
if analyse_image(state_manager.get_item('target_path')):
|
|
||||||
return 3
|
|
||||||
|
|
||||||
if clear_temp_directory(state_manager.get_item('target_path')):
|
|
||||||
logger.debug(translator.get('clearing_temp'), __name__)
|
|
||||||
|
|
||||||
if create_temp_directory(state_manager.get_item('target_path')):
|
|
||||||
logger.debug(translator.get('creating_temp'), __name__)
|
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def prepare_image() -> ErrorCode:
|
|
||||||
output_image_resolution = scale_resolution(detect_image_resolution(state_manager.get_item('target_path')), state_manager.get_item('output_image_scale'))
|
|
||||||
temp_image_resolution = restrict_image_resolution(state_manager.get_item('target_path'), output_image_resolution)
|
|
||||||
|
|
||||||
logger.info(translator.get('copying_image').format(resolution = pack_resolution(temp_image_resolution)), __name__)
|
|
||||||
if ffmpeg.copy_image(state_manager.get_item('target_path'), temp_image_resolution):
|
|
||||||
logger.debug(translator.get('copying_image_succeeded'), __name__)
|
|
||||||
else:
|
|
||||||
logger.error(translator.get('copying_image_failed'), __name__)
|
|
||||||
process_manager.end()
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def process_image() -> ErrorCode:
|
|
||||||
temp_image_path = get_temp_file_path(state_manager.get_item('target_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)
|
|
||||||
|
|
||||||
for processor_module in get_processors_modules(state_manager.get_item('processors')):
|
|
||||||
logger.info(translator.get('processing'), processor_module.__name__)
|
|
||||||
|
|
||||||
temp_vision_frame, temp_vision_mask = processor_module.process_frame(
|
|
||||||
{
|
|
||||||
'reference_vision_frame': reference_vision_frame,
|
|
||||||
'source_vision_frames': source_vision_frames,
|
|
||||||
'source_audio_frame': source_audio_frame,
|
|
||||||
'source_voice_frame': source_voice_frame,
|
|
||||||
'target_vision_frames': [ target_vision_frame ],
|
|
||||||
'temp_vision_frame': temp_vision_frame[:, :, :3],
|
|
||||||
'temp_vision_mask': temp_vision_mask
|
|
||||||
})
|
|
||||||
|
|
||||||
processor_module.post_process()
|
|
||||||
|
|
||||||
temp_vision_frame = conditional_merge_vision_mask(temp_vision_frame, temp_vision_mask)
|
|
||||||
write_image(temp_image_path, temp_vision_frame)
|
|
||||||
|
|
||||||
if is_process_stopping():
|
|
||||||
return 4
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def finalize_image(start_time : float) -> ErrorCode:
|
|
||||||
output_image_resolution = scale_resolution(detect_image_resolution(state_manager.get_item('target_path')), state_manager.get_item('output_image_scale'))
|
|
||||||
|
|
||||||
logger.info(translator.get('finalizing_image').format(resolution = pack_resolution(output_image_resolution)), __name__)
|
|
||||||
if ffmpeg.finalize_image(state_manager.get_item('target_path'), state_manager.get_item('output_path'), output_image_resolution):
|
|
||||||
logger.debug(translator.get('finalizing_image_succeeded'), __name__)
|
|
||||||
else:
|
|
||||||
logger.warn(translator.get('finalizing_image_skipped'), __name__)
|
|
||||||
|
|
||||||
logger.debug(translator.get('clearing_temp'), __name__)
|
|
||||||
clear_temp_directory(state_manager.get_item('target_path'))
|
|
||||||
|
|
||||||
if is_image(state_manager.get_item('output_path')):
|
|
||||||
logger.info(translator.get('processing_image_succeeded').format(seconds = calculate_end_time(start_time)), __name__)
|
|
||||||
else:
|
|
||||||
logger.error(translator.get('processing_image_failed'), __name__)
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
|
|||||||
@@ -1,33 +1,36 @@
|
|||||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
import numpy
|
from facefusion import process_manager, state_manager
|
||||||
from tqdm import tqdm
|
|
||||||
|
|
||||||
from facefusion import ffmpeg
|
|
||||||
from facefusion import logger, process_manager, state_manager, translator, video_manager
|
|
||||||
from facefusion.audio import create_empty_audio_frame, get_audio_frame, get_voice_frame
|
|
||||||
from facefusion.common_helper import get_first
|
|
||||||
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, resolve_temp_frame_set
|
|
||||||
from facefusion.time_helper import calculate_end_time
|
|
||||||
from facefusion.types import ErrorCode
|
from facefusion.types import ErrorCode
|
||||||
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 clear, setup
|
||||||
from facefusion.workflows.core import is_process_stopping
|
from facefusion.workflows.to_video import analyse_video, extract_frames, finalize_video, merge_frames, process_disk_frames, process_stream_frames, restore_audio
|
||||||
|
|
||||||
|
|
||||||
def process(start_time : float) -> ErrorCode:
|
def process(start_time : float) -> ErrorCode:
|
||||||
tasks =\
|
tasks =\
|
||||||
[
|
[
|
||||||
setup,
|
analyse_video,
|
||||||
extract_frames,
|
clear,
|
||||||
process_video,
|
setup
|
||||||
merge_frames,
|
|
||||||
restore_audio,
|
|
||||||
partial(finalize_video, start_time)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if state_manager.get_item('workflow_strategy') == 'disk':
|
||||||
|
tasks.extend(
|
||||||
|
[
|
||||||
|
extract_frames,
|
||||||
|
process_disk_frames,
|
||||||
|
merge_frames
|
||||||
|
])
|
||||||
|
|
||||||
|
if state_manager.get_item('workflow_strategy') == 'stream':
|
||||||
|
tasks.append(process_stream_frames)
|
||||||
|
|
||||||
|
tasks.extend(
|
||||||
|
[
|
||||||
|
restore_audio,
|
||||||
|
partial(finalize_video, start_time),
|
||||||
|
clear
|
||||||
|
])
|
||||||
process_manager.start()
|
process_manager.start()
|
||||||
|
|
||||||
for task in tasks:
|
for task in tasks:
|
||||||
@@ -39,162 +42,3 @@ def process(start_time : float) -> ErrorCode:
|
|||||||
|
|
||||||
process_manager.end()
|
process_manager.end()
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def setup() -> 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'))
|
|
||||||
|
|
||||||
if analyse_video(state_manager.get_item('target_path'), trim_frame_start, trim_frame_end):
|
|
||||||
return 3
|
|
||||||
|
|
||||||
if clear_temp_directory(state_manager.get_item('target_path')):
|
|
||||||
logger.debug(translator.get('clearing_temp'), __name__)
|
|
||||||
|
|
||||||
if create_temp_directory(state_manager.get_item('target_path')):
|
|
||||||
logger.debug(translator.get('creating_temp'), __name__)
|
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def extract_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)
|
|
||||||
temp_video_fps = restrict_video_fps(state_manager.get_item('target_path'), state_manager.get_item('output_video_fps'))
|
|
||||||
logger.info(translator.get('extracting_frames').format(resolution=pack_resolution(temp_video_resolution), fps=temp_video_fps), __name__)
|
|
||||||
|
|
||||||
if ffmpeg.extract_frames(state_manager.get_item('target_path'), temp_video_resolution, temp_video_fps, trim_frame_start, trim_frame_end):
|
|
||||||
logger.debug(translator.get('extracting_frames_succeeded'), __name__)
|
|
||||||
else:
|
|
||||||
if is_process_stopping():
|
|
||||||
return 4
|
|
||||||
logger.error(translator.get('extracting_frames_failed'), __name__)
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def process_video() -> 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():
|
|
||||||
future = executor.submit(process_temp_frame, temp_frame_path, frame_number)
|
|
||||||
futures.append(future)
|
|
||||||
|
|
||||||
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 merge_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_fps = restrict_video_fps(state_manager.get_item('target_path'), state_manager.get_item('output_video_fps'))
|
|
||||||
|
|
||||||
logger.info(translator.get('merging_video').format(resolution = pack_resolution(output_video_resolution), fps = state_manager.get_item('output_video_fps')), __name__)
|
|
||||||
if ffmpeg.merge_video(state_manager.get_item('target_path'), temp_video_fps, output_video_resolution, state_manager.get_item('output_video_fps'), trim_frame_start, trim_frame_end):
|
|
||||||
logger.debug(translator.get('merging_video_succeeded'), __name__)
|
|
||||||
else:
|
|
||||||
if is_process_stopping():
|
|
||||||
return 4
|
|
||||||
logger.error(translator.get('merging_video_failed'), __name__)
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def restore_audio() -> 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'))
|
|
||||||
|
|
||||||
if state_manager.get_item('output_audio_volume') == 0:
|
|
||||||
logger.info(translator.get('skipping_audio'), __name__)
|
|
||||||
move_temp_file(state_manager.get_item('target_path'), state_manager.get_item('output_path'))
|
|
||||||
else:
|
|
||||||
source_audio_path = get_first(filter_audio_paths(state_manager.get_item('source_paths')))
|
|
||||||
if source_audio_path:
|
|
||||||
if ffmpeg.replace_audio(state_manager.get_item('target_path'), source_audio_path, state_manager.get_item('output_path')):
|
|
||||||
video_manager.clear_video_pool()
|
|
||||||
logger.debug(translator.get('replacing_audio_succeeded'), __name__)
|
|
||||||
else:
|
|
||||||
video_manager.clear_video_pool()
|
|
||||||
if is_process_stopping():
|
|
||||||
return 4
|
|
||||||
logger.warn(translator.get('replacing_audio_skipped'), __name__)
|
|
||||||
move_temp_file(state_manager.get_item('target_path'), state_manager.get_item('output_path'))
|
|
||||||
else:
|
|
||||||
if ffmpeg.restore_audio(state_manager.get_item('target_path'), state_manager.get_item('output_path'), trim_frame_start, trim_frame_end):
|
|
||||||
video_manager.clear_video_pool()
|
|
||||||
logger.debug(translator.get('restoring_audio_succeeded'), __name__)
|
|
||||||
else:
|
|
||||||
video_manager.clear_video_pool()
|
|
||||||
if is_process_stopping():
|
|
||||||
return 4
|
|
||||||
logger.warn(translator.get('restoring_audio_skipped'), __name__)
|
|
||||||
move_temp_file(state_manager.get_item('target_path'), state_manager.get_item('output_path'))
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def process_temp_frame(temp_frame_path : str, frame_number : int) -> bool:
|
|
||||||
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'))
|
|
||||||
temp_video_fps = restrict_video_fps(state_manager.get_item('target_path'), state_manager.get_item('output_video_fps'))
|
|
||||||
temp_vision_frame = read_static_image(temp_frame_path, 'rgba')
|
|
||||||
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)
|
|
||||||
source_voice_frame = get_voice_frame(source_audio_path, temp_video_fps, frame_number - trim_frame_start)
|
|
||||||
|
|
||||||
if not numpy.any(source_audio_frame):
|
|
||||||
source_audio_frame = create_empty_audio_frame()
|
|
||||||
if not numpy.any(source_voice_frame):
|
|
||||||
source_voice_frame = create_empty_audio_frame()
|
|
||||||
|
|
||||||
for processor_module in get_processors_modules(state_manager.get_item('processors')):
|
|
||||||
temp_vision_frame, temp_vision_mask = processor_module.process_frame(
|
|
||||||
{
|
|
||||||
'reference_vision_frame': reference_vision_frame,
|
|
||||||
'source_vision_frames': source_vision_frames,
|
|
||||||
'source_audio_frame': source_audio_frame,
|
|
||||||
'source_voice_frame': source_voice_frame,
|
|
||||||
'target_vision_frames': target_vision_frames,
|
|
||||||
'temp_vision_frame': temp_vision_frame[:, :, :3],
|
|
||||||
'temp_vision_mask': temp_vision_mask
|
|
||||||
})
|
|
||||||
|
|
||||||
temp_vision_frame = conditional_merge_vision_mask(temp_vision_frame, temp_vision_mask)
|
|
||||||
return write_image(temp_frame_path, temp_vision_frame)
|
|
||||||
|
|
||||||
|
|
||||||
def finalize_video(start_time : float) -> ErrorCode:
|
|
||||||
logger.debug(translator.get('clearing_temp'), __name__)
|
|
||||||
clear_temp_directory(state_manager.get_item('target_path'))
|
|
||||||
|
|
||||||
if is_video(state_manager.get_item('output_path')):
|
|
||||||
logger.info(translator.get('processing_video_succeeded').format(seconds = calculate_end_time(start_time)), __name__)
|
|
||||||
else:
|
|
||||||
logger.error(translator.get('processing_video_failed'), __name__)
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
from facefusion import content_analyser, ffmpeg, logger, process_manager, state_manager, translator
|
||||||
|
from facefusion.filesystem import is_image
|
||||||
|
from facefusion.processors.core import get_processors_modules
|
||||||
|
from facefusion.temp_helper import get_temp_file_path
|
||||||
|
from facefusion.time_helper import calculate_end_time
|
||||||
|
from facefusion.types import ErrorCode
|
||||||
|
from facefusion.vision import detect_image_resolution, pack_resolution, read_static_image, restrict_image_resolution, scale_resolution, write_image
|
||||||
|
from facefusion.workflows.core import conditional_get_target_vision_frames, is_process_stopping, process_temp_frame
|
||||||
|
|
||||||
|
|
||||||
|
def analyse_image() -> ErrorCode:
|
||||||
|
if content_analyser.analyse_image(state_manager.get_item('target_path')):
|
||||||
|
return 3
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_image() -> ErrorCode:
|
||||||
|
output_image_resolution = scale_resolution(detect_image_resolution(state_manager.get_item('target_path')), state_manager.get_item('output_image_scale'))
|
||||||
|
temp_image_resolution = restrict_image_resolution(state_manager.get_item('target_path'), output_image_resolution)
|
||||||
|
|
||||||
|
logger.info(translator.get('copying_image').format(resolution = pack_resolution(temp_image_resolution)), __name__)
|
||||||
|
if ffmpeg.copy_image(state_manager.get_item('target_path'), temp_image_resolution):
|
||||||
|
logger.debug(translator.get('copying_image_succeeded'), __name__)
|
||||||
|
else:
|
||||||
|
logger.error(translator.get('copying_image_failed'), __name__)
|
||||||
|
process_manager.end()
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def process_image() -> ErrorCode:
|
||||||
|
temp_image_path = get_temp_file_path(state_manager.get_item('target_path'))
|
||||||
|
target_vision_frames = conditional_get_target_vision_frames(0)
|
||||||
|
temp_vision_frame = read_static_image(temp_image_path, 'rgba')
|
||||||
|
temp_vision_frame = process_temp_frame(target_vision_frames, temp_vision_frame, 0)
|
||||||
|
write_image(temp_image_path, temp_vision_frame)
|
||||||
|
|
||||||
|
for processor_module in get_processors_modules(state_manager.get_item('processors')):
|
||||||
|
processor_module.post_process()
|
||||||
|
|
||||||
|
if is_process_stopping():
|
||||||
|
return 4
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def finalize_image(start_time : float) -> ErrorCode:
|
||||||
|
output_image_resolution = scale_resolution(detect_image_resolution(state_manager.get_item('target_path')), state_manager.get_item('output_image_scale'))
|
||||||
|
|
||||||
|
logger.info(translator.get('finalizing_image').format(resolution = pack_resolution(output_image_resolution)), __name__)
|
||||||
|
if ffmpeg.finalize_image(state_manager.get_item('target_path'), state_manager.get_item('output_path'), output_image_resolution):
|
||||||
|
logger.debug(translator.get('finalizing_image_succeeded'), __name__)
|
||||||
|
else:
|
||||||
|
logger.warn(translator.get('finalizing_image_skipped'), __name__)
|
||||||
|
|
||||||
|
if is_image(state_manager.get_item('output_path')):
|
||||||
|
logger.info(translator.get('processing_image_succeeded').format(seconds = calculate_end_time(start_time)), __name__)
|
||||||
|
else:
|
||||||
|
logger.error(translator.get('processing_image_failed'), __name__)
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
import numpy
|
||||||
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
from facefusion import content_analyser, ffmpeg, logger, process_manager, state_manager, translator, video_manager
|
||||||
|
from facefusion.common_helper import get_first, get_middle
|
||||||
|
from facefusion.filesystem import filter_audio_paths, is_video
|
||||||
|
from facefusion.processors.core import get_processors_modules
|
||||||
|
from facefusion.temp_helper import 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 detect_video_resolution, pack_resolution, read_static_image, 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 conditional_get_target_vision_frames, is_process_stopping, process_temp_frame
|
||||||
|
|
||||||
|
|
||||||
|
def analyse_video() -> 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'))
|
||||||
|
|
||||||
|
if content_analyser.analyse_video(state_manager.get_item('target_path'), trim_frame_start, trim_frame_end):
|
||||||
|
return 3
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def extract_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)
|
||||||
|
temp_video_fps = restrict_video_fps(state_manager.get_item('target_path'), state_manager.get_item('output_video_fps'))
|
||||||
|
logger.info(translator.get('extracting_frames').format(resolution=pack_resolution(temp_video_resolution), fps=temp_video_fps), __name__)
|
||||||
|
|
||||||
|
if ffmpeg.extract_frames(state_manager.get_item('target_path'), temp_video_resolution, temp_video_fps, trim_frame_start, trim_frame_end):
|
||||||
|
logger.debug(translator.get('extracting_frames_succeeded'), __name__)
|
||||||
|
else:
|
||||||
|
if is_process_stopping():
|
||||||
|
return 4
|
||||||
|
logger.error(translator.get('extracting_frames_failed'), __name__)
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def process_disk_frame(temp_frame_path : str, frame_number : int) -> bool:
|
||||||
|
target_vision_frames = conditional_get_target_vision_frames(frame_number)
|
||||||
|
temp_vision_frame = read_static_image(temp_frame_path, 'rgba')
|
||||||
|
temp_vision_frame = process_temp_frame(target_vision_frames, temp_vision_frame, frame_number)
|
||||||
|
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'))
|
||||||
|
|
||||||
|
read_static_video_frame(state_manager.get_item('target_path'), state_manager.get_item('reference_frame_number'))
|
||||||
|
|
||||||
|
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():
|
||||||
|
future = executor.submit(process_disk_frame, temp_frame_path, frame_number)
|
||||||
|
futures.append(future)
|
||||||
|
|
||||||
|
for future in as_completed(futures):
|
||||||
|
if is_process_stopping():
|
||||||
|
for pending_future in futures:
|
||||||
|
pending_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_temp_frame(target_vision_frames, temp_vision_frame, frame_number)
|
||||||
|
|
||||||
|
if state_manager.get_item('temp_pixel_format') == 'bgra':
|
||||||
|
temp_vision_frame = cv2.cvtColor(temp_vision_frame, cv2.COLOR_BGR2BGRA)
|
||||||
|
|
||||||
|
if state_manager.get_item('temp_pixel_format') == 'bgr24':
|
||||||
|
temp_vision_frame = temp_vision_frame[:, :, :3]
|
||||||
|
|
||||||
|
return frame_number, numpy.ascontiguousarray(temp_vision_frame)
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
temp_video_fps = restrict_video_fps(state_manager.get_item('target_path'), state_manager.get_item('output_video_fps'))
|
||||||
|
temp_frame_range = range(trim_frame_start, trim_frame_end)
|
||||||
|
|
||||||
|
if temp_frame_range:
|
||||||
|
video_writer = video_manager.get_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(temp_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'))
|
||||||
|
|
||||||
|
read_static_video_frame(state_manager.get_item('target_path'), state_manager.get_item('reference_frame_number'))
|
||||||
|
|
||||||
|
with ThreadPoolExecutor(max_workers = state_manager.get_item('execution_thread_count')) as executor:
|
||||||
|
futures = []
|
||||||
|
|
||||||
|
for frame_number in temp_frame_range:
|
||||||
|
future = executor.submit(process_stream_frame, frame_number, temp_video_resolution)
|
||||||
|
futures.append(future)
|
||||||
|
|
||||||
|
for future in futures:
|
||||||
|
if is_process_stopping():
|
||||||
|
for pending_future in futures:
|
||||||
|
pending_future.cancel()
|
||||||
|
|
||||||
|
if not future.cancelled():
|
||||||
|
_, temp_vision_frame = future.result()
|
||||||
|
video_manager.write_video_frame(video_writer, temp_vision_frame)
|
||||||
|
progress.update()
|
||||||
|
|
||||||
|
if not video_manager.close_video_writer(video_writer):
|
||||||
|
process_manager.stop()
|
||||||
|
|
||||||
|
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 merge_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_fps = restrict_video_fps(state_manager.get_item('target_path'), state_manager.get_item('output_video_fps'))
|
||||||
|
|
||||||
|
logger.info(translator.get('merging_video').format(resolution = pack_resolution(output_video_resolution), fps = state_manager.get_item('output_video_fps')), __name__)
|
||||||
|
if ffmpeg.merge_video(state_manager.get_item('target_path'), temp_video_fps, output_video_resolution, state_manager.get_item('output_video_fps'), trim_frame_start, trim_frame_end):
|
||||||
|
logger.debug(translator.get('merging_video_succeeded'), __name__)
|
||||||
|
else:
|
||||||
|
if is_process_stopping():
|
||||||
|
return 4
|
||||||
|
logger.error(translator.get('merging_video_failed'), __name__)
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def restore_audio() -> 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'))
|
||||||
|
|
||||||
|
if state_manager.get_item('output_audio_volume') == 0:
|
||||||
|
logger.info(translator.get('skipping_audio'), __name__)
|
||||||
|
move_temp_file(state_manager.get_item('target_path'), state_manager.get_item('output_path'))
|
||||||
|
else:
|
||||||
|
source_audio_path = get_first(filter_audio_paths(state_manager.get_item('source_paths')))
|
||||||
|
if source_audio_path:
|
||||||
|
if ffmpeg.replace_audio(state_manager.get_item('target_path'), source_audio_path, state_manager.get_item('output_path')):
|
||||||
|
video_manager.clear_video_pool()
|
||||||
|
logger.debug(translator.get('replacing_audio_succeeded'), __name__)
|
||||||
|
else:
|
||||||
|
video_manager.clear_video_pool()
|
||||||
|
if is_process_stopping():
|
||||||
|
return 4
|
||||||
|
logger.warn(translator.get('replacing_audio_skipped'), __name__)
|
||||||
|
move_temp_file(state_manager.get_item('target_path'), state_manager.get_item('output_path'))
|
||||||
|
else:
|
||||||
|
if ffmpeg.restore_audio(state_manager.get_item('target_path'), state_manager.get_item('output_path'), trim_frame_start, trim_frame_end):
|
||||||
|
video_manager.clear_video_pool()
|
||||||
|
logger.debug(translator.get('restoring_audio_succeeded'), __name__)
|
||||||
|
else:
|
||||||
|
video_manager.clear_video_pool()
|
||||||
|
if is_process_stopping():
|
||||||
|
return 4
|
||||||
|
logger.warn(translator.get('restoring_audio_skipped'), __name__)
|
||||||
|
move_temp_file(state_manager.get_item('target_path'), state_manager.get_item('output_path'))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def finalize_video(start_time : float) -> ErrorCode:
|
||||||
|
if is_video(state_manager.get_item('output_path')):
|
||||||
|
logger.info(translator.get('processing_video_succeeded').format(seconds = calculate_end_time(start_time)), __name__)
|
||||||
|
else:
|
||||||
|
logger.error(translator.get('processing_video_failed'), __name__)
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
+9
-2
@@ -1,7 +1,7 @@
|
|||||||
import subprocess
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager
|
||||||
from facefusion.audio import get_audio_frame, read_static_audio
|
from facefusion.audio import get_audio_frame, read_static_audio
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from .helper import get_test_example_file, get_test_examples_directory
|
from .helper import get_test_example_file, get_test_examples_directory
|
||||||
@@ -9,11 +9,18 @@ from .helper import get_test_example_file, get_test_examples_directory
|
|||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
def before_all() -> None:
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
conditional_download(get_test_examples_directory(),
|
conditional_download(get_test_examples_directory(),
|
||||||
[
|
[
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.mp3'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.mp3'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('source.mp3'), get_test_example_file('source.wav') ])
|
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('source.mp3')),
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('source.wav'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_get_audio_frame() -> None:
|
def test_get_audio_frame() -> None:
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import sys
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
||||||
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
||||||
@@ -10,11 +11,22 @@ from .helper import get_test_example_file, get_test_examples_directory, get_test
|
|||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
def before_all() -> None:
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
conditional_download(get_test_examples_directory(),
|
conditional_download(get_test_examples_directory(),
|
||||||
[
|
[
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '1', get_test_example_file('target-240p.jpg') ])
|
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
[
|
||||||
|
'-vframes',
|
||||||
|
'1'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p.jpg'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope = 'function', autouse = True)
|
@pytest.fixture(scope = 'function', autouse = True)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import sys
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
||||||
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
||||||
@@ -10,12 +11,23 @@ from .helper import get_test_example_file, get_test_examples_directory, get_test
|
|||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
def before_all() -> None:
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
conditional_download(get_test_examples_directory(),
|
conditional_download(get_test_examples_directory(),
|
||||||
[
|
[
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '1', get_test_example_file('target-240p.jpg') ])
|
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
[
|
||||||
|
'-vframes',
|
||||||
|
'1'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p.jpg'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope = 'function', autouse = True)
|
@pytest.fixture(scope = 'function', autouse = True)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import sys
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
||||||
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
||||||
@@ -10,12 +11,23 @@ from .helper import get_test_example_file, get_test_examples_directory, get_test
|
|||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
def before_all() -> None:
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
conditional_download(get_test_examples_directory(),
|
conditional_download(get_test_examples_directory(),
|
||||||
[
|
[
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '1', get_test_example_file('target-240p-batch-1.jpg') ])
|
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '2', get_test_example_file('target-240p-batch-2.jpg') ])
|
for frame_number in [ 1, 2 ]:
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
[
|
||||||
|
'-vframes',
|
||||||
|
str(frame_number)
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p-batch-' + str(frame_number) + '.jpg'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope = 'function', autouse = True)
|
@pytest.fixture(scope = 'function', autouse = True)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import sys
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
||||||
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
||||||
@@ -10,11 +11,22 @@ from .helper import get_test_example_file, get_test_examples_directory, get_test
|
|||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
def before_all() -> None:
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
conditional_download(get_test_examples_directory(),
|
conditional_download(get_test_examples_directory(),
|
||||||
[
|
[
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '1', get_test_example_file('target-240p.jpg') ])
|
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
[
|
||||||
|
'-vframes',
|
||||||
|
'1'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p.jpg'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope = 'function', autouse = True)
|
@pytest.fixture(scope = 'function', autouse = True)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import sys
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
||||||
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
||||||
@@ -10,12 +11,23 @@ from .helper import get_test_example_file, get_test_examples_directory, get_test
|
|||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
def before_all() -> None:
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
conditional_download(get_test_examples_directory(),
|
conditional_download(get_test_examples_directory(),
|
||||||
[
|
[
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '1', get_test_example_file('target-240p.jpg') ])
|
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
[
|
||||||
|
'-vframes',
|
||||||
|
'1'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p.jpg'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope = 'function', autouse = True)
|
@pytest.fixture(scope = 'function', autouse = True)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import sys
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
||||||
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
||||||
@@ -10,12 +11,23 @@ from .helper import get_test_example_file, get_test_examples_directory, get_test
|
|||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
def before_all() -> None:
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
conditional_download(get_test_examples_directory(),
|
conditional_download(get_test_examples_directory(),
|
||||||
[
|
[
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '1', get_test_example_file('target-240p.jpg') ])
|
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
[
|
||||||
|
'-vframes',
|
||||||
|
'1'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p.jpg'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope = 'function', autouse = True)
|
@pytest.fixture(scope = 'function', autouse = True)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import sys
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
||||||
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
||||||
@@ -10,12 +11,23 @@ from .helper import get_test_example_file, get_test_examples_directory, get_test
|
|||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
def before_all() -> None:
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
conditional_download(get_test_examples_directory(),
|
conditional_download(get_test_examples_directory(),
|
||||||
[
|
[
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '1', get_test_example_file('target-240p.jpg') ])
|
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
[
|
||||||
|
'-vframes',
|
||||||
|
'1'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p.jpg'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope = 'function', autouse = True)
|
@pytest.fixture(scope = 'function', autouse = True)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import sys
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
||||||
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
||||||
@@ -10,12 +11,23 @@ from .helper import get_test_example_file, get_test_examples_directory, get_test
|
|||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
def before_all() -> None:
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
conditional_download(get_test_examples_directory(),
|
conditional_download(get_test_examples_directory(),
|
||||||
[
|
[
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '1', get_test_example_file('target-240p.jpg') ])
|
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
[
|
||||||
|
'-vframes',
|
||||||
|
'1'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p.jpg'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope = 'function', autouse = True)
|
@pytest.fixture(scope = 'function', autouse = True)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import sys
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
||||||
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
||||||
@@ -10,13 +11,37 @@ from .helper import get_test_example_file, get_test_examples_directory, get_test
|
|||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
def before_all() -> None:
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
conditional_download(get_test_examples_directory(),
|
conditional_download(get_test_examples_directory(),
|
||||||
[
|
[
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '1', '-vf', 'hue=s=0', get_test_example_file('target-240p-0sat.jpg') ])
|
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vf', 'hue=s=0', get_test_example_file('target-240p-0sat.mp4') ])
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
[
|
||||||
|
'-vframes',
|
||||||
|
'1'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'-vf',
|
||||||
|
'hue=s=0'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p-0sat.jpg'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
[
|
||||||
|
'-vf',
|
||||||
|
'hue=s=0'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p-0sat.mp4'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope = 'function', autouse = True)
|
@pytest.fixture(scope = 'function', autouse = True)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import sys
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
||||||
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
||||||
@@ -10,12 +11,23 @@ from .helper import get_test_example_file, get_test_examples_directory, get_test
|
|||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
def before_all() -> None:
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
conditional_download(get_test_examples_directory(),
|
conditional_download(get_test_examples_directory(),
|
||||||
[
|
[
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '1', get_test_example_file('target-240p.jpg') ])
|
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
[
|
||||||
|
'-vframes',
|
||||||
|
'1'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p.jpg'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope = 'function', autouse = True)
|
@pytest.fixture(scope = 'function', autouse = True)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import sys
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from facefusion.jobs.job_manager import clear_jobs, count_step_total, init_jobs
|
from facefusion.jobs.job_manager import clear_jobs, count_step_total, init_jobs
|
||||||
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_job_file
|
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_job_file
|
||||||
@@ -10,12 +11,23 @@ from .helper import get_test_example_file, get_test_examples_directory, get_test
|
|||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
def before_all() -> None:
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
conditional_download(get_test_examples_directory(),
|
conditional_download(get_test_examples_directory(),
|
||||||
[
|
[
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '1', get_test_example_file('target-240p.jpg') ])
|
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
[
|
||||||
|
'-vframes',
|
||||||
|
'1'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p.jpg'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope = 'function', autouse = True)
|
@pytest.fixture(scope = 'function', autouse = True)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import sys
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from facefusion.jobs.job_manager import clear_jobs, init_jobs, move_job_file, set_steps_status
|
from facefusion.jobs.job_manager import clear_jobs, init_jobs, move_job_file, set_steps_status
|
||||||
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
||||||
@@ -10,12 +11,23 @@ from .helper import get_test_example_file, get_test_examples_directory, get_test
|
|||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
def before_all() -> None:
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
conditional_download(get_test_examples_directory(),
|
conditional_download(get_test_examples_directory(),
|
||||||
[
|
[
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '1', get_test_example_file('target-240p.jpg') ])
|
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
[
|
||||||
|
'-vframes',
|
||||||
|
'1'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p.jpg'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope = 'function', autouse = True)
|
@pytest.fixture(scope = 'function', autouse = True)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import sys
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
||||||
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
from .helper import get_test_example_file, get_test_examples_directory, get_test_jobs_directory, get_test_output_file, is_test_output_file, prepare_test_output_directory
|
||||||
@@ -10,13 +11,24 @@ from .helper import get_test_example_file, get_test_examples_directory, get_test
|
|||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
def before_all() -> None:
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
conditional_download(get_test_examples_directory(),
|
conditional_download(get_test_examples_directory(),
|
||||||
[
|
[
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.mp3',
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.mp3',
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '1', get_test_example_file('target-240p.jpg') ])
|
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
[
|
||||||
|
'-vframes',
|
||||||
|
'1'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p.jpg'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope = 'function', autouse = True)
|
@pytest.fixture(scope = 'function', autouse = True)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import sys
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
from facefusion.jobs.job_manager import clear_jobs, init_jobs
|
||||||
from facefusion.types import Resolution, Scale
|
from facefusion.types import Resolution, Scale
|
||||||
@@ -12,12 +13,23 @@ from .helper import get_test_example_file, get_test_examples_directory, get_test
|
|||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
def before_all() -> None:
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
conditional_download(get_test_examples_directory(),
|
conditional_download(get_test_examples_directory(),
|
||||||
[
|
[
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '1', get_test_example_file('target-240p.jpg') ])
|
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
[
|
||||||
|
'-vframes',
|
||||||
|
'1'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p.jpg'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope = 'function', autouse = True)
|
@pytest.fixture(scope = 'function', autouse = True)
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import subprocess
|
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from facefusion import face_classifier, face_detector, face_landmarker, face_recognizer, state_manager
|
from facefusion import face_classifier, face_detector, face_landmarker, face_recognizer, ffmpeg, ffmpeg_builder, process_manager, state_manager
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from facefusion.face_creator import average_face_geometry, get_many_faces, get_one_face, refill_faces
|
from facefusion.face_creator import average_face_geometry, get_many_faces, get_one_face, refill_faces
|
||||||
from facefusion.face_store import clear_faces
|
from facefusion.face_store import clear_faces
|
||||||
@@ -13,13 +12,23 @@ from .helper import get_test_example_file, get_test_examples_directory
|
|||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
def before_all() -> None:
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
conditional_download(get_test_examples_directory(),
|
conditional_download(get_test_examples_directory(),
|
||||||
[
|
[
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('source.jpg'), '-vf', 'crop=iw*0.8:ih*0.8', get_test_example_file('source-80crop.jpg') ])
|
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('source.jpg'), '-vf', 'crop=iw*0.7:ih*0.7', get_test_example_file('source-70crop.jpg') ])
|
for crop_scale in [ 80, 70, 60 ]:
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('source.jpg'), '-vf', 'crop=iw*0.6:ih*0.6', get_test_example_file('source-60crop.jpg') ])
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('source.jpg')),
|
||||||
|
[
|
||||||
|
'-vf',
|
||||||
|
'crop=iw*0.' + str(crop_scale) + ':ih*0.' + str(crop_scale)
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('source-' + str(crop_scale) + 'crop.jpg'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
state_manager.init_item('execution_device_ids', [ 0 ])
|
state_manager.init_item('execution_device_ids', [ 0 ])
|
||||||
state_manager.init_item('execution_providers', [ 'cpu' ])
|
state_manager.init_item('execution_providers', [ 'cpu' ])
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import subprocess
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from facefusion import face_detector, state_manager
|
from facefusion import face_detector, ffmpeg, ffmpeg_builder, process_manager, state_manager
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from facefusion.face_detector import detect_with_retinaface, detect_with_scrfd, detect_with_yolo_face, detect_with_yunet
|
from facefusion.face_detector import detect_with_retinaface, detect_with_scrfd, detect_with_yolo_face, detect_with_yunet
|
||||||
from facefusion.face_helper import apply_nms, get_nms_threshold
|
from facefusion.face_helper import apply_nms, get_nms_threshold
|
||||||
@@ -12,13 +11,23 @@ from .helper import get_test_example_file, get_test_examples_directory
|
|||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
def before_all() -> None:
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
conditional_download(get_test_examples_directory(),
|
conditional_download(get_test_examples_directory(),
|
||||||
[
|
[
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('source.jpg'), '-vf', 'crop=iw*0.8:ih*0.8', get_test_example_file('source-80crop.jpg') ])
|
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('source.jpg'), '-vf', 'crop=iw*0.7:ih*0.7', get_test_example_file('source-70crop.jpg') ])
|
for crop_scale in [ 80, 70, 60 ]:
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('source.jpg'), '-vf', 'crop=iw*0.6:ih*0.6', get_test_example_file('source-60crop.jpg') ])
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('source.jpg')),
|
||||||
|
[
|
||||||
|
'-vf',
|
||||||
|
'crop=iw*0.' + str(crop_scale) + ':ih*0.' + str(crop_scale)
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('source-' + str(crop_scale) + 'crop.jpg'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
state_manager.init_item('execution_device_ids', [ 0 ])
|
state_manager.init_item('execution_device_ids', [ 0 ])
|
||||||
state_manager.init_item('execution_providers', [ 'cpu' ])
|
state_manager.init_item('execution_providers', [ 'cpu' ])
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from facefusion.download import conditional_download
|
|||||||
from facefusion.face_creator import get_many_faces, get_one_face
|
from facefusion.face_creator import get_many_faces, get_one_face
|
||||||
from facefusion.face_store import clear_faces
|
from facefusion.face_store import clear_faces
|
||||||
from facefusion.face_tracker import create_face_tracks, select_face_track, track_faces
|
from facefusion.face_tracker import create_face_tracks, select_face_track, track_faces
|
||||||
from facefusion.vision import read_static_video_chunk, read_static_video_frame
|
from facefusion.vision import read_static_video_frame, select_video_frames
|
||||||
from .helper import get_test_example_file, get_test_examples_directory
|
from .helper import get_test_example_file, get_test_examples_directory
|
||||||
|
|
||||||
|
|
||||||
@@ -47,8 +47,7 @@ def before_each() -> None:
|
|||||||
|
|
||||||
def test_track_faces() -> None:
|
def test_track_faces() -> None:
|
||||||
target_path = get_test_example_file('target-240p.mp4')
|
target_path = get_test_example_file('target-240p.mp4')
|
||||||
video_frame_chunk = read_static_video_chunk(target_path, 0, 7)
|
target_vision_frames = select_video_frames(target_path, 3, 3)
|
||||||
target_vision_frames = [ video_frame_chunk.get(frame_number) for frame_number in sorted(video_frame_chunk) ]
|
|
||||||
empty_vision_frame = numpy.zeros_like(get_first(target_vision_frames))
|
empty_vision_frame = numpy.zeros_like(get_first(target_vision_frames))
|
||||||
|
|
||||||
target_vision_frames[2] = empty_vision_frame
|
target_vision_frames[2] = empty_vision_frame
|
||||||
@@ -58,7 +57,7 @@ def test_track_faces() -> None:
|
|||||||
|
|
||||||
assert len(track_faces(target_vision_frames, 0.3)) == 1
|
assert len(track_faces(target_vision_frames, 0.3)) == 1
|
||||||
|
|
||||||
target_vision_frames = [ video_frame_chunk.get(frame_number) for frame_number in sorted(video_frame_chunk)[:5] ]
|
target_vision_frames = select_video_frames(target_path, 3, 3)[:5]
|
||||||
target_vision_frames[0] = empty_vision_frame
|
target_vision_frames[0] = empty_vision_frame
|
||||||
target_vision_frames[1] = empty_vision_frame
|
target_vision_frames[1] = empty_vision_frame
|
||||||
target_vision_frames[2] = empty_vision_frame
|
target_vision_frames[2] = empty_vision_frame
|
||||||
|
|||||||
+106
-31
@@ -1,16 +1,17 @@
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import facefusion.ffmpeg
|
import facefusion.ffmpeg
|
||||||
from facefusion import process_manager, state_manager
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager, state_manager
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from facefusion.ffmpeg import concat_video, extract_frames, merge_video, read_audio_buffer, replace_audio, restore_audio
|
from facefusion.ffmpeg import concat_video, extract_frames, fix_audio_encoder, fix_video_encoder, merge_video, read_audio_buffer, replace_audio, restore_audio
|
||||||
|
from facefusion.ffprobe import extract_video_metadata
|
||||||
from facefusion.filesystem import copy_file
|
from facefusion.filesystem import copy_file
|
||||||
from facefusion.temp_helper import clear_temp_directory, create_temp_directory, get_temp_file_path, resolve_temp_frame_set
|
from facefusion.temp_helper import clear_temp_directory, create_temp_directory, get_temp_file_path, resolve_temp_frame_set
|
||||||
from facefusion.types import EncoderSet
|
from facefusion.types import EncoderSet
|
||||||
|
from facefusion.vision import read_image
|
||||||
from .helper import get_test_example_file, get_test_examples_directory, get_test_output_file, prepare_test_output_directory
|
from .helper import get_test_example_file, get_test_examples_directory, get_test_output_file, prepare_test_output_directory
|
||||||
|
|
||||||
|
|
||||||
@@ -23,15 +24,53 @@ def before_all() -> None:
|
|||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.mp3',
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.mp3',
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('source.mp3'), get_test_example_file('source.wav') ])
|
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vf', 'fps=25', get_test_example_file('target-240p-25fps.mp4') ])
|
ffmpeg.run_ffmpeg(
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vf', 'fps=30', get_test_example_file('target-240p-30fps.mp4') ])
|
ffmpeg_builder.chain(
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vf', 'fps=60', get_test_example_file('target-240p-60fps.mp4') ])
|
ffmpeg_builder.set_input(get_test_example_file('source.mp3')),
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('source.wav'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
for video_fps in [ 25, 30, 60 ]:
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
ffmpeg_builder.set_video_fps(video_fps),
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p-' + str(video_fps) + 'fps.mp4'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
[
|
||||||
|
'-vf',
|
||||||
|
'scale=out_transfer=smpte2084'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p-smpte2084.mp4'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
for output_video_format in [ 'avi', 'm4v', 'mkv', 'mov', 'mp4', 'webm', 'wmv' ]:
|
for output_video_format in [ 'avi', 'm4v', 'mkv', 'mov', 'mp4', 'webm', 'wmv' ]:
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('source.mp3'), '-i', get_test_example_file('target-240p.mp4'), '-ar', '16000', get_test_example_file('target-240p-16khz.' + output_video_format) ])
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('source.mp3')),
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
ffmpeg_builder.set_audio_sample_rate(16000),
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p-16khz.' + output_video_format))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('source.mp3')),
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
ffmpeg_builder.set_audio_sample_rate(48000),
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p-48khz.mp4'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('source.mp3'), '-i', get_test_example_file('target-240p.mp4'), '-ar', '48000', get_test_example_file('target-240p-48khz.mp4') ])
|
|
||||||
state_manager.init_item('temp_path', tempfile.gettempdir())
|
state_manager.init_item('temp_path', tempfile.gettempdir())
|
||||||
state_manager.init_item('temp_frame_format', 'png')
|
state_manager.init_item('temp_frame_format', 'png')
|
||||||
state_manager.init_item('output_audio_encoder', 'aac')
|
state_manager.init_item('output_audio_encoder', 'aac')
|
||||||
@@ -67,43 +106,49 @@ def test_get_available_encoder_set() -> None:
|
|||||||
def test_extract_frames() -> None:
|
def test_extract_frames() -> None:
|
||||||
test_set =\
|
test_set =\
|
||||||
[
|
[
|
||||||
(get_test_example_file('target-240p-25fps.mp4'), 0, 270, 324),
|
(get_test_example_file('target-240p-25fps.mp4'), 0, 270, 324, 55, 250),
|
||||||
(get_test_example_file('target-240p-25fps.mp4'), 224, 270, 55),
|
(get_test_example_file('target-240p-25fps.mp4'), 224, 270, 55, 55, 250),
|
||||||
(get_test_example_file('target-240p-25fps.mp4'), 124, 224, 120),
|
(get_test_example_file('target-240p-25fps.mp4'), 124, 224, 120, 55, 250),
|
||||||
(get_test_example_file('target-240p-25fps.mp4'), 0, 100, 120),
|
(get_test_example_file('target-240p-25fps.mp4'), 0, 100, 120, 55, 250),
|
||||||
(get_test_example_file('target-240p-30fps.mp4'), 0, 324, 324),
|
(get_test_example_file('target-240p-30fps.mp4'), 0, 324, 324, 55, 250),
|
||||||
(get_test_example_file('target-240p-30fps.mp4'), 224, 324, 100),
|
(get_test_example_file('target-240p-30fps.mp4'), 224, 324, 100, 55, 250),
|
||||||
(get_test_example_file('target-240p-30fps.mp4'), 124, 224, 100),
|
(get_test_example_file('target-240p-30fps.mp4'), 124, 224, 100, 55, 250),
|
||||||
(get_test_example_file('target-240p-30fps.mp4'), 0, 100, 100),
|
(get_test_example_file('target-240p-30fps.mp4'), 0, 100, 100, 55, 250),
|
||||||
(get_test_example_file('target-240p-60fps.mp4'), 0, 648, 324),
|
(get_test_example_file('target-240p-60fps.mp4'), 0, 648, 324, 55, 250),
|
||||||
(get_test_example_file('target-240p-60fps.mp4'), 224, 648, 212),
|
(get_test_example_file('target-240p-60fps.mp4'), 224, 648, 212, 55, 250),
|
||||||
(get_test_example_file('target-240p-60fps.mp4'), 124, 224, 50),
|
(get_test_example_file('target-240p-60fps.mp4'), 124, 224, 50, 55, 250),
|
||||||
(get_test_example_file('target-240p-60fps.mp4'), 0, 100, 50)
|
(get_test_example_file('target-240p-60fps.mp4'), 0, 100, 50, 55, 250),
|
||||||
|
(get_test_example_file('target-240p-smpte2084.mp4'), 0, 1, 1, 32, 190)
|
||||||
]
|
]
|
||||||
|
|
||||||
for target_path, trim_frame_start, trim_frame_end, frame_total in test_set:
|
for target_path, trim_frame_start, trim_frame_end, frame_total, frame_std, frame_max in test_set:
|
||||||
create_temp_directory(target_path)
|
create_temp_directory(target_path)
|
||||||
|
|
||||||
assert extract_frames(target_path, (452, 240), 30.0, trim_frame_start, trim_frame_end) is True
|
assert extract_frames(target_path, (452, 240), 30.0, trim_frame_start, trim_frame_end) is True
|
||||||
assert len(resolve_temp_frame_set(target_path)) == frame_total
|
assert len(resolve_temp_frame_set(target_path)) == frame_total
|
||||||
|
|
||||||
|
temp_vision_frame = read_image(resolve_temp_frame_set(target_path).get(trim_frame_start))
|
||||||
|
|
||||||
|
assert temp_vision_frame.std() > frame_std
|
||||||
|
assert temp_vision_frame.max() > frame_max
|
||||||
|
|
||||||
clear_temp_directory(target_path)
|
clear_temp_directory(target_path)
|
||||||
|
|
||||||
|
|
||||||
def test_merge_video() -> None:
|
def test_merge_video() -> None:
|
||||||
target_paths =\
|
test_set =\
|
||||||
[
|
[
|
||||||
get_test_example_file('target-240p-16khz.avi'),
|
(get_test_example_file('target-240p-16khz.avi'), [ 'bt709', 'unknown' ]),
|
||||||
get_test_example_file('target-240p-16khz.m4v'),
|
(get_test_example_file('target-240p-16khz.m4v'), [ 'bt709' ]),
|
||||||
get_test_example_file('target-240p-16khz.mkv'),
|
(get_test_example_file('target-240p-16khz.mkv'), [ 'bt709' ]),
|
||||||
get_test_example_file('target-240p-16khz.mp4'),
|
(get_test_example_file('target-240p-16khz.mp4'), [ 'bt709' ]),
|
||||||
get_test_example_file('target-240p-16khz.mov'),
|
(get_test_example_file('target-240p-16khz.mov'), [ 'bt709' ]),
|
||||||
get_test_example_file('target-240p-16khz.webm'),
|
(get_test_example_file('target-240p-16khz.webm'), [ 'bt709' ]),
|
||||||
get_test_example_file('target-240p-16khz.wmv')
|
(get_test_example_file('target-240p-16khz.wmv'), [ 'bt709' ])
|
||||||
]
|
]
|
||||||
output_video_encoders = get_available_encoder_set().get('video')
|
output_video_encoders = get_available_encoder_set().get('video')
|
||||||
|
|
||||||
for target_path in target_paths:
|
for target_path, color_transfers in test_set:
|
||||||
for output_video_encoder in output_video_encoders:
|
for output_video_encoder in output_video_encoders:
|
||||||
state_manager.init_item('output_video_encoder', output_video_encoder)
|
state_manager.init_item('output_video_encoder', output_video_encoder)
|
||||||
create_temp_directory(target_path)
|
create_temp_directory(target_path)
|
||||||
@@ -111,6 +156,10 @@ def test_merge_video() -> None:
|
|||||||
|
|
||||||
assert merge_video(target_path, 25.0, (452, 240), 25.0, 0, 1) is True
|
assert merge_video(target_path, 25.0, (452, 240), 25.0, 0, 1) is True
|
||||||
|
|
||||||
|
video_metadata = extract_video_metadata(get_temp_file_path(target_path))
|
||||||
|
|
||||||
|
assert video_metadata.get('color_transfer') in color_transfers
|
||||||
|
|
||||||
clear_temp_directory(target_path)
|
clear_temp_directory(target_path)
|
||||||
|
|
||||||
state_manager.init_item('output_video_encoder', 'libx264')
|
state_manager.init_item('output_video_encoder', 'libx264')
|
||||||
@@ -187,3 +236,29 @@ def test_replace_audio() -> None:
|
|||||||
clear_temp_directory(target_path)
|
clear_temp_directory(target_path)
|
||||||
|
|
||||||
state_manager.init_item('output_audio_encoder', 'aac')
|
state_manager.init_item('output_audio_encoder', 'aac')
|
||||||
|
|
||||||
|
|
||||||
|
def test_fix_audio_encoder() -> None:
|
||||||
|
assert fix_audio_encoder('avi', 'libopus') == 'aac'
|
||||||
|
assert fix_audio_encoder('m4v', 'libopus') == 'aac'
|
||||||
|
assert fix_audio_encoder('mpeg', 'libopus') == 'aac'
|
||||||
|
assert fix_audio_encoder('wmv', 'libopus') == 'aac'
|
||||||
|
assert fix_audio_encoder('mov', 'flac') == 'aac'
|
||||||
|
assert fix_audio_encoder('mov', 'libopus') == 'aac'
|
||||||
|
assert fix_audio_encoder('mxf', 'libopus') == 'pcm_s16le'
|
||||||
|
assert fix_audio_encoder('webm', 'aac') == 'libopus'
|
||||||
|
assert fix_audio_encoder('mp4', 'aac') == 'aac'
|
||||||
|
assert fix_audio_encoder('avi', 'aac') == 'aac'
|
||||||
|
|
||||||
|
|
||||||
|
def test_fix_video_encoder() -> None:
|
||||||
|
assert fix_video_encoder('m4v', 'libx265') == 'libx264'
|
||||||
|
assert fix_video_encoder('mpeg', 'libx265') == 'libx264'
|
||||||
|
assert fix_video_encoder('mxf', 'libx265') == 'libx264'
|
||||||
|
assert fix_video_encoder('wmv', 'libx265') == 'libx264'
|
||||||
|
assert fix_video_encoder('mkv', 'rawvideo') == 'libx264'
|
||||||
|
assert fix_video_encoder('mp4', 'rawvideo') == 'libx264'
|
||||||
|
assert fix_video_encoder('mov', 'libvpx-vp9') == 'libx264'
|
||||||
|
assert fix_video_encoder('webm', 'libx264') == 'libvpx-vp9'
|
||||||
|
assert fix_video_encoder('mp4', 'libx265') == 'libx265'
|
||||||
|
assert fix_video_encoder('avi', 'rawvideo') == 'rawvideo'
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from shutil import which
|
from shutil import which
|
||||||
|
|
||||||
from facefusion import ffmpeg_builder
|
from facefusion import ffmpeg_builder
|
||||||
from facefusion.ffmpeg_builder import chain, concat, keep_video_alpha, run, select_frame_range, set_audio_quality, set_audio_sample_size, set_faststart, set_stream_mode, set_video_encoder, set_video_fps, set_video_quality, set_video_tag
|
from facefusion.ffmpeg_builder import chain, concat, convert_color_space, keep_video_alpha, restrict_color_transfer, run, seek_to, select_frame_range, set_audio_quality, set_audio_sample_size, set_faststart, set_output_format, set_stream_mode, set_thread_count, set_video_encoder, set_video_fps, set_video_quality, set_video_tag
|
||||||
|
|
||||||
|
|
||||||
def test_run() -> None:
|
def test_run() -> None:
|
||||||
@@ -41,6 +41,15 @@ def test_set_stream_mode() -> None:
|
|||||||
assert set_stream_mode('v4l2') == [ '-f', 'v4l2' ]
|
assert set_stream_mode('v4l2') == [ '-f', 'v4l2' ]
|
||||||
|
|
||||||
|
|
||||||
|
def test_seek_to() -> None:
|
||||||
|
assert seek_to(0.0) == [ '-ss', '0.0' ]
|
||||||
|
assert seek_to(1.5) == [ '-ss', '1.5' ]
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_output_format() -> None:
|
||||||
|
assert set_output_format('rawvideo') == [ '-f', 'rawvideo' ]
|
||||||
|
|
||||||
|
|
||||||
def test_select_frame_range() -> None:
|
def test_select_frame_range() -> None:
|
||||||
assert select_frame_range(0, None, 30) == [ '-vf', 'trim=start_frame=0,fps=30' ]
|
assert select_frame_range(0, None, 30) == [ '-vf', 'trim=start_frame=0,fps=30' ]
|
||||||
assert select_frame_range(None, 100, 30) == [ '-vf', 'trim=end_frame=100,fps=30' ]
|
assert select_frame_range(None, 100, 30) == [ '-vf', 'trim=end_frame=100,fps=30' ]
|
||||||
@@ -48,6 +57,18 @@ def test_select_frame_range() -> None:
|
|||||||
assert select_frame_range(None, None, 30) == [ '-vf', 'fps=30' ]
|
assert select_frame_range(None, None, 30) == [ '-vf', 'fps=30' ]
|
||||||
|
|
||||||
|
|
||||||
|
def test_restrict_color_transfer() -> None:
|
||||||
|
assert restrict_color_transfer('smpte2084') == [ '-vf', 'scale=out_primaries=bt709:out_transfer=bt709:intent=perceptual' ]
|
||||||
|
assert restrict_color_transfer('arib-std-b67') == [ '-vf', 'scale=out_primaries=bt709:out_transfer=bt709:intent=perceptual' ]
|
||||||
|
assert restrict_color_transfer('invalid') == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_convert_color_space() -> None:
|
||||||
|
assert convert_color_space('bt601') == [ '-vf', 'scale=out_color_matrix=bt601:out_range=tv,setparams=colorspace=bt601:color_primaries=bt601:color_trc=bt601' ]
|
||||||
|
assert convert_color_space('bt709') == [ '-vf', 'scale=out_color_matrix=bt709:out_range=tv,setparams=colorspace=bt709:color_primaries=bt709:color_trc=bt709' ]
|
||||||
|
assert convert_color_space('bt2020') == [ '-vf', 'scale=out_color_matrix=bt2020:out_range=tv,setparams=colorspace=bt2020:color_primaries=bt2020:color_trc=bt2020' ]
|
||||||
|
|
||||||
|
|
||||||
def test_set_audio_sample_size() -> None:
|
def test_set_audio_sample_size() -> None:
|
||||||
assert set_audio_sample_size(16) == [ '-f', 's16le' ]
|
assert set_audio_sample_size(16) == [ '-f', 's16le' ]
|
||||||
assert set_audio_sample_size(32) == [ '-f', 's32le' ]
|
assert set_audio_sample_size(32) == [ '-f', 's32le' ]
|
||||||
@@ -71,6 +92,11 @@ def test_set_audio_quality() -> None:
|
|||||||
assert set_audio_quality('flac', 100) == []
|
assert set_audio_quality('flac', 100) == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_thread_count() -> None:
|
||||||
|
assert set_thread_count(8) == [ '-threads', '8' ]
|
||||||
|
assert set_thread_count(16) == [ '-threads', '16' ]
|
||||||
|
|
||||||
|
|
||||||
def test_set_faststart() -> None:
|
def test_set_faststart() -> None:
|
||||||
assert set_faststart('m4v') == [ '-movflags', '+faststart' ]
|
assert set_faststart('m4v') == [ '-movflags', '+faststart' ]
|
||||||
assert set_faststart('mov') == [ '-movflags', '+faststart' ]
|
assert set_faststart('mov') == [ '-movflags', '+faststart' ]
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager
|
||||||
|
from facefusion.download import conditional_download
|
||||||
|
from facefusion.ffprobe import extract_audio_metadata, extract_video_metadata
|
||||||
|
from .helper import get_test_example_file, get_test_examples_directory
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
|
|
||||||
|
conditional_download(get_test_examples_directory(),
|
||||||
|
[
|
||||||
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.mp3',
|
||||||
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
||||||
|
])
|
||||||
|
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('source.mp3')),
|
||||||
|
ffmpeg_builder.set_video_duration(1.9),
|
||||||
|
ffmpeg_builder.set_audio_sample_rate(48000),
|
||||||
|
ffmpeg_builder.set_audio_channel_total(2),
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('source-48000khz-2ch.wav'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for video_format in [ 'mkv', 'mov' ]:
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
ffmpeg_builder.set_video_duration(1),
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p-1s.' + video_format))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_extract_audio_metadata() -> None:
|
||||||
|
audio_metadata = extract_audio_metadata(get_test_example_file('source.mp3'))
|
||||||
|
|
||||||
|
assert audio_metadata.get('sample_rate') == 44100
|
||||||
|
assert audio_metadata.get('channel_total') == 1
|
||||||
|
assert audio_metadata.get('frame_total') == 167040
|
||||||
|
assert audio_metadata.get('bit_rate') == 128000
|
||||||
|
|
||||||
|
audio_metadata = extract_audio_metadata(get_test_example_file('source-48000khz-2ch.wav'))
|
||||||
|
|
||||||
|
assert audio_metadata.get('sample_rate') == 48000
|
||||||
|
assert audio_metadata.get('channel_total') == 2
|
||||||
|
assert audio_metadata.get('frame_total') == 91200
|
||||||
|
assert audio_metadata.get('bit_rate') == 1536328
|
||||||
|
|
||||||
|
|
||||||
|
def test_extract_video_metadata() -> None:
|
||||||
|
video_metadata = extract_video_metadata(get_test_example_file('target-240p.mp4'))
|
||||||
|
|
||||||
|
assert video_metadata.get('fps') == 25.0
|
||||||
|
assert video_metadata.get('duration') == 10.8
|
||||||
|
assert video_metadata.get('resolution') == (426, 226)
|
||||||
|
assert video_metadata.get('bit_rate') == 141981
|
||||||
|
assert video_metadata.get('color_transfer') == 'smpte170m'
|
||||||
|
|
||||||
|
video_metadata = extract_video_metadata(get_test_example_file('target-240p-1s.mkv'))
|
||||||
|
|
||||||
|
assert video_metadata.get('fps') == 25.0
|
||||||
|
assert video_metadata.get('duration') == 1.0
|
||||||
|
assert video_metadata.get('frame_total') == 25
|
||||||
|
assert video_metadata.get('resolution') == (426, 226)
|
||||||
|
|
||||||
|
video_metadata = extract_video_metadata(get_test_example_file('target-240p-1s.mov'))
|
||||||
|
|
||||||
|
assert video_metadata.get('fps') == 25.0
|
||||||
|
assert video_metadata.get('duration') == 1.0
|
||||||
|
assert video_metadata.get('resolution') == (426, 226)
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
from shutil import which
|
||||||
|
|
||||||
|
from facefusion import ffprobe_builder
|
||||||
|
from facefusion.ffprobe_builder import chain, format_to_key_value, run, select_stream, set_input, show_stream_entries
|
||||||
|
|
||||||
|
|
||||||
|
def test_run() -> None:
|
||||||
|
assert run([ '-v', 'error' ]) == [ which('ffprobe'), '-loglevel', 'error', '-v', 'error' ]
|
||||||
|
|
||||||
|
|
||||||
|
def test_chain() -> None:
|
||||||
|
assert chain(
|
||||||
|
ffprobe_builder.select_stream('a:0'),
|
||||||
|
ffprobe_builder.show_stream_entries([ 'sample_rate' ]),
|
||||||
|
ffprobe_builder.format_to_key_value(),
|
||||||
|
ffprobe_builder.set_input('audio.mp3')
|
||||||
|
) == [ '-select_streams', 'a:0', '-show_entries', 'stream=sample_rate', '-of', 'default=noprint_wrappers=1', '-i', 'audio.mp3' ]
|
||||||
|
|
||||||
|
|
||||||
|
def test_select_stream() -> None:
|
||||||
|
assert select_stream('a:0') == [ '-select_streams', 'a:0' ]
|
||||||
|
assert select_stream('v:0') == [ '-select_streams', 'v:0' ]
|
||||||
|
|
||||||
|
|
||||||
|
def test_show_stream_entries() -> None:
|
||||||
|
assert show_stream_entries([ 'duration' ]) == [ '-show_entries', 'stream=duration' ]
|
||||||
|
assert show_stream_entries([ 'duration', 'sample_rate' ]) == [ '-show_entries', 'stream=duration,sample_rate' ]
|
||||||
|
|
||||||
|
|
||||||
|
def test_format_to_key_value() -> None:
|
||||||
|
assert format_to_key_value() == [ '-of', 'default=noprint_wrappers=1' ]
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_input() -> None:
|
||||||
|
assert set_input('input.mp3') == [ '-i', 'input.mp3' ]
|
||||||
|
assert set_input('input.wav') == [ '-i', 'input.wav' ]
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import process_manager
|
||||||
|
from facefusion.download import conditional_download
|
||||||
|
from facefusion.frame_store import clear_frames, get_frame_store, reduce_frames, select_frame_set, set_frame
|
||||||
|
from facefusion.vision import read_video_frame
|
||||||
|
from .helper import get_test_example_file, get_test_examples_directory
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
|
conditional_download(get_test_examples_directory(),
|
||||||
|
[
|
||||||
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope = 'function', autouse = True)
|
||||||
|
def before_each() -> None:
|
||||||
|
clear_frames('reader-1')
|
||||||
|
clear_frames('reader-2')
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_frame_store() -> None:
|
||||||
|
frame_store = get_frame_store('reader-1')
|
||||||
|
|
||||||
|
assert frame_store == {}
|
||||||
|
assert get_frame_store('reader-1') is frame_store
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_frame() -> None:
|
||||||
|
target_frame = read_video_frame(get_test_example_file('target-240p.mp4'), 0)
|
||||||
|
|
||||||
|
set_frame('reader-1', 5, target_frame)
|
||||||
|
|
||||||
|
assert get_frame_store('reader-1').get(5) is target_frame
|
||||||
|
|
||||||
|
|
||||||
|
def test_select_frame_set() -> None:
|
||||||
|
first_frame = read_video_frame(get_test_example_file('target-240p.mp4'), 0)
|
||||||
|
fifth_frame = read_video_frame(get_test_example_file('target-240p.mp4'), 5)
|
||||||
|
|
||||||
|
set_frame('reader-1', 2, first_frame)
|
||||||
|
set_frame('reader-1', 5, fifth_frame)
|
||||||
|
|
||||||
|
assert sorted(select_frame_set('reader-1', 0, 4)) == [ 2 ]
|
||||||
|
assert select_frame_set('reader-1', 0, 4).get(2) is first_frame
|
||||||
|
assert sorted(select_frame_set('reader-1', 2, 6)) == [ 2, 5 ]
|
||||||
|
assert select_frame_set('reader-1', 2, 6).get(5) is fifth_frame
|
||||||
|
assert select_frame_set('reader-1', 8, 12) == {}
|
||||||
|
|
||||||
|
|
||||||
|
def test_reduce_frames() -> None:
|
||||||
|
target_frame = read_video_frame(get_test_example_file('target-240p.mp4'), 0)
|
||||||
|
|
||||||
|
for frame_number in range(0, 10):
|
||||||
|
set_frame('reader-1', frame_number, target_frame)
|
||||||
|
|
||||||
|
reduce_frames('reader-1', 4, 6)
|
||||||
|
|
||||||
|
assert sorted(get_frame_store('reader-1')) == [ 4, 5, 6 ]
|
||||||
|
|
||||||
|
|
||||||
|
def test_clear_frames() -> None:
|
||||||
|
target_frame = read_video_frame(get_test_example_file('target-240p.mp4'), 0)
|
||||||
|
|
||||||
|
set_frame('reader-1', 0, target_frame)
|
||||||
|
set_frame('reader-2', 0, target_frame)
|
||||||
|
clear_frames('reader-1')
|
||||||
|
|
||||||
|
assert get_frame_store('reader-1') == {}
|
||||||
|
assert get_frame_store('reader-2').get(0) is target_frame
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
from unittest.mock import patch
|
from types import SimpleNamespace
|
||||||
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from onnxruntime import InferenceSession
|
from onnxruntime import InferenceSession
|
||||||
|
|
||||||
from facefusion import content_analyser, state_manager
|
from facefusion import content_analyser, state_manager
|
||||||
from facefusion.inference_manager import INFERENCE_POOL_SET, get_inference_pool
|
from facefusion.execution import resolve_cache_path
|
||||||
|
from facefusion.inference_manager import INFERENCE_POOL_SET, get_inference_pool, resolve_static_inference_providers
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
@@ -12,7 +14,6 @@ def before_all() -> None:
|
|||||||
state_manager.init_item('execution_device_ids', [ 0 ])
|
state_manager.init_item('execution_device_ids', [ 0 ])
|
||||||
state_manager.init_item('execution_providers', [ 'cpu' ])
|
state_manager.init_item('execution_providers', [ 'cpu' ])
|
||||||
state_manager.init_item('download_providers', [ 'github' ])
|
state_manager.init_item('download_providers', [ 'github' ])
|
||||||
content_analyser.pre_check()
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_inference_pool() -> None:
|
def test_get_inference_pool() -> None:
|
||||||
@@ -30,3 +31,26 @@ def test_get_inference_pool() -> None:
|
|||||||
assert isinstance(INFERENCE_POOL_SET.get('cli').get('facefusion.content_analyser.nsfw_1.nsfw_2.nsfw_3.0.cpu').get('nsfw_1'), InferenceSession)
|
assert isinstance(INFERENCE_POOL_SET.get('cli').get('facefusion.content_analyser.nsfw_1.nsfw_2.nsfw_3.0.cpu').get('nsfw_1'), InferenceSession)
|
||||||
|
|
||||||
assert INFERENCE_POOL_SET.get('cli').get('facefusion.content_analyser.nsfw_1.nsfw_2.nsfw_3.0.cpu').get('nsfw_1') == INFERENCE_POOL_SET.get('ui').get('facefusion.content_analyser.nsfw_1.nsfw_2.nsfw_3.0.cpu').get('nsfw_1')
|
assert INFERENCE_POOL_SET.get('cli').get('facefusion.content_analyser.nsfw_1.nsfw_2.nsfw_3.0.cpu').get('nsfw_1') == INFERENCE_POOL_SET.get('ui').get('facefusion.content_analyser.nsfw_1.nsfw_2.nsfw_3.0.cpu').get('nsfw_1')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def override_module() -> SimpleNamespace:
|
||||||
|
return SimpleNamespace(override_inference_providers = Mock(return_value = [ ('CoreMLExecutionProvider', { 'ModelFormat': 'MLProgram' }) ]))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def adjust_module() -> SimpleNamespace:
|
||||||
|
return SimpleNamespace(adjust_inference_providers = Mock(return_value = [ ('CoreMLExecutionProvider', { 'ModelFormat': 'MLProgram' }) ]))
|
||||||
|
|
||||||
|
|
||||||
|
def test_resolve_static_inference_providers(override_module : SimpleNamespace, adjust_module : SimpleNamespace) -> None:
|
||||||
|
state_manager.init_item('execution_providers', ['coreml'])
|
||||||
|
resolve_static_inference_providers.cache_clear()
|
||||||
|
|
||||||
|
with patch('facefusion.inference_manager.importlib', Mock(import_module = Mock(return_value = override_module))):
|
||||||
|
assert resolve_static_inference_providers('override_module', 0) == [ ('CoreMLExecutionProvider', { 'ModelFormat': 'MLProgram' }) ]
|
||||||
|
|
||||||
|
with patch('facefusion.inference_manager.importlib', Mock(import_module = Mock(return_value = adjust_module))):
|
||||||
|
assert resolve_static_inference_providers('adjust_module', 0) == [ ('CoreMLExecutionProvider', { 'SpecializationStrategy': 'FastPrediction', 'ModelCacheDirectory': resolve_cache_path(), 'ModelFormat': 'MLProgram' }) ]
|
||||||
|
|
||||||
|
assert resolve_static_inference_providers('test', 0) == [ ('CoreMLExecutionProvider', { 'SpecializationStrategy': 'FastPrediction', 'ModelCacheDirectory': resolve_cache_path() }) ]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import subprocess
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from facefusion.filesystem import copy_file
|
from facefusion.filesystem import copy_file
|
||||||
from facefusion.jobs.job_manager import add_step, clear_jobs, create_job, init_jobs, move_job_file, submit_job, submit_jobs
|
from facefusion.jobs.job_manager import add_step, clear_jobs, create_job, init_jobs, move_job_file, submit_job, submit_jobs
|
||||||
@@ -12,12 +12,23 @@ from .helper import get_test_example_file, get_test_examples_directory, get_test
|
|||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
def before_all() -> None:
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
conditional_download(get_test_examples_directory(),
|
conditional_download(get_test_examples_directory(),
|
||||||
[
|
[
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '1', get_test_example_file('target-240p.jpg') ])
|
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
[
|
||||||
|
'-vframes',
|
||||||
|
'1'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p.jpg'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope = 'function', autouse = True)
|
@pytest.fixture(scope = 'function', autouse = True)
|
||||||
|
|||||||
@@ -0,0 +1,199 @@
|
|||||||
|
import tempfile
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager, state_manager
|
||||||
|
from facefusion.common_helper import is_linux, is_macos, is_windows
|
||||||
|
from facefusion.download import conditional_download
|
||||||
|
from facefusion.ffprobe import extract_video_metadata
|
||||||
|
from facefusion.frame_store import get_frame_store
|
||||||
|
from facefusion.temp_helper import create_temp_directory, get_temp_file_path
|
||||||
|
from facefusion.video_manager import clear_video_pool, close_video_reader, close_video_writer, collect_video_frames, conditional_seek_video_reader, drain_video_reader, get_reader, get_writer, read_video_frame, read_video_frames, seek_video_reader, write_video_frame
|
||||||
|
from .helper import get_test_example_file, get_test_examples_directory
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
|
conditional_download(get_test_examples_directory(),
|
||||||
|
[
|
||||||
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4'
|
||||||
|
])
|
||||||
|
|
||||||
|
for video_fps in [ 25, 30 ]:
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
ffmpeg_builder.set_video_fps(video_fps),
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p-' + str(video_fps) + 'fps.mp4'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
state_manager.init_item('temp_path', tempfile.gettempdir())
|
||||||
|
state_manager.init_item('temp_frame_format', 'png')
|
||||||
|
state_manager.init_item('output_video_encoder', 'libx264')
|
||||||
|
state_manager.init_item('output_video_quality', 80)
|
||||||
|
state_manager.init_item('output_video_preset', 'veryfast')
|
||||||
|
state_manager.init_item('temp_pixel_format', 'bgr24')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope = 'function', autouse = True)
|
||||||
|
def before_each() -> None:
|
||||||
|
clear_video_pool()
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_reader() -> None:
|
||||||
|
video_reader = get_reader(get_test_example_file('target-240p-25fps.mp4'), 'read_video_frame')
|
||||||
|
video_metadata = video_reader.get('metadata')
|
||||||
|
|
||||||
|
assert video_metadata.get('resolution') == (426, 226)
|
||||||
|
assert video_metadata.get('fps') == 25.0
|
||||||
|
assert video_metadata.get('frame_total') == 270
|
||||||
|
assert get_reader(get_test_example_file('target-240p-25fps.mp4'), 'read_video_frame') is video_reader
|
||||||
|
assert not get_reader(get_test_example_file('target-240p-25fps.mp4'), 'select_video_frames').get('id') == video_reader.get('id')
|
||||||
|
|
||||||
|
|
||||||
|
def test_conditional_seek_video_reader() -> None:
|
||||||
|
video_reader = get_reader(get_test_example_file('target-240p-25fps.mp4'), 'read_video_frame')
|
||||||
|
sequential_frames = {}
|
||||||
|
|
||||||
|
for frame_number in range(30):
|
||||||
|
sequential_frames[frame_number] = read_video_frame(video_reader)
|
||||||
|
|
||||||
|
for frame_number in [ 5, 17, 29 ]:
|
||||||
|
conditional_seek_video_reader(video_reader, frame_number)
|
||||||
|
|
||||||
|
assert numpy.array_equal(read_video_frame(video_reader), sequential_frames.get(frame_number)) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_seek_video_reader() -> None:
|
||||||
|
video_reader = get_reader(get_test_example_file('target-240p-25fps.mp4'), 'read_video_frame')
|
||||||
|
sequential_frames = {}
|
||||||
|
|
||||||
|
for frame_number in range(30):
|
||||||
|
sequential_frames[frame_number] = read_video_frame(video_reader)
|
||||||
|
|
||||||
|
for frame_number in [ 5, 17, 29 ]:
|
||||||
|
seek_video_reader(video_reader, frame_number)
|
||||||
|
|
||||||
|
assert numpy.array_equal(read_video_frame(video_reader), sequential_frames.get(frame_number)) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_drain_video_reader() -> None:
|
||||||
|
video_reader = get_reader(get_test_example_file('target-240p-25fps.mp4'), 'read_video_frame')
|
||||||
|
|
||||||
|
drain_video_reader(video_reader, 10)
|
||||||
|
|
||||||
|
assert video_reader.get('frame_number') == 10
|
||||||
|
|
||||||
|
vision_frame = read_video_frame(video_reader)
|
||||||
|
seek_video_reader(video_reader, 10)
|
||||||
|
|
||||||
|
assert numpy.array_equal(vision_frame, read_video_frame(video_reader)) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_read_video_frame() -> None:
|
||||||
|
video_reader = get_reader(get_test_example_file('target-240p-25fps.mp4'), 'read_video_frame')
|
||||||
|
|
||||||
|
assert read_video_frame(video_reader).shape == (226, 426, 3)
|
||||||
|
assert video_reader.get('frame_number') == 1
|
||||||
|
|
||||||
|
seek_video_reader(video_reader, 269)
|
||||||
|
|
||||||
|
assert read_video_frame(video_reader).shape == (226, 426, 3)
|
||||||
|
assert read_video_frame(video_reader) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_read_video_frames() -> None:
|
||||||
|
video_reader = get_reader(get_test_example_file('target-240p-25fps.mp4'), 'read_video_frame')
|
||||||
|
|
||||||
|
assert sorted(read_video_frames(video_reader, 0, 4)) == [ 0, 1, 2, 3, 4 ]
|
||||||
|
|
||||||
|
frame_number = video_reader.get('frame_number')
|
||||||
|
|
||||||
|
assert sorted(read_video_frames(video_reader, 1, 3)) == [ 1, 2, 3 ]
|
||||||
|
assert video_reader.get('frame_number') == frame_number
|
||||||
|
|
||||||
|
read_video_frames(video_reader, 21, 25)
|
||||||
|
|
||||||
|
assert min(get_frame_store(video_reader.get('id'))) == 17
|
||||||
|
assert max(get_frame_store(video_reader.get('id'))) == 25
|
||||||
|
assert sorted(read_video_frames(video_reader, 268, 275)) == [ 268, 269 ]
|
||||||
|
|
||||||
|
|
||||||
|
def test_collect_video_frames() -> None:
|
||||||
|
video_reader = get_reader(get_test_example_file('target-240p-25fps.mp4'), 'select_video_frames')
|
||||||
|
|
||||||
|
collect_video_frames(video_reader, 20, 24)
|
||||||
|
|
||||||
|
assert sorted(get_frame_store(video_reader.get('id'))) == [ 20, 21, 22, 23, 24 ]
|
||||||
|
assert video_reader.get('frame_number') == 25
|
||||||
|
|
||||||
|
|
||||||
|
def test_close_video_reader() -> None:
|
||||||
|
video_reader = get_reader(get_test_example_file('target-240p-25fps.mp4'), 'select_video_frames')
|
||||||
|
read_video_frames(video_reader, 0, 4)
|
||||||
|
close_video_reader(video_reader)
|
||||||
|
|
||||||
|
if is_windows():
|
||||||
|
assert video_reader.get('process').returncode == 1
|
||||||
|
|
||||||
|
if is_linux() or is_macos():
|
||||||
|
assert video_reader.get('process').returncode == -9
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_writer() -> None:
|
||||||
|
target_path = get_test_example_file('target-240p-25fps.mp4')
|
||||||
|
create_temp_directory(target_path)
|
||||||
|
video_writer = get_writer(target_path, 25.0, (426, 226), (426, 226), 25.0)
|
||||||
|
|
||||||
|
assert get_writer(target_path, 25.0, (426, 226), (426, 226), 25.0) is video_writer
|
||||||
|
|
||||||
|
|
||||||
|
def test_write_video_frame() -> None:
|
||||||
|
target_path = get_test_example_file('target-240p-25fps.mp4')
|
||||||
|
create_temp_directory(target_path)
|
||||||
|
video_reader = get_reader(target_path, 'read_video_frame')
|
||||||
|
video_writer = get_writer(target_path, 25.0, (426, 226), (426, 226), 25.0)
|
||||||
|
|
||||||
|
for frame_number in range(25):
|
||||||
|
write_video_frame(video_writer, read_video_frame(video_reader))
|
||||||
|
|
||||||
|
assert close_video_writer(video_writer) is True
|
||||||
|
|
||||||
|
video_metadata = extract_video_metadata(get_temp_file_path(target_path))
|
||||||
|
|
||||||
|
assert video_metadata.get('duration') == 1.0
|
||||||
|
assert video_metadata.get('frame_total') == 25
|
||||||
|
assert video_metadata.get('fps') == 25.0
|
||||||
|
assert video_metadata.get('resolution') == (426, 226)
|
||||||
|
assert video_metadata.get('color_transfer') == 'bt709'
|
||||||
|
|
||||||
|
|
||||||
|
def test_close_video_writer() -> None:
|
||||||
|
target_path = get_test_example_file('target-240p-30fps.mp4')
|
||||||
|
create_temp_directory(target_path)
|
||||||
|
video_reader = get_reader(target_path, 'read_video_frame')
|
||||||
|
video_writer = get_writer(target_path, 30.0, (426, 226), (426, 226), 30.0)
|
||||||
|
write_video_frame(video_writer, read_video_frame(video_reader))
|
||||||
|
|
||||||
|
assert close_video_writer(video_writer) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_clear_video_pool() -> None:
|
||||||
|
target_path = get_test_example_file('target-240p-25fps.mp4')
|
||||||
|
create_temp_directory(target_path)
|
||||||
|
video_reader = get_reader(target_path, 'select_video_frames')
|
||||||
|
video_writer = get_writer(target_path, 25.0, (426, 226), (426, 226), 25.0)
|
||||||
|
read_video_frames(video_reader, 0, 4)
|
||||||
|
write_video_frame(video_writer, read_video_frame(video_reader))
|
||||||
|
clear_video_pool()
|
||||||
|
|
||||||
|
if is_windows():
|
||||||
|
assert video_reader.get('process').returncode == 1
|
||||||
|
|
||||||
|
if is_linux() or is_macos():
|
||||||
|
assert video_reader.get('process').returncode == -9
|
||||||
|
|
||||||
|
assert video_writer.get('process').returncode == 0
|
||||||
+78
-18
@@ -1,34 +1,99 @@
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from facefusion import ffmpeg, ffmpeg_builder, process_manager
|
||||||
from facefusion.common_helper import is_linux
|
from facefusion.common_helper import is_linux
|
||||||
from facefusion.download import conditional_download
|
from facefusion.download import conditional_download
|
||||||
from facefusion.vision import calculate_histogram_difference, count_trim_frame_total, count_video_frame_total, detect_image_resolution, detect_video_duration, detect_video_fps, detect_video_resolution, match_frame_color, normalize_resolution, pack_resolution, predict_video_frame_total, read_image, read_video_chunk, read_video_frame, restrict_image_resolution, restrict_trim_frame, restrict_video_fps, restrict_video_resolution, scale_resolution, select_video_frames, unpack_resolution, write_image
|
from facefusion.vision import calculate_histogram_difference, count_trim_frame_total, count_video_frame_total, detect_image_resolution, detect_video_duration, detect_video_fps, detect_video_resolution, match_frame_color, normalize_resolution, pack_resolution, predict_video_frame_total, read_image, read_video_frame, restrict_image_resolution, restrict_trim_frame, restrict_video_fps, restrict_video_resolution, scale_resolution, select_video_frames, unpack_resolution, write_image
|
||||||
from .helper import get_test_example_file, get_test_examples_directory, get_test_output_file, prepare_test_output_directory
|
from .helper import get_test_example_file, get_test_examples_directory, get_test_output_file, prepare_test_output_directory
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope = 'module', autouse = True)
|
@pytest.fixture(scope = 'module', autouse = True)
|
||||||
def before_all() -> None:
|
def before_all() -> None:
|
||||||
|
process_manager.start()
|
||||||
conditional_download(get_test_examples_directory(),
|
conditional_download(get_test_examples_directory(),
|
||||||
[
|
[
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/source.jpg',
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4',
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-240p.mp4',
|
||||||
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-1080p.mp4'
|
'https://github.com/facefusion/facefusion-assets/releases/download/examples-3.0.0/target-1080p.mp4'
|
||||||
])
|
])
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '1', get_test_example_file('target-240p.jpg') ])
|
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '1', get_test_example_file('目标-240p.webp') ])
|
for target_name in [ 'target-240p', 'target-1080p' ]:
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-1080p.mp4'), '-vframes', '1', get_test_example_file('target-1080p.jpg') ])
|
ffmpeg.run_ffmpeg(
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '1', '-vf', 'hue=s=0', get_test_example_file('target-240p-0sat.jpg') ])
|
ffmpeg_builder.chain(
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vframes', '1', '-vf', 'transpose=0', get_test_example_file('target-240p-90deg.jpg') ])
|
ffmpeg_builder.set_input(get_test_example_file(target_name + '.mp4')),
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-1080p.mp4'), '-vframes', '1', '-vf', 'transpose=0', get_test_example_file('target-1080p-90deg.jpg') ])
|
[
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vf', 'fps=25', get_test_example_file('target-240p-25fps.mp4') ])
|
'-vframes',
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vf', 'fps=30', get_test_example_file('target-240p-30fps.mp4') ])
|
'1'
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vf', 'fps=60', get_test_example_file('target-240p-60fps.mp4') ])
|
],
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vf', 'transpose=0', get_test_example_file('target-240p-90deg.mp4') ])
|
ffmpeg_builder.set_output(get_test_example_file(target_name + '.jpg'))
|
||||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-1080p.mp4'), '-vf', 'transpose=0', get_test_example_file('target-1080p-90deg.mp4') ])
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
[
|
||||||
|
'-vframes',
|
||||||
|
'1'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('目标-240p.webp'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
[
|
||||||
|
'-vframes',
|
||||||
|
'1'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'-vf',
|
||||||
|
'hue=s=0'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p-0sat.jpg'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
for target_name in [ 'target-240p', 'target-1080p' ]:
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file(target_name + '.mp4')),
|
||||||
|
[
|
||||||
|
'-vframes',
|
||||||
|
'1'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'-vf',
|
||||||
|
'transpose=0'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file(target_name + '-90deg.jpg'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
for video_fps in [ 25, 30, 60 ]:
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file('target-240p.mp4')),
|
||||||
|
ffmpeg_builder.set_video_fps(video_fps),
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file('target-240p-' + str(video_fps) + 'fps.mp4'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
for target_name in [ 'target-240p', 'target-1080p' ]:
|
||||||
|
ffmpeg.run_ffmpeg(
|
||||||
|
ffmpeg_builder.chain(
|
||||||
|
ffmpeg_builder.set_input(get_test_example_file(target_name + '.mp4')),
|
||||||
|
[
|
||||||
|
'-vf',
|
||||||
|
'transpose=0'
|
||||||
|
],
|
||||||
|
ffmpeg_builder.set_output(get_test_example_file(target_name + '-90deg.mp4'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope = 'function', autouse = True)
|
@pytest.fixture(scope = 'function', autouse = True)
|
||||||
@@ -73,11 +138,6 @@ def test_read_video_frame() -> None:
|
|||||||
assert read_video_frame('invalid') is None
|
assert read_video_frame('invalid') is None
|
||||||
|
|
||||||
|
|
||||||
def test_read_video_chunk() -> None:
|
|
||||||
assert len(read_video_chunk(get_test_example_file('target-240p-25fps.mp4'), 1, 40)) == 40
|
|
||||||
assert read_video_chunk('invalid', 1, 40) == {}
|
|
||||||
|
|
||||||
|
|
||||||
def test_select_video_frames() -> None:
|
def test_select_video_frames() -> None:
|
||||||
assert len(select_video_frames(get_test_example_file('target-240p-25fps.mp4'), 50, 5)) == 11
|
assert len(select_video_frames(get_test_example_file('target-240p-25fps.mp4'), 50, 5)) == 11
|
||||||
assert len(select_video_frames(get_test_example_file('target-240p-25fps.mp4'), 1, 5)) == 11
|
assert len(select_video_frames(get_test_example_file('target-240p-25fps.mp4'), 1, 5)) == 11
|
||||||
|
|||||||
Reference in New Issue
Block a user