BATUTO-ART commited on
Commit
02350f6
·
verified ·
1 Parent(s): f1558c8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +412 -126
app.py CHANGED
@@ -1,150 +1,436 @@
1
  import os
2
- import time
3
- import json
4
- import base64
5
- import requests
6
  import gradio as gr
 
 
 
 
 
 
 
7
 
8
- # =============================================================
9
- # Utilidades Generales
10
- # =============================================================
11
- def load_image(path):
12
- with open(path, "rb") as f:
13
- return f.read()
14
-
15
- # =============================================================
16
- # FLUX (Optimizado CPU)
17
- # =============================================================
18
- def flux_generate(prompt, seed, steps, width, height):
19
- try:
20
- payload = {
21
- "prompt": prompt,
22
- "seed": seed,
23
- "steps": steps,
24
- "width": width,
25
- "height": height,
26
- }
27
- return "⚠ FLUX CPU: Modelo optimizado próximamente."
28
- except Exception as e:
29
- return f"Error FLUX: {e}"
30
 
31
- # =============================================================
32
- # Stable Diffusion 1.5 (Optimizado CPU, resultados realistas)
33
- # =============================================================
34
- def sd15_generate(prompt, seed, steps, width, height):
35
- try:
36
- payload = {
37
- "prompt": prompt + ", ultra realistic, photorealistic, sharp details, 8k, high texture clarity",
38
- "seed": seed,
39
- "steps": steps,
40
- "width": width,
41
- "height": height,
42
- }
43
- return "⚠ SD1.5 CPU: Generador listo para integración real."
44
- except Exception as e:
45
- return f"Error SD1.5: {e}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
- # =============================================================
48
- # REVE CREATE (Con selector de imágenes 1–8)
49
- # =============================================================
50
- def reve_create_generate(api_key, prompt, seed, steps, num_images):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  try:
52
- url = "https://api.reveai.xyz/v1/generate"
53
-
54
- payload = {
55
- "prompt": prompt,
56
- "seed": seed,
57
- "steps": steps,
58
- "num_images": num_images,
59
- "width": 576,
60
- "height": 1024,
61
- }
62
-
63
- headers = {
64
- "Authorization": f"Bearer {api_key}",
65
- "Content-Type": "application/json",
66
- }
67
-
68
- res = requests.post(url, json=payload, headers=headers)
69
- data = res.json()
70
-
71
- outputs = []
72
- for img_b64 in data.get("images", []):
73
- img_bytes = base64.b64decode(img_b64)
74
- filename = f"reve_{int(time.time())}.png"
75
- filepath = f"/tmp/{filename}"
76
- with open(filepath, "wb") as f:
77
- f.write(img_bytes)
78
- outputs.append(filepath)
79
-
80
- return outputs
81
  except Exception as e:
82
- return [f"Error REVE: {e}"]
83
 
84
- # =============================================================
85
- # INTERFAZ GRADIO (Optimizada)
86
- # =============================================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  def build_ui():
88
  with gr.Blocks(title="BATUTO-ART MIX", theme=gr.themes.Soft()) as demo:
89
 
90
- gr.Markdown("# 🎨 BATUTO-ART MIX – Generador Realista y Rápido en CPU")
 
 
 
91
 
92
  with gr.Tabs():
93
 
94
- # =============================================================
95
- # FLUX TAB
96
- # =============================================================
97
- with gr.TabItem("FLUX Optimizado"):
98
- flux_prompt = gr.Textbox(label="Prompt")
99
- flux_seed = gr.Number(label="Seed", value=1234)
100
- flux_steps = gr.Slider(5, 60, value=20, step=1, label="Steps")
101
- flux_btn = gr.Button("Generar")
102
- flux_output = gr.Textbox(label="Resultado")
103
-
104
- flux_btn.click(
105
- fn=flux_generate,
106
- inputs=[flux_prompt, flux_seed, flux_steps, gr.Number(value=576), gr.Number(value=1024)],
107
- outputs=[flux_output],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  )
109
 
