File size: 5,065 Bytes
1014901 c3fead6 a56b134 ce0c277 c3fead6 a56b134 ce0c277 a56b134 ce0c277 a56b134 ce0c277 a56b134 ce0c277 c113217 a56b134 1014901 a56b134 ce0c277 1014901 c3fead6 a56b134 ec6c728 a56b134 c3fead6 ce0c277 a56b134 1014901 c3fead6 ce0c277 a56b134 ce0c277 c3fead6 ce0c277 1014901 ce0c277 1014901 ce0c277 1014901 a56b134 1014901 c3fead6 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
import gradio as gr
import torch
from transformers import AutoProcessor, AutoModelForVision2Seq
from PIL import Image
import re
from io import BytesIO
# ============================================
# 🔮 CONFIGURACIÓN DEL MODELO
# ============================================
MODEL_ID = "lmms-lab/llava-onevision-1.5-8b-instruct"
print("⏳ Cargando modelo local con trust_remote_code=True...")
processor = AutoProcessor.from_pretrained(MODEL_ID, trust_remote_code=True)
model = AutoModelForVision2Seq.from_pretrained(
MODEL_ID,
trust_remote_code=True,
torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
device_map="auto"
)
print("✅ Modelo cargado correctamente en modo local.")
# ============================================
# 🧠 FUNCIONES DE ANÁLISIS
# ============================================
def extract_macros(text):
"""Extrae proteínas, carbohidratos y grasas del texto generado."""
def find_value(keyword):
m = re.search(rf"{keyword}[^0-9]*([0-9]+)", text.lower())
return int(m.group(1)) if m else 0
p, c, f = find_value("prote"), find_value("carb"), find_value("gras")
kcal = p * 4 + c * 4 + f * 9 if any([p, c, f]) else 0
return {"protein": p, "carbs": c, "fat": f, "kcal": kcal}
def build_macro_card(macros):
"""Genera el HTML visual con barras de progreso tipo dashboard."""
if not any(macros.values()):
return "<div class='card'>⚖️ No se pudieron estimar los macros.</div>"
def bar_html(value, color):
width = min(value, 100)
return f"""
<div class='bar-bg'>
<div class='bar-fill' style='width:{width}%; background:{color};'></div>
</div>
"""
return f"""
<div class='card'>
<h2>🍽️ Estimación Nutricional</h2>
<div class='macro'><span>💪 Proteínas</span><span>{macros['protein']} g</span></div>
{bar_html(macros['protein'], '#b25eff')}
<div class='macro'><span>🥔 Carbohidratos</span><span>{macros['carbs']} g</span></div>
{bar_html(macros['carbs'], '#00f0ff')}
<div class='macro'><span>🥑 Grasas</span><span>{macros['fat']} g</span></div>
{bar_html(macros['fat'], '#ff5efb')}
<div class='macro kcal'><span>🔥 Calorías Totales</span><span>{macros['kcal']} kcal</span></div>
</div>
"""
def analyze_food(image, text_prompt="Describe esta comida y estima sus calorías, proteínas, carbohidratos y grasas."):
"""Procesa la imagen localmente con el modelo y devuelve descripción + macros."""
try:
inputs = processor(text=text_prompt, images=image, return_tensors="pt").to(model.device)
out = model.generate(**inputs, max_new_tokens=400)
answer = processor.decode(out[0], skip_special_tokens=True)
macros = extract_macros(answer)
card = build_macro_card(macros)
return f"<div class='desc'>{answer}</div>{card}"
except Exception as e:
return f"<div class='card error'>⚠️ Error: {e}</div>"
# ============================================
# 💅 INTERFAZ DE USUARIO (Glass Neon)
# ============================================
def build_interface():
with gr.Blocks(css="""
/* --- DARK NEON THEME --- */
body {
background: radial-gradient(circle at 20% 20%, #0d001f, #000);
color: #fff;
font-family: 'Inter', sans-serif;
}
.gradio-container {background: transparent !important;}
.card {
backdrop-filter: blur(12px);
background: rgba(30, 0, 60, 0.3);
border: 1px solid rgba(200, 100, 255, 0.2);
border-radius: 16px;
padding: 1.2em;
margin-top: 1em;
box-shadow: 0 0 25px rgba(180, 0, 255, 0.15);
}
h1,h2 {color:#c18fff;}
.bar-bg {
width:100%; height:8px; border-radius:6px;
background:rgba(255,255,255,0.1); margin:4px 0 12px 0;
overflow:hidden;
}
.bar-fill {height:100%; border-radius:6px; transition:width 1s ease;}
.macro {display:flex; justify-content:space-between; font-size:0.95em;}
.kcal {font-weight:600; color:#ffb3ff;}
.desc {
background:rgba(255,255,255,0.05);
padding:1em; border-radius:10px; line-height:1.5em;
box-shadow:inset 0 0 20px rgba(180,0,255,0.1);
}
button {
background:linear-gradient(90deg,#b25eff,#00f0ff);
color:#fff; border:none; border-radius:12px;
font-weight:600; transition:opacity .2s;
}
button:hover {opacity:0.8;}
""") as demo:
gr.Markdown("""
<h1>💜 NasFit Vision AI</h1>
<p>Analiza tus comidas con IA y obtené tu ficha nutricional instantánea.</p>
""")
with gr.Row():
with gr.Column(scale=1):
img = gr.Image(label="📸 Imagen del plato", type="pil")
txt = gr.Textbox(label="💬 Instrucción (opcional)", placeholder="Ej: ¿Cuántas calorías tiene este plato?")
btn = gr.Button("🔍 Analizar", variant="primary")
with gr.Column(scale=1):
out = gr.HTML(label="🧠 Resultado")
btn.click(analyze_food, [img, txt], out)
return demo
if __name__ == "__main__":
demo = build_interface()
demo.launch() |