* Add real_hatgan_x4 model

* Mark it as NEXT

* Force download to be executed and exit

* Fix frame per second interpolation

* 5 to 68 landmark (#456)

* changes

* changes

* Adjust model url

* Cleanup 5 to 68 landmark convertion

* Move everything to face analyser

* Introduce matrix only face helper

* Revert facefusion.ini

* Adjust limit due false positive analysis

* changes (#457)

* Use pixel format yuv422p to merge video

* Fix some code

* Minor cleanup

* Add gpen_bfr_1024 and gpen_bfr_2048

* Revert it back to yuv420p due compatibility issues

* Add debug back to ffmpeg

* Add debug back to ffmpeg

* Migrate to conda (#461)

* Migrate from venv to conda

* Migrate from venv to conda

* Message when conda is not activated

* Use release for every slider (#463)

* Use release event handler for every slider

* Move more sliders to release handler

* Move more sliders to release handler

* Add get_ui_components() to simplify code

* Revert some changes on frame slider

* Add the first iteration of a frame colorizer

* Support for the DDColor model

* Improve model file handling

* Improve model file handling part2

* Remove deoldify

* Remove deoldify

* Voice separator (#468)

* changes

* changes

* changes

* changes

* changes

* changes

* Rename audio extractor to voice extractor

* Cosmetic changes

* Cosmetic changes

* Fix fps lowering and boosting

* Fix fps lowering and boosting

* Fix fps lowering and boosting

* Some refactoring for audio.py and some astype() here and there (#470)

* Some refactoring for audio.py and some astype() here and there

* Fix lint

* Spacing

* Add mp3 to benchmark suite for lip syncer testing

* Improve naming

* Adjust chunk size

* Use higher quality

* Revert "Use higher quality"

This reverts commit d32f287572.

* Improve naming in ffmpeg.py

* Simplify code

* Better fps calculation

* Fix naming here and there

* Add back real esrgan x2

* Remove trailing comma

* Update wording and README

* Use semaphore to prevent frame colorizer memory issues

* Revert "Remove deoldify"

This reverts commit bd8034cbc7.

* Remove unused type from frame colorizer

* Adjust naming

* Add missing clear of model initializer

* Change nvenc preset mappping to support old FFMPEG 4

* Update onnxruntime to 1.17.1

* Fix lint

* Prepare 2.5.0

* Fix Gradio overrides

* Add Deoldify Artistic back

* Feat/audio refactoring (#476)

* Improve audio naming and variables

* Improve audio naming and variables

* Refactor voice extractor like crazy

* Refactor voice extractor like crazy

* Remove spaces

* Update the usage

---------

Co-authored-by: Harisreedhar <46858047+harisreedhar@users.noreply.github.com>
This commit is contained in:
Henry Ruhs 2024-04-09 15:40:55 +02:00 committed by GitHub
parent 6e67d7bff6
commit 4ccf4c24c7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 1007 additions and 405 deletions

BIN
.github/preview.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -30,6 +30,6 @@ jobs:
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
python-version: '3.10' python-version: '3.10'
- run: python install.py --onnxruntime default --skip-venv - run: python install.py --onnxruntime default --skip-conda
- run: pip install pytest - run: pip install pytest
- run: pytest - run: pytest

View File

@ -37,6 +37,7 @@ options:
-v, --version show program's version number and exit -v, --version show program's version number and exit
misc: misc:
--force-download force automate downloads and exit
--skip-download omit automate downloads and remote lookups --skip-download omit automate downloads and remote lookups
--headless run the program without a user interface --headless run the program without a user interface
--log-level {error,warn,info,debug} adjust the message severity displayed in the terminal --log-level {error,warn,info,debug} adjust the message severity displayed in the terminal
@ -47,11 +48,11 @@ execution:
--execution-queue-count [1-32] specify the amount of frames each thread is processing --execution-queue-count [1-32] specify the amount of frames each thread is processing
memory: memory:
--video-memory-strategy {strict,moderate,tolerant} balance fast frame processing and low vram usage --video-memory-strategy {strict,moderate,tolerant} balance fast frame processing and low VRAM usage
--system-memory-limit [0-128] limit the available ram that can be used while processing --system-memory-limit [0-128] limit the available RAM that can be used while processing
face analyser: face analyser:
--face-analyser-order {left-right,right-left,top-bottom,bottom-top,small-large,large-small,best-worst,worst-best} specify the order in which the face analyser detects faces. --face-analyser-order {left-right,right-left,top-bottom,bottom-top,small-large,large-small,best-worst,worst-best} specify the order in which the face analyser detects faces
--face-analyser-age {child,teen,adult,senior} filter the detected faces based on their age --face-analyser-age {child,teen,adult,senior} filter the detected faces based on their age
--face-analyser-gender {female,male} filter the detected faces based on their gender --face-analyser-gender {female,male} filter the detected faces based on their gender
--face-detector-model {many,retinaface,scrfd,yoloface,yunet} choose the model responsible for detecting the face --face-detector-model {many,retinaface,scrfd,yoloface,yunet} choose the model responsible for detecting the face
@ -88,12 +89,14 @@ output creation:
--skip-audio omit the audio from the target video --skip-audio omit the audio from the target video
frame processors: frame processors:
--frame-processors FRAME_PROCESSORS [FRAME_PROCESSORS ...] load a single or multiple frame processors. (choices: face_debugger, face_enhancer, face_swapper, frame_enhancer, lip_syncer, ...) --frame-processors FRAME_PROCESSORS [FRAME_PROCESSORS ...] load a single or multiple frame processors. (choices: face_debugger, face_enhancer, face_swapper, frame_colorizer, frame_enhancer, lip_syncer, ...)
--face-debugger-items FACE_DEBUGGER_ITEMS [FACE_DEBUGGER_ITEMS ...] load a single or multiple frame processors (choices: bounding-box, face-landmark-5, face-landmark-5/68, face-landmark-68, face-mask, face-detector-score, face-landmarker-score, age, gender) --face-debugger-items FACE_DEBUGGER_ITEMS [FACE_DEBUGGER_ITEMS ...] load a single or multiple frame processors (choices: 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)
--face-enhancer-model {codeformer,gfpgan_1.2,gfpgan_1.3,gfpgan_1.4,gpen_bfr_256,gpen_bfr_512,restoreformer_plus_plus} choose the model responsible for enhancing the face --face-enhancer-model {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} choose the model responsible for enhancing the face
--face-enhancer-blend [0-100] blend the enhanced into the previous face --face-enhancer-blend [0-100] blend the enhanced into the previous face
--face-swapper-model {blendswap_256,inswapper_128,inswapper_128_fp16,simswap_256,simswap_512_unofficial,uniface_256} choose the model responsible for swapping the face --face-swapper-model {blendswap_256,inswapper_128,inswapper_128_fp16,simswap_256,simswap_512_unofficial,uniface_256} choose the model responsible for swapping the face
--frame-enhancer-model {lsdir_x4,nomos8k_sc_x4,real_esrgan_x4,real_esrgan_x4_fp16,span_kendata_x4} choose the model responsible for enhancing the frame --frame-colorizer-model {ddcolor,ddcolor_artistic,deoldify_artistic} choose the model responsible for colorizing the frame
--frame-colorizer-blend [0-100] blend the colorized into the previous frame
--frame-enhancer-model {lsdir_x4,nomos8k_sc_x4,real_esrgan_x2,real_esrgan_x2_fp16,real_esrgan_x4,real_esrgan_x4_fp16,real_hatgan_x4,span_kendata_x4} choose the model responsible for enhancing the frame
--frame-enhancer-blend [0-100] blend the enhanced into the previous frame --frame-enhancer-blend [0-100] blend the enhanced into the previous frame
--lip-syncer-model {wav2lip_gan} choose the model responsible for syncing the lips --lip-syncer-model {wav2lip_gan} choose the model responsible for syncing the lips

View File

@ -4,6 +4,7 @@ target_path =
output_path = output_path =
[misc] [misc]
force_download =
skip_download = skip_download =
headless = headless =
log_level = log_level =
@ -60,6 +61,8 @@ face_debugger_items =
face_enhancer_model = face_enhancer_model =
face_enhancer_blend = face_enhancer_blend =
face_swapper_model = face_swapper_model =
frame_colorizer_model =
frame_colorizer_blend =
frame_enhancer_model = frame_enhancer_model =
frame_enhancer_blend = frame_enhancer_blend =
lip_syncer_model = lip_syncer_model =

View File

@ -5,7 +5,49 @@ import scipy
from facefusion.filesystem import is_audio from facefusion.filesystem import is_audio
from facefusion.ffmpeg import read_audio_buffer from facefusion.ffmpeg import read_audio_buffer
from facefusion.typing import Fps, Audio, Spectrogram, AudioFrame from facefusion.typing import Fps, Audio, AudioFrame, Spectrogram, MelFilterBank
from facefusion.voice_extractor import batch_extract_voice
@lru_cache(maxsize = 128)
def read_static_audio(audio_path : str, fps : Fps) -> Optional[List[AudioFrame]]:
return read_audio(audio_path, fps)
def read_audio(audio_path : str, fps : Fps) -> Optional[List[AudioFrame]]:
sample_rate = 16000
channel_total = 2
if is_audio(audio_path):
audio_buffer = read_audio_buffer(audio_path, sample_rate, channel_total)
audio = numpy.frombuffer(audio_buffer, dtype = numpy.int16).reshape(-1, 2)
audio = prepare_audio(audio)
spectrogram = create_spectrogram(audio)
audio_frames = extract_audio_frames(spectrogram, fps)
return audio_frames
return None
@lru_cache(maxsize = 128)
def read_static_voice(audio_path : str, fps : Fps) -> Optional[List[AudioFrame]]:
return read_voice(audio_path, fps)
def read_voice(audio_path : str, fps : Fps) -> Optional[List[AudioFrame]]:
sample_rate = 16000
channel_total = 2
chunk_size = 1024 ** 3
step_size = chunk_size // 4
if is_audio(audio_path):
audio_buffer = read_audio_buffer(audio_path, sample_rate, channel_total)
audio = numpy.frombuffer(audio_buffer, dtype = numpy.int16).reshape(-1, 2)
audio = batch_extract_voice(audio, chunk_size, step_size)
audio = prepare_audio(audio)
spectrogram = create_spectrogram(audio)
audio_frames = extract_audio_frames(spectrogram, fps)
return audio_frames
return None
def get_audio_frame(audio_path : str, fps : Fps, frame_number : int = 0) -> Optional[AudioFrame]: def get_audio_frame(audio_path : str, fps : Fps, frame_number : int = 0) -> Optional[AudioFrame]:
@ -16,33 +58,26 @@ def get_audio_frame(audio_path : str, fps : Fps, frame_number : int = 0) -> Opti
return None return None
def create_empty_audio_frame() -> AudioFrame: def get_voice_frame(audio_path : str, fps : Fps, frame_number : int = 0) -> Optional[AudioFrame]:
audio_frame = numpy.zeros((80, 16), dtype = numpy.int16)
return audio_frame
@lru_cache(maxsize = None)
def read_static_audio(audio_path : str, fps : Fps) -> Optional[List[AudioFrame]]:
if is_audio(audio_path): if is_audio(audio_path):
audio_buffer = read_audio_buffer(audio_path, 16000, 2) voice_frames = read_static_voice(audio_path, fps)
audio = numpy.frombuffer(audio_buffer, dtype = numpy.int16).reshape(-1, 2) if frame_number in range(len(voice_frames)):
audio = normalize_audio(audio) return voice_frames[frame_number]
audio = filter_audio(audio, -0.97)
spectrogram = create_spectrogram(audio, 16000, 80, 800, 55.0, 7600.0)
audio_frames = extract_audio_frames(spectrogram, 80, 16, fps)
return audio_frames
return None return None
def normalize_audio(audio : numpy.ndarray[Any, Any]) -> Audio: def create_empty_audio_frame() -> AudioFrame:
mel_filter_total = 80
step_size = 16
audio_frame = numpy.zeros((mel_filter_total, step_size)).astype(numpy.int16)
return audio_frame
def prepare_audio(audio : numpy.ndarray[Any, Any]) -> Audio:
if audio.ndim > 1: if audio.ndim > 1:
audio = numpy.mean(audio, axis = 1) audio = numpy.mean(audio, axis = 1)
audio = audio / numpy.max(numpy.abs(audio), axis = 0) audio = audio / numpy.max(numpy.abs(audio), axis = 0)
return audio audio = scipy.signal.lfilter([ 1.0, -0.97 ], [ 1.0 ], audio)
def filter_audio(audio : Audio, filter_coefficient : float) -> Audio:
audio = scipy.signal.lfilter([ 1.0, filter_coefficient ], [1.0], audio)
return audio return audio
@ -54,28 +89,40 @@ def convert_mel_to_hertz(mel : numpy.ndarray[Any, Any]) -> numpy.ndarray[Any, An
return 700 * (10 ** (mel / 2595) - 1) return 700 * (10 ** (mel / 2595) - 1)
@lru_cache(maxsize = None) def create_mel_filter_bank() -> MelFilterBank:
def create_static_mel_filter(sample_rate : int, filter_total : int, filter_size : int, frequency_minimum : float, frequency_maximum : float) -> numpy.ndarray[Any, Any]: mel_filter_total = 80
frequency_maximum = min(sample_rate / 2, frequency_maximum) mel_bin_total = 800
mel_filter = numpy.zeros((filter_total, filter_size // 2 + 1)) sample_rate = 16000
mel_bins = numpy.linspace(convert_hertz_to_mel(frequency_minimum), convert_hertz_to_mel(frequency_maximum), filter_total + 2) min_frequency = 55.0
indices = numpy.floor((filter_size + 1) * convert_mel_to_hertz(mel_bins) / sample_rate).astype(numpy.int16) max_frequency = 7600.0
for index in range(filter_total): mel_filter_bank = numpy.zeros((mel_filter_total, mel_bin_total // 2 + 1))
mel_filter[index, indices[index]: indices[index + 1]] = scipy.signal.windows.triang(indices[index + 1] - indices[index]) mel_frequency_range = numpy.linspace(convert_hertz_to_mel(min_frequency), convert_hertz_to_mel(max_frequency), mel_filter_total + 2)
return mel_filter indices = numpy.floor((mel_bin_total + 1) * convert_mel_to_hertz(mel_frequency_range) / sample_rate).astype(numpy.int16)
for index in range(mel_filter_total):
start = indices[index]
end = indices[index + 1]
mel_filter_bank[index, start:end] = scipy.signal.windows.triang(end - start)
return mel_filter_bank
def create_spectrogram(audio : Audio, sample_rate : int, filter_total : int, filter_size : int, frequency_minimum : float, frequency_maximum : float) -> Spectrogram: def create_spectrogram(audio : Audio) -> Spectrogram:
mel_filter = create_static_mel_filter(sample_rate, filter_total, filter_size, frequency_minimum, frequency_maximum) mel_bin_total = 800
spectrogram = scipy.signal.stft(audio, nperseg = filter_size, noverlap = 600, nfft = filter_size)[2] mel_bin_overlap = 600
spectrogram = numpy.dot(mel_filter, numpy.abs(spectrogram)) mel_filter_bank = create_mel_filter_bank()
spectrogram = scipy.signal.stft(audio, nperseg = mel_bin_total, nfft = mel_bin_total, noverlap = mel_bin_overlap)[2]
spectrogram = numpy.dot(mel_filter_bank, numpy.abs(spectrogram))
return spectrogram return spectrogram
def extract_audio_frames(spectrogram : Spectrogram, filter_total : int, audio_frame_step : int, fps : Fps) -> List[AudioFrame]: def extract_audio_frames(spectrogram : Spectrogram, fps : Fps) -> List[AudioFrame]:
indices = numpy.arange(0, spectrogram.shape[1], filter_total / fps).astype(numpy.int16) mel_filter_total = 80
indices = indices[indices >= audio_frame_step] step_size = 16
audio_frames = [] audio_frames = []
indices = numpy.arange(0, spectrogram.shape[1], mel_filter_total / fps).astype(numpy.int16)
indices = indices[indices >= step_size]
for index in indices: for index in indices:
audio_frames.append(spectrogram[:, max(0, index - audio_frame_step) : index]) start = max(0, index - step_size)
audio_frames.append(spectrogram[:, start:index])
return audio_frames return audio_frames

View File

@ -1,4 +1,4 @@
from typing import List, Any, Tuple from typing import List, Any
import numpy import numpy
@ -16,12 +16,3 @@ def create_float_range(start : float, stop : float, step : float) -> List[float]
def get_first(__list__ : Any) -> Any: def get_first(__list__ : Any) -> Any:
return next(iter(__list__), None) return next(iter(__list__), None)
def extract_major_version(version : str) -> Tuple[int, int]:
versions = version.split('.')
if len(versions) > 1:
return int(versions[0]), int(versions[1])
if len(versions) == 1:
return int(versions[0]), 0
return 0, 0

View File

@ -1,4 +1,4 @@
from typing import Any, Dict from typing import Any
from functools import lru_cache from functools import lru_cache
from time import sleep from time import sleep
import threading import threading
@ -9,15 +9,15 @@ from tqdm import tqdm
import facefusion.globals import facefusion.globals
from facefusion import process_manager, wording from facefusion import process_manager, wording
from facefusion.typing import VisionFrame, ModelValue, Fps from facefusion.typing import VisionFrame, ModelSet, Fps
from facefusion.execution import apply_execution_provider_options from facefusion.execution import apply_execution_provider_options
from facefusion.vision import get_video_frame, count_video_frame_total, read_image, detect_video_fps from facefusion.vision import get_video_frame, count_video_frame_total, read_image, detect_video_fps
from facefusion.filesystem import resolve_relative_path from facefusion.filesystem import resolve_relative_path, is_file
from facefusion.download import conditional_download from facefusion.download import conditional_download
CONTENT_ANALYSER = None CONTENT_ANALYSER = None
THREAD_LOCK : threading.Lock = threading.Lock() THREAD_LOCK : threading.Lock = threading.Lock()
MODELS : Dict[str, ModelValue] =\ MODELS : ModelSet =\
{ {
'open_nsfw': 'open_nsfw':
{ {
@ -26,7 +26,7 @@ MODELS : Dict[str, ModelValue] =\
} }
} }
PROBABILITY_LIMIT = 0.80 PROBABILITY_LIMIT = 0.80
RATE_LIMIT = 5 RATE_LIMIT = 10
STREAM_COUNTER = 0 STREAM_COUNTER = 0
@ -49,13 +49,15 @@ def clear_content_analyser() -> None:
def pre_check() -> bool: def pre_check() -> bool:
if not facefusion.globals.skip_download:
download_directory_path = resolve_relative_path('../.assets/models') download_directory_path = resolve_relative_path('../.assets/models')
model_url = MODELS.get('open_nsfw').get('url') model_url = MODELS.get('open_nsfw').get('url')
model_path = MODELS.get('open_nsfw').get('path')
if not facefusion.globals.skip_download:
process_manager.check() process_manager.check()
conditional_download(download_directory_path, [ model_url ]) conditional_download(download_directory_path, [ model_url ])
process_manager.end() process_manager.end()
return True return is_file(model_path)
def analyse_stream(vision_frame : VisionFrame, video_fps : Fps) -> bool: def analyse_stream(vision_frame : VisionFrame, video_fps : Fps) -> bool:

View File

@ -15,7 +15,7 @@ import facefusion.choices
import facefusion.globals import facefusion.globals
from facefusion.face_analyser import get_one_face, get_average_face from facefusion.face_analyser import get_one_face, get_average_face
from facefusion.face_store import get_reference_faces, append_reference_face from facefusion.face_store import get_reference_faces, append_reference_face
from facefusion import face_analyser, face_masker, content_analyser, config, process_manager, metadata, logger, wording from facefusion import face_analyser, face_masker, content_analyser, config, process_manager, metadata, logger, wording, voice_extractor
from facefusion.content_analyser import analyse_image, analyse_video from facefusion.content_analyser import analyse_image, analyse_video
from facefusion.processors.frame.core import get_frame_processors_modules, load_frame_processor_module from facefusion.processors.frame.core import get_frame_processors_modules, load_frame_processor_module
from facefusion.common_helper import create_metavar, get_first from facefusion.common_helper import create_metavar, get_first
@ -23,7 +23,8 @@ from facefusion.execution import encode_execution_providers, decode_execution_pr
from facefusion.normalizer import normalize_output_path, normalize_padding, normalize_fps from facefusion.normalizer import normalize_output_path, normalize_padding, normalize_fps
from facefusion.memory import limit_system_memory from facefusion.memory import limit_system_memory
from facefusion.statistics import conditional_log_statistics from facefusion.statistics import conditional_log_statistics
from facefusion.filesystem import list_directory, get_temp_frame_paths, create_temp, move_temp, clear_temp, is_image, is_video, filter_audio_paths from facefusion.download import conditional_download
from facefusion.filesystem import list_directory, get_temp_frame_paths, create_temp, move_temp, clear_temp, is_image, is_video, filter_audio_paths, resolve_relative_path
from facefusion.ffmpeg import extract_frames, merge_video, copy_image, finalize_image, restore_audio, replace_audio from facefusion.ffmpeg import extract_frames, merge_video, copy_image, finalize_image, restore_audio, replace_audio
from facefusion.vision import read_image, read_static_images, detect_image_resolution, restrict_video_fps, create_image_resolutions, get_video_frame, detect_video_resolution, detect_video_fps, restrict_video_resolution, restrict_image_resolution, create_video_resolutions, pack_resolution, unpack_resolution from facefusion.vision import read_image, read_static_images, detect_image_resolution, restrict_video_fps, create_image_resolutions, get_video_frame, detect_video_resolution, detect_video_fps, restrict_video_resolution, restrict_image_resolution, create_video_resolutions, pack_resolution, unpack_resolution
@ -33,7 +34,7 @@ warnings.filterwarnings('ignore', category = UserWarning, module = 'gradio')
def cli() -> None: def cli() -> None:
signal.signal(signal.SIGINT, lambda signal_number, frame: destroy()) signal.signal(signal.SIGINT, lambda signal_number, frame: destroy())
program = ArgumentParser(formatter_class = lambda prog: HelpFormatter(prog, max_help_position = 130), add_help = False) program = ArgumentParser(formatter_class = lambda prog: HelpFormatter(prog, max_help_position = 160), add_help = False)
# general # general
program.add_argument('-s', '--source', help = wording.get('help.source'), action = 'append', dest = 'source_paths', default = config.get_str_list('general.source_paths')) program.add_argument('-s', '--source', help = wording.get('help.source'), action = 'append', dest = 'source_paths', default = config.get_str_list('general.source_paths'))
program.add_argument('-t', '--target', help = wording.get('help.target'), dest = 'target_path', default = config.get_str_value('general.target_path')) program.add_argument('-t', '--target', help = wording.get('help.target'), dest = 'target_path', default = config.get_str_value('general.target_path'))
@ -41,6 +42,7 @@ def cli() -> None:
program.add_argument('-v', '--version', version = metadata.get('name') + ' ' + metadata.get('version'), action = 'version') program.add_argument('-v', '--version', version = metadata.get('name') + ' ' + metadata.get('version'), action = 'version')
# misc # misc
group_misc = program.add_argument_group('misc') group_misc = program.add_argument_group('misc')
group_misc.add_argument('--force-download', help = wording.get('help.force_download'), action = 'store_true', default = config.get_bool_value('misc.force_download'))
group_misc.add_argument('--skip-download', help = wording.get('help.skip_download'), action = 'store_true', default = config.get_bool_value('misc.skip_download')) group_misc.add_argument('--skip-download', help = wording.get('help.skip_download'), action = 'store_true', default = config.get_bool_value('misc.skip_download'))
group_misc.add_argument('--headless', help = wording.get('help.headless'), action = 'store_true', default = config.get_bool_value('misc.headless')) group_misc.add_argument('--headless', help = wording.get('help.headless'), action = 'store_true', default = config.get_bool_value('misc.headless'))
group_misc.add_argument('--log-level', help = wording.get('help.log_level'), default = config.get_str_value('misc.log_level', 'info'), choices = logger.get_log_levels()) group_misc.add_argument('--log-level', help = wording.get('help.log_level'), default = config.get_str_value('misc.log_level', 'info'), choices = logger.get_log_levels())
@ -89,7 +91,7 @@ def cli() -> None:
group_output_creation.add_argument('--output-video-preset', help = wording.get('help.output_video_preset'), default = config.get_str_value('output_creation.output_video_preset', 'veryfast'), choices = facefusion.choices.output_video_presets) group_output_creation.add_argument('--output-video-preset', help = wording.get('help.output_video_preset'), default = config.get_str_value('output_creation.output_video_preset', 'veryfast'), choices = facefusion.choices.output_video_presets)
group_output_creation.add_argument('--output-video-quality', help = wording.get('help.output_video_quality'), type = int, default = config.get_int_value('output_creation.output_video_quality', '80'), choices = facefusion.choices.output_video_quality_range, metavar = create_metavar(facefusion.choices.output_video_quality_range)) group_output_creation.add_argument('--output-video-quality', help = wording.get('help.output_video_quality'), type = int, default = config.get_int_value('output_creation.output_video_quality', '80'), choices = facefusion.choices.output_video_quality_range, metavar = create_metavar(facefusion.choices.output_video_quality_range))
group_output_creation.add_argument('--output-video-resolution', help = wording.get('help.output_video_resolution'), default = config.get_str_value('output_creation.output_video_resolution')) group_output_creation.add_argument('--output-video-resolution', help = wording.get('help.output_video_resolution'), default = config.get_str_value('output_creation.output_video_resolution'))
group_output_creation.add_argument('--output-video-fps', help = wording.get('help.output_video_fps'), type = float) group_output_creation.add_argument('--output-video-fps', help = wording.get('help.output_video_fps'), type = float, default = config.get_str_value('output_creation.output_video_fps'))
group_output_creation.add_argument('--skip-audio', help = wording.get('help.skip_audio'), action = 'store_true', default = config.get_bool_value('output_creation.skip_audio')) group_output_creation.add_argument('--skip-audio', help = wording.get('help.skip_audio'), action = 'store_true', default = config.get_bool_value('output_creation.skip_audio'))
# frame processors # frame processors
available_frame_processors = list_directory('facefusion/processors/frame/modules') available_frame_processors = list_directory('facefusion/processors/frame/modules')
@ -113,6 +115,7 @@ def apply_args(program : ArgumentParser) -> None:
facefusion.globals.target_path = args.target_path facefusion.globals.target_path = args.target_path
facefusion.globals.output_path = args.output_path facefusion.globals.output_path = args.output_path
# misc # misc
facefusion.globals.force_download = args.force_download
facefusion.globals.skip_download = args.skip_download facefusion.globals.skip_download = args.skip_download
facefusion.globals.headless = args.headless facefusion.globals.headless = args.headless
facefusion.globals.log_level = args.log_level facefusion.globals.log_level = args.log_level
@ -184,9 +187,13 @@ def apply_args(program : ArgumentParser) -> None:
def run(program : ArgumentParser) -> None: def run(program : ArgumentParser) -> None:
apply_args(program) apply_args(program)
logger.init(facefusion.globals.log_level) logger.init(facefusion.globals.log_level)
if facefusion.globals.system_memory_limit > 0: if facefusion.globals.system_memory_limit > 0:
limit_system_memory(facefusion.globals.system_memory_limit) limit_system_memory(facefusion.globals.system_memory_limit)
if not pre_check() or not content_analyser.pre_check() or not face_analyser.pre_check() or not face_masker.pre_check(): if facefusion.globals.force_download:
force_download()
return
if not pre_check() or not content_analyser.pre_check() or not face_analyser.pre_check() or not face_masker.pre_check() or not voice_extractor.pre_check():
return return
for frame_processor_module in get_frame_processors_modules(facefusion.globals.frame_processors): for frame_processor_module in get_frame_processors_modules(facefusion.globals.frame_processors):
if not frame_processor_module.pre_check(): if not frame_processor_module.pre_check():
@ -256,6 +263,24 @@ def conditional_append_reference_faces() -> None:
append_reference_face(frame_processor_module.__name__, reference_face) append_reference_face(frame_processor_module.__name__, reference_face)
def force_download() -> None:
download_directory_path = resolve_relative_path('../.assets/models')
available_frame_processors = list_directory('facefusion/processors/frame/modules')
model_list =\
[
content_analyser.MODELS,
face_analyser.MODELS,
face_masker.MODELS,
voice_extractor.MODELS
]
for frame_processor_module in get_frame_processors_modules(available_frame_processors):
if hasattr(frame_processor_module, 'MODELS'):
model_list.append(frame_processor_module.MODELS)
model_urls = [ models[model].get('url') for models in model_list for model in models ]
conditional_download(download_directory_path, model_urls)
def process_image(start_time : float) -> None: def process_image(start_time : float) -> None:
normed_output_path = normalize_output_path(facefusion.globals.target_path, facefusion.globals.output_path) normed_output_path = normalize_output_path(facefusion.globals.target_path, facefusion.globals.output_path)
if analyse_image(facefusion.globals.target_path): if analyse_image(facefusion.globals.target_path):

View File

@ -8,11 +8,11 @@ import onnxruntime
import facefusion.globals import facefusion.globals
from facefusion import process_manager from facefusion import process_manager
from facefusion.common_helper import get_first from facefusion.common_helper import get_first
from facefusion.face_helper import warp_face_by_face_landmark_5, warp_face_by_translation, create_static_anchors, distance_to_face_landmark_5, distance_to_bounding_box, convert_face_landmark_68_to_5, apply_nms, categorize_age, categorize_gender from facefusion.face_helper import estimate_matrix_by_face_landmark_5, warp_face_by_face_landmark_5, warp_face_by_translation, create_static_anchors, distance_to_face_landmark_5, distance_to_bounding_box, convert_face_landmark_68_to_5, apply_nms, categorize_age, categorize_gender
from facefusion.face_store import get_static_faces, set_static_faces from facefusion.face_store import get_static_faces, set_static_faces
from facefusion.execution import apply_execution_provider_options from facefusion.execution import apply_execution_provider_options
from facefusion.download import conditional_download from facefusion.download import conditional_download
from facefusion.filesystem import resolve_relative_path from facefusion.filesystem import resolve_relative_path, is_file
from facefusion.typing import VisionFrame, Face, FaceSet, FaceAnalyserOrder, FaceAnalyserAge, FaceAnalyserGender, ModelSet, BoundingBox, FaceLandmarkSet, FaceLandmark5, FaceLandmark68, Score, FaceScoreSet, Embedding from facefusion.typing import VisionFrame, Face, FaceSet, FaceAnalyserOrder, FaceAnalyserAge, FaceAnalyserGender, ModelSet, BoundingBox, FaceLandmarkSet, FaceLandmark5, FaceLandmark68, Score, FaceScoreSet, Embedding
from facefusion.vision import resize_frame_resolution, unpack_resolution from facefusion.vision import resize_frame_resolution, unpack_resolution
@ -61,11 +61,16 @@ MODELS : ModelSet =\
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/arcface_w600k_r50.onnx', 'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/arcface_w600k_r50.onnx',
'path': resolve_relative_path('../.assets/models/arcface_w600k_r50.onnx') 'path': resolve_relative_path('../.assets/models/arcface_w600k_r50.onnx')
}, },
'face_landmarker': 'face_landmarker_68':
{ {
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/2dfan4.onnx', 'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/2dfan4.onnx',
'path': resolve_relative_path('../.assets/models/2dfan4.onnx') 'path': resolve_relative_path('../.assets/models/2dfan4.onnx')
}, },
'face_landmarker_68_5':
{
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/face_landmarker_68_5.onnx',
'path': resolve_relative_path('../.assets/models/face_landmarker_68_5.onnx')
},
'gender_age': 'gender_age':
{ {
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/gender_age.onnx', 'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/gender_age.onnx',
@ -78,22 +83,20 @@ def get_face_analyser() -> Any:
global FACE_ANALYSER global FACE_ANALYSER
face_detectors = {} face_detectors = {}
face_landmarkers = {}
with THREAD_LOCK: with THREAD_LOCK:
while process_manager.is_checking(): while process_manager.is_checking():
sleep(0.5) sleep(0.5)
if FACE_ANALYSER is None: if FACE_ANALYSER is None:
if facefusion.globals.face_detector_model in [ 'many', 'retinaface' ]: if facefusion.globals.face_detector_model in [ 'many', 'retinaface' ]:
face_detector = onnxruntime.InferenceSession(MODELS.get('face_detector_retinaface').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_providers)) face_detectors['retinaface'] = onnxruntime.InferenceSession(MODELS.get('face_detector_retinaface').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_providers))
face_detectors['retinaface'] = face_detector
if facefusion.globals.face_detector_model in [ 'many', 'scrfd' ]: if facefusion.globals.face_detector_model in [ 'many', 'scrfd' ]:
face_detector = onnxruntime.InferenceSession(MODELS.get('face_detector_scrfd').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_providers)) face_detectors['scrfd'] = onnxruntime.InferenceSession(MODELS.get('face_detector_scrfd').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_providers))
face_detectors['scrfd'] = face_detector
if facefusion.globals.face_detector_model in [ 'many', 'yoloface' ]: if facefusion.globals.face_detector_model in [ 'many', 'yoloface' ]:
face_detector = onnxruntime.InferenceSession(MODELS.get('face_detector_yoloface').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_providers)) face_detectors['yoloface'] = onnxruntime.InferenceSession(MODELS.get('face_detector_yoloface').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_providers))
face_detectors['yoloface'] = face_detector
if facefusion.globals.face_detector_model in [ 'yunet' ]: if facefusion.globals.face_detector_model in [ 'yunet' ]:
face_detector = cv2.FaceDetectorYN.create(MODELS.get('face_detector_yunet').get('path'), '', (0, 0)) face_detectors['yunet'] = cv2.FaceDetectorYN.create(MODELS.get('face_detector_yunet').get('path'), '', (0, 0))
face_detectors['yunet'] = face_detector
if facefusion.globals.face_recognizer_model == 'arcface_blendswap': if facefusion.globals.face_recognizer_model == 'arcface_blendswap':
face_recognizer = onnxruntime.InferenceSession(MODELS.get('face_recognizer_arcface_blendswap').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_providers)) face_recognizer = onnxruntime.InferenceSession(MODELS.get('face_recognizer_arcface_blendswap').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_providers))
if facefusion.globals.face_recognizer_model == 'arcface_inswapper': if facefusion.globals.face_recognizer_model == 'arcface_inswapper':
@ -102,13 +105,14 @@ def get_face_analyser() -> Any:
face_recognizer = onnxruntime.InferenceSession(MODELS.get('face_recognizer_arcface_simswap').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_providers)) face_recognizer = onnxruntime.InferenceSession(MODELS.get('face_recognizer_arcface_simswap').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_providers))
if facefusion.globals.face_recognizer_model == 'arcface_uniface': if facefusion.globals.face_recognizer_model == 'arcface_uniface':
face_recognizer = onnxruntime.InferenceSession(MODELS.get('face_recognizer_arcface_uniface').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_providers)) face_recognizer = onnxruntime.InferenceSession(MODELS.get('face_recognizer_arcface_uniface').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_providers))
face_landmarker = onnxruntime.InferenceSession(MODELS.get('face_landmarker').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_providers)) face_landmarkers['68'] = onnxruntime.InferenceSession(MODELS.get('face_landmarker_68').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_providers))
face_landmarkers['68_5'] = onnxruntime.InferenceSession(MODELS.get('face_landmarker_68_5').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_providers))
gender_age = onnxruntime.InferenceSession(MODELS.get('gender_age').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_providers)) gender_age = onnxruntime.InferenceSession(MODELS.get('gender_age').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_providers))
FACE_ANALYSER =\ FACE_ANALYSER =\
{ {
'face_detectors': face_detectors, 'face_detectors': face_detectors,
'face_recognizer': face_recognizer, 'face_recognizer': face_recognizer,
'face_landmarker': face_landmarker, 'face_landmarkers': face_landmarkers,
'gender_age': gender_age 'gender_age': gender_age
} }
return FACE_ANALYSER return FACE_ANALYSER
@ -121,34 +125,50 @@ def clear_face_analyser() -> Any:
def pre_check() -> bool: def pre_check() -> bool:
if not facefusion.globals.skip_download:
download_directory_path = resolve_relative_path('../.assets/models') download_directory_path = resolve_relative_path('../.assets/models')
model_urls =\ model_urls =\
[ [
MODELS.get('face_landmarker').get('url'), MODELS.get('face_landmarker_68').get('url'),
MODELS.get('face_landmarker_68_5').get('url'),
MODELS.get('gender_age').get('url') MODELS.get('gender_age').get('url')
] ]
model_paths =\
[
MODELS.get('face_landmarker_68').get('path'),
MODELS.get('face_landmarker_68_5').get('path'),
MODELS.get('gender_age').get('path')
]
if facefusion.globals.face_detector_model in [ 'many', 'retinaface' ]: if facefusion.globals.face_detector_model in [ 'many', 'retinaface' ]:
model_urls.append(MODELS.get('face_detector_retinaface').get('url')) model_urls.append(MODELS.get('face_detector_retinaface').get('url'))
model_paths.append(MODELS.get('face_detector_retinaface').get('path'))
if facefusion.globals.face_detector_model in [ 'many', 'scrfd' ]: if facefusion.globals.face_detector_model in [ 'many', 'scrfd' ]:
model_urls.append(MODELS.get('face_detector_scrfd').get('url')) model_urls.append(MODELS.get('face_detector_scrfd').get('url'))
model_paths.append(MODELS.get('face_detector_scrfd').get('path'))
if facefusion.globals.face_detector_model in [ 'many', 'yoloface' ]: if facefusion.globals.face_detector_model in [ 'many', 'yoloface' ]:
model_urls.append(MODELS.get('face_detector_yoloface').get('url')) model_urls.append(MODELS.get('face_detector_yoloface').get('url'))
model_paths.append(MODELS.get('face_detector_yoloface').get('path'))
if facefusion.globals.face_detector_model in [ 'yunet' ]: if facefusion.globals.face_detector_model in [ 'yunet' ]:
model_urls.append(MODELS.get('face_detector_yunet').get('url')) model_urls.append(MODELS.get('face_detector_yunet').get('url'))
model_paths.append(MODELS.get('face_detector_yunet').get('path'))
if facefusion.globals.face_recognizer_model == 'arcface_blendswap': if facefusion.globals.face_recognizer_model == 'arcface_blendswap':
model_urls.append(MODELS.get('face_recognizer_arcface_blendswap').get('url')) model_urls.append(MODELS.get('face_recognizer_arcface_blendswap').get('url'))
model_paths.append(MODELS.get('face_recognizer_arcface_blendswap').get('path'))
if facefusion.globals.face_recognizer_model == 'arcface_inswapper': if facefusion.globals.face_recognizer_model == 'arcface_inswapper':
model_urls.append(MODELS.get('face_recognizer_arcface_inswapper').get('url')) model_urls.append(MODELS.get('face_recognizer_arcface_inswapper').get('url'))
model_paths.append(MODELS.get('face_recognizer_arcface_inswapper').get('path'))
if facefusion.globals.face_recognizer_model == 'arcface_simswap': if facefusion.globals.face_recognizer_model == 'arcface_simswap':
model_urls.append(MODELS.get('face_recognizer_arcface_simswap').get('url')) model_urls.append(MODELS.get('face_recognizer_arcface_simswap').get('url'))
model_paths.append(MODELS.get('face_recognizer_arcface_simswap').get('path'))
if facefusion.globals.face_recognizer_model == 'arcface_uniface': if facefusion.globals.face_recognizer_model == 'arcface_uniface':
model_urls.append(MODELS.get('face_recognizer_arcface_uniface').get('url')) model_urls.append(MODELS.get('face_recognizer_arcface_uniface').get('url'))
model_paths.append(MODELS.get('face_recognizer_arcface_uniface').get('path'))
if not facefusion.globals.skip_download:
process_manager.check() process_manager.check()
conditional_download(download_directory_path, model_urls) conditional_download(download_directory_path, model_urls)
process_manager.end() process_manager.end()
return True return all(is_file(model_path) for model_path in model_paths)
def detect_with_retinaface(vision_frame : VisionFrame, face_detector_size : str) -> Tuple[List[BoundingBox], List[FaceLandmark5], List[Score]]: def detect_with_retinaface(vision_frame : VisionFrame, face_detector_size : str) -> Tuple[List[BoundingBox], List[FaceLandmark5], List[Score]]:
@ -321,7 +341,8 @@ def create_faces(vision_frame : VisionFrame, bounding_box_list : List[BoundingBo
for index in keep_indices: for index in keep_indices:
bounding_box = bounding_box_list[index] bounding_box = bounding_box_list[index]
face_landmark_5_68 = face_landmark_5_list[index] face_landmark_5_68 = face_landmark_5_list[index]
face_landmark_68 = None face_landmark_68_5 = expand_face_landmark_68_from_5(face_landmark_5_68)
face_landmark_68 = face_landmark_68_5
face_landmark_68_score = 0.0 face_landmark_68_score = 0.0
if facefusion.globals.face_landmarker_score > 0: if facefusion.globals.face_landmarker_score > 0:
face_landmark_68, face_landmark_68_score = detect_face_landmark_68(vision_frame, bounding_box) face_landmark_68, face_landmark_68_score = detect_face_landmark_68(vision_frame, bounding_box)
@ -331,7 +352,8 @@ def create_faces(vision_frame : VisionFrame, bounding_box_list : List[BoundingBo
{ {
'5': face_landmark_5_list[index], '5': face_landmark_5_list[index],
'5/68': face_landmark_5_68, '5/68': face_landmark_5_68,
'68': face_landmark_68 '68': face_landmark_68,
'68/5': face_landmark_68_5
} }
scores : FaceScoreSet = \ scores : FaceScoreSet = \
{ {
@ -368,7 +390,7 @@ def calc_embedding(temp_vision_frame : VisionFrame, face_landmark_5 : FaceLandma
def detect_face_landmark_68(temp_vision_frame : VisionFrame, bounding_box : BoundingBox) -> Tuple[FaceLandmark68, Score]: def detect_face_landmark_68(temp_vision_frame : VisionFrame, bounding_box : BoundingBox) -> Tuple[FaceLandmark68, Score]:
face_landmarker = get_face_analyser().get('face_landmarker') face_landmarker = get_face_analyser().get('face_landmarkers').get('68')
scale = 195 / numpy.subtract(bounding_box[2:], bounding_box[:2]).max() scale = 195 / numpy.subtract(bounding_box[2:], bounding_box[:2]).max()
translation = (256 - numpy.add(bounding_box[2:], bounding_box[:2]) * scale) * 0.5 translation = (256 - numpy.add(bounding_box[2:], bounding_box[:2]) * scale) * 0.5
crop_vision_frame, affine_matrix = warp_face_by_translation(temp_vision_frame, translation, scale, (256, 256)) crop_vision_frame, affine_matrix = warp_face_by_translation(temp_vision_frame, translation, scale, (256, 256))
@ -390,6 +412,18 @@ def detect_face_landmark_68(temp_vision_frame : VisionFrame, bounding_box : Boun
return face_landmark_68, face_landmark_68_score return face_landmark_68, face_landmark_68_score
def expand_face_landmark_68_from_5(face_landmark_5 : FaceLandmark5) -> FaceLandmark68:
face_landmarker = get_face_analyser().get('face_landmarkers').get('68_5')
affine_matrix = estimate_matrix_by_face_landmark_5(face_landmark_5, 'ffhq_512', (1, 1))
face_landmark_5 = cv2.transform(face_landmark_5.reshape(1, -1, 2), affine_matrix).reshape(-1, 2)
face_landmark_68_5 = face_landmarker.run(None,
{
face_landmarker.get_inputs()[0].name: [ face_landmark_5 ]
})[0][0]
face_landmark_68_5 = cv2.transform(face_landmark_68_5.reshape(1, -1, 2), cv2.invertAffineTransform(affine_matrix)).reshape(-1, 2)
return face_landmark_68_5
def detect_gender_age(temp_vision_frame : VisionFrame, bounding_box : BoundingBox) -> Tuple[int, int]: def detect_gender_age(temp_vision_frame : VisionFrame, bounding_box : BoundingBox) -> Tuple[int, int]:
gender_age = get_face_analyser().get('gender_age') gender_age = get_face_analyser().get('gender_age')
bounding_box = bounding_box.reshape(2, -1) bounding_box = bounding_box.reshape(2, -1)

View File

@ -43,16 +43,21 @@ WARP_TEMPLATES : WarpTemplateSet =\
} }
def warp_face_by_face_landmark_5(temp_vision_frame : VisionFrame, face_landmark_5 : FaceLandmark5, warp_template : WarpTemplate, crop_size : Size) -> Tuple[VisionFrame, Matrix]: def estimate_matrix_by_face_landmark_5(face_landmark_5 : FaceLandmark5, warp_template : WarpTemplate, crop_size : Size) -> Matrix:
normed_warp_template = WARP_TEMPLATES.get(warp_template) * crop_size normed_warp_template = WARP_TEMPLATES.get(warp_template) * crop_size
affine_matrix = cv2.estimateAffinePartial2D(face_landmark_5, normed_warp_template, method = cv2.RANSAC, ransacReprojThreshold = 100)[0] affine_matrix = cv2.estimateAffinePartial2D(face_landmark_5, normed_warp_template, method = cv2.RANSAC, ransacReprojThreshold = 100)[0]
return affine_matrix
def warp_face_by_face_landmark_5(temp_vision_frame : VisionFrame, face_landmark_5 : FaceLandmark5, warp_template : WarpTemplate, crop_size : Size) -> Tuple[VisionFrame, Matrix]:
affine_matrix = estimate_matrix_by_face_landmark_5(face_landmark_5, warp_template, crop_size)
crop_vision_frame = cv2.warpAffine(temp_vision_frame, affine_matrix, crop_size, borderMode = cv2.BORDER_REPLICATE, flags = cv2.INTER_AREA) crop_vision_frame = cv2.warpAffine(temp_vision_frame, affine_matrix, crop_size, borderMode = cv2.BORDER_REPLICATE, flags = cv2.INTER_AREA)
return crop_vision_frame, affine_matrix return crop_vision_frame, affine_matrix
def warp_face_by_bounding_box(temp_vision_frame : VisionFrame, bounding_box : BoundingBox, crop_size : Size) -> Tuple[VisionFrame, Matrix]: def warp_face_by_bounding_box(temp_vision_frame : VisionFrame, bounding_box : BoundingBox, crop_size : Size) -> Tuple[VisionFrame, Matrix]:
source_points = numpy.array([ [ bounding_box[0], bounding_box[1] ], [bounding_box[2], bounding_box[1] ], [ bounding_box[0], bounding_box[3] ] ], dtype = numpy.float32) source_points = numpy.array([ [ bounding_box[0], bounding_box[1] ], [bounding_box[2], bounding_box[1] ], [ bounding_box[0], bounding_box[3] ] ]).astype(numpy.float32)
target_points = numpy.array([ [ 0, 0 ], [ crop_size[0], 0 ], [ 0, crop_size[1] ] ], dtype = numpy.float32) target_points = numpy.array([ [ 0, 0 ], [ crop_size[0], 0 ], [ 0, crop_size[1] ] ]).astype(numpy.float32)
affine_matrix = cv2.getAffineTransform(source_points, target_points) affine_matrix = cv2.getAffineTransform(source_points, target_points)
if bounding_box[2] - bounding_box[0] > crop_size[0] or bounding_box[3] - bounding_box[1] > crop_size[1]: if bounding_box[2] - bounding_box[0] > crop_size[0] or bounding_box[3] - bounding_box[1] > crop_size[1]:
interpolation_method = cv2.INTER_AREA interpolation_method = cv2.INTER_AREA
@ -112,14 +117,14 @@ def distance_to_face_landmark_5(points : numpy.ndarray[Any, Any], distance : num
return face_landmark_5 return face_landmark_5
def convert_face_landmark_68_to_5(landmark_68 : FaceLandmark68) -> FaceLandmark5: def convert_face_landmark_68_to_5(face_landmark_68 : FaceLandmark68) -> FaceLandmark5:
face_landmark_5 = numpy.array( face_landmark_5 = numpy.array(
[ [
numpy.mean(landmark_68[36:42], axis = 0), numpy.mean(face_landmark_68[36:42], axis = 0),
numpy.mean(landmark_68[42:48], axis = 0), numpy.mean(face_landmark_68[42:48], axis = 0),
landmark_68[30], face_landmark_68[30],
landmark_68[48], face_landmark_68[48],
landmark_68[54] face_landmark_68[54]
]) ])
return face_landmark_5 return face_landmark_5

View File

@ -11,7 +11,7 @@ import facefusion.globals
from facefusion import process_manager from facefusion import process_manager
from facefusion.typing import FaceLandmark68, VisionFrame, Mask, Padding, FaceMaskRegion, ModelSet from facefusion.typing import FaceLandmark68, VisionFrame, Mask, Padding, FaceMaskRegion, ModelSet
from facefusion.execution import apply_execution_provider_options from facefusion.execution import apply_execution_provider_options
from facefusion.filesystem import resolve_relative_path from facefusion.filesystem import resolve_relative_path, is_file
from facefusion.download import conditional_download from facefusion.download import conditional_download
FACE_OCCLUDER = None FACE_OCCLUDER = None
@ -49,6 +49,8 @@ def get_face_occluder() -> Any:
global FACE_OCCLUDER global FACE_OCCLUDER
with THREAD_LOCK: with THREAD_LOCK:
while process_manager.is_checking():
sleep(0.5)
if FACE_OCCLUDER is None: if FACE_OCCLUDER is None:
model_path = MODELS.get('face_occluder').get('path') model_path = MODELS.get('face_occluder').get('path')
FACE_OCCLUDER = onnxruntime.InferenceSession(model_path, providers = apply_execution_provider_options(facefusion.globals.execution_providers)) FACE_OCCLUDER = onnxruntime.InferenceSession(model_path, providers = apply_execution_provider_options(facefusion.globals.execution_providers))
@ -80,17 +82,23 @@ def clear_face_parser() -> None:
def pre_check() -> bool: def pre_check() -> bool:
if not facefusion.globals.skip_download:
download_directory_path = resolve_relative_path('../.assets/models') download_directory_path = resolve_relative_path('../.assets/models')
model_urls =\ model_urls =\
[ [
MODELS.get('face_occluder').get('url'), MODELS.get('face_occluder').get('url'),
MODELS.get('face_parser').get('url'), MODELS.get('face_parser').get('url')
] ]
model_paths =\
[
MODELS.get('face_occluder').get('path'),
MODELS.get('face_parser').get('path')
]
if not facefusion.globals.skip_download:
process_manager.check() process_manager.check()
conditional_download(download_directory_path, model_urls) conditional_download(download_directory_path, model_urls)
process_manager.end() process_manager.end()
return True return all(is_file(model_path) for model_path in model_paths)
@lru_cache(maxsize = None) @lru_cache(maxsize = None)
@ -139,7 +147,7 @@ def create_region_mask(crop_vision_frame : VisionFrame, face_mask_regions : List
def create_mouth_mask(face_landmark_68 : FaceLandmark68) -> 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)) convex_hull = cv2.convexHull(face_landmark_68[numpy.r_[3:14, 31:36]].astype(numpy.int32))
mouth_mask : Mask = numpy.zeros((512, 512), dtype = numpy.float32) mouth_mask : Mask = numpy.zeros((512, 512)).astype(numpy.float32)
mouth_mask = cv2.fillConvexPoly(mouth_mask, convex_hull, 1.0) mouth_mask = cv2.fillConvexPoly(mouth_mask, convex_hull, 1.0)
mouth_mask = cv2.erode(mouth_mask.clip(0, 1), numpy.ones((21, 3))) 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) mouth_mask = cv2.GaussianBlur(mouth_mask, (0, 0), sigmaX = 1, sigmaY = 15)

View File

@ -1,20 +1,24 @@
from typing import List, Optional from typing import List, Optional
import os
import subprocess import subprocess
import filetype import filetype
import facefusion.globals import facefusion.globals
from facefusion import process_manager from facefusion import logger, process_manager
from facefusion.typing import OutputVideoPreset, Fps, AudioBuffer from facefusion.typing import OutputVideoPreset, Fps, AudioBuffer
from facefusion.filesystem import get_temp_frames_pattern, get_temp_output_video_path from facefusion.filesystem import get_temp_frames_pattern, get_temp_output_video_path
from facefusion.vision import restrict_video_fps
def run_ffmpeg(args : List[str]) -> bool: def run_ffmpeg(args : List[str]) -> bool:
commands = [ 'ffmpeg', '-hide_banner', '-loglevel', 'quiet' ] commands = [ 'ffmpeg', '-hide_banner', '-loglevel', 'error' ]
commands.extend(args) commands.extend(args)
process = subprocess.Popen(commands, stdout = subprocess.PIPE) process = subprocess.Popen(commands, stderr = subprocess.PIPE, stdout = subprocess.PIPE)
while process_manager.is_processing(): while process_manager.is_processing():
try: try:
if facefusion.globals.log_level == 'debug':
log_debug(process)
return process.wait(timeout = 0.5) == 0 return process.wait(timeout = 0.5) == 0
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
continue continue
@ -27,6 +31,15 @@ def open_ffmpeg(args : List[str]) -> subprocess.Popen[bytes]:
return subprocess.Popen(commands, stdin = subprocess.PIPE, stdout = subprocess.PIPE) return subprocess.Popen(commands, stdin = subprocess.PIPE, stdout = subprocess.PIPE)
def log_debug(process : subprocess.Popen[bytes]) -> None:
_, stderr = process.communicate()
errors = stderr.decode().split(os.linesep)
for error in errors:
if error.strip():
logger.debug(error.strip(), __name__.upper())
def extract_frames(target_path : str, temp_video_resolution : str, temp_video_fps : Fps) -> bool: def extract_frames(target_path : str, temp_video_resolution : str, temp_video_fps : Fps) -> bool:
trim_frame_start = facefusion.globals.trim_frame_start trim_frame_start = facefusion.globals.trim_frame_start
trim_frame_end = facefusion.globals.trim_frame_end trim_frame_end = facefusion.globals.trim_frame_end
@ -46,9 +59,10 @@ def extract_frames(target_path : str, temp_video_resolution : str, temp_video_fp
def merge_video(target_path : str, output_video_resolution : str, output_video_fps : Fps) -> bool: def merge_video(target_path : str, output_video_resolution : str, output_video_fps : Fps) -> bool:
temp_video_fps = restrict_video_fps(target_path, output_video_fps)
temp_output_video_path = get_temp_output_video_path(target_path) temp_output_video_path = get_temp_output_video_path(target_path)
temp_frames_pattern = get_temp_frames_pattern(target_path, '%04d') temp_frames_pattern = get_temp_frames_pattern(target_path, '%04d')
commands = [ '-hwaccel', 'auto', '-s', str(output_video_resolution), '-r', str(output_video_fps), '-i', temp_frames_pattern, '-c:v', facefusion.globals.output_video_encoder ] commands = [ '-hwaccel', 'auto', '-s', str(output_video_resolution), '-r', str(temp_video_fps), '-i', temp_frames_pattern, '-c:v', facefusion.globals.output_video_encoder ]
if facefusion.globals.output_video_encoder in [ 'libx264', 'libx265' ]: if facefusion.globals.output_video_encoder in [ 'libx264', 'libx265' ]:
output_video_compression = round(51 - (facefusion.globals.output_video_quality * 0.51)) output_video_compression = round(51 - (facefusion.globals.output_video_quality * 0.51))
@ -62,7 +76,7 @@ def merge_video(target_path : str, output_video_resolution : str, output_video_f
if facefusion.globals.output_video_encoder in [ 'h264_amf', 'hevc_amf' ]: if facefusion.globals.output_video_encoder in [ 'h264_amf', 'hevc_amf' ]:
output_video_compression = round(51 - (facefusion.globals.output_video_quality * 0.51)) output_video_compression = round(51 - (facefusion.globals.output_video_quality * 0.51))
commands.extend([ '-qp_i', str(output_video_compression), '-qp_p', str(output_video_compression), '-quality', map_amf_preset(facefusion.globals.output_video_preset) ]) commands.extend([ '-qp_i', str(output_video_compression), '-qp_p', str(output_video_compression), '-quality', map_amf_preset(facefusion.globals.output_video_preset) ])
commands.extend([ '-pix_fmt', 'yuv420p', '-colorspace', 'bt709', '-y', temp_output_video_path ]) commands.extend([ '-vf', 'framerate=fps=' + str(output_video_fps), '-pix_fmt', 'yuv420p', '-colorspace', 'bt709', '-y', temp_output_video_path ])
return run_ffmpeg(commands) return run_ffmpeg(commands)
@ -79,8 +93,8 @@ def finalize_image(output_path : str, output_image_resolution : str) -> bool:
return run_ffmpeg(commands) return run_ffmpeg(commands)
def read_audio_buffer(target_path : str, sample_rate : int, total_channel : int) -> Optional[AudioBuffer]: def read_audio_buffer(target_path : str, sample_rate : int, channel_total : int) -> Optional[AudioBuffer]:
commands = [ '-i', target_path, '-vn', '-f', 's16le', '-acodec', 'pcm_s16le', '-ar', str(sample_rate), '-ac', str(total_channel), '-' ] commands = [ '-i', target_path, '-vn', '-f', 's16le', '-acodec', 'pcm_s16le', '-ar', str(sample_rate), '-ac', str(channel_total), '-']
process = open_ffmpeg(commands) process = open_ffmpeg(commands)
audio_buffer, _ = process.communicate() audio_buffer, _ = process.communicate()
if process.returncode == 0: if process.returncode == 0:
@ -106,25 +120,17 @@ def restore_audio(target_path : str, output_path : str, output_video_fps : Fps)
def replace_audio(target_path : str, audio_path : str, output_path : str) -> bool: def replace_audio(target_path : str, audio_path : str, output_path : str) -> bool:
temp_output_path = get_temp_output_video_path(target_path) temp_output_path = get_temp_output_video_path(target_path)
commands = [ '-hwaccel', 'auto', '-i', temp_output_path, '-i', audio_path, '-c:v', 'copy', '-af', 'apad', '-shortest', '-map', '0:v:0', '-map', '1:a:0', '-y', output_path ] commands = [ '-hwaccel', 'auto', '-i', temp_output_path, '-i', audio_path, '-c:v', 'copy', '-af', 'apad', '-map', '0:v:0', '-map', '1:a:0', '-shortest', '-y', output_path ]
return run_ffmpeg(commands) return run_ffmpeg(commands)
def map_nvenc_preset(output_video_preset : OutputVideoPreset) -> Optional[str]: def map_nvenc_preset(output_video_preset : OutputVideoPreset) -> Optional[str]:
if output_video_preset in [ 'ultrafast', 'superfast', 'veryfast' ]: if output_video_preset in [ 'ultrafast', 'superfast', 'veryfast', 'faster', 'fast' ]:
return 'p1' return 'fast'
if output_video_preset == 'faster':
return 'p2'
if output_video_preset == 'fast':
return 'p3'
if output_video_preset == 'medium': if output_video_preset == 'medium':
return 'p4' return 'medium'
if output_video_preset == 'slow': if output_video_preset in [ 'slow', 'slower', 'veryslow' ]:
return 'p5' return 'slow'
if output_video_preset == 'slower':
return 'p6'
if output_video_preset == 'veryslow':
return 'p7'
return None return None

View File

@ -7,6 +7,7 @@ source_paths : Optional[List[str]] = None
target_path : Optional[str] = None target_path : Optional[str] = None
output_path : Optional[str] = None output_path : Optional[str] = None
# misc # misc
force_download : Optional[bool] = None
skip_download : Optional[bool] = None skip_download : Optional[bool] = None
headless : Optional[bool] = None headless : Optional[bool] = None
log_level : Optional[LogLevel] = None log_level : Optional[LogLevel] = None

View File

@ -9,26 +9,29 @@ from argparse import ArgumentParser, HelpFormatter
from facefusion import metadata, wording from facefusion import metadata, wording
if platform.system().lower() == 'darwin':
os.environ['SYSTEM_VERSION_COMPAT'] = '0'
ONNXRUNTIMES : Dict[str, Tuple[str, str]] = {} ONNXRUNTIMES : Dict[str, Tuple[str, str]] = {}
if platform.system().lower() == 'darwin': if platform.system().lower() == 'darwin':
ONNXRUNTIMES['default'] = ('onnxruntime', '1.17.1') ONNXRUNTIMES['default'] = ('onnxruntime', '1.17.1')
else: else:
ONNXRUNTIMES['default'] = ('onnxruntime', '1.16.3') ONNXRUNTIMES['default'] = ('onnxruntime', '1.17.1')
ONNXRUNTIMES['cuda-12.2'] = ('onnxruntime-gpu', '1.17.1') ONNXRUNTIMES['cuda-12.2'] = ('onnxruntime-gpu', '1.17.1')
ONNXRUNTIMES['cuda-11.8'] = ('onnxruntime-gpu', '1.16.3') ONNXRUNTIMES['cuda-11.8'] = ('onnxruntime-gpu', '1.17.1')
ONNXRUNTIMES['openvino'] = ('onnxruntime-openvino', '1.16.0') ONNXRUNTIMES['openvino'] = ('onnxruntime-openvino', '1.17.1')
if platform.system().lower() == 'linux': if platform.system().lower() == 'linux':
ONNXRUNTIMES['rocm-5.4.2'] = ('onnxruntime-rocm', '1.16.3') ONNXRUNTIMES['rocm-5.4.2'] = ('onnxruntime-rocm', '1.16.3')
ONNXRUNTIMES['rocm-5.6'] = ('onnxruntime-rocm', '1.16.3') ONNXRUNTIMES['rocm-5.6'] = ('onnxruntime-rocm', '1.16.3')
if platform.system().lower() == 'windows': if platform.system().lower() == 'windows':
ONNXRUNTIMES['directml'] = ('onnxruntime-directml', '1.16.0') ONNXRUNTIMES['directml'] = ('onnxruntime-directml', '1.17.1')
def cli() -> None: def cli() -> None:
program = ArgumentParser(formatter_class = lambda prog: HelpFormatter(prog, max_help_position = 130)) program = ArgumentParser(formatter_class = lambda prog: HelpFormatter(prog, max_help_position = 130))
program.add_argument('--onnxruntime', help = wording.get('help.install_dependency').format(dependency = 'onnxruntime'), choices = ONNXRUNTIMES.keys()) program.add_argument('--onnxruntime', help = wording.get('help.install_dependency').format(dependency = 'onnxruntime'), choices = ONNXRUNTIMES.keys())
program.add_argument('--skip-venv', help = wording.get('help.skip_venv'), action = 'store_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') program.add_argument('-v', '--version', version = metadata.get('name') + ' ' + metadata.get('version'), action = 'version')
run(program) run(program)
@ -37,10 +40,9 @@ def run(program : ArgumentParser) -> None:
args = program.parse_args() args = program.parse_args()
python_id = 'cp' + str(sys.version_info.major) + str(sys.version_info.minor) python_id = 'cp' + str(sys.version_info.major) + str(sys.version_info.minor)
if platform.system().lower() == 'darwin': if not args.skip_conda and 'CONDA_PREFIX' not in os.environ:
os.environ['SYSTEM_VERSION_COMPAT'] = '0' sys.stdout.write(wording.get('conda_not_activated') + os.linesep)
if not args.skip_venv: sys.exit(1)
os.environ['PIP_REQUIRE_VIRTUALENV'] = '1'
if args.onnxruntime: if args.onnxruntime:
answers =\ answers =\
{ {

View File

@ -2,7 +2,7 @@ METADATA =\
{ {
'name': 'FaceFusion', 'name': 'FaceFusion',
'description': 'Next generation face swapper and enhancer', 'description': 'Next generation face swapper and enhancer',
'version': '2.4.1', 'version': '2.5.0',
'license': 'MIT', 'license': 'MIT',
'author': 'Henry Ruhs', 'author': 'Henry Ruhs',
'url': 'https://facefusion.io' 'url': 'https://facefusion.io'

View File

@ -1,13 +1,15 @@
from typing import List from typing import List
from facefusion.common_helper import create_int_range from facefusion.common_helper import create_int_range
from facefusion.processors.frame.typings import FaceDebuggerItem, FaceEnhancerModel, FaceSwapperModel, FrameEnhancerModel, LipSyncerModel from facefusion.processors.frame.typings import FaceDebuggerItem, FaceEnhancerModel, FaceSwapperModel, FrameColorizerModel, FrameEnhancerModel, LipSyncerModel
face_debugger_items : List[FaceDebuggerItem] = [ 'bounding-box', 'face-landmark-5', 'face-landmark-5/68', 'face-landmark-68', 'face-mask', 'face-detector-score', 'face-landmarker-score', 'age', 'gender' ] face_debugger_items : List[FaceDebuggerItem] = [ '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' ]
face_enhancer_models : List[FaceEnhancerModel] = [ 'codeformer', 'gfpgan_1.2', 'gfpgan_1.3', 'gfpgan_1.4', 'gpen_bfr_256', 'gpen_bfr_512', 'restoreformer_plus_plus' ] face_enhancer_models : List[FaceEnhancerModel] = [ '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' ]
face_swapper_models : List[FaceSwapperModel] = [ 'blendswap_256', 'inswapper_128', 'inswapper_128_fp16', 'simswap_256', 'simswap_512_unofficial', 'uniface_256' ] face_swapper_models : List[FaceSwapperModel] = [ 'blendswap_256', 'inswapper_128', 'inswapper_128_fp16', 'simswap_256', 'simswap_512_unofficial', 'uniface_256' ]
frame_enhancer_models : List[FrameEnhancerModel] = [ 'lsdir_x4', 'nomos8k_sc_x4', 'real_esrgan_x4', 'real_esrgan_x4_fp16', 'span_kendata_x4' ] frame_colorizer_models : List[FrameColorizerModel] = [ 'ddcolor', 'ddcolor_artistic', 'deoldify_artistic' ]
frame_enhancer_models : List[FrameEnhancerModel] = [ 'lsdir_x4', 'nomos8k_sc_x4', 'real_esrgan_x2', 'real_esrgan_x2_fp16', 'real_esrgan_x4', 'real_esrgan_x4_fp16', 'real_hatgan_x4', 'span_kendata_x4' ]
lip_syncer_models : List[LipSyncerModel] = [ 'wav2lip_gan' ] lip_syncer_models : List[LipSyncerModel] = [ 'wav2lip_gan' ]
face_enhancer_blend_range : List[int] = create_int_range(0, 100, 1) face_enhancer_blend_range : List[int] = create_int_range(0, 100, 1)
frame_colorizer_blend_range : List[int] = create_int_range(0, 100, 1)
frame_enhancer_blend_range : List[int] = create_int_range(0, 100, 1) frame_enhancer_blend_range : List[int] = create_int_range(0, 100, 1)

View File

@ -1,11 +1,13 @@
from typing import List, Optional from typing import List, Optional
from facefusion.processors.frame.typings import FaceDebuggerItem, FaceEnhancerModel, FaceSwapperModel, FrameEnhancerModel, LipSyncerModel from facefusion.processors.frame.typings import FaceDebuggerItem, FaceEnhancerModel, FaceSwapperModel, FrameColorizerModel, FrameEnhancerModel, LipSyncerModel
face_debugger_items : Optional[List[FaceDebuggerItem]] = None face_debugger_items : Optional[List[FaceDebuggerItem]] = None
face_enhancer_model : Optional[FaceEnhancerModel] = None face_enhancer_model : Optional[FaceEnhancerModel] = None
face_enhancer_blend : Optional[int] = None face_enhancer_blend : Optional[int] = None
face_swapper_model : Optional[FaceSwapperModel] = None face_swapper_model : Optional[FaceSwapperModel] = None
frame_colorizer_model : Optional[FrameColorizerModel] = None
frame_colorizer_blend : Optional[int] = None
frame_enhancer_model : Optional[FrameEnhancerModel] = None frame_enhancer_model : Optional[FrameEnhancerModel] = None
frame_enhancer_blend : Optional[int] = None frame_enhancer_blend : Optional[int] = None
lip_syncer_model : Optional[LipSyncerModel] = None lip_syncer_model : Optional[LipSyncerModel] = None

View File

@ -11,7 +11,7 @@ from facefusion.face_masker import create_static_box_mask, create_occlusion_mask
from facefusion.face_helper import warp_face_by_face_landmark_5, categorize_age, categorize_gender from facefusion.face_helper import warp_face_by_face_landmark_5, categorize_age, categorize_gender
from facefusion.face_store import get_reference_faces from facefusion.face_store import get_reference_faces
from facefusion.content_analyser import clear_content_analyser from facefusion.content_analyser import clear_content_analyser
from facefusion.typing import Face, VisionFrame, UpdateProcess, ProcessMode, QueuePayload from facefusion.typing import Face, VisionFrame, UpdateProgress, ProcessMode, QueuePayload
from facefusion.vision import read_image, read_static_image, write_image from facefusion.vision import read_image, read_static_image, write_image
from facefusion.processors.frame.typings import FaceDebuggerInputs from facefusion.processors.frame.typings import FaceDebuggerInputs
from facefusion.processors.frame import globals as frame_processors_globals, choices as frame_processors_choices from facefusion.processors.frame import globals as frame_processors_globals, choices as frame_processors_choices
@ -74,6 +74,7 @@ def debug_face(target_face : Face, temp_vision_frame : VisionFrame) -> VisionFra
bounding_box = target_face.bounding_box.astype(numpy.int32) bounding_box = target_face.bounding_box.astype(numpy.int32)
temp_vision_frame = temp_vision_frame.copy() temp_vision_frame = temp_vision_frame.copy()
has_face_landmark_5_fallback = numpy.array_equal(target_face.landmarks.get('5'), target_face.landmarks.get('5/68')) has_face_landmark_5_fallback = numpy.array_equal(target_face.landmarks.get('5'), target_face.landmarks.get('5/68'))
has_face_landmark_68_fallback = numpy.array_equal(target_face.landmarks.get('68'), target_face.landmarks.get('68/5'))
if 'bounding-box' in frame_processors_globals.face_debugger_items: if 'bounding-box' in frame_processors_globals.face_debugger_items:
cv2.rectangle(temp_vision_frame, (bounding_box[0], bounding_box[1]), (bounding_box[2], bounding_box[3]), primary_color, 2) cv2.rectangle(temp_vision_frame, (bounding_box[0], bounding_box[1]), (bounding_box[2], bounding_box[3]), primary_color, 2)
@ -109,7 +110,11 @@ def debug_face(target_face : Face, temp_vision_frame : VisionFrame) -> VisionFra
if 'face-landmark-68' in frame_processors_globals.face_debugger_items and numpy.any(target_face.landmarks.get('68')): if 'face-landmark-68' in frame_processors_globals.face_debugger_items and numpy.any(target_face.landmarks.get('68')):
face_landmark_68 = target_face.landmarks.get('68').astype(numpy.int32) face_landmark_68 = target_face.landmarks.get('68').astype(numpy.int32)
for index in range(face_landmark_68.shape[0]): for index in range(face_landmark_68.shape[0]):
cv2.circle(temp_vision_frame, (face_landmark_68[index][0], face_landmark_68[index][1]), 3, secondary_color, -1) cv2.circle(temp_vision_frame, (face_landmark_68[index][0], face_landmark_68[index][1]), 3, tertiary_color if has_face_landmark_68_fallback else secondary_color, -1)
if 'face-landmark-68/5' in frame_processors_globals.face_debugger_items and numpy.any(target_face.landmarks.get('68')):
face_landmark_68 = target_face.landmarks.get('68/5').astype(numpy.int32)
for index in range(face_landmark_68.shape[0]):
cv2.circle(temp_vision_frame, (face_landmark_68[index][0], face_landmark_68[index][1]), 3, primary_color, -1)
if bounding_box[3] - bounding_box[1] > 50 and bounding_box[2] - bounding_box[0] > 50: if bounding_box[3] - bounding_box[1] > 50 and bounding_box[2] - bounding_box[0] > 50:
top = bounding_box[1] top = bounding_box[1]
left = bounding_box[0] - 20 left = bounding_box[0] - 20
@ -157,7 +162,7 @@ def process_frame(inputs : FaceDebuggerInputs) -> VisionFrame:
return target_vision_frame return target_vision_frame
def process_frames(source_paths : List[str], queue_payloads : List[QueuePayload], update_progress : UpdateProcess) -> None: def process_frames(source_paths : List[str], queue_payloads : List[QueuePayload], update_progress : UpdateProgress) -> None:
reference_faces = get_reference_faces() if 'reference' in facefusion.globals.face_selector_mode else None reference_faces = get_reference_faces() if 'reference' in facefusion.globals.face_selector_mode else None
for queue_payload in process_manager.manage(queue_payloads): for queue_payload in process_manager.manage(queue_payloads):
@ -169,7 +174,7 @@ def process_frames(source_paths : List[str], queue_payloads : List[QueuePayload]
'target_vision_frame': target_vision_frame 'target_vision_frame': target_vision_frame
}) })
write_image(target_vision_path, output_vision_frame) write_image(target_vision_path, output_vision_frame)
update_progress() update_progress(1)
def process_image(source_paths : List[str], target_path : str, output_path : str) -> None: def process_image(source_paths : List[str], target_path : str, output_path : str) -> None:

View File

@ -16,7 +16,7 @@ from facefusion.execution import apply_execution_provider_options
from facefusion.content_analyser import clear_content_analyser from facefusion.content_analyser import clear_content_analyser
from facefusion.face_store import get_reference_faces from facefusion.face_store import get_reference_faces
from facefusion.normalizer import normalize_output_path from facefusion.normalizer import normalize_output_path
from facefusion.typing import Face, VisionFrame, UpdateProcess, ProcessMode, ModelSet, OptionsWithModel, QueuePayload from facefusion.typing import Face, VisionFrame, UpdateProgress, ProcessMode, ModelSet, OptionsWithModel, QueuePayload
from facefusion.common_helper import create_metavar from facefusion.common_helper import create_metavar
from facefusion.filesystem import is_file, is_image, is_video, resolve_relative_path from facefusion.filesystem import is_file, is_image, is_video, resolve_relative_path
from facefusion.download import conditional_download, is_download_done from facefusion.download import conditional_download, is_download_done
@ -73,6 +73,20 @@ MODELS : ModelSet =\
'template': 'ffhq_512', 'template': 'ffhq_512',
'size': (512, 512) 'size': (512, 512)
}, },
'gpen_bfr_1024':
{
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/gpen_bfr_1024.onnx',
'path': resolve_relative_path('../.assets/models/gpen_bfr_1024.onnx'),
'template': 'ffhq_512',
'size': (1024, 1024)
},
'gpen_bfr_2048':
{
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/gpen_bfr_2048.onnx',
'path': resolve_relative_path('../.assets/models/gpen_bfr_2048.onnx'),
'template': 'ffhq_512',
'size': (2048, 2048)
},
'restoreformer_plus_plus': 'restoreformer_plus_plus':
{ {
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/restoreformer_plus_plus.onnx', 'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/restoreformer_plus_plus.onnx',
@ -131,22 +145,25 @@ def apply_args(program : ArgumentParser) -> None:
def pre_check() -> bool: def pre_check() -> bool:
if not facefusion.globals.skip_download:
download_directory_path = resolve_relative_path('../.assets/models') download_directory_path = resolve_relative_path('../.assets/models')
model_url = get_options('model').get('url') model_url = get_options('model').get('url')
model_path = get_options('model').get('path')
if not facefusion.globals.skip_download:
process_manager.check() process_manager.check()
conditional_download(download_directory_path, [ model_url ]) conditional_download(download_directory_path, [ model_url ])
process_manager.end() process_manager.end()
return True return is_file(model_path)
def post_check() -> bool: def post_check() -> bool:
model_url = get_options('model').get('url') model_url = get_options('model').get('url')
model_path = get_options('model').get('path') model_path = get_options('model').get('path')
if not facefusion.globals.skip_download and not is_download_done(model_url, model_path): if not facefusion.globals.skip_download and not is_download_done(model_url, model_path):
logger.error(wording.get('model_download_not_done') + wording.get('exclamation_mark'), NAME) logger.error(wording.get('model_download_not_done') + wording.get('exclamation_mark'), NAME)
return False return False
elif not is_file(model_path): if not is_file(model_path):
logger.error(wording.get('model_file_not_present') + wording.get('exclamation_mark'), NAME) logger.error(wording.get('model_file_not_present') + wording.get('exclamation_mark'), NAME)
return False return False
return True return True
@ -202,7 +219,7 @@ def apply_enhance(crop_vision_frame : VisionFrame) -> VisionFrame:
if frame_processor_input.name == 'input': if frame_processor_input.name == 'input':
frame_processor_inputs[frame_processor_input.name] = crop_vision_frame frame_processor_inputs[frame_processor_input.name] = crop_vision_frame
if frame_processor_input.name == 'weight': if frame_processor_input.name == 'weight':
weight = numpy.array([ 1 ], dtype = numpy.double) weight = numpy.array([ 1 ]).astype(numpy.double)
frame_processor_inputs[frame_processor_input.name] = weight frame_processor_inputs[frame_processor_input.name] = weight
with THREAD_SEMAPHORE: with THREAD_SEMAPHORE:
crop_vision_frame = frame_processor.run(None, frame_processor_inputs)[0][0] crop_vision_frame = frame_processor.run(None, frame_processor_inputs)[0][0]
@ -256,7 +273,7 @@ def process_frame(inputs : FaceEnhancerInputs) -> VisionFrame:
return target_vision_frame return target_vision_frame
def process_frames(source_path : List[str], queue_payloads : List[QueuePayload], update_progress : UpdateProcess) -> None: def process_frames(source_path : List[str], queue_payloads : List[QueuePayload], update_progress : UpdateProgress) -> None:
reference_faces = get_reference_faces() if 'reference' in facefusion.globals.face_selector_mode else None reference_faces = get_reference_faces() if 'reference' in facefusion.globals.face_selector_mode else None
for queue_payload in process_manager.manage(queue_payloads): for queue_payload in process_manager.manage(queue_payloads):
@ -268,7 +285,7 @@ def process_frames(source_path : List[str], queue_payloads : List[QueuePayload],
'target_vision_frame': target_vision_frame 'target_vision_frame': target_vision_frame
}) })
write_image(target_vision_path, output_vision_frame) write_image(target_vision_path, output_vision_frame)
update_progress() update_progress(1)
def process_image(source_path : str, target_path : str, output_path : str) -> None: def process_image(source_path : str, target_path : str, output_path : str) -> None:

View File

@ -1,6 +1,7 @@
from typing import Any, List, Literal, Optional from typing import Any, List, Literal, Optional
from argparse import ArgumentParser from argparse import ArgumentParser
from time import sleep from time import sleep
import platform
import threading import threading
import numpy import numpy
import onnx import onnx
@ -15,10 +16,9 @@ from facefusion.face_analyser import get_one_face, get_average_face, get_many_fa
from facefusion.face_masker import create_static_box_mask, create_occlusion_mask, create_region_mask, clear_face_occluder, clear_face_parser from facefusion.face_masker import create_static_box_mask, create_occlusion_mask, create_region_mask, clear_face_occluder, clear_face_parser
from facefusion.face_helper import warp_face_by_face_landmark_5, paste_back from facefusion.face_helper import warp_face_by_face_landmark_5, paste_back
from facefusion.face_store import get_reference_faces from facefusion.face_store import get_reference_faces
from facefusion.common_helper import extract_major_version
from facefusion.content_analyser import clear_content_analyser from facefusion.content_analyser import clear_content_analyser
from facefusion.normalizer import normalize_output_path from facefusion.normalizer import normalize_output_path
from facefusion.typing import Face, Embedding, VisionFrame, UpdateProcess, ProcessMode, ModelSet, OptionsWithModel, QueuePayload from facefusion.typing import Face, Embedding, VisionFrame, UpdateProgress, ProcessMode, ModelSet, OptionsWithModel, QueuePayload
from facefusion.filesystem import is_file, is_image, has_image, is_video, filter_image_paths, resolve_relative_path from facefusion.filesystem import is_file, is_image, has_image, is_video, filter_image_paths, resolve_relative_path
from facefusion.download import conditional_download, is_download_done from facefusion.download import conditional_download, is_download_done
from facefusion.vision import read_image, read_static_image, read_static_images, write_image from facefusion.vision import read_image, read_static_image, read_static_images, write_image
@ -27,7 +27,7 @@ from facefusion.processors.frame import globals as frame_processors_globals
from facefusion.processors.frame import choices as frame_processors_choices from facefusion.processors.frame import choices as frame_processors_choices
FRAME_PROCESSOR = None FRAME_PROCESSOR = None
MODEL_MATRIX = None MODEL_INITIALIZER = None
THREAD_LOCK : threading.Lock = threading.Lock() THREAD_LOCK : threading.Lock = threading.Lock()
NAME = __name__.upper() NAME = __name__.upper()
MODELS : ModelSet =\ MODELS : ModelSet =\
@ -114,23 +114,23 @@ def clear_frame_processor() -> None:
FRAME_PROCESSOR = None FRAME_PROCESSOR = None
def get_model_matrix() -> Any: def get_model_initializer() -> Any:
global MODEL_MATRIX global MODEL_INITIALIZER
with THREAD_LOCK: with THREAD_LOCK:
while process_manager.is_checking(): while process_manager.is_checking():
sleep(0.5) sleep(0.5)
if MODEL_MATRIX is None: if MODEL_INITIALIZER is None:
model_path = get_options('model').get('path') model_path = get_options('model').get('path')
model = onnx.load(model_path) model = onnx.load(model_path)
MODEL_MATRIX = numpy_helper.to_array(model.graph.initializer[-1]) MODEL_INITIALIZER = numpy_helper.to_array(model.graph.initializer[-1])
return MODEL_MATRIX return MODEL_INITIALIZER
def clear_model_matrix() -> None: def clear_model_initializer() -> None:
global MODEL_MATRIX global MODEL_INITIALIZER
MODEL_MATRIX = None MODEL_INITIALIZER = None
def get_options(key : Literal['model']) -> Any: def get_options(key : Literal['model']) -> Any:
@ -151,8 +151,7 @@ def set_options(key : Literal['model'], value : Any) -> None:
def register_args(program : ArgumentParser) -> None: def register_args(program : ArgumentParser) -> None:
onnxruntime_version = extract_major_version(onnxruntime.__version__) if platform.system().lower() == 'darwin':
if onnxruntime_version > (1, 16):
face_swapper_model_fallback = 'inswapper_128' face_swapper_model_fallback = 'inswapper_128'
else: else:
face_swapper_model_fallback = 'inswapper_128_fp16' face_swapper_model_fallback = 'inswapper_128_fp16'
@ -173,22 +172,25 @@ def apply_args(program : ArgumentParser) -> None:
def pre_check() -> bool: def pre_check() -> bool:
if not facefusion.globals.skip_download:
download_directory_path = resolve_relative_path('../.assets/models') download_directory_path = resolve_relative_path('../.assets/models')
model_url = get_options('model').get('url') model_url = get_options('model').get('url')
model_path = get_options('model').get('path')
if not facefusion.globals.skip_download:
process_manager.check() process_manager.check()
conditional_download(download_directory_path, [ model_url ]) conditional_download(download_directory_path, [ model_url ])
process_manager.end() process_manager.end()
return True return is_file(model_path)
def post_check() -> bool: def post_check() -> bool:
model_url = get_options('model').get('url') model_url = get_options('model').get('url')
model_path = get_options('model').get('path') model_path = get_options('model').get('path')
if not facefusion.globals.skip_download and not is_download_done(model_url, model_path): if not facefusion.globals.skip_download and not is_download_done(model_url, model_path):
logger.error(wording.get('model_download_not_done') + wording.get('exclamation_mark'), NAME) logger.error(wording.get('model_download_not_done') + wording.get('exclamation_mark'), NAME)
return False return False
elif not is_file(model_path): if not is_file(model_path):
logger.error(wording.get('model_file_not_present') + wording.get('exclamation_mark'), NAME) logger.error(wording.get('model_file_not_present') + wording.get('exclamation_mark'), NAME)
return False return False
return True return True
@ -216,8 +218,8 @@ def pre_process(mode : ProcessMode) -> bool:
def post_process() -> None: def post_process() -> None:
read_static_image.cache_clear() read_static_image.cache_clear()
if facefusion.globals.video_memory_strategy == 'strict' or facefusion.globals.video_memory_strategy == 'moderate': if facefusion.globals.video_memory_strategy == 'strict' or facefusion.globals.video_memory_strategy == 'moderate':
clear_model_initializer()
clear_frame_processor() clear_frame_processor()
clear_model_matrix()
if facefusion.globals.video_memory_strategy == 'strict': if facefusion.globals.video_memory_strategy == 'strict':
clear_face_analyser() clear_face_analyser()
clear_content_analyser() clear_content_analyser()
@ -281,9 +283,9 @@ def prepare_source_frame(source_face : Face) -> VisionFrame:
def prepare_source_embedding(source_face : Face) -> Embedding: def prepare_source_embedding(source_face : Face) -> Embedding:
model_type = get_options('model').get('type') model_type = get_options('model').get('type')
if model_type == 'inswapper': if model_type == 'inswapper':
model_matrix = get_model_matrix() model_initializer = get_model_initializer()
source_embedding = source_face.embedding.reshape((1, -1)) source_embedding = source_face.embedding.reshape((1, -1))
source_embedding = numpy.dot(source_embedding, model_matrix) / numpy.linalg.norm(source_embedding) source_embedding = numpy.dot(source_embedding, model_initializer) / numpy.linalg.norm(source_embedding)
else: else:
source_embedding = source_face.normed_embedding.reshape(1, -1) source_embedding = source_face.normed_embedding.reshape(1, -1)
return source_embedding return source_embedding
@ -332,7 +334,7 @@ def process_frame(inputs : FaceSwapperInputs) -> VisionFrame:
return target_vision_frame return target_vision_frame
def process_frames(source_paths : List[str], queue_payloads : List[QueuePayload], update_progress : UpdateProcess) -> None: def process_frames(source_paths : List[str], queue_payloads : List[QueuePayload], update_progress : UpdateProgress) -> None:
reference_faces = get_reference_faces() if 'reference' in facefusion.globals.face_selector_mode else None reference_faces = get_reference_faces() if 'reference' in facefusion.globals.face_selector_mode else None
source_frames = read_static_images(source_paths) source_frames = read_static_images(source_paths)
source_face = get_average_face(source_frames) source_face = get_average_face(source_frames)
@ -347,7 +349,7 @@ def process_frames(source_paths : List[str], queue_payloads : List[QueuePayload]
'target_vision_frame': target_vision_frame 'target_vision_frame': target_vision_frame
}) })
write_image(target_vision_path, output_vision_frame) write_image(target_vision_path, output_vision_frame)
update_progress() update_progress(1)
def process_image(source_paths : List[str], target_path : str, output_path : str) -> None: def process_image(source_paths : List[str], target_path : str, output_path : str) -> None:

View File

@ -0,0 +1,232 @@
from typing import Any, List, Literal, Optional
from argparse import ArgumentParser
from time import sleep
import threading
import cv2
import numpy
import onnxruntime
import facefusion.globals
import facefusion.processors.frame.core as frame_processors
from facefusion import config, process_manager, logger, wording
from facefusion.face_analyser import clear_face_analyser
from facefusion.content_analyser import clear_content_analyser
from facefusion.execution import apply_execution_provider_options
from facefusion.normalizer import normalize_output_path
from facefusion.typing import Face, VisionFrame, UpdateProgress, ProcessMode, ModelSet, OptionsWithModel, QueuePayload
from facefusion.common_helper import create_metavar
from facefusion.filesystem import is_file, resolve_relative_path, is_image, is_video
from facefusion.download import conditional_download, is_download_done
from facefusion.vision import read_image, read_static_image, write_image
from facefusion.processors.frame.typings import FrameColorizerInputs
from facefusion.processors.frame import globals as frame_processors_globals
from facefusion.processors.frame import choices as frame_processors_choices
FRAME_PROCESSOR = None
THREAD_LOCK : threading.Lock = threading.Lock()
THREAD_SEMAPHORE : threading.Semaphore = threading.Semaphore()
NAME = __name__.upper()
MODELS : ModelSet =\
{
'ddcolor':
{
'type': 'ddcolor',
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/ddcolor.onnx',
'path': resolve_relative_path('../.assets/models/ddcolor.onnx'),
'size': (512, 512)
},
'ddcolor_artistic':
{
'type': 'ddcolor',
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/ddcolor_artistic.onnx',
'path': resolve_relative_path('../.assets/models/ddcolor_artistic.onnx'),
'size': (512, 512)
},
'deoldify_artistic':
{
'type': 'deoldify',
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/deoldify_artistic.onnx',
'path': resolve_relative_path('../.assets/models/deoldify_artistic.onnx'),
'size': (512, 512)
}
}
OPTIONS : Optional[OptionsWithModel] = None
def get_frame_processor() -> Any:
global FRAME_PROCESSOR
with THREAD_LOCK:
while process_manager.is_checking():
sleep(0.5)
if FRAME_PROCESSOR is None:
model_path = get_options('model').get('path')
FRAME_PROCESSOR = onnxruntime.InferenceSession(model_path, providers = apply_execution_provider_options(facefusion.globals.execution_providers))
return FRAME_PROCESSOR
def clear_frame_processor() -> None:
global FRAME_PROCESSOR
FRAME_PROCESSOR = None
def get_options(key : Literal['model']) -> Any:
global OPTIONS
if OPTIONS is None:
OPTIONS =\
{
'model': MODELS[frame_processors_globals.frame_colorizer_model]
}
return OPTIONS.get(key)
def set_options(key : Literal['model'], value : Any) -> None:
global OPTIONS
OPTIONS[key] = value
def register_args(program : ArgumentParser) -> None:
program.add_argument('--frame-colorizer-model', help = wording.get('help.frame_colorizer_model'), default = config.get_str_value('frame_processors.frame_colorizer_model', 'ddcolor'), choices = frame_processors_choices.frame_colorizer_models)
program.add_argument('--frame-colorizer-blend', help = wording.get('help.frame_colorizer_blend'), type = int, default = config.get_int_value('frame_processors.frame_colorizer_blend', '100'), choices = frame_processors_choices.frame_colorizer_blend_range, metavar = create_metavar(frame_processors_choices.frame_colorizer_blend_range))
def apply_args(program : ArgumentParser) -> None:
args = program.parse_args()
frame_processors_globals.frame_colorizer_model = args.frame_colorizer_model
frame_processors_globals.frame_colorizer_blend = args.frame_colorizer_blend
def pre_check() -> bool:
download_directory_path = resolve_relative_path('../.assets/models')
model_url = get_options('model').get('url')
model_path = get_options('model').get('path')
if not facefusion.globals.skip_download:
process_manager.check()
conditional_download(download_directory_path, [ model_url ])
process_manager.end()
return is_file(model_path)
def post_check() -> bool:
model_url = get_options('model').get('url')
model_path = get_options('model').get('path')
if not facefusion.globals.skip_download and not is_download_done(model_url, model_path):
logger.error(wording.get('model_download_not_done') + wording.get('exclamation_mark'), NAME)
return False
if not is_file(model_path):
logger.error(wording.get('model_file_not_present') + wording.get('exclamation_mark'), NAME)
return False
return True
def pre_process(mode : ProcessMode) -> bool:
if mode in [ 'output', 'preview' ] and not is_image(facefusion.globals.target_path) and not is_video(facefusion.globals.target_path):
logger.error(wording.get('select_image_or_video_target') + wording.get('exclamation_mark'), NAME)
return False
if mode == 'output' and not normalize_output_path(facefusion.globals.target_path, facefusion.globals.output_path):
logger.error(wording.get('select_file_or_directory_output') + wording.get('exclamation_mark'), NAME)
return False
return True
def post_process() -> None:
read_static_image.cache_clear()
if facefusion.globals.video_memory_strategy == 'strict' or facefusion.globals.video_memory_strategy == 'moderate':
clear_frame_processor()
if facefusion.globals.video_memory_strategy == 'strict':
clear_face_analyser()
clear_content_analyser()
def colorize_frame(temp_vision_frame : VisionFrame) -> VisionFrame:
frame_processor = get_frame_processor()
prepare_vision_frame = prepare_temp_frame(temp_vision_frame)
with THREAD_SEMAPHORE:
color_vision_frame = frame_processor.run(None,
{
frame_processor.get_inputs()[0].name: prepare_vision_frame
})[0][0]
color_vision_frame = merge_color_frame(temp_vision_frame, color_vision_frame)
color_vision_frame = blend_frame(temp_vision_frame, color_vision_frame)
return color_vision_frame
def prepare_temp_frame(temp_vision_frame : VisionFrame) -> VisionFrame:
model_size = get_options('model').get('size')
model_type = get_options('model').get('type')
temp_vision_frame = cv2.cvtColor(temp_vision_frame, cv2.COLOR_BGR2GRAY)
temp_vision_frame = cv2.cvtColor(temp_vision_frame, cv2.COLOR_GRAY2RGB)
if model_type == 'ddcolor':
temp_vision_frame = (temp_vision_frame / 255.0).astype(numpy.float32)
temp_vision_frame = cv2.cvtColor(temp_vision_frame, cv2.COLOR_RGB2LAB)[:, :, :1]
temp_vision_frame = numpy.concatenate((temp_vision_frame, numpy.zeros_like(temp_vision_frame), numpy.zeros_like(temp_vision_frame)), axis = -1)
temp_vision_frame = cv2.cvtColor(temp_vision_frame, cv2.COLOR_LAB2RGB)
temp_vision_frame = cv2.resize(temp_vision_frame, model_size)
temp_vision_frame = temp_vision_frame.transpose((2, 0, 1))
temp_vision_frame = numpy.expand_dims(temp_vision_frame, axis = 0).astype(numpy.float32)
return temp_vision_frame
def merge_color_frame(temp_vision_frame : VisionFrame, color_vision_frame : VisionFrame) -> VisionFrame:
model_type = get_options('model').get('type')
color_vision_frame = color_vision_frame.transpose(1, 2, 0)
color_vision_frame = cv2.resize(color_vision_frame, (temp_vision_frame.shape[1], temp_vision_frame.shape[0]))
if model_type == 'ddcolor':
temp_vision_frame = (temp_vision_frame / 255.0).astype(numpy.float32)
temp_vision_frame = cv2.cvtColor(temp_vision_frame, cv2.COLOR_BGR2LAB)[:, :, :1]
color_vision_frame = numpy.concatenate((temp_vision_frame, color_vision_frame), axis = -1)
color_vision_frame = cv2.cvtColor(color_vision_frame, cv2.COLOR_LAB2BGR)
color_vision_frame = (color_vision_frame * 255.0).round().astype(numpy.uint8)
if model_type == 'deoldify':
temp_blue_channel, _, _ = cv2.split(temp_vision_frame)
color_vision_frame = cv2.cvtColor(color_vision_frame, cv2.COLOR_BGR2RGB).astype(numpy.uint8)
color_vision_frame = cv2.cvtColor(color_vision_frame, cv2.COLOR_BGR2LAB)
_, color_green_channel, color_red_channel = cv2.split(color_vision_frame)
color_vision_frame = cv2.merge((temp_blue_channel, color_green_channel, color_red_channel))
color_vision_frame = cv2.cvtColor(color_vision_frame, cv2.COLOR_LAB2BGR)
return color_vision_frame
def blend_frame(temp_vision_frame : VisionFrame, paste_vision_frame : VisionFrame) -> VisionFrame:
frame_colorizer_blend = 1 - (frame_processors_globals.frame_colorizer_blend / 100)
temp_vision_frame = cv2.addWeighted(temp_vision_frame, frame_colorizer_blend, paste_vision_frame, 1 - frame_colorizer_blend, 0)
return temp_vision_frame
def get_reference_frame(source_face : Face, target_face : Face, temp_vision_frame : VisionFrame) -> VisionFrame:
pass
def process_frame(inputs : FrameColorizerInputs) -> VisionFrame:
target_vision_frame = inputs.get('target_vision_frame')
return colorize_frame(target_vision_frame)
def process_frames(source_paths : List[str], queue_payloads : List[QueuePayload], update_progress : UpdateProgress) -> None:
for queue_payload in process_manager.manage(queue_payloads):
target_vision_path = queue_payload['frame_path']
target_vision_frame = read_image(target_vision_path)
output_vision_frame = process_frame(
{
'target_vision_frame': target_vision_frame
})
write_image(target_vision_path, output_vision_frame)
update_progress(1)
def process_image(source_paths : List[str], target_path : str, output_path : str) -> None:
target_vision_frame = read_static_image(target_path)
output_vision_frame = process_frame(
{
'target_vision_frame': target_vision_frame
})
write_image(output_path, output_vision_frame)
def process_video(source_paths : List[str], temp_frame_paths : List[str]) -> None:
frame_processors.multi_process_frames(None, temp_frame_paths, process_frames)

View File

@ -13,7 +13,7 @@ from facefusion.face_analyser import clear_face_analyser
from facefusion.content_analyser import clear_content_analyser from facefusion.content_analyser import clear_content_analyser
from facefusion.execution import apply_execution_provider_options from facefusion.execution import apply_execution_provider_options
from facefusion.normalizer import normalize_output_path from facefusion.normalizer import normalize_output_path
from facefusion.typing import Face, VisionFrame, UpdateProcess, ProcessMode, ModelSet, OptionsWithModel, QueuePayload from facefusion.typing import Face, VisionFrame, UpdateProgress, ProcessMode, ModelSet, OptionsWithModel, QueuePayload
from facefusion.common_helper import create_metavar from facefusion.common_helper import create_metavar
from facefusion.filesystem import is_file, resolve_relative_path, is_image, is_video from facefusion.filesystem import is_file, resolve_relative_path, is_image, is_video
from facefusion.download import conditional_download, is_download_done from facefusion.download import conditional_download, is_download_done
@ -41,6 +41,20 @@ MODELS : ModelSet =\
'size': (128, 8, 2), 'size': (128, 8, 2),
'scale': 4 'scale': 4
}, },
'real_esrgan_x2':
{
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/real_esrgan_x2.onnx',
'path': resolve_relative_path('../.assets/models/real_esrgan_x2.onnx'),
'size': (128, 8, 2),
'scale': 2
},
'real_esrgan_x2_fp16':
{
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/real_esrgan_x2_fp16.onnx',
'path': resolve_relative_path('../.assets/models/real_esrgan_x2_fp16.onnx'),
'size': (128, 8, 2),
'scale': 2
},
'real_esrgan_x4': 'real_esrgan_x4':
{ {
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/real_esrgan_x4.onnx', 'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/real_esrgan_x4.onnx',
@ -55,6 +69,13 @@ MODELS : ModelSet =\
'size': (128, 8, 2), 'size': (128, 8, 2),
'scale': 4 'scale': 4
}, },
'real_hatgan_x4':
{
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/real_hatgan_x4.onnx',
'path': resolve_relative_path('../.assets/models/real_hatgan_x4.onnx'),
'size': (256, 8, 2),
'scale': 4
},
'span_kendata_x4': 'span_kendata_x4':
{ {
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/span_kendata_x4.onnx', 'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/span_kendata_x4.onnx',
@ -113,22 +134,25 @@ def apply_args(program : ArgumentParser) -> None:
def pre_check() -> bool: def pre_check() -> bool:
if not facefusion.globals.skip_download:
download_directory_path = resolve_relative_path('../.assets/models') download_directory_path = resolve_relative_path('../.assets/models')
model_url = get_options('model').get('url') model_url = get_options('model').get('url')
model_path = get_options('model').get('path')
if not facefusion.globals.skip_download:
process_manager.check() process_manager.check()
conditional_download(download_directory_path, [ model_url ]) conditional_download(download_directory_path, [ model_url ])
process_manager.end() process_manager.end()
return True return is_file(model_path)
def post_check() -> bool: def post_check() -> bool:
model_url = get_options('model').get('url') model_url = get_options('model').get('url')
model_path = get_options('model').get('path') model_path = get_options('model').get('path')
if not facefusion.globals.skip_download and not is_download_done(model_url, model_path): if not facefusion.globals.skip_download and not is_download_done(model_url, model_path):
logger.error(wording.get('model_download_not_done') + wording.get('exclamation_mark'), NAME) logger.error(wording.get('model_download_not_done') + wording.get('exclamation_mark'), NAME)
return False return False
elif not is_file(model_path): if not is_file(model_path):
logger.error(wording.get('model_file_not_present') + wording.get('exclamation_mark'), NAME) logger.error(wording.get('model_file_not_present') + wording.get('exclamation_mark'), NAME)
return False return False
return True return True
@ -184,10 +208,10 @@ def normalize_tile_frame(vision_tile_frame : VisionFrame) -> VisionFrame:
return vision_tile_frame return vision_tile_frame
def blend_frame(temp_vision_frame : VisionFrame, paste_vision_frame : VisionFrame) -> VisionFrame: def blend_frame(temp_vision_frame : VisionFrame, merge_vision_frame : VisionFrame) -> VisionFrame:
frame_enhancer_blend = 1 - (frame_processors_globals.frame_enhancer_blend / 100) frame_enhancer_blend = 1 - (frame_processors_globals.frame_enhancer_blend / 100)
temp_vision_frame = cv2.resize(temp_vision_frame, (paste_vision_frame.shape[1], paste_vision_frame.shape[0])) temp_vision_frame = cv2.resize(temp_vision_frame, (merge_vision_frame.shape[1], merge_vision_frame.shape[0]))
temp_vision_frame = cv2.addWeighted(temp_vision_frame, frame_enhancer_blend, paste_vision_frame, 1 - frame_enhancer_blend, 0) temp_vision_frame = cv2.addWeighted(temp_vision_frame, frame_enhancer_blend, merge_vision_frame, 1 - frame_enhancer_blend, 0)
return temp_vision_frame return temp_vision_frame
@ -200,7 +224,7 @@ def process_frame(inputs : FrameEnhancerInputs) -> VisionFrame:
return enhance_frame(target_vision_frame) return enhance_frame(target_vision_frame)
def process_frames(source_paths : List[str], queue_payloads : List[QueuePayload], update_progress : UpdateProcess) -> None: def process_frames(source_paths : List[str], queue_payloads : List[QueuePayload], update_progress : UpdateProgress) -> None:
for queue_payload in process_manager.manage(queue_payloads): for queue_payload in process_manager.manage(queue_payloads):
target_vision_path = queue_payload['frame_path'] target_vision_path = queue_payload['frame_path']
target_vision_frame = read_image(target_vision_path) target_vision_frame = read_image(target_vision_path)
@ -209,7 +233,7 @@ def process_frames(source_paths : List[str], queue_payloads : List[QueuePayload]
'target_vision_frame': target_vision_frame 'target_vision_frame': target_vision_frame
}) })
write_image(target_vision_path, output_vision_frame) write_image(target_vision_path, output_vision_frame)
update_progress() update_progress(1)
def process_image(source_paths : List[str], target_path : str, output_path : str) -> None: def process_image(source_paths : List[str], target_path : str, output_path : str) -> None:

