BIN
.github/preview.png
vendored
BIN
.github/preview.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
@@ -37,6 +37,7 @@ commands:
|
||||
headless-run run the program in headless mode
|
||||
batch-run run the program in batch mode
|
||||
force-download force automate downloads and exit
|
||||
benchmark benchmark the program
|
||||
job-list list jobs by status
|
||||
job-create create a drafted job
|
||||
job-submit submit a drafted job to become a queued job
|
||||
|
||||
@@ -35,9 +35,10 @@ reference_frame_number =
|
||||
face_occluder_model =
|
||||
face_parser_model =
|
||||
face_mask_types =
|
||||
face_mask_areas =
|
||||
face_mask_regions =
|
||||
face_mask_blur =
|
||||
face_mask_padding =
|
||||
face_mask_regions =
|
||||
|
||||
[frame_extraction]
|
||||
trim_frame_start =
|
||||
@@ -92,22 +93,27 @@ frame_colorizer_blend =
|
||||
frame_enhancer_model =
|
||||
frame_enhancer_blend =
|
||||
lip_syncer_model =
|
||||
lip_syncer_weight =
|
||||
|
||||
[uis]
|
||||
open_browser =
|
||||
ui_layouts =
|
||||
ui_workflow =
|
||||
|
||||
[download]
|
||||
download_providers =
|
||||
download_scope =
|
||||
|
||||
[benchmark]
|
||||
benchmark_resolutions =
|
||||
benchmark_cycle_count =
|
||||
|
||||
[execution]
|
||||
execution_device_id =
|
||||
execution_providers =
|
||||
execution_thread_count =
|
||||
execution_queue_count =
|
||||
|
||||
[download]
|
||||
download_providers =
|
||||
download_scope =
|
||||
|
||||
[memory]
|
||||
video_memory_strategy =
|
||||
system_memory_limit =
|
||||
|
||||
@@ -74,9 +74,10 @@ def apply_args(args : Args, apply_state_item : ApplyStateItem) -> None:
|
||||
apply_state_item('face_occluder_model', args.get('face_occluder_model'))
|
||||
apply_state_item('face_parser_model', args.get('face_parser_model'))
|
||||
apply_state_item('face_mask_types', args.get('face_mask_types'))
|
||||
apply_state_item('face_mask_areas', args.get('face_mask_areas'))
|
||||
apply_state_item('face_mask_regions', args.get('face_mask_regions'))
|
||||
apply_state_item('face_mask_blur', args.get('face_mask_blur'))
|
||||
apply_state_item('face_mask_padding', normalize_padding(args.get('face_mask_padding')))
|
||||
apply_state_item('face_mask_regions', args.get('face_mask_regions'))
|
||||
# frame extraction
|
||||
apply_state_item('trim_frame_start', args.get('trim_frame_start'))
|
||||
apply_state_item('trim_frame_end', args.get('trim_frame_end'))
|
||||
@@ -124,6 +125,9 @@ def apply_args(args : Args, apply_state_item : ApplyStateItem) -> None:
|
||||
# download
|
||||
apply_state_item('download_providers', args.get('download_providers'))
|
||||
apply_state_item('download_scope', args.get('download_scope'))
|
||||
# benchmark
|
||||
apply_state_item('benchmark_resolutions', args.get('benchmark_resolutions'))
|
||||
apply_state_item('benchmark_cycle_count', args.get('benchmark_cycle_count'))
|
||||
# memory
|
||||
apply_state_item('video_memory_strategy', args.get('video_memory_strategy'))
|
||||
apply_state_item('system_memory_limit', args.get('system_memory_limit'))
|
||||
|
||||
106
facefusion/benchmarker.py
Normal file
106
facefusion/benchmarker.py
Normal file
@@ -0,0 +1,106 @@
|
||||
import hashlib
|
||||
import os
|
||||
import statistics
|
||||
import tempfile
|
||||
from time import perf_counter
|
||||
from typing import Generator, List
|
||||
|
||||
import facefusion.choices
|
||||
from facefusion import core, state_manager
|
||||
from facefusion.cli_helper import render_table
|
||||
from facefusion.download import conditional_download, resolve_download_url
|
||||
from facefusion.filesystem import get_file_extension
|
||||
from facefusion.types import BenchmarkCycleSet
|
||||
from facefusion.vision import count_video_frame_total, detect_video_fps, detect_video_resolution, pack_resolution
|
||||
|
||||
|
||||
def pre_check() -> bool:
|
||||
conditional_download('.assets/examples',
|
||||
[
|
||||
resolve_download_url('examples-3.0.0', 'source.jpg'),
|
||||
resolve_download_url('examples-3.0.0', 'source.mp3'),
|
||||
resolve_download_url('examples-3.0.0', 'target-240p.mp4'),
|
||||
resolve_download_url('examples-3.0.0', 'target-360p.mp4'),
|
||||
resolve_download_url('examples-3.0.0', 'target-540p.mp4'),
|
||||
resolve_download_url('examples-3.0.0', 'target-720p.mp4'),
|
||||
resolve_download_url('examples-3.0.0', 'target-1080p.mp4'),
|
||||
resolve_download_url('examples-3.0.0', 'target-1440p.mp4'),
|
||||
resolve_download_url('examples-3.0.0', 'target-2160p.mp4')
|
||||
])
|
||||
return True
|
||||
|
||||
|
||||
def run() -> Generator[List[BenchmarkCycleSet], None, None]:
|
||||
benchmark_resolutions = state_manager.get_item('benchmark_resolutions')
|
||||
benchmark_cycle_count = state_manager.get_item('benchmark_cycle_count')
|
||||
|
||||
state_manager.init_item('source_paths', [ '.assets/examples/source.jpg', '.assets/examples/source.mp3' ])
|
||||
state_manager.init_item('face_landmarker_score', 0)
|
||||
state_manager.init_item('temp_frame_format', 'bmp')
|
||||
state_manager.init_item('output_audio_volume', 0)
|
||||
state_manager.init_item('output_video_preset', 'ultrafast')
|
||||
state_manager.init_item('video_memory_strategy', 'tolerant')
|
||||
|
||||
benchmarks = []
|
||||
target_paths = [facefusion.choices.benchmark_set.get(benchmark_resolution) for benchmark_resolution in benchmark_resolutions if benchmark_resolution in facefusion.choices.benchmark_set]
|
||||
|
||||
for target_path in target_paths:
|
||||
state_manager.set_item('target_path', target_path)
|
||||
state_manager.set_item('output_path', suggest_output_path(state_manager.get_item('target_path')))
|
||||
benchmarks.append(cycle(benchmark_cycle_count))
|
||||
yield benchmarks
|
||||
|
||||
|
||||
def cycle(cycle_count : int) -> BenchmarkCycleSet:
|
||||
process_times = []
|
||||
video_frame_total = count_video_frame_total(state_manager.get_item('target_path'))
|
||||
output_video_resolution = detect_video_resolution(state_manager.get_item('target_path'))
|
||||
state_manager.set_item('output_video_resolution', pack_resolution(output_video_resolution))
|
||||
state_manager.set_item('output_video_fps', detect_video_fps(state_manager.get_item('target_path')))
|
||||
|
||||
core.conditional_process()
|
||||
|
||||
for index in range(cycle_count):
|
||||
start_time = perf_counter()
|
||||
core.conditional_process()
|
||||
end_time = perf_counter()
|
||||
process_times.append(end_time - start_time)
|
||||
|
||||
average_run = round(statistics.mean(process_times), 2)
|
||||
fastest_run = round(min(process_times), 2)
|
||||
slowest_run = round(max(process_times), 2)
|
||||
relative_fps = round(video_frame_total * cycle_count / sum(process_times), 2)
|
||||
|
||||
return\
|
||||
{
|
||||
'target_path': state_manager.get_item('target_path'),
|
||||
'cycle_count': cycle_count,
|
||||
'average_run': average_run,
|
||||
'fastest_run': fastest_run,
|
||||
'slowest_run': slowest_run,
|
||||
'relative_fps': relative_fps
|
||||
}
|
||||
|
||||
|
||||
def suggest_output_path(target_path : str) -> str:
|
||||
target_file_extension = get_file_extension(target_path)
|
||||
return os.path.join(tempfile.gettempdir(), hashlib.sha1().hexdigest()[:8] + target_file_extension)
|
||||
|
||||
|
||||
def render() -> None:
|
||||
benchmarks = []
|
||||
headers =\
|
||||
[
|
||||
'target_path',
|
||||
'cycle_count',
|
||||
'average_run',
|
||||
'fastest_run',
|
||||
'slowest_run',
|
||||
'relative_fps'
|
||||
]
|
||||
|
||||
for benchmark in run():
|
||||
benchmarks = benchmark
|
||||
|
||||
contents = [ list(benchmark_set.values()) for benchmark_set in benchmarks ]
|
||||
render_table(headers, contents)
|
||||
@@ -2,7 +2,7 @@ import logging
|
||||
from typing import List, Sequence
|
||||
|
||||
from facefusion.common_helper import create_float_range, create_int_range
|
||||
from facefusion.types import Angle, AudioEncoder, AudioFormat, AudioTypeSet, DownloadProvider, DownloadProviderSet, DownloadScope, EncoderSet, ExecutionProvider, ExecutionProviderSet, FaceDetectorModel, FaceDetectorSet, FaceLandmarkerModel, FaceMaskRegion, FaceMaskRegionSet, FaceMaskType, FaceOccluderModel, FaceParserModel, FaceSelectorMode, FaceSelectorOrder, Gender, ImageFormat, ImageTypeSet, JobStatus, LogLevel, LogLevelSet, Race, Score, TempFrameFormat, UiWorkflow, VideoEncoder, VideoFormat, VideoMemoryStrategy, VideoPreset, VideoTypeSet, WebcamMode
|
||||
from facefusion.types import Angle, AudioEncoder, AudioFormat, AudioTypeSet, BenchmarkResolution, BenchmarkSet, DownloadProvider, DownloadProviderSet, DownloadScope, EncoderSet, ExecutionProvider, ExecutionProviderSet, FaceDetectorModel, FaceDetectorSet, FaceLandmarkerModel, FaceMaskArea, FaceMaskAreaSet, FaceMaskRegion, FaceMaskRegionSet, FaceMaskType, FaceOccluderModel, FaceParserModel, FaceSelectorMode, FaceSelectorOrder, Gender, ImageFormat, ImageTypeSet, JobStatus, LogLevel, LogLevelSet, Race, Score, TempFrameFormat, UiWorkflow, VideoEncoder, VideoFormat, VideoMemoryStrategy, VideoPreset, VideoTypeSet, WebcamMode
|
||||
|
||||
face_detector_set : FaceDetectorSet =\
|
||||
{
|
||||
@@ -19,7 +19,13 @@ face_selector_genders : List[Gender] = [ 'female', 'male' ]
|
||||
face_selector_races : List[Race] = [ 'white', 'black', 'latino', 'asian', 'indian', 'arabic' ]
|
||||
face_occluder_models : List[FaceOccluderModel] = [ 'xseg_1', 'xseg_2', 'xseg_3' ]
|
||||
face_parser_models : List[FaceParserModel] = [ 'bisenet_resnet_18', 'bisenet_resnet_34' ]
|
||||
face_mask_types : List[FaceMaskType] = [ 'box', 'occlusion', 'region' ]
|
||||
face_mask_types : List[FaceMaskType] = [ 'box', 'occlusion', 'area', 'region' ]
|
||||
face_mask_area_set : FaceMaskAreaSet =\
|
||||
{
|
||||
'upper-face': [ 0, 1, 2, 31, 32, 33, 34, 35, 14, 15, 16, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17 ],
|
||||
'lower-face': [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 35, 34, 33, 32, 31 ],
|
||||
'mouth': [ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67 ]
|
||||
}
|
||||
face_mask_region_set : FaceMaskRegionSet =\
|
||||
{
|
||||
'skin': 1,
|
||||
@@ -33,6 +39,7 @@ face_mask_region_set : FaceMaskRegionSet =\
|
||||
'upper-lip': 12,
|
||||
'lower-lip': 13
|
||||
}
|
||||
face_mask_areas : List[FaceMaskArea] = list(face_mask_area_set.keys())
|
||||
face_mask_regions : List[FaceMaskRegion] = list(face_mask_region_set.keys())
|
||||
|
||||
audio_type_set : AudioTypeSet =\
|
||||
@@ -78,6 +85,18 @@ output_video_presets : List[VideoPreset] = [ 'ultrafast', 'superfast', 'veryfast
|
||||
image_template_sizes : List[float] = [ 0.25, 0.5, 0.75, 1, 1.5, 2, 2.5, 3, 3.5, 4 ]
|
||||
video_template_sizes : List[int] = [ 240, 360, 480, 540, 720, 1080, 1440, 2160, 4320 ]
|
||||
|
||||
benchmark_set : BenchmarkSet =\
|
||||
{
|
||||
'240p': '.assets/examples/target-240p.mp4',
|
||||
'360p': '.assets/examples/target-360p.mp4',
|
||||
'540p': '.assets/examples/target-540p.mp4',
|
||||
'720p': '.assets/examples/target-720p.mp4',
|
||||
'1080p': '.assets/examples/target-1080p.mp4',
|
||||
'1440p': '.assets/examples/target-1440p.mp4',
|
||||
'2160p': '.assets/examples/target-2160p.mp4'
|
||||
}
|
||||
benchmark_resolutions : List[BenchmarkResolution] = list(benchmark_set.keys())
|
||||
|
||||
webcam_modes : List[WebcamMode] = [ 'inline', 'udp', 'v4l2' ]
|
||||
webcam_resolutions : List[str] = [ '320x240', '640x480', '800x600', '1024x768', '1280x720', '1280x960', '1920x1080', '2560x1440', '3840x2160' ]
|
||||
|
||||
@@ -129,6 +148,7 @@ log_levels : List[LogLevel] = list(log_level_set.keys())
|
||||
ui_workflows : List[UiWorkflow] = [ 'instant_runner', 'job_runner', 'job_manager' ]
|
||||
job_statuses : List[JobStatus] = [ 'drafted', 'queued', 'completed', 'failed' ]
|
||||
|
||||
benchmark_cycle_count_range : Sequence[int] = create_int_range(1, 10, 1)
|
||||
execution_thread_count_range : Sequence[int] = create_int_range(1, 32, 1)
|
||||
execution_queue_count_range : Sequence[int] = create_int_range(1, 4, 1)
|
||||
system_memory_limit_range : Sequence[int] = create_int_range(0, 128, 4)
|
||||
|
||||
@@ -8,15 +8,15 @@ def render_table(headers : TableHeaders, contents : TableContents) -> None:
|
||||
package_logger = get_package_logger()
|
||||
table_column, table_separator = create_table_parts(headers, contents)
|
||||
|
||||
package_logger.info(table_separator)
|
||||
package_logger.info(table_column.format(*headers))
|
||||
package_logger.info(table_separator)
|
||||
package_logger.critical(table_separator)
|
||||
package_logger.critical(table_column.format(*headers))
|
||||
package_logger.critical(table_separator)
|
||||
|
||||
for content in contents:
|
||||
content = [ value if value else '' for value in content ]
|
||||
package_logger.info(table_column.format(*content))
|
||||
content = [ str(value) for value in content ]
|
||||
package_logger.critical(table_column.format(*content))
|
||||
|
||||
package_logger.info(table_separator)
|
||||
package_logger.critical(table_separator)
|
||||
|
||||
|
||||
def create_table_parts(headers : TableHeaders, contents : TableContents) -> Tuple[str, str]:
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
from functools import lru_cache
|
||||
from typing import List
|
||||
from typing import List, Tuple
|
||||
|
||||
import numpy
|
||||
from tqdm import tqdm
|
||||
|
||||
from facefusion import inference_manager, state_manager, wording
|
||||
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
|
||||
from facefusion.execution import has_execution_provider
|
||||
from facefusion.filesystem import resolve_relative_path
|
||||
from facefusion.thread_helper import conditional_thread_semaphore
|
||||
from facefusion.types import Detection, DownloadScope, Fps, InferencePool, ModelOptions, ModelSet, Score, VisionFrame
|
||||
from facefusion.types import Detection, DownloadScope, DownloadSet, ExecutionProvider, Fps, InferencePool, ModelSet, VisionFrame
|
||||
from facefusion.vision import detect_video_fps, fit_frame, read_image, read_video_frame
|
||||
|
||||
STREAM_COUNTER = 0
|
||||
@@ -18,48 +19,107 @@ STREAM_COUNTER = 0
|
||||
def create_static_model_set(download_scope : DownloadScope) -> ModelSet:
|
||||
return\
|
||||
{
|
||||
'yolo_nsfw':
|
||||
'nsfw_1':
|
||||
{
|
||||
'hashes':
|
||||
{
|
||||
'content_analyser':
|
||||
{
|
||||
'url': resolve_download_url('models-3.2.0', 'yolo_11m_nsfw.hash'),
|
||||
'path': resolve_relative_path('../.assets/models/yolo_11m_nsfw.hash')
|
||||
'url': resolve_download_url('models-3.3.0', 'nsfw_1.hash'),
|
||||
'path': resolve_relative_path('../.assets/models/nsfw_1.hash')
|
||||
}
|
||||
},
|
||||
'sources':
|
||||
{
|
||||
'content_analyser':
|
||||
{
|
||||
'url': resolve_download_url('models-3.2.0', 'yolo_11m_nsfw.onnx'),
|
||||
'path': resolve_relative_path('../.assets/models/yolo_11m_nsfw.onnx')
|
||||
'url': resolve_download_url('models-3.3.0', 'nsfw_1.onnx'),
|
||||
'path': resolve_relative_path('../.assets/models/nsfw_1.onnx')
|
||||
}
|
||||
},
|
||||
'size': (640, 640)
|
||||
'size': (640, 640),
|
||||
'mean': (0.0, 0.0, 0.0),
|
||||
'standard_deviation': (1.0, 1.0, 1.0)
|
||||
},
|
||||
'nsfw_2':
|
||||
{
|
||||
'hashes':
|
||||
{
|
||||
'content_analyser':
|
||||
{
|
||||
'url': resolve_download_url('models-3.3.0', 'nsfw_2.hash'),
|
||||
'path': resolve_relative_path('../.assets/models/nsfw_2.hash')
|
||||
}
|
||||
},
|
||||
'sources':
|
||||
{
|
||||
'content_analyser':
|
||||
{
|
||||
'url': resolve_download_url('models-3.3.0', 'nsfw_2.onnx'),
|
||||
'path': resolve_relative_path('../.assets/models/nsfw_2.onnx')
|
||||
}
|
||||
},
|
||||
'size': (384, 384),
|
||||
'mean': (0.5, 0.5, 0.5),
|
||||
'standard_deviation': (0.5, 0.5, 0.5)
|
||||
},
|
||||
'nsfw_3':
|
||||
{
|
||||
'hashes':
|
||||
{
|
||||
'content_analyser':
|
||||
{
|
||||
'url': resolve_download_url('models-3.3.0', 'nsfw_3.hash'),
|
||||
'path': resolve_relative_path('../.assets/models/nsfw_3.hash')
|
||||
}
|
||||
},
|
||||
'sources':
|
||||
{
|
||||
'content_analyser':
|
||||
{
|
||||
'url': resolve_download_url('models-3.3.0', 'nsfw_3.onnx'),
|
||||
'path': resolve_relative_path('../.assets/models/nsfw_3.onnx')
|
||||
}
|
||||
},
|
||||
'size': (448, 448),
|
||||
'mean': (0.48145466, 0.4578275, 0.40821073),
|
||||
'standard_deviation': (0.26862954, 0.26130258, 0.27577711)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def get_inference_pool() -> InferencePool:
|
||||
model_names = [ 'yolo_nsfw' ]
|
||||
model_source_set = get_model_options().get('sources')
|
||||
model_names = [ 'nsfw_1', 'nsfw_2', 'nsfw_3' ]
|
||||
_, model_source_set = collect_model_downloads()
|
||||
|
||||
return inference_manager.get_inference_pool(__name__, model_names, model_source_set)
|
||||
|
||||
|
||||
def clear_inference_pool() -> None:
|
||||
model_names = [ 'yolo_nsfw' ]
|
||||
model_names = [ 'nsfw_1', 'nsfw_2', 'nsfw_3' ]
|
||||
inference_manager.clear_inference_pool(__name__, model_names)
|
||||
|
||||
|
||||
def get_model_options() -> ModelOptions:
|
||||
return create_static_model_set('full').get('yolo_nsfw')
|
||||
def resolve_execution_providers() -> List[ExecutionProvider]:
|
||||
if has_execution_provider('coreml'):
|
||||
return [ 'cpu' ]
|
||||
return state_manager.get_item('execution_providers')
|
||||
|
||||
|
||||
def collect_model_downloads() -> Tuple[DownloadSet, DownloadSet]:
|
||||
model_set = create_static_model_set('full')
|
||||
model_hash_set = {}
|
||||
model_source_set = {}
|
||||
|
||||
for content_analyser_model in [ 'nsfw_1', 'nsfw_2', 'nsfw_3' ]:
|
||||
model_hash_set[content_analyser_model] = model_set.get(content_analyser_model).get('hashes').get('content_analyser')
|
||||
model_source_set[content_analyser_model] = model_set.get(content_analyser_model).get('sources').get('content_analyser')
|
||||
|
||||
return model_hash_set, model_source_set
|
||||
|
||||
|
||||
def pre_check() -> bool:
|
||||
model_hash_set = get_model_options().get('hashes')
|
||||
model_source_set = get_model_options().get('sources')
|
||||
model_hash_set, model_source_set = collect_model_downloads()
|
||||
|
||||
return conditional_download_hashes(model_hash_set) and conditional_download_sources(model_source_set)
|
||||
|
||||
@@ -74,9 +134,7 @@ def analyse_stream(vision_frame : VisionFrame, video_fps : Fps) -> bool:
|
||||
|
||||
|
||||
def analyse_frame(vision_frame : VisionFrame) -> bool:
|
||||
nsfw_scores = detect_nsfw(vision_frame)
|
||||
|
||||
return len(nsfw_scores) > 0
|
||||
return detect_nsfw(vision_frame)
|
||||
|
||||
|
||||
@lru_cache(maxsize = None)
|
||||
@@ -106,39 +164,62 @@ def analyse_video(video_path : str, trim_frame_start : int, trim_frame_end : int
|
||||
progress.set_postfix(rate = rate)
|
||||
progress.update()
|
||||
|
||||
return rate > 10.0
|
||||
return bool(rate > 10.0)
|
||||
|
||||
|
||||
def detect_nsfw(vision_frame : VisionFrame) -> List[Score]:
|
||||
nsfw_scores = []
|
||||
model_size = get_model_options().get('size')
|
||||
temp_vision_frame = fit_frame(vision_frame, model_size)
|
||||
detect_vision_frame = prepare_detect_frame(temp_vision_frame)
|
||||
detection = forward(detect_vision_frame)
|
||||
detection = numpy.squeeze(detection).T
|
||||
nsfw_scores_raw = numpy.amax(detection[:, 4:], axis = 1)
|
||||
keep_indices = numpy.where(nsfw_scores_raw > 0.2)[0]
|
||||
def detect_nsfw(vision_frame : VisionFrame) -> bool:
|
||||
is_nsfw_1 = detect_with_nsfw_1(vision_frame)
|
||||
is_nsfw_2 = detect_with_nsfw_2(vision_frame)
|
||||
is_nsfw_3 = detect_with_nsfw_3(vision_frame)
|
||||
|
||||
if numpy.any(keep_indices):
|
||||
nsfw_scores_raw = nsfw_scores_raw[keep_indices]
|
||||
nsfw_scores = nsfw_scores_raw.ravel().tolist()
|
||||
|
||||
return nsfw_scores
|
||||
return is_nsfw_1 and is_nsfw_2 or is_nsfw_1 and is_nsfw_3 or is_nsfw_2 and is_nsfw_3
|
||||
|
||||
|
||||
def forward(vision_frame : VisionFrame) -> Detection:
|
||||
content_analyser = get_inference_pool().get('content_analyser')
|
||||
def detect_with_nsfw_1(vision_frame : VisionFrame) -> bool:
|
||||
detect_vision_frame = prepare_detect_frame(vision_frame, 'nsfw_1')
|
||||
detection = forward_nsfw(detect_vision_frame, 'nsfw_1')
|
||||
detection_score = numpy.max(numpy.amax(detection[:, 4:], axis = 1))
|
||||
return bool(detection_score > 0.2)
|
||||
|
||||
|
||||
def detect_with_nsfw_2(vision_frame : VisionFrame) -> bool:
|
||||
detect_vision_frame = prepare_detect_frame(vision_frame, 'nsfw_2')
|
||||
detection = forward_nsfw(detect_vision_frame, 'nsfw_2')
|
||||
detection_score = detection[0] - detection[1]
|
||||
return bool(detection_score > 0.25)
|
||||
|
||||
|
||||
def detect_with_nsfw_3(vision_frame : VisionFrame) -> bool:
|
||||
detect_vision_frame = prepare_detect_frame(vision_frame, 'nsfw_3')
|
||||
detection = forward_nsfw(detect_vision_frame, 'nsfw_3')
|
||||
detection_score = (detection[2] + detection[3]) - (detection[0] + detection[1])
|
||||
return bool(detection_score > 10.5)
|
||||
|
||||
|
||||
def forward_nsfw(vision_frame : VisionFrame, nsfw_model : str) -> Detection:
|
||||
content_analyser = get_inference_pool().get(nsfw_model)
|
||||
|
||||
with conditional_thread_semaphore():
|
||||
detection = content_analyser.run(None,
|
||||
{
|
||||
'input': vision_frame
|
||||
})
|
||||
})[0]
|
||||
|
||||
if nsfw_model in [ 'nsfw_2', 'nsfw_3' ]:
|
||||
return detection[0]
|
||||
|
||||
return detection
|
||||
|
||||
|
||||
def prepare_detect_frame(temp_vision_frame : VisionFrame) -> VisionFrame:
|
||||
detect_vision_frame = temp_vision_frame / 255.0
|
||||
def prepare_detect_frame(temp_vision_frame : VisionFrame, model_name : str) -> VisionFrame:
|
||||
model_set = create_static_model_set('full').get(model_name)
|
||||
model_size = model_set.get('size')
|
||||
model_mean = model_set.get('mean')
|
||||
model_standard_deviation = model_set.get('standard_deviation')
|
||||
|
||||
detect_vision_frame = fit_frame(temp_vision_frame, model_size)
|
||||
detect_vision_frame = detect_vision_frame[:, :, ::-1] / 255.0
|
||||
detect_vision_frame -= model_mean
|
||||
detect_vision_frame /= model_standard_deviation
|
||||
detect_vision_frame = numpy.expand_dims(detect_vision_frame.transpose(2, 0, 1), axis = 0).astype(numpy.float32)
|
||||
return detect_vision_frame
|
||||
|
||||
@@ -6,12 +6,12 @@ from time import time
|
||||
|
||||
import numpy
|
||||
|
||||
from facefusion import cli_helper, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, logger, process_manager, state_manager, voice_extractor, wording
|
||||
from facefusion import benchmarker, cli_helper, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, logger, process_manager, state_manager, video_manager, voice_extractor, wording
|
||||
from facefusion.args import apply_args, collect_job_args, reduce_job_args, reduce_step_args
|
||||
from facefusion.common_helper import get_first
|
||||
from facefusion.content_analyser import analyse_image, analyse_video
|
||||
from facefusion.download import conditional_download_hashes, conditional_download_sources
|
||||
from facefusion.exit_helper import graceful_exit, hard_exit
|
||||
from facefusion.exit_helper import hard_exit, signal_exit
|
||||
from facefusion.face_analyser import get_average_face, get_many_faces, get_one_face
|
||||
from facefusion.face_selector import sort_and_filter_faces
|
||||
from facefusion.face_store import append_reference_face, clear_reference_faces, get_reference_faces
|
||||
@@ -23,7 +23,6 @@ from facefusion.memory import limit_system_memory
|
||||
from facefusion.processors.core import get_processors_modules
|
||||
from facefusion.program import create_program
|
||||
from facefusion.program_helper import validate_args
|
||||
from facefusion.statistics import conditional_log_statistics
|
||||
from facefusion.temp_helper import clear_temp_directory, create_temp_directory, get_temp_file_path, move_temp_file, resolve_temp_frame_paths
|
||||
from facefusion.types import Args, ErrorCode
|
||||
from facefusion.vision import pack_resolution, read_image, read_static_images, read_video_frame, restrict_image_resolution, restrict_trim_frame, restrict_video_fps, restrict_video_resolution, unpack_resolution
|
||||
@@ -31,7 +30,7 @@ from facefusion.vision import pack_resolution, read_image, read_static_images, r
|
||||
|
||||
def cli() -> None:
|
||||
if pre_check():
|
||||
signal.signal(signal.SIGINT, lambda signal_number, frame: graceful_exit(0))
|
||||
signal.signal(signal.SIGINT, signal_exit)
|
||||
program = create_program()
|
||||
|
||||
if validate_args(program):
|
||||
@@ -59,6 +58,11 @@ def route(args : Args) -> None:
|
||||
error_code = force_download()
|
||||
return hard_exit(error_code)
|
||||
|
||||
if state_manager.get_item('command') == 'benchmark':
|
||||
if not common_pre_check() or not processors_pre_check() or not benchmarker.pre_check():
|
||||
return hard_exit(2)
|
||||
benchmarker.render()
|
||||
|
||||
if state_manager.get_item('command') in [ 'job-list', 'job-create', 'job-submit', 'job-submit-all', 'job-delete', 'job-delete-all', 'job-add-step', 'job-remix-step', 'job-insert-step', 'job-remove-step' ]:
|
||||
if not job_manager.init_jobs(state_manager.get_item('jobs_path')):
|
||||
hard_exit(1)
|
||||
@@ -383,10 +387,10 @@ def process_image(start_time : float) -> ErrorCode:
|
||||
process_manager.end()
|
||||
return 1
|
||||
|
||||
temp_file_path = get_temp_file_path(state_manager.get_item('target_path'))
|
||||
temp_image_path = get_temp_file_path(state_manager.get_item('target_path'))
|
||||
for processor_module in get_processors_modules(state_manager.get_item('processors')):
|
||||
logger.info(wording.get('processing'), processor_module.__name__)
|
||||
processor_module.process_image(state_manager.get_item('source_paths'), temp_file_path, temp_file_path)
|
||||
processor_module.process_image(state_manager.get_item('source_paths'), temp_image_path, temp_image_path)
|
||||
processor_module.post_process()
|
||||
if is_process_stopping():
|
||||
process_manager.end()
|
||||
@@ -404,7 +408,6 @@ def process_image(start_time : float) -> ErrorCode:
|
||||
if is_image(state_manager.get_item('output_path')):
|
||||
seconds = '{:.2f}'.format((time() - start_time) % 60)
|
||||
logger.info(wording.get('processing_image_succeed').format(seconds = seconds), __name__)
|
||||
conditional_log_statistics()
|
||||
else:
|
||||
logger.error(wording.get('processing_image_failed'), __name__)
|
||||
process_manager.end()
|
||||
@@ -468,8 +471,10 @@ def process_video(start_time : float) -> ErrorCode:
|
||||
source_audio_path = get_first(filter_audio_paths(state_manager.get_item('source_paths')))
|
||||
if source_audio_path:
|
||||
if replace_audio(state_manager.get_item('target_path'), source_audio_path, state_manager.get_item('output_path')):
|
||||
video_manager.clear_video_pool()
|
||||
logger.debug(wording.get('replacing_audio_succeed'), __name__)
|
||||
else:
|
||||
video_manager.clear_video_pool()
|
||||
if is_process_stopping():
|
||||
process_manager.end()
|
||||
return 4
|
||||
@@ -477,8 +482,10 @@ def process_video(start_time : float) -> ErrorCode:
|
||||
move_temp_file(state_manager.get_item('target_path'), state_manager.get_item('output_path'))
|
||||
else:
|
||||
if 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(wording.get('restoring_audio_succeed'), __name__)
|
||||
else:
|
||||
video_manager.clear_video_pool()
|
||||
if is_process_stopping():
|
||||
process_manager.end()
|
||||
return 4
|
||||
@@ -491,7 +498,6 @@ def process_video(start_time : float) -> ErrorCode:
|
||||
if is_video(state_manager.get_item('output_path')):
|
||||
seconds = '{:.2f}'.format((time() - start_time))
|
||||
logger.info(wording.get('processing_video_succeed').format(seconds = seconds), __name__)
|
||||
conditional_log_statistics()
|
||||
else:
|
||||
logger.error(wording.get('processing_video_failed'), __name__)
|
||||
process_manager.end()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import signal
|
||||
import sys
|
||||
from time import sleep
|
||||
from types import FrameType
|
||||
|
||||
from facefusion import process_manager, state_manager
|
||||
from facefusion.temp_helper import clear_temp_directory
|
||||
@@ -12,6 +13,10 @@ def hard_exit(error_code : ErrorCode) -> None:
|
||||
sys.exit(error_code)
|
||||
|
||||
|
||||
def signal_exit(signum : int, frame : FrameType) -> None:
|
||||
graceful_exit(0)
|
||||
|
||||
|
||||
def graceful_exit(error_code : ErrorCode) -> None:
|
||||
process_manager.stop()
|
||||
while process_manager.is_processing():
|
||||
|
||||
@@ -99,15 +99,35 @@ def warp_face_by_translation(temp_vision_frame : VisionFrame, translation : Tran
|
||||
|
||||
|
||||
def paste_back(temp_vision_frame : VisionFrame, crop_vision_frame : VisionFrame, crop_mask : Mask, affine_matrix : Matrix) -> VisionFrame:
|
||||
paste_bounding_box, paste_matrix = calc_paste_area(temp_vision_frame, crop_vision_frame, affine_matrix)
|
||||
x_min, y_min, x_max, y_max = paste_bounding_box
|
||||
paste_width = x_max - x_min
|
||||
paste_height = y_max - y_min
|
||||
inverse_mask = cv2.warpAffine(crop_mask, paste_matrix, (paste_width, paste_height)).clip(0, 1)
|
||||
inverse_mask = numpy.expand_dims(inverse_mask, axis = -1)
|
||||
inverse_vision_frame = cv2.warpAffine(crop_vision_frame, paste_matrix, (paste_width, paste_height), borderMode = cv2.BORDER_REPLICATE)
|
||||
temp_vision_frame = temp_vision_frame.copy()
|
||||
paste_vision_frame = temp_vision_frame[y_min:y_max, x_min:x_max]
|
||||
paste_vision_frame = paste_vision_frame * (1 - inverse_mask) + inverse_vision_frame * inverse_mask
|
||||
temp_vision_frame[y_min:y_max, x_min:x_max] = paste_vision_frame.astype(temp_vision_frame.dtype)
|
||||
return temp_vision_frame
|
||||
|
||||
|
||||
def calc_paste_area(temp_vision_frame : VisionFrame, crop_vision_frame : VisionFrame, affine_matrix : Matrix) -> Tuple[BoundingBox, Matrix]:
|
||||
temp_height, temp_width = temp_vision_frame.shape[:2]
|
||||
crop_height, crop_width = crop_vision_frame.shape[:2]
|
||||
inverse_matrix = cv2.invertAffineTransform(affine_matrix)
|
||||
temp_size = temp_vision_frame.shape[:2][::-1]
|
||||
inverse_mask = cv2.warpAffine(crop_mask, inverse_matrix, temp_size).clip(0, 1)
|
||||
inverse_vision_frame = cv2.warpAffine(crop_vision_frame, inverse_matrix, temp_size, borderMode = cv2.BORDER_REPLICATE)
|
||||
paste_vision_frame = temp_vision_frame.copy()
|
||||
paste_vision_frame[:, :, 0] = inverse_mask * inverse_vision_frame[:, :, 0] + (1 - inverse_mask) * temp_vision_frame[:, :, 0]
|
||||
paste_vision_frame[:, :, 1] = inverse_mask * inverse_vision_frame[:, :, 1] + (1 - inverse_mask) * temp_vision_frame[:, :, 1]
|
||||
paste_vision_frame[:, :, 2] = inverse_mask * inverse_vision_frame[:, :, 2] + (1 - inverse_mask) * temp_vision_frame[:, :, 2]
|
||||
return paste_vision_frame
|
||||
crop_points = numpy.array([ [ 0, 0 ], [ crop_width, 0 ], [ crop_width, crop_height ], [ 0, crop_height ] ])
|
||||
paste_region_points = transform_points(crop_points, inverse_matrix)
|
||||
min_point = numpy.floor(paste_region_points.min(axis = 0)).astype(int)
|
||||
max_point = numpy.ceil(paste_region_points.max(axis = 0)).astype(int)
|
||||
x_min, y_min = numpy.clip(min_point, 0, [ temp_width, temp_height ])
|
||||
x_max, y_max = numpy.clip(max_point, 0, [ temp_width, temp_height ])
|
||||
paste_bounding_box = numpy.array([ x_min, y_min, x_max, y_max ])
|
||||
paste_matrix = inverse_matrix.copy()
|
||||
paste_matrix[0, 2] -= x_min
|
||||
paste_matrix[1, 2] -= y_min
|
||||
return paste_bounding_box, paste_matrix
|
||||
|
||||
|
||||
@lru_cache(maxsize = None)
|
||||
|
||||
@@ -3,14 +3,13 @@ from typing import List, Tuple
|
||||
|
||||
import cv2
|
||||
import numpy
|
||||
from cv2.typing import Size
|
||||
|
||||
import facefusion.choices
|
||||
from facefusion import inference_manager, state_manager
|
||||
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
|
||||
from facefusion.filesystem import resolve_relative_path
|
||||
from facefusion.thread_helper import conditional_thread_semaphore
|
||||
from facefusion.types import DownloadScope, DownloadSet, FaceLandmark68, FaceMaskRegion, InferencePool, Mask, ModelSet, Padding, VisionFrame
|
||||
from facefusion.types import DownloadScope, DownloadSet, FaceLandmark68, FaceMaskArea, FaceMaskRegion, InferencePool, Mask, ModelSet, Padding, VisionFrame
|
||||
|
||||
|
||||
@lru_cache(maxsize = None)
|
||||
@@ -156,8 +155,8 @@ def pre_check() -> bool:
|
||||
return conditional_download_hashes(model_hash_set) and conditional_download_sources(model_source_set)
|
||||
|
||||
|
||||
@lru_cache(maxsize = None)
|
||||
def create_static_box_mask(crop_size : Size, face_mask_blur : float, face_mask_padding : Padding) -> Mask:
|
||||
def create_box_mask(crop_vision_frame : VisionFrame, face_mask_blur : float, face_mask_padding : Padding) -> Mask:
|
||||
crop_size = crop_vision_frame.shape[:2][::-1]
|
||||
blur_amount = int(crop_size[0] * 0.5 * face_mask_blur)
|
||||
blur_area = max(blur_amount // 2, 1)
|
||||
box_mask : Mask = numpy.ones(crop_size).astype(numpy.float32)
|
||||
@@ -165,6 +164,7 @@ def create_static_box_mask(crop_size : Size, face_mask_blur : float, face_mask_p
|
||||
box_mask[-max(blur_area, int(crop_size[1] * face_mask_padding[2] / 100)):, :] = 0
|
||||
box_mask[:, :max(blur_area, int(crop_size[0] * face_mask_padding[3] / 100))] = 0
|
||||
box_mask[:, -max(blur_area, int(crop_size[0] * face_mask_padding[1] / 100)):] = 0
|
||||
|
||||
if blur_amount > 0:
|
||||
box_mask = cv2.GaussianBlur(box_mask, (0, 0), blur_amount * 0.25)
|
||||
return box_mask
|
||||
@@ -183,6 +183,21 @@ def create_occlusion_mask(crop_vision_frame : VisionFrame) -> Mask:
|
||||
return occlusion_mask
|
||||
|
||||
|
||||
def create_area_mask(crop_vision_frame : VisionFrame, face_landmark_68 : FaceLandmark68, face_mask_areas : List[FaceMaskArea]) -> Mask:
|
||||
crop_size = crop_vision_frame.shape[:2][::-1]
|
||||
landmark_points = []
|
||||
|
||||
for face_mask_area in face_mask_areas:
|
||||
if face_mask_area in facefusion.choices.face_mask_area_set:
|
||||
landmark_points.extend(facefusion.choices.face_mask_area_set.get(face_mask_area))
|
||||
|
||||
convex_hull = cv2.convexHull(face_landmark_68[landmark_points].astype(numpy.int32))
|
||||
area_mask = numpy.zeros(crop_size).astype(numpy.float32)
|
||||
cv2.fillConvexPoly(area_mask, convex_hull, 1.0) # type: ignore[call-overload]
|
||||
area_mask = (cv2.GaussianBlur(area_mask.clip(0, 1), (0, 0), 5).clip(0.5, 1) - 0.5) * 2
|
||||
return area_mask
|
||||
|
||||
|
||||
def create_region_mask(crop_vision_frame : VisionFrame, face_mask_regions : List[FaceMaskRegion]) -> Mask:
|
||||
model_name = state_manager.get_item('face_parser_model')
|
||||
model_size = create_static_model_set('full').get(model_name).get('size')
|
||||
@@ -199,15 +214,6 @@ def create_region_mask(crop_vision_frame : VisionFrame, face_mask_regions : List
|
||||
return region_mask
|
||||
|
||||
|
||||
def create_mouth_mask(face_landmark_68 : FaceLandmark68) -> Mask:
|
||||
convex_hull = cv2.convexHull(face_landmark_68[numpy.r_[3:14, 31:36]].astype(numpy.int32))
|
||||
mouth_mask : Mask = numpy.zeros((512, 512)).astype(numpy.float32)
|
||||
mouth_mask = cv2.fillConvexPoly(mouth_mask, convex_hull, 1.0) #type:ignore[call-overload]
|
||||
mouth_mask = cv2.erode(mouth_mask.clip(0, 1), numpy.ones((21, 3)))
|
||||
mouth_mask = cv2.GaussianBlur(mouth_mask, (0, 0), sigmaX = 1, sigmaY = 15)
|
||||
return mouth_mask
|
||||
|
||||
|
||||
def forward_occlude_face(prepare_vision_frame : VisionFrame) -> Mask:
|
||||
model_name = state_manager.get_item('face_occluder_model')
|
||||
face_occluder = get_inference_pool().get(model_name)
|
||||
|
||||
@@ -3,7 +3,7 @@ from typing import List
|
||||
import numpy
|
||||
|
||||
from facefusion import state_manager
|
||||
from facefusion.types import Face, FaceSelectorOrder, FaceSet, Gender, Race
|
||||
from facefusion.types import Face, FaceSelectorOrder, FaceSet, Gender, Race, Score
|
||||
|
||||
|
||||
def find_similar_faces(faces : List[Face], reference_faces : FaceSet, face_distance : float) -> List[Face]:
|
||||
@@ -46,24 +46,40 @@ def sort_and_filter_faces(faces : List[Face]) -> List[Face]:
|
||||
|
||||
def sort_faces_by_order(faces : List[Face], order : FaceSelectorOrder) -> List[Face]:
|
||||
if order == 'left-right':
|
||||
return sorted(faces, key = lambda face: face.bounding_box[0])
|
||||
return sorted(faces, key = get_bounding_box_left)
|
||||
if order == 'right-left':
|
||||
return sorted(faces, key = lambda face: face.bounding_box[0], reverse = True)
|
||||
return sorted(faces, key = get_bounding_box_left, reverse = True)
|
||||
if order == 'top-bottom':
|
||||
return sorted(faces, key = lambda face: face.bounding_box[1])
|
||||
return sorted(faces, key = get_bounding_box_top)
|
||||
if order == 'bottom-top':
|
||||
return sorted(faces, key = lambda face: face.bounding_box[1], reverse = True)
|
||||
return sorted(faces, key = get_bounding_box_top, reverse = True)
|
||||
if order == 'small-large':
|
||||
return sorted(faces, key = lambda face: (face.bounding_box[2] - face.bounding_box[0]) * (face.bounding_box[3] - face.bounding_box[1]))
|
||||
return sorted(faces, key = get_bounding_box_area)
|
||||
if order == 'large-small':
|
||||
return sorted(faces, key = lambda face: (face.bounding_box[2] - face.bounding_box[0]) * (face.bounding_box[3] - face.bounding_box[1]), reverse = True)
|
||||
return sorted(faces, key = get_bounding_box_area, reverse = True)
|
||||
if order == 'best-worst':
|
||||
return sorted(faces, key = lambda face: face.score_set.get('detector'), reverse = True)
|
||||
return sorted(faces, key = get_face_detector_score, reverse = True)
|
||||
if order == 'worst-best':
|
||||
return sorted(faces, key = lambda face: face.score_set.get('detector'))
|
||||
return sorted(faces, key = get_face_detector_score)
|
||||
return faces
|
||||
|
||||
|
||||
def get_bounding_box_left(face : Face) -> float:
|
||||
return face.bounding_box[0]
|
||||
|
||||
|
||||
def get_bounding_box_top(face : Face) -> float:
|
||||
return face.bounding_box[1]
|
||||
|
||||
|
||||
def get_bounding_box_area(face : Face) -> float:
|
||||
return (face.bounding_box[2] - face.bounding_box[0]) * (face.bounding_box[3] - face.bounding_box[1])
|
||||
|
||||
|
||||
def get_face_detector_score(face : Face) -> Score:
|
||||
return face.score_set.get('detector')
|
||||
|
||||
|
||||
def filter_faces_by_gender(faces : List[Face], gender : Gender) -> List[Face]:
|
||||
filter_faces = []
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import hashlib
|
||||
from typing import List, Optional
|
||||
|
||||
import numpy
|
||||
|
||||
from facefusion.hash_helper import create_hash
|
||||
from facefusion.types import Face, FaceSet, FaceStore, VisionFrame
|
||||
|
||||
FACE_STORE : FaceStore =\
|
||||
@@ -17,27 +15,22 @@ def get_face_store() -> FaceStore:
|
||||
|
||||
|
||||
def get_static_faces(vision_frame : VisionFrame) -> Optional[List[Face]]:
|
||||
frame_hash = create_frame_hash(vision_frame)
|
||||
if frame_hash in FACE_STORE['static_faces']:
|
||||
return FACE_STORE['static_faces'][frame_hash]
|
||||
vision_area = crop_vision_area(vision_frame)
|
||||
vision_hash = create_hash(vision_area.tobytes())
|
||||
if vision_hash in FACE_STORE['static_faces']:
|
||||
return FACE_STORE['static_faces'][vision_hash]
|
||||
return None
|
||||
|
||||
|
||||
def set_static_faces(vision_frame : VisionFrame, faces : List[Face]) -> None:
|
||||
frame_hash = create_frame_hash(vision_frame)
|
||||
if frame_hash:
|
||||
FACE_STORE['static_faces'][frame_hash] = faces
|
||||
vision_area = crop_vision_area(vision_frame)
|
||||
vision_hash = create_hash(vision_area.tobytes())
|
||||
if vision_hash:
|
||||
FACE_STORE['static_faces'][vision_hash] = faces
|
||||
|
||||
|
||||
def clear_static_faces() -> None:
|
||||
FACE_STORE['static_faces'] = {}
|
||||
|
||||
|
||||
def create_frame_hash(vision_frame : VisionFrame) -> Optional[str]:
|
||||
if numpy.any(vision_frame):
|
||||
frame_hash = hashlib.blake2b(vision_frame.tobytes(), digest_size = 16).hexdigest()
|
||||
return frame_hash
|
||||
return None
|
||||
FACE_STORE['static_faces'].clear()
|
||||
|
||||
|
||||
def get_reference_faces() -> Optional[FaceSet]:
|
||||
@@ -53,4 +46,11 @@ def append_reference_face(name : str, face : Face) -> None:
|
||||
|
||||
|
||||
def clear_reference_faces() -> None:
|
||||
FACE_STORE['reference_faces'] = {}
|
||||
FACE_STORE['reference_faces'].clear()
|
||||
|
||||
|
||||
def crop_vision_area(vision_frame : VisionFrame) -> VisionFrame:
|
||||
height, width = vision_frame.shape[:2]
|
||||
center_y, center_x = height // 2, width // 2
|
||||
vision_area = vision_frame[center_y - 16 : center_y + 16, center_x - 16 : center_x + 16]
|
||||
return vision_area
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
from typing import List, Optional
|
||||
from functools import partial
|
||||
from typing import List, Optional, cast
|
||||
|
||||
from tqdm import tqdm
|
||||
|
||||
@@ -9,7 +10,7 @@ import facefusion.choices
|
||||
from facefusion import ffmpeg_builder, logger, process_manager, state_manager, wording
|
||||
from facefusion.filesystem import get_file_format, remove_file
|
||||
from facefusion.temp_helper import get_temp_file_path, get_temp_frames_pattern
|
||||
from facefusion.types import AudioBuffer, Commands, EncoderSet, Fps, UpdateProgress
|
||||
from facefusion.types import AudioBuffer, AudioEncoder, Commands, EncoderSet, Fps, UpdateProgress, VideoEncoder, VideoFormat
|
||||
from facefusion.vision import detect_video_duration, detect_video_fps, predict_video_frame_total
|
||||
|
||||
|
||||
@@ -40,6 +41,10 @@ def run_ffmpeg_with_progress(commands : Commands, update_progress : UpdateProgre
|
||||
return process
|
||||
|
||||
|
||||
def update_progress(progress : tqdm, frame_number : int) -> None:
|
||||
progress.update(frame_number - progress.n)
|
||||
|
||||
|
||||
def run_ffmpeg(commands : Commands) -> subprocess.Popen[bytes]:
|
||||
log_level = state_manager.get_item('log_level')
|
||||
commands = ffmpeg_builder.run(commands)
|
||||
@@ -114,26 +119,26 @@ def extract_frames(target_path : str, temp_video_resolution : str, temp_video_fp
|
||||
)
|
||||
|
||||
with tqdm(total = extract_frame_total, desc = wording.get('extracting'), unit = 'frame', ascii = ' =', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress:
|
||||
process = run_ffmpeg_with_progress(commands, lambda frame_number: progress.update(frame_number - progress.n))
|
||||
process = run_ffmpeg_with_progress(commands, partial(update_progress, progress))
|
||||
return process.returncode == 0
|
||||
|
||||
|
||||
def copy_image(target_path : str, temp_image_resolution : str) -> bool:
|
||||
temp_file_path = get_temp_file_path(target_path)
|
||||
temp_image_path = get_temp_file_path(target_path)
|
||||
commands = ffmpeg_builder.chain(
|
||||
ffmpeg_builder.set_input(target_path),
|
||||
ffmpeg_builder.set_media_resolution(temp_image_resolution),
|
||||
ffmpeg_builder.set_image_quality(target_path, 100),
|
||||
ffmpeg_builder.force_output(temp_file_path)
|
||||
ffmpeg_builder.force_output(temp_image_path)
|
||||
)
|
||||
return run_ffmpeg(commands).returncode == 0
|
||||
|
||||
|
||||
def finalize_image(target_path : str, output_path : str, output_image_resolution : str) -> bool:
|
||||
output_image_quality = state_manager.get_item('output_image_quality')
|
||||
temp_file_path = get_temp_file_path(target_path)
|
||||
temp_image_path = get_temp_file_path(target_path)
|
||||
commands = ffmpeg_builder.chain(
|
||||
ffmpeg_builder.set_input(temp_file_path),
|
||||
ffmpeg_builder.set_input(temp_image_path),
|
||||
ffmpeg_builder.set_media_resolution(output_image_resolution),
|
||||
ffmpeg_builder.set_image_quality(target_path, output_image_quality),
|
||||
ffmpeg_builder.force_output(output_path)
|
||||
@@ -163,11 +168,13 @@ def restore_audio(target_path : str, output_path : str, trim_frame_start : int,
|
||||
output_audio_quality = state_manager.get_item('output_audio_quality')
|
||||
output_audio_volume = state_manager.get_item('output_audio_volume')
|
||||
target_video_fps = detect_video_fps(target_path)
|
||||
temp_file_path = get_temp_file_path(target_path)
|
||||
temp_video_duration = detect_video_duration(temp_file_path)
|
||||
temp_video_path = get_temp_file_path(target_path)
|
||||
temp_video_format = cast(VideoFormat, get_file_format(temp_video_path))
|
||||
temp_video_duration = detect_video_duration(temp_video_path)
|
||||
|
||||
output_audio_encoder = fix_audio_encoder(temp_video_format, output_audio_encoder)
|
||||
commands = ffmpeg_builder.chain(
|
||||
ffmpeg_builder.set_input(temp_file_path),
|
||||
ffmpeg_builder.set_input(temp_video_path),
|
||||
ffmpeg_builder.select_media_range(trim_frame_start, trim_frame_end, target_video_fps),
|
||||
ffmpeg_builder.set_input(target_path),
|
||||
ffmpeg_builder.copy_video_encoder(),
|
||||
@@ -186,11 +193,13 @@ def replace_audio(target_path : str, audio_path : str, output_path : str) -> boo
|
||||
output_audio_encoder = state_manager.get_item('output_audio_encoder')
|
||||
output_audio_quality = state_manager.get_item('output_audio_quality')
|
||||
output_audio_volume = state_manager.get_item('output_audio_volume')
|
||||
temp_file_path = get_temp_file_path(target_path)
|
||||
temp_video_duration = detect_video_duration(temp_file_path)
|
||||
temp_video_path = get_temp_file_path(target_path)
|
||||
temp_video_format = cast(VideoFormat, get_file_format(temp_video_path))
|
||||
temp_video_duration = detect_video_duration(temp_video_path)
|
||||
|
||||
output_audio_encoder = fix_audio_encoder(temp_video_format, output_audio_encoder)
|
||||
commands = ffmpeg_builder.chain(
|
||||
ffmpeg_builder.set_input(temp_file_path),
|
||||
ffmpeg_builder.set_input(temp_video_path),
|
||||
ffmpeg_builder.set_input(audio_path),
|
||||
ffmpeg_builder.copy_video_encoder(),
|
||||
ffmpeg_builder.set_audio_encoder(output_audio_encoder),
|
||||
@@ -207,14 +216,13 @@ def merge_video(target_path : str, temp_video_fps : Fps, output_video_resolution
|
||||
output_video_quality = state_manager.get_item('output_video_quality')
|
||||
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)
|
||||
temp_file_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_frames_pattern = get_temp_frames_pattern(target_path, '%08d')
|
||||
|
||||
if get_file_format(target_path) == 'webm':
|
||||
output_video_encoder = 'libvpx-vp9'
|
||||
|
||||
output_video_encoder = fix_video_encoder(temp_video_format, output_video_encoder)
|
||||
commands = ffmpeg_builder.chain(
|
||||
ffmpeg_builder.set_conditional_fps(temp_video_fps),
|
||||
ffmpeg_builder.set_input_fps(temp_video_fps),
|
||||
ffmpeg_builder.set_input(temp_frames_pattern),
|
||||
ffmpeg_builder.set_media_resolution(output_video_resolution),
|
||||
ffmpeg_builder.set_video_encoder(output_video_encoder),
|
||||
@@ -223,11 +231,11 @@ def merge_video(target_path : str, temp_video_fps : Fps, output_video_resolution
|
||||
ffmpeg_builder.set_video_fps(output_video_fps),
|
||||
ffmpeg_builder.set_pixel_format(output_video_encoder),
|
||||
ffmpeg_builder.set_video_colorspace('bt709'),
|
||||
ffmpeg_builder.force_output(temp_file_path)
|
||||
ffmpeg_builder.force_output(temp_video_path)
|
||||
)
|
||||
|
||||
with tqdm(total = merge_frame_total, desc = wording.get('merging'), unit = 'frame', ascii = ' =', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress:
|
||||
process = run_ffmpeg_with_progress(commands, lambda frame_number: progress.update(frame_number - progress.n))
|
||||
process = run_ffmpeg_with_progress(commands, partial(update_progress, progress))
|
||||
return process.returncode == 0
|
||||
|
||||
|
||||
@@ -252,3 +260,27 @@ def concat_video(output_path : str, temp_output_paths : List[str]) -> bool:
|
||||
process.communicate()
|
||||
remove_file(concat_video_path)
|
||||
return process.returncode == 0
|
||||
|
||||
|
||||
def fix_audio_encoder(video_format : VideoFormat, audio_encoder : AudioEncoder) -> AudioEncoder:
|
||||
if video_format == 'avi' and audio_encoder == 'libopus':
|
||||
return 'aac'
|
||||
if video_format == 'm4v':
|
||||
return 'aac'
|
||||
if video_format == 'mov' and audio_encoder in [ 'flac', 'libopus' ]:
|
||||
return 'aac'
|
||||
if video_format == 'webm':
|
||||
return 'libopus'
|
||||
return audio_encoder
|
||||
|
||||
|
||||
def fix_video_encoder(video_format : VideoFormat, video_encoder : VideoEncoder) -> VideoEncoder:
|
||||
if video_format == 'm4v':
|
||||
return 'libx264'
|
||||
if video_format in [ 'mkv', 'mp4' ] and video_encoder == 'rawvideo':
|
||||
return 'libx264'
|
||||
if video_format == 'mov' and video_encoder == 'libvpx-vp9':
|
||||
return 'libx264'
|
||||
if video_format == 'webm':
|
||||
return 'libvpx-vp9'
|
||||
return video_encoder
|
||||
|
||||
@@ -20,6 +20,10 @@ def get_encoders() -> Commands:
|
||||
return [ '-encoders' ]
|
||||
|
||||
|
||||
def set_hardware_accelerator(value : str) -> Commands:
|
||||
return [ '-hwaccel', value ]
|
||||
|
||||
|
||||
def set_progress() -> Commands:
|
||||
return [ '-progress' ]
|
||||
|
||||
@@ -28,8 +32,8 @@ def set_input(input_path : str) -> Commands:
|
||||
return [ '-i', input_path ]
|
||||
|
||||
|
||||
def set_conditional_fps(conditional_fps : Fps) -> Commands:
|
||||
return [ '-r', str(conditional_fps) ]
|
||||
def set_input_fps(input_fps : Fps) -> Commands:
|
||||
return [ '-r', str(input_fps)]
|
||||
|
||||
|
||||
def set_output(output_path : str) -> Commands:
|
||||
@@ -52,6 +56,10 @@ def set_stream_mode(stream_mode : StreamMode) -> Commands:
|
||||
return []
|
||||
|
||||
|
||||
def set_stream_quality(stream_quality : int) -> Commands:
|
||||
return [ '-b:v', str(stream_quality) + 'k' ]
|
||||
|
||||
|
||||
def unsafe_concat() -> Commands:
|
||||
return [ '-f', 'concat', '-safe', '0' ]
|
||||
|
||||
@@ -138,7 +146,7 @@ def set_audio_quality(audio_encoder : AudioEncoder, audio_quality : int) -> Comm
|
||||
audio_compression = round(numpy.interp(audio_quality, [ 0, 100 ], [ 9, 0 ]))
|
||||
return [ '-q:a', str(audio_compression) ]
|
||||
if audio_encoder == 'libopus':
|
||||
audio_bit_rate = round(numpy.interp(audio_quality, [ 0, 100 ], [ 64, 320 ]))
|
||||
audio_bit_rate = round(numpy.interp(audio_quality, [ 0, 100 ], [ 64, 256 ]))
|
||||
return [ '-b:a', str(audio_bit_rate) + 'k' ]
|
||||
if audio_encoder == 'libvorbis':
|
||||
audio_compression = round(numpy.interp(audio_quality, [ 0, 100 ], [ -1, 10 ]), 1)
|
||||
@@ -205,7 +213,7 @@ def set_video_duration(video_duration : Duration) -> Commands:
|
||||
|
||||
|
||||
def capture_video() -> Commands:
|
||||
return [ '-f', 'rawvideo' ]
|
||||
return [ '-f', 'rawvideo', '-pix_fmt', 'rgb24' ]
|
||||
|
||||
|
||||
def ignore_video_stream() -> Commands:
|
||||
|
||||
@@ -4,17 +4,19 @@ import signal
|
||||
import subprocess
|
||||
import sys
|
||||
from argparse import ArgumentParser, HelpFormatter
|
||||
from functools import partial
|
||||
from types import FrameType
|
||||
|
||||
from facefusion import metadata, wording
|
||||
from facefusion.common_helper import is_linux, is_windows
|
||||
|
||||
ONNXRUNTIME_SET =\
|
||||
{
|
||||
'default': ('onnxruntime', '1.21.1')
|
||||
'default': ('onnxruntime', '1.22.0')
|
||||
}
|
||||
if is_windows() or is_linux():
|
||||
ONNXRUNTIME_SET['cuda'] = ('onnxruntime-gpu', '1.21.1')
|
||||
ONNXRUNTIME_SET['openvino'] = ('onnxruntime-openvino', '1.21.0')
|
||||
ONNXRUNTIME_SET['cuda'] = ('onnxruntime-gpu', '1.22.0')
|
||||
ONNXRUNTIME_SET['openvino'] = ('onnxruntime-openvino', '1.22.0')
|
||||
if is_windows():
|
||||
ONNXRUNTIME_SET['directml'] = ('onnxruntime-directml', '1.17.3')
|
||||
if is_linux():
|
||||
@@ -22,14 +24,18 @@ if is_linux():
|
||||
|
||||
|
||||
def cli() -> None:
|
||||
signal.signal(signal.SIGINT, lambda signal_number, frame: sys.exit(0))
|
||||
program = ArgumentParser(formatter_class = lambda prog: HelpFormatter(prog, max_help_position = 50))
|
||||
signal.signal(signal.SIGINT, signal_exit)
|
||||
program = ArgumentParser(formatter_class = partial(HelpFormatter, max_help_position = 50))
|
||||
program.add_argument('--onnxruntime', help = wording.get('help.install_dependency').format(dependency = 'onnxruntime'), choices = ONNXRUNTIME_SET.keys(), required = True)
|
||||
program.add_argument('--skip-conda', help = wording.get('help.skip_conda'), action = 'store_true')
|
||||
program.add_argument('-v', '--version', version = metadata.get('name') + ' ' + metadata.get('version'), action = 'version')
|
||||
run(program)
|
||||
|
||||
|
||||
def signal_exit(signum : int, frame : FrameType) -> None:
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def run(program : ArgumentParser) -> None:
|
||||
args = program.parse_args()
|
||||
has_conda = 'CONDA_PREFIX' in os.environ
|
||||
|
||||
@@ -4,7 +4,7 @@ METADATA =\
|
||||
{
|
||||
'name': 'FaceFusion',
|
||||
'description': 'Industry leading face manipulation platform',
|
||||
'version': '3.2.0',
|
||||
'version': '3.3.0',
|
||||
'license': 'OpenRAIL-AS',
|
||||
'author': 'Henry Ruhs',
|
||||
'url': 'https://facefusion.io'
|
||||
|
||||
@@ -7,6 +7,7 @@ from facefusion.processors.types import AgeModifierModel, DeepSwapperModel, Expr
|
||||
age_modifier_models : List[AgeModifierModel] = [ 'styleganex_age' ]
|
||||
deep_swapper_models : List[DeepSwapperModel] =\
|
||||
[
|
||||
'druuzil/adam_levine_320',
|
||||
'druuzil/adrianne_palicki_384',
|
||||
'druuzil/agnetha_falskog_224',
|
||||
'druuzil/alan_ritchson_320',
|
||||
@@ -14,6 +15,7 @@ deep_swapper_models : List[DeepSwapperModel] =\
|
||||
'druuzil/amber_midthunder_320',
|
||||
'druuzil/andras_arato_384',
|
||||
'druuzil/andrew_tate_320',
|
||||
'druuzil/angelina_jolie_384',
|
||||
'druuzil/anne_hathaway_320',
|
||||
'druuzil/anya_chalotra_320',
|
||||
'druuzil/arnold_schwarzenegger_320',
|
||||
@@ -21,6 +23,7 @@ deep_swapper_models : List[DeepSwapperModel] =\
|
||||
'druuzil/benjamin_stiller_384',
|
||||
'druuzil/bradley_pitt_224',
|
||||
'druuzil/brie_larson_384',
|
||||
'druuzil/bruce_campbell_384',
|
||||
'druuzil/bryan_cranston_320',
|
||||
'druuzil/catherine_blanchett_352',
|
||||
'druuzil/christian_bale_320',
|
||||
@@ -50,6 +53,7 @@ deep_swapper_models : List[DeepSwapperModel] =\
|
||||
'druuzil/hugh_jackman_384',
|
||||
'druuzil/idris_elba_320',
|
||||
'druuzil/jack_nicholson_320',
|
||||
'druuzil/james_carrey_384',
|
||||
'druuzil/james_mcavoy_320',
|
||||
'druuzil/james_varney_320',
|
||||
'druuzil/jason_momoa_320',
|
||||
@@ -61,6 +65,7 @@ deep_swapper_models : List[DeepSwapperModel] =\
|
||||
'druuzil/kate_beckinsale_384',
|
||||
'druuzil/laurence_fishburne_384',
|
||||
'druuzil/lili_reinhart_320',
|
||||
'druuzil/luke_evans_384',
|
||||
'druuzil/mads_mikkelsen_384',
|
||||
'druuzil/mary_winstead_320',
|
||||
'druuzil/margaret_qualley_384',
|
||||
@@ -69,13 +74,16 @@ deep_swapper_models : List[DeepSwapperModel] =\
|
||||
'druuzil/michael_fox_320',
|
||||
'druuzil/millie_bobby_brown_320',
|
||||
'druuzil/morgan_freeman_320',
|
||||
'druuzil/patrick_stewart_320',
|
||||
'druuzil/patrick_stewart_224',
|
||||
'druuzil/rachel_weisz_384',
|
||||
'druuzil/rebecca_ferguson_320',
|
||||
'druuzil/scarlett_johansson_320',
|
||||
'druuzil/shannen_doherty_384',
|
||||
'druuzil/seth_macfarlane_384',
|
||||
'druuzil/thomas_cruise_320',
|
||||
'druuzil/thomas_hanks_384',
|
||||
'druuzil/william_murray_384',
|
||||
'druuzil/zoe_saldana_384',
|
||||
'edel/emma_roberts_224',
|
||||
'edel/ivanka_trump_224',
|
||||
'edel/lize_dzjabrailova_224',
|
||||
@@ -177,6 +185,9 @@ face_swapper_set : FaceSwapperSet =\
|
||||
'ghost_2_256': [ '256x256', '512x512', '768x768', '1024x1024' ],
|
||||
'ghost_3_256': [ '256x256', '512x512', '768x768', '1024x1024' ],
|
||||
'hififace_unofficial_256': [ '256x256', '512x512', '768x768', '1024x1024' ],
|
||||
'hyperswap_1a_256': [ '256x256', '512x512', '768x768', '1024x1024' ],
|
||||
'hyperswap_1b_256': [ '256x256', '512x512', '768x768', '1024x1024' ],
|
||||
'hyperswap_1c_256': [ '256x256', '512x512', '768x768', '1024x1024' ],
|
||||
'inswapper_128': [ '128x128', '256x256', '384x384', '512x512', '768x768', '1024x1024' ],
|
||||
'inswapper_128_fp16': [ '128x128', '256x256', '384x384', '512x512', '768x768', '1024x1024' ],
|
||||
'simswap_256': [ '256x256', '512x512', '768x768', '1024x1024' ],
|
||||
@@ -186,8 +197,8 @@ face_swapper_set : FaceSwapperSet =\
|
||||
face_swapper_models : List[FaceSwapperModel] = list(face_swapper_set.keys())
|
||||
frame_colorizer_models : List[FrameColorizerModel] = [ 'ddcolor', 'ddcolor_artistic', 'deoldify', 'deoldify_artistic', 'deoldify_stable' ]
|
||||
frame_colorizer_sizes : List[str] = [ '192x192', '256x256', '384x384', '512x512' ]
|
||||
frame_enhancer_models : List[FrameEnhancerModel] = [ 'clear_reality_x4', 'lsdir_x4', 'nomos8k_sc_x4', 'real_esrgan_x2', 'real_esrgan_x2_fp16', 'real_esrgan_x4', 'real_esrgan_x4_fp16', 'real_esrgan_x8', 'real_esrgan_x8_fp16', 'real_hatgan_x4', 'real_web_photo_x4', 'realistic_rescaler_x4', 'remacri_x4', 'siax_x4', 'span_kendata_x4', 'swin2_sr_x4', 'ultra_sharp_x4' ]
|
||||
lip_syncer_models : List[LipSyncerModel] = [ 'wav2lip_96', 'wav2lip_gan_96' ]
|
||||
frame_enhancer_models : List[FrameEnhancerModel] = [ 'clear_reality_x4', 'lsdir_x4', 'nomos8k_sc_x4', 'real_esrgan_x2', 'real_esrgan_x2_fp16', 'real_esrgan_x4', 'real_esrgan_x4_fp16', 'real_esrgan_x8', 'real_esrgan_x8_fp16', 'real_hatgan_x4', 'real_web_photo_x4', 'realistic_rescaler_x4', 'remacri_x4', 'siax_x4', 'span_kendata_x4', 'swin2_sr_x4', 'ultra_sharp_x4', 'ultra_sharp_2_x4' ]
|
||||
lip_syncer_models : List[LipSyncerModel] = [ 'edtalk_256', 'wav2lip_96', 'wav2lip_gan_96' ]
|
||||
|
||||
age_modifier_direction_range : Sequence[int] = create_int_range(-100, 100, 1)
|
||||
deep_swapper_morph_range : Sequence[int] = create_int_range(0, 100, 1)
|
||||
@@ -210,3 +221,4 @@ face_enhancer_blend_range : Sequence[int] = create_int_range(0, 100, 1)
|
||||
face_enhancer_weight_range : Sequence[float] = create_float_range(0.0, 1.0, 0.05)
|
||||
frame_colorizer_blend_range : Sequence[int] = create_int_range(0, 100, 1)
|
||||
frame_enhancer_blend_range : Sequence[int] = create_int_range(0, 100, 1)
|
||||
lip_syncer_weight_range : Sequence[float] = create_float_range(0.0, 1.0, 0.05)
|
||||
|
||||
@@ -9,13 +9,13 @@ import facefusion.choices
|
||||
import facefusion.jobs.job_manager
|
||||
import facefusion.jobs.job_store
|
||||
import facefusion.processors.core as processors
|
||||
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, wording
|
||||
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, video_manager, wording
|
||||
from facefusion.common_helper import create_int_metavar
|
||||
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
|
||||
from facefusion.execution import has_execution_provider
|
||||
from facefusion.face_analyser import get_many_faces, get_one_face
|
||||
from facefusion.face_helper import merge_matrix, paste_back, scale_face_landmark_5, warp_face_by_face_landmark_5
|
||||
from facefusion.face_masker import create_occlusion_mask, create_static_box_mask
|
||||
from facefusion.face_masker import create_box_mask, create_occlusion_mask
|
||||
from facefusion.face_selector import find_similar_faces, sort_and_filter_faces
|
||||
from facefusion.face_store import get_reference_faces
|
||||
from facefusion.filesystem import in_directory, is_image, is_video, resolve_relative_path, same_file_extension
|
||||
@@ -115,6 +115,7 @@ def pre_process(mode : ProcessMode) -> bool:
|
||||
|
||||
def post_process() -> None:
|
||||
read_static_image.cache_clear()
|
||||
video_manager.clear_video_pool()
|
||||
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
||||
clear_inference_pool()
|
||||
if state_manager.get_item('video_memory_strategy') == 'strict':
|
||||
@@ -134,7 +135,7 @@ def modify_age(target_face : Face, temp_vision_frame : VisionFrame) -> VisionFra
|
||||
extend_face_landmark_5 = scale_face_landmark_5(face_landmark_5, 0.875)
|
||||
extend_vision_frame, extend_affine_matrix = warp_face_by_face_landmark_5(temp_vision_frame, extend_face_landmark_5, model_templates.get('target_with_background'), model_sizes.get('target_with_background'))
|
||||
extend_vision_frame_raw = extend_vision_frame.copy()
|
||||
box_mask = create_static_box_mask(model_sizes.get('target_with_background'), state_manager.get_item('face_mask_blur'), (0, 0, 0, 0))
|
||||
box_mask = create_box_mask(extend_vision_frame, state_manager.get_item('face_mask_blur'), (0, 0, 0, 0))
|
||||
crop_masks =\
|
||||
[
|
||||
box_mask
|
||||
|
||||
@@ -9,12 +9,12 @@ from cv2.typing import Size
|
||||
import facefusion.jobs.job_manager
|
||||
import facefusion.jobs.job_store
|
||||
import facefusion.processors.core as processors
|
||||
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, wording
|
||||
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, video_manager, wording
|
||||
from facefusion.common_helper import create_int_metavar
|
||||
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url_by_provider
|
||||
from facefusion.face_analyser import get_many_faces, get_one_face
|
||||
from facefusion.face_helper import paste_back, warp_face_by_face_landmark_5
|
||||
from facefusion.face_masker import create_occlusion_mask, create_region_mask, create_static_box_mask
|
||||
from facefusion.face_masker import create_area_mask, create_box_mask, create_occlusion_mask, create_region_mask
|
||||
from facefusion.face_selector import find_similar_faces, sort_and_filter_faces
|
||||
from facefusion.face_store import get_reference_faces
|
||||
from facefusion.filesystem import get_file_name, in_directory, is_image, is_video, resolve_file_paths, resolve_relative_path, same_file_extension
|
||||
@@ -33,6 +33,7 @@ def create_static_model_set(download_scope : DownloadScope) -> ModelSet:
|
||||
if download_scope == 'full':
|
||||
model_config.extend(
|
||||
[
|
||||
('druuzil', 'adam_levine_320'),
|
||||
('druuzil', 'adrianne_palicki_384'),
|
||||
('druuzil', 'agnetha_falskog_224'),
|
||||
('druuzil', 'alan_ritchson_320'),
|
||||
@@ -40,6 +41,7 @@ def create_static_model_set(download_scope : DownloadScope) -> ModelSet:
|
||||
('druuzil', 'amber_midthunder_320'),
|
||||
('druuzil', 'andras_arato_384'),
|
||||
('druuzil', 'andrew_tate_320'),
|
||||
('druuzil', 'angelina_jolie_384'),
|
||||
('druuzil', 'anne_hathaway_320'),
|
||||
('druuzil', 'anya_chalotra_320'),
|
||||
('druuzil', 'arnold_schwarzenegger_320'),
|
||||
@@ -47,6 +49,7 @@ def create_static_model_set(download_scope : DownloadScope) -> ModelSet:
|
||||
('druuzil', 'benjamin_stiller_384'),
|
||||
('druuzil', 'bradley_pitt_224'),
|
||||
('druuzil', 'brie_larson_384'),
|
||||
('druuzil', 'bruce_campbell_384'),
|
||||
('druuzil', 'bryan_cranston_320'),
|
||||
('druuzil', 'catherine_blanchett_352'),
|
||||
('druuzil', 'christian_bale_320'),
|
||||
@@ -76,6 +79,7 @@ def create_static_model_set(download_scope : DownloadScope) -> ModelSet:
|
||||
('druuzil', 'hugh_jackman_384'),
|
||||
('druuzil', 'idris_elba_320'),
|
||||
('druuzil', 'jack_nicholson_320'),
|
||||
('druuzil', 'james_carrey_384'),
|
||||
('druuzil', 'james_mcavoy_320'),
|
||||
('druuzil', 'james_varney_320'),
|
||||
('druuzil', 'jason_momoa_320'),
|
||||
@@ -87,6 +91,7 @@ def create_static_model_set(download_scope : DownloadScope) -> ModelSet:
|
||||
('druuzil', 'kate_beckinsale_384'),
|
||||
('druuzil', 'laurence_fishburne_384'),
|
||||
('druuzil', 'lili_reinhart_320'),
|
||||
('druuzil', 'luke_evans_384'),
|
||||
('druuzil', 'mads_mikkelsen_384'),
|
||||
('druuzil', 'mary_winstead_320'),
|
||||
('druuzil', 'margaret_qualley_384'),
|
||||
@@ -95,13 +100,16 @@ def create_static_model_set(download_scope : DownloadScope) -> ModelSet:
|
||||
('druuzil', 'michael_fox_320'),
|
||||
('druuzil', 'millie_bobby_brown_320'),
|
||||
('druuzil', 'morgan_freeman_320'),
|
||||
('druuzil', 'patrick_stewart_320'),
|
||||
('druuzil', 'patrick_stewart_224'),
|
||||
('druuzil', 'rachel_weisz_384'),
|
||||
('druuzil', 'rebecca_ferguson_320'),
|
||||
('druuzil', 'scarlett_johansson_320'),
|
||||
('druuzil', 'shannen_doherty_384'),
|
||||
('druuzil', 'seth_macfarlane_384'),
|
||||
('druuzil', 'thomas_cruise_320'),
|
||||
('druuzil', 'thomas_hanks_384'),
|
||||
('druuzil', 'william_murray_384'),
|
||||
('druuzil', 'zoe_saldana_384'),
|
||||
('edel', 'emma_roberts_224'),
|
||||
('edel', 'ivanka_trump_224'),
|
||||
('edel', 'lize_dzjabrailova_224'),
|
||||
@@ -303,6 +311,7 @@ def pre_process(mode : ProcessMode) -> bool:
|
||||
|
||||
def post_process() -> None:
|
||||
read_static_image.cache_clear()
|
||||
video_manager.clear_video_pool()
|
||||
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
||||
clear_inference_pool()
|
||||
if state_manager.get_item('video_memory_strategy') == 'strict':
|
||||
@@ -319,7 +328,7 @@ def swap_face(target_face : Face, temp_vision_frame : VisionFrame) -> VisionFram
|
||||
model_size = get_model_size()
|
||||
crop_vision_frame, affine_matrix = warp_face_by_face_landmark_5(temp_vision_frame, target_face.landmark_set.get('5/68'), model_template, model_size)
|
||||
crop_vision_frame_raw = crop_vision_frame.copy()
|
||||
box_mask = create_static_box_mask(crop_vision_frame.shape[:2][::-1], state_manager.get_item('face_mask_blur'), state_manager.get_item('face_mask_padding'))
|
||||
box_mask = create_box_mask(crop_vision_frame, state_manager.get_item('face_mask_blur'), state_manager.get_item('face_mask_padding'))
|
||||
crop_masks =\
|
||||
[
|
||||
box_mask
|
||||
@@ -336,6 +345,11 @@ def swap_face(target_face : Face, temp_vision_frame : VisionFrame) -> VisionFram
|
||||
crop_vision_frame = conditional_match_frame_color(crop_vision_frame_raw, crop_vision_frame)
|
||||
crop_masks.append(prepare_crop_mask(crop_source_mask, crop_target_mask))
|
||||
|
||||
if 'area' in state_manager.get_item('face_mask_types'):
|
||||
face_landmark_68 = cv2.transform(target_face.landmark_set.get('68').reshape(1, -1, 2), affine_matrix).reshape(-1, 2)
|
||||
area_mask = create_area_mask(crop_vision_frame, face_landmark_68, state_manager.get_item('face_mask_areas'))
|
||||
crop_masks.append(area_mask)
|
||||
|
||||
if 'region' in state_manager.get_item('face_mask_types'):
|
||||
region_mask = create_region_mask(crop_vision_frame, state_manager.get_item('face_mask_regions'))
|
||||
crop_masks.append(region_mask)
|
||||
|
||||
@@ -8,12 +8,12 @@ import numpy
|
||||
import facefusion.jobs.job_manager
|
||||
import facefusion.jobs.job_store
|
||||
import facefusion.processors.core as processors
|
||||
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, wording
|
||||
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, video_manager, wording
|
||||
from facefusion.common_helper import create_int_metavar
|
||||
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
|
||||
from facefusion.face_analyser import get_many_faces, get_one_face
|
||||
from facefusion.face_helper import paste_back, warp_face_by_face_landmark_5
|
||||
from facefusion.face_masker import create_occlusion_mask, create_static_box_mask
|
||||
from facefusion.face_masker import create_box_mask, create_occlusion_mask
|
||||
from facefusion.face_selector import find_similar_faces, sort_and_filter_faces
|
||||
from facefusion.face_store import get_reference_faces
|
||||
from facefusion.filesystem import in_directory, is_image, is_video, resolve_relative_path, same_file_extension
|
||||
@@ -129,6 +129,7 @@ def pre_process(mode : ProcessMode) -> bool:
|
||||
|
||||
def post_process() -> None:
|
||||
read_static_image.cache_clear()
|
||||
video_manager.clear_video_pool()
|
||||
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
||||
clear_inference_pool()
|
||||
if state_manager.get_item('video_memory_strategy') == 'strict':
|
||||
@@ -147,7 +148,7 @@ def restore_expression(source_vision_frame : VisionFrame, target_face : Face, te
|
||||
source_vision_frame = cv2.resize(source_vision_frame, temp_vision_frame.shape[:2][::-1])
|
||||
source_crop_vision_frame, _ = warp_face_by_face_landmark_5(source_vision_frame, target_face.landmark_set.get('5/68'), model_template, model_size)
|
||||
target_crop_vision_frame, affine_matrix = warp_face_by_face_landmark_5(temp_vision_frame, target_face.landmark_set.get('5/68'), model_template, model_size)
|
||||
box_mask = create_static_box_mask(target_crop_vision_frame.shape[:2][::-1], state_manager.get_item('face_mask_blur'), (0, 0, 0, 0))
|
||||
box_mask = create_box_mask(target_crop_vision_frame, state_manager.get_item('face_mask_blur'), (0, 0, 0, 0))
|
||||
crop_masks =\
|
||||
[
|
||||
box_mask
|
||||
|
||||
@@ -7,10 +7,10 @@ import numpy
|
||||
import facefusion.jobs.job_manager
|
||||
import facefusion.jobs.job_store
|
||||
import facefusion.processors.core as processors
|
||||
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, logger, process_manager, state_manager, wording
|
||||
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, logger, process_manager, state_manager, video_manager, wording
|
||||
from facefusion.face_analyser import get_many_faces, get_one_face
|
||||
from facefusion.face_helper import warp_face_by_face_landmark_5
|
||||
from facefusion.face_masker import create_occlusion_mask, create_region_mask, create_static_box_mask
|
||||
from facefusion.face_masker import create_area_mask, create_box_mask, create_occlusion_mask, create_region_mask
|
||||
from facefusion.face_selector import find_similar_faces, sort_and_filter_faces
|
||||
from facefusion.face_store import get_reference_faces
|
||||
from facefusion.filesystem import in_directory, same_file_extension
|
||||
@@ -56,6 +56,7 @@ def pre_process(mode : ProcessMode) -> bool:
|
||||
|
||||
def post_process() -> None:
|
||||
read_static_image.cache_clear()
|
||||
video_manager.clear_video_pool()
|
||||
if state_manager.get_item('video_memory_strategy') == 'strict':
|
||||
content_analyser.clear_inference_pool()
|
||||
face_classifier.clear_inference_pool()
|
||||
@@ -82,11 +83,11 @@ def debug_face(target_face : Face, temp_vision_frame : VisionFrame) -> VisionFra
|
||||
|
||||
if target_face.angle == 0:
|
||||
cv2.line(temp_vision_frame, (x1, y1), (x2, y1), primary_light_color, 3)
|
||||
elif target_face.angle == 180:
|
||||
if target_face.angle == 180:
|
||||
cv2.line(temp_vision_frame, (x1, y2), (x2, y2), primary_light_color, 3)
|
||||
elif target_face.angle == 90:
|
||||
if target_face.angle == 90:
|
||||
cv2.line(temp_vision_frame, (x2, y1), (x2, y2), primary_light_color, 3)
|
||||
elif target_face.angle == 270:
|
||||
if target_face.angle == 270:
|
||||
cv2.line(temp_vision_frame, (x1, y1), (x1, y2), primary_light_color, 3)
|
||||
|
||||
if 'face-mask' in face_debugger_items:
|
||||
@@ -96,13 +97,18 @@ def debug_face(target_face : Face, temp_vision_frame : VisionFrame) -> VisionFra
|
||||
crop_masks = []
|
||||
|
||||
if 'box' in state_manager.get_item('face_mask_types'):
|
||||
box_mask = create_static_box_mask(crop_vision_frame.shape[:2][::-1], 0, state_manager.get_item('face_mask_padding'))
|
||||
box_mask = create_box_mask(crop_vision_frame, 0, state_manager.get_item('face_mask_padding'))
|
||||
crop_masks.append(box_mask)
|
||||
|
||||
if 'occlusion' in state_manager.get_item('face_mask_types'):
|
||||
occlusion_mask = create_occlusion_mask(crop_vision_frame)
|
||||
crop_masks.append(occlusion_mask)
|
||||
|
||||
if 'area' in state_manager.get_item('face_mask_types'):
|
||||
face_landmark_68 = cv2.transform(target_face.landmark_set.get('68').reshape(1, -1, 2), affine_matrix).reshape(-1, 2)
|
||||
area_mask = create_area_mask(crop_vision_frame, face_landmark_68, state_manager.get_item('face_mask_areas'))
|
||||
crop_masks.append(area_mask)
|
||||
|
||||
if 'region' in state_manager.get_item('face_mask_types'):
|
||||
region_mask = create_region_mask(crop_vision_frame, state_manager.get_item('face_mask_regions'))
|
||||
crop_masks.append(region_mask)
|
||||
|
||||
@@ -8,12 +8,12 @@ import numpy
|
||||
import facefusion.jobs.job_manager
|
||||
import facefusion.jobs.job_store
|
||||
import facefusion.processors.core as processors
|
||||
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, wording
|
||||
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, video_manager, wording
|
||||
from facefusion.common_helper import create_float_metavar
|
||||
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
|
||||
from facefusion.face_analyser import get_many_faces, get_one_face
|
||||
from facefusion.face_helper import paste_back, scale_face_landmark_5, warp_face_by_face_landmark_5
|
||||
from facefusion.face_masker import create_static_box_mask
|
||||
from facefusion.face_masker import create_box_mask
|
||||
from facefusion.face_selector import find_similar_faces, sort_and_filter_faces
|
||||
from facefusion.face_store import get_reference_faces
|
||||
from facefusion.filesystem import in_directory, is_image, is_video, resolve_relative_path, same_file_extension
|
||||
@@ -182,6 +182,7 @@ def pre_process(mode : ProcessMode) -> bool:
|
||||
|
||||
def post_process() -> None:
|
||||
read_static_image.cache_clear()
|
||||
video_manager.clear_video_pool()
|
||||
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
||||
clear_inference_pool()
|
||||
if state_manager.get_item('video_memory_strategy') == 'strict':
|
||||
@@ -198,7 +199,7 @@ def edit_face(target_face : Face, temp_vision_frame : VisionFrame) -> VisionFram
|
||||
model_size = get_model_options().get('size')
|
||||
face_landmark_5 = scale_face_landmark_5(target_face.landmark_set.get('5/68'), 1.5)
|
||||
crop_vision_frame, affine_matrix = warp_face_by_face_landmark_5(temp_vision_frame, face_landmark_5, model_template, model_size)
|
||||
box_mask = create_static_box_mask(crop_vision_frame.shape[:2][::-1], state_manager.get_item('face_mask_blur'), (0, 0, 0, 0))
|
||||
box_mask = create_box_mask(crop_vision_frame, state_manager.get_item('face_mask_blur'), (0, 0, 0, 0))
|
||||
crop_vision_frame = prepare_crop_frame(crop_vision_frame)
|
||||
crop_vision_frame = apply_edit(crop_vision_frame, target_face.landmark_set.get('68'))
|
||||
crop_vision_frame = normalize_crop_frame(crop_vision_frame)
|
||||
|
||||
@@ -8,12 +8,12 @@ import numpy
|
||||
import facefusion.jobs.job_manager
|
||||
import facefusion.jobs.job_store
|
||||
import facefusion.processors.core as processors
|
||||
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, wording
|
||||
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, video_manager, wording
|
||||
from facefusion.common_helper import create_float_metavar, create_int_metavar
|
||||
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
|
||||
from facefusion.face_analyser import get_many_faces, get_one_face
|
||||
from facefusion.face_helper import paste_back, warp_face_by_face_landmark_5
|
||||
from facefusion.face_masker import create_occlusion_mask, create_static_box_mask
|
||||
from facefusion.face_masker import create_box_mask, create_occlusion_mask
|
||||
from facefusion.face_selector import find_similar_faces, sort_and_filter_faces
|
||||
from facefusion.face_store import get_reference_faces
|
||||
from facefusion.filesystem import in_directory, is_image, is_video, resolve_relative_path, same_file_extension
|
||||
@@ -275,6 +275,7 @@ def pre_process(mode : ProcessMode) -> bool:
|
||||
|
||||
def post_process() -> None:
|
||||
read_static_image.cache_clear()
|
||||
video_manager.clear_video_pool()
|
||||
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
||||
clear_inference_pool()
|
||||
if state_manager.get_item('video_memory_strategy') == 'strict':
|
||||
@@ -290,7 +291,7 @@ def enhance_face(target_face : Face, temp_vision_frame : VisionFrame) -> VisionF
|
||||
model_template = get_model_options().get('template')
|
||||
model_size = get_model_options().get('size')
|
||||
crop_vision_frame, affine_matrix = warp_face_by_face_landmark_5(temp_vision_frame, target_face.landmark_set.get('5/68'), model_template, model_size)
|
||||
box_mask = create_static_box_mask(crop_vision_frame.shape[:2][::-1], state_manager.get_item('face_mask_blur'), (0, 0, 0, 0))
|
||||
box_mask = create_box_mask(crop_vision_frame, state_manager.get_item('face_mask_blur'), (0, 0, 0, 0))
|
||||
crop_masks =\
|
||||
[
|
||||
box_mask
|
||||
|
||||
@@ -2,19 +2,20 @@ from argparse import ArgumentParser
|
||||
from functools import lru_cache
|
||||
from typing import List, Tuple
|
||||
|
||||
import cv2
|
||||
import numpy
|
||||
|
||||
import facefusion.choices
|
||||
import facefusion.jobs.job_manager
|
||||
import facefusion.jobs.job_store
|
||||
import facefusion.processors.core as processors
|
||||
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, wording
|
||||
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, video_manager, wording
|
||||
from facefusion.common_helper import get_first
|
||||
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
|
||||
from facefusion.execution import has_execution_provider
|
||||
from facefusion.face_analyser import get_average_face, get_many_faces, get_one_face
|
||||
from facefusion.face_helper import paste_back, warp_face_by_face_landmark_5
|
||||
from facefusion.face_masker import create_occlusion_mask, create_region_mask, create_static_box_mask
|
||||
from facefusion.face_masker import create_area_mask, create_box_mask, create_occlusion_mask, create_region_mask
|
||||
from facefusion.face_selector import find_similar_faces, sort_and_filter_faces, sort_faces_by_order
|
||||
from facefusion.face_store import get_reference_faces
|
||||
from facefusion.filesystem import filter_image_paths, has_image, in_directory, is_image, is_video, resolve_relative_path, same_file_extension
|
||||
@@ -192,6 +193,78 @@ def create_static_model_set(download_scope : DownloadScope) -> ModelSet:
|
||||
'mean': [ 0.5, 0.5, 0.5 ],
|
||||
'standard_deviation': [ 0.5, 0.5, 0.5 ]
|
||||
},
|
||||
'hyperswap_1a_256':
|
||||
{
|
||||
'hashes':
|
||||
{
|
||||
'face_swapper':
|
||||
{
|
||||
'url': resolve_download_url('models-3.3.0', 'hyperswap_1a_256.hash'),
|
||||
'path': resolve_relative_path('../.assets/models/hyperswap_1a_256.hash')
|
||||
}
|
||||
},
|
||||
'sources':
|
||||
{
|
||||
'face_swapper':
|
||||
{
|
||||
'url': resolve_download_url('models-3.3.0', 'hyperswap_1a_256.onnx'),
|
||||
'path': resolve_relative_path('../.assets/models/hyperswap_1a_256.onnx')
|
||||
}
|
||||
},
|
||||
'type': 'hyperswap',
|
||||
'template': 'arcface_128',
|
||||
'size': (256, 256),
|
||||
'mean': [ 0.5, 0.5, 0.5 ],
|
||||
'standard_deviation': [ 0.5, 0.5, 0.5 ]
|
||||
},
|
||||
'hyperswap_1b_256':
|
||||
{
|
||||
'hashes':
|
||||
{
|
||||
'face_swapper':
|
||||
{
|
||||
'url': resolve_download_url('models-3.3.0', 'hyperswap_1b_256.hash'),
|
||||
'path': resolve_relative_path('../.assets/models/hyperswap_1b_256.hash')
|
||||
}
|
||||
},
|
||||
'sources':
|
||||
{
|
||||
'face_swapper':
|
||||
{
|
||||
'url': resolve_download_url('models-3.3.0', 'hyperswap_1b_256.onnx'),
|
||||
'path': resolve_relative_path('../.assets/models/hyperswap_1b_256.onnx')
|
||||
}
|
||||
},
|
||||
'type': 'hyperswap',
|
||||
'template': 'arcface_128',
|
||||
'size': (256, 256),
|
||||
'mean': [ 0.5, 0.5, 0.5 ],
|
||||
'standard_deviation': [ 0.5, 0.5, 0.5 ]
|
||||
},
|
||||
'hyperswap_1c_256':
|
||||
{
|
||||
'hashes':
|
||||
{
|
||||
'face_swapper':
|
||||
{
|
||||
'url': resolve_download_url('models-3.3.0', 'hyperswap_1c_256.hash'),
|
||||
'path': resolve_relative_path('../.assets/models/hyperswap_1c_256.hash')
|
||||
}
|
||||
},
|
||||
'sources':
|
||||
{
|
||||
'face_swapper':
|
||||
{
|
||||
'url': resolve_download_url('models-3.3.0', 'hyperswap_1c_256.onnx'),
|
||||
'path': resolve_relative_path('../.assets/models/hyperswap_1c_256.onnx')
|
||||
}
|
||||
},
|
||||
'type': 'hyperswap',
|
||||
'template': 'arcface_128',
|
||||
'size': (256, 256),
|
||||
'mean': [ 0.5, 0.5, 0.5 ],
|
||||
'standard_deviation': [ 0.5, 0.5, 0.5 ]
|
||||
},
|
||||
'inswapper_128':
|
||||
{
|
||||
'hashes':
|
||||
@@ -406,9 +479,10 @@ def pre_process(mode : ProcessMode) -> bool:
|
||||
|
||||
def post_process() -> None:
|
||||
read_static_image.cache_clear()
|
||||
video_manager.clear_video_pool()
|
||||
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
||||
clear_inference_pool()
|
||||
get_static_model_initializer.cache_clear()
|
||||
clear_inference_pool()
|
||||
if state_manager.get_item('video_memory_strategy') == 'strict':
|
||||
content_analyser.clear_inference_pool()
|
||||
face_classifier.clear_inference_pool()
|
||||
@@ -428,7 +502,7 @@ def swap_face(source_face : Face, target_face : Face, temp_vision_frame : Vision
|
||||
crop_masks = []
|
||||
|
||||
if 'box' in state_manager.get_item('face_mask_types'):
|
||||
box_mask = create_static_box_mask(crop_vision_frame.shape[:2][::-1], state_manager.get_item('face_mask_blur'), state_manager.get_item('face_mask_padding'))
|
||||
box_mask = create_box_mask(crop_vision_frame, state_manager.get_item('face_mask_blur'), state_manager.get_item('face_mask_padding'))
|
||||
crop_masks.append(box_mask)
|
||||
|
||||
if 'occlusion' in state_manager.get_item('face_mask_types'):
|
||||
@@ -443,6 +517,11 @@ def swap_face(source_face : Face, target_face : Face, temp_vision_frame : Vision
|
||||
temp_vision_frames.append(pixel_boost_vision_frame)
|
||||
crop_vision_frame = explode_pixel_boost(temp_vision_frames, pixel_boost_total, model_size, pixel_boost_size)
|
||||
|
||||
if 'area' in state_manager.get_item('face_mask_types'):
|
||||
face_landmark_68 = cv2.transform(target_face.landmark_set.get('68').reshape(1, -1, 2), affine_matrix).reshape(-1, 2)
|
||||
area_mask = create_area_mask(crop_vision_frame, face_landmark_68, state_manager.get_item('face_mask_areas'))
|
||||
crop_masks.append(area_mask)
|
||||
|
||||
if 'region' in state_manager.get_item('face_mask_types'):
|
||||
region_mask = create_region_mask(crop_vision_frame, state_manager.get_item('face_mask_regions'))
|
||||
crop_masks.append(region_mask)
|
||||
@@ -507,12 +586,19 @@ def prepare_source_embedding(source_face : Face) -> Embedding:
|
||||
if model_type == 'ghost':
|
||||
source_embedding, _ = convert_embedding(source_face)
|
||||
source_embedding = source_embedding.reshape(1, -1)
|
||||
elif model_type == 'inswapper':
|
||||
return source_embedding
|
||||
|
||||
if model_type == 'hyperswap':
|
||||
source_embedding = source_face.normed_embedding.reshape((1, -1))
|
||||
return source_embedding
|
||||
|
||||
if model_type == 'inswapper':
|
||||
model_path = get_model_options().get('sources').get('face_swapper').get('path')
|
||||
model_initializer = get_static_model_initializer(model_path)
|
||||
source_embedding = source_face.embedding.reshape((1, -1))
|
||||
source_embedding = numpy.dot(source_embedding, model_initializer) / numpy.linalg.norm(source_embedding)
|
||||
else:
|
||||
return source_embedding
|
||||
|
||||
_, source_normed_embedding = convert_embedding(source_face)
|
||||
source_embedding = source_normed_embedding.reshape(1, -1)
|
||||
return source_embedding
|
||||
@@ -543,7 +629,7 @@ def normalize_crop_frame(crop_vision_frame : VisionFrame) -> VisionFrame:
|
||||
model_standard_deviation = get_model_options().get('standard_deviation')
|
||||
|
||||
crop_vision_frame = crop_vision_frame.transpose(1, 2, 0)
|
||||
if model_type in [ 'ghost', 'hififace', 'uniface' ]:
|
||||
if model_type in [ 'ghost', 'hififace', 'hyperswap', 'uniface' ]:
|
||||
crop_vision_frame = crop_vision_frame * model_standard_deviation + model_mean
|
||||
crop_vision_frame = crop_vision_frame.clip(0, 1)
|
||||
crop_vision_frame = crop_vision_frame[:, :, ::-1] * 255
|
||||
|
||||
@@ -8,7 +8,7 @@ import numpy
|
||||
import facefusion.jobs.job_manager
|
||||
import facefusion.jobs.job_store
|
||||
import facefusion.processors.core as processors
|
||||
from facefusion import config, content_analyser, inference_manager, logger, process_manager, state_manager, wording
|
||||
from facefusion import config, content_analyser, inference_manager, logger, process_manager, state_manager, video_manager, wording
|
||||
from facefusion.common_helper import create_int_metavar
|
||||
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
|
||||
from facefusion.execution import has_execution_provider
|
||||
@@ -188,6 +188,7 @@ def pre_process(mode : ProcessMode) -> bool:
|
||||
|
||||
def post_process() -> None:
|
||||
read_static_image.cache_clear()
|
||||
video_manager.clear_video_pool()
|
||||
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
||||
clear_inference_pool()
|
||||
if state_manager.get_item('video_memory_strategy') == 'strict':
|
||||
|
||||
@@ -8,7 +8,7 @@ import numpy
|
||||
import facefusion.jobs.job_manager
|
||||
import facefusion.jobs.job_store
|
||||
import facefusion.processors.core as processors
|
||||
from facefusion import config, content_analyser, inference_manager, logger, process_manager, state_manager, wording
|
||||
from facefusion import config, content_analyser, inference_manager, logger, process_manager, state_manager, video_manager, wording
|
||||
from facefusion.common_helper import create_int_metavar
|
||||
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
|
||||
from facefusion.execution import has_execution_provider
|
||||
@@ -381,6 +381,27 @@ def create_static_model_set(download_scope : DownloadScope) -> ModelSet:
|
||||
},
|
||||
'size': (128, 8, 4),
|
||||
'scale': 4
|
||||
},
|
||||
'ultra_sharp_2_x4':
|
||||
{
|
||||
'hashes':
|
||||
{
|
||||
'frame_enhancer':
|
||||
{
|
||||
'url': resolve_download_url('models-3.3.0', 'ultra_sharp_2_x4.hash'),
|
||||
'path': resolve_relative_path('../.assets/models/ultra_sharp_2_x4.hash')
|
||||
}
|
||||
},
|
||||
'sources':
|
||||
{
|
||||
'frame_enhancer':
|
||||
{
|
||||
'url': resolve_download_url('models-3.3.0', 'ultra_sharp_2_x4.onnx'),
|
||||
'path': resolve_relative_path('../.assets/models/ultra_sharp_2_x4.onnx')
|
||||
}
|
||||
},
|
||||
'size': (1024, 64, 32),
|
||||
'scale': 4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,6 +471,7 @@ def pre_process(mode : ProcessMode) -> bool:
|
||||
|
||||
def post_process() -> None:
|
||||
read_static_image.cache_clear()
|
||||
video_manager.clear_video_pool()
|
||||
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
||||
clear_inference_pool()
|
||||
if state_manager.get_item('video_memory_strategy') == 'strict':
|
||||
|
||||
@@ -8,21 +8,22 @@ import numpy
|
||||
import facefusion.jobs.job_manager
|
||||
import facefusion.jobs.job_store
|
||||
import facefusion.processors.core as processors
|
||||
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, voice_extractor, wording
|
||||
from facefusion import config, content_analyser, face_classifier, face_detector, face_landmarker, face_masker, face_recognizer, inference_manager, logger, process_manager, state_manager, video_manager, voice_extractor, wording
|
||||
from facefusion.audio import create_empty_audio_frame, get_voice_frame, read_static_voice
|
||||
from facefusion.common_helper import create_float_metavar
|
||||
from facefusion.common_helper import get_first
|
||||
from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url
|
||||
from facefusion.face_analyser import get_many_faces, get_one_face
|
||||
from facefusion.face_helper import create_bounding_box, paste_back, warp_face_by_bounding_box, warp_face_by_face_landmark_5
|
||||
from facefusion.face_masker import create_mouth_mask, create_occlusion_mask, create_static_box_mask
|
||||
from facefusion.face_masker import create_area_mask, create_box_mask, create_occlusion_mask
|
||||
from facefusion.face_selector import find_similar_faces, sort_and_filter_faces
|
||||
from facefusion.face_store import get_reference_faces
|
||||
from facefusion.filesystem import filter_audio_paths, has_audio, in_directory, is_image, is_video, resolve_relative_path, same_file_extension
|
||||
from facefusion.processors import choices as processors_choices
|
||||
from facefusion.processors.types import LipSyncerInputs
|
||||
from facefusion.processors.types import LipSyncerInputs, LipSyncerWeight
|
||||
from facefusion.program_helper import find_argument_group
|
||||
from facefusion.thread_helper import conditional_thread_semaphore
|
||||
from facefusion.types import ApplyStateItem, Args, AudioFrame, DownloadScope, Face, InferencePool, ModelOptions, ModelSet, ProcessMode, QueuePayload, UpdateProgress, VisionFrame
|
||||
from facefusion.types import ApplyStateItem, Args, AudioFrame, BoundingBox, DownloadScope, Face, InferencePool, ModelOptions, ModelSet, ProcessMode, QueuePayload, UpdateProgress, VisionFrame
|
||||
from facefusion.vision import read_image, read_static_image, restrict_video_fps, write_image
|
||||
|
||||
|
||||
@@ -30,6 +31,27 @@ from facefusion.vision import read_image, read_static_image, restrict_video_fps,
|
||||
def create_static_model_set(download_scope : DownloadScope) -> ModelSet:
|
||||
return\
|
||||
{
|
||||
'edtalk_256':
|
||||
{
|
||||
'hashes':
|
||||
{
|
||||
'lip_syncer':
|
||||
{
|
||||
'url': resolve_download_url('models-3.3.0', 'edtalk_256.hash'),
|
||||
'path': resolve_relative_path('../.assets/models/edtalk_256.hash')
|
||||
}
|
||||
},
|
||||
'sources':
|
||||
{
|
||||
'lip_syncer':
|
||||
{
|
||||
'url': resolve_download_url('models-3.3.0', 'edtalk_256.onnx'),
|
||||
'path': resolve_relative_path('../.assets/models/edtalk_256.onnx')
|
||||
}
|
||||
},
|
||||
'type': 'edtalk',
|
||||
'size': (256, 256)
|
||||
},
|
||||
'wav2lip_96':
|
||||
{
|
||||
'hashes':
|
||||
@@ -48,6 +70,7 @@ def create_static_model_set(download_scope : DownloadScope) -> ModelSet:
|
||||
'path': resolve_relative_path('../.assets/models/wav2lip_96.onnx')
|
||||
}
|
||||
},
|
||||
'type': 'wav2lip',
|
||||
'size': (96, 96)
|
||||
},
|
||||
'wav2lip_gan_96':
|
||||
@@ -68,6 +91,7 @@ def create_static_model_set(download_scope : DownloadScope) -> ModelSet:
|
||||
'path': resolve_relative_path('../.assets/models/wav2lip_gan_96.onnx')
|
||||
}
|
||||
},
|
||||
'type': 'wav2lip',
|
||||
'size': (96, 96)
|
||||
}
|
||||
}
|
||||
@@ -94,11 +118,13 @@ def register_args(program : ArgumentParser) -> None:
|
||||
group_processors = find_argument_group(program, 'processors')
|
||||
if group_processors:
|
||||
group_processors.add_argument('--lip-syncer-model', help = wording.get('help.lip_syncer_model'), default = config.get_str_value('processors', 'lip_syncer_model', 'wav2lip_gan_96'), choices = processors_choices.lip_syncer_models)
|
||||
facefusion.jobs.job_store.register_step_keys([ 'lip_syncer_model' ])
|
||||
group_processors.add_argument('--lip-syncer-weight', help = wording.get('help.lip_syncer_weight'), type = float, default = config.get_float_value('processors', 'lip_syncer_weight', '0.5'), choices = processors_choices.lip_syncer_weight_range, metavar = create_float_metavar(processors_choices.lip_syncer_weight_range))
|
||||
facefusion.jobs.job_store.register_step_keys([ 'lip_syncer_model', 'lip_syncer_weight' ])
|
||||
|
||||
|
||||
def apply_args(args : Args, apply_state_item : ApplyStateItem) -> None:
|
||||
apply_state_item('lip_syncer_model', args.get('lip_syncer_model'))
|
||||
apply_state_item('lip_syncer_weight', args.get('lip_syncer_weight'))
|
||||
|
||||
|
||||
def pre_check() -> bool:
|
||||
@@ -127,6 +153,7 @@ def pre_process(mode : ProcessMode) -> bool:
|
||||
def post_process() -> None:
|
||||
read_static_image.cache_clear()
|
||||
read_static_voice.cache_clear()
|
||||
video_manager.clear_video_pool()
|
||||
if state_manager.get_item('video_memory_strategy') in [ 'strict', 'moderate' ]:
|
||||
clear_inference_pool()
|
||||
if state_manager.get_item('video_memory_strategy') == 'strict':
|
||||
@@ -140,68 +167,115 @@ def post_process() -> None:
|
||||
|
||||
|
||||
def sync_lip(target_face : Face, temp_audio_frame : AudioFrame, temp_vision_frame : VisionFrame) -> VisionFrame:
|
||||
model_type = get_model_options().get('type')
|
||||
model_size = get_model_options().get('size')
|
||||
temp_audio_frame = prepare_audio_frame(temp_audio_frame)
|
||||
crop_vision_frame, affine_matrix = warp_face_by_face_landmark_5(temp_vision_frame, target_face.landmark_set.get('5/68'), 'ffhq_512', (512, 512))
|
||||
face_landmark_68 = cv2.transform(target_face.landmark_set.get('68').reshape(1, -1, 2), affine_matrix).reshape(-1, 2)
|
||||
bounding_box = create_bounding_box(face_landmark_68)
|
||||
bounding_box[1] -= numpy.abs(bounding_box[3] - bounding_box[1]) * 0.125
|
||||
mouth_mask = create_mouth_mask(face_landmark_68)
|
||||
box_mask = create_static_box_mask(crop_vision_frame.shape[:2][::-1], state_manager.get_item('face_mask_blur'), state_manager.get_item('face_mask_padding'))
|
||||
crop_masks =\
|
||||
[
|
||||
mouth_mask,
|
||||
box_mask
|
||||
]
|
||||
crop_masks = []
|
||||
|
||||
if 'occlusion' in state_manager.get_item('face_mask_types'):
|
||||
occlusion_mask = create_occlusion_mask(crop_vision_frame)
|
||||
crop_masks.append(occlusion_mask)
|
||||
|
||||
close_vision_frame, close_matrix = warp_face_by_bounding_box(crop_vision_frame, bounding_box, model_size)
|
||||
close_vision_frame = prepare_crop_frame(close_vision_frame)
|
||||
close_vision_frame = forward(temp_audio_frame, close_vision_frame)
|
||||
close_vision_frame = normalize_close_frame(close_vision_frame)
|
||||
crop_vision_frame = cv2.warpAffine(close_vision_frame, cv2.invertAffineTransform(close_matrix), (512, 512), borderMode = cv2.BORDER_REPLICATE)
|
||||
if model_type == 'edtalk':
|
||||
lip_syncer_weight = numpy.array([ state_manager.get_item('lip_syncer_weight') ]).astype(numpy.float32)
|
||||
box_mask = create_box_mask(crop_vision_frame, state_manager.get_item('face_mask_blur'), state_manager.get_item('face_mask_padding'))
|
||||
crop_masks.append(box_mask)
|
||||
crop_vision_frame = prepare_crop_frame(crop_vision_frame)
|
||||
crop_vision_frame = forward_edtalk(temp_audio_frame, crop_vision_frame, lip_syncer_weight)
|
||||
crop_vision_frame = normalize_crop_frame(crop_vision_frame)
|
||||
if model_type == 'wav2lip':
|
||||
face_landmark_68 = cv2.transform(target_face.landmark_set.get('68').reshape(1, -1, 2), affine_matrix).reshape(-1, 2)
|
||||
area_mask = create_area_mask(crop_vision_frame, face_landmark_68, [ 'lower-face' ])
|
||||
crop_masks.append(area_mask)
|
||||
bounding_box = create_bounding_box(face_landmark_68)
|
||||
bounding_box = resize_bounding_box(bounding_box, 1 / 8)
|
||||
area_vision_frame, area_matrix = warp_face_by_bounding_box(crop_vision_frame, bounding_box, model_size)
|
||||
area_vision_frame = prepare_crop_frame(area_vision_frame)
|
||||
area_vision_frame = forward_wav2lip(temp_audio_frame, area_vision_frame)
|
||||
area_vision_frame = normalize_crop_frame(area_vision_frame)
|
||||
crop_vision_frame = cv2.warpAffine(area_vision_frame, cv2.invertAffineTransform(area_matrix), (512, 512), borderMode = cv2.BORDER_REPLICATE)
|
||||
|
||||
crop_mask = numpy.minimum.reduce(crop_masks)
|
||||
paste_vision_frame = paste_back(temp_vision_frame, crop_vision_frame, crop_mask, affine_matrix)
|
||||
return paste_vision_frame
|
||||
|
||||
|
||||
def forward(temp_audio_frame : AudioFrame, close_vision_frame : VisionFrame) -> VisionFrame:
|
||||
def forward_edtalk(temp_audio_frame : AudioFrame, crop_vision_frame : VisionFrame, lip_syncer_weight : LipSyncerWeight) -> VisionFrame:
|
||||
lip_syncer = get_inference_pool().get('lip_syncer')
|
||||
|
||||
with conditional_thread_semaphore():
|
||||
close_vision_frame = lip_syncer.run(None,
|
||||
crop_vision_frame = lip_syncer.run(None,
|
||||
{
|
||||
'source': temp_audio_frame,
|
||||
'target': close_vision_frame
|
||||
'target': crop_vision_frame,
|
||||
'weight': lip_syncer_weight
|
||||
})[0]
|
||||
|
||||
return close_vision_frame
|
||||
return crop_vision_frame
|
||||
|
||||
|
||||
def forward_wav2lip(temp_audio_frame : AudioFrame, area_vision_frame : VisionFrame) -> VisionFrame:
|
||||
lip_syncer = get_inference_pool().get('lip_syncer')
|
||||
|
||||
with conditional_thread_semaphore():
|
||||
area_vision_frame = lip_syncer.run(None,
|
||||
{
|
||||
'source': temp_audio_frame,
|
||||
'target': area_vision_frame
|
||||
})[0]
|
||||
|
||||
return area_vision_frame
|
||||
|
||||
|
||||
def prepare_audio_frame(temp_audio_frame : AudioFrame) -> AudioFrame:
|
||||
model_type = get_model_options().get('type')
|
||||
temp_audio_frame = numpy.maximum(numpy.exp(-5 * numpy.log(10)), temp_audio_frame)
|
||||
temp_audio_frame = numpy.log10(temp_audio_frame) * 1.6 + 3.2
|
||||
temp_audio_frame = temp_audio_frame.clip(-4, 4).astype(numpy.float32)
|
||||
|
||||
if model_type == 'wav2lip':
|
||||
temp_audio_frame = temp_audio_frame * state_manager.get_item('lip_syncer_weight') * 2.0
|
||||
|
||||
temp_audio_frame = numpy.expand_dims(temp_audio_frame, axis = (0, 1))
|
||||
return temp_audio_frame
|
||||
|
||||
|
||||
def prepare_crop_frame(crop_vision_frame : VisionFrame) -> VisionFrame:
|
||||
model_type = get_model_options().get('type')
|
||||
model_size = get_model_options().get('size')
|
||||
|
||||
if model_type == 'edtalk':
|
||||
crop_vision_frame = cv2.resize(crop_vision_frame, model_size, interpolation = cv2.INTER_AREA)
|
||||
crop_vision_frame = crop_vision_frame[:, :, ::-1] / 255.0
|
||||
crop_vision_frame = numpy.expand_dims(crop_vision_frame.transpose(2, 0, 1), axis = 0).astype(numpy.float32)
|
||||
if model_type == 'wav2lip':
|
||||
crop_vision_frame = numpy.expand_dims(crop_vision_frame, axis = 0)
|
||||
prepare_vision_frame = crop_vision_frame.copy()
|
||||
prepare_vision_frame[:, 48:] = 0
|
||||
prepare_vision_frame[:, model_size[0] // 2:] = 0
|
||||
crop_vision_frame = numpy.concatenate((prepare_vision_frame, crop_vision_frame), axis = 3)
|
||||
crop_vision_frame = crop_vision_frame.transpose(0, 3, 1, 2).astype('float32') / 255.0
|
||||
|
||||
return crop_vision_frame
|
||||
|
||||
|
||||
def normalize_close_frame(crop_vision_frame : VisionFrame) -> VisionFrame:
|
||||
def resize_bounding_box(bounding_box : BoundingBox, aspect_ratio : float) -> BoundingBox:
|
||||
x1, y1, x2, y2 = bounding_box
|
||||
y1 -= numpy.abs(y2 - y1) * aspect_ratio
|
||||
bounding_box[1] = max(y1, 0)
|
||||
return bounding_box
|
||||
|
||||
|
||||
def normalize_crop_frame(crop_vision_frame : VisionFrame) -> VisionFrame:
|
||||
model_type = get_model_options().get('type')
|
||||
crop_vision_frame = crop_vision_frame[0].transpose(1, 2, 0)
|
||||
crop_vision_frame = crop_vision_frame.clip(0, 1) * 255
|
||||
crop_vision_frame = crop_vision_frame.astype(numpy.uint8)
|
||||
|
||||
if model_type == 'edtalk':
|
||||
crop_vision_frame = crop_vision_frame[:, :, ::-1]
|
||||
crop_vision_frame = cv2.resize(crop_vision_frame, (512, 512), interpolation = cv2.INTER_CUBIC)
|
||||
|
||||
return crop_vision_frame
|
||||
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ ExpressionRestorerModel = Literal['live_portrait']
|
||||
FaceDebuggerItem = Literal['bounding-box', 'face-landmark-5', 'face-landmark-5/68', 'face-landmark-68', 'face-landmark-68/5', 'face-mask', 'face-detector-score', 'face-landmarker-score', 'age', 'gender', 'race']
|
||||
FaceEditorModel = Literal['live_portrait']
|
||||
FaceEnhancerModel = Literal['codeformer', 'gfpgan_1.2', 'gfpgan_1.3', 'gfpgan_1.4', 'gpen_bfr_256', 'gpen_bfr_512', 'gpen_bfr_1024', 'gpen_bfr_2048', 'restoreformer_plus_plus']
|
||||
FaceSwapperModel = Literal['blendswap_256', 'ghost_1_256', 'ghost_2_256', 'ghost_3_256', 'hififace_unofficial_256', 'inswapper_128', 'inswapper_128_fp16', 'simswap_256', 'simswap_unofficial_512', 'uniface_256']
|
||||
FaceSwapperModel = Literal['blendswap_256', 'ghost_1_256', 'ghost_2_256', 'ghost_3_256', 'hififace_unofficial_256', 'hyperswap_1a_256', 'hyperswap_1b_256', 'hyperswap_1c_256', 'inswapper_128', 'inswapper_128_fp16', 'simswap_256', 'simswap_unofficial_512', 'uniface_256']
|
||||
FrameColorizerModel = Literal['ddcolor', 'ddcolor_artistic', 'deoldify', 'deoldify_artistic', 'deoldify_stable']
|
||||
FrameEnhancerModel = Literal['clear_reality_x4', 'lsdir_x4', 'nomos8k_sc_x4', 'real_esrgan_x2', 'real_esrgan_x2_fp16', 'real_esrgan_x4', 'real_esrgan_x4_fp16', 'real_esrgan_x8', 'real_esrgan_x8_fp16', 'real_hatgan_x4', 'real_web_photo_x4', 'realistic_rescaler_x4', 'remacri_x4', 'siax_x4', 'span_kendata_x4', 'swin2_sr_x4', 'ultra_sharp_x4']
|
||||
LipSyncerModel = Literal['wav2lip_96', 'wav2lip_gan_96']
|
||||
FrameEnhancerModel = Literal['clear_reality_x4', 'lsdir_x4', 'nomos8k_sc_x4', 'real_esrgan_x2', 'real_esrgan_x2_fp16', 'real_esrgan_x4', 'real_esrgan_x4_fp16', 'real_esrgan_x8', 'real_esrgan_x8_fp16', 'real_hatgan_x4', 'real_web_photo_x4', 'realistic_rescaler_x4', 'remacri_x4', 'siax_x4', 'span_kendata_x4', 'swin2_sr_x4', 'ultra_sharp_x4', 'ultra_sharp_2_x4']
|
||||
LipSyncerModel = Literal['edtalk_256', 'wav2lip_96', 'wav2lip_gan_96']
|
||||
|
||||
FaceSwapperSet : TypeAlias = Dict[FaceSwapperModel, List[str]]
|
||||
|
||||
@@ -103,7 +103,8 @@ ProcessorStateKey = Literal\
|
||||
'frame_colorizer_blend',
|
||||
'frame_enhancer_model',
|
||||
'frame_enhancer_blend',
|
||||
'lip_syncer_model'
|
||||
'lip_syncer_model',
|
||||
'lip_syncer_weight'
|
||||
]
|
||||
ProcessorState = TypedDict('ProcessorState',
|
||||
{
|
||||
@@ -146,6 +147,7 @@ ProcessorStateSet : TypeAlias = Dict[AppContext, ProcessorState]
|
||||
AgeModifierDirection : TypeAlias = NDArray[Any]
|
||||
DeepSwapperMorph : TypeAlias = NDArray[Any]
|
||||
FaceEnhancerWeight : TypeAlias = NDArray[Any]
|
||||
LipSyncerWeight : TypeAlias = NDArray[Any]
|
||||
LivePortraitPitch : TypeAlias = float
|
||||
LivePortraitYaw : TypeAlias = float
|
||||
LivePortraitRoll : TypeAlias = float
|
||||
|
||||
@@ -136,10 +136,11 @@ def create_face_masker_program() -> ArgumentParser:
|
||||
group_face_masker.add_argument('--face-occluder-model', help = wording.get('help.face_occluder_model'), default = config.get_str_value('face_masker', 'face_occluder_model', 'xseg_1'), choices = facefusion.choices.face_occluder_models)
|
||||
group_face_masker.add_argument('--face-parser-model', help = wording.get('help.face_parser_model'), default = config.get_str_value('face_masker', 'face_parser_model', 'bisenet_resnet_34'), choices = facefusion.choices.face_parser_models)
|
||||
group_face_masker.add_argument('--face-mask-types', help = wording.get('help.face_mask_types').format(choices = ', '.join(facefusion.choices.face_mask_types)), default = config.get_str_list('face_masker', 'face_mask_types', 'box'), choices = facefusion.choices.face_mask_types, nargs = '+', metavar = 'FACE_MASK_TYPES')
|
||||
group_face_masker.add_argument('--face-mask-areas', help = wording.get('help.face_mask_areas').format(choices = ', '.join(facefusion.choices.face_mask_areas)), default = config.get_str_list('face_masker', 'face_mask_areas', ' '.join(facefusion.choices.face_mask_areas)), choices = facefusion.choices.face_mask_areas, nargs = '+', metavar = 'FACE_MASK_AREAS')
|
||||
group_face_masker.add_argument('--face-mask-regions', help = wording.get('help.face_mask_regions').format(choices = ', '.join(facefusion.choices.face_mask_regions)), default = config.get_str_list('face_masker', 'face_mask_regions', ' '.join(facefusion.choices.face_mask_regions)), choices = facefusion.choices.face_mask_regions, nargs = '+', metavar = 'FACE_MASK_REGIONS')
|
||||
group_face_masker.add_argument('--face-mask-blur', help = wording.get('help.face_mask_blur'), type = float, default = config.get_float_value('face_masker', 'face_mask_blur', '0.3'), choices = facefusion.choices.face_mask_blur_range, metavar = create_float_metavar(facefusion.choices.face_mask_blur_range))
|
||||
group_face_masker.add_argument('--face-mask-padding', help = wording.get('help.face_mask_padding'), type = int, default = config.get_int_list('face_masker', 'face_mask_padding', '0 0 0 0'), nargs = '+')
|
||||
group_face_masker.add_argument('--face-mask-regions', help = wording.get('help.face_mask_regions').format(choices = ', '.join(facefusion.choices.face_mask_regions)), default = config.get_str_list('face_masker', 'face_mask_regions', ' '.join(facefusion.choices.face_mask_regions)), choices = facefusion.choices.face_mask_regions, nargs = '+', metavar = 'FACE_MASK_REGIONS')
|
||||
job_store.register_step_keys([ 'face_occluder_model', 'face_parser_model', 'face_mask_types', 'face_mask_blur', 'face_mask_padding', 'face_mask_regions' ])
|
||||
job_store.register_step_keys([ 'face_occluder_model', 'face_parser_model', 'face_mask_types', 'face_mask_areas', 'face_mask_regions', 'face_mask_blur', 'face_mask_padding' ])
|
||||
return program
|
||||
|
||||
|
||||
@@ -193,18 +194,6 @@ def create_uis_program() -> ArgumentParser:
|
||||
return program
|
||||
|
||||
|
||||
def create_execution_program() -> ArgumentParser:
|
||||
program = ArgumentParser(add_help = False)
|
||||
available_execution_providers = get_available_execution_providers()
|
||||
group_execution = program.add_argument_group('execution')
|
||||
group_execution.add_argument('--execution-device-id', help = wording.get('help.execution_device_id'), default = config.get_str_value('execution', 'execution_device_id', '0'))
|
||||
group_execution.add_argument('--execution-providers', help = wording.get('help.execution_providers').format(choices = ', '.join(available_execution_providers)), default = config.get_str_list('execution', 'execution_providers', get_first(available_execution_providers)), choices = available_execution_providers, nargs = '+', metavar = 'EXECUTION_PROVIDERS')
|
||||
group_execution.add_argument('--execution-thread-count', help = wording.get('help.execution_thread_count'), type = int, default = config.get_int_value('execution', 'execution_thread_count', '4'), choices = facefusion.choices.execution_thread_count_range, metavar = create_int_metavar(facefusion.choices.execution_thread_count_range))
|
||||
group_execution.add_argument('--execution-queue-count', help = wording.get('help.execution_queue_count'), type = int, default = config.get_int_value('execution', 'execution_queue_count', '1'), choices = facefusion.choices.execution_queue_count_range, metavar = create_int_metavar(facefusion.choices.execution_queue_count_range))
|
||||
job_store.register_job_keys([ 'execution_device_id', 'execution_providers', 'execution_thread_count', 'execution_queue_count' ])
|
||||
return program
|
||||
|
||||
|
||||
def create_download_providers_program() -> ArgumentParser:
|
||||
program = ArgumentParser(add_help = False)
|
||||
group_download = program.add_argument_group('download')
|
||||
@@ -221,6 +210,26 @@ def create_download_scope_program() -> ArgumentParser:
|
||||
return program
|
||||
|
||||
|
||||
def create_benchmark_program() -> ArgumentParser:
|
||||
program = ArgumentParser(add_help = False)
|
||||
group_benchmark = program.add_argument_group('benchmark')
|
||||
group_benchmark.add_argument('--benchmark-resolutions', help = wording.get('help.benchmark_resolutions'), default = config.get_str_list('benchmark', 'benchmark_resolutions', get_first(facefusion.choices.benchmark_resolutions)), choices = facefusion.choices.benchmark_resolutions, nargs = '+')
|
||||
group_benchmark.add_argument('--benchmark-cycle-count', help = wording.get('help.benchmark_cycle_count'), type = int, default = config.get_int_value('benchmark', 'benchmark_cycle_count', '5'), choices = facefusion.choices.benchmark_cycle_count_range)
|
||||
return program
|
||||
|
||||
|
||||
def create_execution_program() -> ArgumentParser:
|
||||
program = ArgumentParser(add_help = False)
|
||||
available_execution_providers = get_available_execution_providers()
|
||||
group_execution = program.add_argument_group('execution')
|
||||
group_execution.add_argument('--execution-device-id', help = wording.get('help.execution_device_id'), default = config.get_str_value('execution', 'execution_device_id', '0'))
|
||||
group_execution.add_argument('--execution-providers', help = wording.get('help.execution_providers').format(choices = ', '.join(available_execution_providers)), default = config.get_str_list('execution', 'execution_providers', get_first(available_execution_providers)), choices = available_execution_providers, nargs = '+', metavar = 'EXECUTION_PROVIDERS')
|
||||
group_execution.add_argument('--execution-thread-count', help = wording.get('help.execution_thread_count'), type = int, default = config.get_int_value('execution', 'execution_thread_count', '4'), choices = facefusion.choices.execution_thread_count_range, metavar = create_int_metavar(facefusion.choices.execution_thread_count_range))
|
||||
group_execution.add_argument('--execution-queue-count', help = wording.get('help.execution_queue_count'), type = int, default = config.get_int_value('execution', 'execution_queue_count', '1'), choices = facefusion.choices.execution_queue_count_range, metavar = create_int_metavar(facefusion.choices.execution_queue_count_range))
|
||||
job_store.register_job_keys([ 'execution_device_id', 'execution_providers', 'execution_thread_count', 'execution_queue_count' ])
|
||||
return program
|
||||
|
||||
|
||||
def create_memory_program() -> ArgumentParser:
|
||||
program = ArgumentParser(add_help = False)
|
||||
group_memory = program.add_argument_group('memory')
|
||||
@@ -283,6 +292,7 @@ def create_program() -> ArgumentParser:
|
||||
sub_program.add_parser('headless-run', help = wording.get('help.headless_run'), parents = [ create_config_path_program(), create_temp_path_program(), create_jobs_path_program(), create_source_paths_program(), create_target_path_program(), create_output_path_program(), collect_step_program(), collect_job_program() ], formatter_class = create_help_formatter_large)
|
||||
sub_program.add_parser('batch-run', help = wording.get('help.batch_run'), parents = [ create_config_path_program(), create_temp_path_program(), create_jobs_path_program(), create_source_pattern_program(), create_target_pattern_program(), create_output_pattern_program(), collect_step_program(), collect_job_program() ], formatter_class = create_help_formatter_large)
|
||||
sub_program.add_parser('force-download', help = wording.get('help.force_download'), parents = [ create_download_providers_program(), create_download_scope_program(), create_log_level_program() ], formatter_class = create_help_formatter_large)
|
||||
sub_program.add_parser('benchmark', help = wording.get('help.benchmark'), parents = [ create_temp_path_program(), collect_step_program(), create_benchmark_program(), collect_job_program() ], formatter_class = create_help_formatter_large)
|
||||
# job manager
|
||||
sub_program.add_parser('job-list', help = wording.get('help.job_list'), parents = [ create_job_status_program(), create_jobs_path_program(), create_log_level_program() ], formatter_class = create_help_formatter_large)
|
||||
sub_program.add_parser('job-create', help = wording.get('help.job_create'), parents = [ create_job_id_program(), create_jobs_path_program(), create_log_level_program() ], formatter_class = create_help_formatter_large)
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
from typing import Any, Dict
|
||||
|
||||
import numpy
|
||||
|
||||
from facefusion import logger, state_manager
|
||||
from facefusion.face_store import get_face_store
|
||||
from facefusion.types import FaceSet
|
||||
|
||||
|
||||
def create_statistics(static_faces : FaceSet) -> Dict[str, Any]:
|
||||
face_detector_scores = []
|
||||
face_landmarker_scores = []
|
||||
statistics =\
|
||||
{
|
||||
'min_face_detector_score': 0,
|
||||
'min_face_landmarker_score': 0,
|
||||
'max_face_detector_score': 0,
|
||||
'max_face_landmarker_score': 0,
|
||||
'average_face_detector_score': 0,
|
||||
'average_face_landmarker_score': 0,
|
||||
'total_face_landmark_5_fallbacks': 0,
|
||||
'total_frames_with_faces': 0,
|
||||
'total_faces': 0
|
||||
}
|
||||
|
||||
for faces in static_faces.values():
|
||||
statistics['total_frames_with_faces'] = statistics.get('total_frames_with_faces') + 1
|
||||
for face in faces:
|
||||
statistics['total_faces'] = statistics.get('total_faces') + 1
|
||||
face_detector_scores.append(face.score_set.get('detector'))
|
||||
face_landmarker_scores.append(face.score_set.get('landmarker'))
|
||||
if numpy.array_equal(face.landmark_set.get('5'), face.landmark_set.get('5/68')):
|
||||
statistics['total_face_landmark_5_fallbacks'] = statistics.get('total_face_landmark_5_fallbacks') + 1
|
||||
|
||||
if face_detector_scores:
|
||||
statistics['min_face_detector_score'] = round(min(face_detector_scores), 2)
|
||||
statistics['max_face_detector_score'] = round(max(face_detector_scores), 2)
|
||||
statistics['average_face_detector_score'] = round(numpy.mean(face_detector_scores), 2)
|
||||
if face_landmarker_scores:
|
||||
statistics['min_face_landmarker_score'] = round(min(face_landmarker_scores), 2)
|
||||
statistics['max_face_landmarker_score'] = round(max(face_landmarker_scores), 2)
|
||||
statistics['average_face_landmarker_score'] = round(numpy.mean(face_landmarker_scores), 2)
|
||||
return statistics
|
||||
|
||||
|
||||
def conditional_log_statistics() -> None:
|
||||
if state_manager.get_item('log_level') == 'debug':
|
||||
statistics = create_statistics(get_face_store().get('static_faces'))
|
||||
|
||||
for name, value in statistics.items():
|
||||
logger.debug(str(name) + ': ' + str(value), __name__)
|
||||
@@ -1,6 +1,7 @@
|
||||
from collections import namedtuple
|
||||
from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, TypeAlias, TypedDict
|
||||
|
||||
import cv2
|
||||
import numpy
|
||||
from numpy.typing import NDArray
|
||||
from onnxruntime import InferenceSession
|
||||
@@ -49,6 +50,7 @@ FaceStore = TypedDict('FaceStore',
|
||||
'static_faces' : FaceSet,
|
||||
'reference_faces' : FaceSet
|
||||
})
|
||||
VideoPoolSet : TypeAlias = Dict[str, cv2.VideoCapture]
|
||||
|
||||
VisionFrame : TypeAlias = NDArray[Any]
|
||||
Mask : TypeAlias = NDArray[Any]
|
||||
@@ -105,9 +107,11 @@ FaceSelectorMode = Literal['many', 'one', 'reference']
|
||||
FaceSelectorOrder = Literal['left-right', 'right-left', 'top-bottom', 'bottom-top', 'small-large', 'large-small', 'best-worst', 'worst-best']
|
||||
FaceOccluderModel = Literal['xseg_1', 'xseg_2', 'xseg_3']
|
||||
FaceParserModel = Literal['bisenet_resnet_18', 'bisenet_resnet_34']
|
||||
FaceMaskType = Literal['box', 'occlusion', 'region']
|
||||
FaceMaskType = Literal['box', 'occlusion', 'area', 'region']
|
||||
FaceMaskArea = Literal['upper-face', 'lower-face', 'mouth']
|
||||
FaceMaskRegion = Literal['skin', 'left-eyebrow', 'right-eyebrow', 'left-eye', 'right-eye', 'glasses', 'nose', 'mouth', 'upper-lip', 'lower-lip']
|
||||
FaceMaskRegionSet : TypeAlias = Dict[FaceMaskRegion, int]
|
||||
FaceMaskAreaSet : TypeAlias = Dict[FaceMaskArea, List[int]]
|
||||
|
||||
AudioFormat = Literal['flac', 'm4a', 'mp3', 'ogg', 'opus', 'wav']
|
||||
ImageFormat = Literal['bmp', 'jpeg', 'png', 'tiff', 'webp']
|
||||
@@ -126,6 +130,18 @@ EncoderSet = TypedDict('EncoderSet',
|
||||
})
|
||||
VideoPreset = Literal['ultrafast', 'superfast', 'veryfast', 'faster', 'fast', 'medium', 'slow', 'slower', 'veryslow']
|
||||
|
||||
BenchmarkResolution = Literal['240p', '360p', '540p', '720p', '1080p', '1440p', '2160p']
|
||||
BenchmarkSet : TypeAlias = Dict[BenchmarkResolution, str]
|
||||
BenchmarkCycleSet = TypedDict('BenchmarkCycleSet',
|
||||
{
|
||||
'target_path' : str,
|
||||
'cycle_count' : int,
|
||||
'average_run' : float,
|
||||
'fastest_run' : float,
|
||||
'slowest_run' : float,
|
||||
'relative_fps' : float
|
||||
})
|
||||
|
||||
WebcamMode = Literal['inline', 'udp', 'v4l2']
|
||||
StreamMode = Literal['udp', 'v4l2']
|
||||
|
||||
@@ -222,7 +238,6 @@ Job = TypedDict('Job',
|
||||
})
|
||||
JobSet : TypeAlias = Dict[str, Job]
|
||||
|
||||
ApplyStateItem : TypeAlias = Callable[[Any, Any], None]
|
||||
StateKey = Literal\
|
||||
[
|
||||
'command',
|
||||
@@ -235,6 +250,10 @@ StateKey = Literal\
|
||||
'source_pattern',
|
||||
'target_pattern',
|
||||
'output_pattern',
|
||||
'download_providers',
|
||||
'download_scope',
|
||||
'benchmark_resolutions',
|
||||
'benchmark_cycle_count',
|
||||
'face_detector_model',
|
||||
'face_detector_size',
|
||||
'face_detector_angles',
|
||||
@@ -253,9 +272,10 @@ StateKey = Literal\
|
||||
'face_occluder_model',
|
||||
'face_parser_model',
|
||||
'face_mask_types',
|
||||
'face_mask_areas',
|
||||
'face_mask_regions',
|
||||
'face_mask_blur',
|
||||
'face_mask_padding',
|
||||
'face_mask_regions',
|
||||
'trim_frame_start',
|
||||
'trim_frame_end',
|
||||
'temp_frame_format',
|
||||
@@ -278,8 +298,6 @@ StateKey = Literal\
|
||||
'execution_providers',
|
||||
'execution_thread_count',
|
||||
'execution_queue_count',
|
||||
'download_providers',
|
||||
'download_scope',
|
||||
'video_memory_strategy',
|
||||
'system_memory_limit',
|
||||
'log_level',
|
||||
@@ -300,6 +318,10 @@ State = TypedDict('State',
|
||||
'source_pattern' : str,
|
||||
'target_pattern' : str,
|
||||
'output_pattern' : str,
|
||||
'download_providers': List[DownloadProvider],
|
||||
'download_scope': DownloadScope,
|
||||
'benchmark_resolutions': List[BenchmarkResolution],
|
||||
'benchmark_cycle_count': int,
|
||||
'face_detector_model' : FaceDetectorModel,
|
||||
'face_detector_size' : str,
|
||||
'face_detector_angles' : List[Angle],
|
||||
@@ -318,9 +340,10 @@ State = TypedDict('State',
|
||||
'face_occluder_model' : FaceOccluderModel,
|
||||
'face_parser_model' : FaceParserModel,
|
||||
'face_mask_types' : List[FaceMaskType],
|
||||
'face_mask_areas' : List[FaceMaskArea],
|
||||
'face_mask_regions' : List[FaceMaskRegion],
|
||||
'face_mask_blur' : float,
|
||||
'face_mask_padding' : Padding,
|
||||
'face_mask_regions' : List[FaceMaskRegion],
|
||||
'trim_frame_start' : int,
|
||||
'trim_frame_end' : int,
|
||||
'temp_frame_format' : TempFrameFormat,
|
||||
@@ -343,8 +366,6 @@ State = TypedDict('State',
|
||||
'execution_providers' : List[ExecutionProvider],
|
||||
'execution_thread_count' : int,
|
||||
'execution_queue_count' : int,
|
||||
'download_providers' : List[DownloadProvider],
|
||||
'download_scope' : DownloadScope,
|
||||
'video_memory_strategy' : VideoMemoryStrategy,
|
||||
'system_memory_limit' : int,
|
||||
'log_level' : LogLevel,
|
||||
@@ -353,5 +374,6 @@ State = TypedDict('State',
|
||||
'job_status' : JobStatus,
|
||||
'step_index' : int
|
||||
})
|
||||
ApplyStateItem : TypeAlias = Callable[[Any, Any], None]
|
||||
StateSet : TypeAlias = Dict[AppContext, State]
|
||||
|
||||
|
||||
@@ -1,31 +1,13 @@
|
||||
import hashlib
|
||||
import os
|
||||
import statistics
|
||||
import tempfile
|
||||
from time import perf_counter
|
||||
from typing import Any, Dict, Generator, List, Optional
|
||||
from typing import Any, Generator, List, Optional
|
||||
|
||||
import gradio
|
||||
|
||||
from facefusion import state_manager, wording
|
||||
from facefusion.core import conditional_process
|
||||
from facefusion.filesystem import get_file_extension, is_video
|
||||
from facefusion.memory import limit_system_memory
|
||||
from facefusion import benchmarker, state_manager, wording
|
||||
from facefusion.types import BenchmarkResolution
|
||||
from facefusion.uis.core import get_ui_component
|
||||
from facefusion.vision import count_video_frame_total, detect_video_fps, detect_video_resolution, pack_resolution
|
||||
|
||||
BENCHMARK_BENCHMARKS_DATAFRAME : Optional[gradio.Dataframe] = None
|
||||
BENCHMARK_START_BUTTON : Optional[gradio.Button] = None
|
||||
BENCHMARKS : Dict[str, str] =\
|
||||
{
|
||||
'240p': '.assets/examples/target-240p.mp4',
|
||||
'360p': '.assets/examples/target-360p.mp4',
|
||||
'540p': '.assets/examples/target-540p.mp4',
|
||||
'720p': '.assets/examples/target-720p.mp4',
|
||||
'1080p': '.assets/examples/target-1080p.mp4',
|
||||
'1440p': '.assets/examples/target-1440p.mp4',
|
||||
'2160p': '.assets/examples/target-2160p.mp4'
|
||||
}
|
||||
|
||||
|
||||
def render() -> None:
|
||||
@@ -36,7 +18,7 @@ def render() -> None:
|
||||
headers =
|
||||
[
|
||||
'target_path',
|
||||
'benchmark_cycles',
|
||||
'cycle_count',
|
||||
'average_run',
|
||||
'fastest_run',
|
||||
'slowest_run',
|
||||
@@ -61,72 +43,19 @@ def render() -> None:
|
||||
|
||||
|
||||
def listen() -> None:
|
||||
benchmark_runs_checkbox_group = get_ui_component('benchmark_runs_checkbox_group')
|
||||
benchmark_cycles_slider = get_ui_component('benchmark_cycles_slider')
|
||||
benchmark_resolutions_checkbox_group = get_ui_component('benchmark_resolutions_checkbox_group')
|
||||
benchmark_cycle_count_slider = get_ui_component('benchmark_cycle_count_slider')
|
||||
|
||||
if benchmark_runs_checkbox_group and benchmark_cycles_slider:
|
||||
BENCHMARK_START_BUTTON.click(start, inputs = [ benchmark_runs_checkbox_group, benchmark_cycles_slider ], outputs = BENCHMARK_BENCHMARKS_DATAFRAME)
|
||||
if benchmark_resolutions_checkbox_group and benchmark_cycle_count_slider:
|
||||
BENCHMARK_START_BUTTON.click(start, inputs = [ benchmark_resolutions_checkbox_group, benchmark_cycle_count_slider ], outputs = BENCHMARK_BENCHMARKS_DATAFRAME)
|
||||
|
||||
|
||||
def suggest_output_path(target_path : str) -> Optional[str]:
|
||||
if is_video(target_path):
|
||||
target_file_extension = get_file_extension(target_path)
|
||||
return os.path.join(tempfile.gettempdir(), hashlib.sha1().hexdigest()[:8] + target_file_extension)
|
||||
return None
|
||||
|
||||
|
||||
def start(benchmark_runs : List[str], benchmark_cycles : int) -> Generator[List[Any], None, None]:
|
||||
state_manager.init_item('source_paths', [ '.assets/examples/source.jpg', '.assets/examples/source.mp3' ])
|
||||
state_manager.init_item('face_landmarker_score', 0)
|
||||
state_manager.init_item('temp_frame_format', 'bmp')
|
||||
state_manager.init_item('output_audio_volume', 0)
|
||||
state_manager.init_item('output_video_preset', 'ultrafast')
|
||||
def start(benchmark_resolutions : List[BenchmarkResolution], benchmark_cycle_count : int) -> Generator[List[Any], None, None]:
|
||||
state_manager.set_item('benchmark_resolutions', benchmark_resolutions)
|
||||
state_manager.set_item('benchmark_cycle_count', benchmark_cycle_count)
|
||||
state_manager.sync_item('execution_providers')
|
||||
state_manager.sync_item('execution_thread_count')
|
||||
state_manager.sync_item('execution_queue_count')
|
||||
state_manager.sync_item('system_memory_limit')
|
||||
benchmark_results = []
|
||||
target_paths = [ BENCHMARKS[benchmark_run] for benchmark_run in benchmark_runs if benchmark_run in BENCHMARKS ]
|
||||
|
||||
if target_paths:
|
||||
pre_process()
|
||||
for target_path in target_paths:
|
||||
state_manager.init_item('target_path', target_path)
|
||||
state_manager.init_item('output_path', suggest_output_path(state_manager.get_item('target_path')))
|
||||
benchmark_results.append(benchmark(benchmark_cycles))
|
||||
yield benchmark_results
|
||||
|
||||
|
||||
def pre_process() -> None:
|
||||
system_memory_limit = state_manager.get_item('system_memory_limit')
|
||||
if system_memory_limit and system_memory_limit > 0:
|
||||
limit_system_memory(system_memory_limit)
|
||||
|
||||
|
||||
def benchmark(benchmark_cycles : int) -> List[Any]:
|
||||
process_times = []
|
||||
video_frame_total = count_video_frame_total(state_manager.get_item('target_path'))
|
||||
output_video_resolution = detect_video_resolution(state_manager.get_item('target_path'))
|
||||
state_manager.init_item('output_video_resolution', pack_resolution(output_video_resolution))
|
||||
state_manager.init_item('output_video_fps', detect_video_fps(state_manager.get_item('target_path')))
|
||||
|
||||
conditional_process()
|
||||
for index in range(benchmark_cycles):
|
||||
start_time = perf_counter()
|
||||
conditional_process()
|
||||
end_time = perf_counter()
|
||||
process_times.append(end_time - start_time)
|
||||
average_run = round(statistics.mean(process_times), 2)
|
||||
fastest_run = round(min(process_times), 2)
|
||||
slowest_run = round(max(process_times), 2)
|
||||
relative_fps = round(video_frame_total * benchmark_cycles / sum(process_times), 2)
|
||||
|
||||
return\
|
||||
[
|
||||
state_manager.get_item('target_path'),
|
||||
benchmark_cycles,
|
||||
average_run,
|
||||
fastest_run,
|
||||
slowest_run,
|
||||
relative_fps
|
||||
]
|
||||
for benchmark in benchmarker.run():
|
||||
yield [ list(benchmark_set.values()) for benchmark_set in benchmark ]
|
||||
|
||||
@@ -2,29 +2,29 @@ from typing import Optional
|
||||
|
||||
import gradio
|
||||
|
||||
import facefusion.choices
|
||||
from facefusion import wording
|
||||
from facefusion.uis.components.benchmark import BENCHMARKS
|
||||
from facefusion.uis.core import register_ui_component
|
||||
|
||||
BENCHMARK_RUNS_CHECKBOX_GROUP : Optional[gradio.CheckboxGroup] = None
|
||||
BENCHMARK_CYCLES_SLIDER : Optional[gradio.Button] = None
|
||||
BENCHMARK_RESOLUTIONS_CHECKBOX_GROUP : Optional[gradio.CheckboxGroup] = None
|
||||
BENCHMARK_CYCLE_COUNT_SLIDER : Optional[gradio.Button] = None
|
||||
|
||||
|
||||
def render() -> None:
|
||||
global BENCHMARK_RUNS_CHECKBOX_GROUP
|
||||
global BENCHMARK_CYCLES_SLIDER
|
||||
global BENCHMARK_RESOLUTIONS_CHECKBOX_GROUP
|
||||
global BENCHMARK_CYCLE_COUNT_SLIDER
|
||||
|
||||
BENCHMARK_RUNS_CHECKBOX_GROUP = gradio.CheckboxGroup(
|
||||
label = wording.get('uis.benchmark_runs_checkbox_group'),
|
||||
choices = list(BENCHMARKS.keys()),
|
||||
value = list(BENCHMARKS.keys())
|
||||
BENCHMARK_RESOLUTIONS_CHECKBOX_GROUP = gradio.CheckboxGroup(
|
||||
label = wording.get('uis.benchmark_resolutions_checkbox_group'),
|
||||
choices = facefusion.choices.benchmark_resolutions,
|
||||
value = facefusion.choices.benchmark_resolutions
|
||||
)
|
||||
BENCHMARK_CYCLES_SLIDER = gradio.Slider(
|
||||
label = wording.get('uis.benchmark_cycles_slider'),
|
||||
BENCHMARK_CYCLE_COUNT_SLIDER = gradio.Slider(
|
||||
label = wording.get('uis.benchmark_cycle_count_slider'),
|
||||
value = 5,
|
||||
step = 1,
|
||||
minimum = 1,
|
||||
maximum = 10
|
||||
minimum = min(facefusion.choices.benchmark_cycle_count_range),
|
||||
maximum = max(facefusion.choices.benchmark_cycle_count_range)
|
||||
)
|
||||
register_ui_component('benchmark_runs_checkbox_group', BENCHMARK_RUNS_CHECKBOX_GROUP)
|
||||
register_ui_component('benchmark_cycles_slider', BENCHMARK_CYCLES_SLIDER)
|
||||
register_ui_component('benchmark_resolutions_checkbox_group', BENCHMARK_RESOLUTIONS_CHECKBOX_GROUP)
|
||||
register_ui_component('benchmark_cycle_count_slider', BENCHMARK_CYCLE_COUNT_SLIDER)
|
||||
|
||||
@@ -5,12 +5,13 @@ import gradio
|
||||
import facefusion.choices
|
||||
from facefusion import face_masker, state_manager, wording
|
||||
from facefusion.common_helper import calc_float_step, calc_int_step
|
||||
from facefusion.types import FaceMaskRegion, FaceMaskType, FaceOccluderModel, FaceParserModel
|
||||
from facefusion.types import FaceMaskArea, FaceMaskRegion, FaceMaskType, FaceOccluderModel, FaceParserModel
|
||||
from facefusion.uis.core import register_ui_component
|
||||
|
||||
FACE_OCCLUDER_MODEL_DROPDOWN : Optional[gradio.Dropdown] = None
|
||||
FACE_PARSER_MODEL_DROPDOWN : Optional[gradio.Dropdown] = None
|
||||
FACE_MASK_TYPES_CHECKBOX_GROUP : Optional[gradio.CheckboxGroup] = None
|
||||
FACE_MASK_AREAS_CHECKBOX_GROUP : Optional[gradio.CheckboxGroup] = None
|
||||
FACE_MASK_REGIONS_CHECKBOX_GROUP : Optional[gradio.CheckboxGroup] = None
|
||||
FACE_MASK_BLUR_SLIDER : Optional[gradio.Slider] = None
|
||||
FACE_MASK_PADDING_TOP_SLIDER : Optional[gradio.Slider] = None
|
||||
@@ -23,6 +24,7 @@ def render() -> None:
|
||||
global FACE_OCCLUDER_MODEL_DROPDOWN
|
||||
global FACE_PARSER_MODEL_DROPDOWN
|
||||
global FACE_MASK_TYPES_CHECKBOX_GROUP
|
||||
global FACE_MASK_AREAS_CHECKBOX_GROUP
|
||||
global FACE_MASK_REGIONS_CHECKBOX_GROUP
|
||||
global FACE_MASK_BLUR_SLIDER
|
||||
global FACE_MASK_PADDING_TOP_SLIDER
|
||||
@@ -32,6 +34,7 @@ def render() -> None:
|
||||
|
||||
has_box_mask = 'box' in state_manager.get_item('face_mask_types')
|
||||
has_region_mask = 'region' in state_manager.get_item('face_mask_types')
|
||||
has_area_mask = 'area' in state_manager.get_item('face_mask_types')
|
||||
with gradio.Row():
|
||||
FACE_OCCLUDER_MODEL_DROPDOWN = gradio.Dropdown(
|
||||
label = wording.get('uis.face_occluder_model_dropdown'),
|
||||
@@ -48,6 +51,12 @@ def render() -> None:
|
||||
choices = facefusion.choices.face_mask_types,
|
||||
value = state_manager.get_item('face_mask_types')
|
||||
)
|
||||
FACE_MASK_AREAS_CHECKBOX_GROUP = gradio.CheckboxGroup(
|
||||
label = wording.get('uis.face_mask_areas_checkbox_group'),
|
||||
choices = facefusion.choices.face_mask_areas,
|
||||
value = state_manager.get_item('face_mask_areas'),
|
||||
visible = has_area_mask
|
||||
)
|
||||
FACE_MASK_REGIONS_CHECKBOX_GROUP = gradio.CheckboxGroup(
|
||||
label = wording.get('uis.face_mask_regions_checkbox_group'),
|
||||
choices = facefusion.choices.face_mask_regions,
|
||||
@@ -100,6 +109,7 @@ def render() -> None:
|
||||
register_ui_component('face_occluder_model_dropdown', FACE_OCCLUDER_MODEL_DROPDOWN)
|
||||
register_ui_component('face_parser_model_dropdown', FACE_PARSER_MODEL_DROPDOWN)
|
||||
register_ui_component('face_mask_types_checkbox_group', FACE_MASK_TYPES_CHECKBOX_GROUP)
|
||||
register_ui_component('face_mask_areas_checkbox_group', FACE_MASK_AREAS_CHECKBOX_GROUP)
|
||||
register_ui_component('face_mask_regions_checkbox_group', FACE_MASK_REGIONS_CHECKBOX_GROUP)
|
||||
register_ui_component('face_mask_blur_slider', FACE_MASK_BLUR_SLIDER)
|
||||
register_ui_component('face_mask_padding_top_slider', FACE_MASK_PADDING_TOP_SLIDER)
|
||||
@@ -111,9 +121,11 @@ def render() -> None:
|
||||
def listen() -> None:
|
||||
FACE_OCCLUDER_MODEL_DROPDOWN.change(update_face_occluder_model, inputs = FACE_OCCLUDER_MODEL_DROPDOWN)
|
||||
FACE_PARSER_MODEL_DROPDOWN.change(update_face_parser_model, inputs = FACE_PARSER_MODEL_DROPDOWN)
|
||||
FACE_MASK_TYPES_CHECKBOX_GROUP.change(update_face_mask_types, inputs = FACE_MASK_TYPES_CHECKBOX_GROUP, outputs = [ FACE_MASK_TYPES_CHECKBOX_GROUP, FACE_MASK_REGIONS_CHECKBOX_GROUP, FACE_MASK_BLUR_SLIDER, FACE_MASK_PADDING_TOP_SLIDER, FACE_MASK_PADDING_RIGHT_SLIDER, FACE_MASK_PADDING_BOTTOM_SLIDER, FACE_MASK_PADDING_LEFT_SLIDER ])
|
||||
FACE_MASK_TYPES_CHECKBOX_GROUP.change(update_face_mask_types, inputs = FACE_MASK_TYPES_CHECKBOX_GROUP, outputs = [ FACE_MASK_TYPES_CHECKBOX_GROUP, FACE_MASK_AREAS_CHECKBOX_GROUP, FACE_MASK_REGIONS_CHECKBOX_GROUP, FACE_MASK_BLUR_SLIDER, FACE_MASK_PADDING_TOP_SLIDER, FACE_MASK_PADDING_RIGHT_SLIDER, FACE_MASK_PADDING_BOTTOM_SLIDER, FACE_MASK_PADDING_LEFT_SLIDER ])
|
||||
FACE_MASK_AREAS_CHECKBOX_GROUP.change(update_face_mask_areas, inputs = FACE_MASK_AREAS_CHECKBOX_GROUP, outputs = FACE_MASK_AREAS_CHECKBOX_GROUP)
|
||||
FACE_MASK_REGIONS_CHECKBOX_GROUP.change(update_face_mask_regions, inputs = FACE_MASK_REGIONS_CHECKBOX_GROUP, outputs = FACE_MASK_REGIONS_CHECKBOX_GROUP)
|
||||
FACE_MASK_BLUR_SLIDER.release(update_face_mask_blur, inputs = FACE_MASK_BLUR_SLIDER)
|
||||
|
||||
face_mask_padding_sliders = [ FACE_MASK_PADDING_TOP_SLIDER, FACE_MASK_PADDING_RIGHT_SLIDER, FACE_MASK_PADDING_BOTTOM_SLIDER, FACE_MASK_PADDING_LEFT_SLIDER ]
|
||||
for face_mask_padding_slider in face_mask_padding_sliders:
|
||||
face_mask_padding_slider.release(update_face_mask_padding, inputs = face_mask_padding_sliders)
|
||||
@@ -137,12 +149,19 @@ def update_face_parser_model(face_parser_model : FaceParserModel) -> gradio.Drop
|
||||
return gradio.Dropdown()
|
||||
|
||||
|
||||
def update_face_mask_types(face_mask_types : List[FaceMaskType]) -> Tuple[gradio.CheckboxGroup, gradio.CheckboxGroup, gradio.Slider, gradio.Slider, gradio.Slider, gradio.Slider, gradio.Slider]:
|
||||
def update_face_mask_types(face_mask_types : List[FaceMaskType]) -> Tuple[gradio.CheckboxGroup, gradio.CheckboxGroup, gradio.CheckboxGroup, gradio.Slider, gradio.Slider, gradio.Slider, gradio.Slider, gradio.Slider]:
|
||||
face_mask_types = face_mask_types or facefusion.choices.face_mask_types
|
||||
state_manager.set_item('face_mask_types', face_mask_types)
|
||||
has_box_mask = 'box' in face_mask_types
|
||||
has_area_mask = 'area' in face_mask_types
|
||||
has_region_mask = 'region' in face_mask_types
|
||||
return gradio.CheckboxGroup(value = state_manager.get_item('face_mask_types')), gradio.CheckboxGroup(visible = has_region_mask), gradio.Slider(visible = has_box_mask), gradio.Slider(visible = has_box_mask), gradio.Slider(visible = has_box_mask), gradio.Slider(visible = has_box_mask), gradio.Slider(visible = has_box_mask)
|
||||
return gradio.CheckboxGroup(value = state_manager.get_item('face_mask_types')), gradio.CheckboxGroup(visible = has_area_mask), gradio.CheckboxGroup(visible = has_region_mask), gradio.Slider(visible = has_box_mask), gradio.Slider(visible = has_box_mask), gradio.Slider(visible = has_box_mask), gradio.Slider(visible = has_box_mask), gradio.Slider(visible = has_box_mask)
|
||||
|
||||
|
||||
def update_face_mask_areas(face_mask_areas : List[FaceMaskArea]) -> gradio.CheckboxGroup:
|
||||
face_mask_areas = face_mask_areas or facefusion.choices.face_mask_areas
|
||||
state_manager.set_item('face_mask_areas', face_mask_areas)
|
||||
return gradio.CheckboxGroup(value = state_manager.get_item('face_mask_areas'))
|
||||
|
||||
|
||||
def update_face_mask_regions(face_mask_regions : List[FaceMaskRegion]) -> gradio.CheckboxGroup:
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
from typing import List, Optional
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
import gradio
|
||||
|
||||
from facefusion import state_manager, wording
|
||||
from facefusion.common_helper import calc_float_step
|
||||
from facefusion.processors import choices as processors_choices
|
||||
from facefusion.processors.core import load_processor_module
|
||||
from facefusion.processors.types import LipSyncerModel
|
||||
from facefusion.uis.core import get_ui_component, register_ui_component
|
||||
|
||||
LIP_SYNCER_MODEL_DROPDOWN : Optional[gradio.Dropdown] = None
|
||||
LIP_SYNCER_WEIGHT_SLIDER : Optional[gradio.Slider] = None
|
||||
|
||||
|
||||
def render() -> None:
|
||||
global LIP_SYNCER_MODEL_DROPDOWN
|
||||
global LIP_SYNCER_WEIGHT_SLIDER
|
||||
|
||||
has_lip_syncer = 'lip_syncer' in state_manager.get_item('processors')
|
||||
LIP_SYNCER_MODEL_DROPDOWN = gradio.Dropdown(
|
||||
@@ -21,20 +24,30 @@ def render() -> None:
|
||||
value = state_manager.get_item('lip_syncer_model'),
|
||||
visible = has_lip_syncer
|
||||
)
|
||||
LIP_SYNCER_WEIGHT_SLIDER = gradio.Slider(
|
||||
label = wording.get('uis.lip_syncer_weight_slider'),
|
||||
value = state_manager.get_item('lip_syncer_weight'),
|
||||
step = calc_float_step(processors_choices.lip_syncer_weight_range),
|
||||
minimum = processors_choices.lip_syncer_weight_range[0],
|
||||
maximum = processors_choices.lip_syncer_weight_range[-1],
|
||||
visible = has_lip_syncer
|
||||
)
|
||||
register_ui_component('lip_syncer_model_dropdown', LIP_SYNCER_MODEL_DROPDOWN)
|
||||
register_ui_component('lip_syncer_weight_slider', LIP_SYNCER_WEIGHT_SLIDER)
|
||||
|
||||
|
||||
def listen() -> None:
|
||||
LIP_SYNCER_MODEL_DROPDOWN.change(update_lip_syncer_model, inputs = LIP_SYNCER_MODEL_DROPDOWN, outputs = LIP_SYNCER_MODEL_DROPDOWN)
|
||||
LIP_SYNCER_WEIGHT_SLIDER.release(update_lip_syncer_weight, inputs = LIP_SYNCER_WEIGHT_SLIDER)
|
||||
|
||||
processors_checkbox_group = get_ui_component('processors_checkbox_group')
|
||||
if processors_checkbox_group:
|
||||
processors_checkbox_group.change(remote_update, inputs = processors_checkbox_group, outputs = LIP_SYNCER_MODEL_DROPDOWN)
|
||||
processors_checkbox_group.change(remote_update, inputs = processors_checkbox_group, outputs = [ LIP_SYNCER_MODEL_DROPDOWN, LIP_SYNCER_WEIGHT_SLIDER ])
|
||||
|
||||
|
||||
def remote_update(processors : List[str]) -> gradio.Dropdown:
|
||||
def remote_update(processors : List[str]) -> Tuple[gradio.Dropdown, gradio.Slider]:
|
||||
has_lip_syncer = 'lip_syncer' in processors
|
||||
return gradio.Dropdown(visible = has_lip_syncer)
|
||||
return gradio.Dropdown(visible = has_lip_syncer), gradio.Slider(visible = has_lip_syncer)
|
||||
|
||||
|
||||
def update_lip_syncer_model(lip_syncer_model : LipSyncerModel) -> gradio.Dropdown:
|
||||
@@ -45,3 +58,7 @@ def update_lip_syncer_model(lip_syncer_model : LipSyncerModel) -> gradio.Dropdow
|
||||
if lip_syncer_module.pre_check():
|
||||
return gradio.Dropdown(value = state_manager.get_item('lip_syncer_model'))
|
||||
return gradio.Dropdown()
|
||||
|
||||
|
||||
def update_lip_syncer_weight(lip_syncer_weight : float) -> None:
|
||||
state_manager.set_item('lip_syncer_weight', lip_syncer_weight)
|
||||
|
||||
@@ -104,6 +104,7 @@ def listen() -> None:
|
||||
'face_debugger_items_checkbox_group',
|
||||
'frame_colorizer_size_dropdown',
|
||||
'face_mask_types_checkbox_group',
|
||||
'face_mask_areas_checkbox_group',
|
||||
'face_mask_regions_checkbox_group'
|
||||
]):
|
||||
ui_component.change(update_preview_image, inputs = PREVIEW_FRAME_SLIDER, outputs = PREVIEW_IMAGE)
|
||||
@@ -131,6 +132,7 @@ def listen() -> None:
|
||||
'face_enhancer_weight_slider',
|
||||
'frame_colorizer_blend_slider',
|
||||
'frame_enhancer_blend_slider',
|
||||
'lip_syncer_weight_slider',
|
||||
'reference_face_distance_slider',
|
||||
'face_selector_age_range_slider',
|
||||
'face_mask_blur_slider',
|
||||
|
||||
@@ -40,4 +40,10 @@ def update_processors(processors : List[str]) -> gradio.CheckboxGroup:
|
||||
|
||||
def sort_processors(processors : List[str]) -> List[str]:
|
||||
available_processors = [ get_file_name(file_path) for file_path in resolve_file_paths('facefusion/processors/modules') ]
|
||||
return sorted(available_processors, key = lambda processor : processors.index(processor) if processor in processors else len(processors))
|
||||
current_processors = []
|
||||
|
||||
for processor in processors + available_processors:
|
||||
if processor in available_processors and processor not in current_processors:
|
||||
current_processors.append(processor)
|
||||
|
||||
return current_processors
|
||||
|
||||
@@ -107,8 +107,9 @@ def start(webcam_device_id : int, webcam_mode : WebcamMode, webcam_resolution :
|
||||
webcam_capture.set(cv2.CAP_PROP_FPS, webcam_fps)
|
||||
|
||||
for capture_frame in multi_process_capture(source_face, webcam_capture, webcam_fps):
|
||||
capture_frame = normalize_frame_color(capture_frame)
|
||||
if webcam_mode == 'inline':
|
||||
yield normalize_frame_color(capture_frame)
|
||||
yield capture_frame
|
||||
else:
|
||||
try:
|
||||
stream.stdin.write(capture_frame.tobytes())
|
||||
@@ -166,19 +167,20 @@ def open_stream(stream_mode : StreamMode, stream_resolution : str, stream_fps :
|
||||
commands = ffmpeg_builder.chain(
|
||||
ffmpeg_builder.capture_video(),
|
||||
ffmpeg_builder.set_media_resolution(stream_resolution),
|
||||
ffmpeg_builder.set_conditional_fps(stream_fps)
|
||||
ffmpeg_builder.set_input_fps(stream_fps)
|
||||
)
|
||||
|
||||
if stream_mode == 'udp':
|
||||
commands.extend(ffmpeg_builder.set_input('-'))
|
||||
commands.extend(ffmpeg_builder.set_stream_mode('udp'))
|
||||
commands.extend(ffmpeg_builder.set_stream_quality(2000))
|
||||
commands.extend(ffmpeg_builder.set_output('udp://localhost:27000?pkt_size=1316'))
|
||||
|
||||
if stream_mode == 'v4l2':
|
||||
device_directory_path = '/sys/devices/virtual/video4linux'
|
||||
|
||||
commands.extend(ffmpeg_builder.set_input('-'))
|
||||
commands.extend(ffmpeg_builder.set_stream_mode('v4l2'))
|
||||
|
||||
if is_directory(device_directory_path):
|
||||
device_names = os.listdir(device_directory_path)
|
||||
|
||||
|
||||
@@ -1,24 +1,12 @@
|
||||
import gradio
|
||||
|
||||
from facefusion import state_manager
|
||||
from facefusion.download import conditional_download, resolve_download_url
|
||||
from facefusion.benchmarker import pre_check as benchmarker_pre_check
|
||||
from facefusion.uis.components import about, age_modifier_options, benchmark, benchmark_options, deep_swapper_options, download, execution, execution_queue_count, execution_thread_count, expression_restorer_options, face_debugger_options, face_editor_options, face_enhancer_options, face_swapper_options, frame_colorizer_options, frame_enhancer_options, lip_syncer_options, memory, processors
|
||||
|
||||
|
||||
def pre_check() -> bool:
|
||||
conditional_download('.assets/examples',
|
||||
[
|
||||
resolve_download_url('examples-3.0.0', 'source.jpg'),
|
||||
resolve_download_url('examples-3.0.0', 'source.mp3'),
|
||||
resolve_download_url('examples-3.0.0', 'target-240p.mp4'),
|
||||
resolve_download_url('examples-3.0.0', 'target-360p.mp4'),
|
||||
resolve_download_url('examples-3.0.0', 'target-540p.mp4'),
|
||||
resolve_download_url('examples-3.0.0', 'target-720p.mp4'),
|
||||
resolve_download_url('examples-3.0.0', 'target-1080p.mp4'),
|
||||
resolve_download_url('examples-3.0.0', 'target-1440p.mp4'),
|
||||
resolve_download_url('examples-3.0.0', 'target-2160p.mp4')
|
||||
])
|
||||
return True
|
||||
return benchmarker_pre_check()
|
||||
|
||||
|
||||
def render() -> gradio.Blocks:
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from gradio.processing_utils import video_is_playable
|
||||
|
||||
from facefusion import ffmpeg_builder
|
||||
from facefusion.ffmpeg import run_ffmpeg
|
||||
from facefusion.filesystem import get_file_size
|
||||
@@ -10,28 +8,14 @@ def convert_video_to_playable_mp4(video_path : str) -> str:
|
||||
video_file_size = get_file_size(video_path)
|
||||
max_file_size = 512 * 1024 * 1024
|
||||
|
||||
create_temp_directory(video_path)
|
||||
temp_video_path = get_temp_file_path(video_path)
|
||||
commands = ffmpeg_builder.set_input(video_path)
|
||||
|
||||
if video_file_size > max_file_size:
|
||||
create_temp_directory(video_path)
|
||||
temp_video_path = get_temp_file_path(video_path)
|
||||
commands = ffmpeg_builder.chain(
|
||||
ffmpeg_builder.set_input(video_path),
|
||||
ffmpeg_builder.set_video_duration(10),
|
||||
ffmpeg_builder.force_output(temp_video_path)
|
||||
)
|
||||
commands.extend(ffmpeg_builder.set_video_duration(10))
|
||||
|
||||
process = run_ffmpeg(commands)
|
||||
process.communicate()
|
||||
|
||||
if process.returncode == 0:
|
||||
return temp_video_path
|
||||
|
||||
if not video_is_playable(video_path):
|
||||
create_temp_directory(video_path)
|
||||
temp_video_path = get_temp_file_path(video_path)
|
||||
commands = ffmpeg_builder.chain(
|
||||
ffmpeg_builder.set_input(video_path),
|
||||
ffmpeg_builder.force_output(temp_video_path)
|
||||
)
|
||||
commands.extend(ffmpeg_builder.force_output(temp_video_path))
|
||||
|
||||
process = run_ffmpeg(commands)
|
||||
process.communicate()
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
from typing import Any, Dict, IO, Literal, TypeAlias
|
||||
|
||||
File : TypeAlias = IO[Any]
|
||||
Component : TypeAlias = Any
|
||||
ComponentOptions : TypeAlias = Dict[str, Any]
|
||||
ComponentName = Literal\
|
||||
[
|
||||
'age_modifier_direction_slider',
|
||||
'age_modifier_model_dropdown',
|
||||
'benchmark_cycles_slider',
|
||||
'benchmark_runs_checkbox_group',
|
||||
'benchmark_cycle_count_slider',
|
||||
'benchmark_resolutions_checkbox_group',
|
||||
'deep_swapper_model_dropdown',
|
||||
'deep_swapper_morph_slider',
|
||||
'expression_restorer_factor_slider',
|
||||
@@ -38,13 +36,14 @@ ComponentName = Literal\
|
||||
'face_enhancer_weight_slider',
|
||||
'face_landmarker_model_dropdown',
|
||||
'face_landmarker_score_slider',
|
||||
'face_mask_types_checkbox_group',
|
||||
'face_mask_areas_checkbox_group',
|
||||
'face_mask_regions_checkbox_group',
|
||||
'face_mask_blur_slider',
|
||||
'face_mask_padding_bottom_slider',
|
||||
'face_mask_padding_left_slider',
|
||||
'face_mask_padding_right_slider',
|
||||
'face_mask_padding_top_slider',
|
||||
'face_mask_regions_checkbox_group',
|
||||
'face_mask_types_checkbox_group',
|
||||
'face_selector_age_range_slider',
|
||||
'face_selector_gender_dropdown',
|
||||
'face_selector_mode_dropdown',
|
||||
@@ -61,6 +60,7 @@ ComponentName = Literal\
|
||||
'frame_enhancer_model_dropdown',
|
||||
'job_list_job_status_checkbox_group',
|
||||
'lip_syncer_model_dropdown',
|
||||
'lip_syncer_weight_slider',
|
||||
'output_image',
|
||||
'output_video',
|
||||
'output_video_fps_slider',
|
||||
@@ -78,6 +78,8 @@ ComponentName = Literal\
|
||||
'webcam_mode_radio',
|
||||
'webcam_resolution_dropdown'
|
||||
]
|
||||
Component : TypeAlias = Any
|
||||
ComponentOptions : TypeAlias = Dict[str, Any]
|
||||
|
||||
JobManagerAction = Literal['job-create', 'job-submit', 'job-delete', 'job-add-step', 'job-remix-step', 'job-insert-step', 'job-remove-step']
|
||||
JobRunnerAction = Literal['job-run', 'job-run-all', 'job-retry', 'job-retry-all']
|
||||
|
||||
19
facefusion/video_manager.py
Normal file
19
facefusion/video_manager.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import cv2
|
||||
|
||||
from facefusion.types import VideoPoolSet
|
||||
|
||||
VIDEO_POOL_SET : VideoPoolSet = {}
|
||||
|
||||
|
||||
def get_video_capture(video_path : str) -> cv2.VideoCapture:
|
||||
if video_path not in VIDEO_POOL_SET:
|
||||
VIDEO_POOL_SET[video_path] = cv2.VideoCapture(video_path)
|
||||
|
||||
return VIDEO_POOL_SET.get(video_path)
|
||||
|
||||
|
||||
def clear_video_pool() -> None:
|
||||
for video_capture in VIDEO_POOL_SET.values():
|
||||
video_capture.release()
|
||||
|
||||
VIDEO_POOL_SET.clear()
|
||||
@@ -9,7 +9,9 @@ from cv2.typing import Size
|
||||
import facefusion.choices
|
||||
from facefusion.common_helper import is_windows
|
||||
from facefusion.filesystem import get_file_extension, is_image, is_video
|
||||
from facefusion.thread_helper import thread_semaphore
|
||||
from facefusion.types import Duration, Fps, Orientation, Resolution, VisionFrame
|
||||
from facefusion.video_manager import get_video_capture
|
||||
|
||||
|
||||
@lru_cache()
|
||||
@@ -81,42 +83,50 @@ def create_image_resolutions(resolution : Resolution) -> List[str]:
|
||||
|
||||
def read_video_frame(video_path : str, frame_number : int = 0) -> Optional[VisionFrame]:
|
||||
if is_video(video_path):
|
||||
video_capture = cv2.VideoCapture(video_path)
|
||||
video_capture = get_video_capture(video_path)
|
||||
|
||||
if video_capture.isOpened():
|
||||
frame_total = video_capture.get(cv2.CAP_PROP_FRAME_COUNT)
|
||||
|
||||
with thread_semaphore():
|
||||
video_capture.set(cv2.CAP_PROP_POS_FRAMES, min(frame_total, frame_number - 1))
|
||||
has_vision_frame, vision_frame = video_capture.read()
|
||||
video_capture.release()
|
||||
|
||||
if has_vision_frame:
|
||||
return vision_frame
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def count_video_frame_total(video_path : str) -> int:
|
||||
if is_video(video_path):
|
||||
video_capture = cv2.VideoCapture(video_path)
|
||||
video_capture = get_video_capture(video_path)
|
||||
|
||||
if video_capture.isOpened():
|
||||
with thread_semaphore():
|
||||
video_frame_total = int(video_capture.get(cv2.CAP_PROP_FRAME_COUNT))
|
||||
video_capture.release()
|
||||
return video_frame_total
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def predict_video_frame_total(video_path : str, fps : Fps, trim_frame_start : int, trim_frame_end : int) -> int:
|
||||
if is_video(video_path):
|
||||
target_video_fps = detect_video_fps(video_path)
|
||||
extract_frame_total = count_trim_frame_total(video_path, trim_frame_start, trim_frame_end) * fps / target_video_fps
|
||||
video_fps = detect_video_fps(video_path)
|
||||
extract_frame_total = count_trim_frame_total(video_path, trim_frame_start, trim_frame_end) * fps / video_fps
|
||||
return math.floor(extract_frame_total)
|
||||
return 0
|
||||
|
||||
|
||||
def detect_video_fps(video_path : str) -> Optional[float]:
|
||||
if is_video(video_path):
|
||||
video_capture = cv2.VideoCapture(video_path)
|
||||
video_capture = get_video_capture(video_path)
|
||||
|
||||
if video_capture.isOpened():
|
||||
with thread_semaphore():
|
||||
video_fps = video_capture.get(cv2.CAP_PROP_FPS)
|
||||
video_capture.release()
|
||||
return video_fps
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@@ -163,12 +173,14 @@ def restrict_trim_frame(video_path : str, trim_frame_start : Optional[int], trim
|
||||
|
||||
def detect_video_resolution(video_path : str) -> Optional[Resolution]:
|
||||
if is_video(video_path):
|
||||
video_capture = cv2.VideoCapture(video_path)
|
||||
video_capture = get_video_capture(video_path)
|
||||
|
||||
if video_capture.isOpened():
|
||||
with thread_semaphore():
|
||||
width = video_capture.get(cv2.CAP_PROP_FRAME_WIDTH)
|
||||
height = video_capture.get(cv2.CAP_PROP_FRAME_HEIGHT)
|
||||
video_capture.release()
|
||||
return int(width), int(height)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ WORDING : Dict[str, Any] =\
|
||||
{
|
||||
'conda_not_activated': 'Conda is not activated',
|
||||
'python_not_supported': 'Python version is not supported, upgrade to {version} or higher',
|
||||
'curl_not_installed': 'CURL is not installed',
|
||||
'curl_not_installed': 'cURL is not installed',
|
||||
'ffmpeg_not_installed': 'FFMpeg is not installed',
|
||||
'creating_temp': 'Creating temporary resources',
|
||||
'extracting_frames': 'Extracting frames with a resolution of {resolution} and {fps} frames per second',
|
||||
@@ -129,9 +129,10 @@ WORDING : Dict[str, Any] =\
|
||||
'face_occluder_model': 'choose the model responsible for the occlusion mask',
|
||||
'face_parser_model': 'choose the model responsible for the region mask',
|
||||
'face_mask_types': 'mix and match different face mask types (choices: {choices})',
|
||||
'face_mask_areas': 'choose the items used for the area mask (choices: {choices})',
|
||||
'face_mask_regions': 'choose the items used for the region mask (choices: {choices})',
|
||||
'face_mask_blur': 'specify the degree of blur applied to the box mask',
|
||||
'face_mask_padding': 'apply top, right, bottom and left padding to the box mask',
|
||||
'face_mask_regions': 'choose the facial features used for the region mask (choices: {choices})',
|
||||
# frame extraction
|
||||
'trim_frame_start': 'specify the starting frame of the target video',
|
||||
'trim_frame_end': 'specify the ending frame of the target video',
|
||||
@@ -183,18 +184,22 @@ WORDING : Dict[str, Any] =\
|
||||
'frame_enhancer_model': 'choose the model responsible for enhancing the frame',
|
||||
'frame_enhancer_blend': 'blend the enhanced into the previous frame',
|
||||
'lip_syncer_model': 'choose the model responsible for syncing the lips',
|
||||
'lip_syncer_weight': 'specify the degree of weight applied to the lips',
|
||||
# uis
|
||||
'open_browser': 'open the browser once the program is ready',
|
||||
'ui_layouts': 'launch a single or multiple UI layouts (choices: {choices}, ...)',
|
||||
'ui_workflow': 'choose the ui workflow',
|
||||
# download
|
||||
'download_providers': 'download using different providers (choices: {choices}, ...)',
|
||||
'download_scope': 'specify the download scope',
|
||||
# benchmark
|
||||
'benchmark_resolutions': 'choose the resolutions for the benchmarks (choices: {choices}, ...)',
|
||||
'benchmark_cycle_count': 'specify the amount of cycles per benchmark',
|
||||
# execution
|
||||
'execution_device_id': 'specify the device used for processing',
|
||||
'execution_providers': 'inference using different providers (choices: {choices}, ...)',
|
||||
'execution_thread_count': 'specify the amount of parallel threads while processing',
|
||||
'execution_queue_count': 'specify the amount of frames each thread is processing',
|
||||
# download
|
||||
'download_providers': 'download using different providers (choices: {choices}, ...)',
|
||||
'download_scope': 'specify the download scope',
|
||||
# memory
|
||||
'video_memory_strategy': 'balance fast processing and low VRAM usage',
|
||||
'system_memory_limit': 'limit the available RAM that can be used while processing',
|
||||
@@ -206,6 +211,7 @@ WORDING : Dict[str, Any] =\
|
||||
'headless_run': 'run the program in headless mode',
|
||||
'batch_run': 'run the program in batch mode',
|
||||
'force_download': 'force automate downloads and exit',
|
||||
'benchmark': 'benchmark the program',
|
||||
# jobs
|
||||
'job_id': 'specify the job id',
|
||||
'job_status': 'specify the job status',
|
||||
@@ -238,8 +244,8 @@ WORDING : Dict[str, Any] =\
|
||||
'age_modifier_direction_slider': 'AGE MODIFIER DIRECTION',
|
||||
'age_modifier_model_dropdown': 'AGE MODIFIER MODEL',
|
||||
'apply_button': 'APPLY',
|
||||
'benchmark_cycles_slider': 'BENCHMARK CYCLES',
|
||||
'benchmark_runs_checkbox_group': 'BENCHMARK RUNS',
|
||||
'benchmark_cycle_count_slider': 'BENCHMARK CYCLE COUNT',
|
||||
'benchmark_resolutions_checkbox_group': 'BENCHMARK RESOLUTIONS',
|
||||
'clear_button': 'CLEAR',
|
||||
'common_options_checkbox_group': 'OPTIONS',
|
||||
'download_providers_checkbox_group': 'DOWNLOAD PROVIDERS',
|
||||
@@ -280,6 +286,7 @@ WORDING : Dict[str, Any] =\
|
||||
'face_mask_padding_left_slider': 'FACE MASK PADDING LEFT',
|
||||
'face_mask_padding_right_slider': 'FACE MASK PADDING RIGHT',
|
||||
'face_mask_padding_top_slider': 'FACE MASK PADDING TOP',
|
||||
'face_mask_areas_checkbox_group': 'FACE MASK AREAS',
|
||||
'face_mask_regions_checkbox_group': 'FACE MASK REGIONS',
|
||||
'face_mask_types_checkbox_group': 'FACE MASK TYPES',
|
||||
'face_selector_age_range_slider': 'FACE SELECTOR AGE',
|
||||
@@ -303,6 +310,7 @@ WORDING : Dict[str, Any] =\
|
||||
'job_runner_job_action_dropdown': 'JOB ACTION',
|
||||
'job_runner_job_id_dropdown': 'JOB ID',
|
||||
'lip_syncer_model_dropdown': 'LIP SYNCER MODEL',
|
||||
'lip_syncer_weight_slider': 'LIP SYNCER WEIGHT',
|
||||
'log_level_dropdown': 'LOG LEVEL',
|
||||
'output_audio_encoder_dropdown': 'OUTPUT AUDIO ENCODER',
|
||||
'output_audio_quality_slider': 'OUTPUT AUDIO QUALITY',
|
||||
|
||||
@@ -2,7 +2,7 @@ gradio==5.25.2
|
||||
gradio-rangeslider==0.0.8
|
||||
numpy==2.2.4
|
||||
onnx==1.17.0
|
||||
onnxruntime==1.21.1
|
||||
onnxruntime==1.22.0
|
||||
opencv-python==4.11.0.86
|
||||
psutil==7.0.0
|
||||
tqdm==4.67.1
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
|
||||
import facefusion.ffmpeg
|
||||
from facefusion import process_manager, state_manager
|
||||
from facefusion.download import conditional_download
|
||||
from facefusion.ffmpeg import concat_video, extract_frames, get_available_encoder_set, read_audio_buffer, replace_audio, restore_audio
|
||||
from facefusion.ffmpeg import concat_video, extract_frames, merge_video, read_audio_buffer, replace_audio, restore_audio
|
||||
from facefusion.filesystem import copy_file
|
||||
from facefusion.temp_helper import clear_temp_directory, create_temp_directory, get_temp_file_path, resolve_temp_frame_paths
|
||||
from facefusion.types import EncoderSet
|
||||
from .helper import get_test_example_file, get_test_examples_directory, get_test_output_file, prepare_test_output_directory
|
||||
|
||||
|
||||
@@ -24,13 +27,19 @@ def before_all() -> None:
|
||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vf', 'fps=25', get_test_example_file('target-240p-25fps.mp4') ])
|
||||
subprocess.run([ 'ffmpeg', '-i', get_test_example_file('target-240p.mp4'), '-vf', 'fps=30', get_test_example_file('target-240p-30fps.mp4') ])
|
||||
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('source.mp3'), '-i', get_test_example_file('target-240p.mp4'), '-ar', '16000', get_test_example_file('target-240p-16khz.mp4') ])
|
||||
|
||||
for output_video_format in [ 'avi', 'm4v', 'mkv', 'mov', 'mp4', 'webm' ]:
|
||||
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) ])
|
||||
|
||||
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_frame_format', 'png')
|
||||
state_manager.init_item('output_audio_encoder', 'aac')
|
||||
state_manager.init_item('output_audio_quality', 80)
|
||||
state_manager.init_item('output_audio_quality', 100)
|
||||
state_manager.init_item('output_audio_volume', 100)
|
||||
state_manager.init_item('output_video_encoder', 'libx264')
|
||||
state_manager.init_item('output_video_quality', 100)
|
||||
state_manager.init_item('output_video_preset', 'ultrafast')
|
||||
|
||||
|
||||
@pytest.fixture(scope = 'function', autouse = True)
|
||||
@@ -38,7 +47,16 @@ def before_each() -> None:
|
||||
prepare_test_output_directory()
|
||||
|
||||
|
||||
@pytest.mark.skip()
|
||||
def get_available_encoder_set() -> EncoderSet:
|
||||
if os.getenv('CI'):
|
||||
return\
|
||||
{
|
||||
'audio': [ 'aac' ],
|
||||
'video': [ 'libx264' ]
|
||||
}
|
||||
return facefusion.ffmpeg.get_available_encoder_set()
|
||||
|
||||
|
||||
def test_get_available_encoder_set() -> None:
|
||||
available_encoder_set = get_available_encoder_set()
|
||||
|
||||
@@ -47,7 +65,7 @@ def test_get_available_encoder_set() -> None:
|
||||
|
||||
|
||||
def test_extract_frames() -> None:
|
||||
extract_set =\
|
||||
test_set =\
|
||||
[
|
||||
(get_test_example_file('target-240p-25fps.mp4'), 0, 270, 324),
|
||||
(get_test_example_file('target-240p-25fps.mp4'), 224, 270, 55),
|
||||
@@ -63,7 +81,7 @@ def test_extract_frames() -> None:
|
||||
(get_test_example_file('target-240p-60fps.mp4'), 0, 100, 50)
|
||||
]
|
||||
|
||||
for target_path, trim_frame_start, trim_frame_end, frame_total in extract_set:
|
||||
for target_path, trim_frame_start, trim_frame_end, frame_total in test_set:
|
||||
create_temp_directory(target_path)
|
||||
|
||||
assert extract_frames(target_path, '452x240', 30.0, trim_frame_start, trim_frame_end) is True
|
||||
@@ -72,12 +90,37 @@ def test_extract_frames() -> None:
|
||||
clear_temp_directory(target_path)
|
||||
|
||||
|
||||
def test_merge_video() -> None:
|
||||
target_paths =\
|
||||
[
|
||||
get_test_example_file('target-240p-16khz.avi'),
|
||||
get_test_example_file('target-240p-16khz.m4v'),
|
||||
get_test_example_file('target-240p-16khz.mkv'),
|
||||
get_test_example_file('target-240p-16khz.mp4'),
|
||||
get_test_example_file('target-240p-16khz.mov'),
|
||||
get_test_example_file('target-240p-16khz.webm')
|
||||
]
|
||||
output_video_encoders = get_available_encoder_set().get('video')
|
||||
|
||||
for target_path in target_paths:
|
||||
for output_video_encoder in output_video_encoders:
|
||||
state_manager.init_item('output_video_encoder', output_video_encoder)
|
||||
create_temp_directory(target_path)
|
||||
extract_frames(target_path, '452x240', 25.0, 0, 1)
|
||||
|
||||
assert merge_video(target_path, 25.0, '452x240', 25.0, 0, 1) is True
|
||||
|
||||
clear_temp_directory(target_path)
|
||||
|
||||
state_manager.init_item('output_video_encoder', 'libx264')
|
||||
|
||||
|
||||
def test_concat_video() -> None:
|
||||
output_path = get_test_output_file('test-concat-video.mp4')
|
||||
temp_output_paths =\
|
||||
[
|
||||
get_test_example_file('target-240p.mp4'),
|
||||
get_test_example_file('target-240p.mp4')
|
||||
get_test_example_file('target-240p-16khz.mp4'),
|
||||
get_test_example_file('target-240p-16khz.mp4')
|
||||
]
|
||||
|
||||
assert concat_video(output_path, temp_output_paths) is True
|
||||
@@ -90,30 +133,55 @@ def test_read_audio_buffer() -> None:
|
||||
|
||||
|
||||
def test_restore_audio() -> None:
|
||||
target_paths =\
|
||||
test_set =\
|
||||
[
|
||||
get_test_example_file('target-240p-16khz.mp4'),
|
||||
get_test_example_file('target-240p-48khz.mp4')
|
||||
(get_test_example_file('target-240p-16khz.avi'), get_test_output_file('target-240p-16khz.avi')),
|
||||
(get_test_example_file('target-240p-16khz.m4v'), get_test_output_file('target-240p-16khz.m4v')),
|
||||
(get_test_example_file('target-240p-16khz.mkv'), get_test_output_file('target-240p-16khz.mkv')),
|
||||
(get_test_example_file('target-240p-16khz.mov'), get_test_output_file('target-240p-16khz.mov')),
|
||||
(get_test_example_file('target-240p-16khz.mp4'), get_test_output_file('target-240p-16khz.mp4')),
|
||||
(get_test_example_file('target-240p-48khz.mp4'), get_test_output_file('target-240p-48khz.mp4')),
|
||||
(get_test_example_file('target-240p-16khz.webm'), get_test_output_file('target-240p-16khz.webm'))
|
||||
]
|
||||
output_path = get_test_output_file('test-restore-audio.mp4')
|
||||
output_audio_encoders = get_available_encoder_set().get('audio')
|
||||
|
||||
for target_path in target_paths:
|
||||
for target_path, output_path in test_set:
|
||||
create_temp_directory(target_path)
|
||||
|
||||
for output_audio_encoder in output_audio_encoders:
|
||||
state_manager.init_item('output_audio_encoder', output_audio_encoder)
|
||||
copy_file(target_path, get_temp_file_path(target_path))
|
||||
|
||||
assert restore_audio(target_path, output_path, 0, 270) is True
|
||||
|
||||
clear_temp_directory(target_path)
|
||||
|
||||
state_manager.init_item('output_audio_encoder', 'aac')
|
||||
|
||||
|
||||
def test_replace_audio() -> None:
|
||||
target_path = get_test_example_file('target-240p.mp4')
|
||||
output_path = get_test_output_file('test-replace-audio.mp4')
|
||||
test_set =\
|
||||
[
|
||||
(get_test_example_file('target-240p-16khz.avi'), get_test_output_file('target-240p-16khz.avi')),
|
||||
(get_test_example_file('target-240p-16khz.m4v'), get_test_output_file('target-240p-16khz.m4v')),
|
||||
(get_test_example_file('target-240p-16khz.mkv'), get_test_output_file('target-240p-16khz.mkv')),
|
||||
(get_test_example_file('target-240p-16khz.mov'), get_test_output_file('target-240p-16khz.mov')),
|
||||
(get_test_example_file('target-240p-16khz.mp4'), get_test_output_file('target-240p-16khz.mp4')),
|
||||
(get_test_example_file('target-240p-48khz.mp4'), get_test_output_file('target-240p-48khz.mp4')),
|
||||
(get_test_example_file('target-240p-16khz.webm'), get_test_output_file('target-240p-16khz.webm'))
|
||||
]
|
||||
output_audio_encoders = get_available_encoder_set().get('audio')
|
||||
|
||||
for target_path, output_path in test_set:
|
||||
create_temp_directory(target_path)
|
||||
|
||||
for output_audio_encoder in output_audio_encoders:
|
||||
state_manager.init_item('output_audio_encoder', output_audio_encoder)
|
||||
copy_file(target_path, get_temp_file_path(target_path))
|
||||
|
||||
assert replace_audio(target_path, get_test_example_file('source.mp3'), output_path) is True
|
||||
assert replace_audio(target_path, get_test_example_file('source.wav'), output_path) is True
|
||||
|
||||
clear_temp_directory(target_path)
|
||||
|
||||
state_manager.init_item('output_audio_encoder', 'aac')
|
||||
|
||||
@@ -37,8 +37,8 @@ def test_set_audio_quality() -> None:
|
||||
assert set_audio_quality('libmp3lame', 50) == [ '-q:a', '4' ]
|
||||
assert set_audio_quality('libmp3lame', 100) == [ '-q:a', '0' ]
|
||||
assert set_audio_quality('libopus', 0) == [ '-b:a', '64k' ]
|
||||
assert set_audio_quality('libopus', 50) == [ '-b:a', '192k' ]
|
||||
assert set_audio_quality('libopus', 100) == [ '-b:a', '320k' ]
|
||||
assert set_audio_quality('libopus', 50) == [ '-b:a', '160k' ]
|
||||
assert set_audio_quality('libopus', 100) == [ '-b:a', '256k' ]
|
||||
assert set_audio_quality('libvorbis', 0) == [ '-q:a', '-1.0' ]
|
||||
assert set_audio_quality('libvorbis', 50) == [ '-q:a', '4.5' ]
|
||||
assert set_audio_quality('libvorbis', 100) == [ '-q:a', '10.0' ]
|
||||
|
||||
@@ -16,17 +16,17 @@ def before_all() -> None:
|
||||
|
||||
|
||||
def test_get_inference_pool() -> None:
|
||||
model_names = [ 'yolo_nsfw' ]
|
||||
model_source_set = content_analyser.get_model_options().get('sources')
|
||||
model_names = [ 'nsfw_1', 'nsfw_2', 'nsfw_3' ]
|
||||
_, model_source_set = content_analyser.collect_model_downloads()
|
||||
|
||||
with patch('facefusion.inference_manager.detect_app_context', return_value = 'cli'):
|
||||
get_inference_pool('facefusion.content_analyser', model_names, model_source_set)
|
||||
|
||||
assert isinstance(INFERENCE_POOL_SET.get('cli').get('facefusion.content_analyser.yolo_nsfw.0.cpu').get('content_analyser'), InferenceSession)
|
||||
assert isinstance(INFERENCE_POOL_SET.get('cli').get('facefusion.content_analyser.nsfw_1.nsfw_2.nsfw_3.0.cpu').get('nsfw_1'), InferenceSession)
|
||||
|
||||
with patch('facefusion.inference_manager.detect_app_context', return_value = 'ui'):
|
||||
get_inference_pool('facefusion.content_analyser', model_names, model_source_set)
|
||||
|
||||
assert isinstance(INFERENCE_POOL_SET.get('ui').get('facefusion.content_analyser.yolo_nsfw.0.cpu').get('content_analyser'), 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.yolo_nsfw.0.cpu').get('content_analyser') == INFERENCE_POOL_SET.get('ui').get('facefusion.content_analyser.yolo_nsfw.0.cpu').get('content_analyser')
|
||||
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')
|
||||
|
||||
@@ -3,7 +3,7 @@ from time import sleep
|
||||
import pytest
|
||||
|
||||
from facefusion.jobs.job_helper import get_step_output_path
|
||||
from facefusion.jobs.job_manager import add_step, clear_jobs, count_step_total, create_job, delete_job, delete_jobs, find_job_ids, get_steps, init_jobs, insert_step, move_job_file, remix_step, remove_step, set_step_status, set_steps_status, submit_job, submit_jobs
|
||||
from facefusion.jobs.job_manager import add_step, clear_jobs, count_step_total, create_job, delete_job, delete_jobs, find_job_ids, find_jobs, get_steps, init_jobs, insert_step, move_job_file, remix_step, remove_step, set_step_status, set_steps_status, submit_job, submit_jobs
|
||||
from .helper import get_test_jobs_directory
|
||||
|
||||
|
||||
@@ -99,9 +99,19 @@ def test_delete_jobs() -> None:
|
||||
assert delete_jobs(halt_on_error) is True
|
||||
|
||||
|
||||
@pytest.mark.skip()
|
||||
def test_find_jobs() -> None:
|
||||
pass
|
||||
create_job('job-test-find-jobs-1')
|
||||
sleep(0.5)
|
||||
create_job('job-test-find-jobs-2')
|
||||
|
||||
assert 'job-test-find-jobs-1' in find_jobs('drafted')
|
||||
assert 'job-test-find-jobs-2' in find_jobs('drafted')
|
||||
assert not find_jobs('queued')
|
||||
|
||||
move_job_file('job-test-find-jobs-1', 'queued')
|
||||
|
||||
assert 'job-test-find-jobs-2' in find_jobs('drafted')
|
||||
assert 'job-test-find-jobs-1' in find_jobs('queued')
|
||||
|
||||
|
||||
def test_find_job_ids() -> None:
|
||||
|
||||
@@ -4,8 +4,8 @@ import pytest
|
||||
|
||||
from facefusion.download import conditional_download
|
||||
from facefusion.filesystem import copy_file
|
||||
from facefusion.jobs.job_manager import add_step, clear_jobs, create_job, init_jobs, submit_job, submit_jobs
|
||||
from facefusion.jobs.job_runner import collect_output_set, finalize_steps, run_job, run_jobs, run_steps
|
||||
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_runner import collect_output_set, finalize_steps, retry_job, retry_jobs, run_job, run_jobs, run_steps
|
||||
from facefusion.types import Args
|
||||
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
|
||||
|
||||
@@ -48,7 +48,7 @@ def test_run_job() -> None:
|
||||
{
|
||||
'source_path': get_test_example_file('source.jpg'),
|
||||
'target_path': get_test_example_file('target-240p.jpg'),
|
||||
'output_path': get_test_output_file('output-1.jpg')
|
||||
'output_path': get_test_output_file('output-3.jpg')
|
||||
}
|
||||
|
||||
assert run_job('job-invalid', process_step) is False
|
||||
@@ -83,7 +83,7 @@ def test_run_jobs() -> None:
|
||||
{
|
||||
'source_path': get_test_example_file('source.jpg'),
|
||||
'target_path': get_test_example_file('target-240p.jpg'),
|
||||
'output_path': get_test_output_file('output-1.jpg')
|
||||
'output_path': get_test_output_file('output-3.jpg')
|
||||
}
|
||||
halt_on_error = True
|
||||
|
||||
@@ -103,14 +103,63 @@ def test_run_jobs() -> None:
|
||||
assert run_jobs(process_step, halt_on_error) is True
|
||||
|
||||
|
||||
@pytest.mark.skip()
|
||||
def test_retry_job() -> None:
|
||||
pass
|
||||
args_1 =\
|
||||
{
|
||||
'source_path': get_test_example_file('source.jpg'),
|
||||
'target_path': get_test_example_file('target-240p.mp4'),
|
||||
'output_path': get_test_output_file('output-1.mp4')
|
||||
}
|
||||
|
||||
assert retry_job('job-invalid', process_step) is False
|
||||
|
||||
create_job('job-test-retry-job')
|
||||
add_step('job-test-retry-job', args_1)
|
||||
submit_job('job-test-retry-job')
|
||||
|
||||
assert retry_job('job-test-retry-job', process_step) is False
|
||||
|
||||
move_job_file('job-test-retry-job', 'failed')
|
||||
|
||||
assert retry_job('job-test-retry-job', process_step) is True
|
||||
|
||||
|
||||
@pytest.mark.skip()
|
||||
def test_retry_jobs() -> None:
|
||||
pass
|
||||
args_1 =\
|
||||
{
|
||||
'source_path': get_test_example_file('source.jpg'),
|
||||
'target_path': get_test_example_file('target-240p.mp4'),
|
||||
'output_path': get_test_output_file('output-1.mp4')
|
||||
}
|
||||
args_2 =\
|
||||
{
|
||||
'source_path': get_test_example_file('source.jpg'),
|
||||
'target_path': get_test_example_file('target-240p.mp4'),
|
||||
'output_path': get_test_output_file('output-2.mp4')
|
||||
}
|
||||
args_3 =\
|
||||
{
|
||||
'source_path': get_test_example_file('source.jpg'),
|
||||
'target_path': get_test_example_file('target-240p.jpg'),
|
||||
'output_path': get_test_output_file('output-3.jpg')
|
||||
}
|
||||
halt_on_error = True
|
||||
|
||||
assert retry_jobs(process_step, halt_on_error) is False
|
||||
|
||||
create_job('job-test-retry-jobs-1')
|
||||
create_job('job-test-retry-jobs-2')
|
||||
add_step('job-test-retry-jobs-1', args_1)
|
||||
add_step('job-test-retry-jobs-1', args_1)
|
||||
add_step('job-test-retry-jobs-2', args_2)
|
||||
add_step('job-test-retry-jobs-3', args_3)
|
||||
|
||||
assert retry_jobs(process_step, halt_on_error) is False
|
||||
|
||||
move_job_file('job-test-retry-jobs-1', 'failed')
|
||||
move_job_file('job-test-retry-jobs-2', 'failed')
|
||||
|
||||
assert retry_jobs(process_step, halt_on_error) is True
|
||||
|
||||
|
||||
def test_run_steps() -> None:
|
||||
@@ -130,7 +179,7 @@ def test_run_steps() -> None:
|
||||
{
|
||||
'source_path': get_test_example_file('source.jpg'),
|
||||
'target_path': get_test_example_file('target-240p.jpg'),
|
||||
'output_path': get_test_output_file('output-1.jpg')
|
||||
'output_path': get_test_output_file('output-3.jpg')
|
||||
}
|
||||
|
||||
assert run_steps('job-invalid', process_step) is False
|
||||
@@ -161,7 +210,7 @@ def test_finalize_steps() -> None:
|
||||
{
|
||||
'source_path': get_test_example_file('source.jpg'),
|
||||
'target_path': get_test_example_file('target-240p.jpg'),
|
||||
'output_path': get_test_output_file('output-1.jpg')
|
||||
'output_path': get_test_output_file('output-3.jpg')
|
||||
}
|
||||
|
||||
create_job('job-test-finalize-steps')
|
||||
@@ -173,12 +222,12 @@ def test_finalize_steps() -> None:
|
||||
copy_file(args_1.get('target_path'), get_test_output_file('output-1-job-test-finalize-steps-0.mp4'))
|
||||
copy_file(args_1.get('target_path'), get_test_output_file('output-1-job-test-finalize-steps-1.mp4'))
|
||||
copy_file(args_2.get('target_path'), get_test_output_file('output-2-job-test-finalize-steps-2.mp4'))
|
||||
copy_file(args_3.get('target_path'), get_test_output_file('output-1-job-test-finalize-steps-3.jpg'))
|
||||
copy_file(args_3.get('target_path'), get_test_output_file('output-3-job-test-finalize-steps-3.jpg'))
|
||||
|
||||
assert finalize_steps('job-test-finalize-steps') is True
|
||||
assert is_test_output_file('output-1.mp4') is True
|
||||
assert is_test_output_file('output-2.mp4') is True
|
||||
assert is_test_output_file('output-1.jpg') is True
|
||||
assert is_test_output_file('output-3.jpg') is True
|
||||
|
||||
|
||||
def test_collect_output_set() -> None:
|
||||
@@ -198,7 +247,7 @@ def test_collect_output_set() -> None:
|
||||
{
|
||||
'source_path': get_test_example_file('source.jpg'),
|
||||
'target_path': get_test_example_file('target-240p.jpg'),
|
||||
'output_path': get_test_output_file('output-1.jpg')
|
||||
'output_path': get_test_output_file('output-3.jpg')
|
||||
}
|
||||
|
||||
create_job('job-test-collect-output-set')
|
||||
@@ -218,9 +267,9 @@ def test_collect_output_set() -> None:
|
||||
[
|
||||
get_test_output_file('output-2-job-test-collect-output-set-2.mp4')
|
||||
],
|
||||
get_test_output_file('output-1.jpg'):
|
||||
get_test_output_file('output-3.jpg'):
|
||||
[
|
||||
get_test_output_file('output-1-job-test-collect-output-set-3.jpg')
|
||||
get_test_output_file('output-3-job-test-collect-output-set-3.jpg')
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
35
tests/test_state_manager.py
Normal file
35
tests/test_state_manager.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from typing import Union
|
||||
|
||||
import pytest
|
||||
|
||||
from facefusion.processors.types import ProcessorState
|
||||
from facefusion.state_manager import STATE_SET, get_item, init_item, set_item
|
||||
from facefusion.types import AppContext, State
|
||||
|
||||
|
||||
def get_state(app_context : AppContext) -> Union[State, ProcessorState]:
|
||||
return STATE_SET.get(app_context)
|
||||
|
||||
|
||||
def clear_state(app_context : AppContext) -> None:
|
||||
STATE_SET[app_context] = {} #type:ignore[typeddict-item]
|
||||
|
||||
|
||||
@pytest.fixture(scope = 'function', autouse = True)
|
||||
def before_each() -> None:
|
||||
clear_state('cli')
|
||||
clear_state('ui')
|
||||
|
||||
|
||||
def test_init_item() -> None:
|
||||
init_item('video_memory_strategy', 'tolerant')
|
||||
|
||||
assert get_state('cli').get('video_memory_strategy') == 'tolerant'
|
||||
assert get_state('ui').get('video_memory_strategy') == 'tolerant'
|
||||
|
||||
|
||||
def test_get_item_and_set_item() -> None:
|
||||
set_item('video_memory_strategy', 'tolerant')
|
||||
|
||||
assert get_item('video_memory_strategy') == 'tolerant'
|
||||
assert get_state('ui').get('video_memory_strategy') is None
|
||||
Reference in New Issue
Block a user