diff --git a/scripts/face_swap.py b/scripts/face_swap.py index b80b758..99d9ce5 100644 --- a/scripts/face_swap.py +++ b/scripts/face_swap.py @@ -2,16 +2,29 @@ import modules.scripts as scripts import gradio as gr import os -from modules import images -from modules.processing import process_images, Processed, StableDiffusionProcessing +from modules import images, masking +from modules.processing import process_images, Processed, StableDiffusionProcessingImg2Img, StableDiffusionProcessing from modules.shared import opts, cmd_opts, state import cv2 import mediapipe as mp import numpy as np -from PIL import Image, UnidentifiedImageError +from PIL import Image, UnidentifiedImageError, ImageOps import math +def apply_overlay(image, paste_loc, overlay): + if paste_loc is not None: + x, y, w, h = paste_loc + base_image = Image.new('RGBA', (overlay.width, overlay.height)) + image = images.resize_image(1, image, w, h) + base_image.paste(image, (x, y)) + image = base_image + + image = image.convert('RGBA') + image.alpha_composite(overlay) + + return image + def findBiggestBlob(inputImage): # Store a copy of the input image: biggestBlob = inputImage.copy() @@ -169,7 +182,7 @@ def findFaceDivide(image, width, height, divider, onlyHorizontal, onlyVertical, return masks, totalNumberOfFaces, skip def generateMasks(path, divider, howSplit, saveMask, pathToSave): - p = StableDiffusionProcessing + p = StableDiffusionProcessingImg2Img(StableDiffusionProcessing) if howSplit == "Horizontal only ▤": onlyHorizontal = True onlyVertical = False @@ -211,12 +224,12 @@ def generateMasks(path, divider, howSplit, saveMask, pathToSave): if pathToSave != "": for i, mask in enumerate(masks): mask = Image.fromarray(mask) - images.save_image(mask, pathToSave, f"{str(i+1).join(os.path.splitext(file))}", p=p, suffix="_mask") + images.save_image(mask, pathToSave, "", p.seed, p.prompt, opts.samples_format, p=p, suffix=suffix) elif pathToSave == "": for i, mask in enumerate(masks): mask = Image.fromarray(mask) - images.save_image(mask, opts.outdir_img2img_samples, f"{str(i+1).join(os.path.splitext(file))}", p=p, suffix="_mask") + images.save_image(mask, opts.outdir_img2img_samples, "", p.seed, p.prompt, opts.samples_format, p=p, suffix=suffix) except cv2.error as e: print(e) @@ -249,14 +262,16 @@ class Script(scripts.Script): path = gr.Textbox(label="Images directory",placeholder=r"C:\Users\dude\Desktop\images") with gr.Column(): gr.HTML("
Step 2: Image splitter:
") - htmlTip2 = gr.HTML("This divides image to smaller images and tries to find a face in the individual smaller images.
Useful when faces are small in relation to the size of the whole picture.
(may result in mask that only covers a part of a face if the division goes right through the face)
",visible=False) + htmlTip2 = gr.HTML("This divides image to smaller images and tries to find a face in the individual smaller images.
Useful when faces are small in relation to the size of the whole picture and not being detected.
(may result in mask that only covers a part of a face or no detection if the division goes right through the face)
",visible=False) divider = gr.Slider(minimum=1, maximum=5, step=1, value=1, label="How many times to divide image") howSplit = gr.Radio(["Horizontal only ▤", "Vertical only ▥", "Both ▦"], value = "Both ▦", label = "How to divide") with gr.Column(): gr.HTML("Other:
") - htmlTip3 = gr.HTML("Simple utility to see how many faces do your current settings detect without generating SD image.
You can also save generated masks to disk. (if you leave path empty, it will save the masks to your default webui outputs directory)
",visible=False) + htmlTip3 = gr.HTML("Press 'Generate masks' button to see how many faces do your current settings detect without generating SD image.
You can also save generated masks to disk. (if you leave path empty, it will save the masks to your default webui outputs directory)
Activate 'View all results' checkbox to see results in the WebUI at the end (not recommended when processing a large number of images)
",visible=False) showTips = gr.Checkbox(value=False, label="Show tips") - saveMask = gr.Checkbox(value=False, label="Save masks to disk") + viewResults = gr.Checkbox(value=False, label="View all results") + with gr.Column(): + saveMask = gr.Checkbox(value=False, label="Save masks to disk") pathToSave = gr.Textbox(label="Mask save directory (OPTIONAL)",placeholder=r"C:\Users\dude\Desktop\masks (OPTIONAL)",visible=False) testMask = gr.Button(value="Generate masks",variant="primary") testMaskOut = gr.HTML(value="",visible=False) @@ -270,9 +285,9 @@ class Script(scripts.Script): - return [overrideDenoising, overrideMaskBlur, path, divider, howSplit, testMask, saveMask, pathToSave] + return [overrideDenoising, overrideMaskBlur, path, divider, howSplit, testMask, saveMask, pathToSave, viewResults] - def run(self, p, overrideDenoising, overrideMaskBlur, path, divider, howSplit, testMask, saveMask, pathToSave): + def run(self, p, overrideDenoising, overrideMaskBlur, path, divider, howSplit, testMask, saveMask, pathToSave, viewResults): if howSplit == "Horizontal only ▤": onlyHorizontal = True onlyVertical = False @@ -283,7 +298,7 @@ class Script(scripts.Script): onlyHorizontal = False onlyVertical = False - history = [] + finishedImages = [] all_images = [] totalNumberOfFaces = 0 dirPath = path @@ -291,7 +306,7 @@ class Script(scripts.Script): files = os.listdir(dirPath) print(f"\nWill process {len(files)} images, creating {p.n_iter * p.batch_size} new images for each.") - state.job_count = len(files) * p.n_iter * p.batch_size + state.job_count = len(files) * p.n_iter for i, file in enumerate(files): state.job = f"{i+1} out of {len(files)}" @@ -319,34 +334,72 @@ class Script(scripts.Script): if skip == 1: state.skipped = True continue - p.init_images = [image] + if len(masks) == 1: + if not viewResults: + finishedImages = [] + mask = Image.fromarray(masks[0]) + + p.init_images = [image] p.image_mask = mask + proc = process_images(p) - for i in range(p.batch_size): - history.append(proc.images[i]) + + for n in range(p.batch_size): + finishedImages.append(proc.images[n]) else: - originalBatchSize = p.batch_size - for i in range(originalBatchSize): - for j, mask in enumerate(masks): - mask = Image.fromarray(mask) - p.image_mask = mask - p.batch_size = 1 - p.do_not_save_samples = True - if j == len(masks) - 1: - p.do_not_save_samples = False - proc = process_images(p) - p.init_images = [proc.images[0]] - history.append(proc.images[0]) - p.batch_size = originalBatchSize + if not viewResults: + finishedImages = [] + + generatedImages = [] + paste_to = [] + imageOriginal = image + overlay_image = image + p.do_not_save_samples = True + + for n, mask in enumerate(masks): + mask = Image.fromarray(masks[n]) + + image_masked = Image.new('RGBa', (image.width, image.height)) + image_masked.paste(overlay_image.convert("RGBA").convert("RGBa"), mask=ImageOps.invert(mask.convert('L'))) + + overlay_image = image_masked.convert('RGBA') + + crop_region = masking.get_crop_region(np.array(mask), p.inpaint_full_res_padding) + crop_region = masking.expand_crop_region(crop_region, p.width, p.height, mask.width, mask.height) + x1, y1, x2, y2 = crop_region + paste_to.append((x1, y1, x2-x1, y2-y1)) + + mask = mask.crop(crop_region) + image_mask = images.resize_image(2, mask, p.width, p.height) + + + image = image.crop(crop_region) + image = images.resize_image(2, image, p.width, p.height) + + p.init_images = [image] + p.image_mask = image_mask + proc = process_images(p) + generatedImages.append(proc.images) + + image = imageOriginal + + for j in range(p.batch_size): + image = overlay_image + for k in range(len(generatedImages)): + image = apply_overlay(generatedImages[k][j], paste_to[k], image) + images.save_image(image, p.outpath_samples, "", p.seed, p.prompt, opts.samples_format, p=p) + finishedImages.append(image) + + p.do_not_save_samples = False except cv2.error as e: print(e) print(f"Found {totalNumberOfFaces} faces in {len(files)} images.") - all_images += history + all_images += finishedImages proc = Processed(p, all_images) return proc