View File

@ -16,19 +16,19 @@ from facefusion.face_helper import warp_face_by_face_landmark_5, warp_face_by_bo
from facefusion.face_store import get_reference_faces from facefusion.face_store import get_reference_faces
from facefusion.content_analyser import clear_content_analyser from facefusion.content_analyser import clear_content_analyser
from facefusion.normalizer import normalize_output_path from facefusion.normalizer import normalize_output_path
from facefusion.typing import Face, VisionFrame, UpdateProcess, ProcessMode, ModelSet, OptionsWithModel, AudioFrame, QueuePayload from facefusion.typing import Face, VisionFrame, UpdateProgress, ProcessMode, ModelSet, OptionsWithModel, AudioFrame, QueuePayload
from facefusion.filesystem import is_file, has_audio, resolve_relative_path from facefusion.filesystem import is_file, has_audio, resolve_relative_path
from facefusion.download import conditional_download, is_download_done from facefusion.download import conditional_download, is_download_done
from facefusion.audio import read_static_audio, get_audio_frame, create_empty_audio_frame from facefusion.audio import read_static_voice, get_voice_frame, create_empty_audio_frame
from facefusion.filesystem import is_image, is_video, filter_audio_paths from facefusion.filesystem import is_image, is_video, filter_audio_paths
from facefusion.common_helper import get_first from facefusion.common_helper import get_first
from facefusion.vision import read_image, write_image, read_static_image from facefusion.vision import read_image, read_static_image, write_image, restrict_video_fps
from facefusion.processors.frame.typings import LipSyncerInputs from facefusion.processors.frame.typings import LipSyncerInputs
from facefusion.voice_extractor import clear_voice_extractor
from facefusion.processors.frame import globals as frame_processors_globals from facefusion.processors.frame import globals as frame_processors_globals
from facefusion.processors.frame import choices as frame_processors_choices from facefusion.processors.frame import choices as frame_processors_choices
FRAME_PROCESSOR = None FRAME_PROCESSOR = None
MODEL_MATRIX = None
THREAD_LOCK : threading.Lock = threading.Lock() THREAD_LOCK : threading.Lock = threading.Lock()
NAME = __name__.upper() NAME = __name__.upper()
MODELS : ModelSet =\ MODELS : ModelSet =\
@ -36,7 +36,7 @@ MODELS : ModelSet =\
'wav2lip_gan': 'wav2lip_gan':
{ {
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/wav2lip_gan.onnx', 'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/wav2lip_gan.onnx',
'path': resolve_relative_path('../.assets/models/wav2lip_gan.onnx'), 'path': resolve_relative_path('../.assets/models/wav2lip_gan.onnx')
} }
} }
OPTIONS : Optional[OptionsWithModel] = None OPTIONS : Optional[OptionsWithModel] = None
@ -87,22 +87,25 @@ def apply_args(program : ArgumentParser) -> None:
def pre_check() -> bool: def pre_check() -> bool:
if not facefusion.globals.skip_download:
download_directory_path = resolve_relative_path('../.assets/models') download_directory_path = resolve_relative_path('../.assets/models')
model_url = get_options('model').get('url') model_url = get_options('model').get('url')
model_path = get_options('model').get('path')
if not facefusion.globals.skip_download:
process_manager.check() process_manager.check()
conditional_download(download_directory_path, [ model_url ]) conditional_download(download_directory_path, [ model_url ])
process_manager.end() process_manager.end()
return True return is_file(model_path)
def post_check() -> bool: def post_check() -> bool:
model_url = get_options('model').get('url') model_url = get_options('model').get('url')
model_path = get_options('model').get('path') model_path = get_options('model').get('path')
if not facefusion.globals.skip_download and not is_download_done(model_url, model_path): if not facefusion.globals.skip_download and not is_download_done(model_url, model_path):
logger.error(wording.get('model_download_not_done') + wording.get('exclamation_mark'), NAME) logger.error(wording.get('model_download_not_done') + wording.get('exclamation_mark'), NAME)
return False return False
elif not is_file(model_path): if not is_file(model_path):
logger.error(wording.get('model_file_not_present') + wording.get('exclamation_mark'), NAME) logger.error(wording.get('model_file_not_present') + wording.get('exclamation_mark'), NAME)
return False return False
return True return True
@ -123,7 +126,7 @@ def pre_process(mode : ProcessMode) -> bool:
def post_process() -> None: def post_process() -> None:
read_static_image.cache_clear() read_static_image.cache_clear()
read_static_audio.cache_clear() read_static_voice.cache_clear()
if facefusion.globals.video_memory_strategy == 'strict' or facefusion.globals.video_memory_strategy == 'moderate': if facefusion.globals.video_memory_strategy == 'strict' or facefusion.globals.video_memory_strategy == 'moderate':
clear_frame_processor() clear_frame_processor()
if facefusion.globals.video_memory_strategy == 'strict': if facefusion.globals.video_memory_strategy == 'strict':
@ -131,6 +134,7 @@ def post_process() -> None:
clear_content_analyser() clear_content_analyser()
clear_face_occluder() clear_face_occluder()
clear_face_parser() clear_face_parser()
clear_voice_extractor()
def sync_lip(target_face : Face, temp_audio_frame : AudioFrame, temp_vision_frame : VisionFrame) -> VisionFrame: def sync_lip(target_face : Face, temp_audio_frame : AudioFrame, temp_vision_frame : VisionFrame) -> VisionFrame:
@ -138,14 +142,11 @@ def sync_lip(target_face : Face, temp_audio_frame : AudioFrame, temp_vision_fram
crop_mask_list = [] crop_mask_list = []
temp_audio_frame = prepare_audio_frame(temp_audio_frame) 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.landmarks.get('5/68'), 'ffhq_512', (512, 512)) crop_vision_frame, affine_matrix = warp_face_by_face_landmark_5(temp_vision_frame, target_face.landmarks.get('5/68'), 'ffhq_512', (512, 512))
if numpy.any(target_face.landmarks.get('68')):
face_landmark_68 = cv2.transform(target_face.landmarks.get('68').reshape(1, -1, 2), affine_matrix).reshape(-1, 2) face_landmark_68 = cv2.transform(target_face.landmarks.get('68').reshape(1, -1, 2), affine_matrix).reshape(-1, 2)
bounding_box = create_bounding_box_from_face_landmark_68(face_landmark_68) bounding_box = create_bounding_box_from_face_landmark_68(face_landmark_68)
bounding_box[1] -= numpy.abs(bounding_box[3] - bounding_box[1]) * 0.125 bounding_box[1] -= numpy.abs(bounding_box[3] - bounding_box[1]) * 0.125
mouth_mask = create_mouth_mask(face_landmark_68) mouth_mask = create_mouth_mask(face_landmark_68)
crop_mask_list.append(mouth_mask) crop_mask_list.append(mouth_mask)
else:
bounding_box = target_face.bounding_box
box_mask = create_static_box_mask(crop_vision_frame.shape[:2][::-1], facefusion.globals.face_mask_blur, facefusion.globals.face_mask_padding) box_mask = create_static_box_mask(crop_vision_frame.shape[:2][::-1], facefusion.globals.face_mask_blur, facefusion.globals.face_mask_padding)
crop_mask_list.append(box_mask) crop_mask_list.append(box_mask)
@ -216,14 +217,15 @@ def process_frame(inputs : LipSyncerInputs) -> VisionFrame:
return target_vision_frame return target_vision_frame
def process_frames(source_paths : List[str], queue_payloads : List[QueuePayload], update_progress : UpdateProcess) -> None: def process_frames(source_paths : List[str], queue_payloads : List[QueuePayload], update_progress : UpdateProgress) -> None:
reference_faces = get_reference_faces() if 'reference' in facefusion.globals.face_selector_mode else None reference_faces = get_reference_faces() if 'reference' in facefusion.globals.face_selector_mode else None
source_audio_path = get_first(filter_audio_paths(source_paths)) source_audio_path = get_first(filter_audio_paths(source_paths))
temp_video_fps = restrict_video_fps(facefusion.globals.target_path, facefusion.globals.output_video_fps)
for queue_payload in process_manager.manage(queue_payloads): for queue_payload in process_manager.manage(queue_payloads):
frame_number = queue_payload['frame_number'] frame_number = queue_payload['frame_number']
target_vision_path = queue_payload['frame_path'] target_vision_path = queue_payload['frame_path']
source_audio_frame = get_audio_frame(source_audio_path, facefusion.globals.output_video_fps, frame_number) source_audio_frame = get_voice_frame(source_audio_path, temp_video_fps, frame_number)
if not numpy.any(source_audio_frame): if not numpy.any(source_audio_frame):
source_audio_frame = create_empty_audio_frame() source_audio_frame = create_empty_audio_frame()
target_vision_frame = read_image(target_vision_path) target_vision_frame = read_image(target_vision_path)
@ -234,7 +236,7 @@ def process_frames(source_paths : List[str], queue_payloads : List[QueuePayload]
'target_vision_frame': target_vision_frame 'target_vision_frame': target_vision_frame
}) })
write_image(target_vision_path, output_vision_frame) write_image(target_vision_path, output_vision_frame)
update_progress() update_progress(1)
def process_image(source_paths : List[str], target_path : str, output_path : str) -> None: def process_image(source_paths : List[str], target_path : str, output_path : str) -> None:

