add a ldsr inswapper option in sd roop settings

pull/118/head
Tran Xen 2023-07-07 00:35:22 +02:00
parent 44ccfc0521
commit b7872cb9e4
4 changed files with 146 additions and 30 deletions

View File

@ -102,6 +102,10 @@ class FaceSwapUnitSettings:
enable: bool
# Use same gender filtering
same_gender: bool
# If True, discard images with low similarity
check_similarity : bool
# Minimum similarity against the used face (reference, batch or checkpoint)
min_sim: float
# Minimum similarity against the reference (reference or checkpoint if checkpoint is given)
@ -345,7 +349,7 @@ def upscaler_ui():
with gr.Accordion(f"Post Inpainting (Beta)", open=True):
gr.Markdown(
"""Inpainting sends image to inpainting with a mask on face (once for each faces).""")
inpainting_when = gr.Dropdown(choices = [e.value for e in upscaling.InpaintingWhen.__members__.values()],value=[upscaling.InpaintingWhen.BEFORE_RESTORE_FACE.value], label="When")
inpainting_when = gr.Dropdown(choices = [e.value for e in upscaling.InpaintingWhen.__members__.values()],value=[upscaling.InpaintingWhen.BEFORE_RESTORE_FACE.value], label="Enable/When")
inpainting_denoising_strength = gr.Slider(
0, 1, 0, step=0.01, label="Denoising strenght (will send face to img2img after processing)"
)
@ -451,7 +455,8 @@ def on_ui_settings():
section = ('roop', "Roop")
shared.opts.add_option("roop_units_count", shared.OptionInfo(
3, "Max faces units (requires restart)", gr.Slider, {"minimum": 1, "maximum": 10, "step": 1}, section=section))
shared.opts.add_option("roop_upscaled_swapper", shared.OptionInfo(
False, "Upscaled swapper", gr.Checkbox, {"interactive": True}, section=section))
script_callbacks.on_ui_settings(on_ui_settings)
@ -461,6 +466,10 @@ class FaceSwapScript(scripts.Script):
def units_count(self) :
return opts.data.get("roop_units_count", 3)
@property
def upscaled_swapper(self) :
return opts.data.get("roop_upscaled_swapper", False)
def title(self):
return f"roop"
@ -502,7 +511,8 @@ class FaceSwapScript(scripts.Script):
blend_faces = gr.Checkbox(
True, placeholder="Blend Faces", label="Blend Faces ((Source|Checkpoint)+References = 1)"
)
gr.Markdown("""Discard images with low similarity or no faces :""")
gr.Markdown("""Discard images with low similarity or no faces :""")
check_similarity = gr.Checkbox(False, placeholder="discard", label="Check similarity")
min_sim = gr.Slider(0, 1, 0, step=0.01, label="Min similarity")
min_ref_sim = gr.Slider(
0, 1, 0, step=0.01, label="Min reference similarity"
@ -532,6 +542,7 @@ class FaceSwapScript(scripts.Script):
blend_faces,
enable,
same_gender,
check_similarity,
min_sim,
min_ref_sim,
faces_index,
@ -599,7 +610,7 @@ class FaceSwapScript(scripts.Script):
p.init_images = init_images
def process_images_unit(self, unit, images, infos = None) :
def process_images_unit(self, unit : FaceSwapUnitSettings, images, infos = None) :
if unit.enable :
result_images = []
result_infos = []
@ -625,8 +636,9 @@ class FaceSwapScript(scripts.Script):
faces_index=unit.faces_index,
model=self.model,
same_gender=unit.same_gender,
upscaled_swapper=self.upscaled_swapper
)
if result.similarity and all([result.similarity.values()!=0]+[x >= unit.min_sim for x in result.similarity.values()]) and all([result.ref_similarity.values()!=0]+[x >= unit.min_ref_sim for x in result.ref_similarity.values()]):
if (not unit.check_similarity) or result.similarity and all([result.similarity.values()!=0]+[x >= unit.min_sim for x in result.similarity.values()]) and all([result.ref_similarity.values()!=0]+[x >= unit.min_ref_sim for x in result.ref_similarity.values()]):
result_infos.append(f"{info}, similarity = {result.similarity}, ref_similarity = {result.ref_similarity}")
result_images.append(result.image)
else:
@ -644,7 +656,7 @@ class FaceSwapScript(scripts.Script):
if any([u.enable for u in self.units]):
result_images = processed.images[:]
result_infos = processed.infotexts[:]
if p.batch_size > 1 and p.n_iter > 1:
if p.batch_size > 1 or p.n_iter > 1:
# Remove grid image if batch size is greater than 1 :
result_images = result_images[1:]
result_infos = result_infos[1:]

