LPX55 commited on
Commit
763f9d6
·
verified ·
1 Parent(s): b94a79e
Files changed (1) hide show
  1. app.py +46 -215
app.py CHANGED
@@ -1,14 +1,12 @@
1
- import gradio as gr
2
  import spaces
 
3
  import torch
4
  from diffusers import AutoencoderKL, TCDScheduler
5
  from diffusers.models.model_loading_utils import load_state_dict
6
  from gradio_imageslider import ImageSlider
7
  from huggingface_hub import hf_hub_download
8
-
9
  from controlnet_union import ControlNetModel_Union
10
  from pipeline_fill_sd_xl import StableDiffusionXLFillPipeline
11
-
12
  from PIL import Image, ImageDraw
13
  import numpy as np
14
 
@@ -23,7 +21,6 @@ config_file = hf_hub_download(
23
  "xinsir/controlnet-union-sdxl-1.0",
24
  filename="config_promax.json",
25
  )
26
-
27
  config = ControlNetModel_Union.load_config(config_file)
28
  controlnet_model = ControlNetModel_Union.from_config(config)
29
  model_file = hf_hub_download(
@@ -35,11 +32,9 @@ model, _, _, _, _ = ControlNetModel_Union._load_pretrained_model(
35
  controlnet_model, state_dict, model_file, "xinsir/controlnet-union-sdxl-1.0"
36
  )
37
  model.to(device="cuda", dtype=torch.float16)
38
-
39
  vae = AutoencoderKL.from_pretrained(
40
  "madebyollin/sdxl-vae-fp16-fix", torch_dtype=torch.float16
41
  ).to("cuda")
42
-
43
  pipe = StableDiffusionXLFillPipeline.from_pretrained(
44
  "SG161222/RealVisXL_V5.0_Lightning",
45
  torch_dtype=torch.float16,
@@ -47,34 +42,21 @@ pipe = StableDiffusionXLFillPipeline.from_pretrained(
47
  controlnet=model,
48
  variant="fp16",
49
  )
50
-
51
  pipe = StableDiffusionXLFillPipeline.from_pretrained(
52
  "GraydientPlatformAPI/lustify-lightning",
53
  torch_dtype=torch.float16,
54
  vae=vae,
55
  controlnet=model,
56
  )
57
-
58
  pipe.scheduler = TCDScheduler.from_config(pipe.scheduler.config)
59
-
60
  pipe.to("cuda")
61
 
62
- # inpaint_model = hf_hub_download(
63
- # "andro-flock/LUSTIFY-SDXL-NSFW-checkpoint-v2-0-INPAINTING",
64
- # "lustifySDXLNSFW_v20-inpainting.safetensors",
65
- # )
66
- # pipe_inpaint = StableDiffusionXLFillPipeline.from_single_file(
67
- # "https://huggingface.co/andro-flock/LUSTIFY-SDXL-NSFW-checkpoint-v2-0-INPAINTING/raw/main/lustifySDXLNSFW_v20-inpainting.safetensors",
68
- # torch_dtype=torch.float16,
69
- # vae=vae,
70
- # controlnet=model,
71
- # use_safetensors=True
72
- # )
73
- # pipe_inpaint.to("cuda")
74
-
75
  @spaces.GPU(duration=12)
76
  def fill_image(prompt, image, model_selection, paste_back):
77
- print(f"Received image: {image}") # Debugging statement
 
 
 
78
 
79
  (
80
  prompt_embeds,
@@ -82,10 +64,8 @@ def fill_image(prompt, image, model_selection, paste_back):
82
  pooled_prompt_embeds,
83
  negative_pooled_prompt_embeds,
84
  ) = pipe.encode_prompt(prompt, "cuda", True)
85
-
86
  source = image["background"]
87
  mask = image["layers"][0]
88
-
89
  alpha_channel = mask.split()[3]
90
  binary_mask = alpha_channel.point(lambda p: p > 0 and 255)
91
  cnet_image = source.copy()
@@ -102,21 +82,17 @@ def fill_image(prompt, image, model_selection, paste_back):
102
 
103
  print(f"{model_selection=}")
104
  print(f"{paste_back=}")
105
-
106
  if paste_back:
107
  image = image.convert("RGBA")
108
  cnet_image.paste(image, (0, 0), binary_mask)
109
  else:
110
  cnet_image = image
111
-
112
  yield source, cnet_image
113
 
114
-
115
  def clear_result():
116
  return gr.update(value=None)
117
-
118
  def can_expand(source_width, source_height, target_width, target_height, alignment):
119
- """Checks if the image can be expanded based on the alignment."""
120
  if alignment in ("Left", "Right") and source_width >= target_width:
121
  return False
122
  if alignment in ("Top", "Bottom") and source_height >= target_height:
@@ -125,16 +101,11 @@ def can_expand(source_width, source_height, target_width, target_height, alignme
125
 
126
  def prepare_image_and_mask(image, width, height, overlap_percentage, resize_option, custom_resize_percentage, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom):
127
  target_size = (width, height)
128
-
129
- # Calculate the scaling factor to fit the image within the target size
130
  scale_factor = min(target_size[0] / image.width, target_size[1] / image.height)
131
  new_width = int(image.width * scale_factor)
132
  new_height = int(image.height * scale_factor)
133
-
134
- # Resize the source image to fit within target size
135
- source = image.resize((new_width, new_height), Image.LANCZOS)
136
 
137
- # Apply resize option using percentages
138
  if resize_option == "Full":
139
  resize_percentage = 100
140
  elif resize_option == "80%":
@@ -148,27 +119,19 @@ def prepare_image_and_mask(image, width, height, overlap_percentage, resize_opti
148
  else: # Custom
149
  resize_percentage = custom_resize_percentage
150
 
151
- # Calculate new dimensions based on percentage
152
  resize_factor = resize_percentage / 100
153
  new_width = int(source.width * resize_factor)
154
  new_height = int(source.height * resize_factor)
155
-
156
- # Ensure minimum size of 64 pixels
157
  new_width = max(new_width, 64)
158
  new_height = max(new_height, 64)
159
 
160
- # Resize the image
161
  source = source.resize((new_width, new_height), Image.LANCZOS)
162
 
163
- # Calculate the overlap in pixels based on the percentage
164
  overlap_x = int(new_width * (overlap_percentage / 100))
165
  overlap_y = int(new_height * (overlap_percentage / 100))
166
-
167
- # Ensure minimum overlap of 1 pixel
168
  overlap_x = max(overlap_x, 1)
169
  overlap_y = max(overlap_y, 1)
170
 
171
- # Calculate margins based on alignment
172
  if alignment == "Middle":
173
  margin_x = (target_size[0] - new_width) // 2
174
  margin_y = (target_size[1] - new_height) // 2
@@ -185,26 +148,21 @@ def prepare_image_and_mask(image, width, height, overlap_percentage, resize_opti
185
  margin_x = (target_size[0] - new_width) // 2
186
  margin_y = target_size[1] - new_height
187
 
188
- # Adjust margins to eliminate gaps
189
  margin_x = max(0, min(margin_x, target_size[0] - new_width))
190
  margin_y = max(0, min(margin_y, target_size[1] - new_height))
191
 
192
- # Create a new background image and paste the resized source image
193
  background = Image.new('RGB', target_size, (255, 255, 255))
194
  background.paste(source, (margin_x, margin_y))
195
 
196
- # Create the mask
197
  mask = Image.new('L', target_size, 255)
198
  mask_draw = ImageDraw.Draw(mask)
199
 
200
- # Calculate overlap areas
201
  white_gaps_patch = 2
202
-
203
  left_overlap = margin_x + overlap_x if overlap_left else margin_x + white_gaps_patch
204
  right_overlap = margin_x + new_width - overlap_x if overlap_right else margin_x + new_width - white_gaps_patch
205
  top_overlap = margin_y + overlap_y if overlap_top else margin_y + white_gaps_patch
206
  bottom_overlap = margin_y + new_height - overlap_y if overlap_bottom else margin_y + new_height - white_gaps_patch
207
-
208
  if alignment == "Left":
209
  left_overlap = margin_x + overlap_x if overlap_left else margin_x
210
  elif alignment == "Right":
@@ -214,34 +172,21 @@ def prepare_image_and_mask(image, width, height, overlap_percentage, resize_opti
214
  elif alignment == "Bottom":
215
  bottom_overlap = margin_y + new_height - overlap_y if overlap_bottom else margin_y + new_height
216
 
217
-
218
- # Draw the mask
219
  mask_draw.rectangle([
220
  (left_overlap, top_overlap),
221
  (right_overlap, bottom_overlap)
222
  ], fill=0)
223
-
224
  return background, mask
225
 
226
  def preview_image_and_mask(image, width, height, overlap_percentage, resize_option, custom_resize_percentage, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom):
227
  background, mask = prepare_image_and_mask(image, width, height, overlap_percentage, resize_option, custom_resize_percentage, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom)
228
-
229
- # Create a preview image showing the mask
230
  preview = background.copy().convert('RGBA')
231
-
232
- # Create a semi-transparent red overlay
233
- red_overlay = Image.new('RGBA', background.size, (255, 0, 0, 64)) # Reduced alpha to 64 (25% opacity)
234
-
235
- # Convert black pixels in the mask to semi-transparent red
236
  red_mask = Image.new('RGBA', background.size, (0, 0, 0, 0))
237
  red_mask.paste(red_overlay, (0, 0), mask)
238
-
239
- # Overlay the red mask on the background
240
  preview = Image.alpha_composite(preview, red_mask)
241
-
242
  return preview
243
 
244
-
245
  @spaces.GPU(duration=12)
246
  def inpaint(prompt, image, inpaint_model, paste_back):
247
  global pipe
@@ -252,39 +197,27 @@ def inpaint(prompt, image, inpaint_model, paste_back):
252
  vae=vae,
253
  controlnet=model,
254
  ).to("cuda")
255
- # if pipe.config.model_name == "Lustify Inpaint":
256
-
257
-
258
  mask = Image.fromarray(image["mask"]).convert("L")
259
  image = Image.fromarray(image["image"])
260
-
261
  result = pipe(prompt=prompt, image=image, mask_image=mask).images[0]
262
- # result = pipe_inpaint(prompt=prompt, image=image, mask_image=mask).images[0]
263
-
264
  if paste_back:
265
  result.paste(image, (0, 0), Image.fromarray(255 - np.array(mask)))
266
-
267
  return result
268
 
269
  @spaces.GPU(duration=12)
270
  def outpaint(image, width, height, overlap_percentage, num_inference_steps, resize_option, custom_resize_percentage, prompt_input, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom):
271
  background, mask = prepare_image_and_mask(image, width, height, overlap_percentage, resize_option, custom_resize_percentage, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom)
272
-
273
  if not can_expand(background.width, background.height, width, height, alignment):
274
  alignment = "Middle"
275
-
276
  cnet_image = background.copy()
277
  cnet_image.paste(0, (0, 0), mask)
278
-
279
  final_prompt = f"{prompt_input} , high quality, 4k"
280
-
281
  (
282
  prompt_embeds,
283
  negative_prompt_embeds,
284
  pooled_prompt_embeds,
285
  negative_pooled_prompt_embeds,
286
  ) = pipe.encode_prompt(final_prompt, "cuda", True)
287
-
288
  for image in pipe(
289
  prompt_embeds=prompt_embeds,
290
  negative_prompt_embeds=negative_prompt_embeds,
@@ -294,31 +227,24 @@ def outpaint(image, width, height, overlap_percentage, num_inference_steps, resi
294
  num_inference_steps=num_inference_steps
295
  ):
296
  yield cnet_image, image
297
-
298
  image = image.convert("RGBA")
299
  cnet_image.paste(image, (0, 0), mask)
300
-
301
  yield background, cnet_image
302
 
303
  @spaces.GPU(duration=12)
304
  def infer(image, width, height, overlap_percentage, num_inference_steps, resize_option, custom_resize_percentage, prompt_input, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom):
305
  background, mask = prepare_image_and_mask(image, width, height, overlap_percentage, resize_option, custom_resize_percentage, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom)
306
-
307
  if not can_expand(background.width, background.height, width, height, alignment):
308
  alignment = "Middle"
309
-
310
  cnet_image = background.copy()
311
  cnet_image.paste(0, (0, 0), mask)
312
-
313
  final_prompt = f"{prompt_input} , high quality, 4k"
314
-
315
  (
316
  prompt_embeds,
317
  negative_prompt_embeds,
318
  pooled_prompt_embeds,
319
  negative_pooled_prompt_embeds,
320
  ) = pipe.encode_prompt(final_prompt, "cuda", True)
321
-
322
  for image in pipe(
323
  prompt_embeds=prompt_embeds,
324
  negative_prompt_embeds=negative_prompt_embeds,
@@ -328,17 +254,14 @@ def infer(image, width, height, overlap_percentage, num_inference_steps, resize_
328
  num_inference_steps=num_inference_steps
329
  ):
330
  yield cnet_image, image
331
-
332
  image = image.convert("RGBA")
333
  cnet_image.paste(image, (0, 0), mask)
334
-
335
  yield background, cnet_image
336
- def clear_result():
337
- """Clears the result ImageSlider."""
338
- return gr.update(value=None)
339
 
340
  def preload_presets(target_ratio, ui_width, ui_height):
341
- """Updates the width and height sliders based on the selected aspect ratio."""
342
  if target_ratio == "9:16":
343
  changed_width = 720
344
  changed_height = 1280
@@ -357,6 +280,8 @@ def preload_presets(target_ratio, ui_width, ui_height):
357
  return changed_width, changed_height, gr.update()
358
  elif target_ratio == "Custom":
359
  return ui_width, ui_height, gr.update(open=True)
 
 
360
 
361
  def select_the_right_preset(user_width, user_height):
362
  if user_width == 720 and user_height == 1280:
@@ -374,7 +299,6 @@ def toggle_custom_resize_slider(resize_option):
374
  return gr.update(visible=(resize_option == "Custom"))
375
 
376
  def update_history(new_image, history):
377
- """Updates the history gallery with the new image."""
378
  if history is None:
379
  history = []
380
  history.insert(0, new_image)
@@ -399,14 +323,13 @@ title = """<h1 align="center">Diffusers Image Outpaint</h1>
399
  <p style="display: flex;gap: 6px;">
400
  <a href="https://huggingface.co/spaces/fffiloni/diffusers-image-outpout?duplicate=true">
401
  <img src="https://huggingface.co/datasets/huggingface/badges/resolve/main/duplicate-this-space-md.svg" alt="Duplicate this Space">
402
- </a> to skip the queue and enjoy faster inference on the GPU of your choice
403
  </p>
404
  </div>
405
  """
406
 
407
  with gr.Blocks(css=css, fill_height=True) as demo:
408
  gr.Markdown("# Diffusers Inpaint and Outpaint")
409
-
410
  with gr.Tabs():
411
  with gr.TabItem("Inpaint"):
412
  with gr.Column():
@@ -423,31 +346,21 @@ with gr.Blocks(css=css, fill_height=True) as demo:
423
  value="RealVisXL V5.0 Lightning",
424
  label="Model",
425
  )
426
-
427
  with gr.Row():
428
  run_button = gr.Button("Generate")
429
  paste_back = gr.Checkbox(True, label="Paste back original")
430
-
431
  with gr.Row(equal_height=False):
432
  input_image = gr.ImageMask(
433
  type="pil", label="Input Image", layers=True
434
- # type="pil", label="Input Image", crop_size=(1024, 1024), layers=False
435
  )
436
-
437
  result = ImageSlider(
438
  interactive=False,
439
  label="Generated Image",
440
  )
441
-
442
  use_as_input_button = gr.Button("Use as Input Image", visible=False)
443
-
444
- def use_output_as_input(output_image):
445
- return gr.update(value=output_image[1])
446
-
447
  use_as_input_button.click(
448
  fn=use_output_as_input, inputs=[result], outputs=[input_image]
449
  )
450
-
451
  run_button.click(
452
  fn=clear_result,
453
  inputs=None,
@@ -459,13 +372,12 @@ with gr.Blocks(css=css, fill_height=True) as demo:
459
  ).then(
460
  fn=fill_image,
461
  inputs=[prompt, input_image, model_selection, paste_back],
462
- outputs=result,
463
  ).then(
464
  fn=lambda: gr.update(visible=True),
465
  inputs=None,
466
  outputs=use_as_input_button,
467
  )
468
-
469
  prompt.submit(
470
  fn=clear_result,
471
  inputs=None,
@@ -477,29 +389,25 @@ with gr.Blocks(css=css, fill_height=True) as demo:
477
  ).then(
478
  fn=fill_image,
479
  inputs=[prompt, input_image, model_selection, paste_back],
480
- outputs=result,
481
  ).then(
482
  fn=lambda: gr.update(visible=True),
483
  inputs=None,
484
  outputs=use_as_input_button,
485
  )
486
-
487
  with gr.TabItem("Outpaint"):
488
  with gr.Column():
489
-
490
  with gr.Row():
491
  with gr.Column():
492
- outpaint_input_image = gr.Image(
493
  type="pil",
494
  label="Input Image"
495
  )
496
-
497
  with gr.Row():
498
  with gr.Column(scale=2):
499
  prompt_input = gr.Textbox(label="Prompt (Optional)")
500
  with gr.Column(scale=1):
501
  runout_button = gr.Button("Generate")
502
-
503
  with gr.Row():
504
  target_ratio = gr.Radio(
505
  label="Expected Ratio",
@@ -507,13 +415,11 @@ with gr.Blocks(css=css, fill_height=True) as demo:
507
  value="1:1",
508
  scale=2
509
  )
510
-
511
  alignment_dropdown = gr.Dropdown(
512
  choices=["Middle", "Left", "Right", "Top", "Bottom"],
513
  value="Middle",
514
  label="Alignment"
515
  )
516
-
517
  with gr.Accordion(label="Advanced settings", open=False) as settings_panel:
518
  with gr.Column():
519
  with gr.Row():
@@ -522,16 +428,15 @@ with gr.Blocks(css=css, fill_height=True) as demo:
522
  minimum=720,
523
  maximum=1536,
524
  step=8,
525
- value=1280, # Set a default value
526
  )
527
  height_slider = gr.Slider(
528
  label="Target Height",
529
  minimum=720,
530
  maximum=1536,
531
  step=8,
532
- value=1280, # Set a default value
533
  )
534
-
535
  num_inference_steps = gr.Slider(label="Steps", minimum=4, maximum=12, step=1, value=8)
536
  with gr.Group():
537
  overlap_percentage = gr.Slider(
@@ -561,11 +466,8 @@ with gr.Blocks(css=css, fill_height=True) as demo:
561
  value=50,
562
  visible=False
563
  )
564
-
565
  with gr.Column():
566
  preview_button = gr.Button("Preview alignment and mask")
567
-
568
-
569
  gr.Examples(
570
  examples=[
571
  ["./examples/example_1.webp", 1280, 720, "Middle"],
@@ -573,136 +475,65 @@ with gr.Blocks(css=css, fill_height=True) as demo:
573
  ["./examples/example_3.jpg", 1024, 1024, "Top"],
574
  ["./examples/example_3.jpg", 1024, 1024, "Bottom"],
575
  ],
576
- inputs=[outpaint_input_image, width_slider, height_slider, alignment_dropdown],
577
  )
578
-
579
-
580
-
581
  with gr.Column():
582
- result = ImageSlider(
583
  interactive=False,
584
  label="Generated Image",
585
  )
586
- use_as_input_button = gr.Button("Use as Input Image", visible=False)
587
-
588
  history_gallery = gr.Gallery(label="History", columns=6, object_fit="contain", interactive=False)
589
  preview_image = gr.Image(label="Preview")
590
-
591
-
592
-
593
- def use_output_as_input(output_image):
594
- """Sets the generated output as the new input image."""
595
- return gr.update(value=output_image[1])
596
-
597
- use_as_input_button.click(
598
  fn=use_output_as_input,
599
- inputs=[result],
600
- outputs=[outpaint_input_image]
601
- )
602
-
603
- # Set up event handlers
604
- run_button.click(
605
- fn=fill_image,
606
- inputs=[prompt, input_image, model_selection, paste_back],
607
- outputs=result,
608
- )
609
-
610
- target_ratio.change(
611
- fn=preload_presets,
612
- inputs=[target_ratio, width_slider, height_slider],
613
- outputs=[width_slider, height_slider, settings_panel],
614
- queue=False
615
- )
616
-
617
- width_slider.change(
618
- fn=select_the_right_preset,
619
- inputs=[width_slider, height_slider],
620
- outputs=[target_ratio],
621
- queue=False
622
- )
623
-
624
- height_slider.change(
625
- fn=select_the_right_preset,
626
- inputs=[width_slider, height_slider],
627
- outputs=[target_ratio],
628
- queue=False
629
  )
630
-
631
- resize_option.change(
632
- fn=toggle_custom_resize_slider,
633
- inputs=[resize_option],
634
- outputs=[custom_resize_percentage],
635
- queue=False
636
- )
637
-
638
- runout_button.click( # Clear the result
639
  fn=clear_result,
640
  inputs=None,
641
- outputs=result,
642
- ).then( # Generate the new image
643
  fn=infer,
644
- inputs=[outpaint_input_image, width_slider, height_slider, overlap_percentage, num_inference_steps,
645
  resize_option, custom_resize_percentage, prompt_input, alignment_dropdown,
646
  overlap_left, overlap_right, overlap_top, overlap_bottom],
647
- outputs=result,
648
- ).then( # Update the history gallery
649
  fn=lambda x, history: update_history(x[1], history),
650
- inputs=[result, history_gallery],
651
  outputs=history_gallery,
652
- ).then( # Show the "Use as Input Image" button
653
  fn=lambda: gr.update(visible=True),
654
  inputs=None,
655
- outputs=use_as_input_button,
656
  )
657
-
658
- prompt_input.submit( # Clear the result
659
  fn=clear_result,
660
  inputs=None,
661
- outputs=result,
662
- ).then( # Generate the new image
663
  fn=infer,
664
- inputs=[outpaint_input_image, width_slider, height_slider, overlap_percentage, num_inference_steps,
665
  resize_option, custom_resize_percentage, prompt_input, alignment_dropdown,
666
  overlap_left, overlap_right, overlap_top, overlap_bottom],
667
- outputs=result,
668
- ).then( # Update the history gallery
669
  fn=lambda x, history: update_history(x[1], history),
670
- inputs=[result, history_gallery],
671
  outputs=history_gallery,
672
- ).then( # Show the "Use as Input Image" button
673
  fn=lambda: gr.update(visible=True),
674
  inputs=None,
675
- outputs=use_as_input_button,
676
- )
677
-
678
- preview_button.click(
679
- fn=preview_image_and_mask,
680
- inputs=[outpaint_input_image, width_slider, height_slider, overlap_percentage, resize_option, custom_resize_percentage, alignment_dropdown,
681
- overlap_left, overlap_right, overlap_top, overlap_bottom],
682
- outputs=preview_image,
683
- queue=False
684
- )
685
-
686
- runout_button.click(
687
- fn=infer,
688
- inputs=[outpaint_input_image, width_slider, height_slider, overlap_percentage, num_inference_steps,
689
- resize_option, custom_resize_percentage, prompt_input, alignment_dropdown,
690
- overlap_left, overlap_right, overlap_top, overlap_bottom],
691
- outputs=result,
692
  )
693
-
694
  preview_button.click(
695
  fn=preview_image_and_mask,
696
- inputs=[outpaint_input_image, width_slider, height_slider, overlap_percentage, resize_option, custom_resize_percentage, alignment_dropdown,
697
  overlap_left, overlap_right, overlap_top, overlap_bottom],
698
- outputs=preview_image,
699
  queue=False
700
  )
701
 
702
- resize_option.change(
703
- fn=lambda x: gr.update(visible=(x == "Custom")),
704
- inputs=[resize_option],
705
- outputs=[custom_resize_percentage]
706
- )
707
-
708
  demo.launch(show_error=True)
 
 
1
  import spaces
2
+ import gradio as gr
3
  import torch
4
  from diffusers import AutoencoderKL, TCDScheduler
5
  from diffusers.models.model_loading_utils import load_state_dict
6
  from gradio_imageslider import ImageSlider
7
  from huggingface_hub import hf_hub_download
 
8
  from controlnet_union import ControlNetModel_Union
9
  from pipeline_fill_sd_xl import StableDiffusionXLFillPipeline
 
10
  from PIL import Image, ImageDraw
11
  import numpy as np
12
 
 
21
  "xinsir/controlnet-union-sdxl-1.0",
22
  filename="config_promax.json",
23
  )
 
24
  config = ControlNetModel_Union.load_config(config_file)
25
  controlnet_model = ControlNetModel_Union.from_config(config)
26
  model_file = hf_hub_download(
 
32
  controlnet_model, state_dict, model_file, "xinsir/controlnet-union-sdxl-1.0"
33
  )
34
  model.to(device="cuda", dtype=torch.float16)
 
35
  vae = AutoencoderKL.from_pretrained(
36
  "madebyollin/sdxl-vae-fp16-fix", torch_dtype=torch.float16
37
  ).to("cuda")
 
38
  pipe = StableDiffusionXLFillPipeline.from_pretrained(
39
  "SG161222/RealVisXL_V5.0_Lightning",
40
  torch_dtype=torch.float16,
 
42
  controlnet=model,
43
  variant="fp16",
44
  )
 
45
  pipe = StableDiffusionXLFillPipeline.from_pretrained(
46
  "GraydientPlatformAPI/lustify-lightning",
47
  torch_dtype=torch.float16,
48
  vae=vae,
49
  controlnet=model,
50
  )
 
51
  pipe.scheduler = TCDScheduler.from_config(pipe.scheduler.config)
 
52
  pipe.to("cuda")
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  @spaces.GPU(duration=12)
55
  def fill_image(prompt, image, model_selection, paste_back):
56
+ print(f"Received image: {image}")
57
+ if image is None:
58
+ yield None, None
59
+ return
60
 
61
  (
62
  prompt_embeds,
 
64
  pooled_prompt_embeds,
65
  negative_pooled_prompt_embeds,
66
  ) = pipe.encode_prompt(prompt, "cuda", True)
 
67
  source = image["background"]
68
  mask = image["layers"][0]
 
69
  alpha_channel = mask.split()[3]
70
  binary_mask = alpha_channel.point(lambda p: p > 0 and 255)
71
  cnet_image = source.copy()
 
82
 
83
  print(f"{model_selection=}")
84
  print(f"{paste_back=}")
 
85
  if paste_back:
86
  image = image.convert("RGBA")
87
  cnet_image.paste(image, (0, 0), binary_mask)
88
  else:
89
  cnet_image = image
 
90
  yield source, cnet_image
91
 
 
92
  def clear_result():
93
  return gr.update(value=None)
94
+
95
  def can_expand(source_width, source_height, target_width, target_height, alignment):
 
96
  if alignment in ("Left", "Right") and source_width >= target_width:
97
  return False
98
  if alignment in ("Top", "Bottom") and source_height >= target_height:
 
101
 
102
  def prepare_image_and_mask(image, width, height, overlap_percentage, resize_option, custom_resize_percentage, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom):
103
  target_size = (width, height)
 
 
104
  scale_factor = min(target_size[0] / image.width, target_size[1] / image.height)
105
  new_width = int(image.width * scale_factor)
106
  new_height = int(image.height * scale_factor)
 
 
 
107
 
108
+ source = image.resize((new_width, new_height), Image.LANCZOS)
109
  if resize_option == "Full":
110
  resize_percentage = 100
111
  elif resize_option == "80%":
 
119
  else: # Custom
120
  resize_percentage = custom_resize_percentage
121
 
 
122
  resize_factor = resize_percentage / 100
123
  new_width = int(source.width * resize_factor)
124
  new_height = int(source.height * resize_factor)
 
 
125
  new_width = max(new_width, 64)
126
  new_height = max(new_height, 64)
127
 
 
128
  source = source.resize((new_width, new_height), Image.LANCZOS)
129
 
 
130
  overlap_x = int(new_width * (overlap_percentage / 100))
131
  overlap_y = int(new_height * (overlap_percentage / 100))
 
 
132
  overlap_x = max(overlap_x, 1)
133
  overlap_y = max(overlap_y, 1)
134
 
 
135
  if alignment == "Middle":
136
  margin_x = (target_size[0] - new_width) // 2
137
  margin_y = (target_size[1] - new_height) // 2
 
148
  margin_x = (target_size[0] - new_width) // 2
149
  margin_y = target_size[1] - new_height
150
 
 
151
  margin_x = max(0, min(margin_x, target_size[0] - new_width))
152
  margin_y = max(0, min(margin_y, target_size[1] - new_height))
153
 
 
154
  background = Image.new('RGB', target_size, (255, 255, 255))
155
  background.paste(source, (margin_x, margin_y))
156
 
 
157
  mask = Image.new('L', target_size, 255)
158
  mask_draw = ImageDraw.Draw(mask)
159
 
 
160
  white_gaps_patch = 2
 
161
  left_overlap = margin_x + overlap_x if overlap_left else margin_x + white_gaps_patch
162
  right_overlap = margin_x + new_width - overlap_x if overlap_right else margin_x + new_width - white_gaps_patch
163
  top_overlap = margin_y + overlap_y if overlap_top else margin_y + white_gaps_patch
164
  bottom_overlap = margin_y + new_height - overlap_y if overlap_bottom else margin_y + new_height - white_gaps_patch
165
+
166
  if alignment == "Left":
167
  left_overlap = margin_x + overlap_x if overlap_left else margin_x
168
  elif alignment == "Right":
 
172
  elif alignment == "Bottom":
173
  bottom_overlap = margin_y + new_height - overlap_y if overlap_bottom else margin_y + new_height
174
 
 
 
175
  mask_draw.rectangle([
176
  (left_overlap, top_overlap),
177
  (right_overlap, bottom_overlap)
178
  ], fill=0)
 
179
  return background, mask
180
 
181
  def preview_image_and_mask(image, width, height, overlap_percentage, resize_option, custom_resize_percentage, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom):
182
  background, mask = prepare_image_and_mask(image, width, height, overlap_percentage, resize_option, custom_resize_percentage, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom)
 
 
183
  preview = background.copy().convert('RGBA')
184
+ red_overlay = Image.new('RGBA', background.size, (255, 0, 0, 64))
 
 
 
 
185
  red_mask = Image.new('RGBA', background.size, (0, 0, 0, 0))
186
  red_mask.paste(red_overlay, (0, 0), mask)
 
 
187
  preview = Image.alpha_composite(preview, red_mask)
 
188
  return preview
189
 
 
190
  @spaces.GPU(duration=12)
191
  def inpaint(prompt, image, inpaint_model, paste_back):
192
  global pipe
 
197
  vae=vae,
198
  controlnet=model,
199
  ).to("cuda")
 
 
 
200
  mask = Image.fromarray(image["mask"]).convert("L")
201
  image = Image.fromarray(image["image"])
 
202
  result = pipe(prompt=prompt, image=image, mask_image=mask).images[0]
 
 
203
  if paste_back:
204
  result.paste(image, (0, 0), Image.fromarray(255 - np.array(mask)))
 
205
  return result
206
 
207
  @spaces.GPU(duration=12)
208
  def outpaint(image, width, height, overlap_percentage, num_inference_steps, resize_option, custom_resize_percentage, prompt_input, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom):
209
  background, mask = prepare_image_and_mask(image, width, height, overlap_percentage, resize_option, custom_resize_percentage, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom)
 
210
  if not can_expand(background.width, background.height, width, height, alignment):
211
  alignment = "Middle"
 
212
  cnet_image = background.copy()
213
  cnet_image.paste(0, (0, 0), mask)
 
214
  final_prompt = f"{prompt_input} , high quality, 4k"
 
215
  (
216
  prompt_embeds,
217
  negative_prompt_embeds,
218
  pooled_prompt_embeds,
219
  negative_pooled_prompt_embeds,
220
  ) = pipe.encode_prompt(final_prompt, "cuda", True)
 
221
  for image in pipe(
222
  prompt_embeds=prompt_embeds,
223
  negative_prompt_embeds=negative_prompt_embeds,
 
227
  num_inference_steps=num_inference_steps
228
  ):
229
  yield cnet_image, image
 
230
  image = image.convert("RGBA")
231
  cnet_image.paste(image, (0, 0), mask)
 
232
  yield background, cnet_image
233
 
234
  @spaces.GPU(duration=12)
235
  def infer(image, width, height, overlap_percentage, num_inference_steps, resize_option, custom_resize_percentage, prompt_input, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom):
236
  background, mask = prepare_image_and_mask(image, width, height, overlap_percentage, resize_option, custom_resize_percentage, alignment, overlap_left, overlap_right, overlap_top, overlap_bottom)
 
237
  if not can_expand(background.width, background.height, width, height, alignment):
238
  alignment = "Middle"
 
239
  cnet_image = background.copy()
240
  cnet_image.paste(0, (0, 0), mask)
 
241
  final_prompt = f"{prompt_input} , high quality, 4k"
 
242
  (
243
  prompt_embeds,
244
  negative_prompt_embeds,
245
  pooled_prompt_embeds,
246
  negative_pooled_prompt_embeds,
247
  ) = pipe.encode_prompt(final_prompt, "cuda", True)
 
248
  for image in pipe(
249
  prompt_embeds=prompt_embeds,
250
  negative_prompt_embeds=negative_prompt_embeds,
 
254
  num_inference_steps=num_inference_steps
255
  ):
256
  yield cnet_image, image
 
257
  image = image.convert("RGBA")
258
  cnet_image.paste(image, (0, 0), mask)
 
259
  yield background, cnet_image
260
+
261
+ def use_output_as_input(output_image):
262
+ return gr.update(value=output_image[1])
263
 
264
  def preload_presets(target_ratio, ui_width, ui_height):
 
265
  if target_ratio == "9:16":
266
  changed_width = 720
267
  changed_height = 1280
 
280
  return changed_width, changed_height, gr.update()
281
  elif target_ratio == "Custom":
282
  return ui_width, ui_height, gr.update(open=True)
283
+ else:
284
+ return ui_width, ui_height, gr.update()
285
 
286
  def select_the_right_preset(user_width, user_height):
287
  if user_width == 720 and user_height == 1280:
 
299
  return gr.update(visible=(resize_option == "Custom"))
300
 
301
  def update_history(new_image, history):
 
302
  if history is None:
303
  history = []
304
  history.insert(0, new_image)
 
323
  <p style="display: flex;gap: 6px;">
324
  <a href="https://huggingface.co/spaces/fffiloni/diffusers-image-outpout?duplicate=true">
325
  <img src="https://huggingface.co/datasets/huggingface/badges/resolve/main/duplicate-this-space-md.svg" alt="Duplicate this Space">
326
+ </a> to skip the queue and enjoy faster inference on the GPU of your choice
327
  </p>
328
  </div>
329
  """
330
 
331
  with gr.Blocks(css=css, fill_height=True) as demo:
332
  gr.Markdown("# Diffusers Inpaint and Outpaint")
 
333
  with gr.Tabs():
334
  with gr.TabItem("Inpaint"):
335
  with gr.Column():
 
346
  value="RealVisXL V5.0 Lightning",
347
  label="Model",
348
  )
 
349
  with gr.Row():
350
  run_button = gr.Button("Generate")
351
  paste_back = gr.Checkbox(True, label="Paste back original")
 
352
  with gr.Row(equal_height=False):
353
  input_image = gr.ImageMask(
354
  type="pil", label="Input Image", layers=True
 
355
  )
 
356
  result = ImageSlider(
357
  interactive=False,
358
  label="Generated Image",
359
  )
 
360
  use_as_input_button = gr.Button("Use as Input Image", visible=False)
 
 
 
 
361
  use_as_input_button.click(
362
  fn=use_output_as_input, inputs=[result], outputs=[input_image]
363
  )
 
364
  run_button.click(
365
  fn=clear_result,
366
  inputs=None,
 
372
  ).then(
373
  fn=fill_image,
374
  inputs=[prompt, input_image, model_selection, paste_back],
375
+ outputs=[result],
376
  ).then(
377
  fn=lambda: gr.update(visible=True),
378
  inputs=None,
379
  outputs=use_as_input_button,
380
  )
 
381
  prompt.submit(
382
  fn=clear_result,
383
  inputs=None,
 
389
  ).then(
390
  fn=fill_image,
391
  inputs=[prompt, input_image, model_selection, paste_back],
392
+ outputs=[result],
393
  ).then(
394
  fn=lambda: gr.update(visible=True),
395
  inputs=None,
396
  outputs=use_as_input_button,
397
  )
 
398
  with gr.TabItem("Outpaint"):
399
  with gr.Column():
 
400
  with gr.Row():
401
  with gr.Column():
402
+ input_image_outpaint = gr.Image(
403
  type="pil",
404
  label="Input Image"
405
  )
 
406
  with gr.Row():
407
  with gr.Column(scale=2):
408
  prompt_input = gr.Textbox(label="Prompt (Optional)")
409
  with gr.Column(scale=1):
410
  runout_button = gr.Button("Generate")
 
411
  with gr.Row():
412
  target_ratio = gr.Radio(
413
  label="Expected Ratio",
 
415
  value="1:1",
416
  scale=2
417
  )
 
418
  alignment_dropdown = gr.Dropdown(
419
  choices=["Middle", "Left", "Right", "Top", "Bottom"],
420
  value="Middle",
421
  label="Alignment"
422
  )
 
423
  with gr.Accordion(label="Advanced settings", open=False) as settings_panel:
424
  with gr.Column():
425
  with gr.Row():
 
428
  minimum=720,
429
  maximum=1536,
430
  step=8,
431
+ value=1280,
432
  )
433
  height_slider = gr.Slider(
434
  label="Target Height",
435
  minimum=720,
436
  maximum=1536,
437
  step=8,
438
+ value=1280,
439
  )
 
440
  num_inference_steps = gr.Slider(label="Steps", minimum=4, maximum=12, step=1, value=8)
441
  with gr.Group():
442
  overlap_percentage = gr.Slider(
 
466
  value=50,
467
  visible=False
468
  )
 
469
  with gr.Column():
470
  preview_button = gr.Button("Preview alignment and mask")
 
 
471
  gr.Examples(
472
  examples=[
473
  ["./examples/example_1.webp", 1280, 720, "Middle"],
 
475
  ["./examples/example_3.jpg", 1024, 1024, "Top"],
476
  ["./examples/example_3.jpg", 1024, 1024, "Bottom"],
477
  ],
478
+ inputs=[input_image_outpaint, width_slider, height_slider, alignment_dropdown],
479
  )
 
 
 
480
  with gr.Column():
481
+ result_outpaint = ImageSlider(
482
  interactive=False,
483
  label="Generated Image",
484
  )
485
+ use_as_input_button_outpaint = gr.Button("Use as Input Image", visible=False)
 
486
  history_gallery = gr.Gallery(label="History", columns=6, object_fit="contain", interactive=False)
487
  preview_image = gr.Image(label="Preview")
488
+ use_as_input_button_outpaint.click(
 
 
 
 
 
 
 
489
  fn=use_output_as_input,
490
+ inputs=[result_outpaint],
491
+ outputs=[input_image_outpaint]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
492
  )
493
+ runout_button.click(
 
 
 
 
 
 
 
 
494
  fn=clear_result,
495
  inputs=None,
496
+ outputs=result_outpaint,
497
+ ).then(
498
  fn=infer,
499
+ inputs=[input_image_outpaint, width_slider, height_slider, overlap_percentage, num_inference_steps,
500
  resize_option, custom_resize_percentage, prompt_input, alignment_dropdown,
501
  overlap_left, overlap_right, overlap_top, overlap_bottom],
502
+ outputs=[result_outpaint],
503
+ ).then(
504
  fn=lambda x, history: update_history(x[1], history),
505
+ inputs=[result_outpaint, history_gallery],
506
  outputs=history_gallery,
507
+ ).then(
508
  fn=lambda: gr.update(visible=True),
509
  inputs=None,
510
+ outputs=[use_as_input_button_outpaint],
511
  )
512
+ prompt_input.submit(
 
513
  fn=clear_result,
514
  inputs=None,
515
+ outputs=result_outpaint,
516
+ ).then(
517
  fn=infer,
518
+ inputs=[input_image_outpaint, width_slider, height_slider, overlap_percentage, num_inference_steps,
519
  resize_option, custom_resize_percentage, prompt_input, alignment_dropdown,
520
  overlap_left, overlap_right, overlap_top, overlap_bottom],
521
+ outputs=[result_outpaint],
522
+ ).then(
523
  fn=lambda x, history: update_history(x[1], history),
524
+ inputs=[result_outpaint, history_gallery],
525
  outputs=history_gallery,
526
+ ).then(
527
  fn=lambda: gr.update(visible=True),
528
  inputs=None,
529
+ outputs=[use_as_input_button_outpaint],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
530
  )
 
531
  preview_button.click(
532
  fn=preview_image_and_mask,
533
+ inputs=[input_image_outpaint, width_slider, height_slider, overlap_percentage, resize_option, custom_resize_percentage, alignment_dropdown,
534
  overlap_left, overlap_right, overlap_top, overlap_bottom],
535
+ outputs=[preview_image],
536
  queue=False
537
  )
538
 
 
 
 
 
 
 
539
  demo.launch(show_error=True)