optimized multi face processing for batch_size > 1

pull/17/head
kex0 2023-01-16 22:18:14 +01:00
parent 0b759c6e4e
commit a936634493
1 changed files with 83 additions and 30 deletions

View File

@ -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("<p style=\"margin-top:0.75em;font-size:1.25em\"><strong>Step 2:</strong> Image splitter:</p>")
htmlTip2 = gr.HTML("<p>This divides image to smaller images and tries to find a face in the individual smaller images.</p><p>Useful when faces are small in relation to the size of the whole picture.</p><p>(may result in mask that only covers a part of a face if the division goes right through the face)</p>",visible=False)
htmlTip2 = gr.HTML("<p>This divides image to smaller images and tries to find a face in the individual smaller images.</p><p>Useful when faces are small in relation to the size of the whole picture and not being detected.</p><p>(may result in mask that only covers a part of a face or no detection if the division goes right through the face)</p>",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("<p style=\"margin-top:0.75em;font-size:1.25em\">Other:</p>")
htmlTip3 = gr.HTML("<p>Simple utility to see how many faces do your current settings detect without generating SD image.</p><p>You can also save generated masks to disk. (if you leave path empty, it will save the masks to your default webui outputs directory)</p>",visible=False)
htmlTip3 = gr.HTML("<p>Press 'Generate masks' button to see how many faces do your current settings detect without generating SD image.</p><p>You can also save generated masks to disk. (if you leave path empty, it will save the masks to your default webui outputs directory)</p><p>Activate 'View all results' checkbox to see results in the WebUI at the end (not recommended when processing a large number of images)</p>",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