salomonsky commited on
Commit
fc85da7
1 Parent(s): 62efc9f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +123 -171
app.py CHANGED
@@ -1,210 +1,162 @@
1
- import os
2
- import numpy as np
3
- import random
4
  from pathlib import Path
5
  from PIL import Image
 
6
  import streamlit as st
7
  from huggingface_hub import InferenceClient, AsyncInferenceClient
8
  from gradio_client import Client, handle_file
9
- import asyncio
10
- from concurrent.futures import ThreadPoolExecutor
11
  import yaml
12
- import cv2
13
- import dlib
14
-
15
- try:
16
- with open("config.yaml", "r") as file:
17
- credentials = yaml.safe_load(file)
18
- except Exception as e:
19
- st.error(f"Error al cargar el archivo de configuración: {e}")
20
- credentials = {"username": "", "password": ""}
21
 
22
  MAX_SEED = np.iinfo(np.int32).max
 
23
  HF_TOKEN_UPSCALER = os.environ.get("HF_TOKEN_UPSCALER")
24
- client = AsyncInferenceClient()
25
- llm_client = InferenceClient("mistralai/Mixtral-8x7B-Instruct-v0.1")
26
- DATA_PATH = Path("./data")
27
- DATA_PATH.mkdir(exist_ok=True)
28
-
29
- def run_async(func):
30
- loop = asyncio.new_event_loop()
31
- asyncio.set_event_loop(loop)
32
- executor = ThreadPoolExecutor(max_workers=1)
33
- result = loop.run_in_executor(executor, func)
34
- return loop.run_until_complete(result)
35
-
36
- async def generate_image(combined_prompt, model, width, height, scales, steps, seed):
37
- seed = int(seed) if seed != -1 else random.randint(0, MAX_SEED)
38
- try:
39
- image = await client.text_to_image(
40
- prompt=combined_prompt, height=height, width=width, guidance_scale=scales,
41
- num_inference_steps=steps, model=model
42
- )
43
- return image, seed
44
- except Exception as e:
45
- return f"Error al generar imagen: {e}", None
46
-
47
- def get_upscale_finegrain(prompt, img_path, upscale_factor):
48
- try:
49
- client = Client("finegrain/finegrain-image-enhancer", hf_token=HF_TOKEN_UPSCALER)
50
- result = client.predict(
51
- input_image=handle_file(img_path), prompt=prompt, upscale_factor=upscale_factor
52
- )
53
- return result[1] if isinstance(result, list) and len(result) > 1 else None
54
- except Exception:
55
- return None
56
 
57
- def save_prompt(prompt_text, seed):
58
- try:
59
- prompt_file_path = DATA_PATH / f"prompt_{seed}.txt"
60
- with open(prompt_file_path, "w") as prompt_file:
61
- prompt_file.write(prompt_text)
62
- return prompt_file_path
63
- except Exception as e:
64
- st.error(f"Error al guardar el prompt: {e}")
65
- return None
66
 
67
- async def gen(prompt, basemodel, width, height, scales, steps, seed, upscale_factor, process_upscale, process_enhancer, language):
68
- combined_prompt = f"{prompt} {await improve_prompt(prompt, language) if process_enhancer else ''}".strip()
69
- seed = int(seed) if seed != -1 else random.randint(0, MAX_SEED)
70
- progress_bar = st.progress(0)
71
 
72
- image, seed = await generate_image(combined_prompt, basemodel, width, height, scales, steps, seed)
73
- progress_bar.progress(50)
74
 
75
- if isinstance(image, str) and image.startswith("Error"):
76
- progress_bar.empty()
77
- return [image, None, combined_prompt]
 
78
 
79
- image_path = save_image(image, seed)
80
- prompt_file_path = save_prompt(combined_prompt, seed)
 
 
 
81
 
 
 
 
 
 
82
  if process_upscale:
83
  upscale_image_path = get_upscale_finegrain(combined_prompt, image_path, upscale_factor)
84
  if upscale_image_path:
85
  Image.open(upscale_image_path).save(DATA_PATH / f"upscale_image_{seed}.jpg", format="JPEG")
86
- progress_bar.progress(100)
87
  image_path.unlink()