110
- # =============================================================
111
- # SD 1.5 TAB
112
- # =============================================================
113
- with gr.TabItem("Stable Diffusion 1.5 CPU"):
114
- sd_prompt = gr.Textbox(label="Prompt")
115
- sd_seed = gr.Number(label="Seed", value=1234)
116
- sd_steps = gr.Slider(5, 50, value=20, step=1, label="Steps")
117
- sd_btn = gr.Button("Generar Imagen")
118
- sd_output = gr.Textbox(label="Resultado")
119
-
120
- sd_btn.click(
121
- fn=sd15_generate,
122
- inputs=[sd_prompt, sd_seed, sd_steps, gr.Number(value=576), gr.Number(value=1024)],
123
- outputs=[sd_output],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  )
125
 
126
- # =============================================================
127
- # REVE CREATE TAB (Selector 1–8 imágenes)
128
- # =============================================================
129
- with gr.TabItem("REVE CREATE Avanzado"):
130
- reve_api_key = gr.Textbox(label="API Key de REVE", type="password")
131
- reve_prompt = gr.Textbox(label="Prompt")
132
- reve_seed = gr.Number(label="Seed", value=1234)
133
- reve_steps = gr.Slider(5, 50, value=20, step=1, label="Steps")
134
- reve_num = gr.Slider(1, 8, value=1, step=1, label="Cantidad de Imágenes (1–8)")
135
-
136
- reve_btn = gr.Button("Generar con REVE")
137
- reve_gallery = gr.Gallery(label="Imágenes Generadas", columns=2, height=500)
138
-
139
- reve_btn.click(
140
- fn=lambda key, p, s, st, n: reve_create_generate(key, p, s, st, int(n)),
141
- inputs=[reve_api_key, reve_prompt, reve_seed, reve_steps, reve_num],
142
- outputs=[reve_gallery],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  )
144
 
145
  return demo
146
 
147
- # Ejecutar local o en HuggingFace
 
 
148
  if __name__ == "__main__":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  demo = build_ui()
150
- demo.launch()
 
 
 
 
 
 
1
  import os
 
 
 
 
2
  import gradio as gr
3
+ from diffusers import DiffusionPipeline
4
+ import torch
5
+ import requests
6
+ from PIL import Image
7
+ from io import BytesIO
8
+ import concurrent.futures
9
+ import threading
10
 
