nftnik commited on
Commit
2685d15
·
verified ·
1 Parent(s): 92a7021

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +379 -92
app.py CHANGED
@@ -1,105 +1,392 @@
1
  import os
2
- import gradio as gr
3
- import json
4
- import torch
5
  import random
6
- import time
7
- from diffusers import DiffusionPipeline, AutoencoderTiny, AutoencoderKL, AutoPipelineForImage2Image
8
- from live_preview_helpers import calculate_shift, retrieve_timesteps, flux_pipe_call_that_returns_an_iterable_of_images
9
- from diffusers.utils import load_image
10
- from huggingface_hub import ModelCard
11
-
12
- # -------------------------------------------------------------------------
13
- # CONFIGURAÇÃO GERAL
14
- # -------------------------------------------------------------------------
15
- CONFIG = {
16
- "base_model": "black-forest-labs/FLUX.1-dev",
17
- "dtype": torch.float16, # Substituído por torch.float16 para economizar VRAM
18
- "device": "cuda" if torch.cuda.is_available() else "cpu",
19
- "max_seed": 2**32 - 1
20
- }
21
 
22
- # Limpa cache da GPU para evitar erro de falta de memória
23
- torch.cuda.empty_cache()
24
- torch.cuda.ipc_collect()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
- # -------------------------------------------------------------------------
27
- # CARREGANDO O MODELO BASE
28
- # -------------------------------------------------------------------------
29
- taef1 = AutoencoderTiny.from_pretrained("madebyollin/taef1", torch_dtype=CONFIG["dtype"]).to(CONFIG["device"])
30
- good_vae = AutoencoderKL.from_pretrained(CONFIG["base_model"], subfolder="vae", torch_dtype=CONFIG["dtype"]).to(CONFIG["device"])
31
 