88
  return [str(DATA_PATH / f"upscale_image_{seed}.jpg"), str(prompt_file_path)]
89
-
90
- progress_bar.progress(100)
91
  return [str(image_path), str(prompt_file_path)]
92
 
93
  async def improve_prompt(prompt, language):
94
- instruction = (
95
- "Con esta idea, describe en español un prompt detallado de txt2img en un máximo de 500 caracteres, "
96
- "con iluminación, atmósfera, elementos cinematográficos y en su caso personajes..."
97
- if language == "es" else
98
- "With this idea, describe in English a detailed txt2img prompt in 500 characters at most, "
99
- "add illumination, atmosphere, cinematic elements, and characters if needed..."
100
- )
101
- formatted_prompt = f"{prompt}: {instruction}"
102
- response = llm_client.text_generation(formatted_prompt, max_new_tokens=500)
103
- improved_text = response.get('generated_text', '').strip() if 'generated_text' in response else response.strip()
104
- return improved_text[:500] if len(improved_text) > 500 else improved_text
105
 
106
  def save_image(image, seed):
107
- try:
108
- image_path = DATA_PATH / f"image_{seed}.jpg"
109
- image.save(image_path, format="JPEG")
110
- return image_path
111
- except Exception as e:
112
- st.error(f"Error al guardar la imagen: {e}")
113
- return None
 
 
114
 
115
  def get_storage():
116
  files = [file for file in DATA_PATH.glob("*.jpg") if file.is_file()]
117
- files.sort(key=lambda x: x.stat().st_mtime, reverse=True)
118
- usage = sum(file.stat().st_size for file in files)
119
- return [str(file.resolve()) for file in files], f"Uso total: {usage / (1024.0 ** 3):.3f}GB"
120
-
121
- def get_prompts():
122
- prompt_files = [file for file in DATA_PATH.glob("*.txt") if file.is_file()]
123
- return {file.stem.replace("prompt_", ""): file for file in prompt_files}
124
 
125
  def delete_image(image_path):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  try:
127
- if Path(image_path).exists():
128
- Path(image_path).unlink()
129
- st.success(f"Imagen {image_path} borrada.")
130
- else:
131
- st.error("El archivo de imagen no existe.")
132
- except Exception as e:
133
- st.error(f"Error al borrar la imagen: {e}")
134
-
135
- def swap_faces(image_path):
136
- try:
137
- image = cv2.imread(str(image_path))
138
- detector = dlib.get_frontal_face_detector()
139
- predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
140
- faces = detector(image)
141
-
142
- if len(faces) != 2:
143
- st.error("Se necesitan exactamente dos caras para realizar el intercambio.")
144
- return None
145
-
146
- landmarks1 = predictor(image, faces[0])
147
- landmarks2 = predictor(image, faces[1])
148
- points1 = np.array([[p.x, p.y] for p in landmarks1.parts()])
149
- points2 = np.array([[p.x, p.y] for p in landmarks2.parts()])
150
-
151
- mask1 = np.zeros(image.shape[:2], dtype=np.uint8)
152
- cv2.fillConvexPoly(mask1, cv2.convexHull(points1), 255)
153
- mask2 = np.zeros(image.shape[:2], dtype=np.uint8)
154
- cv2.fillConvexPoly(mask2, cv2.convexHull(points2), 255)
155
- face1 = cv2.bitwise_and(image, image, mask=mask1)
156
- face2 = cv2.bitwise_and(image, image, mask=mask2)
157
- image[mask1 == 255] = face2[mask1 == 255]
158
- image[mask2 == 255] = face1[mask2 == 255]
159
- swapped_image_path = DATA_PATH / f"swapped_image_{Path(image_path).stem}.jpg"
160
- cv2.imwrite(str(swapped_image_path), image)
161
- return str(swapped_image_path)
162
- except Exception as e:
163
- st.error(f"Error en el face swap: {e}")
164
- return None
165
-
166
- def main():
167
  st.set_page_config(layout="wide")
 
 
168
  prompt = st.sidebar.text_input("Descripción de la imagen", max_chars=900)
