File size: 5,167 Bytes
8540d8b
 
 
 
 
 
 
 
 
547e122
8540d8b
 
56ecc4f
8540d8b
547e122
 
 
 
 
 
 
 
 
 
 
8540d8b
547e122
 
 
 
 
 
8540d8b
 
 
 
 
 
 
 
 
 
 
 
56ecc4f
8540d8b
 
 
 
 
 
547e122
 
8540d8b
 
547e122
8540d8b
 
 
 
 
547e122
8540d8b
547e122
 
8540d8b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56ecc4f
8540d8b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56ecc4f
 
 
 
 
 
 
8540d8b
 
 
 
 
 
56ecc4f
8540d8b
 
 
6e23648
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
147
148
149
150
151
152
153
154
155
156
157
158
159
import random

import gradio as gr
import numpy as np
import spaces
import torch

from diffusers import ModularPipeline
from diffusers.modular_pipelines import SequentialPipelineBlocks
from diffusers.modular_pipelines.flux2.decoders import Flux2UnpackLatentsStep


repo_id = "black-forest-labs/FLUX.2-klein-4B"

# Take the pipeline apart into stages: each stage only loads the components it needs.
blocks = ModularPipeline.from_pretrained(repo_id).blocks
text_encoder_block = blocks.sub_blocks.pop("text_encoder")
decode_block = blocks.sub_blocks.pop("decode")
blocks.sub_blocks.pop("vae_encoder")  # image-conditioning branch, unused in this text-to-image demo

text_encoder_pipe = text_encoder_block.init_pipeline(repo_id)  # text encoder + tokenizer
pipe = blocks.init_pipeline(repo_id)  # transformer + scheduler

# The preview decoder unpacks the in-loop (packed) latents, then runs the pipeline's own decode
# block — the same block popped from the pipeline above.
preview = SequentialPipelineBlocks.from_blocks_dict(
    {"unpack": Flux2UnpackLatentsStep(), "decode": decode_block}
).init_pipeline(repo_id)  # vae + image processor

for stage in (text_encoder_pipe, pipe, preview):
    stage.load_components(dtype=torch.bfloat16)
    stage.to("cuda")

MAX_SEED = np.iinfo(np.int32).max
MAX_IMAGE_SIZE = 2048


@spaces.GPU(duration=120)
def infer(
    prompt,
    seed=42,
    randomize_seed=False,
    width=1024,
    height=1024,
    num_inference_steps=4,
    progress=gr.Progress(track_tqdm=True),
):
    if randomize_seed:
        seed = random.randint(0, MAX_SEED)
    generator = torch.Generator().manual_seed(seed)

    text_embeddings = text_encoder_pipe(prompt=prompt).get_by_kwargs("denoiser_input_fields")

    # `pipe.stream()` yields an event with the live pipeline state after every denoising step
    stream = pipe.stream(
        **text_embeddings,
        num_inference_steps=num_inference_steps,
        width=width,
        height=height,
        generator=generator,
    )
    for event in stream:
        # flow matching: after step i the latents sit at sigmas[i + 1]; project to the predicted
        # clean image x0 = x_t - sigma * v so the preview shows the image forming, not noise.
        # At the last step sigma is 0, so the last preview is exactly the final image.
        latents = event.state.get("latents")
        sigma = pipe.scheduler.sigmas[event.loop_kwargs["i"] + 1].to(latents.device, latents.dtype)
        x0 = latents - sigma * event.state.get("noise_pred")
        image = preview(
            latents=x0,
            latent_ids=event.state.get("latent_ids"),
            output="images",
        )[0]
        yield image, seed


examples = [
    "a tiny astronaut hatching from an egg on the moon",
    "a cat holding a sign that says hello world",
    "an anime illustration of a wiener schnitzel",
]

css = """
#col-container {
    margin: 0 auto;
    max-width: 520px;
}
"""

with gr.Blocks() as demo:
    with gr.Column(elem_id="col-container"):
        gr.Markdown(
            """# FLUX.2 [klein] — Live Preview with Modular Diffusers
Live latent preview powered by `pipe.stream()`: the pipeline yields its live state after every
denoising step, and a preview pipeline built from flux2's own unpack + decode blocks renders it.
No custom blocks, queues, or threads — see [huggingface/diffusers#14159](https://github.com/huggingface/diffusers/pull/14159).
"""
        )

        with gr.Row():
            prompt = gr.Text(
                label="Prompt",
                show_label=False,
                max_lines=1,
                placeholder="Enter your prompt",
                container=False,
            )

            run_button = gr.Button("Run", scale=0)

        result = gr.Image(label="Result", show_label=False)

        with gr.Accordion("Advanced Settings", open=False):
            seed = gr.Slider(
                label="Seed",
                minimum=0,
                maximum=MAX_SEED,
                step=1,
                value=0,
            )

            randomize_seed = gr.Checkbox(label="Randomize seed", value=True)

            with gr.Row():
                width = gr.Slider(
                    label="Width",
                    minimum=256,
                    maximum=MAX_IMAGE_SIZE,
                    step=32,
                    value=1024,
                )

                height = gr.Slider(
                    label="Height",
                    minimum=256,
                    maximum=MAX_IMAGE_SIZE,
                    step=32,
                    value=1024,
                )

            num_inference_steps = gr.Slider(
                label="Number of inference steps",
                minimum=1,
                maximum=16,
                step=1,
                value=4,
            )

        gr.Examples(examples=examples, fn=infer, inputs=[prompt], outputs=[result, seed], cache_examples=False)

    gr.on(
        triggers=[run_button.click, prompt.submit],
        fn=infer,
        inputs=[prompt, seed, randomize_seed, width, height, num_inference_steps],
        outputs=[result, seed],
    )

demo.launch(css=css, show_error=True)