32
- pipe = DiffusionPipeline.from_pretrained(
33
- CONFIG["base_model"],
34
- torch_dtype=CONFIG["dtype"],
35
- vae=taef1,
36
- low_cpu_mem_usage=True # Economiza memória na GPU
37
- ).to(CONFIG["device"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
- pipe_i2i = AutoPipelineForImage2Image.from_pretrained(
40
- CONFIG["base_model"],
41
- vae=good_vae,
42
- transformer=pipe.transformer,
43
- text_encoder=pipe.text_encoder,
44
- tokenizer=pipe.tokenizer,
45
- text_encoder_2=pipe.text_encoder_2,
46
- tokenizer_2=pipe.tokenizer_2,
47
- torch_dtype=CONFIG["dtype"]
48
- )
 
 
 
 
 
 
 
 
 
49
 
50
- pipe.flux_pipe_call_that_returns_an_iterable_of_images = flux_pipe_call_that_returns_an_iterable_of_images.__get__(pipe)
 
 
 
 
 
 
 
 
 
51
 
52
- # -------------------------------------------------------------------------
53
- # FUNÇÃO PARA GERAR IMAGEM
54
- # -------------------------------------------------------------------------
55
- def generate_image(prompt, steps, seed, cfg_scale, width, height, lora_scale, progress):
56
- pipe.to(CONFIG["device"]) # Garante que o modelo está na GPU
57
- generator = torch.Generator(device=CONFIG["device"]).manual_seed(seed)
58
-
59
- # Medir tempo de geração
60
- start_time = time.time()
61
-
62
- # Gerando a imagem
63
- for img in pipe.flux_pipe_call_that_returns_an_iterable_of_images(
64
- prompt=prompt,
65
- num_inference_steps=steps,
66
- guidance_scale=cfg_scale,
67
- width=width,
68
- height=height,
69
- generator=generator,
70
- joint_attention_kwargs={"scale": lora_scale},
71
- output_type="pil",
72
- good_vae=good_vae,
73
- ):
74
- end_time = time.time()
75
- print(f"Tempo de geração: {end_time - start_time:.2f}s")
76
- yield img
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
- # -------------------------------------------------------------------------
79
- # INTERFACE GRADIO
80
- # -------------------------------------------------------------------------
81
- with gr.Blocks(theme=gr.themes.Soft()) as app:
82
- gr.Markdown("# FLUX Avatar Generator")
83
 
84
- with gr.Row():
85
- with gr.Column():
86
- prompt = gr.Textbox(label="Prompt", placeholder="Descreva sua imagem...")
87
- steps = gr.Slider(1, 50, 25, step=1, label="Passos")
88
- cfg_scale = gr.Slider(1, 20, 3.5, step=0.5, label="Escala CFG")
89
- width = gr.Slider(512, 1536, 896, step=64, label="Largura")
90
- height = gr.Slider(512, 1536, 1152, step=64, label="Altura")
91
- lora_scale = gr.Slider(0.1, 2.0, 1.0, step=0.1, label="LoRA Scale")
92
- seed = gr.Slider(0, CONFIG["max_seed"], random.randint(0, CONFIG["max_seed"]), step=1, label="Seed", randomize=True)
93
- generate_button = gr.Button("Gerar Imagem")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
 
95
- with gr.Column():
96
- output_image = gr.Image(label="Imagem Gerada")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
- generate_button.click(
99
- fn=generate_image,
100
- inputs=[prompt, steps, seed, cfg_scale, width, height, lora_scale],
101
- outputs=[output_image]
102
- )
103
 
104
- app.queue()
105
- app.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
 
 
 
2
  import random
3
+ import torch
4
+ import numpy as np
5
+ import gradio as gr
6
+ import spaces
7
+ from diffusers import FluxPipeline
8
+ from translatepy import Translator
 
 
 
 
 
 
 
 
 
9
 
10
+ # -----------------------------------------------------------------------------
11
+ # CONFIGURATION
12
+ # -----------------------------------------------------------------------------
13
+ class Config:
14
+ MODEL_ID = "black-forest-labs/FLUX.1-dev"
15
+ DEFAULT_LORA = "nftnik/BR_ohwx_V1"
16
+ DEFAULT_WEIGHT_NAME = "BR_ohwx.safetensors"
17
+ MAX_SEED = int(np.iinfo(np.int32).max)
18
+ CSS = "footer { visibility: hidden; }"
19
+ DEFAULT_WIDTH = 896
20
+ DEFAULT_HEIGHT = 1152
21
+ DEFAULT_GUIDANCE_SCALE = 3.5
22
+ DEFAULT_STEPS = 35
23
+ DEFAULT_LORA_SCALE = 1.0
24
+ DEFAULT_TRIGGER_WORD = "ohwx"
25
+ # Memory optimization configs
26
+ ENABLE_MEMORY_EFFICIENT_ATTENTION = True
27
+ ENABLE_SEQUENTIAL_CPU_OFFLOAD = True
28
+ ENABLE_ATTENTION_SLICING = "max"
29
 
 
 
 
 
 
30
 
31
+ # -----------------------------------------------------------------------------
32
+ # FluxGenerator class to handle image generation
33
+ # -----------------------------------------------------------------------------
34
+ class FluxGenerator:
35
+ def __init__(self):
36
+ # Environment setup
37
+ os.environ["HF_HUB_ENABLE_HF_TRANSFER"] = "1"
38
+ self.translator = Translator()
39
+ self.device = self._get_optimal_device()
40
+ print(f"Using {self.device.upper()}")
41
+
42
+ # Initialize pipeline
43
+ self.pipe = None
44
+ self._initialize_pipeline()
45
+
46
+ def _get_optimal_device(self):
47
+ """Determine the optimal device based on available resources"""
48
+ if torch.cuda.is_available():
49
+ # Check GPU memory
50
+ try:
51
+ gpu_memory = torch.cuda.get_device_properties(0).total_memory
52
+ if gpu_memory > 10 * 1024 * 1024 * 1024: # More than 10GB
53
+ return "cuda"
54
+ else:
55
+ print("Limited GPU memory detected, using CPU with GPU acceleration")
56
+ return "cuda" # Still use CUDA but will apply memory optimizations
57
+ except:
58
+ print("Error checking GPU memory, falling back to CPU")
59
+ return "cpu"
60
+ else:
61
+ return "cpu"
62
+
63
+ def _initialize_pipeline(self):
64
+ """Initialize the Flux pipeline with memory optimizations"""
65
+ try:
66
+ print("Loading Flux model...")
67
+ # Use more memory-efficient settings
68
+ pipe_kwargs = {
69
+ "torch_dtype": torch.bfloat16 if self.device == "cuda" else torch.float32,
70
+ }
71
+
72
+ # Initialize the pipeline
73
+ self.pipe = FluxPipeline.from_pretrained(
74
+ Config.MODEL_ID,
75
+ **pipe_kwargs
76
+ )
77
+
78
+ # Apply memory optimizations
79
+ if Config.ENABLE_MEMORY_EFFICIENT_ATTENTION and self.device == "cuda":
80
+ print("Enabling memory efficient attention")
81
+ self.pipe.enable_xformers_memory_efficient_attention()
82
+
83
+ if Config.ENABLE_ATTENTION_SLICING:
84
+ print("Enabling attention slicing")
85
+ self.pipe.enable_attention_slicing(Config.ENABLE_ATTENTION_SLICING)
86
+
87
+ if Config.ENABLE_SEQUENTIAL_CPU_OFFLOAD and self.device == "cuda":
88
+ print("Enabling sequential CPU offload")
89
+ self.pipe.enable_sequential_cpu_offload()
90
+ else:
91
+ # Only move to device if not using CPU offload
92
+ self.pipe = self.pipe.to(self.device)
93
+
94
+ # Load default LoRA
95
+ print(f"Loading default LoRA: {Config.DEFAULT_LORA}")
96
+ self.pipe.load_lora_weights(Config.DEFAULT_LORA, weight_name=Config.DEFAULT_WEIGHT_NAME)
97
+
98
+ print("Model initialization complete")
99
+ return self.pipe
100
+
101
+ except Exception as e:
102
+ error_msg = f"Error initializing pipeline: {str(e)}"
103
+ print(error_msg)
104
+ raise
105
 
106
+ def load_lora(self, lora_path):
107
+ """Load a new LoRA model"""
108
+ try:
109
+ print(f"Unloading previous LoRA weights...")
110
+ self.pipe.unload_lora_weights()
111
+
112
+ if not lora_path:
113
+ print("No LoRA path provided, skipping LoRA loading")
114
+ return gr.update(value="")
115
+
116
+ print(f"Loading LoRA from {lora_path}...")
117
+ self.pipe.load_lora_weights(lora_path)
118
+ print("LoRA loaded successfully")
119
+ return gr.update(label="LoRA Loaded Successfully")
120
+
121
+ except Exception as e:
122
+ error_msg = f"Failed to load LoRA from {lora_path}: {str(e)}"
123
+ print(error_msg)
124
+ raise gr.Error(error_msg)
125
 
126
+ def _clear_memory(self):
127
+ """Clear CUDA memory cache"""
128
+ if self.device == "cuda":
129
+ try:
130
+ print("Clearing CUDA memory cache...")
131
+ torch.cuda.empty_cache()
132
+ if hasattr(torch.cuda, 'amp') and hasattr(torch.cuda.amp, 'autocast'):
133
+ torch.cuda.amp.clear_autocast_cache()
134
+ except Exception as e:
135
+ print(f"Warning: Failed to clear CUDA memory: {str(e)}")
136
 
137
+ @spaces.GPU()
138
+ def generate(self, prompt, lora_word, lora_scale=Config.DEFAULT_LORA_SCALE,
139
+ width=Config.DEFAULT_WIDTH, height=Config.DEFAULT_HEIGHT,
140
+ guidance_scale=Config.DEFAULT_GUIDANCE_SCALE, steps=Config.DEFAULT_STEPS,
141
+ seed=-1, num_images=1):
142
+ """Generate images from a prompt with memory optimizations"""
143
+ try:
144
+ print(f"Generating image for prompt: '{prompt}'")
145
+
146
+ # Clear memory before generation
147
+ self._clear_memory()
148
+
149
+ # Ensure we're using the right device
150
+ if not Config.ENABLE_SEQUENTIAL_CPU_OFFLOAD:
151
+ print(f"Moving model to {self.device}")
152
+ self.pipe.to(self.device)
153
+
154
+ # Handle seed
155
+ seed = random.randint(0, Config.MAX_SEED) if seed == -1 else int(seed)
156
+ print(f"Using seed: {seed}")
157
+ generator = torch.Generator(device=self.device).manual_seed(seed)
158
+
159
+ # Translate prompt if not in English
160
+ print("Translating prompt if needed...")
161
+ prompt_english = str(self.translator.translate(prompt, "English"))
162
+ full_prompt = f"{prompt_english} {lora_word}"
163
+ print(f"Full prompt: '{full_prompt}'")
164
+
165
+ # Lower resolution if on limited memory
166
+ if self.device == "cuda" and torch.cuda.get_device_properties(0).total_memory < 8 * 1024 * 1024 * 1024:
167
+ original_width, original_height = width, height
168
+ # Scale down to 85% if memory is tight
169
+ width = int(width * 0.85)
170
+ height = int(height * 0.85)
171
+ print(f"Limited memory detected. Scaling down resolution from {original_width}x{original_height} to {width}x{height}")
172
+
173
+ # Generate with autocast for memory efficiency
174
+ print(f"Starting generation with {steps} steps, guidance scale {guidance_scale}")
175
+ with torch.cuda.amp.autocast(enabled=self.device == "cuda"):
176
+ result = self.pipe(
177
+ prompt=full_prompt,
178
+ height=height,
179
+ width=width,
180
+ guidance_scale=guidance_scale,
181
+ output_type="pil",
182
+ num_inference_steps=steps,
183
+ num_images_per_prompt=num_images,
184
+ generator=generator,
185
+ joint_attention_kwargs={"scale": lora_scale},
186
+ )
187
+
188
+ print("Generation complete, returning images")
189
+ self._clear_memory() # Clear memory after generation
190
+ return result.images, seed
191
+
192
+ except Exception as e:
193
+ error_msg = f"Image generation failed: {str(e)}"
194
+ print(error_msg)
195
+ # Clear memory after error
196
+ self._clear_memory()
197
+ raise gr.Error(error_msg)
198
 
 
 
 
 
 
199
 
200
+ # -----------------------------------------------------------------------------
201
+ # UI Builder class
202
+ # -----------------------------------------------------------------------------
203
+ class FluxUI:
204
+ def __init__(self, generator):
205
+ self.generator = generator
206
+ self.example_prompts = [
207
+ ["Medium-shot portrait, ohwx blue alien, wearing black techwear with a high collar, standing inside a futuristic VR showroom.", "ohwx", 0.9],
208
+ ["ohwx blue alien, wearing black techwear with a high collar, immersed in a digital cybernetic landscape.", "ohwx", 0.9],
209
+ ["full-body shot, ohwx blue alien, wearing black techwear with a high collar, black cyber sneakers, running through a neon-lit cyberpunk alley at night.", "ohwx", 0.9],
210
+ ["ohwx blue alien, wearing black techwear with a high collar, sitting inside a sleek, high-tech VR capsule, immersed in an augmented reality experience.", "ohwx", 0.9]
211
+ ]
212
+
213
+ def build(self):
214
+ """Build and return the Gradio interface"""
215
+ with gr.Blocks(css=Config.CSS) as demo:
216
+ gr.HTML("<h1><center>BR METAVERSO - Avatar Generator</center></h1>")
217
+
218
+ # Status indicator
219
+ processing_status = gr.Markdown("**🟢 Ready**", visible=True)
220
+
221
+ with gr.Row():
222
+ with gr.Column(scale=4):
223
+ gallery = gr.Gallery(label="Flux Generated Image", columns=1, preview=True, height=600)
224
+ prompt_input = gr.Textbox(
225
+ label="Enter Your Prompt",
226
+ lines=2,
227
+ placeholder="Enter prompt for your avatar..."
228
+ )
229
+ generate_btn = gr.Button(value="Generate", variant="primary")
230
+
231
+ with gr.Accordion("Advanced Options", open=True):
232
+ with gr.Row():
233
+ with gr.Column():
234
+ width_slider = gr.Slider(
235
+ label="Width",
236
+ minimum=512,
237
+ maximum=1920,
238
+ step=8,
239
+ value=Config.DEFAULT_WIDTH
240
+ )
241
+ height_slider = gr.Slider(
242
+ label="Height",
243
+ minimum=512,
244
+ maximum=1920,
245
+ step=8,
246
+ value=Config.DEFAULT_HEIGHT
247
+ )
248
+ with gr.Column():
249
+ guidance_slider = gr.Slider(
250
+ label="Guidance Scale",
251
+ minimum=3.5,
252
+ maximum=7,
253
+ step=0.1,
254
+ value=Config.DEFAULT_GUIDANCE_SCALE
255
+ )
256
+ steps_slider = gr.Slider(
257
+ label="Steps",
258
+ minimum=1,
259
+ maximum=100,
260
+ step=1,
261
+ value=Config.DEFAULT_STEPS
262
+ )
263
+
264
+ with gr.Row():
265
+ with gr.Column():
266
+ seed_slider = gr.Slider(
267
+ label="Seed (-1 for random)",
268
+ minimum=-1,
269
+ maximum=Config.MAX_SEED,
270
+ step=1,
271
+ value=-1
272
+ )
273
+ nums_slider = gr.Slider(
274
+ label="Image Count",
275
+ minimum=1,
276
+ maximum=2,
277
+ step=1,
278
+ value=1
279
+ )
280
+ with gr.Column():
281
+ lora_scale_slider = gr.Slider(
282
+ label="LoRA Scale",
283
+ minimum=0.1,
284
+ maximum=2.0,
285
+ step=0.1,
286
+ value=Config.DEFAULT_LORA_SCALE
287
+ )
288
+
289
+ with gr.Row():
290
+ with gr.Column():
291
+ lora_add_text = gr.Textbox(
292
+ label="Flux LoRA Path",
293
+ lines=1,
294
+ value=Config.DEFAULT_LORA
295
+ )
296
+ with gr.Column():
297
+ lora_word_text = gr.Textbox(
298
+ label="Flux LoRA Trigger Word",
299
+ lines=1,
300
+ value=Config.DEFAULT_TRIGGER_WORD
301
+ )
302
+
303
+ load_lora_btn = gr.Button(value="Load Custom LoRA", variant="secondary")
304
+
305
+ # Memory optimization checkbox
306
+ with gr.Row():
307
+ memory_efficient = gr.Checkbox(
308
+ label="Enable Memory Optimizations",
309
+ value=True,
310
+ info="Reduces memory usage but may increase generation time"
311
+ )
312
+
313
+ # Examples section
314
+ gr.Examples(
315
+ examples=self.example_prompts,
316
+ inputs=[prompt_input, lora_word_text, lora_scale_slider],
317
+ cache_examples=False,
318
+ examples_per_page=4
319
+ )
320
+
321
+ # Wire up the event handlers
322
+ # Status update functions
323
+ def update_status_processing():
324
+ return "**⏳ Processing...**"
325
+
326
+ def update_status_done():
327
+ return "**✅ Done!**"
328
+
329
+ def update_memory_settings(enable_memory_opt):
330
+ global Config
331
+ Config.ENABLE_MEMORY_EFFICIENT_ATTENTION = enable_memory_opt
332
+ Config.ENABLE_SEQUENTIAL_CPU_OFFLOAD = enable_memory_opt
333
+ Config.ENABLE_ATTENTION_SLICING = "max" if enable_memory_opt else None
334
+ return gr.update()
335
 
336
+ # Generate button click workflow
337
+ generate_btn.click(
338
+ fn=update_status_processing,
339
+ inputs=[],
340
+ outputs=[processing_status]
341
+ ).then(
342
+ fn=self.generator.generate,
343
+ inputs=[
344
+ prompt_input, lora_word_text, lora_scale_slider,
345
+ width_slider, height_slider, guidance_slider,
346
+ steps_slider, seed_slider, nums_slider
347
+ ],
348
+ outputs=[gallery, seed_slider]
349
+ ).then(
350
+ fn=update_status_done,
351
+ inputs=[],
352
+ outputs=[processing_status]
353
+ )
354
+
355
+ # Load LoRA button click workflow
356
+ load_lora_btn.click(
357
+ fn=self.generator.load_lora,
358
+ inputs=[lora_add_text],
359
+ outputs=[lora_add_text]
360
+ )
361
+
362
+ # Memory optimization checkbox event
363
+ memory_efficient.change(
364
+ fn=update_memory_settings,
365
+ inputs=[memory_efficient],
366
+ outputs=[]
367
+ )
368
+
369
+ return demo
370
 
 
 
 
 
 
371
 
372
+ # -----------------------------------------------------------------------------
373
+ # Main application
374
+ # -----------------------------------------------------------------------------
375
+ def main():
376
+ try:
377
+ # Create a generator with memory optimizations
378
+ generator = FluxGenerator()
379
+
380
+ # Build and launch UI
381
+ ui = FluxUI(generator)
382
+ demo = ui.build()
383
+
384
+ # Launch with low cache size to prevent memory issues
385
+ demo.queue(max_size=1).launch(share=False)
386
+
387
+ except Exception as e:
388
+ print(f"Application startup failed: {str(e)}")
389
+ # Show error in UI if possible
390
+ with gr.Blocks() as error_demo:
391
+ gr.Markdown(f"# Error Starting Application\n\n{str(e)}\n\nPlease check the logs for more details.")
392
+ gr.Markdown("This might be due to memory limitations or