Fabrice-TIERCELIN commited on
Commit
3816ea0
1 Parent(s): 4d128e4

New version, extremely detailed, not blur, fine options

Browse files

Hi,

I suggest this version that is already running on [this space](https://huggingface.co/spaces/Fabrice-TIERCELIN/Uncrop). I have adapted the title, the description and the example. The size can be set pixel per pixel.

Fabrice

Files changed (1) hide show
  1. app.py +465 -290
app.py CHANGED
@@ -1,307 +1,482 @@
1
- import cv2
2
- import numpy as np
3
- import torch
4
  import gradio as gr
 
 
 
5
  import random
6
- import spaces
7
-
8
- from diffusers import DPMSolverMultistepScheduler, StableDiffusionXLPipeline
9
- from diffusers.utils import load_image
10
-
11
- DESCRIPTION='''
12
- This uses code lifted almost verbatim from
13
- [Outpainting II - Differential Diffusion](https://huggingface.co/blog/OzzyGT/outpainting-differential-diffusion). This only works well on blurry edges.
14
- '''
15
 
16
- ARTICLE='''
17
- The [example image](https://commons.wikimedia.org/wiki/File:Coucang.jpg) is by Aprisonsan
18
- and licensed under CC-BY-SA 4.0 International.
19
- '''
20
 
21
- xlp_kwargs = {
22
- 'custom_pipeline': 'pipeline_stable_diffusion_xl_differential_img2img'
23
- }
24
 
25
  if torch.cuda.is_available():
26
- device = 'cuda'
27
- device_dtype = torch.float16
28
- xlp_kwargs['variant'] = 'fp16'
29
  else:
30
- device = 'cpu'
31
- device_dtype = torch.float32
32
- DESCRIPTION+='''
33
-
34
- This Space appears to be running on a CPU; it will take hours to get results. You may [duplicate this space](https://huggingface.co/spaces/clinteroni/outpainting-demo?duplicate=true) and pay for an upgraded runtime instead.
35
- '''
36
-
37
- xlp_kwargs['torch_dtype'] = device_dtype
38
-
39
-
40
- def merge_images(original, new_image, offset, direction):
41
- if direction in ["left", "right"]:
42
- merged_image = np.zeros(
43
- (original.shape[0], original.shape[1] + offset, 3), dtype=np.uint8)
44
- elif direction in ["top", "bottom"]:
45
- merged_image = np.zeros(
46
- (original.shape[0] + offset, original.shape[1], 3), dtype=np.uint8)
47
-
48
- if direction == "left":
49
- merged_image[:, offset:] = original
50
- merged_image[:, : new_image.shape[1]] = new_image
51
- elif direction == "right":
52
- merged_image[:, : original.shape[1]] = original
53
- merged_image[:, original.shape[1] + offset -
54
- new_image.shape[1]: original.shape[1] + offset] = new_image
55
- elif direction == "top":
56
- merged_image[offset:, :] = original
57
- merged_image[: new_image.shape[0], :] = new_image
58
- elif direction == "bottom":
59
- merged_image[: original.shape[0], :] = original
60
- merged_image[original.shape[0] + offset - new_image.shape[0]:original.shape[0] + offset, :] = new_image
61
-
62
- return merged_image
63
-
64
-
65
- def slice_image(image):
66
- height, width, _ = image.shape
67
- slice_size = min(width // 2, height // 3)
68
-
69
- slices = []
70
-
71
- for h in range(3):
72
- for w in range(2):
73
- left = w * slice_size
74
- upper = h * slice_size
75
- right = left + slice_size
76
- lower = upper + slice_size
77
-
78
- if w == 1 and right > width:
79
- left -= right - width
80
- right = width
81
- if h == 2 and lower > height:
82
- upper -= lower - height
83
- lower = height
84
-
85
- slice = image[upper:lower, left:right]
86
- slices.append(slice)
87
-
88
- return slices
89
-
90
-
91
- def process_image(
92
- image,
93
- fill_color=(0, 0, 0),
94
- mask_offset=50,
95
- blur_radius=500,
96
- expand_pixels=256,
97
- direction="left",
98
- inpaint_mask_color=50,
99
- max_size=1024,
100
- ):
101
- height, width = image.shape[:2]
102
-
103
- new_height = height + \
104
- (expand_pixels if direction in ["top", "bottom"] else 0)
105
- new_width = width + \
106
- (expand_pixels if direction in ["left", "right"] else 0)
107
-
108
- if new_height > max_size:
109
- # If so, crop the image from the opposite side
110
- if direction == "top":
111
- image = image[:max_size, :]
112
- elif direction == "bottom":
113
- image = image[new_height - max_size:, :]
114
- new_height = max_size
115
-
116
- if new_width > max_size:
117
- # If so, crop the image from the opposite side
118
- if direction == "left":
119
- image = image[:, :max_size]
120
- elif direction == "right":
121
- image = image[:, new_width - max_size:]
122
- new_width = max_size
123
-
124
- height, width = image.shape[:2]
125
-
126
- new_image = np.full((new_height, new_width, 3), fill_color, dtype=np.uint8)
127
- mask = np.full_like(new_image, 255, dtype=np.uint8)
128
- inpaint_mask = np.full_like(new_image, 0, dtype=np.uint8)
129
-
130
- mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
131
- inpaint_mask = cv2.cvtColor(inpaint_mask, cv2.COLOR_BGR2GRAY)
132
-
133
- if direction == "left":
134
- new_image[:, expand_pixels:] = image[:, : max_size - expand_pixels]
135
- mask[:, : expand_pixels + mask_offset] = inpaint_mask_color
136
- inpaint_mask[:, :expand_pixels] = 255
137
- elif direction == "right":
138
- new_image[:, :width] = image
139
- mask[:, width - mask_offset:] = inpaint_mask_color
140
- inpaint_mask[:, width:] = 255
141
- elif direction == "top":
142
- new_image[expand_pixels:, :] = image[: max_size - expand_pixels, :]
143
- mask[: expand_pixels + mask_offset, :] = inpaint_mask_color
144
- inpaint_mask[:expand_pixels, :] = 255
145
- elif direction == "bottom":
146
- new_image[:height, :] = image
147
- mask[height - mask_offset:, :] = inpaint_mask_color
148
- inpaint_mask[height:, :] = 255
149
-
150
- # mask blur
151
- if blur_radius % 2 == 0:
152
- blur_radius += 1
153
- mask = cv2.GaussianBlur(mask, (blur_radius, blur_radius), 0)
154
-
155
- # telea inpaint
156
- _, mask_np = cv2.threshold(
157
- inpaint_mask, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
158
- inpaint = cv2.inpaint(new_image, mask_np, 3, cv2.INPAINT_TELEA)
159
-
160
- # convert image to tensor
161
- inpaint = cv2.cvtColor(inpaint, cv2.COLOR_BGR2RGB)
162
- inpaint = torch.from_numpy(inpaint).permute(2, 0, 1).float()
163
- inpaint = inpaint / 127.5 - 1
164
- inpaint = inpaint.unsqueeze(0).to(device)
165
-
166
- # convert mask to tensor
167
- mask = torch.from_numpy(mask)
168
- mask = mask.unsqueeze(0).float() / 255.0
169
- mask = mask.to(device)
170
-
171
- return inpaint, mask
172
-
173
-
174
- def image_resize(image, new_size=1024):
175
- height, width = image.shape[:2]
176
-
177
- aspect_ratio = width / height
178
- new_width = new_size
179
- new_height = new_size
180
-
181
- if aspect_ratio != 1:
182
- if width > height:
183
- new_height = int(new_size / aspect_ratio)
184
- else:
185
- new_width = int(new_size * aspect_ratio)
186
 
187
- image = cv2.resize(image, (new_width, new_height),
188
- interpolation=cv2.INTER_LANCZOS4)
189
 
190
- return image
 
191
 
 
 
192
 
193
- @spaces.GPU
194
- def outpaint(pil_image, direction='right', times_to_expand=4, guidance_scale=4.0, blur_radius=500):
195
- if torch.cuda.is_available():
196
- torch.cuda.empty_cache()
197
 
198
- pipeline = StableDiffusionXLPipeline.from_pretrained(
199
- "stabilityai/stable-diffusion-xl-base-1.0",
200
- **xlp_kwargs
201
- ).to(device)
202
- pipeline.scheduler = DPMSolverMultistepScheduler.from_config(
203
- pipeline.scheduler.config, use_karras_sigmas=True)
204
 
205
- pipeline.load_ip_adapter(
206
- "h94/IP-Adapter",
207
- subfolder="sdxl_models",
208
- weight_name=[
209
- "ip-adapter-plus_sdxl_vit-h.safetensors",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  ],
211
- image_encoder_folder="models/image_encoder",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  )
213
- pipeline.set_ip_adapter_scale(0.1)
214
-
215
- def generate_image(prompt, negative_prompt, image, mask, ip_adapter_image, seed: int = None):
216
- if seed is None:
217
- seed = random.randint(0, 2**32 - 1)
218
-
219
- generator = torch.Generator(device="cpu").manual_seed(seed)
220
-
221
- image = pipeline(
222
- prompt=prompt,
223
- negative_prompt=negative_prompt,
224
- width=1024,
225
- height=1024,
226
- guidance_scale=guidance_scale,
227
- num_inference_steps=25,
228
- original_image=image,
229
- image=image,
230
- strength=1.0,
231
- map=mask,
232
- generator=generator,
233
- ip_adapter_image=[ip_adapter_image],
234
- output_type="np",
235
- ).images[0]
236
-
237
- image = (image * 255).astype(np.uint8)
238
- image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
239
-
240
- return image
241
-
242
- prompt = ""
243
- negative_prompt = ""
244
- inpaint_mask_color = 50 # lighter use more of the Telea inpainting
245
- # I recommend to don't go more than half of the picture so it has context
246
- expand_pixels = 256
247
-
248
- original = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR)
249
- image = image_resize(original)
250
- # image.shape[1] for horizontal, image.shape[0] for vertical
251
- expand_pixels_to_square = 1024 - image.shape[1]
252
- image, mask = process_image(
253
- image, expand_pixels=expand_pixels_to_square, direction=direction, inpaint_mask_color=inpaint_mask_color, blur_radius=blur_radius
254
  )
255
 
256
- ip_adapter_image = []
257
- for index, part in enumerate(slice_image(original)):
258
- ip_adapter_image.append(part)
259
-
260
- generated = generate_image(
261
- prompt, negative_prompt, image, mask, ip_adapter_image)
262
- final_image = generated
263
-
264
- for i in range(times_to_expand):
265
- image, mask = process_image(
266
- final_image, direction=direction, expand_pixels=expand_pixels, inpaint_mask_color=inpaint_mask_color, blur_radius=blur_radius
267
- )
268
-
269
- ip_adapter_image = []
270
- for index, part in enumerate(slice_image(generated)):
271
- ip_adapter_image.append(part)
272
-
273
- generated = generate_image(
274
- prompt, negative_prompt, image, mask, ip_adapter_image)
275
- final_image = merge_images(final_image, generated, 256, direction)
276
-
277
- color_converted = cv2.cvtColor(final_image, cv2.COLOR_BGR2RGB)
278
- return color_converted
279
-
280
- example_image=load_image('examples/Coucang.jpg')
281
-
282
- gradio_app = gr.Interface(
283
- outpaint,
284
- inputs=[
285
- gr.Image(label="Select start image", sources=[
286
- 'upload', 'clipboard'], type='pil'),
287
- gr.Radio(["left", "right", "top", 'bottom'], label="Direction",
288
- info="Outward from which edge to paint?", value='right'),
289
- gr.Slider(2, 4, step=1, value=4, label="Times to expand",
290
- info="Choose between 2 and 4"),
291
- gr.Slider(1, 12, step=0.1, value=4, label="Guidance scale",
292
- info="Choose between 1 and 12"),
293
- gr.Slider(250, 500, step=1, value=500, label="Mask blur radius",
294
- info="Choose between 250 and 500"),
295
- ],
296
- outputs=[gr.Image(label="Processed Image")],
297
- examples=[
298
- [example_image, 'right', 4, 5, 500],
299
- [example_image, 'left', 4, 6, 500],
300
- ],
301
- title="Outpainting with differential diffusion demo",
302
- description=DESCRIPTION,
303
- article=ARTICLE
304
- )
305
-
306
- if __name__ == "__main__":
307
- gradio_app.queue(max_size=20).launch()
 
 
 
 
1
  import gradio as gr
2
+ import numpy as np
3
+ import time
4
+ import math
5
  import random
6
+ import torch
 
 
 
 
 
 
 
 
7
 
8
+ from diffusers import StableDiffusionXLInpaintPipeline
9
+ from PIL import Image, ImageFilter
 
 
10
 
11
+ max_64_bit_int = 2**63 - 1
 
 
12
 
13
  if torch.cuda.is_available():
14
+ device = "cuda"
15
+ floatType = torch.float16
16
+ variant = "fp16"
17
  else:
18
+ device = "cpu"
19
+ floatType = torch.float32
20
+ variant = None
21
+
22
+ pipe = StableDiffusionXLInpaintPipeline.from_pretrained("diffusers/stable-diffusion-xl-1.0-inpainting-0.1", torch_dtype = floatType, variant = variant)
23
+ pipe = pipe.to(device)
24
+
25
+ def update_seed(is_randomize_seed, seed):
26
+ if is_randomize_seed:
27
+ return random.randint(0, max_64_bit_int)
28
+ return seed
29
+
30
+ def toggle_debug(is_debug_mode):
31
+ return [gr.update(visible = is_debug_mode)] * 3
32
+
33
+ def noise_color(color, noise):
34
+ return color + random.randint(- noise, noise)
35
+
36
+ def check(
37
+ input_image,
38
+ enlarge_top,
39
+ enlarge_right,
40
+ enlarge_bottom,
41
+ enlarge_left,
42
+ prompt,
43
+ negative_prompt,
44
+ smooth_border,
45
+ num_inference_steps,
46
+ guidance_scale,
47
+ image_guidance_scale,
48
+ strength,
49
+ denoising_steps,
50
+ is_randomize_seed,
51
+ seed,
52
+ debug_mode,
53
+ progress = gr.Progress()):
54
+ if input_image is None:
55
+ raise gr.Error("Please provide an image.")
56
+
57
+ if prompt is None or prompt == "":
58
+ raise gr.Error("Please provide a prompt input.")
59
+
60
+ if (not (enlarge_top is None)) and enlarge_top < 0:
61
+ raise gr.Error("Please provide positive top margin.")
62
+
63
+ if (not (enlarge_right is None)) and enlarge_right < 0:
64
+ raise gr.Error("Please provide positive right margin.")
65
+
66
+ if (not (enlarge_bottom is None)) and enlarge_bottom < 0:
67
+ raise gr.Error("Please provide positive bottom margin.")
68
+
69
+ if (not (enlarge_left is None)) and enlarge_left < 0:
70
+ raise gr.Error("Please provide positive left margin.")
71
+
72
+ if (
73
+ (enlarge_top is None or enlarge_top == 0)
74
+ and (enlarge_right is None or enlarge_right == 0)
75
+ and (enlarge_bottom is None or enlarge_bottom == 0)
76
+ and (enlarge_left is None or enlarge_left == 0)
77
+ ):
78
+ raise gr.Error("At least one border must be enlarged.")
79
+
80
+ def uncrop(
81
+ input_image,
82
+ enlarge_top,
83
+ enlarge_right,
84
+ enlarge_bottom,
85
+ enlarge_left,
86
+ prompt,
87
+ negative_prompt,
88
+ smooth_border,
89
+ num_inference_steps,
90
+ guidance_scale,
91
+ image_guidance_scale,
92
+ strength,
93
+ denoising_steps,
94
+ is_randomize_seed,
95
+ seed,
96
+ debug_mode,
97
+ progress = gr.Progress()):
98
+ check(
99
+ input_image,
100
+ enlarge_top,
101
+ enlarge_right,
102
+ enlarge_bottom,
103
+ enlarge_left,
104
+ prompt,
105
+ negative_prompt,
106
+ smooth_border,
107
+ num_inference_steps,
108
+ guidance_scale,
109
+ image_guidance_scale,
110
+ strength,
111
+ denoising_steps,
112
+ is_randomize_seed,
113
+ seed,
114
+ debug_mode
115
+ )
116
+ start = time.time()
117
+ progress(0, desc = "Preparing data...")
118
+
119
+ if enlarge_top is None or enlarge_top == "":
120
+ enlarge_top = 0
121
+
122
+ if enlarge_right is None or enlarge_right == "":
123
+ enlarge_right = 0
124
+
125
+ if enlarge_bottom is None or enlarge_bottom == "":
126
+ enlarge_bottom = 0
127
+
128
+ if enlarge_left is None or enlarge_left == "":
129
+ enlarge_left = 0
130
+
131
+ if negative_prompt is None:
132
+ negative_prompt = ""
133
+
134
+ if smooth_border is None:
135
+ smooth_border = 0
136
+
137
+ if num_inference_steps is None:
138
+ num_inference_steps = 50
139
+
140
+ if guidance_scale is None:
141
+ guidance_scale = 7
142
+
143
+ if image_guidance_scale is None:
144
+ image_guidance_scale = 1.5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
 
146
+ if strength is None:
147
+ strength = 0.99
148
 
149
+ if denoising_steps is None:
150
+ denoising_steps = 1000
151
 
152
+ if seed is None:
153
+ seed = random.randint(0, max_64_bit_int)
154
 
155
+ random.seed(seed)
156
+ torch.manual_seed(seed)
 
 
157
 
158
+ original_height, original_width, original_channel = np.array(input_image).shape
159
+ output_width = enlarge_left + original_width + enlarge_right
160
+ output_height = enlarge_top + original_height + enlarge_bottom
 
 
 
161
 
162
+ # Enlarged image
163
+ enlarged_image = Image.new(mode = input_image.mode, size = (original_width, original_height), color = "black")
164
+ enlarged_image.paste(input_image, (0, 0))
165
+ enlarged_image = enlarged_image.resize((output_width, output_height))
166
+ enlarged_image = enlarged_image.filter(ImageFilter.BoxBlur(20))
167
+
168
+ enlarged_image.paste(input_image, (enlarge_left, enlarge_top))
169
+
170
+ horizontally_mirrored_input_image = input_image.transpose(Image.FLIP_LEFT_RIGHT).resize((original_width * 2, original_height))
171
+ enlarged_image.paste(horizontally_mirrored_input_image, (enlarge_left - (original_width * 2), enlarge_top))
172
+ enlarged_image.paste(horizontally_mirrored_input_image, (enlarge_left + original_width, enlarge_top))
173
+
174
+ vertically_mirrored_input_image = input_image.transpose(Image.FLIP_TOP_BOTTOM).resize((original_width, original_height * 2))
175
+ enlarged_image.paste(vertically_mirrored_input_image, (enlarge_left, enlarge_top - (original_height * 2)))
176
+ enlarged_image.paste(vertically_mirrored_input_image, (enlarge_left, enlarge_top + original_height))
177
+
178
+ returned_input_image = input_image.transpose(Image.ROTATE_180).resize((original_width * 2, original_height * 2))
179
+ enlarged_image.paste(returned_input_image, (enlarge_left - (original_width * 2), enlarge_top - (original_height * 2)))
180
+ enlarged_image.paste(returned_input_image, (enlarge_left - (original_width * 2), enlarge_top + original_height))
181
+ enlarged_image.paste(returned_input_image, (enlarge_left + original_width, enlarge_top - (original_height * 2)))
182
+ enlarged_image.paste(returned_input_image, (enlarge_left + original_width, enlarge_top + original_height))
183
+
184
+ enlarged_image = enlarged_image.filter(ImageFilter.BoxBlur(20))
185
+
186
+ # Noise image
187
+ noise_image = Image.new(mode = input_image.mode, size = (output_width, output_height), color = "black")
188
+ enlarged_pixels = enlarged_image.load()
189
+
190
+ for i in range(output_width):
191
+ for j in range(output_height):
192
+ enlarged_pixel = enlarged_pixels[i, j]
193
+ noise = min(max(enlarge_left - i, i - (enlarge_left + original_width), enlarge_top - j, j - (enlarge_top + original_height), 0), 255)
194
+ noise_image.putpixel((i, j), (noise_color(enlarged_pixel[0], noise), noise_color(enlarged_pixel[1], noise), noise_color(enlarged_pixel[2], noise), 255))
195
+
196
+ enlarged_image.paste(noise_image, (0, 0))
197
+ enlarged_image.paste(input_image, (enlarge_left, enlarge_top))
198
+
199
+ # Mask
200
+ mask_image = Image.new(mode = input_image.mode, size = (output_width, output_height), color = (255, 255, 255, 0))
201
+ black_mask = Image.new(mode = input_image.mode, size = (original_width - smooth_border, original_height - smooth_border), color = (0, 0, 0, 0))
202
+ mask_image.paste(black_mask, (enlarge_left + (smooth_border // 2), enlarge_top + (smooth_border // 2)))
203
+ mask_image = mask_image.filter(ImageFilter.BoxBlur((smooth_border // 2)))
204
+
205
+ # Limited to 1 million pixels
206
+ if 1024 * 1024 < output_width * output_height:
207
+ factor = ((1024 * 1024) / (output_width * output_height))**0.5
208
+ process_width = math.floor(output_width * factor)
209
+ process_height = math.floor(output_height * factor)
210
+
211
+ limitation = " Due to technical limitation, the image have been downscaled and then upscaled.";
212
+ else:
213
+ process_width = output_width
214
+ process_height = output_height
215
+
216
+ limitation = "";
217
+
218
+ # Width and height must be multiple of 8
219
+ if (process_width % 8) != 0 or (process_height % 8) != 0:
220
+ if ((process_width - (process_width % 8) + 8) * (process_height - (process_height % 8) + 8)) <= (1024 * 1024):
221
+ process_width = process_width - (process_width % 8) + 8
222
+ process_height = process_height - (process_height % 8) + 8
223
+ elif (process_height % 8) <= (process_width % 8) and ((process_width - (process_width % 8) + 8) * process_height) <= (1024 * 1024):
224
+ process_width = process_width - (process_width % 8) + 8
225
+ process_height = process_height - (process_height % 8)
226
+ elif (process_width % 8) <= (process_height % 8) and (process_width * (process_height - (process_height % 8) + 8)) <= (1024 * 1024):
227
+ process_width = process_width - (process_width % 8)
228
+ process_height = process_height - (process_height % 8) + 8
229
+ else:
230
+ process_width = process_width - (process_width % 8)
231
+ process_height = process_height - (process_height % 8)
232
+
233
+ progress(None, desc = "Processing...")
234
+
235
+ output_image = pipe(
236
+ seeds = [seed],
237
+ width = process_width,
238
+ height = process_height,
239
+ prompt = prompt,
240
+ negative_prompt = negative_prompt,
241
+ image = enlarged_image,
242
+ mask_image = mask_image,
243
+ num_inference_steps = num_inference_steps,
244
+ guidance_scale = guidance_scale,
245
+ image_guidance_scale = image_guidance_scale,
246
+ strength = strength,
247
+ denoising_steps = denoising_steps,
248
+ show_progress_bar = True
249
+ ).images[0]
250
+
251
+ if limitation != "":
252
+ output_image = output_image.resize((output_width, output_height))
253
+
254
+ if debug_mode == False:
255
+ input_image = None
256
+ enlarged_image = None
257
+ mask_image = None
258
+
259
+ end = time.time()
260
+ secondes = int(end - start)
261
+ minutes = math.floor(secondes / 60)
262
+ secondes = secondes - (minutes * 60)
263
+ hours = math.floor(minutes / 60)
264
+ minutes = minutes - (hours * 60)
265
+ return [
266
+ output_image,
267
+ ("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,
268
+ input_image,
269
+ enlarged_image,
270
+ mask_image
271
+ ]
272
+
273
+ with gr.Blocks() as interface:
274
+ gr.HTML(
275
+ """
276
+ <h1 style="text-align: center;">Outpainting demo</h1>
277
+ <p style="text-align: center;">Enlarges the point of view of your image, freely, without account, without watermark, without installation, which can be downloaded</p>
278
+ <br/>
279
+ <br/>
280
+ ✨ Powered by <i>SDXL 1.0</i> artificial intellingence.
281
+ <br/>
282
+ 💻 Your computer must <u>not</u> enter into standby mode.<br/>You can duplicate this space on a free account, it works on CPU and CUDA.<br/>
283
+ <a href='https://huggingface.co/spaces/clinteroni/outpainting-with-differential-diffusion-demo?duplicate=true'><img src='https://img.shields.io/badge/-Duplicate%20Space-blue?labelColor=white&style=flat&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAP5JREFUOE+lk7FqAkEURY+ltunEgFXS2sZGIbXfEPdLlnxJyDdYB62sbbUKpLbVNhyYFzbrrA74YJlh9r079973psed0cvUD4A+4HoCjsA85X0Dfn/RBLBgBDxnQPfAEJgBY+A9gALA4tcbamSzS4xq4FOQAJgCDwV2CPKV8tZAJcAjMMkUe1vX+U+SMhfAJEHasQIWmXNN3abzDwHUrgcRGmYcgKe0bxrblHEB4E/pndMazNpSZGcsZdBlYJcEL9Afo75molJyM2FxmPgmgPqlWNLGfwZGG6UiyEvLzHYDmoPkDDiNm9JR9uboiONcBXrpY1qmgs21x1QwyZcpvxt9NS09PlsPAAAAAElFTkSuQmCC&logoWidth=14'></a>
284
+ <br/>
285
+ ⚖️ You can use, modify and share the generated images but not for commercial uses.
286
+
287
+ """
288
+ )
289
+ with gr.Row():
290
+ with gr.Column():
291
+ dummy_1 = gr.Label(visible = False)
292
+ with gr.Column():
293
+ enlarge_top = gr.Number(minimum = 0, value = 64, precision = 0, label = "Uncrop on top ⬆️", info = "in pixels")
294
+ with gr.Column():
295
+ dummy_2 = gr.Label(visible = False)
296
+ with gr.Row():
297
+ with gr.Column():
298
+ enlarge_left = gr.Number(minimum = 0, value = 64, precision = 0, label = "Uncrop on left ⬅️", info = "in pixels")
299
+ with gr.Column():
300
+ input_image = gr.Image(label = "Your image", sources = ["upload", "webcam", "clipboard"], type = "pil")
301
+ with gr.Column():
302
+ enlarge_right = gr.Number(minimum = 0, value = 64, precision = 0, label = "Uncrop on right ➡️", info = "in pixels")
303
+ with gr.Row():
304
+ with gr.Column():
305
+ dummy_3 = gr.Label(visible = False)
306
+ with gr.Column():
307
+ enlarge_bottom = gr.Number(minimum = 0, value = 64, precision = 0, label = "Uncrop on bottom ⬇️", info = "in pixels")
308
+ with gr.Column():
309
+ dummy_4 = gr.Label(visible = False)
310
+ with gr.Row():
311
+ prompt = gr.Textbox(label = "Prompt", info = "Describe the subject, the background and the style of image; 77 token limit", placeholder = "Describe what you want to see in the entire image", lines = 2)
312
+ with gr.Row():
313
+ with gr.Accordion("Advanced options", open = False):
314
+ negative_prompt = gr.Textbox(label = "Negative prompt", placeholder = "Describe what you do NOT want to see in the entire image", value = 'Border, frame, painting, scribbling, smear, noise, blur, watermark')
315
+ smooth_border = gr.Slider(minimum = 0, maximum = 1024, value = 0, step = 2, label = "Smooth border", info = "lower=preserve original, higher=seamless")
316
+ num_inference_steps = gr.Slider(minimum = 10, maximum = 100, value = 50, step = 1, label = "Number of inference steps", info = "lower=faster, higher=image quality")
317
+ guidance_scale = gr.Slider(minimum = 1, maximum = 13, value = 7, step = 0.1, label = "Classifier-Free Guidance Scale", info = "lower=image quality, higher=follow the prompt")
318
+ image_guidance_scale = gr.Slider(minimum = 1, value = 1.5, step = 0.1, label = "Image Guidance Scale", info = "lower=image quality, higher=follow the image")
319
+ strength = gr.Slider(value = 0.99, minimum = 0.01, maximum = 1.0, step = 0.01, label = "Strength", info = "lower=follow the original area (discouraged), higher=redraw from scratch")
320
+ denoising_steps = gr.Number(minimum = 0, value = 1000, step = 1, label = "Denoising", info = "lower=irrelevant result, higher=relevant result")
321
+ randomize_seed = gr.Checkbox(label = "\U0001F3B2 Randomize seed", value = True, info = "If checked, result is always different")
322
+ seed = gr.Slider(minimum = 0, maximum = max_64_bit_int, step = 1, randomize = True, label = "Seed")
323
+ debug_mode = gr.Checkbox(label = "Debug mode", value = False, info = "Show intermediate results")
324
+
325
+ with gr.Row():
326
+ submit = gr.Button("🚀 Outpaint", variant = "primary")
327
+
328
+ with gr.Row():
329
+ uncropped_image = gr.Image(label = "Outpainted image")
330
+ with gr.Row():
331
+ information = gr.HTML()
332
+ with gr.Row():
333
+ original_image = gr.Image(label = "Original image", visible = False)
334
+ with gr.Row():
335
+ enlarged_image = gr.Image(label = "Enlarged image", visible = False)
336
+ with gr.Row():
337
+ mask_image = gr.Image(label = "Mask image", visible = False)
338
+
339
+ submit.click(fn = update_seed, inputs = [
340
+ randomize_seed,
341
+ seed
342
+ ], outputs = [
343
+ seed
344
+ ], queue = False, show_progress = False).then(toggle_debug, debug_mode, [
345
+ original_image,
346
+ enlarged_image,
347
+ mask_image
348
+ ], queue = False, show_progress = False).then(check, inputs = [
349
+ input_image,
350
+ enlarge_top,
351
+ enlarge_right,
352
+ enlarge_bottom,
353
+ enlarge_left,
354
+ prompt,
355
+ negative_prompt,
356
+ smooth_border,
357
+ num_inference_steps,
358
+ guidance_scale,
359
+ image_guidance_scale,
360
+ strength,
361
+ denoising_steps,
362
+ randomize_seed,
363
+ seed,
364
+ debug_mode
365
+ ], outputs = [], queue = False,
366
+ show_progress = False).success(uncrop, inputs = [
367
+ input_image,
368
+ enlarge_top,
369
+ enlarge_right,
370
+ enlarge_bottom,
371
+ enlarge_left,
372
+ prompt,
373
+ negative_prompt,
374
+ smooth_border,
375
+ num_inference_steps,
376
+ guidance_scale,
377
+ image_guidance_scale,
378
+ strength,
379
+ denoising_steps,
380
+ randomize_seed,
381
+ seed,
382
+ debug_mode
383
+ ], outputs = [
384
+ uncropped_image,
385
+ information,
386
+ original_image,
387
+ enlarged_image,
388
+ mask_image
389
+ ], scroll_to_output = True)
390
+
391
+ gr.Examples(
392
+ run_on_click = True,
393
+ fn = uncrop,
394
+ inputs = [
395
+ input_image,
396
+ enlarge_top,
397
+ enlarge_right,
398
+ enlarge_bottom,
399
+ enlarge_left,
400
+ prompt,
401
+ negative_prompt,
402
+ smooth_border,
403
+ num_inference_steps,
404
+ guidance_scale,
405
+ image_guidance_scale,
406
+ strength,
407
+ denoising_steps,
408
+ randomize_seed,
409
+ seed,
410
+ debug_mode
411
+ ],
412
+ outputs = [
413
+ uncropped_image,
414
+ information,
415
+ original_image,
416
+ enlarged_image,
417
+ mask_image
418
  ],
419
+ examples = [
420
+ [
421
+ "./examples/Coucang.jpg",
422
+ 1024,
423
+ 1024,
424
+ 1024,
425
+ 1024,
426
+ "A white Coucang, in a tree, ultrarealistic, realistic, photorealistic, 8k, bokeh",
427
+ "Border, frame, painting, drawing, cartoon, anime, 3d, scribbling, smear, noise, blur, watermark",
428
+ 0,
429
+ 50,
430
+ 7,
431
+ 1.5,
432
+ 0.99,
433
+ 1000,
434
+ False,
435
+ 123,
436
+ False
437
+ ],
438
+ ],
439
+ cache_examples = False,
440
  )
441
+
442
+ gr.Markdown(
443
+ """
444
+ ## How to prompt your image
445
+
446
+ To easily read your prompt, start with the subject, then describ the pose or action, then secondary elements, then the background, then the graphical style, then the image quality:
447
+ ```
448
+ A Vietnamese woman, red clothes, walking, smilling, in the street, a car on the left, in a modern city, photorealistic, 8k
449
+ ```
450
+
451
+ You can use round brackets to increase the importance of a part:
452
+ ```
453
+ A Vietnamese woman, (red clothes), walking, smilling, in the street, a car on the left, in a modern city, photorealistic, 8k
454
+ ```
455
+
456
+ You can use several levels of round brackets to even more increase the importance of a part:
457
+ ```
458
+ A Vietnamese woman, ((red clothes)), (walking), smilling, in the street, a car on the left, in a modern city, photorealistic, 8k
459
+ ```
460
+
461
+ You can use number instead of several round brackets:
462
+ ```
463
+ A Vietnamese woman, (red clothes:1.5), (walking), smilling, in the street, a car on the left, in a modern city, photorealistic, 8k
464
+ ```
465
+
466
+ You can do the same thing with square brackets to decrease the importance of a part:
467
+ ```
468
+ A [Vietnamese] woman, (red clothes:1.5), (walking), smilling, in the street, a car on the left, in a modern city, photorealistic, 8k
469
+ ```
470
+
471
+ To easily read your negative prompt, organize it the same way as your prompt (not important for the AI):
472
+ ```
473
+ man, boy, hat, running, tree, bicycle, forest, drawing, painting, cartoon, 3d, monochrome, blurry, noisy, bokeh
474
+ ```
475
+
476
+ ## Credit
477
+ The [example image](https://commons.wikimedia.org/wiki/File:Coucang.jpg) is by Aprisonsan
478
+ and licensed under CC-BY-SA 4.0 International.
479
+ """
 
 
480
  )
481
 
482
+ interface.queue().launch()