Move code to face helper

This commit is contained in:
henryruhs 2023-10-14 16:16:13 +02:00
parent f34a3574ac
commit 5dcf36ea64
4 changed files with 59 additions and 47 deletions

52
facefusion/face_helper.py Normal file
View File

@ -0,0 +1,52 @@
from typing import Tuple, Dict
import cv2
import numpy
from facefusion.typing import Face, Frame, Matrix, Template
TEMPLATES : Dict[str, numpy.array] =\
{
'arface': numpy.array(
[
[ 38.2946, 51.6963 ],
[ 73.5318, 51.5014 ],
[ 56.0252, 71.7366 ],
[ 41.5493, 92.3655 ],
[ 70.7299, 92.2041 ]
]),
'ffhq': numpy.array(
[
[ 192.98138, 239.94708 ],
[ 318.90277, 240.1936 ],
[ 256.63416, 314.01935 ],
[ 201.26117, 371.41043 ],
[ 313.08905, 371.15118 ]
])
}
def warp_face(target_face : Face, temp_frame : Frame, template : Template) -> Tuple[Frame, Matrix]:
affine_matrix = cv2.estimateAffinePartial2D(target_face.kps, TEMPLATES[template], method = cv2.LMEDS)[0]
crop_frame = cv2.warpAffine(temp_frame, affine_matrix, (512, 512))
return crop_frame, affine_matrix
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

View File

@ -46,16 +46,8 @@ def run(program : ArgumentParser) -> None:
else: else:
answers = inquirer.prompt( answers = inquirer.prompt(
[ [
inquirer.List( inquirer.List('torch', message = wording.get('install_dependency_help').format(dependency = 'torch'), choices = list(TORCH.keys())),
'torch', inquirer.List('onnxruntime', message = wording.get('install_dependency_help').format(dependency = 'onnxruntime'), choices = list(ONNXRUNTIMES.keys()))
message = wording.get('install_dependency_help').format(dependency = 'torch'),
choices = list(TORCH.keys())
),
inquirer.List(
'onnxruntime',
message = wording.get('install_dependency_help').format(dependency = 'onnxruntime'),
choices = list(ONNXRUNTIMES.keys())
)
]) ])
if answers is not None: if answers is not None:
torch = answers['torch'] torch = answers['torch']

View File

@ -1,4 +1,4 @@
from typing import Any, List, Tuple, Dict, Literal, Optional from typing import Any, List, Dict, Literal, Optional
from argparse import ArgumentParser from argparse import ArgumentParser
import cv2 import cv2
import threading import threading
@ -9,7 +9,8 @@ import facefusion.globals
from facefusion import wording from facefusion import wording
from facefusion.core import update_status from facefusion.core import update_status
from facefusion.face_analyser import get_many_faces, clear_face_analyser from facefusion.face_analyser import get_many_faces, clear_face_analyser
from facefusion.typing import Face, Frame, Matrix, Update_Process, ProcessMode, ModelValue, OptionsWithModel from facefusion.face_helper import warp_face, paste_back
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.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.vision import read_image, read_static_image, write_image
from facefusion.processors.frame import globals as frame_processors_globals from facefusion.processors.frame import globals as frame_processors_globals
@ -128,7 +129,7 @@ def post_process() -> None:
def enhance_face(target_face: Face, temp_frame: Frame) -> Frame: def enhance_face(target_face: Face, temp_frame: Frame) -> Frame:
frame_processor = get_frame_processor() frame_processor = get_frame_processor()
crop_frame, affine_matrix = warp_face(target_face, temp_frame) crop_frame, affine_matrix = warp_face(target_face, temp_frame, 'ffhq')
crop_frame = prepare_crop_frame(crop_frame) crop_frame = prepare_crop_frame(crop_frame)
frame_processor_inputs = {} frame_processor_inputs = {}
for frame_processor_input in frame_processor.get_inputs(): for frame_processor_input in frame_processor.get_inputs():
@ -144,20 +145,6 @@ def enhance_face(target_face: Face, temp_frame: Frame) -> Frame:
return temp_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: def prepare_crop_frame(crop_frame : Frame) -> Frame:
crop_frame = crop_frame[:, :, ::-1] / 255.0 crop_frame = crop_frame[:, :, ::-1] / 255.0
crop_frame = (crop_frame - 0.5) / 0.5 crop_frame = (crop_frame - 0.5) / 0.5
@ -174,26 +161,6 @@ def normalize_crop_frame(crop_frame : Frame) -> Frame:
return crop_frame 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: def blend_frame(temp_frame : Frame, paste_frame : Frame) -> Frame:
face_enhancer_blend = 1 - (frame_processors_globals.face_enhancer_blend / 100) 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) temp_frame = cv2.addWeighted(temp_frame, face_enhancer_blend, paste_frame, 1 - face_enhancer_blend, 0)

View File

@ -9,6 +9,7 @@ Matrix = numpy.ndarray[Any, Any]
Update_Process = Callable[[], None] Update_Process = Callable[[], None]
Process_Frames = Callable[[str, List[str], Update_Process], None] Process_Frames = Callable[[str, List[str], Update_Process], None]
Template = Literal[ 'arcface', 'ffhq' ]
ProcessMode = Literal[ 'output', 'preview', 'stream' ] ProcessMode = Literal[ 'output', 'preview', 'stream' ]
FaceRecognition = Literal[ 'reference', 'many' ] FaceRecognition = Literal[ 'reference', 'many' ]
FaceAnalyserDirection = Literal[ 'left-right', 'right-left', 'top-bottom', 'bottom-top', 'small-large', 'large-small' ] FaceAnalyserDirection = Literal[ 'left-right', 'right-left', 'top-bottom', 'bottom-top', 'small-large', 'large-small' ]