mrboost commited on
Commit
d793324
路
verified 路
1 Parent(s): 0ad4e7c

Deploy flux-image-studio on ZeroGPU

Browse files
Files changed (3) hide show
  1. README.md +21 -8
  2. app.py +219 -0
  3. requirements.txt +10 -0
README.md CHANGED
@@ -1,13 +1,26 @@
1
  ---
2
- title: Flux Image Studio
3
- emoji: 馃悹
4
- colorFrom: blue
5
- colorTo: yellow
6
  sdk: gradio
7
- sdk_version: 6.31.0
8
- python_version: '3.12'
9
  app_file: app.py
10
- pinned: false
 
 
 
 
 
 
 
 
 
 
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
1
  ---
2
+ title: FLUX.1 & Image Edit Studio
3
+ emoji: 馃帹
4
+ colorFrom: pink
5
+ colorTo: purple
6
  sdk: gradio
7
+ sdk_version: 6.13.0
 
8
  app_file: app.py
9
+ python_version: "3.12"
10
+ startup_duration_timeout: 1h
11
+ short_description: Photorealistic 1080p Image Generation & Image Editing Studio
12
+ models:
13
+ - black-forest-labs/FLUX.1-schnell
14
+ - stabilityai/sdxl-turbo
15
+ tags:
16
+ - flux
17
+ - text-to-image
18
+ - image-to-image
19
+ - image-editing
20
+ - zerogpu
21
+ - mcp-server
22
  ---
23
 