169
- process_enhancer = st.sidebar.checkbox("Mejorar Prompt", value=False)
170
- language = st.sidebar.selectbox("Idioma", ["en", "es"])
171
- basemodel = st.sidebar.selectbox("Modelo Base", ["black-forest-labs/FLUX.1-DEV", "black-forest-labs/FLUX.1-schnell"])
172
- format_option = st.sidebar.selectbox("Formato", ["9:16", "16:9"])
173
- process_upscale = st.sidebar.checkbox("Procesar Escalador", value=False)
174
- upscale_factor = st.sidebar.selectbox("Factor de Escala", [2, 4, 8], index=0)
175
- scales = st.sidebar.slider("Escalado", 1, 20, 10)
176
- steps = st.sidebar.slider("Pasos", 1, 100, 20)
177
- seed = st.sidebar.number_input("Semilla", value=-1)
178
-
179
- width, height = (720, 1280) if format_option == "9:16" else (1280, 720)
180
 
 
181
  if st.sidebar.button("Generar Imagen"):
182
- with st.spinner("Mejorando y generando imagen..."):
183
- result = asyncio.run(gen(prompt, basemodel, width, height, scales, steps, seed, upscale_factor, process_upscale, process_enhancer, language))
184
- image_paths, prompt_file = result[0], result[1]
185
-
186
- st.write(f"Image paths: {image_paths}")
187
-
188
- if image_paths and Path(image_paths).exists():
189
- st.image(image_paths, caption="Imagen generada", use_column_width=True)
190
- if prompt_file and Path(prompt_file).exists():
191
- with open(prompt_file, "r") as file:
192
- st.text_area("Prompt utilizado", file.read(), height=150)
193
-
194
- st.sidebar.header("Galería de Imágenes")
195
- image_storage, usage = get_storage()
196
- st.sidebar.write(usage)
197
- for img_path in image_storage:
198
- st.sidebar.image(img_path, width=100)
199
 
200
- if st.sidebar.button("Borrar Imagen"):
201
- delete_image(image_paths)
202
-
203
- if st.sidebar.button("Intercambiar Caras"):
204
- if image_paths:
205
- swapped_path = swap_faces(image_paths)
206
- if swapped_path:
207
- st.image(swapped_path, caption="Imagen con caras intercambiadas", use_column_width=True)
208
 
209
  if __name__ == "__main__":
210
- main()
 
1
+ import os, random, asyncio, numpy as np
 
 
2
  from pathlib import Path
3
  from PIL import Image
4
+ from insightface.app import FaceAnalysis
5
  import streamlit as st
6
  from huggingface_hub import InferenceClient, AsyncInferenceClient
7
  from gradio_client import Client, handle_file
 
 
8
  import yaml
9
+ import insightface
 
 
 
 
 
 
 
 
10
 
11
  MAX_SEED = np.iinfo(np.int32).max
12
+ DATA_PATH = Path("./data"); DATA_PATH.mkdir(exist_ok=True)
13
  HF_TOKEN_UPSCALER = os.environ.get("HF_TOKEN_UPSCALER")
14
+ client, llm_client = AsyncInferenceClient(), InferenceClient("mistralai/Mixtral-8x7B-Instruct-v0.1")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
+ try:
17
+ credentials = yaml.safe_load(open("config.yaml"))
18
+ except Exception as e:
19
+ st.error(f"Error al cargar config: {e}"); credentials = {"username": "", "password": ""}
 
 
 
 
 
20
 
21
+ def prepare_face_app():
22
+ app = FaceAnalysis(name='buffalo_l'); app.prepare(ctx_id=0, det_size=(640, 640))
23
+ return app, insightface.model_zoo.get_model('onix.onnx')
 
24
 
25
+ app, swapper = prepare_face_app()
 
26
 
27
+ async def generate_image(prompt, model, w, h, scale, steps, seed):
28
+ seed = random.randint(0, MAX_SEED) if seed == -1 else seed
29
+ image = await client.text_to_image(prompt=prompt, height=h, width=w, guidance_scale=scale, num_inference_steps=steps, model=model)
30
+ return image, seed if not isinstance(image, str) else (None, None)
31
 