11
+ # ==============================
12
+ # CONFIGURACIÓN BASE CPU
13
+ # ==============================
14
+ DEVICE = "cpu"
15
+ torch.set_grad_enabled(False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
+ # PARÁMETROS POR DEFECTO AJUSTADOS PARA CPU MÁS RÁPIDO
18
+ DEFAULT_STEPS = 15 # Reducido de 20 para más velocidad
19
+ DEFAULT_WIDTH = 512 # Reducido de 576 para menos carga
20
+ DEFAULT_HEIGHT = 768 # Reducido de 1024 para menos carga (mantiene relación aproximada)
21
+
22
+ def load_flux(model_id):
23
+ pipe = DiffusionPipeline.from_pretrained(model_id, torch_dtype=torch.float32)
24
+ pipe.to(DEVICE)
25
+ pipe.enable_attention_slicing()
26
+ return pipe
27
+
28
+ # Cache de modelos
29
+ MODEL_CACHE = {}
30
+
31
+ # ==============================
32
+ # GENERADOR FLUX
33
+ # ==============================
34
+ def generate_flux(model_name, prompt, steps, guidance, width, height, seed):
35
+ if model_name not in MODEL_CACHE:
36
+ MODEL_CACHE[model_name] = load_flux(model_name)
37
+ pipe = MODEL_CACHE[model_name]
38
+
39
+ generator = torch.manual_seed(seed) if seed else None
40
+
41
+ image = pipe(
42
+ prompt=prompt,
43
+ num_inference_steps=steps,
44
+ guidance_scale=guidance,
45
+ width=width,
46
+ height=height,
47
+ generator=generator
48
+ ).images[0]
49
 
50
+ out = "/tmp/flux_output.png"
51
+ image.save(out)
52
+ return out
53
+
54
+ # ==============================
55
+ # GENERADOR SD1.5
56
+ # ==============================
57
+ def load_sd15():
58
+ pipe = DiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5", torch_dtype=torch.float32)
59
+ pipe.to(DEVICE)
60
+ pipe.enable_attention_slicing()
61
+ return pipe
62
+
63
+ # SD15 load único
64
+ def generate_sd(prompt, steps, guidance, width, height, seed):
65
+ if "sd15" not in MODEL_CACHE:
66
+ MODEL_CACHE["sd15"] = load_sd15()
67
+ pipe = MODEL_CACHE["sd15"]
68
+
69
+ generator = torch.manual_seed(seed) if seed else None
70
+
71
+ image = pipe(
72
+ prompt=prompt,
73
+ num_inference_steps=steps,
74
+ guidance_scale=guidance,
75
+ width=width,
76
+ height=height,
77
+ generator=generator
78
+ ).images[0]
79
+
80
+ out = "/tmp/sd15_output.png"
81
+ image.save(out)
82
+ return out
83
+
84
+ # ==============================
85
+ # REVE CREATE MODIFICADO (Generación múltiple con threads)
86
+ # ==============================
87
+ def generate_single_reve_image(prompt, key, model, index, results_list, lock, progress_callback=None):
88
+ """Función auxiliar para generar una sola imagen"""
89
  try:
90
+ url = "https://api.reveai.xyz/v1/images"
91
+ headers = {"Authorization": f"Bearer {key}"}
92
+ data = {"prompt": prompt, "model": model}
93
+
94
+ resp = requests.post(url, json=data, headers=headers, timeout=30)
95
+
96
+ if resp.status_code != 200:
97
+ print(f"Error en imagen {index+1}: {resp.status_code}")
98
+ return
99
+
100
+ img_url = resp.json().get("image")
101
+ if not img_url:
102
+ return
103
+
104
+ img_data = requests.get(img_url, timeout=30).content
105
+ img = Image.open(BytesIO(img_data))
106
+
107
+ out = f"/tmp/reve_{index}_{threading.current_thread().ident}.png"
108
+ img.save(out)
109
+
110
+ with lock:
111
+ results_list.append(out)
112
+
113
+ # Notificar progreso si hay callback
114
+ if progress_callback:
115
+ progress_callback(index + 1)
116
+
 
 
117
  except Exception as e:
118
+ print(f"Error generando imagen {index+1}: {e}")
119
 
120
+ def reve_generate_multiple(prompt, key, model, num_images, progress_callback=None):
121
+ if not key:
122
+ return None
123
+
124
+ num_images = min(num_images, 8) # Máximo 8 imágenes
125
+ results = []
126
+ lock = threading.Lock()
127
+
128
+ # Usamos ThreadPoolExecutor para generar imágenes en paralelo
129
+ with concurrent.futures.ThreadPoolExecutor(max_workers=min(num_images, 4)) as executor:
130
+ futures = []
131
+ for i in range(num_images):
132
+ future = executor.submit(
133
+ generate_single_reve_image,
134
+ prompt, key, model, i, results, lock, progress_callback
135
+ )
136
+ futures.append(future)
137
+
138
+ # Esperar a que todas terminen
139
+ concurrent.futures.wait(futures)
140
+
141
+ return results if results else None
142
+
143
+ # ==============================
144
+ # UI COMPLETA
145
+ # ==============================
146
  def build_ui():
147
  with gr.Blocks(title="BATUTO-ART MIX", theme=gr.themes.Soft()) as demo:
148
 
149
+ gr.Markdown("""
150
+ # 🖼️ **BATUTO-ART MIX**
151
+ *Generador de imágenes con FLUX, Stable Diffusion 1.5 y REVE CREATE*
152
+ """)
153
 
154
  with gr.Tabs():
155
 
156
+ # ============================
157
+ # TAB: FLUX
158
+ # ============================
159
+ with gr.Tab("FLUX.2 / 1-Schnell"):
160
+ with gr.Row():
161
+ with gr.Column(scale=1):
162
+ flux_prompt = gr.Textbox(
163
+ label="Prompt",
164
+ lines=3,
165
+ placeholder="Describe la imagen que quieres generar con FLUX..."
166
+ )
167
+ model_select = gr.Dropdown([
168
+ "black-forest-labs/FLUX.1-schnell",
169
+ "black-forest-labs/FLUX.1-dev",
170
+ "black-forest-labs/FLUX.2-dev"
171
+ ], value="black-forest-labs/FLUX.1-schnell", label="Modelo FLUX")
172
+
173
+ with gr.Row():
174
+ steps = gr.Slider(5, 50, value=DEFAULT_STEPS, step=1, label="Steps")
175
+ guidance = gr.Slider(0, 10, value=3, step=0.1, label="Guidance Scale")
176
+
177
+ with gr.Row():
178
+ width = gr.Number(value=DEFAULT_WIDTH, label="Width", precision=0)
179
+ height = gr.Number(value=DEFAULT_HEIGHT, label="Height", precision=0)
180
+
181
+ seed = gr.Number(value=0, label="Seed (0 = aleatorio)", precision=0)
182
+
183
+ btn_flux = gr.Button("✨ Generar Imagen", variant="primary", size="lg")
184
+
185
+ with gr.Column(scale=1):
186
+ out_flux_img = gr.Image(
187
+ label="Resultado FLUX",
188
+ height=400,
189
+ interactive=False
190
+ )
191
+ out_flux_file = gr.File(
192
+ label="Descargar imagen",
193
+ visible=False
194
+ )
195
+
196
+ # Acción del botón FLUX
197
+ def generate_flux_wrapper(model_name, prompt, steps, guidance, width, height, seed):
198
+ if not prompt.strip():
199
+ return None, "❌ Error: Ingresa un prompt válido"
200
+ try:
201
+ file_path = generate_flux(
202
+ model_name, prompt, int(steps), float(guidance),
203
+ int(width), int(height), int(seed)
204
+ )
205
+ return file_path, gr.update(visible=True)
206
+ except Exception as e:
207
+ return None, f"❌ Error: {str(e)}"
208
+
209
+ btn_flux.click(
210
+ fn=generate_flux_wrapper,
211
+ inputs=[model_select, flux_prompt, steps, guidance, width, height, seed],
212
+ outputs=[out_flux_file, out_flux_file]
213
+ )
214
+
215
+ # Mostrar imagen automáticamente
216
+ out_flux_file.change(
217
+ fn=lambda f: Image.open(f) if f else None,
218
+ inputs=[out_flux_file],
219
+ outputs=[out_flux_img]
220
  )
221
 
222
+ # ============================
223
+ # TAB: SD1.5
224
+ # ============================
225
+ with gr.Tab("Stable Diffusion 1.5"):
226
+ with gr.Row():
227
+ with gr.Column(scale=1):
228
+ sd_prompt = gr.Textbox(
229
+ label="Prompt",
230
+ lines=3,
231
+ placeholder="Describe la imagen que quieres generar con SD1.5..."
232
+ )
233
+
234
+ with gr.Row():
235
+ sd_steps = gr.Slider(5, 50, value=DEFAULT_STEPS, step=1, label="Steps")
236
+ sd_guidance = gr.Slider(0, 10, value=3, step=0.1, label="Guidance Scale")
237
+
238
+ with gr.Row():
239
+ sd_width = gr.Number(value=DEFAULT_WIDTH, label="Width", precision=0)
240
+ sd_height = gr.Number(value=DEFAULT_HEIGHT, label="Height", precision=0)
241
+
242
+ sd_seed = gr.Number(value=0, label="Seed (0 = aleatorio)", precision=0)
243
+
244
+ btn_sd = gr.Button("✨ Generar Imagen", variant="primary", size="lg")
245
+
246
+ with gr.Column(scale=1):
247
+ out_sd_img = gr.Image(
248
+ label="Resultado SD1.5",
249
+ height=400,
250
+ interactive=False
251
+ )
252
+ out_sd_file = gr.File(
253
+ label="Descargar imagen",
254
+ visible=False
255
+ )
256
+
257
+ # Acción del botón SD1.5
258
+ def generate_sd_wrapper(prompt, steps, guidance, width, height, seed):
259
+ if not prompt.strip():
260
+ return None, "❌ Error: Ingresa un prompt válido"
261
+ try:
262
+ file_path = generate_sd(
263
+ prompt, int(steps), float(guidance),
264
+ int(width), int(height), int(seed)
265
+ )
266
+ return file_path, gr.update(visible=True)
267
+ except Exception as e:
268
+ return None, f"❌ Error: {str(e)}"
269
+
270
+ btn_sd.click(
271
+ fn=generate_sd_wrapper,
272
+ inputs=[sd_prompt, sd_steps, sd_guidance, sd_width, sd_height, sd_seed],
273
+ outputs=[out_sd_file, out_sd_file]
274
+ )
275
+
276
+ out_sd_file.change(
277
+ fn=lambda f: Image.open(f) if f else None,
278
+ inputs=[out_sd_file],
279
+ outputs=[out_sd_img]
280
  )
281
 
282
+ # ============================
283
+ # TAB: REVE CREATE MODIFICADA
284
+ # ============================
285
+ with gr.Tab("REVE CREATE"):
286
+ with gr.Row():
287
+ with gr.Column(scale=1):
288
+ reve_api = gr.Textbox(
289
+ label="API Key REVE",
290
+ type="password",
291
+ placeholder="Ingresa tu API key de REVE",
292
+ info="Necesitas una clave API válida de REVE"
293
+ )
294
+ reve_prompt = gr.Textbox(
295
+ label="Prompt",
296
+ lines=3,
297
+ placeholder="Describe la imagen que quieres generar..."
298
+ )
299
+ reve_model = gr.Dropdown([
300
+ "reve-1",
301
+ "reve-2",
302
+ "reve-fast"
303
+ ], value="reve-fast", label="Modelo REVE")
304
+
305
+ # Slider para cantidad de imágenes
306
+ num_images_slider = gr.Slider(
307
+ minimum=1,
308
+ maximum=8,
309
+ value=4,
310
+ step=1,
311
+ label="Cantidad de imágenes a generar"
312
+ )
313
+
314
+ with gr.Row():
315
+ btn_reve = gr.Button("🚀 Generar Imágenes", variant="primary", size="lg")
316
+ btn_clear = gr.Button("🗑️ Limpiar", variant="secondary")
317
+
318
+ # Indicador de progreso
319
+ progress_info = gr.Markdown("Esperando generación...")
320
+
321
+ with gr.Column(scale=2):
322
+ # Galería para mostrar múltiples imágenes
323
+ gallery = gr.Gallery(
324
+ label="Imágenes generadas",
325
+ show_label=True,
326
+ columns=4,
327
+ rows=2,
328
+ height="auto",
329
+ object_fit="contain"
330
+ )
331
+
332
+ # Información de resultados
333
+ result_info = gr.Markdown("")
334
+
335
+ # Botón de descarga
336
+ with gr.Row():
337
+ btn_download_all = gr.Button(
338
+ "📥 Descargar todas las imágenes",
339
+ size="lg",
340
+ variant="secondary"
341
+ )
342
+ download_output = gr.File(
343
+ label="Archivos descargables",
344
+ file_count="multiple",
345
+ visible=False
346
+ )
347
+
348
+ # Estado para almacenar las rutas de archivos
349
+ last_files = gr.State([])
350
+
351
+ # Función para generar múltiples imágenes con progreso
352
+ def generate_and_update_gallery(prompt, key, model, num_images, progress=gr.Progress()):
353
+ if not key:
354
+ return [], [], "❌ Error: Ingresa una API key válida", progress_info.update(value="")
355
+
356
+ if not prompt.strip():
357
+ return [], [], "❌ Error: Ingresa un prompt válido", progress_info.update(value="")
358
+
359
+ progress_info.update(value="⏳ Iniciando generación...")
360
+
361
+ # Función de callback para progreso
362
+ def update_progress(current):
363
+ progress_info.update(value=f"⏳ Generando imagen {current}/{num_images}...")
364
+
365
+ files = reve_generate_multiple(prompt, key, model, num_images, update_progress)
366
+
367
+ if files:
368
+ # Crear lista de imágenes para la galería
369
+ images = [(file,) for file in files]
370
+ info_text = f"✅ Generadas {len(files)} de {num_images} imágenes"
371
+ progress_info.update(value="✅ Generación completada")
372
+ return images, files, info_text
373
+ else:
374
+ progress_info.update(value="❌ Error en la generación")
375
+ return [], [], "❌ Error: No se pudieron generar imágenes. Verifica tu API key y conexión."
376
+
377
+ # Acción del botón de generación
378
+ btn_reve.click(
379
+ fn=generate_and_update_gallery,
380
+ inputs=[reve_prompt, reve_api, reve_model, num_images_slider],
381
+ outputs=[gallery, last_files, result_info]
382
+ )
383
+
384
+ # Acción del botón de descarga
385
+ def prepare_download(files):
386
+ if files:
387
+ return gr.update(value=files, visible=True)
388
+ return gr.update(value=[], visible=False)
389
+
390
+ btn_download_all.click(
391
+ fn=prepare_download,
392
+ inputs=[last_files],
393
+ outputs=[download_output]
394
+ )
395
+
396
+ # Acción del botón de limpiar
397
+ def clear_gallery():
398
+ return [], [], "✅ Galería limpiada", gr.update(value="Listo para nueva generación")
399
+
400
+ btn_clear.click(
401
+ fn=clear_gallery,
402
+ outputs=[gallery, last_files, result_info, progress_info]
403
+ ).then(
404
+ fn=lambda: gr.update(visible=False),
405
+ outputs=[download_output]
406
  )
407
 
408
  return demo
409
 
410
+ # ==============================
411
+ # EJECUCIÓN PRINCIPAL
412
+ # ==============================
413
  if __name__ == "__main__":
414
+ # Configuración para HuggingFace Spaces
415
+ import argparse
416
+ parser = argparse.ArgumentParser()
417
+ parser.add_argument("--share", action="store_true", help="Crear enlace público")
418
+ parser.add_argument("--server-name", type=str, default="0.0.0.0", help="Dirección del servidor")
419
+ parser.add_argument("--server-port", type=int, default=7860, help="Puerto del servidor")
420
+ args = parser.parse_args()
421
+
422
+ print("=" * 50)
423
+ print("🚀 Iniciando BATUTO-ART MIX")
424
+ print("=" * 50)
425
+ print(f"📱 Dispositivo: {DEVICE}")
426
+ print(f"⚡ Steps por defecto: {DEFAULT_STEPS}")
427
+ print(f"📐 Resolución por defecto: {DEFAULT_WIDTH}x{DEFAULT_HEIGHT}")
428
+ print("=" * 50)
429
+
430
  demo = build_ui()
431
+ demo.launch(
432
+ share=args.share,
433
+ server_name=args.server_name,
434
+ server_port=args.server_port,
435
+ show_error=True
436
+ )