Adanbalf commited on
Commit
a56b134
·
verified ·
1 Parent(s): ec6c728

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +41 -84
app.py CHANGED
@@ -1,36 +1,32 @@
1
- import os
2
  import gradio as gr
3
  import torch
4
- from PIL import Image
5
  from transformers import AutoProcessor, AutoModelForVision2Seq
6
- import requests, base64, re
 
7
  from io import BytesIO
8
 
9
- # Configuración del modelo
10
- LOCAL_MODEL_ID = "lmms-lab/llava-onevision-1.5-8b-instruct"
11
- HF_API_URL = f"https://api-inference.huggingface.co/models/{LOCAL_MODEL_ID}"
12
- HF_API_KEY = os.getenv("API_KEY")
13
-
14
- model, processor = None, None
15
- use_local = False
16
-
17
- try:
18
- print("⏳ Cargando modelo local...")
19
- processor = AutoProcessor.from_pretrained(LOCAL_MODEL_ID, trust_remote_code=True)
20
- model = AutoModelForVision2Seq.from_pretrained(
21
- LOCAL_MODEL_ID,
22
- trust_remote_code=True,
23
- torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
24
- device_map="auto"
25
- )
26
- use_local = True
27
- print("✅ Modelo local cargado correctamente.")
28
- except Exception as e:
29
- print(f"⚠️ No se pudo cargar localmente: {e}")
30
- print("➡️ Se usará la API de Hugging Face.")
31
-
32
- # ---------- Utilidades ----------
33
  def extract_macros(text):
 
34
  def find_value(keyword):
35
  m = re.search(rf"{keyword}[^0-9]*([0-9]+)", text.lower())
36
  return int(m.group(1)) if m else 0
@@ -38,7 +34,9 @@ def extract_macros(text):
38
  kcal = p * 4 + c * 4 + f * 9 if any([p, c, f]) else 0
39
  return {"protein": p, "carbs": c, "fat": f, "kcal": kcal}
40
 
 
41
  def build_macro_card(macros):
 
42
  if not any(macros.values()):
43
  return "<div class='card'>⚖️ No se pudieron estimar los macros.</div>"
44
 
@@ -63,65 +61,25 @@ def build_macro_card(macros):
63
  </div>
64
  """
65
 
66
- # ---------- Lógica principal ----------
67
-
68
- def analyze_food(image, text_prompt=""):
69
- if image is None:
70
- return "<div class='card'>Subí una imagen del plato 🍽️</div>"
71
- if not text_prompt.strip():
72
- text_prompt = "Describe esta comida y estima calorías, proteínas, carbohidratos y grasas."
73
 
 
 
74
  try:
75
- if use_local:
76
- inputs = processor(text=text_prompt, images=image, return_tensors="pt").to(model.device)
77
- out = model.generate(**inputs, max_new_tokens=400)
78
- answer = processor.decode(out[0], skip_special_tokens=True)
79
- else:
80
- buffered = BytesIO()
81
- image.save(buffered, format="JPEG")
82
- img_b64 = base64.b64encode(buffered.getvalue()).decode("utf-8")
83
- headers = {
84
- "Authorization": f"Bearer {HF_API_KEY}",
85
- "Content-Type": "application/json"
86
- }
87
- data = {
88
- "inputs": {
89
- "text": text_prompt,
90
- "image": f"data:image/jpeg;base64,{img_b64}"
91
- }
92
- }
93
-
94
- print("📡 Enviando request a:", HF_API_URL)
95
- r = requests.post(HF_API_URL, headers=headers, json=data)
96
-
97
- print("📩 Status code:", r.status_code)
98
- print("🧾 Respuesta cruda:", r.text[:500])
99
-
100
- # si la respuesta viene vacía
101
- if not r.text.strip():
102
- return "<div class='card error'>⚠️ El modelo no devolvió respuesta. Espera unos segundos y reintenta.</div>"
103
-
104
- # si no es JSON válido
105
- try:
106
- resp = r.json()
107
- except Exception as e:
108
- return f"<div class='card error'>⚠️ Error al decodificar respuesta: {e}<br><pre>{r.text[:300]}</pre></div>"
109
-
110
- # Hugging Face a veces devuelve dict con 'error' o 'generated_text'
111
- if isinstance(resp, dict) and "error" in resp:
112
- return f"<div class='card error'>⚠️ Error del modelo: {resp['error']}</div>"
113
-
114
- answer = str(resp)
115
 
116
  macros = extract_macros(answer)
117
  card = build_macro_card(macros)
118
  return f"<div class='desc'>{answer}</div>{card}"
119
 
120
  except Exception as e:
121
- return f"<div class='card error'>⚠️ Error general: {e}</div>"
122
 
 
 
 
123
 
124
- # ---------- Interfaz ----------
125
  def build_interface():
126
  with gr.Blocks(css="""
127
  /* --- DARK NEON THEME --- */