24
+ # 馃帹 FLUX.1 & Image Edit ZeroGPU Studio
25
+
26
+ Generate photorealistic **1080p / Cinema starting frames**, logos, UI mockups, and perform **Image-to-Image / Photo Editing** on **ZeroGPU (`zero-a10g`)** at **$0/hour**. Designed to pair directly with your **LTX-2.5** and **SkyReels-V2** video studios!
app.py ADDED
@@ -0,0 +1,219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FLUX.1 & Image-to-Image Edit Studio on ZeroGPU."""
2
+
3
+ import os
4
+ import random
5
+ import shutil
6
+ import tempfile
7
+
8
+ os.environ.setdefault("PYTORCH_ALLOC_CONF", "backend:cudaMallocAsync")
9
+ os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "backend:cudaMallocAsync")
10
+
11
+ import spaces # Must be imported before torch
12
+ import gradio as gr
13
+ import PIL.Image
14
+ import torch
15
+ from diffusers import AutoPipelineForImage2Image, AutoPipelineForText2Image
16
+ from gradio_client import Client
17
+
18
+ HF_TOKEN = os.environ.get("HF_TOKEN")
19
+ LOCAL_MODEL_ID = "stabilityai/sdxl-turbo"
20
+ MAX_SEED = 2**31 - 1
21
+
22
+ ASPECT_PRESETS = {
23
+ "Cinema 16:9 (1472 脳 832 路 LTX-2.5 Ready)": (1472, 832),
24
+ "720P 16:9 (1280 脳 720 路 SkyReels-V2 Ready)": (1280, 720),
25
+ "Full HD 16:9 (1920 脳 1088)": (1920, 1088),
26
+ "Portrait 9:16 (832 脳 1472 路 Reels/Shorts)": (832, 1472),
27
+ "Square 1:1 (1024 脳 1024 路 Logo/Product)": (1024, 1024),
28
+ }
29
+
30
+ print(f"[flux-image-studio] Loading {LOCAL_MODEL_ID} pipelines at module scope for ZeroGPU...", flush=True)
31
+ pipe_t2i = AutoPipelineForText2Image.from_pretrained(
32
+ LOCAL_MODEL_ID,
33
+ torch_dtype=torch.float16,
34
+ variant="fp16",
35
+ )
36
+ pipe_t2i.to("cuda")
37
+ pipe_i2i = AutoPipelineForImage2Image.from_pipe(pipe_t2i)
38
+ print("[flux-image-studio] Local ZeroGPU T2I + I2I pipelines ready!", flush=True)
39
+
40
+
41
+ @spaces.GPU(duration=45)
42
+ def generate_or_edit_local(
43
+ prompt: str,
44
+ init_image_path: str | None,
45
+ width: int,
46
+ height: int,
47
+ steps: int,
48
+ strength: float,
49
+ seed: int,
50
+ ) -> tuple[str, int, str]:
51
+ """Run local ZeroGPU Text-to-Image or Image-to-Image editing."""
52
+ generator = torch.Generator("cuda").manual_seed(int(seed))
53
+ w = max(512, min(1536, (int(width) // 64) * 64))
54
+ h = max(512, min(1536, (int(height) // 64) * 64))
55
+ num_steps = max(2, min(8, int(steps)))
56
+
57
+ if init_image_path:
58
+ init_img = PIL.Image.open(init_image_path).convert("RGB").resize((w, h), PIL.Image.Resampling.LANCZOS)
59
+ eff_steps = max(2, int(num_steps / max(0.2, float(strength))))
60
+ image = pipe_i2i(
61
+ prompt=prompt.strip(),
62
+ image=init_img,
63
+ num_inference_steps=eff_steps,
64
+ strength=float(strength),
65
+ guidance_scale=0.0,
66
+ generator=generator,
67
+ ).images[0]
68
+ mode = f"Local ZeroGPU Image Edit (strength={strength:.2f})"
69
+ else:
70
+ image = pipe_t2i(
71
+ prompt=prompt.strip(),
72
+ width=w,
73
+ height=h,
74
+ num_inference_steps=num_steps,
75
+ guidance_scale=0.0,
76
+ generator=generator,
77
+ ).images[0]
78
+ mode = "Local ZeroGPU SDXL-Turbo"
79
+
80
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
81
+ image.save(tmp.name, format="PNG")
82
+ return tmp.name, int(seed), f"{w}脳{h} 路 {mode}"
83
+
84
+
85
+ def generate_image(
86
+ prompt: str,
87
+ init_image_path: str | None = None,
88
+ engine: str = "FLUX.1-schnell (12B Photorealistic 路 ZeroGPU Cluster)",
89
+ aspect_preset: str = "Cinema 16:9 (1472 脳 832 路 LTX-2.5 Ready)",
90
+ width: int = 1472,
91
+ height: int = 832,
92
+ steps: int = 4,
93
+ strength: float = 0.65,
94
+ seed: int = 42,
95
+ randomize_seed: bool = True,
96
+ ) -> tuple[str, int, str]:
97
+ """Generate a photorealistic image or edit an existing uploaded image."""
98
+ del aspect_preset
99
+ if not prompt or not prompt.strip():
100
+ raise gr.Error("Please enter a prompt.")
101
+ if randomize_seed:
102
+ seed = random.randint(0, MAX_SEED)
103
+ seed = int(seed)
104
+
105
+ if not init_image_path and "FLUX.1" in str(engine):
106
+ try:
107
+ client = Client("black-forest-labs/FLUX.1-schnell", token=HF_TOKEN, httpx_kwargs={"timeout": 180.0})
108
+ w = max(256, min(2048, (int(width) // 32) * 32))
109
+ h = max(256, min(2048, (int(height) // 32) * 32))
110
+ res = client.predict(
111
+ prompt=prompt.strip(),
112
+ seed=seed,
113
+ randomize_seed=False,
114
+ width=w,
115
+ height=h,
116
+ num_inference_steps=max(1, min(16, int(steps))),
117
+ api_name="/infer",
118
+ )
119
+ img_path = res[0] if isinstance(res, (list, tuple)) else res
120
+ used_s = int(res[1]) if isinstance(res, (list, tuple)) and len(res) > 1 else seed
121
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
122
+ shutil.copyfile(img_path, tmp.name)
123
+ return tmp.name, used_s, f"{w}脳{h} 路 FLUX.1-schnell (12B ZeroGPU)"
124
+ except Exception as err: # noqa: BLE001
125
+ print(f"[flux-image-studio] FLUX relay fallback to local ZeroGPU ({err!r})", flush=True)
126
+
127
+ return generate_or_edit_local(
128
+ prompt=prompt,
129
+ init_image_path=init_image_path,
130
+ width=width,
131
+ height=height,
132
+ steps=steps,
133
+ strength=strength,
134
+ seed=seed,
135
+ )
136
+
137
+
138
+ def apply_aspect(preset: str):
139
+ dims = ASPECT_PRESETS.get(preset)
140
+ if not dims:
141
+ return gr.update(), gr.update()
142
+ return dims[0], dims[1]
143
+
144
+
145
+ with gr.Blocks(title="FLUX.1 & Image Edit ZeroGPU Studio", fill_width=True) as demo:
146
+ gr.Markdown(
147
+ "# 馃帹 FLUX.1 & Image Edit ZeroGPU Studio\n"
148
+ "Generate **1080p photorealistic starting frames** for your **LTX-2.5** and **SkyReels-V2** video studios, "
149
+ "design logos/UI mockups, or upload an image to edit on **ZeroGPU (`zero-a10g`)** at **$0/hour**."
150
+ )
151
+ with gr.Row():
152
+ with gr.Column(scale=1):
153
+ prompt_input = gr.Textbox(
154
+ label="Image Prompt / Edit Instruction",
155
+ lines=4,
156
+ placeholder="Describe the scene, lighting, camera lens, logo, or image edit...",
157
+ )
158
+ init_image_input = gr.Image(
159
+ label="Upload Image to Edit / Restyle (Optional 路 Image-to-Image)",
160
+ type="filepath",
161
+ )
162
+ with gr.Accordion("Studio Settings", open=True):
163
+ engine_choice = gr.Dropdown(
164
+ choices=[
165
+ "FLUX.1-schnell (12B Photorealistic 路 ZeroGPU Cluster)",
166
+ "SDXL-Turbo (Local ZeroGPU 路 Instant T2I + Image Editing)",
167
+ ],
168
+ value="FLUX.1-schnell (12B Photorealistic 路 ZeroGPU Cluster)",
169
+ label="Generation Engine",
170
+ )
171
+ aspect_dropdown = gr.Dropdown(
172
+ choices=list(ASPECT_PRESETS),
173
+ value="Cinema 16:9 (1472 脳 832 路 LTX-2.5 Ready)",
174
+ label="Aspect Ratio Preset (Video-Ready)",
175
+ )
176
+ with gr.Row():
177
+ width_slider = gr.Slider(512, 1920, value=1472, step=64, label="Width (px)")
178
+ height_slider = gr.Slider(512, 1920, value=832, step=64, label="Height (px)")
179
+ with gr.Row():
180
+ steps_slider = gr.Slider(1, 12, value=4, step=1, label="Inference Steps")
181
+ strength_slider = gr.Slider(
182
+ 0.2, 0.95, value=0.65, step=0.05, label="Edit Strength (for Image-to-Image)"
183
+ )
184
+ with gr.Row():
185
+ seed_slider = gr.Slider(0, MAX_SEED, value=42, step=1, label="Seed")
186
+ rand_check = gr.Checkbox(value=True, label="Randomize seed")
187
+ gen_btn = gr.Button("馃帹 Generate / Edit Image", variant="primary")
188
+ with gr.Column(scale=1):
189
+ out_img = gr.Image(label="Generated Image (Ready for Video Studio)", type="filepath")
190
+ with gr.Row():
191
+ out_seed = gr.Number(label="Seed Used", precision=0, interactive=False)
192
+ out_info = gr.Textbox(label="Render Info", interactive=False)
193
+
194
+ aspect_dropdown.change(
195
+ fn=apply_aspect,
196
+ inputs=aspect_dropdown,
197
+ outputs=[width_slider, height_slider],
198
+ api_name=False,
199
+ )
200
+ gen_btn.click(
201
+ fn=generate_image,
202
+ inputs=[
203
+ prompt_input,
204
+ init_image_input,
205
+ engine_choice,
206
+ aspect_dropdown,
207
+ width_slider,
208
+ height_slider,
209
+ steps_slider,
210
+ strength_slider,
211
+ seed_slider,
212
+ rand_check,
213
+ ],
214
+ outputs=[out_img, out_seed, out_info],
215
+ api_name="generate_image",
216
+ )
217
+
218
+ if __name__ == "__main__":
219
+ demo.launch(theme=gr.themes.Citrus(), mcp_server=True)
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ diffusers>=0.34.0
2
+ transformers>=4.49.0
3
+ accelerate>=1.6.0
4
+ safetensors
5
+ sentencepiece
6
+ protobuf
7
+ gradio_client
8
+ Pillow
9
+ numpy
10
+ torchvision