32
+ def get_upscale_finegrain(prompt, img_path, upscale_factor):
33
+ try:
34
+ result = Client("finegrain/finegrain-image-enhancer", hf_token=HF_TOKEN_UPSCALER).predict(input_image=handle_file(img_path), prompt=prompt, upscale_factor=upscale_factor)
35
+ return result[1] if isinstance(result, list) and len(result) > 1 else None
36
+ except Exception: return None
37
 
38
+ async def gen(prompt, basemodel, w, h, scales, steps, seed, upscale_factor, process_upscale, process_enhancer, language):
39
+ combined_prompt = f"{prompt} {await improve_prompt(prompt, language)}" if process_enhancer else prompt
40
+ image, seed = await generate_image(combined_prompt, basemodel, w, h, scales, steps, seed)
41
+ if image is None: return ["Error al generar imagen", None, combined_prompt]
42
+ image_path = save_image(image, seed); prompt_file_path = save_prompt(combined_prompt, seed)
43
  if process_upscale:
44
  upscale_image_path = get_upscale_finegrain(combined_prompt, image_path, upscale_factor)
45
  if upscale_image_path:
46
  Image.open(upscale_image_path).save(DATA_PATH / f"upscale_image_{seed}.jpg", format="JPEG")
 
47
  image_path.unlink()
48
  return [str(DATA_PATH / f"upscale_image_{seed}.jpg"), str(prompt_file_path)]
 
 
49
  return [str(image_path), str(prompt_file_path)]
50
 
51
  async def improve_prompt(prompt, language):
52
+ instruction = "With this idea, describe in English a detailed txt2img prompt in 500 characters at most..." if language == "en" else "Con esta idea, describe en español un prompt detallado de txt2img..."
53
+ response = await llm_client.text_generation(f"{prompt}: {instruction}", max_new_tokens=500)
54
+ return response.get('generated_text', '').strip()[:500]
 
 
 
 
 
 
 
 
55
 
56
  def save_image(image, seed):
57
+ if image.mode == 'RGBA': image = image.convert('RGB')
58
+ image_path = DATA_PATH / f"image_{seed}.jpg"
59
+ image.save(image_path, format="JPEG")
60
+ return image_path
61
+
62
+ def save_prompt(prompt_text, seed):
63
+ prompt_file_path = DATA_PATH / f"prompt_{seed}.txt"
64
+ open(prompt_file_path, "w").write(prompt_text)
65
+ return prompt_file_path
66
 
67
  def get_storage():
68
  files = [file for file in DATA_PATH.glob("*.jpg") if file.is_file()]
69
+ total_size = sum([file.stat().st_size for file in files]) / (1024.0 ** 3)
70
+ return files, f"Uso total: {total_size:.3f} GB"
 
 
 
 
 
71
 
72
  def delete_image(image_path):
73
+ try:
74
+ Path(image_path).unlink(); st.success(f"Imagen {image_path} borrada.")
75
+ except Exception as e: st.error(f"Error al borrar imagen: {e}")
76
+
77
+ def delete_all_images():
78
+ for file in DATA_PATH.glob("*.jpg"): file.unlink(); st.success("Todas las imágenes han sido borradas.")
79
+
80
+ def authenticate_user(username, password, credentials):
81
+ return username == credentials["username"] and password == credentials["password"]
82
+
83
+ def login_form(credentials):
84
+ if 'authenticated' not in st.session_state: st.session_state['authenticated'] = False
85
+ if not st.session_state['authenticated']:
86
+ username = st.text_input("Usuario"); password = st.text_input("Contraseña", type='password')
87
+ if st.button("Iniciar Sesión"):
88
+ if authenticate_user(username, password, credentials):
89
+ st.session_state['authenticated'] = True; st.success("Inicio de sesión exitoso.")
90
+ else: st.error("Credenciales incorrectas.")
91
+
92
+ def upload_image():
93
+ uploaded_file = st.sidebar.file_uploader("Sube una imagen", type=["png", "jpg", "jpeg"])
94
+ if uploaded_file:
95
+ image_path = DATA_PATH / uploaded_file.name
96
+ with open(image_path, "wb") as f: f.write(uploaded_file.getbuffer())
97
+ st.sidebar.success(f"Imagen {uploaded_file.name} cargada correctamente.")
98
+ return image_path, save_prompt("#uploadedbyuser", image_path.stem)
99
+ return None
100
+
101
+ def gallery():
102
+ files, usage = get_storage()
103
+ st.sidebar.write(f"{usage}")
104
+ if st.sidebar.button("Borrar Todas las Imágenes"): delete_all_images()
105
+ cols = st.columns(6)
106
+ for idx, file in enumerate(files):
107
+ with cols[idx % 6]:
108
+ st.image(str(file))
109
+ try:
110
+ prompt_file_path = DATA_PATH / f"prompt_{file.stem.split('_')[-1]}.txt"
111
+ st.write(f"Prompt: {open(prompt_file_path).read()}")
112
+ except FileNotFoundError:
113
+ st.write("Prompt no encontrado.")
114
+ st.button(f"Borrar Imagen {file.name}", on_click=delete_image, args=(file,))
115
+ if st.button(f"Swap Face en {file.name}"): upload_source_and_swap(file)
116
+
117
+ def face_swap(image_path, source_image_path):
118
  try:
