Spaces:
Running on Zero
Running on Zero
feat(app): add auto-canvas selection and update conditioner space
Browse files- Update `CONDITIONER_SPACE` default to `mpasila/qwen3vl-conditioner`.
- Add 3:2 and 2:3 aspect ratio options to `CANVASES`.
- Implement `auto_canvas` logic to automatically select the closest canvas aspect ratio based on uploaded image or video dimensions.
- Bind `auto_canvas` to image and video component change events in the Gradio UI.
app.py
CHANGED
|
@@ -20,7 +20,7 @@ import spaces
|
|
| 20 |
import gradio as gr
|
| 21 |
|
| 22 |
MODEL_REPO = os.environ.get("H3_MODEL_REPO", "MiniMaxAI/MiniMax-H3")
|
| 23 |
-
CONDITIONER_SPACE = os.environ.get("H3_CONDITIONER", "
|
| 24 |
# `lazy` moves all 72.16 GiB onto the card on the first GPU call and leaves it there; `offload` hands placement to
|
| 25 |
# `ComponentsManager.enable_auto_cpu_offload`. Startup placement is not an option here — see `load_models`.
|
| 26 |
PLACEMENT = os.environ.get("H3_PLACEMENT", "lazy").lower()
|
|
@@ -54,6 +54,11 @@ CANVASES = {
|
|
| 54 |
"1024x768 · 4:3 full": (768, 1024),
|
| 55 |
"576x768 · 3:4 fast": (768, 576),
|
| 56 |
"768x1024 · 3:4 full": (1024, 768),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
# 21:9
|
| 58 |
"1152x512 · 21:9 fast": (512, 1152),
|
| 59 |
"1536x672 · 21:9 full": (672, 1536),
|
|
@@ -498,6 +503,43 @@ def probe(path: str) -> tuple[float | None, float | None]:
|
|
| 498 |
return video, audio
|
| 499 |
|
| 500 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 501 |
def collect(image_paths, audio_path, video_path) -> list[tuple[str, str]]:
|
| 502 |
"""The `(kind, path)` references of a request, **in the order the model reads them**.
|
| 503 |
|
|
@@ -947,6 +989,11 @@ with gr.Blocks(title="MiniMax-H3 Reference Custom Lora") as demo:
|
|
| 947 |
duration_controls, [audio, video, match], [match, duration], show_progress="hidden", api_name=False
|
| 948 |
)
|
| 949 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 950 |
# `reference, strength, reference, strength, ...`, which is how `generate` unpacks them.
|
| 951 |
lora_inputs = [field for pair in zip(lora_references, lora_scales) for field in pair]
|
| 952 |
lora_upload.upload(_fill_lora_slots, [lora_upload, *lora_references], lora_references, api_name=False)
|
|
|
|
| 20 |
import gradio as gr
|
| 21 |
|
| 22 |
MODEL_REPO = os.environ.get("H3_MODEL_REPO", "MiniMaxAI/MiniMax-H3")
|
| 23 |
+
CONDITIONER_SPACE = os.environ.get("H3_CONDITIONER", "mpasila/qwen3vl-conditioner")
|
| 24 |
# `lazy` moves all 72.16 GiB onto the card on the first GPU call and leaves it there; `offload` hands placement to
|
| 25 |
# `ComponentsManager.enable_auto_cpu_offload`. Startup placement is not an option here — see `load_models`.
|
| 26 |
PLACEMENT = os.environ.get("H3_PLACEMENT", "lazy").lower()
|
|
|
|
| 54 |
"1024x768 · 4:3 full": (768, 1024),
|
| 55 |
"576x768 · 3:4 fast": (768, 576),
|
| 56 |
"768x1024 · 3:4 full": (1024, 768),
|
| 57 |
+
# 3:2 / 2:3
|
| 58 |
+
"864x576 · 3:2 fast": (576, 864),
|
| 59 |
+
"1152x768 · 3:2 full": (768, 1152),
|
| 60 |
+
"576x864 · 2:3 fast": (864, 576),
|
| 61 |
+
"768x1152 · 2:3 full": (1152, 768),
|
| 62 |
# 21:9
|
| 63 |
"1152x512 · 21:9 fast": (512, 1152),
|
| 64 |
"1536x672 · 21:9 full": (672, 1536),
|
|
|
|
| 503 |
return video, audio
|
| 504 |
|
| 505 |
|
| 506 |
+
def _media_dimensions(path: str) -> tuple[int, int]:
|
| 507 |
+
"""`(width, height)` of an image or a video file, from its first stream."""
|
| 508 |
+
from PIL import Image
|
| 509 |
+
|
| 510 |
+
try:
|
| 511 |
+
with Image.open(path) as image:
|
| 512 |
+
return image.size
|
| 513 |
+
except Exception:
|
| 514 |
+
pass
|
| 515 |
+
import av
|
| 516 |
+
|
| 517 |
+
with av.open(path) as container:
|
| 518 |
+
stream = container.streams.video[0]
|
| 519 |
+
return stream.width, stream.height
|
| 520 |
+
|
| 521 |
+
|
| 522 |
+
def closest_canvas(path: str | None) -> str | None:
|
| 523 |
+
"""The canvas label whose aspect ratio is closest to a media file's, or `None` when the file is
|
| 524 |
+
missing or unreadable."""
|
| 525 |
+
if not path:
|
| 526 |
+
return None
|
| 527 |
+
try:
|
| 528 |
+
width, height = _media_dimensions(path)
|
| 529 |
+
except Exception:
|
| 530 |
+
return None
|
| 531 |
+
if not width or not height:
|
| 532 |
+
return None
|
| 533 |
+
target = width / height
|
| 534 |
+
return min(CANVASES, key=lambda label: abs(CANVASES[label][1] / CANVASES[label][0] - target))
|
| 535 |
+
|
| 536 |
+
|
| 537 |
+
def auto_canvas(path):
|
| 538 |
+
"""Set the canvas to the closest aspect ratio of an uploaded image or video."""
|
| 539 |
+
label = closest_canvas(path)
|
| 540 |
+
return gr.update(value=label) if label else gr.update()
|
| 541 |
+
|
| 542 |
+
|
| 543 |
def collect(image_paths, audio_path, video_path) -> list[tuple[str, str]]:
|
| 544 |
"""The `(kind, path)` references of a request, **in the order the model reads them**.
|
| 545 |
|
|
|
|
| 989 |
duration_controls, [audio, video, match], [match, duration], show_progress="hidden", api_name=False
|
| 990 |
)
|
| 991 |
|
| 992 |
+
# Auto-select the canvas whose aspect ratio is closest to an uploaded image or video.
|
| 993 |
+
for image in images:
|
| 994 |
+
image.change(auto_canvas, image, canvas, show_progress="hidden", api_name=False)
|
| 995 |
+
video.change(auto_canvas, video, canvas, show_progress="hidden", api_name=False)
|
| 996 |
+
|
| 997 |
# `reference, strength, reference, strength, ...`, which is how `generate` unpacks them.
|
| 998 |
lora_inputs = [field for pair in zip(lora_references, lora_scales) for field in pair]
|
| 999 |
lora_upload.upload(_fill_lora_slots, [lora_upload, *lora_references], lora_references, api_name=False)
|