linoyts HF Staff commited on
Commit
79a2d13
ยท
verified ยท
1 Parent(s): 37d085c
Files changed (1) hide show
  1. app.py +146 -8
app.py CHANGED
@@ -5,6 +5,7 @@ import torch
5
  import spaces
6
  import os
7
  import json
 
8
 
9
  from PIL import Image, ImageDraw
10
  import torch
@@ -277,6 +278,36 @@ optimize_pipeline_(pipe, image=Image.new("RGB", (1024, 1024)), prompt="prompt")
277
  # --- UI Constants and Helpers ---
278
  MAX_SEED = np.iinfo(np.int32).max
279
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
  def preload_presets(target_ratio, ui_width, ui_height):
281
  """Updates the width and height sliders based on the selected aspect ratio."""
282
  if target_ratio == "9:16":
@@ -380,12 +411,16 @@ def infer(
380
  return result_image, seed
381
 
382
  # --- Examples and UI Layout ---
383
- examples = []
 
 
 
 
384
 
385
  css = """
386
  #col-container {
387
  margin: 0 auto;
388
- max-width: 1024px;
389
  }
390
  #edit_text{margin-top: -62px !important}
391
  .preview-container {
@@ -394,6 +429,9 @@ css = """
394
  padding: 10px;
395
  margin-top: 10px;
396
  }
 
 
 
397
  """
398
 
399
  with gr.Blocks(css=css) as demo:
@@ -408,6 +446,11 @@ with gr.Blocks(css=css) as demo:
408
 
409
  Extend your images beyond their original boundaries with intelligent outpainting. The model will generate new content that seamlessly blends with your original image.
410
 
 
 
 
 
 
411
  [Learn more](https://github.com/QwenLM/Qwen-Image) about the Qwen-Image series.
412
  Try on [Qwen Chat](https://chat.qwen.ai/), or [download model](https://huggingface.co/Qwen/Qwen-Image-Edit) to run locally.
413
  """)
@@ -435,9 +478,9 @@ with gr.Blocks(css=css) as demo:
435
  label="Alignment"
436
  )
437
 
438
- run_button = gr.Button("Outpaint!", variant="primary")
439
 
440
- with gr.Accordion("Outpainting Settings", open=False) as settings_panel:
441
  with gr.Row():
442
  width_slider = gr.Slider(
443
  label="Target Width",
@@ -487,9 +530,9 @@ with gr.Blocks(css=css) as demo:
487
  visible=False
488
  )
489
 
490
- preview_button = gr.Button("Preview alignment and mask", variant="secondary")
491
 
