File size: 5,639 Bytes
d58fd10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ca5f734
 
 
 
 
d58fd10
 
14aedbe
 
 
d58fd10
14aedbe
 
d58fd10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14aedbe
d58fd10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14aedbe
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import os

os.environ.setdefault("VF_HF_ATTN_IMPL", "sdpa")

import spaces
import torch
import gradio as gr
import random

from mage_flow import MageFlowPipeline
from mage_flow.models.modules._attn_backend import set_attn_backend

MODEL_ID = "microsoft/Mage-Flow-Base"

pipe = MageFlowPipeline.from_pretrained(MODEL_ID, device="cuda")

# Override the attention backend to SDPA after model load — the model's
# __init__ resets it to "flash2" (the default attn_type), but flash_attn
# is not available on ZeroGPU. SDPA is torch-native and works everywhere.
set_attn_backend("sdpa")


def _estimate_duration(prompt, negative_prompt, height, width, steps, cfg, seed, *args, **kwargs):
    """Estimate GPU time needed based on steps and resolution.
    Measured: ~15s for 30 steps at 1024x1024 with SDPA backend + content screening.
    """
    pixels = height * width
    base = 20  # content screening + text encoding overhead
    step_time = 0.5 if pixels <= 1024 * 1024 else 1.0
    return min(300, base + int(steps * step_time))


@spaces.GPU(duration=_estimate_duration)
def generate(
    prompt: str,
    negative_prompt: str = "",
    height: int = 1024,
    width: int = 1024,
    steps: int = 30,
    cfg: float = 5.0,
    seed: int = 42,
    randomize_seed: bool = False,
):
    """Generate an image from a text prompt using Mage-Flow-Base.

    Args:
        prompt: Text description of the image to generate.
        negative_prompt: What to avoid in the generated image.
        height: Output image height (512-2048, multiple of 16).
        width: Output image width (512-2048, multiple of 16).
        steps: Number of denoising steps (more = higher quality, slower).
        cfg: Classifier-free guidance scale (higher = more prompt adherence).
        seed: Random seed for reproducibility.
        randomize_seed: If True, use a random seed each time.
    """
    if randomize_seed:
        seed = random.randint(0, 2**32 - 1)
    seed = int(seed)
    height = max(512, min(2048, int(height) // 16 * 16))
    width = max(512, min(2048, int(width) // 16 * 16))

    neg = negative_prompt.strip() if negative_prompt else " "

    result = pipe.generate(
        [prompt],
        neg_prompts=[neg],
        seeds=[seed],
        steps=int(steps),
        cfg=float(cfg),
        heights=[height],
        widths=[width],
    )
    return result[0], seed


CSS = """
#col-container { max-width: 1100px; margin: 0 auto; }
.dark .gradio-container { color: var(--body-text-color); }
"""

with gr.Blocks() as demo:
    gr.Markdown("# 🎨 Mage-Flow-Base Text-to-Image")
    gr.Markdown(
        "Generate images with [microsoft/Mage-Flow-Base](https://huggingface.co/microsoft/Mage-Flow-Base), "
        "a 4.1B parameter native-resolution MMDiT model. Supports any resolution from 512 to 2048."
    )

    with gr.Column(elem_id="col-container"):
        with gr.Row():
            prompt = gr.Textbox(
                label="Prompt",
                placeholder="Describe the image you want to generate...",
                show_label=False,
                container=False,
                scale=4,
            )
            run_btn = gr.Button("Generate", variant="primary", scale=1)

        output = gr.Image(label="Generated Image", show_label=False, height=512)

        with gr.Accordion("Advanced settings", open=False):
            negative_prompt = gr.Textbox(
                label="Negative prompt",
                placeholder="What to avoid...",
                value="",
            )
            with gr.Row():
                height = gr.Slider(
                    label="Height", minimum=512, maximum=2048, step=16, value=1024
                )
                width = gr.Slider(
                    label="Width", minimum=512, maximum=2048, step=16, value=1024
                )
            with gr.Row():
                steps = gr.Slider(
                    label="Steps", minimum=1, maximum=100, step=1, value=30
                )
                cfg = gr.Slider(
                    label="CFG scale", minimum=1.0, maximum=20.0, step=0.1, value=5.0
                )
            with gr.Row():
                seed = gr.Number(label="Seed", value=42, precision=0)
                randomize_seed = gr.Checkbox(label="Randomize seed", value=True)

        gr.Examples(
            examples=[
                ["A close-up portrait of an elderly African man with deep wrinkles, wearing a traditional hat, soft natural lighting, ultra realistic."],
                ["An immersive close-up of a steaming bowl of Sichuan mapo tofu over jasmine rice served on a hand-thrown ceramic plate, finished with a wedge of citrus. Surface oils catch a tiny specular highlight. Shot with a Hasselblad H6D-100c, ambient window light, the kind of image that makes the viewer hungry."],
                ["the Salar de Uyuni mirror surface captured at high noon, with intimate stillness permeating the air. dew beads on every blade of grass. National Geographic editorial, cinematic depth, fine-grained natural texture."],
                ["A serene Japanese garden with a red wooden bridge over a koi pond, cherry blossoms floating on still water, morning mist, photorealistic."],
            ],
            inputs=[prompt],
            outputs=[output, seed],
            fn=generate,
            cache_examples=True,
            cache_mode="lazy",
        )

    run_btn.click(
        fn=generate,
        inputs=[prompt, negative_prompt, height, width, steps, cfg, seed, randomize_seed],
        outputs=[output, seed],
        api_name="generate",
    )

demo.launch(mcp_server=True, theme=gr.themes.Citrus(), css=CSS)