View File

@ -2,10 +2,11 @@ from typing import Literal, TypedDict
from facefusion.typing import Face, FaceSet, AudioFrame, VisionFrame from facefusion.typing import Face, FaceSet, AudioFrame, VisionFrame
FaceDebuggerItem = Literal['bounding-box', 'face-landmark-5', 'face-landmark-5/68', 'face-landmark-68', 'face-mask', 'face-detector-score', 'face-landmarker-score', 'age', 'gender'] 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']
FaceEnhancerModel = Literal['codeformer', 'gfpgan_1.2', 'gfpgan_1.3', 'gfpgan_1.4', 'gpen_bfr_256', 'gpen_bfr_512', 'restoreformer_plus_plus'] 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', 'inswapper_128', 'inswapper_128_fp16', 'simswap_256', 'simswap_512_unofficial', 'uniface_256'] FaceSwapperModel = Literal['blendswap_256', 'inswapper_128', 'inswapper_128_fp16', 'simswap_256', 'simswap_512_unofficial', 'uniface_256']
FrameEnhancerModel = Literal['lsdir_x4', 'nomos8k_sc_x4', 'real_esrgan_x4', 'real_esrgan_x4_fp16', 'span_kendata_x4'] FrameColorizerModel = Literal['ddcolor', 'ddcolor_artistic', 'deoldify_artistic']
FrameEnhancerModel = Literal['lsdir_x4', 'nomos8k_sc_x4', 'real_esrgan_x2', 'real_esrgan_x2_fp16', 'real_esrgan_x4', 'real_esrgan_x4_fp16', 'real_hatgan_x4', 'span_kendata_x4']
LipSyncerModel = Literal['wav2lip_gan'] LipSyncerModel = Literal['wav2lip_gan']
FaceDebuggerInputs = TypedDict('FaceDebuggerInputs', FaceDebuggerInputs = TypedDict('FaceDebuggerInputs',
@ -24,6 +25,10 @@ FaceSwapperInputs = TypedDict('FaceSwapperInputs',
'source_face' : Face, 'source_face' : Face,
'target_vision_frame' : VisionFrame 'target_vision_frame' : VisionFrame
}) })
FrameColorizerInputs = TypedDict('FrameColorizerInputs',
{
'target_vision_frame' : VisionFrame
})
FrameEnhancerInputs = TypedDict('FrameEnhancerInputs', FrameEnhancerInputs = TypedDict('FrameEnhancerInputs',
{ {
'target_vision_frame' : VisionFrame 'target_vision_frame' : VisionFrame

View File

@ -9,7 +9,8 @@ FaceLandmarkSet = TypedDict('FaceLandmarkSet',
{ {
'5' : FaceLandmark5, # type: ignore[valid-type] '5' : FaceLandmark5, # type: ignore[valid-type]
'5/68' : FaceLandmark5, # type: ignore[valid-type] '5/68' : FaceLandmark5, # type: ignore[valid-type]
'68' : FaceLandmark68 # type: ignore[valid-type] '68' : FaceLandmark68, # type: ignore[valid-type]
'68/5' : FaceLandmark68 # type: ignore[valid-type]
}) })
Score = float Score = float
FaceScoreSet = TypedDict('FaceScoreSet', FaceScoreSet = TypedDict('FaceScoreSet',
@ -42,8 +43,10 @@ Translation = numpy.ndarray[Any, Any]
AudioBuffer = bytes AudioBuffer = bytes
Audio = numpy.ndarray[Any, Any] Audio = numpy.ndarray[Any, Any]
AudioChunk = numpy.ndarray[Any, Any]
AudioFrame = numpy.ndarray[Any, Any] AudioFrame = numpy.ndarray[Any, Any]
Spectrogram = numpy.ndarray[Any, Any] Spectrogram = numpy.ndarray[Any, Any]
MelFilterBank = numpy.ndarray[Any, Any]
Fps = float Fps = float
Padding = Tuple[int, int, int, int] Padding = Tuple[int, int, int, int]
@ -55,8 +58,8 @@ QueuePayload = TypedDict('QueuePayload',
'frame_number' : int, 'frame_number' : int,
'frame_path' : str 'frame_path' : str
}) })
UpdateProcess = Callable[[], None] UpdateProgress = Callable[[int], None]
ProcessFrames = Callable[[List[str], List[QueuePayload], UpdateProcess], None] ProcessFrames = Callable[[List[str], List[QueuePayload], UpdateProgress], None]
WarpTemplate = Literal['arcface_112_v1', 'arcface_112_v2', 'arcface_128_v2', 'ffhq_512'] WarpTemplate = Literal['arcface_112_v1', 'arcface_112_v2', 'arcface_128_v2', 'ffhq_512']
WarpTemplateSet = Dict[WarpTemplate, numpy.ndarray[Any, Any]] WarpTemplateSet = Dict[WarpTemplate, numpy.ndarray[Any, Any]]

View File

@ -76,7 +76,7 @@ def listen() -> None:
def start(benchmark_runs : List[str], benchmark_cycles : int) -> Generator[List[Any], None, None]: def start(benchmark_runs : List[str], benchmark_cycles : int) -> Generator[List[Any], None, None]:
facefusion.globals.source_paths = [ '.assets/examples/source.jpg' ] facefusion.globals.source_paths = [ '.assets/examples/source.jpg', '.assets/examples/source.mp3' ]
facefusion.globals.output_path = tempfile.gettempdir() facefusion.globals.output_path = tempfile.gettempdir()
facefusion.globals.face_landmarker_score = 0 facefusion.globals.face_landmarker_score = 0
facefusion.globals.temp_frame_format = 'bmp' facefusion.globals.temp_frame_format = 'bmp'
@ -87,7 +87,8 @@ def start(benchmark_runs : List[str], benchmark_cycles : int) -> Generator[List[
if target_paths: if target_paths:
pre_process() pre_process()
for target_path in target_paths: for target_path in target_paths:
benchmark_results.append(benchmark(target_path, benchmark_cycles)) facefusion.globals.target_path = target_path
benchmark_results.append(benchmark(benchmark_cycles))
yield benchmark_results yield benchmark_results
post_process() post_process()
@ -103,10 +104,8 @@ def post_process() -> None:
clear_static_faces() clear_static_faces()
def benchmark(target_path : str, benchmark_cycles : int) -> List[Any]: def benchmark(benchmark_cycles : int) -> List[Any]:
process_times = [] process_times = []
total_fps = 0.0
facefusion.globals.target_path = target_path
video_frame_total = count_video_frame_total(facefusion.globals.target_path) video_frame_total = count_video_frame_total(facefusion.globals.target_path)
output_video_resolution = detect_video_resolution(facefusion.globals.target_path) output_video_resolution = detect_video_resolution(facefusion.globals.target_path)
facefusion.globals.output_video_resolution = pack_resolution(output_video_resolution) facefusion.globals.output_video_resolution = pack_resolution(output_video_resolution)
@ -116,13 +115,12 @@ def benchmark(target_path : str, benchmark_cycles : int) -> List[Any]:
start_time = perf_counter() start_time = perf_counter()
conditional_process() conditional_process()
end_time = perf_counter() end_time = perf_counter()
process_time = end_time - start_time process_times.append(end_time - start_time)
total_fps += video_frame_total / process_time
process_times.append(process_time)
average_run = round(statistics.mean(process_times), 2) average_run = round(statistics.mean(process_times), 2)
fastest_run = round(min(process_times), 2) fastest_run = round(min(process_times), 2)
slowest_run = round(max(process_times), 2) slowest_run = round(max(process_times), 2)
relative_fps = round(total_fps / benchmark_cycles, 2) relative_fps = round(video_frame_total * benchmark_cycles / sum(process_times), 2)
return\ return\
[ [
facefusion.globals.target_path, facefusion.globals.target_path,

View File

@ -21,7 +21,7 @@ def render() -> None:
def listen() -> None: def listen() -> None:
EXECUTION_QUEUE_COUNT_SLIDER.change(update_execution_queue_count, inputs = EXECUTION_QUEUE_COUNT_SLIDER) EXECUTION_QUEUE_COUNT_SLIDER.release(update_execution_queue_count, inputs = EXECUTION_QUEUE_COUNT_SLIDER)
def update_execution_queue_count(execution_queue_count : int = 1) -> None: def update_execution_queue_count(execution_queue_count : int = 1) -> None:

View File

@ -21,7 +21,7 @@ def render() -> None:
def listen() -> None: def listen() -> None:
EXECUTION_THREAD_COUNT_SLIDER.change(update_execution_thread_count, inputs = EXECUTION_THREAD_COUNT_SLIDER) EXECUTION_THREAD_COUNT_SLIDER.release(update_execution_thread_count, inputs = EXECUTION_THREAD_COUNT_SLIDER)
def update_execution_thread_count(execution_thread_count : int = 1) -> None: def update_execution_thread_count(execution_thread_count : int = 1) -> None:

View File

@ -92,11 +92,11 @@ def render() -> None:
def listen() -> None: def listen() -> None:
FACE_MASK_TYPES_CHECKBOX_GROUP.change(update_face_mask_type, inputs = FACE_MASK_TYPES_CHECKBOX_GROUP, outputs = [ FACE_MASK_TYPES_CHECKBOX_GROUP, FACE_MASK_BOX_GROUP, FACE_MASK_REGION_CHECKBOX_GROUP ]) FACE_MASK_TYPES_CHECKBOX_GROUP.change(update_face_mask_type, inputs = FACE_MASK_TYPES_CHECKBOX_GROUP, outputs = [ FACE_MASK_TYPES_CHECKBOX_GROUP, FACE_MASK_BOX_GROUP, FACE_MASK_REGION_CHECKBOX_GROUP ])
FACE_MASK_BLUR_SLIDER.change(update_face_mask_blur, inputs = FACE_MASK_BLUR_SLIDER) FACE_MASK_BLUR_SLIDER.release(update_face_mask_blur, inputs = FACE_MASK_BLUR_SLIDER)
FACE_MASK_REGION_CHECKBOX_GROUP.change(update_face_mask_regions, inputs = FACE_MASK_REGION_CHECKBOX_GROUP, outputs = FACE_MASK_REGION_CHECKBOX_GROUP) FACE_MASK_REGION_CHECKBOX_GROUP.change(update_face_mask_regions, inputs = FACE_MASK_REGION_CHECKBOX_GROUP, outputs = FACE_MASK_REGION_CHECKBOX_GROUP)
face_mask_padding_sliders = [ FACE_MASK_PADDING_TOP_SLIDER, FACE_MASK_PADDING_RIGHT_SLIDER, FACE_MASK_PADDING_BOTTOM_SLIDER, FACE_MASK_PADDING_LEFT_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: for face_mask_padding_slider in face_mask_padding_sliders:
face_mask_padding_slider.change(update_face_mask_padding, inputs = face_mask_padding_sliders) face_mask_padding_slider.release(update_face_mask_padding, inputs = face_mask_padding_sliders)
def update_face_mask_type(face_mask_types : List[FaceMaskType]) -> Tuple[gradio.CheckboxGroup, gradio.Group, gradio.CheckboxGroup]: def update_face_mask_type(face_mask_types : List[FaceMaskType]) -> Tuple[gradio.CheckboxGroup, gradio.Group, gradio.CheckboxGroup]:

View File

@ -10,8 +10,7 @@ from facefusion.vision import get_video_frame, read_static_image, normalize_fram
from facefusion.filesystem import is_image, is_video from facefusion.filesystem import is_image, is_video
from facefusion.face_analyser import get_many_faces from facefusion.face_analyser import get_many_faces
from facefusion.typing import VisionFrame, FaceSelectorMode from facefusion.typing import VisionFrame, FaceSelectorMode
from facefusion.uis.core import get_ui_component, register_ui_component from facefusion.uis.core import get_ui_component, get_ui_components, register_ui_component
from facefusion.uis.typing import ComponentName
FACE_SELECTOR_MODE_DROPDOWN : Optional[gradio.Dropdown] = None FACE_SELECTOR_MODE_DROPDOWN : Optional[gradio.Dropdown] = None
REFERENCE_FACE_POSITION_GALLERY : Optional[gradio.Gallery] = None REFERENCE_FACE_POSITION_GALLERY : Optional[gradio.Gallery] = None
@ -59,39 +58,39 @@ def render() -> None:
def listen() -> None: def listen() -> None:
FACE_SELECTOR_MODE_DROPDOWN.change(update_face_selector_mode, inputs = FACE_SELECTOR_MODE_DROPDOWN, outputs = [ REFERENCE_FACE_POSITION_GALLERY, REFERENCE_FACE_DISTANCE_SLIDER ]) FACE_SELECTOR_MODE_DROPDOWN.change(update_face_selector_mode, inputs = FACE_SELECTOR_MODE_DROPDOWN, outputs = [ REFERENCE_FACE_POSITION_GALLERY, REFERENCE_FACE_DISTANCE_SLIDER ])
REFERENCE_FACE_POSITION_GALLERY.select(clear_and_update_reference_face_position) REFERENCE_FACE_POSITION_GALLERY.select(clear_and_update_reference_face_position)
REFERENCE_FACE_DISTANCE_SLIDER.change(update_reference_face_distance, inputs = REFERENCE_FACE_DISTANCE_SLIDER) REFERENCE_FACE_DISTANCE_SLIDER.release(update_reference_face_distance, inputs = REFERENCE_FACE_DISTANCE_SLIDER)
multi_component_names : List[ComponentName] =\
for ui_component in get_ui_components(
[ [
'target_image', 'target_image',
'target_video' 'target_video'
] ]):
for component_name in multi_component_names:
component = get_ui_component(component_name)
if component:
for method in [ 'upload', 'change', 'clear' ]: for method in [ 'upload', 'change', 'clear' ]:
getattr(component, method)(update_reference_face_position) getattr(ui_component, method)(update_reference_face_position)
getattr(component, method)(update_reference_position_gallery, outputs = REFERENCE_FACE_POSITION_GALLERY) getattr(ui_component, method)(update_reference_position_gallery, outputs = REFERENCE_FACE_POSITION_GALLERY)
change_one_component_names : List[ComponentName] =\
for ui_component in get_ui_components(
[ [
'face_analyser_order_dropdown', 'face_analyser_order_dropdown',
'face_analyser_age_dropdown', 'face_analyser_age_dropdown',
'face_analyser_gender_dropdown' 'face_analyser_gender_dropdown'
] ]):
for component_name in change_one_component_names: ui_component.change(update_reference_position_gallery, outputs = REFERENCE_FACE_POSITION_GALLERY)
component = get_ui_component(component_name)
if component: for ui_component in get_ui_components(
component.change(update_reference_position_gallery, outputs = REFERENCE_FACE_POSITION_GALLERY)
change_two_component_names : List[ComponentName] =\
[ [
'face_detector_model_dropdown', 'face_detector_model_dropdown',
'face_detector_size_dropdown', 'face_detector_size_dropdown'
]):
ui_component.change(clear_and_update_reference_position_gallery, outputs = REFERENCE_FACE_POSITION_GALLERY)
for ui_component in get_ui_components(
[
'face_detector_score_slider', 'face_detector_score_slider',
'face_landmarker_score_slider' 'face_landmarker_score_slider'
] ]):
for component_name in change_two_component_names: ui_component.release(clear_and_update_reference_position_gallery, outputs=REFERENCE_FACE_POSITION_GALLERY)
component = get_ui_component(component_name)
if component:
component.change(clear_and_update_reference_position_gallery, outputs = REFERENCE_FACE_POSITION_GALLERY)
preview_frame_slider = get_ui_component('preview_frame_slider') preview_frame_slider = get_ui_component('preview_frame_slider')
if preview_frame_slider: if preview_frame_slider:
preview_frame_slider.change(update_reference_frame_number, inputs = preview_frame_slider) preview_frame_slider.change(update_reference_frame_number, inputs = preview_frame_slider)

View File

@ -5,13 +5,15 @@ import facefusion.globals
from facefusion import face_analyser, wording from facefusion import face_analyser, wording
from facefusion.processors.frame.core import load_frame_processor_module from facefusion.processors.frame.core import load_frame_processor_module
from facefusion.processors.frame import globals as frame_processors_globals, choices as frame_processors_choices from facefusion.processors.frame import globals as frame_processors_globals, choices as frame_processors_choices
from facefusion.processors.frame.typings import FaceDebuggerItem, FaceEnhancerModel, FaceSwapperModel, FrameEnhancerModel, LipSyncerModel from facefusion.processors.frame.typings import FaceDebuggerItem, FaceEnhancerModel, FaceSwapperModel, FrameColorizerModel, FrameEnhancerModel, LipSyncerModel
from facefusion.uis.core import get_ui_component, register_ui_component from facefusion.uis.core import get_ui_component, register_ui_component
FACE_DEBUGGER_ITEMS_CHECKBOX_GROUP : Optional[gradio.CheckboxGroup] = None FACE_DEBUGGER_ITEMS_CHECKBOX_GROUP : Optional[gradio.CheckboxGroup] = None
FACE_ENHANCER_MODEL_DROPDOWN : Optional[gradio.Dropdown] = None FACE_ENHANCER_MODEL_DROPDOWN : Optional[gradio.Dropdown] = None
FACE_ENHANCER_BLEND_SLIDER : Optional[gradio.Slider] = None FACE_ENHANCER_BLEND_SLIDER : Optional[gradio.Slider] = None
FACE_SWAPPER_MODEL_DROPDOWN : Optional[gradio.Dropdown] = None FACE_SWAPPER_MODEL_DROPDOWN : Optional[gradio.Dropdown] = None
FRAME_COLORIZER_MODEL_DROPDOWN : Optional[gradio.Dropdown] = None
FRAME_COLORIZER_BLEND_SLIDER : Optional[gradio.Slider] = None
FRAME_ENHANCER_MODEL_DROPDOWN : Optional[gradio.Dropdown] = None FRAME_ENHANCER_MODEL_DROPDOWN : Optional[gradio.Dropdown] = None
FRAME_ENHANCER_BLEND_SLIDER : Optional[gradio.Slider] = None FRAME_ENHANCER_BLEND_SLIDER : Optional[gradio.Slider] = None
LIP_SYNCER_MODEL_DROPDOWN : Optional[gradio.Dropdown] = None LIP_SYNCER_MODEL_DROPDOWN : Optional[gradio.Dropdown] = None
@ -22,6 +24,8 @@ def render() -> None:
global FACE_ENHANCER_MODEL_DROPDOWN global FACE_ENHANCER_MODEL_DROPDOWN
global FACE_ENHANCER_BLEND_SLIDER global FACE_ENHANCER_BLEND_SLIDER
global FACE_SWAPPER_MODEL_DROPDOWN global FACE_SWAPPER_MODEL_DROPDOWN
global FRAME_COLORIZER_MODEL_DROPDOWN
global FRAME_COLORIZER_BLEND_SLIDER
global FRAME_ENHANCER_MODEL_DROPDOWN global FRAME_ENHANCER_MODEL_DROPDOWN
global FRAME_ENHANCER_BLEND_SLIDER global FRAME_ENHANCER_BLEND_SLIDER
global LIP_SYNCER_MODEL_DROPDOWN global LIP_SYNCER_MODEL_DROPDOWN
@ -52,6 +56,20 @@ def render() -> None:
value = frame_processors_globals.face_swapper_model, value = frame_processors_globals.face_swapper_model,
visible = 'face_swapper' in facefusion.globals.frame_processors visible = 'face_swapper' in facefusion.globals.frame_processors
) )
FRAME_COLORIZER_MODEL_DROPDOWN = gradio.Dropdown(
label = wording.get('uis.frame_colorizer_model_dropdown'),
choices = frame_processors_choices.frame_colorizer_models,
value = frame_processors_globals.frame_colorizer_model,
visible = 'frame_colorizer' in facefusion.globals.frame_processors
)
FRAME_COLORIZER_BLEND_SLIDER = gradio.Slider(
label = wording.get('uis.frame_colorizer_blend_slider'),
value = frame_processors_globals.frame_colorizer_blend,
step = frame_processors_choices.frame_colorizer_blend_range[1] - frame_processors_choices.frame_colorizer_blend_range[0],
minimum = frame_processors_choices.frame_colorizer_blend_range[0],
maximum = frame_processors_choices.frame_colorizer_blend_range[-1],
visible = 'frame_colorizer' in facefusion.globals.frame_processors
)
FRAME_ENHANCER_MODEL_DROPDOWN = gradio.Dropdown( FRAME_ENHANCER_MODEL_DROPDOWN = gradio.Dropdown(
label = wording.get('uis.frame_enhancer_model_dropdown'), label = wording.get('uis.frame_enhancer_model_dropdown'),
choices = frame_processors_choices.frame_enhancer_models, choices = frame_processors_choices.frame_enhancer_models,
@ -76,6 +94,8 @@ def render() -> None:
register_ui_component('face_enhancer_model_dropdown', FACE_ENHANCER_MODEL_DROPDOWN) register_ui_component('face_enhancer_model_dropdown', FACE_ENHANCER_MODEL_DROPDOWN)
register_ui_component('face_enhancer_blend_slider', FACE_ENHANCER_BLEND_SLIDER) register_ui_component('face_enhancer_blend_slider', FACE_ENHANCER_BLEND_SLIDER)
register_ui_component('face_swapper_model_dropdown', FACE_SWAPPER_MODEL_DROPDOWN) register_ui_component('face_swapper_model_dropdown', FACE_SWAPPER_MODEL_DROPDOWN)
register_ui_component('frame_colorizer_model_dropdown', FRAME_COLORIZER_MODEL_DROPDOWN)
register_ui_component('frame_colorizer_blend_slider', FRAME_COLORIZER_BLEND_SLIDER)
register_ui_component('frame_enhancer_model_dropdown', FRAME_ENHANCER_MODEL_DROPDOWN) register_ui_component('frame_enhancer_model_dropdown', FRAME_ENHANCER_MODEL_DROPDOWN)
register_ui_component('frame_enhancer_blend_slider', FRAME_ENHANCER_BLEND_SLIDER) register_ui_component('frame_enhancer_blend_slider', FRAME_ENHANCER_BLEND_SLIDER)
register_ui_component('lip_syncer_model_dropdown', LIP_SYNCER_MODEL_DROPDOWN) register_ui_component('lip_syncer_model_dropdown', LIP_SYNCER_MODEL_DROPDOWN)
@ -84,23 +104,26 @@ def render() -> None:
def listen() -> None: def listen() -> None:
FACE_DEBUGGER_ITEMS_CHECKBOX_GROUP.change(update_face_debugger_items, inputs = FACE_DEBUGGER_ITEMS_CHECKBOX_GROUP) FACE_DEBUGGER_ITEMS_CHECKBOX_GROUP.change(update_face_debugger_items, inputs = FACE_DEBUGGER_ITEMS_CHECKBOX_GROUP)
FACE_ENHANCER_MODEL_DROPDOWN.change(update_face_enhancer_model, inputs = FACE_ENHANCER_MODEL_DROPDOWN, outputs = FACE_ENHANCER_MODEL_DROPDOWN) FACE_ENHANCER_MODEL_DROPDOWN.change(update_face_enhancer_model, inputs = FACE_ENHANCER_MODEL_DROPDOWN, outputs = FACE_ENHANCER_MODEL_DROPDOWN)
FACE_ENHANCER_BLEND_SLIDER.change(update_face_enhancer_blend, inputs = FACE_ENHANCER_BLEND_SLIDER) FACE_ENHANCER_BLEND_SLIDER.release(update_face_enhancer_blend, inputs = FACE_ENHANCER_BLEND_SLIDER)
FACE_SWAPPER_MODEL_DROPDOWN.change(update_face_swapper_model, inputs = FACE_SWAPPER_MODEL_DROPDOWN, outputs = FACE_SWAPPER_MODEL_DROPDOWN) FACE_SWAPPER_MODEL_DROPDOWN.change(update_face_swapper_model, inputs = FACE_SWAPPER_MODEL_DROPDOWN, outputs = FACE_SWAPPER_MODEL_DROPDOWN)
FRAME_COLORIZER_MODEL_DROPDOWN.change(update_frame_colorizer_model, inputs = FRAME_COLORIZER_MODEL_DROPDOWN, outputs = FRAME_COLORIZER_MODEL_DROPDOWN)
FRAME_COLORIZER_BLEND_SLIDER.release(update_frame_colorizer_blend, inputs = FRAME_COLORIZER_BLEND_SLIDER)
FRAME_ENHANCER_MODEL_DROPDOWN.change(update_frame_enhancer_model, inputs = FRAME_ENHANCER_MODEL_DROPDOWN, outputs = FRAME_ENHANCER_MODEL_DROPDOWN) FRAME_ENHANCER_MODEL_DROPDOWN.change(update_frame_enhancer_model, inputs = FRAME_ENHANCER_MODEL_DROPDOWN, outputs = FRAME_ENHANCER_MODEL_DROPDOWN)
FRAME_ENHANCER_BLEND_SLIDER.change(update_frame_enhancer_blend, inputs = FRAME_ENHANCER_BLEND_SLIDER) FRAME_ENHANCER_BLEND_SLIDER.release(update_frame_enhancer_blend, inputs = FRAME_ENHANCER_BLEND_SLIDER)
LIP_SYNCER_MODEL_DROPDOWN.change(update_lip_syncer_model, inputs = LIP_SYNCER_MODEL_DROPDOWN, outputs = LIP_SYNCER_MODEL_DROPDOWN) LIP_SYNCER_MODEL_DROPDOWN.change(update_lip_syncer_model, inputs = LIP_SYNCER_MODEL_DROPDOWN, outputs = LIP_SYNCER_MODEL_DROPDOWN)
frame_processors_checkbox_group = get_ui_component('frame_processors_checkbox_group') frame_processors_checkbox_group = get_ui_component('frame_processors_checkbox_group')
if frame_processors_checkbox_group: if frame_processors_checkbox_group:
frame_processors_checkbox_group.change(update_frame_processors, inputs = frame_processors_checkbox_group, outputs = [ FACE_DEBUGGER_ITEMS_CHECKBOX_GROUP, FACE_ENHANCER_MODEL_DROPDOWN, FACE_ENHANCER_BLEND_SLIDER, FACE_SWAPPER_MODEL_DROPDOWN, FRAME_ENHANCER_MODEL_DROPDOWN, FRAME_ENHANCER_BLEND_SLIDER, LIP_SYNCER_MODEL_DROPDOWN ]) frame_processors_checkbox_group.change(update_frame_processors, inputs = frame_processors_checkbox_group, outputs = [ FACE_DEBUGGER_ITEMS_CHECKBOX_GROUP, FACE_ENHANCER_MODEL_DROPDOWN, FACE_ENHANCER_BLEND_SLIDER, FACE_SWAPPER_MODEL_DROPDOWN, FRAME_COLORIZER_MODEL_DROPDOWN, FRAME_COLORIZER_BLEND_SLIDER, FRAME_ENHANCER_MODEL_DROPDOWN, FRAME_ENHANCER_BLEND_SLIDER, LIP_SYNCER_MODEL_DROPDOWN ])
def update_frame_processors(frame_processors : List[str]) -> Tuple[gradio.CheckboxGroup, gradio.Dropdown, gradio.Slider, gradio.Dropdown, gradio.Dropdown, gradio.Slider, gradio.Dropdown]: def update_frame_processors(frame_processors : List[str]) -> Tuple[gradio.CheckboxGroup, gradio.Dropdown, gradio.Slider, gradio.Dropdown, gradio.Dropdown, gradio.Slider, gradio.Dropdown, gradio.Slider, gradio.Dropdown]:
has_face_debugger = 'face_debugger' in frame_processors has_face_debugger = 'face_debugger' in frame_processors
has_face_enhancer = 'face_enhancer' in frame_processors has_face_enhancer = 'face_enhancer' in frame_processors
has_face_swapper = 'face_swapper' in frame_processors has_face_swapper = 'face_swapper' in frame_processors
has_frame_colorizer = 'frame_colorizer' in frame_processors
has_frame_enhancer = 'frame_enhancer' in frame_processors has_frame_enhancer = 'frame_enhancer' in frame_processors
has_lip_syncer = 'lip_syncer' in frame_processors has_lip_syncer = 'lip_syncer' in frame_processors
return gradio.CheckboxGroup(visible = has_face_debugger), gradio.Dropdown(visible = has_face_enhancer), gradio.Slider(visible = has_face_enhancer), gradio.Dropdown(visible = has_face_swapper), gradio.Dropdown(visible = has_frame_enhancer), gradio.Slider(visible = has_frame_enhancer), gradio.Dropdown(visible = has_lip_syncer) return gradio.CheckboxGroup(visible = has_face_debugger), gradio.Dropdown(visible = has_face_enhancer), gradio.Slider(visible = has_face_enhancer), gradio.Dropdown(visible = has_face_swapper), gradio.Dropdown(visible = has_frame_colorizer), gradio.Slider(visible = has_frame_colorizer), gradio.Dropdown(visible = has_frame_enhancer), gradio.Slider(visible = has_frame_enhancer), gradio.Dropdown(visible = has_lip_syncer)
def update_face_debugger_items(face_debugger_items : List[FaceDebuggerItem]) -> None: def update_face_debugger_items(face_debugger_items : List[FaceDebuggerItem]) -> None:
@ -132,6 +155,7 @@ def update_face_swapper_model(face_swapper_model : FaceSwapperModel) -> gradio.D
if face_swapper_model == 'uniface_256': if face_swapper_model == 'uniface_256':
facefusion.globals.face_recognizer_model = 'arcface_uniface' facefusion.globals.face_recognizer_model = 'arcface_uniface'
face_swapper_module = load_frame_processor_module('face_swapper') face_swapper_module = load_frame_processor_module('face_swapper')
face_swapper_module.clear_model_initializer()
face_swapper_module.clear_frame_processor() face_swapper_module.clear_frame_processor()
face_swapper_module.set_options('model', face_swapper_module.MODELS[face_swapper_model]) face_swapper_module.set_options('model', face_swapper_module.MODELS[face_swapper_model])
if face_analyser.pre_check() and face_swapper_module.pre_check(): if face_analyser.pre_check() and face_swapper_module.pre_check():
@ -139,6 +163,20 @@ def update_face_swapper_model(face_swapper_model : FaceSwapperModel) -> gradio.D
return gradio.Dropdown() return gradio.Dropdown()
def update_frame_colorizer_model(frame_colorizer_model : FrameColorizerModel) -> gradio.Dropdown:
frame_processors_globals.frame_colorizer_model = frame_colorizer_model
frame_colorizer_module = load_frame_processor_module('frame_colorizer')
frame_colorizer_module.clear_frame_processor()
frame_colorizer_module.set_options('model', frame_colorizer_module.MODELS[frame_colorizer_model])
if frame_colorizer_module.pre_check():
return gradio.Dropdown(value = frame_processors_globals.frame_colorizer_model)
return gradio.Dropdown()
def update_frame_colorizer_blend(frame_colorizer_blend : int) -> None:
frame_processors_globals.frame_colorizer_blend = frame_colorizer_blend
def update_frame_enhancer_model(frame_enhancer_model : FrameEnhancerModel) -> gradio.Dropdown: def update_frame_enhancer_model(frame_enhancer_model : FrameEnhancerModel) -> gradio.Dropdown:
frame_processors_globals.frame_enhancer_model = frame_enhancer_model frame_processors_globals.frame_enhancer_model = frame_enhancer_model
frame_enhancer_module = load_frame_processor_module('frame_enhancer') frame_enhancer_module = load_frame_processor_module('frame_enhancer')

View File

@ -6,15 +6,15 @@ import facefusion.choices
from facefusion.typing import VideoMemoryStrategy from facefusion.typing import VideoMemoryStrategy
from facefusion import wording from facefusion import wording
VIDEO_MEMORY_STRATEGY : Optional[gradio.Dropdown] = None VIDEO_MEMORY_STRATEGY_DROPDOWN : Optional[gradio.Dropdown] = None
SYSTEM_MEMORY_LIMIT_SLIDER : Optional[gradio.Slider] = None SYSTEM_MEMORY_LIMIT_SLIDER : Optional[gradio.Slider] = None
def render() -> None: def render() -> None:
global VIDEO_MEMORY_STRATEGY global VIDEO_MEMORY_STRATEGY_DROPDOWN
global SYSTEM_MEMORY_LIMIT_SLIDER global SYSTEM_MEMORY_LIMIT_SLIDER
VIDEO_MEMORY_STRATEGY = gradio.Dropdown( VIDEO_MEMORY_STRATEGY_DROPDOWN = gradio.Dropdown(
label = wording.get('uis.video_memory_strategy_dropdown'), label = wording.get('uis.video_memory_strategy_dropdown'),
choices = facefusion.choices.video_memory_strategies, choices = facefusion.choices.video_memory_strategies,
value = facefusion.globals.video_memory_strategy value = facefusion.globals.video_memory_strategy
@ -29,8 +29,8 @@ def render() -> None:
def listen() -> None: def listen() -> None:
VIDEO_MEMORY_STRATEGY.change(update_video_memory_strategy, inputs = VIDEO_MEMORY_STRATEGY) VIDEO_MEMORY_STRATEGY_DROPDOWN.change(update_video_memory_strategy, inputs = VIDEO_MEMORY_STRATEGY_DROPDOWN)
SYSTEM_MEMORY_LIMIT_SLIDER.change(update_system_memory_limit, inputs = SYSTEM_MEMORY_LIMIT_SLIDER) SYSTEM_MEMORY_LIMIT_SLIDER.release(update_system_memory_limit, inputs = SYSTEM_MEMORY_LIMIT_SLIDER)
def update_video_memory_strategy(video_memory_strategy : VideoMemoryStrategy) -> None: def update_video_memory_strategy(video_memory_strategy : VideoMemoryStrategy) -> None:

View File

@ -1,4 +1,4 @@
from typing import Optional, Tuple, List from typing import Optional, Tuple
import gradio import gradio
import facefusion.globals import facefusion.globals
@ -6,8 +6,7 @@ import facefusion.choices
from facefusion import wording from facefusion import wording
from facefusion.typing import OutputVideoEncoder, OutputVideoPreset, Fps from facefusion.typing import OutputVideoEncoder, OutputVideoPreset, Fps
from facefusion.filesystem import is_image, is_video from facefusion.filesystem import is_image, is_video
from facefusion.uis.typing import ComponentName from facefusion.uis.core import get_ui_components, register_ui_component
from facefusion.uis.core import get_ui_component, register_ui_component
from facefusion.vision import detect_image_resolution, create_image_resolutions, detect_video_fps, detect_video_resolution, create_video_resolutions, pack_resolution from facefusion.vision import detect_image_resolution, create_image_resolutions, detect_video_fps, detect_video_resolution, create_video_resolutions, pack_resolution
OUTPUT_PATH_TEXTBOX : Optional[gradio.Textbox] = None OUTPUT_PATH_TEXTBOX : Optional[gradio.Textbox] = None
@ -98,23 +97,21 @@ def render() -> None:
def listen() -> None: def listen() -> None:
OUTPUT_PATH_TEXTBOX.change(update_output_path, inputs = OUTPUT_PATH_TEXTBOX) OUTPUT_PATH_TEXTBOX.change(update_output_path, inputs = OUTPUT_PATH_TEXTBOX)
OUTPUT_IMAGE_QUALITY_SLIDER.change(update_output_image_quality, inputs = OUTPUT_IMAGE_QUALITY_SLIDER) OUTPUT_IMAGE_QUALITY_SLIDER.release(update_output_image_quality, inputs = OUTPUT_IMAGE_QUALITY_SLIDER)
OUTPUT_IMAGE_RESOLUTION_DROPDOWN.change(update_output_image_resolution, inputs = OUTPUT_IMAGE_RESOLUTION_DROPDOWN) OUTPUT_IMAGE_RESOLUTION_DROPDOWN.change(update_output_image_resolution, inputs = OUTPUT_IMAGE_RESOLUTION_DROPDOWN)
OUTPUT_VIDEO_ENCODER_DROPDOWN.change(update_output_video_encoder, inputs = OUTPUT_VIDEO_ENCODER_DROPDOWN) OUTPUT_VIDEO_ENCODER_DROPDOWN.change(update_output_video_encoder, inputs = OUTPUT_VIDEO_ENCODER_DROPDOWN)
OUTPUT_VIDEO_PRESET_DROPDOWN.change(update_output_video_preset, inputs = OUTPUT_VIDEO_PRESET_DROPDOWN) OUTPUT_VIDEO_PRESET_DROPDOWN.change(update_output_video_preset, inputs = OUTPUT_VIDEO_PRESET_DROPDOWN)
OUTPUT_VIDEO_QUALITY_SLIDER.change(update_output_video_quality, inputs = OUTPUT_VIDEO_QUALITY_SLIDER) OUTPUT_VIDEO_QUALITY_SLIDER.release(update_output_video_quality, inputs = OUTPUT_VIDEO_QUALITY_SLIDER)
OUTPUT_VIDEO_RESOLUTION_DROPDOWN.change(update_output_video_resolution, inputs = OUTPUT_VIDEO_RESOLUTION_DROPDOWN) OUTPUT_VIDEO_RESOLUTION_DROPDOWN.change(update_output_video_resolution, inputs = OUTPUT_VIDEO_RESOLUTION_DROPDOWN)
OUTPUT_VIDEO_FPS_SLIDER.change(update_output_video_fps, inputs = OUTPUT_VIDEO_FPS_SLIDER) OUTPUT_VIDEO_FPS_SLIDER.release(update_output_video_fps, inputs = OUTPUT_VIDEO_FPS_SLIDER)
multi_component_names : List[ComponentName] =\
for ui_component in get_ui_components(
[ [
'target_image', 'target_image',
'target_video' 'target_video'
] ]):
for component_name in multi_component_names:
component = get_ui_component(component_name)
if component:
for method in [ 'upload', 'change', 'clear' ]: for method in [ 'upload', 'change', 'clear' ]:
getattr(component, method)(remote_update, outputs = [ OUTPUT_IMAGE_QUALITY_SLIDER, OUTPUT_IMAGE_RESOLUTION_DROPDOWN, OUTPUT_VIDEO_ENCODER_DROPDOWN, OUTPUT_VIDEO_PRESET_DROPDOWN, OUTPUT_VIDEO_QUALITY_SLIDER, OUTPUT_VIDEO_RESOLUTION_DROPDOWN, OUTPUT_VIDEO_FPS_SLIDER ]) getattr(ui_component, method)(remote_update, outputs = [ OUTPUT_IMAGE_QUALITY_SLIDER, OUTPUT_IMAGE_RESOLUTION_DROPDOWN, OUTPUT_VIDEO_ENCODER_DROPDOWN, OUTPUT_VIDEO_PRESET_DROPDOWN, OUTPUT_VIDEO_QUALITY_SLIDER, OUTPUT_VIDEO_RESOLUTION_DROPDOWN, OUTPUT_VIDEO_FPS_SLIDER ])
def remote_update() -> Tuple[gradio.Slider, gradio.Dropdown, gradio.Dropdown, gradio.Dropdown, gradio.Slider, gradio.Dropdown, gradio.Slider]: def remote_update() -> Tuple[gradio.Slider, gradio.Dropdown, gradio.Dropdown, gradio.Dropdown, gradio.Slider, gradio.Dropdown, gradio.Slider]:

View File

@ -1,4 +1,4 @@
from typing import Any, Dict, List, Optional from typing import Any, Dict, Optional
from time import sleep from time import sleep
import cv2 import cv2
import gradio import gradio
@ -16,8 +16,7 @@ from facefusion.vision import get_video_frame, count_video_frame_total, normaliz
from facefusion.filesystem import is_image, is_video, filter_audio_paths from facefusion.filesystem import is_image, is_video, filter_audio_paths
from facefusion.content_analyser import analyse_frame from facefusion.content_analyser import analyse_frame
from facefusion.processors.frame.core import load_frame_processor_module from facefusion.processors.frame.core import load_frame_processor_module
from facefusion.uis.typing import ComponentName from facefusion.uis.core import get_ui_component, get_ui_components, register_ui_component
from facefusion.uis.core import get_ui_component, register_ui_component
PREVIEW_IMAGE : Optional[gradio.Image] = None PREVIEW_IMAGE : Optional[gradio.Image] = None
PREVIEW_FRAME_SLIDER : Optional[gradio.Slider] = None PREVIEW_FRAME_SLIDER : Optional[gradio.Slider] = None
@ -72,69 +71,73 @@ def listen() -> None:
reference_face_position_gallery = get_ui_component('reference_face_position_gallery') reference_face_position_gallery = get_ui_component('reference_face_position_gallery')
if reference_face_position_gallery: if reference_face_position_gallery:
reference_face_position_gallery.select(update_preview_image, inputs = PREVIEW_FRAME_SLIDER, outputs = PREVIEW_IMAGE) reference_face_position_gallery.select(update_preview_image, inputs = PREVIEW_FRAME_SLIDER, outputs = PREVIEW_IMAGE)
multi_one_component_names : List[ComponentName] =\
for ui_component in get_ui_components(
[ [
'source_audio', 'source_audio',
'source_image', 'source_image',
'target_image', 'target_image',
'target_video' 'target_video'
] ]):
for component_name in multi_one_component_names:
component = get_ui_component(component_name)
if component:
for method in [ 'upload', 'change', 'clear' ]: for method in [ 'upload', 'change', 'clear' ]:
getattr(component, method)(update_preview_image, inputs = PREVIEW_FRAME_SLIDER, outputs = PREVIEW_IMAGE) getattr(ui_component, method)(update_preview_image, inputs = PREVIEW_FRAME_SLIDER, outputs = PREVIEW_IMAGE)
multi_two_component_names : List[ComponentName] =\
for ui_component in get_ui_components(
[ [
'target_image', 'target_image',
'target_video' 'target_video'
] ]):
for component_name in multi_two_component_names:
component = get_ui_component(component_name)
if component:
for method in [ 'upload', 'change', 'clear' ]: for method in [ 'upload', 'change', 'clear' ]:
getattr(component, method)(update_preview_frame_slider, outputs = PREVIEW_FRAME_SLIDER) getattr(ui_component, method)(update_preview_frame_slider, outputs = PREVIEW_FRAME_SLIDER)
change_one_component_names : List[ComponentName] =\
for ui_component in get_ui_components(
[ [
'face_debugger_items_checkbox_group', 'face_debugger_items_checkbox_group',
'face_selector_mode_dropdown',
'face_mask_types_checkbox_group',
'face_mask_region_checkbox_group',
'face_analyser_order_dropdown',
'face_analyser_age_dropdown',
'face_analyser_gender_dropdown'
]):
ui_component.change(update_preview_image, inputs = PREVIEW_FRAME_SLIDER, outputs = PREVIEW_IMAGE)
for ui_component in get_ui_components(
[
'face_enhancer_blend_slider', 'face_enhancer_blend_slider',
'frame_colorizer_blend_slider',
'frame_enhancer_blend_slider', 'frame_enhancer_blend_slider',
'trim_frame_start_slider', 'trim_frame_start_slider',
'trim_frame_end_slider', 'trim_frame_end_slider',
'face_selector_mode_dropdown',
'reference_face_distance_slider', 'reference_face_distance_slider',
'face_mask_types_checkbox_group',
'face_mask_blur_slider', 'face_mask_blur_slider',
'face_mask_padding_top_slider', 'face_mask_padding_top_slider',
'face_mask_padding_bottom_slider', 'face_mask_padding_bottom_slider',
'face_mask_padding_left_slider', 'face_mask_padding_left_slider',
'face_mask_padding_right_slider', 'face_mask_padding_right_slider',
'face_mask_region_checkbox_group',
'face_analyser_order_dropdown',
'face_analyser_age_dropdown',
'face_analyser_gender_dropdown',
'output_video_fps_slider' 'output_video_fps_slider'
] ]):
for component_name in change_one_component_names: ui_component.release(update_preview_image, inputs = PREVIEW_FRAME_SLIDER, outputs = PREVIEW_IMAGE)
component = get_ui_component(component_name)
if component: for ui_component in get_ui_components(
component.change(update_preview_image, inputs = PREVIEW_FRAME_SLIDER, outputs = PREVIEW_IMAGE)
change_two_component_names : List[ComponentName] =\
[ [
'frame_processors_checkbox_group', 'frame_processors_checkbox_group',
'face_enhancer_model_dropdown', 'face_enhancer_model_dropdown',
'face_swapper_model_dropdown', 'face_swapper_model_dropdown',
'frame_colorizer_model_dropdown',
'frame_enhancer_model_dropdown', 'frame_enhancer_model_dropdown',
'lip_syncer_model_dropdown', 'lip_syncer_model_dropdown',
'face_detector_model_dropdown', 'face_detector_model_dropdown',
'face_detector_size_dropdown', 'face_detector_size_dropdown'
]):
ui_component.change(clear_and_update_preview_image, inputs = PREVIEW_FRAME_SLIDER, outputs = PREVIEW_IMAGE)
for ui_component in get_ui_components(
[
'face_detector_score_slider', 'face_detector_score_slider',
'face_landmarker_score_slider' 'face_landmarker_score_slider'
] ]):
for component_name in change_two_component_names: ui_component.release(clear_and_update_preview_image, inputs = PREVIEW_FRAME_SLIDER, outputs = PREVIEW_IMAGE)
component = get_ui_component(component_name)
if component:
component.change(clear_and_update_preview_image, inputs = PREVIEW_FRAME_SLIDER, outputs = PREVIEW_IMAGE)
def clear_and_update_preview_image(frame_number : int = 0) -> gradio.Image: def clear_and_update_preview_image(frame_number : int = 0) -> gradio.Image:

View File

@ -47,8 +47,8 @@ def render() -> None:
def listen() -> None: def listen() -> None:
TRIM_FRAME_START_SLIDER.change(update_trim_frame_start, inputs = TRIM_FRAME_START_SLIDER) TRIM_FRAME_START_SLIDER.release(update_trim_frame_start, inputs = TRIM_FRAME_START_SLIDER)
TRIM_FRAME_END_SLIDER.change(update_trim_frame_end, inputs = TRIM_FRAME_END_SLIDER) TRIM_FRAME_END_SLIDER.release(update_trim_frame_end, inputs = TRIM_FRAME_END_SLIDER)
target_video = get_ui_component('target_video') target_video = get_ui_component('target_video')
if target_video: if target_video:
for method in [ 'upload', 'change', 'clear' ]: for method in [ 'upload', 'change', 'clear' ]:

View File

@ -1,4 +1,4 @@
from typing import Optional, Generator, Deque, List from typing import Optional, Generator, Deque
import os import os
import platform import platform
import subprocess import subprocess
@ -19,8 +19,8 @@ from facefusion.face_analyser import get_average_face
from facefusion.processors.frame.core import get_frame_processors_modules, load_frame_processor_module from facefusion.processors.frame.core import get_frame_processors_modules, load_frame_processor_module
from facefusion.ffmpeg import open_ffmpeg from facefusion.ffmpeg import open_ffmpeg
from facefusion.vision import normalize_frame_color, read_static_images, unpack_resolution from facefusion.vision import normalize_frame_color, read_static_images, unpack_resolution
from facefusion.uis.typing import StreamMode, WebcamMode, ComponentName from facefusion.uis.typing import StreamMode, WebcamMode
from facefusion.uis.core import get_ui_component from facefusion.uis.core import get_ui_component, get_ui_components
WEBCAM_CAPTURE : Optional[cv2.VideoCapture] = None WEBCAM_CAPTURE : Optional[cv2.VideoCapture] = None
WEBCAM_IMAGE : Optional[gradio.Image] = None WEBCAM_IMAGE : Optional[gradio.Image] = None
@ -76,7 +76,8 @@ def listen() -> None:
if webcam_mode_radio and webcam_resolution_dropdown and webcam_fps_slider: if webcam_mode_radio and webcam_resolution_dropdown and webcam_fps_slider:
start_event = WEBCAM_START_BUTTON.click(start, inputs = [ webcam_mode_radio, webcam_resolution_dropdown, webcam_fps_slider ], outputs = WEBCAM_IMAGE) start_event = WEBCAM_START_BUTTON.click(start, inputs = [ webcam_mode_radio, webcam_resolution_dropdown, webcam_fps_slider ], outputs = WEBCAM_IMAGE)
WEBCAM_STOP_BUTTON.click(stop, cancels = start_event) WEBCAM_STOP_BUTTON.click(stop, cancels = start_event)
change_two_component_names : List[ComponentName] =\
for ui_component in get_ui_components(
[ [
'frame_processors_checkbox_group', 'frame_processors_checkbox_group',
'face_swapper_model_dropdown', 'face_swapper_model_dropdown',
@ -84,11 +85,8 @@ def listen() -> None:
'frame_enhancer_model_dropdown', 'frame_enhancer_model_dropdown',
'lip_syncer_model_dropdown', 'lip_syncer_model_dropdown',
'source_image' 'source_image'
] ]):
for component_name in change_two_component_names: ui_component.change(update, cancels = start_event)
component = get_ui_component(component_name)
if component:
component.change(update, cancels = start_event)
def start(webcam_mode : WebcamMode, webcam_resolution : str, webcam_fps : Fps) -> Generator[VisionFrame, None, None]: def start(webcam_mode : WebcamMode, webcam_resolution : str, webcam_fps : Fps) -> Generator[VisionFrame, None, None]:

View File

@ -51,14 +51,24 @@ def get_ui_layouts_modules(ui_layouts : List[str]) -> List[ModuleType]:
return UI_LAYOUT_MODULES return UI_LAYOUT_MODULES
def get_ui_component(name : ComponentName) -> Optional[Component]: def get_ui_component(component_name : ComponentName) -> Optional[Component]:
if name in UI_COMPONENTS: if component_name in UI_COMPONENTS:
return UI_COMPONENTS[name] return UI_COMPONENTS[component_name]
return None return None
def register_ui_component(name : ComponentName, component: Component) -> None: def get_ui_components(component_names : List[ComponentName]) -> Optional[List[Component]]:
UI_COMPONENTS[name] = component ui_components = []
for component_name in component_names:
component = get_ui_component(component_name)
if component:
ui_components.append(component)
return ui_components
def register_ui_component(component_name : ComponentName, component: Component) -> None:
UI_COMPONENTS[component_name] = component
def launch() -> None: def launch() -> None:

View File

@ -11,6 +11,7 @@ def pre_check() -> bool:
conditional_download('.assets/examples', conditional_download('.assets/examples',
[ [
'https://github.com/facefusion/facefusion-assets/releases/download/examples/source.jpg', 'https://github.com/facefusion/facefusion-assets/releases/download/examples/source.jpg',
'https://github.com/facefusion/facefusion-assets/releases/download/examples/source.mp3',
'https://github.com/facefusion/facefusion-assets/releases/download/examples/target-240p.mp4', 'https://github.com/facefusion/facefusion-assets/releases/download/examples/target-240p.mp4',
'https://github.com/facefusion/facefusion-assets/releases/download/examples/target-360p.mp4', 'https://github.com/facefusion/facefusion-assets/releases/download/examples/target-360p.mp4',
'https://github.com/facefusion/facefusion-assets/releases/download/examples/target-540p.mp4', 'https://github.com/facefusion/facefusion-assets/releases/download/examples/target-540p.mp4',

View File

@ -6,7 +6,7 @@ import base64
def encode_array_to_base64(array : numpy.ndarray[Any, Any]) -> str: def encode_array_to_base64(array : numpy.ndarray[Any, Any]) -> str:
buffer = cv2.imencode('.jpg', array[:, :, ::-1])[1] buffer = cv2.imencode('.jpg', array[:, :, ::-1])[1]
return 'data:image/jpg;base64,' + base64.b64encode(buffer.tobytes()).decode('utf-8') return 'data:image/jpeg;base64,' + base64.b64encode(buffer.tobytes()).decode('utf-8')
def encode_pil_to_base64(image : Any) -> str: def encode_pil_to_base64(image : Any) -> str:

View File

@ -34,6 +34,8 @@ ComponentName = Literal\
'face_enhancer_model_dropdown', 'face_enhancer_model_dropdown',
'face_enhancer_blend_slider', 'face_enhancer_blend_slider',
'face_swapper_model_dropdown', 'face_swapper_model_dropdown',
'frame_colorizer_model_dropdown',
'frame_colorizer_blend_slider',
'frame_enhancer_model_dropdown', 'frame_enhancer_model_dropdown',
'frame_enhancer_blend_slider', 'frame_enhancer_blend_slider',
'lip_syncer_model_dropdown', 'lip_syncer_model_dropdown',

View File

@ -0,0 +1,132 @@
from typing import Any, Tuple
from time import sleep
import threading
import scipy
import numpy
import onnxruntime
import facefusion.globals
from facefusion import process_manager
from facefusion.typing import ModelSet, AudioChunk, Audio
from facefusion.execution import apply_execution_provider_options
from facefusion.filesystem import resolve_relative_path, is_file
from facefusion.download import conditional_download
VOICE_EXTRACTOR = None
THREAD_SEMAPHORE : threading.Semaphore = threading.Semaphore()
THREAD_LOCK : threading.Lock = threading.Lock()
MODELS : ModelSet =\
{
'voice_extractor':
{
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/voice_extractor.onnx',
'path': resolve_relative_path('../.assets/models/voice_extractor.onnx')
}
}
def get_voice_extractor() -> Any:
global VOICE_EXTRACTOR
with THREAD_LOCK:
while process_manager.is_checking():
sleep(0.5)
if VOICE_EXTRACTOR is None:
model_path = MODELS.get('voice_extractor').get('path')
VOICE_EXTRACTOR = onnxruntime.InferenceSession(model_path, providers = apply_execution_provider_options(facefusion.globals.execution_providers))
return VOICE_EXTRACTOR
def clear_voice_extractor() -> None:
global VOICE_EXTRACTOR
VOICE_EXTRACTOR = None
def pre_check() -> bool:
download_directory_path = resolve_relative_path('../.assets/models')
model_url = MODELS.get('voice_extractor').get('url')
model_path = MODELS.get('voice_extractor').get('path')
if not facefusion.globals.skip_download:
process_manager.check()
conditional_download(download_directory_path, [ model_url ])
process_manager.end()
return is_file(model_path)
def batch_extract_voice(audio : Audio, chunk_size : int, step_size : int) -> Audio:
temp_audio = numpy.zeros((audio.shape[0], 2)).astype(numpy.float32)
temp_chunk = numpy.zeros((audio.shape[0], 2)).astype(numpy.float32)
for start in range(0, audio.shape[0], step_size):
end = min(start + chunk_size, audio.shape[0])
temp_audio[start:end, ...] += extract_voice(audio[start:end, ...])
temp_chunk[start:end, ...] += 1
audio = temp_audio / temp_chunk
return audio
def extract_voice(temp_audio_chunk : AudioChunk) -> AudioChunk:
voice_extractor = get_voice_extractor()
chunk_size = 1024 * (voice_extractor.get_inputs()[0].shape[3] - 1)
trim_size = 3840
temp_audio_chunk, pad_size = prepare_audio_chunk(temp_audio_chunk.T, chunk_size, trim_size)
temp_audio_chunk = decompose_audio_chunk(temp_audio_chunk, trim_size)
with THREAD_SEMAPHORE:
temp_audio_chunk = voice_extractor.run(None,
{
voice_extractor.get_inputs()[0].name: temp_audio_chunk
})[0]
temp_audio_chunk = compose_audio_chunk(temp_audio_chunk, trim_size)
temp_audio_chunk = normalize_audio_chunk(temp_audio_chunk, chunk_size, trim_size, pad_size)
return temp_audio_chunk
def prepare_audio_chunk(temp_audio_chunk : AudioChunk, chunk_size : int, trim_size : int) -> Tuple[AudioChunk, int]:
step_size = chunk_size - 2 * trim_size
pad_size = step_size - temp_audio_chunk.shape[1] % step_size
audio_chunk_size = temp_audio_chunk.shape[1] + pad_size
temp_audio_chunk = temp_audio_chunk.astype(numpy.float32) / numpy.iinfo(numpy.int16).max
temp_audio_chunk = numpy.pad(temp_audio_chunk, ((0, 0), (trim_size, trim_size + pad_size)))
temp_audio_chunks = []
for index in range(0, audio_chunk_size, step_size):
temp_audio_chunk = temp_audio_chunk[:, index:index + chunk_size]
temp_audio_chunks.append(temp_audio_chunk)
temp_audio_chunk = numpy.concatenate(temp_audio_chunks, axis = 0)
temp_audio_chunk = temp_audio_chunk.reshape((-1, chunk_size))
return temp_audio_chunk, pad_size
def decompose_audio_chunk(temp_audio_chunk : AudioChunk, trim_size : int) -> AudioChunk:
frame_size = 7680
frame_overlap = 6656
voice_extractor_shape = get_voice_extractor().get_inputs()[0].shape
window = scipy.signal.windows.hann(frame_size)
temp_audio_chunk = scipy.signal.stft(temp_audio_chunk, nperseg = frame_size, noverlap = frame_overlap, window = window)[2]
temp_audio_chunk = numpy.stack((numpy.real(temp_audio_chunk), numpy.imag(temp_audio_chunk)), axis = -1).transpose((0, 3, 1, 2))
temp_audio_chunk = temp_audio_chunk.reshape(-1, 2, 2, trim_size + 1, voice_extractor_shape[3]).reshape(-1, voice_extractor_shape[1], trim_size + 1, voice_extractor_shape[3])
temp_audio_chunk = temp_audio_chunk[:, :, :voice_extractor_shape[2]]
temp_audio_chunk /= numpy.sqrt(1.0 / window.sum() ** 2)
return temp_audio_chunk
def compose_audio_chunk(temp_audio_chunk : AudioChunk, trim_size : int) -> AudioChunk:
frame_size = 7680
frame_overlap = 6656
voice_extractor_shape = get_voice_extractor().get_inputs()[0].shape
window = scipy.signal.windows.hann(frame_size)
temp_audio_chunk = numpy.pad(temp_audio_chunk, ((0, 0), (0, 0), (0, trim_size + 1 - voice_extractor_shape[2]), (0, 0)))
temp_audio_chunk = temp_audio_chunk.reshape(-1, 2, trim_size + 1, voice_extractor_shape[3]).transpose((0, 2, 3, 1))
temp_audio_chunk = temp_audio_chunk[:, :, :, 0] + 1j * temp_audio_chunk[:, :, :, 1]
temp_audio_chunk = scipy.signal.istft(temp_audio_chunk, nperseg = frame_size, noverlap = frame_overlap, window = window)[1]
temp_audio_chunk *= numpy.sqrt(1.0 / window.sum() ** 2)
return temp_audio_chunk
def normalize_audio_chunk(temp_audio_chunk : AudioChunk, chunk_size : int, trim_size : int, pad_size : int) -> AudioChunk:
temp_audio_chunk = temp_audio_chunk.reshape((-1, 2, chunk_size))
temp_audio_chunk = temp_audio_chunk[:, :, trim_size:-trim_size].transpose(1, 0, 2)
temp_audio_chunk = temp_audio_chunk.reshape(2, -1)[:, :-pad_size].T
return temp_audio_chunk

View File

@ -2,6 +2,7 @@ from typing import Any, Dict, Optional
WORDING : Dict[str, Any] =\ WORDING : Dict[str, Any] =\
{ {
'conda_not_activated': 'Conda is not activated',
'python_not_supported': 'Python version is not supported, upgrade to {version} or higher', 'python_not_supported': 'Python version is not supported, upgrade to {version} or higher',
'ffmpeg_not_installed': 'FFMpeg is not installed', 'ffmpeg_not_installed': 'FFMpeg is not installed',
'creating_temp': 'Creating temporary resources', 'creating_temp': 'Creating temporary resources',
@ -52,12 +53,13 @@ WORDING : Dict[str, Any] =\
{ {
# installer # installer
'install_dependency': 'select the variant of {dependency} to install', 'install_dependency': 'select the variant of {dependency} to install',
'skip_venv': 'skip the virtual environment check', 'skip_conda': 'skip the conda environment check',
# general # general
'source': 'choose single or multiple source images or audios', 'source': 'choose single or multiple source images or audios',
'target': 'choose single target image or video', 'target': 'choose single target image or video',
'output': 'specify the output file or directory', 'output': 'specify the output file or directory',
# misc # misc
'force_download': 'force automate downloads and exit',
'skip_download': 'omit automate downloads and remote lookups', 'skip_download': 'omit automate downloads and remote lookups',
'headless': 'run the program without a user interface', 'headless': 'run the program without a user interface',
'log_level': 'adjust the message severity displayed in the terminal', 'log_level': 'adjust the message severity displayed in the terminal',
@ -66,10 +68,10 @@ WORDING : Dict[str, Any] =\
'execution_thread_count': 'specify the amount of parallel threads while processing', 'execution_thread_count': 'specify the amount of parallel threads while processing',
'execution_queue_count': 'specify the amount of frames each thread is processing', 'execution_queue_count': 'specify the amount of frames each thread is processing',
# memory # memory
'video_memory_strategy': 'balance fast frame processing and low vram usage', 'video_memory_strategy': 'balance fast frame processing and low VRAM usage',
'system_memory_limit': 'limit the available ram that can be used while processing', 'system_memory_limit': 'limit the available RAM that can be used while processing',
# face analyser # face analyser
'face_analyser_order': 'specify the order in which the face analyser detects faces.', 'face_analyser_order': 'specify the order in which the face analyser detects faces',
'face_analyser_age': 'filter the detected faces based on their age', 'face_analyser_age': 'filter the detected faces based on their age',
'face_analyser_gender': 'filter the detected faces based on their gender', 'face_analyser_gender': 'filter the detected faces based on their gender',
'face_detector_model': 'choose the model responsible for detecting the face', 'face_detector_model': 'choose the model responsible for detecting the face',
@ -106,6 +108,8 @@ WORDING : Dict[str, Any] =\
'face_enhancer_model': 'choose the model responsible for enhancing the face', 'face_enhancer_model': 'choose the model responsible for enhancing the face',
'face_enhancer_blend': 'blend the enhanced into the previous face', 'face_enhancer_blend': 'blend the enhanced into the previous face',
'face_swapper_model': 'choose the model responsible for swapping the face', 'face_swapper_model': 'choose the model responsible for swapping the face',
'frame_colorizer_model': 'choose the model responsible for colorizing the frame',
'frame_colorizer_blend': 'blend the colorized into the previous frame',
'frame_enhancer_model': 'choose the model responsible for enhancing the frame', 'frame_enhancer_model': 'choose the model responsible for enhancing the frame',
'frame_enhancer_blend': 'blend the enhanced into the previous frame', 'frame_enhancer_blend': 'blend the enhanced into the previous frame',
'lip_syncer_model': 'choose the model responsible for syncing the lips', 'lip_syncer_model': 'choose the model responsible for syncing the lips',
@ -160,6 +164,8 @@ WORDING : Dict[str, Any] =\
'face_enhancer_model_dropdown': 'FACE ENHANCER MODEL', 'face_enhancer_model_dropdown': 'FACE ENHANCER MODEL',
'face_enhancer_blend_slider': 'FACE ENHANCER BLEND', 'face_enhancer_blend_slider': 'FACE ENHANCER BLEND',
'face_swapper_model_dropdown': 'FACE SWAPPER MODEL', 'face_swapper_model_dropdown': 'FACE SWAPPER MODEL',
'frame_colorizer_model_dropdown': 'FRAME COLORIZER MODEL',
'frame_colorizer_blend_slider': 'FRAME COLORIZER BLEND',
'frame_enhancer_model_dropdown': 'FRAME ENHANCER MODEL', 'frame_enhancer_model_dropdown': 'FRAME ENHANCER MODEL',
'frame_enhancer_blend_slider': 'FRAME ENHANCER BLEND', 'frame_enhancer_blend_slider': 'FRAME ENHANCER BLEND',
'lip_syncer_model_dropdown': 'LIP SYNCER MODEL', 'lip_syncer_model_dropdown': 'LIP SYNCER MODEL',

View File

@ -1,7 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os
import subprocess import subprocess
os.environ['PIP_BREAK_SYSTEM_PACKAGES'] = '1'
subprocess.call([ 'pip', 'install', 'inquirer', '-q' ]) subprocess.call([ 'pip', 'install', 'inquirer', '-q' ])
from facefusion import installer from facefusion import installer

View File

@ -1,8 +1,8 @@
filetype==1.2.0 filetype==1.2.0
gradio==3.50.2 gradio==3.50.2
numpy==1.26.4 numpy==1.26.4
onnx==1.15.0 onnx==1.16.0
onnxruntime==1.16.3 onnxruntime==1.17.1
opencv-python==4.8.1.78 opencv-python==4.8.1.78
psutil==5.9.8 psutil==5.9.8
tqdm==4.66.2 tqdm==4.66.2

View File

@ -1,4 +1,4 @@
from facefusion.common_helper import create_metavar, create_int_range, create_float_range, extract_major_version from facefusion.common_helper import create_metavar, create_int_range, create_float_range
def test_create_metavar() -> None: def test_create_metavar() -> None:
@ -14,8 +14,3 @@ def test_create_float_range() -> None:
assert create_float_range(0.0, 1.0, 0.5) == [ 0.0, 0.5, 1.0 ] assert create_float_range(0.0, 1.0, 0.5) == [ 0.0, 0.5, 1.0 ]
assert create_float_range(0.0, 0.2, 0.05) == [ 0.0, 0.05, 0.10, 0.15, 0.20 ] assert create_float_range(0.0, 0.2, 0.05) == [ 0.0, 0.05, 0.10, 0.15, 0.20 ]
def test_extract_major_version() -> None:
assert extract_major_version('1') == (1, 0)
assert extract_major_version('1.1') == (1, 1)
assert extract_major_version('1.2.0') == (1, 2)