import gradio as gr import numpy as np import time import math import random import torch import spaces from diffusers import StableDiffusionXLInpaintPipeline from PIL import Image, ImageFilter from pillow_heif import register_heif_opener register_heif_opener() max_64_bit_int = np.iinfo(np.int32).max if torch.cuda.is_available(): device = "cuda" floatType = torch.float16 else: device = "cpu" floatType = torch.float32 pipe = StableDiffusionXLInpaintPipeline.from_pretrained("stabilityai/stable-diffusion-xl-refiner-1.0", torch_dtype = floatType) pipe = pipe.to(device) def update_seed(is_randomize_seed, seed): if is_randomize_seed: return random.randint(0, max_64_bit_int) return seed def toggle_debug( is_debug_mode, repeating_horizontally, repeating_vertically ): if is_debug_mode: return [ gr.update(visible = True), gr.update(visible = repeating_horizontally), gr.update(visible = repeating_horizontally), gr.update(visible = repeating_vertically), gr.update(visible = repeating_vertically), gr.update(visible = (repeating_horizontally and repeating_vertically)), gr.update(visible = (repeating_horizontally and repeating_vertically)) ] return [gr.update(visible = False)] * 7 def flip(input_image, horizontally_flipped, vertically_flipped): image_height, image_width, dummy_channel = np.array(input_image).shape fliped_image = Image.new(mode = input_image.mode, size = (image_width, image_height), color = "black") middle_width = image_width // 2 middle_height = image_height // 2 if horizontally_flipped and vertically_flipped: fliped_image.paste(input_image, (middle_width, middle_height)) fliped_image.paste(input_image, (middle_width - image_width, middle_height)) fliped_image.paste(input_image, (middle_width, middle_height - image_height)) fliped_image.paste(input_image, (middle_width - image_width, middle_height - image_height)) elif horizontally_flipped: fliped_image.paste(input_image, (middle_width, 0)) fliped_image.paste(input_image, (middle_width - image_width, 0)) elif vertically_flipped: fliped_image.paste(input_image, (0, middle_height)) fliped_image.paste(input_image, (0, middle_height - image_height)) return fliped_image def blur(input_image, radius): image_height, image_width, dummy_channel = np.array(input_image).shape duplicated_image = Image.new(mode = input_image.mode, size = (image_width * 3, image_height * 3), color = "black") for i in range(3): for j in range(3): duplicated_image.paste(input_image, (image_width * i, image_height * j)) duplicated_image = duplicated_image.filter(ImageFilter.GaussianBlur(radius)) blurred_image = Image.new(mode = input_image.mode, size = (image_width, image_height), color = "black") blurred_image.paste(duplicated_image, (-image_width, -image_height)) return blurred_image def mask(input_image, enlarge_left, enlarge_right, enlarge_top, enlarge_bottom, horizontally_flipped, vertically_flipped, smooth_border): image_height, image_width, dummy_channel = np.array(input_image).shape if horizontally_flipped and vertically_flipped: mask_image = Image.new(mode = input_image.mode, size = (enlarge_left + image_width + enlarge_right, enlarge_top + image_height + enlarge_bottom), color = (255, 255, 255, 0)) black_mask = Image.new(mode = input_image.mode, size = (image_width - smooth_border, enlarge_top + image_height + enlarge_bottom), color = (127, 127, 127, 0)) mask_image.paste(black_mask, (enlarge_left + (smooth_border // 2), 0)) black_mask = Image.new(mode = input_image.mode, size = (enlarge_left + image_width + enlarge_right, image_height - smooth_border), color = (127, 127, 127, 0)) mask_image.paste(black_mask, (0, enlarge_top + (smooth_border // 2))) elif horizontally_flipped: mask_image = Image.new(mode = input_image.mode, size = (enlarge_left + image_width + enlarge_right, image_height), color = (255, 255, 255, 0)) black_mask = Image.new(mode = input_image.mode, size = (image_width - smooth_border, image_height), color = (127, 127, 127, 0)) mask_image.paste(black_mask, (enlarge_left + (smooth_border // 2), 0)) elif vertically_flipped: mask_image = Image.new(mode = input_image.mode, size = (image_width, enlarge_top + image_height + enlarge_bottom), color = (255, 255, 255, 0)) black_mask = Image.new(mode = input_image.mode, size = (image_width, image_height - smooth_border), color = (127, 127, 127, 0)) mask_image.paste(black_mask, (0, enlarge_top + (smooth_border // 2))) mask_image = blur(mask_image, 10) return mask_image def canva(input_image, enlarge_left, enlarge_right, enlarge_top, enlarge_bottom): image_height, image_width, dummy_channel = np.array(input_image).shape output_width = enlarge_left + image_width + enlarge_right output_height = enlarge_top + image_height + enlarge_bottom canva_image = Image.new(mode = input_image.mode, size = (image_width, image_height), color = "black") canva_image.paste(input_image, (0, 0)) canva_image = canva_image.resize((output_width, output_height), Image.LANCZOS) canva_image = blur(canva_image, 20) canva_image.paste(input_image, (enlarge_left, enlarge_top)) horizontally_mirrored_input_image = input_image.transpose(Image.FLIP_LEFT_RIGHT).resize((image_width * 2, image_height), Image.LANCZOS) canva_image.paste(horizontally_mirrored_input_image, (enlarge_left - (image_width * 2), enlarge_top)) canva_image.paste(horizontally_mirrored_input_image, (enlarge_left + image_width, enlarge_top)) vertically_mirrored_input_image = input_image.transpose(Image.FLIP_TOP_BOTTOM).resize((image_width, image_height * 2), Image.LANCZOS) canva_image.paste(vertically_mirrored_input_image, (enlarge_left, enlarge_top - (image_height * 2))) canva_image.paste(vertically_mirrored_input_image, (enlarge_left, enlarge_top + image_height)) returned_input_image = input_image.transpose(Image.ROTATE_180).resize((image_width * 2, image_height * 2), Image.LANCZOS) canva_image.paste(returned_input_image, (enlarge_left - (image_width * 2), enlarge_top - (image_height * 2))) canva_image.paste(returned_input_image, (enlarge_left - (image_width * 2), enlarge_top + image_height)) canva_image.paste(returned_input_image, (enlarge_left + image_width, enlarge_top - (image_height * 2))) canva_image.paste(returned_input_image, (enlarge_left + image_width, enlarge_top + image_height)) canva_image = blur(canva_image, 20) canva_image.paste(input_image, (enlarge_left, enlarge_top)) return canva_image def noise_color(color, noise): return color + random.randint(- noise, noise) def add_noise( input_image, canva_image, enlarge_left, enlarge_right, enlarge_top, enlarge_bottom ): input_height, input_width, dummy_channel = np.array(input_image).shape canva_height, canva_width, dummy_channel_2 = np.array(canva_image).shape noise_image = Image.new(mode = input_image.mode, size = (canva_width, canva_height), color = "black") canva_pixels = canva_image.load() for x in range(canva_width): for y in range(canva_height): canva_pixel = canva_pixels[x, y] noise = min(max(enlarge_left - x, x - (enlarge_left + input_width), enlarge_top - y, y - (enlarge_top + input_height), 0), 255) noise_image.putpixel((x, y), (noise_color(canva_pixel[0], noise), noise_color(canva_pixel[1], noise), noise_color(canva_pixel[2], noise), 255)) canva_image.paste(noise_image, (0, 0)) return canva_image def resizing(output_width, output_height, limitation): resized_width = output_width resized_height = output_height if 1024 * 1024 < output_width * output_height: factor = ((1024 * 1024) / (output_width * output_height))**0.5 resized_width = math.floor(output_width * factor) resized_height = math.floor(output_height * factor) limitation = " Due to technical limitation, the image have been downscaled and then upscaled."; # Width and height must be multiple of 8 resized_width = resized_width - (resized_width % 8) resized_height = resized_height - (resized_height % 8) return resized_width, resized_height, limitation def join_edge( input_image, enlarge_left, enlarge_right, enlarge_top, enlarge_bottom, horizontally_flipped, vertically_flipped, limitation, prompt, negative_prompt, smooth_border, num_inference_steps, guidance_scale, image_guidance_scale, denoising_steps, seed, debug_mode, progress ): original_height, original_width, dummy_channel = np.array(input_image).shape output_width = (0 if vertically_flipped else enlarge_left) + original_width + (0 if vertically_flipped else enlarge_right) output_height = (0 if horizontally_flipped else enlarge_top) + original_height + (0 if horizontally_flipped else enlarge_bottom) current_image = canva( input_image, (0 if vertically_flipped else enlarge_left), (0 if vertically_flipped else enlarge_right), (0 if horizontally_flipped else enlarge_top), (0 if horizontally_flipped else enlarge_bottom) ) no_noise_image = current_image current_image = add_noise( input_image, current_image, (0 if vertically_flipped else enlarge_left), (0 if vertically_flipped else enlarge_right), (0 if horizontally_flipped else enlarge_top), (0 if horizontally_flipped else enlarge_bottom) ) current_image = flip(current_image, horizontally_flipped, vertically_flipped) mask_image = mask(input_image, enlarge_left, enlarge_right, enlarge_top, enlarge_bottom, horizontally_flipped, vertically_flipped, smooth_border) mask_image = flip(mask_image, horizontally_flipped, vertically_flipped) resized_width, resized_height, limitation = resizing(output_width, output_height, limitation) if horizontally_flipped and vertically_flipped: progress(.85, desc = "Processing (3/3)...") elif horizontally_flipped: progress(.16, desc = "Processing (1/3)...") elif vertically_flipped: progress(.51, desc = "Processing (2/3)...") # Artificial Intelligence computation output_image = pipe( seeds = [seed], width = resized_width, height = resized_height, prompt = prompt, negative_prompt = negative_prompt, image = current_image, mask_image = mask_image, num_inference_steps = num_inference_steps, guidance_scale = guidance_scale, #image_guidance_scale = image_guidance_scale, denoising_steps = denoising_steps, show_progress_bar = False ).images[0] output_image = output_image.resize((output_width, output_height), Image.LANCZOS) output_image = flip(output_image, horizontally_flipped, vertically_flipped) return output_image, limitation, input_image, no_noise_image, current_image, mask_image def additional_information(processed_image, repeating_horizontally, repeating_vertically): try: output_height, output_width, dummy_channel = np.array(processed_image).shape horizontal_loops = 2 if repeating_horizontally else 1 vertical_loops = 2 if repeating_vertically else 1 demonstration = Image.new(mode = processed_image.mode, size = (output_width * horizontal_loops, output_height * vertical_loops), color = "black") for x in range(horizontal_loops): for y in range(vertical_loops): demonstration.paste(processed_image, (output_width * x, output_height * y)) except: output_height = 0 output_width = 0 demonstration = None return output_height, output_width, demonstration def check( processed_image, enlarge_top, enlarge_right, enlarge_bottom, enlarge_left, prompt, negative_prompt, repeating_horizontally, repeating_vertically, smooth_border, num_inference_steps, guidance_scale, image_guidance_scale, denoising_steps, is_randomize_seed, seed, debug_mode, progress = gr.Progress() ): if processed_image is None: raise gr.Error("Please provide an image.") if prompt is None or prompt == "": raise gr.Error("Please provide a prompt input.") if repeating_horizontally == False and repeating_vertically == False: raise gr.Error("The image should loop at least in one direction.") if (not (enlarge_top is None)) and enlarge_top < 0: raise gr.Error("Please only provide positive margins.") if (not (enlarge_right is None)) and enlarge_right < 0: raise gr.Error("Please only provide positive margins.") if (not (enlarge_bottom is None)) and enlarge_bottom < 0: raise gr.Error("Please only provide positive margins.") if (not (enlarge_left is None)) and enlarge_left < 0: raise gr.Error("Please only provide positive margins.") if (smooth_border is None or smooth_border == 0) and (enlarge_top is None or enlarge_top == 0) and (enlarge_right is None or enlarge_right == 0) and (enlarge_bottom is None or enlarge_bottom == 0) and (enlarge_left is None or enlarge_left == 0): raise gr.Error("At least one border must be enlarged or smoothed.") @spaces.GPU(duration=420) def image_to_tile( processed_image, enlarge_top, enlarge_right, enlarge_bottom, enlarge_left, prompt, negative_prompt, repeating_horizontally, repeating_vertically, smooth_border, num_inference_steps, guidance_scale, image_guidance_scale, denoising_steps, is_randomize_seed, seed, debug_mode, progress = gr.Progress() ): start = time.time() progress(0, desc = "Preparing data...") limitation = ""; if negative_prompt is None: negative_prompt = "" if smooth_border is None: smooth_border = 64 if guidance_scale is None: guidance_scale = 7 if image_guidance_scale is None: image_guidance_scale = 1.5 if enlarge_top is None or enlarge_top == "": enlarge_top = 0 if enlarge_right is None or enlarge_right == "": enlarge_right = 0 if enlarge_bottom is None or enlarge_bottom == "": enlarge_bottom = 0 if enlarge_left is None or enlarge_left == "": enlarge_left = 0 if denoising_steps is None: denoising_steps = 1000 if seed is None: seed = random.randint(0, max_64_bit_int) random.seed(seed) torch.manual_seed(seed) original_image = processed_image horizontally_mirrored_image = None horizontally_mirrored_mask = None vertically_mirrored_image = None vertically_mirrored_mask = None last_image = None last_mask = None if repeating_horizontally: processed_image, limitation, start_image, no_noise_image, horizontally_mirrored_image, horizontally_mirrored_mask = join_edge( processed_image, enlarge_left, enlarge_right, enlarge_top, enlarge_bottom, True, False, limitation, prompt, negative_prompt, smooth_border, num_inference_steps, guidance_scale, image_guidance_scale, denoising_steps, seed, debug_mode, progress ) if repeating_vertically: processed_image, limitation, start_image, no_noise_image, vertically_mirrored_image, vertically_mirrored_mask = join_edge( processed_image, enlarge_left, enlarge_right, enlarge_top, enlarge_bottom, False, True, limitation, prompt, negative_prompt, smooth_border, num_inference_steps, guidance_scale, image_guidance_scale, denoising_steps, seed, debug_mode, progress ) if repeating_horizontally and repeating_vertically: processed_image, limitation, start_image, no_noise_image, last_image, last_mask = join_edge( processed_image, enlarge_left, enlarge_right, enlarge_top, enlarge_bottom, True, True, limitation, prompt, negative_prompt, smooth_border, num_inference_steps, guidance_scale, image_guidance_scale, denoising_steps, seed, debug_mode, progress ) progress(.99, desc = "Finishing...") output_height, output_width, demonstration = additional_information(processed_image, repeating_horizontally, repeating_vertically) end = time.time() secondes = int(end - start) minutes = math.floor(secondes / 60) secondes = secondes - (minutes * 60) hours = math.floor(minutes / 60) minutes = minutes - (hours * 60) return [ processed_image, ("Start again to get a different result. " if is_randomize_seed else "") + "The new image is " + str(output_width) + " pixels large and " + str(output_height) + " pixels high, so an image of " + f'{output_width * output_height:,}' + " pixels. The image has been generated in " + ((str(hours) + " h, ") if hours != 0 else "") + ((str(minutes) + " min, ") if hours != 0 or minutes != 0 else "") + str(secondes) + " sec." + limitation, demonstration, original_image, horizontally_mirrored_image, horizontally_mirrored_mask, vertically_mirrored_image, vertically_mirrored_mask, last_image, last_mask ] with gr.Blocks() as interface: gr.HTML( """
Modify the edges of your image to make your image loop horizontally and vertically seamlessly, up to 1 million pixels, freely, without account, without watermark, which can be downloaded