* Improve typing for our callbacks

* Return 0 for get_download_size

* Introduce ONNX powered face enhancer

* Introduce ONNX powered face enhancer

* Introduce ONNX powered face enhancer

* Remove tile processing from frame enhancer

* Fix video compress translation for libvpx-vp9

* Allow zero values for video compression

* Develop (#134)

* Introduce model options to the frame processors

* Finish UI to select frame processors models

* Simplify frame processors options

* Fix lint in CI

* Rename all kind of settings to options

* Add blend to enhancers

* Simplify webcam mode naming

* Bypass SSL issues under Windows

* Fix blend of frame enhancer

* Massive CLI refactoring, Register and apply ARGS via the frame processors

* Refine UI theme and introduce donate button

* Update dependencies and fix cpu only torch

* Update dependencies and fix cpu only torch

* Fix theme, Fix frame_processors in headless mode

* Remove useless astype

* Disable CoreML for the ONNX face enhancer

* Disable CoreML for the ONNX face enhancer

* Predict webcam too

* Improve resize of preview

* Change output quality defaults, Move options to the right

* Support for codeformer model

* Update the typo

* Add GPEN and GFPGAN 1.2

* Extract blend_frame methods

* Extend the installer

* Revert broken Gradio

* Rework on ui components

* Move output path selector to the output options

* Remove tons of pointless component updates

* Reset more base theme styling

* Use latest Gradio

* Fix the sliders

* More styles

* Update torch to 2.1.0

* Add RealESRNet_x4plus

* Fix that button

* Use latest onnxruntime-silicon

* Looks stable to me

* Lowercase model keys, Update preview and readme
This commit is contained in:
Henry Ruhs
2023-10-09 10:16:13 +02:00
committed by GitHub
parent 3e361e7701
commit a6809c3ccb
53 changed files with 1105 additions and 563 deletions
+5
View File
@@ -0,0 +1,5 @@
from typing import List
face_swapper_models : List[str] = [ 'inswapper_128', 'inswapper_128_fp16' ]
face_enhancer_models : List[str] = [ 'codeformer', 'gfpgan_1.2', 'gfpgan_1.3', 'gfpgan_1.4', 'gpen_bfr_512' ]
frame_enhancer_models : List[str] = [ 'realesrgan_x2plus', 'realesrgan_x4plus', 'realesrnet_x4plus' ]
+7 -2
View File
@@ -5,17 +5,22 @@ import psutil
from concurrent.futures import ThreadPoolExecutor, as_completed
from queue import Queue
from types import ModuleType
from typing import Any, List, Callable
from typing import Any, List
from tqdm import tqdm
import facefusion.globals
from facefusion import wording
from facefusion.typing import Process_Frames
FRAME_PROCESSORS_MODULES : List[ModuleType] = []
FRAME_PROCESSORS_METHODS =\
[
'get_frame_processor',
'clear_frame_processor',
'get_options',
'set_options',
'register_args',
'apply_args',
'pre_check',
'pre_process',
'process_frame',
@@ -57,7 +62,7 @@ def clear_frame_processors_modules() -> None:
FRAME_PROCESSORS_MODULES = []
def multi_process_frames(source_path : str, temp_frame_paths : List[str], process_frames : Callable[[str, List[str], Callable[[], None]], None]) -> None:
def multi_process_frames(source_path : str, temp_frame_paths : List[str], process_frames : Process_Frames) -> None:
progress_bar_format = '{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, {rate_fmt}{postfix}]'
with tqdm(total = len(temp_frame_paths), desc = wording.get('processing'), unit = 'frame', dynamic_ncols = True, bar_format = progress_bar_format) as progress:
with ThreadPoolExecutor(max_workers = facefusion.globals.execution_thread_count) as executor:
+7
View File
@@ -0,0 +1,7 @@
from typing import Optional
face_swapper_model : Optional[str] = None
face_enhancer_model : Optional[str] = None
face_enhancer_blend : Optional[int] = None
frame_enhancer_model : Optional[str] = None
frame_enhancer_blend : Optional[int] = None
@@ -1,21 +1,53 @@
from typing import Any, List, Callable
from typing import Any, List, Tuple, Dict, Literal, Optional
from argparse import ArgumentParser
import cv2
import threading
from gfpgan.utils import GFPGANer
import numpy
import onnxruntime
import facefusion.globals
from facefusion import wording, utilities
from facefusion import wording
from facefusion.core import update_status
from facefusion.face_analyser import get_many_faces, clear_face_analyser
from facefusion.typing import Frame, Face, ProcessMode
from facefusion.typing import Face, Frame, Matrix, Update_Process, ProcessMode, ModelValue, OptionsWithModel
from facefusion.utilities import conditional_download, resolve_relative_path, is_image, is_video, is_file, is_download_done
from facefusion.vision import read_image, read_static_image, write_image
from facefusion.processors.frame import globals as frame_processors_globals
from facefusion.processors.frame import choices as frame_processors_choices
FRAME_PROCESSOR = None
THREAD_SEMAPHORE : threading.Semaphore = threading.Semaphore()
THREAD_LOCK : threading.Lock = threading.Lock()
NAME = 'FACEFUSION.FRAME_PROCESSOR.FACE_ENHANCER'
MODEL_URL = 'https://github.com/facefusion/facefusion-assets/releases/download/models/GFPGANv1.4.pth'
MODEL_PATH = resolve_relative_path('../.assets/models/GFPGANv1.4.pth')
MODELS : Dict[str, ModelValue] =\
{
'codeformer':
{
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/codeformer.onnx',
'path': resolve_relative_path('../.assets/models/codeformer.onnx')
},
'gfpgan_1.2':
{
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/GFPGANv1.2.onnx',
'path': resolve_relative_path('../.assets/models/GFPGANv1.2.onnx')
},
'gfpgan_1.3':
{
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/GFPGANv1.3.onnx',
'path': resolve_relative_path('../.assets/models/GFPGANv1.3.onnx')
},
'gfpgan_1.4':
{
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/GFPGANv1.4.onnx',
'path': resolve_relative_path('../.assets/models/GFPGANv1.4.onnx')
},
'gpen_bfr_512':
{
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/GPEN-BFR-512.onnx',
'path': resolve_relative_path('../.assets/models/GPEN-BFR-512.onnx')
}
}
OPTIONS : Optional[OptionsWithModel] = None
def get_frame_processor() -> Any:
@@ -23,11 +55,8 @@ def get_frame_processor() -> Any:
with THREAD_LOCK:
if FRAME_PROCESSOR is None:
FRAME_PROCESSOR = GFPGANer(
model_path = MODEL_PATH,
upscale = 1,
device = utilities.get_device(facefusion.globals.execution_providers)
)
model_path = get_options('model').get('path')
FRAME_PROCESSOR = onnxruntime.InferenceSession(model_path, providers = facefusion.globals.execution_providers)
return FRAME_PROCESSOR
@@ -37,18 +66,49 @@ def clear_frame_processor() -> None:
FRAME_PROCESSOR = None
def get_options(key : Literal[ 'model' ]) -> Any:
global OPTIONS
if OPTIONS is None:
OPTIONS =\
{
'model': MODELS[frame_processors_globals.face_enhancer_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('--face-enhancer-model', help = wording.get('frame_processor_model_help'), dest = 'face_enhancer_model', default = 'gfpgan_1.4', choices = frame_processors_choices.face_enhancer_models)
program.add_argument('--face-enhancer-blend', help = wording.get('frame_processor_blend_help'), dest= 'face_enhancer_blend', type = int, default= 100, choices = range(101), metavar = '[0-100]')
def apply_args(program : ArgumentParser) -> None:
args = program.parse_args()
frame_processors_globals.face_enhancer_model = args.face_enhancer_model
frame_processors_globals.face_enhancer_blend = args.face_enhancer_blend
def pre_check() -> bool:
if not facefusion.globals.skip_download:
download_directory_path = resolve_relative_path('../.assets/models')
conditional_download(download_directory_path, [ MODEL_URL ])
model_url = get_options('model').get('url')
conditional_download(download_directory_path, [ model_url ])
return True
def pre_process(mode : ProcessMode) -> bool:
if not facefusion.globals.skip_download and not is_download_done(MODEL_URL, MODEL_PATH):
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):
update_status(wording.get('model_download_not_done') + wording.get('exclamation_mark'), NAME)
return False
elif not is_file(MODEL_PATH):
elif not is_file(model_path):
update_status(wording.get('model_file_not_present') + wording.get('exclamation_mark'), NAME)
return False
if mode in [ 'output', 'preview' ] and not is_image(facefusion.globals.target_path) and not is_video(facefusion.globals.target_path):
@@ -66,22 +126,77 @@ def post_process() -> None:
read_static_image.cache_clear()
def enhance_face(target_face : Face, temp_frame : Frame) -> Frame:
start_x, start_y, end_x, end_y = map(int, target_face['bbox'])
padding_x = int((end_x - start_x) * 0.5)
padding_y = int((end_y - start_y) * 0.5)
start_x = max(0, start_x - padding_x)
start_y = max(0, start_y - padding_y)
end_x = max(0, end_x + padding_x)
end_y = max(0, end_y + padding_y)
crop_frame = temp_frame[start_y:end_y, start_x:end_x]
if crop_frame.size:
with THREAD_SEMAPHORE:
_, _, crop_frame = get_frame_processor().enhance(
crop_frame,
paste_back = True
)
temp_frame[start_y:end_y, start_x:end_x] = crop_frame
def enhance_face(target_face: Face, temp_frame: Frame) -> Frame:
frame_processor = get_frame_processor()
crop_frame, affine_matrix = warp_face(target_face, temp_frame)
crop_frame = prepare_crop_frame(crop_frame)
frame_processor_inputs = {}
for frame_processor_input in frame_processor.get_inputs():
if frame_processor_input.name == 'input':
frame_processor_inputs[frame_processor_input.name] = crop_frame
if frame_processor_input.name == 'weight':
frame_processor_inputs[frame_processor_input.name] = numpy.array([ 1 ], dtype = numpy.double)
with THREAD_SEMAPHORE:
crop_frame = frame_processor.run(None, frame_processor_inputs)[0][0]
crop_frame = normalize_crop_frame(crop_frame)
paste_frame = paste_back(temp_frame, crop_frame, affine_matrix)
temp_frame = blend_frame(temp_frame, paste_frame)
return temp_frame
def warp_face(target_face : Face, temp_frame : Frame) -> Tuple[Frame, Matrix]:
template = numpy.array(
[
[ 192.98138, 239.94708 ],
[ 318.90277, 240.1936 ],
[ 256.63416, 314.01935 ],
[ 201.26117, 371.41043 ],
[ 313.08905, 371.15118 ]
])
affine_matrix = cv2.estimateAffinePartial2D(target_face['kps'], template, method = cv2.LMEDS)[0]
crop_frame = cv2.warpAffine(temp_frame, affine_matrix, (512, 512))
return crop_frame, affine_matrix
def prepare_crop_frame(crop_frame : Frame) -> Frame:
crop_frame = crop_frame[:, :, ::-1] / 255.0
crop_frame = (crop_frame - 0.5) / 0.5
crop_frame = numpy.expand_dims(crop_frame.transpose(2, 0, 1), axis = 0).astype(numpy.float32)
return crop_frame
def normalize_crop_frame(crop_frame : Frame) -> Frame:
crop_frame = numpy.clip(crop_frame, -1, 1)
crop_frame = (crop_frame + 1) / 2
crop_frame = crop_frame.transpose(1, 2, 0)
crop_frame = (crop_frame * 255.0).round()
crop_frame = crop_frame.astype(numpy.uint8)[:, :, ::-1]
return crop_frame
def paste_back(temp_frame : Frame, crop_frame : Frame, affine_matrix : Matrix) -> Frame:
inverse_affine_matrix = cv2.invertAffineTransform(affine_matrix)
temp_frame_height, temp_frame_width = temp_frame.shape[0:2]
crop_frame_height, crop_frame_width = crop_frame.shape[0:2]
inverse_crop_frame = cv2.warpAffine(crop_frame, inverse_affine_matrix, (temp_frame_width, temp_frame_height))
inverse_mask = numpy.ones((crop_frame_height, crop_frame_width, 3), dtype = numpy.float32)
inverse_mask_frame = cv2.warpAffine(inverse_mask, inverse_affine_matrix, (temp_frame_width, temp_frame_height))
inverse_mask_frame = cv2.erode(inverse_mask_frame, numpy.ones((2, 2)))
inverse_mask_border = inverse_mask_frame * inverse_crop_frame
inverse_mask_area = numpy.sum(inverse_mask_frame) // 3
inverse_mask_edge = int(inverse_mask_area ** 0.5) // 20
inverse_mask_radius = inverse_mask_edge * 2
inverse_mask_center = cv2.erode(inverse_mask_frame, numpy.ones((inverse_mask_radius, inverse_mask_radius)))
inverse_mask_blur_size = inverse_mask_edge * 2 + 1
inverse_mask_blur_area = cv2.GaussianBlur(inverse_mask_center, (inverse_mask_blur_size, inverse_mask_blur_size), 0)
temp_frame = inverse_mask_blur_area * inverse_mask_border + (1 - inverse_mask_blur_area) * temp_frame
temp_frame = temp_frame.clip(0, 255).astype(numpy.uint8)
return temp_frame
def blend_frame(temp_frame : Frame, paste_frame : Frame) -> Frame:
face_enhancer_blend = 1 - (frame_processors_globals.face_enhancer_blend / 100)
temp_frame = cv2.addWeighted(temp_frame, face_enhancer_blend, paste_frame, 1 - face_enhancer_blend, 0)
return temp_frame
@@ -93,7 +208,7 @@ def process_frame(source_face : Face, reference_face : Face, temp_frame : Frame)
return temp_frame
def process_frames(source_path : str, temp_frame_paths : List[str], update_progress: Callable[[], None]) -> None:
def process_frames(source_path : str, temp_frame_paths : List[str], update_progress : Update_Process) -> None:
for temp_frame_path in temp_frame_paths:
temp_frame = read_image(temp_frame_path)
result_frame = process_frame(None, None, temp_frame)
@@ -1,4 +1,5 @@
from typing import Any, List, Callable
from typing import Any, List, Dict, Literal, Optional
from argparse import ArgumentParser
import insightface
import threading
@@ -8,15 +9,29 @@ from facefusion import wording
from facefusion.core import update_status
from facefusion.face_analyser import get_one_face, get_many_faces, find_similar_faces, clear_face_analyser
from facefusion.face_reference import get_face_reference, set_face_reference
from facefusion.typing import Face, Frame, ProcessMode
from facefusion.typing import Face, Frame, Update_Process, ProcessMode, ModelValue, OptionsWithModel
from facefusion.utilities import conditional_download, resolve_relative_path, is_image, is_video, is_file, is_download_done
from facefusion.vision import read_image, read_static_image, write_image
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()
NAME = 'FACEFUSION.FRAME_PROCESSOR.FACE_SWAPPER'
MODEL_URL = 'https://github.com/facefusion/facefusion-assets/releases/download/models/inswapper_128.onnx'
MODEL_PATH = resolve_relative_path('../.assets/models/inswapper_128.onnx')
MODELS : Dict[str, ModelValue] =\
{
'inswapper_128':
{
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/inswapper_128.onnx',
'path': resolve_relative_path('../.assets/models/inswapper_128.onnx')
},
'inswapper_128_fp16':
{
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/inswapper_128_fp16.onnx',
'path': resolve_relative_path('../.assets/models/inswapper_128_fp16.onnx')
}
}
OPTIONS : Optional[OptionsWithModel] = None
def get_frame_processor() -> Any:
@@ -24,7 +39,8 @@ def get_frame_processor() -> Any:
with THREAD_LOCK:
if FRAME_PROCESSOR is None:
FRAME_PROCESSOR = insightface.model_zoo.get_model(MODEL_PATH, providers = facefusion.globals.execution_providers)
model_path = get_options('model').get('path')
FRAME_PROCESSOR = insightface.model_zoo.get_model(model_path, providers = facefusion.globals.execution_providers)
return FRAME_PROCESSOR
@@ -34,18 +50,47 @@ def clear_frame_processor() -> None:
FRAME_PROCESSOR = None
def get_options(key : Literal[ 'model' ]) -> Any:
global OPTIONS
if OPTIONS is None:
OPTIONS = \
{
'model': MODELS[frame_processors_globals.face_swapper_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('--face-swapper-model', help = wording.get('frame_processor_model_help'), dest = 'face_swapper_model', default = 'inswapper_128', choices = frame_processors_choices.face_swapper_models)
def apply_args(program : ArgumentParser) -> None:
args = program.parse_args()
frame_processors_globals.face_swapper_model = args.face_swapper_model
def pre_check() -> bool:
if not facefusion.globals.skip_download:
download_directory_path = resolve_relative_path('../.assets/models')
conditional_download(download_directory_path, [ MODEL_URL ])
model_url = get_options('model').get('url')
conditional_download(download_directory_path, [ model_url ])
return True
def pre_process(mode : ProcessMode) -> bool:
if not facefusion.globals.skip_download and not is_download_done(MODEL_URL, MODEL_PATH):
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):
update_status(wording.get('model_download_not_done') + wording.get('exclamation_mark'), NAME)
return False
elif not is_file(MODEL_PATH):
elif not is_file(model_path):
update_status(wording.get('model_file_not_present') + wording.get('exclamation_mark'), NAME)
return False
if not is_image(facefusion.globals.source_path):
@@ -87,7 +132,7 @@ def process_frame(source_face : Face, reference_face : Face, temp_frame : Frame)
return temp_frame
def process_frames(source_path : str, temp_frame_paths : List[str], update_progress: Callable[[], None]) -> None:
def process_frames(source_path : str, temp_frame_paths : List[str], update_progress : Update_Process) -> None:
source_face = get_one_face(read_static_image(source_path))
reference_face = get_face_reference() if 'reference' in facefusion.globals.face_recognition else None
for temp_frame_path in temp_frame_paths:
@@ -1,23 +1,47 @@
from typing import Any, List, Callable
from typing import Any, List, Dict, Literal, Optional
from argparse import ArgumentParser
import threading
import cv2
from basicsr.archs.rrdbnet_arch import RRDBNet
from realesrgan import RealESRGANer
import facefusion.globals
import facefusion.processors.frame.core as frame_processors
from facefusion import wording, utilities
from facefusion import wording
from facefusion.core import update_status
from facefusion.face_analyser import clear_face_analyser
from facefusion.typing import Frame, Face, ProcessMode
from facefusion.utilities import conditional_download, resolve_relative_path, is_file, is_download_done
from facefusion.typing import Frame, Face, Update_Process, ProcessMode, ModelValue, OptionsWithModel
from facefusion.utilities import conditional_download, resolve_relative_path, is_file, is_download_done, get_device
from facefusion.vision import read_image, read_static_image, write_image
from facefusion.processors.frame import globals as frame_processors_globals
from facefusion.processors.frame import choices as frame_processors_choices
FRAME_PROCESSOR = None
THREAD_SEMAPHORE : threading.Semaphore = threading.Semaphore()
THREAD_LOCK : threading.Lock = threading.Lock()
NAME = 'FACEFUSION.FRAME_PROCESSOR.FRAME_ENHANCER'
MODEL_URL = 'https://github.com/facefusion/facefusion-assets/releases/download/models/RealESRGAN_x4plus.pth'
MODEL_PATH = resolve_relative_path('../.assets/models/RealESRGAN_x4plus.pth')
MODELS: Dict[str, ModelValue] =\
{
'realesrgan_x2plus':
{
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/RealESRGAN_x2plus.pth',
'path': resolve_relative_path('../.assets/models/RealESRGAN_x2plus.pth'),
'scale': 2
},
'realesrgan_x4plus':
{
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/RealESRGAN_x4plus.pth',
'path': resolve_relative_path('../.assets/models/RealESRGAN_x4plus.pth'),
'scale': 4
},
'realesrnet_x4plus':
{
'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/RealESRNet_x4plus.pth',
'path': resolve_relative_path('../.assets/models/RealESRNet_x4plus.pth'),
'scale': 4
}
}
OPTIONS : Optional[OptionsWithModel] = None
def get_frame_processor() -> Any:
@@ -25,21 +49,17 @@ def get_frame_processor() -> Any:
with THREAD_LOCK:
if FRAME_PROCESSOR is None:
model_path = get_options('model').get('path')
model_scale = get_options('model').get('scale')
FRAME_PROCESSOR = RealESRGANer(
model_path = MODEL_PATH,
model_path = model_path,
model = RRDBNet(
num_in_ch = 3,
num_out_ch = 3,
num_feat = 64,
num_block = 23,
num_grow_ch = 32,
scale = 4
scale = model_scale
),
device = utilities.get_device(facefusion.globals.execution_providers),
tile = 512,
tile_pad = 32,
pre_pad = 0,
scale = 4
device = get_device(facefusion.globals.execution_providers),
scale = model_scale
)
return FRAME_PROCESSOR
@@ -50,18 +70,49 @@ def clear_frame_processor() -> None:
FRAME_PROCESSOR = None
def get_options(key : Literal[ 'model' ]) -> Any:
global OPTIONS
if OPTIONS is None:
OPTIONS = \
{
'model': MODELS[frame_processors_globals.frame_enhancer_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-enhancer-model', help = wording.get('frame_processor_model_help'), dest = 'frame_enhancer_model', default = 'realesrgan_x2plus', choices = frame_processors_choices.frame_enhancer_models)
program.add_argument('--frame-enhancer-blend', help = wording.get('frame_processor_blend_help'), dest = 'frame_enhancer_blend', type = int, default = 100, choices = range(101), metavar = '[0-100]')
def apply_args(program : ArgumentParser) -> None:
args = program.parse_args()
frame_processors_globals.frame_enhancer_model = args.frame_enhancer_model
frame_processors_globals.frame_enhancer_blend = args.frame_enhancer_blend
def pre_check() -> bool:
if not facefusion.globals.skip_download:
download_directory_path = resolve_relative_path('../.assets/models')
conditional_download(download_directory_path, [ MODEL_URL ])
model_url = get_options('model').get('url')
conditional_download(download_directory_path, [ model_url ])
return True
def pre_process(mode : ProcessMode) -> bool:
if not facefusion.globals.skip_download and not is_download_done(MODEL_URL, MODEL_PATH):
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):
update_status(wording.get('model_download_not_done') + wording.get('exclamation_mark'), NAME)
return False
elif not is_file(MODEL_PATH):
elif not is_file(model_path):
update_status(wording.get('model_file_not_present') + wording.get('exclamation_mark'), NAME)
return False
if mode == 'output' and not facefusion.globals.output_path:
@@ -78,7 +129,15 @@ def post_process() -> None:
def enhance_frame(temp_frame : Frame) -> Frame:
with THREAD_SEMAPHORE:
temp_frame, _ = get_frame_processor().enhance(temp_frame, outscale = 1)
paste_frame, _ = get_frame_processor().enhance(temp_frame)
temp_frame = blend_frame(temp_frame, paste_frame)
return temp_frame
def blend_frame(temp_frame : Frame, paste_frame : Frame) -> Frame:
frame_enhancer_blend = 1 - (frame_processors_globals.frame_enhancer_blend / 100)
temp_frame = cv2.resize(temp_frame, (paste_frame.shape[1], paste_frame.shape[0]))
temp_frame = cv2.addWeighted(temp_frame, frame_enhancer_blend, paste_frame, 1 - frame_enhancer_blend, 0)
return temp_frame
@@ -86,7 +145,7 @@ def process_frame(source_face : Face, reference_face : Face, temp_frame : Frame)
return enhance_frame(temp_frame)
def process_frames(source_path : str, temp_frame_paths : List[str], update_progress: Callable[[], None]) -> None:
def process_frames(source_path : str, temp_frame_paths : List[str], update_progress : Update_Process) -> None:
for temp_frame_path in temp_frame_paths:
temp_frame = read_image(temp_frame_path)
result_frame = process_frame(None, None, temp_frame)