119
+ img_dest, img_src = Image.open(image_path), Image.open(source_image_path)
120
+ faces = app.get(img_src)
121
+ if not faces: st.error("No se encontraron caras en la imagen source."); return None
122
+ swapped_img = swapper.get(img_dest, faces[0])
123
+ swapped_img_path = DATA_PATH / f"swapped_{Path(image_path).stem}.jpg"
124
+ swapped_img.save(swapped_img_path, format="JPEG")
125
+ return swapped_img_path
126
+ except Exception as e: st.error(f"Error en face swap: {e}"); return None
127
+
128
+ def upload_source_and_swap(image_path):
129
+ source_image = st.file_uploader("Sube la imagen source para face swap", type=["png", "jpg", "jpeg"])
130
+ if source_image:
131
+ source_image_path = DATA_PATH / source_image.name
132
+ with open(source_image_path, "wb") as f: f.write(source_image.getbuffer())
133
+ st.success(f"Imagen source {source_image.name} cargada correctamente.")
134
+ swapped_image_path = face_swap(image_path, source_image_path)
135
+ if swapped_image_path: st.image(str(swapped_image_path), caption="Imagen con Face Swap", use_column_width=True)
136
+
137
+ async def main():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  st.set_page_config(layout="wide")
139
+ login_form(credentials)
140
+ if not st.session_state['authenticated']: st.warning("Por favor, inicia sesión para acceder a la aplicación."); return
141
  prompt = st.sidebar.text_input("Descripción de la imagen", max_chars=900)
142
+ process_enhancer, language = st.sidebar.checkbox("Mejorar Prompt", value=False), st.sidebar.selectbox("Idioma", ["en", "es"])
143
+ basemodel, format_option, process_upscale = st.sidebar.selectbox("Modelo Base", ["black-forest-labs/FLUX.1-DEV", "black-forest-labs/FLUX.1-schnell"]), st.sidebar.selectbox("Formato", ["9:16", "16:9"]), st.sidebar.checkbox("Procesar Escalador", value=False)
144
+ upscale_factor, scales, steps, seed = st.sidebar.selectbox("Factor de Escala", [2, 4, 8], index=0), st.sidebar.slider("Escalado", 1, 20, 10), st.sidebar.slider("Pasos", 1, 100, 20), st.sidebar.number_input("Semilla", value=-1)
145
+ w, h = (1080, 1920) if format_option == "9:16" else (1920, 1080)
146
+ upload_image()
 
 
 
 
 
 
147
 
148
+ image_path, prompt_file_path = None, None
149
  if st.sidebar.button("Generar Imagen"):
150
+ with st.spinner("Generando..."):
151
+ image_path, prompt_file_path = await gen(prompt, basemodel, w, h, scales, steps, seed, upscale_factor, process_upscale, process_enhancer, language)
152
+ if image_path:
153
+ st.image(image_path, caption="Imagen Generada")
154
+ st.write(f"Prompt: {open(prompt_file_path).read()}")
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
+ if image_path:
157
+ st.success("Imagen generada y almacenada.")
158
+
159
+ gallery()
 
 
 
 
160
 
161
  if __name__ == "__main__":
162
+ asyncio.run(main())