Spaces:
Running
on
Zero
Running
on
Zero
Update app.py (#2)
Browse files- Update app.py (cb6a3673a5f67465153a5b5fd72087d8fef4e32c)
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:
|
| 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 |
-
|
| 577 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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__":
|