492
- with gr.Accordion("Advanced Settings", open=False):
493
  seed = gr.Slider(
494
  label="Seed",
495
  minimum=0,
@@ -525,10 +568,50 @@ with gr.Blocks(css=css) as demo:
525
  with gr.Column():
526
  result = gr.Image(label="Result", type="pil")
527
 
 
 
528
  with gr.Column(visible=False) as preview_container:
529
  preview_image = gr.Image(label="Preview (red area will be generated)", type="pil")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
530
 
531
  # Event handlers
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
532
  target_ratio.change(
533
  fn=preload_presets,
534
  inputs=[target_ratio, width_slider, height_slider],
@@ -573,8 +656,13 @@ with gr.Blocks(css=css) as demo:
573
  queue=False,
574
  )
575
 
576
- gr.on(
577
- triggers=[run_button.click, prompt.submit],
 
 
 
 
 
578
  fn=infer,
579
  inputs=[
580
  input_image,
@@ -596,6 +684,56 @@ with gr.Blocks(css=css) as demo:
596
  rewrite_prompt,
597
  ],
598
  outputs=[result, seed],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
599
  )
600
 
601
  if __name__ == "__main__":
 
5
  import spaces
6
  import os
7
  import json
8
+ import time
9
 
10
  from PIL import Image, ImageDraw
11
  import torch
 
278
  # --- UI Constants and Helpers ---
279
  MAX_SEED = np.iinfo(np.int32).max
280
 
281
+ def clear_result():
282
+ """Clears the result image."""
283
+ return gr.update(value=None)
284
+
285
+ def update_history(new_image, history):
286
+ """Updates the history gallery with the new image."""
287
+ time.sleep(0.5) # Small delay to ensure image is ready
288
+ if history is None:
289
+ history = []
290
+ if new_image is not None:
291
+ # Convert to list if needed (Gradio sometimes returns tuples)
292
+ if not isinstance(history, list):
293
+ history = list(history) if history else []
294
+ history.insert(0, new_image)
295
+ # Keep only the last 20 images in history
296
+ history = history[:20]
297
+ return history
298
+
299
+ def use_history_as_input(evt: gr.SelectData, history):
300
+ """Sets the selected history image as the new input image."""
301
+ if history and evt.index < len(history):
302
+ return gr.update(value=history[evt.index])
303
+ return gr.update()
304
+
305
+ def use_output_as_input(output_image):
306
+ """Sets the generated output as the new input image."""
307
+ if output_image is not None:
308
+ return gr.update(value=output_image)
309
+ return gr.update()
310
+
311
  def preload_presets(target_ratio, ui_width, ui_height):
312
  """Updates the width and height sliders based on the selected aspect ratio."""
313
  if target_ratio == "9:16":
 
411
  return result_image, seed
412
 
413
  # --- Examples and UI Layout ---
414
+ # You can add examples here if you have sample images
415
+ # examples = [
416
+ # ["path/to/example1.jpg", "extend the landscape", 1280, 720, "Middle"],
417
+ # ["path/to/example2.jpg", "add more sky", 1024, 1024, "Top"],
418
+ # ]
419
 
420
  css = """
421
  #col-container {
422
  margin: 0 auto;
423
+ max-width: 1200px;
424
  }
425
  #edit_text{margin-top: -62px !important}
426
  .preview-container {
 
429
  padding: 10px;
430
  margin-top: 10px;
431
  }
432
+ .gallery-container {
433
+ margin-top: 20px;
434
+ }
435
  """
436
 
437
  with gr.Blocks(css=css) as demo:
 
446
 
447
  Extend your images beyond their original boundaries with intelligent outpainting. The model will generate new content that seamlessly blends with your original image.
448
 
449
+ **Tips:**
450
+ - Use the preview button to see which areas will be generated before running
451
+ - Click on any image in the history to use it as a new input
452
+ - Try different alignments to expand your image in specific directions
453
+
454
  [Learn more](https://github.com/QwenLM/Qwen-Image) about the Qwen-Image series.
455
  Try on [Qwen Chat](https://chat.qwen.ai/), or [download model](https://huggingface.co/Qwen/Qwen-Image-Edit) to run locally.
456
  """)
 
478
  label="Alignment"
479
  )
480
 
481
+ run_button = gr.Button("๐ŸŽจ Outpaint!", variant="primary")
482
 
483
+ with gr.Accordion("โš™๏ธ Outpainting Settings", open=False) as settings_panel:
484
  with gr.Row():
485
  width_slider = gr.Slider(
486
  label="Target Width",
 
530
  visible=False
531
  )
532
 
533
+ preview_button = gr.Button("๐Ÿ‘๏ธ Preview alignment and mask", variant="secondary")
534
 
535
+ with gr.Accordion("๐Ÿ”ง Advanced Settings", open=False):
536
  seed = gr.Slider(
537
  label="Seed",
538
  minimum=0,
 
568
  with gr.Column():
569
  result = gr.Image(label="Result", type="pil")
570
 
571
+ use_as_input_button = gr.Button("๐Ÿ”„ Use as Input Image", visible=False, variant="secondary")
572
+
573
  with gr.Column(visible=False) as preview_container:
574
  preview_image = gr.Image(label="Preview (red area will be generated)", type="pil")
575
+
576
+ gr.Markdown("---")
577
+
578
+ with gr.Row():
579
+ gr.Markdown("### ๐Ÿ“œ History")
580
+ clear_history_button = gr.Button("๐Ÿ—‘๏ธ Clear History", size="sm", variant="stop")
581
+
582
+ history_gallery = gr.Gallery(
583
+ label="Click any image to use as input",
584
+ columns=4,
585
+ rows=2,
586
+ object_fit="contain",
587
+ height="auto",
588
+ interactive=True,
589
+ show_label=True,
590
+ elem_classes=["gallery-container"]
591
+ )
592
 
593
  # Event handlers
594
+ use_as_input_button.click(
595
+ fn=use_output_as_input,
596
+ inputs=[result],
597
+ outputs=[input_image],
598
+ show_api=False
599
+ )
600
+
601
+ history_gallery.select(
602
+ fn=use_history_as_input,
603
+ inputs=[history_gallery],
604
+ outputs=[input_image],
605
+ show_api=False
606
+ )
607
+
608
+ clear_history_button.click(
609
+ fn=lambda: [],
610
+ inputs=None,
611
+ outputs=history_gallery,
612
+ show_api=False
613
+ )
614
+
615
  target_ratio.change(
616
  fn=preload_presets,
617
  inputs=[target_ratio, width_slider, height_slider],
 
656
  queue=False,
657
  )
658
 
659
+ # Main generation pipeline with result clearing, history update, and button visibility
660
+ run_button.click(
661
+ fn=clear_result,
662
+ inputs=None,
663
+ outputs=result,
664
+ show_api=False
665
+ ).then(
666
  fn=infer,
667
  inputs=[
668
  input_image,
 
684
  rewrite_prompt,
685
  ],
686
  outputs=[result, seed],
687
+ ).then(
688
+ fn=lambda: gr.update(visible=True),
689
+ inputs=None,
690
+ outputs=use_as_input_button,
691
+ show_api=False
692
+ ).then(
693
+ fn=update_history,
694
+ inputs=[result, history_gallery],
695
+ outputs=history_gallery,
696
+ show_api=False
697
+ )
698
+
699
+ # Also trigger on prompt submit
700
+ prompt.submit(
701
+ fn=clear_result,
702
+ inputs=None,
703
+ outputs=result,
704
+ show_api=False
705
+ ).then(
706
+ fn=infer,
707
+ inputs=[
708
+ input_image,
709
+ prompt,
710
+ width_slider,
711
+ height_slider,
712
+ overlap_percentage,
713
+ resize_option,
714
+ custom_resize_percentage,
715
+ alignment_dropdown,
716
+ overlap_left,
717
+ overlap_right,
718
+ overlap_top,
719
+ overlap_bottom,
720
+ seed,
721
+ randomize_seed,
722
+ true_guidance_scale,
723
+ num_inference_steps,
724
+ rewrite_prompt,
725
+ ],
726
+ outputs=[result, seed],
727
+ ).then(
728
+ fn=lambda: gr.update(visible=True),
729
+ inputs=None,
730
+ outputs=use_as_input_button,
731
+ show_api=False
732
+ ).then(
733
+ fn=update_history,
734
+ inputs=[result, history_gallery],
735
+ outputs=history_gallery,
736
+ show_api=False
737
  )
738
 
739
  if __name__ == "__main__":