Path: blob/master/scripts/poor_mans_outpainting.py
3055 views
import math12import modules.scripts as scripts3import gradio as gr4from PIL import Image, ImageDraw56from modules import images, devices7from modules.processing import Processed, process_images8from modules.shared import opts, state91011class Script(scripts.Script):12def title(self):13return "Poor man's outpainting"1415def show(self, is_img2img):16return is_img2img1718def ui(self, is_img2img):19if not is_img2img:20return None2122pixels = gr.Slider(label="Pixels to expand", minimum=8, maximum=256, step=8, value=128, elem_id=self.elem_id("pixels"))23mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=4, elem_id=self.elem_id("mask_blur"))24inpainting_fill = gr.Radio(label='Masked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='fill', type="index", elem_id=self.elem_id("inpainting_fill"))25direction = gr.CheckboxGroup(label="Outpainting direction", choices=['left', 'right', 'up', 'down'], value=['left', 'right', 'up', 'down'], elem_id=self.elem_id("direction"))2627return [pixels, mask_blur, inpainting_fill, direction]2829def run(self, p, pixels, mask_blur, inpainting_fill, direction):30initial_seed = None31initial_info = None3233p.mask_blur = mask_blur * 234p.inpainting_fill = inpainting_fill35p.inpaint_full_res = False3637left = pixels if "left" in direction else 038right = pixels if "right" in direction else 039up = pixels if "up" in direction else 040down = pixels if "down" in direction else 04142init_img = p.init_images[0]43target_w = math.ceil((init_img.width + left + right) / 64) * 6444target_h = math.ceil((init_img.height + up + down) / 64) * 644546if left > 0:47left = left * (target_w - init_img.width) // (left + right)48if right > 0:49right = target_w - init_img.width - left5051if up > 0:52up = up * (target_h - init_img.height) // (up + down)5354if down > 0:55down = target_h - init_img.height - up5657img = Image.new("RGB", (target_w, target_h))58img.paste(init_img, (left, up))5960mask = Image.new("L", (img.width, img.height), "white")61draw = ImageDraw.Draw(mask)62draw.rectangle((63left + (mask_blur * 2 if left > 0 else 0),64up + (mask_blur * 2 if up > 0 else 0),65mask.width - right - (mask_blur * 2 if right > 0 else 0),66mask.height - down - (mask_blur * 2 if down > 0 else 0)67), fill="black")6869latent_mask = Image.new("L", (img.width, img.height), "white")70latent_draw = ImageDraw.Draw(latent_mask)71latent_draw.rectangle((72left + (mask_blur//2 if left > 0 else 0),73up + (mask_blur//2 if up > 0 else 0),74mask.width - right - (mask_blur//2 if right > 0 else 0),75mask.height - down - (mask_blur//2 if down > 0 else 0)76), fill="black")7778devices.torch_gc()7980grid = images.split_grid(img, tile_w=p.width, tile_h=p.height, overlap=pixels)81grid_mask = images.split_grid(mask, tile_w=p.width, tile_h=p.height, overlap=pixels)82grid_latent_mask = images.split_grid(latent_mask, tile_w=p.width, tile_h=p.height, overlap=pixels)8384p.n_iter = 185p.batch_size = 186p.do_not_save_grid = True87p.do_not_save_samples = True8889work = []90work_mask = []91work_latent_mask = []92work_results = []9394for (y, h, row), (_, _, row_mask), (_, _, row_latent_mask) in zip(grid.tiles, grid_mask.tiles, grid_latent_mask.tiles):95for tiledata, tiledata_mask, tiledata_latent_mask in zip(row, row_mask, row_latent_mask):96x, w = tiledata[0:2]9798if x >= left and x+w <= img.width - right and y >= up and y+h <= img.height - down:99continue100101work.append(tiledata[2])102work_mask.append(tiledata_mask[2])103work_latent_mask.append(tiledata_latent_mask[2])104105batch_count = len(work)106print(f"Poor man's outpainting will process a total of {len(work)} images tiled as {len(grid.tiles[0][2])}x{len(grid.tiles)}.")107108state.job_count = batch_count109110for i in range(batch_count):111p.init_images = [work[i]]112p.image_mask = work_mask[i]113p.latent_mask = work_latent_mask[i]114115state.job = f"Batch {i + 1} out of {batch_count}"116processed = process_images(p)117118if initial_seed is None:119initial_seed = processed.seed120initial_info = processed.info121122p.seed = processed.seed + 1123work_results += processed.images124125126image_index = 0127for y, h, row in grid.tiles:128for tiledata in row:129x, w = tiledata[0:2]130131if x >= left and x+w <= img.width - right and y >= up and y+h <= img.height - down:132continue133134tiledata[2] = work_results[image_index] if image_index < len(work_results) else Image.new("RGB", (p.width, p.height))135image_index += 1136137combined_image = images.combine_grid(grid)138139if opts.samples_save:140images.save_image(combined_image, p.outpath_samples, "", initial_seed, p.prompt, opts.samples_format, info=initial_info, p=p)141142processed = Processed(p, [combined_image], initial_seed, initial_info)143144return processed145146147148