View File

@ -9,9 +9,11 @@ import insightface
import numpy as np
import onnxruntime
from insightface.app.common import Face
from PIL import Image
from sklearn.metrics.pairwise import cosine_similarity
from scripts import upscaled_inswapper
from scripts.imgutils import cv2_to_pil, pil_to_cv2
from scripts.roop_logging import logger
@ -124,7 +126,7 @@ def getFaceSwapModel(model_path: str):
try :
CURRENT_FS_MODEL_PATH = model_path
# Initializes the face swap model using the specified model path.
FS_MODEL = insightface.model_zoo.get_model(model_path, providers=providers)
FS_MODEL = upscaled_inswapper.UpscaledINSwapper(insightface.model_zoo.get_model(model_path, providers=providers))
except Exception as e :
logger.error("Loading of swapping model failed, please check the requirements (On Windows, download and install Visual Studio. During the install, make sure to include the Python and C++ packages.)")
return FS_MODEL
@ -274,6 +276,7 @@ def swap_face(
model: str,
faces_index: Set[int] = {0},
same_gender=True,
upscaled_swapper = False
) -> ImageResult:
"""
Swaps faces in the target image with the source face.
@ -309,7 +312,7 @@ def swap_face(
for i, swapped_face in enumerate(target_faces):
logger.info(f"swap face {i}")
if i in faces_index:
result = face_swapper.get(result, swapped_face, source_face)
result = face_swapper.get(result, swapped_face, source_face, upscale = upscaled_swapper)
result_image = Image.fromarray(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))
return_result.image = result_image

View File

@ -0,0 +1,119 @@
import time
import numpy as np
import onnxruntime
import cv2
import onnx
from onnx import numpy_helper
from insightface.utils import face_align
from insightface.model_zoo.inswapper import INSwapper
from modules import scripts, shared, processing
from modules.face_restoration import FaceRestoration
from modules.upscaler import UpscalerData
from PIL import Image
from scripts.roop_logging import logger
from scripts.imgutils import cv2_to_pil, pil_to_cv2
def get_ldsr() -> UpscalerData:
for upscaler in shared.sd_upscalers:
if upscaler.name == "LDSR":
return upscaler
return None
class UpscaledINSwapper():
def __init__(self, inswapper : INSwapper):
self.__dict__.update(inswapper.__dict__)
def forward(self, img, latent):
img = (img - self.input_mean) / self.input_std
pred = self.session.run(self.output_names, {self.input_names[0]: img, self.input_names[1]: latent})[0]
return pred
def super_resolution(self,img, k = 2) :
pil_img = cv2_to_pil(img)
upscaled = get_ldsr().scaler.upscale(
pil_img, k, get_ldsr().data_path
)
return pil_to_cv2(upscaled)
def get(self, img, target_face, source_face, paste_back=True, upscale = True):
aimg, M = face_align.norm_crop2(img, target_face.kps, self.input_size[0])
blob = cv2.dnn.blobFromImage(aimg, 1.0 / self.input_std, self.input_size,
(self.input_mean, self.input_mean, self.input_mean), swapRB=True)
latent = source_face.normed_embedding.reshape((1,-1))
latent = np.dot(latent, self.emap)
latent /= np.linalg.norm(latent)
pred = self.session.run(self.output_names, {self.input_names[0]: blob, self.input_names[1]: latent})[0]
#print(latent.shape, latent.dtype, pred.shape)
img_fake = pred.transpose((0,2,3,1))[0]
bgr_fake = np.clip(255 * img_fake, 0, 255).astype(np.uint8)[:,:,::-1]
try :
if not paste_back:
return bgr_fake, M
else:
if upscale :
print("*"*80)
print("LDSR inswapper")
print("*"*80)
k = 4
aimg, M = face_align.norm_crop2(img, target_face.kps, self.input_size[0]*k)
bgr_fake = self.super_resolution(bgr_fake, k)
# Add sharpness
blurred = cv2.GaussianBlur(bgr_fake, (0, 0), 3)
bgr_fake = cv2.addWeighted(bgr_fake, 1.5, blurred, -0.5, 0)
target_img = img
fake_diff = bgr_fake.astype(np.float32) - aimg.astype(np.float32)
fake_diff = np.abs(fake_diff).mean(axis=2)
fake_diff[:2,:] = 0
fake_diff[-2:,:] = 0
fake_diff[:,:2] = 0
fake_diff[:,-2:] = 0
IM = cv2.invertAffineTransform(M)
img_white = np.full((aimg.shape[0],aimg.shape[1]), 255, dtype=np.float32)
bgr_fake = cv2.warpAffine(bgr_fake, IM, (target_img.shape[1], target_img.shape[0]), borderValue=0.0)
img_white = cv2.warpAffine(img_white, IM, (target_img.shape[1], target_img.shape[0]), borderValue=0.0)
fake_diff = cv2.warpAffine(fake_diff, IM, (target_img.shape[1], target_img.shape[0]), borderValue=0.0)
img_white[img_white>20] = 255
fthresh = 10
fake_diff[fake_diff<fthresh] = 0
fake_diff[fake_diff>=fthresh] = 255
img_mask = img_white
mask_h_inds, mask_w_inds = np.where(img_mask==255)
mask_h = np.max(mask_h_inds) - np.min(mask_h_inds)
mask_w = np.max(mask_w_inds) - np.min(mask_w_inds)
mask_size = int(np.sqrt(mask_h*mask_w))
k = max(mask_size//10, 10)
kernel = np.ones((k,k),np.uint8)
img_mask = cv2.erode(img_mask,kernel,iterations = 1)
kernel = np.ones((2,2),np.uint8)
fake_diff = cv2.dilate(fake_diff,kernel,iterations = 1)
k = max(mask_size//20, 5)
kernel_size = (k, k)
blur_size = tuple(2*i+1 for i in kernel_size)
img_mask = cv2.GaussianBlur(img_mask, blur_size, 0)
k = 5
kernel_size = (k, k)
blur_size = tuple(2*i+1 for i in kernel_size)
fake_diff = cv2.GaussianBlur(fake_diff, blur_size, 0)
img_mask /= 255
fake_diff /= 255
img_mask = np.reshape(img_mask, [img_mask.shape[0],img_mask.shape[1],1])
fake_merged = img_mask * bgr_fake + (1-img_mask) * target_img.astype(np.float32)
fake_merged = fake_merged.astype(np.uint8)
return fake_merged
except Exception as e :
import traceback
traceback.print_exc()
raise e

View File

@ -115,27 +115,7 @@ def resize_bbox(bbox):
y_max = int(y_max // 8 + 1) * 8 if y_max % 8 != 0 else y_max
return x_min, y_min, x_max, y_max
def get_ldsr() -> UpscalerData:
for upscaler in shared.sd_upscalers:
if upscaler.name == "LDSR":
return upscaler
return None
def resize_small_image(img: Image.Image, min_resolution=512, use_ldsr = True):
width, height = img.size
if min(width, height) > min_resolution:
return img
k = float(min_resolution) / float(min(width, height))
target_width = int(round(width * k))
target_height = int(round(height * k))
if not use_ldsr :
resized_img = img.resize((target_width, target_height), resample=Image.Resampling.LANCZOS)
else :
logger.info("Upscale face with LDSR")
resized_img = get_ldsr().scaler.upscale(
img, k, get_ldsr().data_path
)
return resized_img
def create_mask(image, box_coords):
width, height = image.size
@ -161,7 +141,9 @@ inpainting_denoising_strength : {inpainting_denoising_strength}
inpainting_steps : {inpainting_steps}
"""
)
if not isinstance(inpainting_sampler, str) :
inpainting_sampler = "Euler"
logger.info("send faces to image to image")
img = img.copy()
faces = swapper.get_faces(imgutils.pil_to_cv2(img))