prithivMLmods commited on
Commit
ceae1d0
·
1 Parent(s): 589a007

update app

Browse files
Files changed (1) hide show
  1. app.py +351 -0
app.py ADDED
@@ -0,0 +1,351 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gc
3
+ import gradio as gr
4
+ import numpy as np
5
+ import spaces
6
+ import torch
7
+ import random
8
+ from PIL import Image
9
+ from typing import Iterable
10
+ from gradio.themes import Soft
11
+ from gradio.themes.utils import colors, fonts, sizes
12
+ from diffusers import Flux2KleinPipeline
13
+
14
+ colors.orange_red = colors.Color(
15
+ name="orange_red",
16
+ c50="#FFF0E5",
17
+ c100="#FFE0CC",
18
+ c200="#FFC299",
19
+ c300="#FFA366",
20
+ c400="#FF8533",
21
+ c500="#FF4500",
22
+ c600="#E63E00",
23
+ c700="#CC3700",
24
+ c800="#B33000",
25
+ c900="#992900",
26
+ c950="#802200",
27
+ )
28
+
29
+ class OrangeRedTheme(Soft):
30
+ def __init__(
31
+ self,
32
+ *,
33
+ primary_hue: colors.Color | str = colors.gray,
34
+ secondary_hue: colors.Color | str = colors.orange_red,
35
+ neutral_hue: colors.Color | str = colors.slate,
36
+ text_size: sizes.Size | str = sizes.text_lg,
37
+ font: fonts.Font | str | Iterable[fonts.Font | str] = (
38
+ fonts.GoogleFont("Outfit"), "Arial", "sans-serif",
39
+ ),
40
+ font_mono: fonts.Font | str | Iterable[fonts.Font | str] = (
41
+ fonts.GoogleFont("IBM Plex Mono"), "ui-monospace", "monospace",
42
+ ),
43
+ ):
44
+ super().__init__(
45
+ primary_hue=primary_hue,
46
+ secondary_hue=secondary_hue,
47
+ neutral_hue=neutral_hue,
48
+ text_size=text_size,
49
+ font=font,
50
+ font_mono=font_mono,
51
+ )
52
+ super().set(
53
+ background_fill_primary="*primary_50",
54
+ background_fill_primary_dark="*primary_900",
55
+ body_background_fill="linear-gradient(135deg, *primary_200, *primary_100)",
56
+ body_background_fill_dark="linear-gradient(135deg, *primary_900, *primary_800)",
57
+ button_primary_text_color="white",
58
+ button_primary_text_color_hover="white",
59
+ button_primary_background_fill="linear-gradient(90deg, *secondary_500, *secondary_600)",
60
+ button_primary_background_fill_hover="linear-gradient(90deg, *secondary_600, *secondary_700)",
61
+ button_primary_background_fill_dark="linear-gradient(90deg, *secondary_600, *secondary_700)",
62
+ button_primary_background_fill_hover_dark="linear-gradient(90deg, *secondary_500, *secondary_600)",
63
+ button_secondary_text_color="black",
64
+ button_secondary_text_color_hover="white",
65
+ button_secondary_background_fill="linear-gradient(90deg, *primary_300, *primary_300)",
66
+ button_secondary_background_fill_hover="linear-gradient(90deg, *primary_400, *primary_400)",
67
+ button_secondary_background_fill_dark="linear-gradient(90deg, *primary_500, *primary_600)",
68
+ button_secondary_background_fill_hover_dark="linear-gradient(90deg, *primary_500, *primary_500)",
69
+ slider_color="*secondary_500",
70
+ slider_color_dark="*secondary_600",
71
+ block_title_text_weight="600",
72
+ block_border_width="3px",
73
+ block_shadow="*shadow_drop_lg",
74
+ button_primary_shadow="*shadow_drop_lg",
75
+ button_large_padding="11px",
76
+ color_accent_soft="*primary_100",
77
+ block_label_background_fill="*primary_200",
78
+ )
79
+
80
+ orange_red_theme = OrangeRedTheme()
81
+
82
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
83
+ dtype = torch.bfloat16
84
+
85
+ print("CUDA_VISIBLE_DEVICES=", os.environ.get("CUDA_VISIBLE_DEVICES"))
86
+ print("torch.__version__ =", torch.__version__)
87
+ print("Using device:", device)
88
+
89
+ REPO_ID_DISTILLED = "black-forest-labs/FLUX.2-klein-4B"
90
+
91
+ print(f"Loading 4B Distilled model from {REPO_ID_DISTILLED}...")
92
+ try:
93
+ pipe = Flux2KleinPipeline.from_pretrained(REPO_ID_DISTILLED, torch_dtype=dtype)
94
+ pipe.to(device)
95
+ except Exception as e:
96
+ print(f"Error loading model: {e}")
97
+ raise e
98
+
99
+ MAX_SEED = np.iinfo(np.int32).max
100
+ MAX_IMAGE_SIZE = 1024
101
+
102
+ ADAPTER_SPECS = {
103
+ "None": {
104
+ "repo": None,
105
+ "weights": None,
106
+ "adapter_name": "none"
107
+ },
108
+ "Outpaint": {
109
+ "repo": "fal/flux-2-klein-4B-outpaint-lora",
110
+ "weights": "flux-outpaint-lora.safetensors",
111
+ "adapter_name": "outpaint"
112
+ },
113
+ "Zoom": {
114
+ "repo": "fal/flux-2-klein-4B-zoom-lora",
115
+ "weights": "flux-red-zoom-lora.safetensors",
116
+ "adapter_name": "zoom"
117
+ },
118
+ "Background-Remove": {
119
+ "repo": "fal/flux-2-klein-4B-background-remove-lora",
120
+ "weights": "flux-background-remove-lora.safetensors",
121
+ "adapter_name": "rmbg"
122
+ },
123
+ "Object-Remove": {
124
+ "repo": "fal/flux-2-klein-4B-object-remove-lora",
125
+ "weights": "flux-object-remove-lora.safetensors",
126
+ "adapter_name": "object-remove"
127
+ },
128
+ "Sprite-Sheet": {
129
+ "repo": "fal/flux-2-klein-4b-spritesheet-lora",
130
+ "weights": "flux-spritesheet-lora.safetensors",
131
+ "adapter_name": "spritesheet"
132
+ },
133
+ }
134
+
135
+ CURRENT_LORA_NAME = "None"
136
+
137
+ def update_dimensions_from_image(image_list):
138
+ """Update width/height based on upload to maintain aspect ratio."""
139
+ if image_list is None or not image_list:
140
+ return 1024, 1024
141
+
142
+ try:
143
+ if isinstance(image_list[0], tuple) or isinstance(image_list[0], list):
144
+ img = image_list[0][0]
145
+ else:
146
+ img = image_list[0]
147
+
148
+ if isinstance(img, str):
149
+ img = Image.open(img)
150
+
151
+ img_width, img_height = img.size
152
+
153
+ if img_width > img_height:
154
+ new_width = 1024
155
+ new_height = int(1024 * (img_height / img_width))
156
+ else:
157
+ new_height = 1024
158
+ new_width = int(1024 * (img_width / img_height))
159
+
160
+ # Round to nearest 8 (Flux requirement)
161
+ new_width = round(new_width / 16) * 16
162
+ new_height = round(new_height / 16) * 16
163
+
164
+ return new_width, new_height
165
+ except Exception as e:
166
+ print(f"Error sizing image: {e}")
167
+ return 1024, 1024
168
+
169
+ @spaces.GPU
170
+ def infer(
171
+ prompt,
172
+ input_images,
173
+ lora_choice,
174
+ seed,
175
+ randomize_seed,
176
+ width,
177
+ height,
178
+ num_inference_steps,
179
+ guidance_scale,
180
+ progress=gr.Progress(track_tqdm=True)
181
+ ):
182
+ global CURRENT_LORA_NAME
183
+
184
+ gc.collect()
185
+ torch.cuda.empty_cache()
186
+
187
+ if lora_choice != CURRENT_LORA_NAME:
188
+ print(f"--- Switching Adapter: {CURRENT_LORA_NAME} -> {lora_choice} ---")
189
+
190
+ if CURRENT_LORA_NAME != "None":
191
+ try:
192
+ pipe.unload_lora_weights()
193
+ print("Unloaded previous weights.")
194
+ except Exception as e:
195
+ print(f"Warning unloading weights: {e}")
196
+
197
+ if lora_choice != "None":
198
+ spec = ADAPTER_SPECS[lora_choice]
199
+ try:
200
+ print(f"Loading {lora_choice} weights from {spec['repo']}...")
201
+ pipe.load_lora_weights(
202
+ spec["repo"],
203
+ weight_name=spec["weights"],
204
+ adapter_name=spec["adapter_name"]
205
+ )
206
+ except Exception as e:
207
+ raise gr.Error(f"Failed to load adapter {lora_choice}: {e}")
208
+
209
+ CURRENT_LORA_NAME = lora_choice
210
+
211
+ if randomize_seed:
212
+ seed = random.randint(0, MAX_SEED)
213
+
214
+ generator = torch.Generator(device=device).manual_seed(seed)
215
+
216
+ pil_images = []
217
+ if input_images:
218
+ for item in input_images:
219
+ if isinstance(item, tuple) or isinstance(item, list):
220
+ img_obj = item[0]
221
+ else:
222
+ img_obj = item
223
+
224
+ if isinstance(img_obj, str):
225
+ pil_images.append(Image.open(img_obj).convert("RGB"))
226
+ elif isinstance(img_obj, Image.Image):
227
+ pil_images.append(img_obj.convert("RGB"))
228
+
229
+ pipe_kwargs = {
230
+ "prompt": prompt,
231
+ "height": height,
232
+ "width": width,
233
+ "num_inference_steps": num_inference_steps,
234
+ "guidance_scale": guidance_scale,
235
+ "generator": generator,
236
+ }
237
+
238
+ if pil_images and len(pil_images) > 0:
239
+
240
+ pipe_kwargs["image"] = pil_images[0]
241
+
242
+ pipe_kwargs["strength"] = 0.85
243
+
244
+ try:
245
+ print(f"Generating with Prompt: {prompt[:50]}... | LoRA: {CURRENT_LORA_NAME}")
246
+ result_image = pipe(**pipe_kwargs).images[0]
247
+ return result_image, seed
248
+ except Exception as e:
249
+ raise gr.Error(f"Generation failed: {str(e)}")
250
+ finally:
251
+ gc.collect()
252
+ torch.cuda.empty_cache()
253
+
254
+ @spaces.GPU
255
+ def infer_example(prompt, images, lora_choice):
256
+ return infer(
257
+ prompt=prompt,
258
+ input_images=images,
259
+ lora_choice=lora_choice,
260
+ seed=42,
261
+ randomize_seed=False,
262
+ width=1024,
263
+ height=1024,
264
+ num_inference_steps=4,
265
+ guidance_scale=1.0
266
+ )
267
+
268
+ css = """
269
+ #col-container {
270
+ margin: 0 auto;
271
+ max-width: 1000px;
272
+ }
273
+ #main-title h1 {font-size: 2.3em !important;}
274
+ """
275
+
276
+ with gr.Blocks() as demo:
277
+ with gr.Column(elem_id="col-container"):
278
+ gr.Markdown("# **FLUX.2-klein-LoRA-Studio**", elem_id="main-title")
279
+ gr.Markdown("Generate and Edit using the unified **FLUX.2-klein-4B** (Distilled) model with specialized LoRA adapters.")
280
+
281
+ with gr.Row(equal_height=True):
282
+ with gr.Column():
283
+ prompt = gr.Text(
284
+ label="Prompt",
285
+ show_label=True,
286
+ placeholder="Describe your image or edit...",
287
+ lines=3
288
+ )
289
+
290
+ with gr.Row():
291
+ lora_choice = gr.Dropdown(
292
+ label="Select Adapter",
293
+ choices=list(ADAPTER_SPECS.keys()),
294
+ value="None",
295
+ info="Select 'None' for raw 4B Distilled generation."
296
+ )
297
+ run_button = gr.Button("Run / Edit", variant="primary", scale=1)
298
+
299
+ input_images = gr.Gallery(
300
+ label="Input Image (Optional for T2I, Required for Edits)",
301
+ type="pil",
302
+ columns=2,
303
+ rows=1,
304
+ height=250,
305
+ allow_preview=True
306
+ )
307
+
308
+ with gr.Column():
309
+ output_image = gr.Image(label="Result", interactive=False, format="png")
310
+
311
+ with gr.Accordion("Advanced Settings", open=False):
312
+ seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=0)
313
+ randomize_seed = gr.Checkbox(label="Randomize Seed", value=True)
314
+
315
+ with gr.Row():
316
+ width = gr.Slider(label="Width", minimum=256, maximum=MAX_IMAGE_SIZE, step=16, value=1024)
317
+ height = gr.Slider(label="Height", minimum=256, maximum=MAX_IMAGE_SIZE, step=16, value=1024)
318
+
319
+ with gr.Row():
320
+ guidance_scale = gr.Slider(label="Guidance Scale", minimum=1.0, maximum=10.0, step=0.1, value=1.0, info="Keep low (1.0) for Distilled model")
321
+ num_inference_steps = gr.Slider(label="Steps", minimum=1, maximum=50, step=1, value=4, info="4-8 steps recommended for Distilled")
322
+
323
+ gr.Examples(
324
+ examples=[
325
+ ["A futuristic city with glowing neon lights, cinematic view.", None, "None"],
326
+ ["Zoom out to show the table.", ["examples/cup.jpg"], "Zoom"],
327
+ ["Remove the background.", ["examples/object.jpg"], "Background-Remove"],
328
+ ["Make a sprite sheet of this character.", ["examples/char.jpg"], "Sprite-Sheet"],
329
+ ],
330
+ inputs=[prompt, input_images, lora_choice],
331
+ outputs=[output_image, seed],
332
+ # fn=infer_example, # Disabled cache_examples for speed in snippet
333
+ label="Examples (Note: Upload images for edit examples)"
334
+ )
335
+
336
+ gr.Markdown("Running on **FLUX.2-klein-4B** (Distilled). Adapters provided by Fal.ai.")
337
+
338
+ input_images.upload(
339
+ fn=update_dimensions_from_image,
340
+ inputs=[input_images],
341
+ outputs=[width, height]
342
+ )
343
+
344
+ run_button.click(
345
+ fn=infer,
346
+ inputs=[prompt, input_images, lora_choice, seed, randomize_seed, width, height, num_inference_steps, guidance_scale],
347
+ outputs=[output_image, seed]
348
+ )
349
+
350
+ if __name__ == "__main__":
351
+ demo.queue(max_size=20).launch(css=css, theme=orange_red_theme, ssr_mode=False, show_error=True)