@@ -161,18 +119,16 @@ button {
161
  }
162
  button:hover {opacity:0.8;}
163
  """) as demo:
164
- gr.Markdown(
165
- """
166
- <h1>💜 NasFit Vision AI</h1>
167
- <p>Analiza tus comidas con IA y obtené tu ficha nutricional instantánea.</p>
168
- """
169
- )
170
 
171
  with gr.Row():
172
  with gr.Column(scale=1):
173
  img = gr.Image(label="📸 Imagen del plato", type="pil")
174
- txt = gr.Textbox(label="💬 Instrucción (opcional)",
175
- placeholder="Ej: ¿Cuántas calorías tiene este plato?")
176
  btn = gr.Button("🔍 Analizar", variant="primary")
177
  with gr.Column(scale=1):
178
  out = gr.HTML(label="🧠 Resultado")
@@ -181,6 +137,7 @@ button:hover {opacity:0.8;}
181
 
182
  return demo
183
 
 
184
  if __name__ == "__main__":
185
  demo = build_interface()
186
  demo.launch()
 
 
1
  import gradio as gr
2
  import torch
 
3
  from transformers import AutoProcessor, AutoModelForVision2Seq
4
+ from PIL import Image
5
+ import re
6
  from io import BytesIO
7
 
8
+ # ============================================
9
+ # 🔮 CONFIGURACIÓN DEL MODELO
10
+ # ============================================
11
+
12
+ MODEL_ID = "lmms-lab/llava-onevision-1.5-8b-instruct"
13
+
14
+ print("⏳ Cargando modelo local con trust_remote_code=True...")
15
+ processor = AutoProcessor.from_pretrained(MODEL_ID, trust_remote_code=True)
16
+ model = AutoModelForVision2Seq.from_pretrained(
17
+ MODEL_ID,
18
+ trust_remote_code=True,
19
+ torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
20
+ device_map="auto"
21
+ )
22
+ print("✅ Modelo cargado correctamente en modo local.")
23
+
24
+ # ============================================
25
+ # 🧠 FUNCIONES DE ANÁLISIS
26
+ # ============================================
27
+
 
 
 
 
28
  def extract_macros(text):
29
+ """Extrae proteínas, carbohidratos y grasas del texto generado."""
30
  def find_value(keyword):
31
  m = re.search(rf"{keyword}[^0-9]*([0-9]+)", text.lower())
32
  return int(m.group(1)) if m else 0
 
34
  kcal = p * 4 + c * 4 + f * 9 if any([p, c, f]) else 0
35
  return {"protein": p, "carbs": c, "fat": f, "kcal": kcal}
36
 
37
+
38
  def build_macro_card(macros):
39
+ """Genera el HTML visual con barras de progreso tipo dashboard."""
40
  if not any(macros.values()):
41
  return "<div class='card'>⚖️ No se pudieron estimar los macros.</div>"
42
 
 
61
  </div>
62
  """
63
 
 
 
 
 
 
 
 
64
 
65
+ def analyze_food(image, text_prompt="Describe esta comida y estima sus calorías, proteínas, carbohidratos y grasas."):
66
+ """Procesa la imagen localmente con el modelo y devuelve descripción + macros."""
67
  try:
68
+ inputs = processor(text=text_prompt, images=image, return_tensors="pt").to(model.device)
69
+ out = model.generate(**inputs, max_new_tokens=400)
70
+ answer = processor.decode(out[0], skip_special_tokens=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
  macros = extract_macros(answer)
73
  card = build_macro_card(macros)
74
  return f"<div class='desc'>{answer}</div>{card}"
75
 
76
  except Exception as e:
77
+ return f"<div class='card error'>⚠️ Error: {e}</div>"
78
 
79
+ # ============================================
80
+ # 💅 INTERFAZ DE USUARIO (Glass Neon)
81
+ # ============================================
82
 
 
83
  def build_interface():
84
  with gr.Blocks(css="""
85
  /* --- DARK NEON THEME --- */
 
119
  }
120
  button:hover {opacity:0.8;}
121
  """) as demo:
122
+
123
+ gr.Markdown("""
124
+ <h1>💜 NasFit Vision AI</h1>
125
+ <p>Analiza tus comidas con IA y obtené tu ficha nutricional instantánea.</p>
126
+ """)
 
127
 
128
  with gr.Row():
129
  with gr.Column(scale=1):
130
  img = gr.Image(label="📸 Imagen del plato", type="pil")
131
+ txt = gr.Textbox(label="💬 Instrucción (opcional)", placeholder="Ej: ¿Cuántas calorías tiene este plato?")
 
132
  btn = gr.Button("🔍 Analizar", variant="primary")
133
  with gr.Column(scale=1):
134
  out = gr.HTML(label="🧠 Resultado")
 
137
 
138
  return demo
139
 
140
+
141
  if __name__ == "__main__":
142
  demo = build_interface()
143
  demo.launch()