File size: 5,402 Bytes
21e657e 1014901 c3fead6 21e657e a56b134 c3fead6 a56b134 21e657e a56b134 21e657e a56b134 21e657e a56b134 21e657e a56b134 ce0c277 a56b134 ce0c277 a56b134 ce0c277 a56b134 ce0c277 c113217 a56b134 1014901 a56b134 ce0c277 1014901 c3fead6 a56b134 ec6c728 a56b134 21e657e 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 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# ============================================
# 💜 NasFit Vision AI — Dark Neon Edition
# ============================================
# 🚧 Asegúrate de tener este contenido en tu requirements.txt:
# transformers>=4.43.0
# torch
# accelerate
# gradio
# Pillow
import gradio as gr
import torch
from transformers import AutoModelForVision2Seq
from transformers import LlavaOneVisionProcessor # 👈 clase correcta
from PIL import Image
import re
# ============================================
# 🔮 CARGA DEL MODELO LOCAL
# ============================================
MODEL_ID = "lmms-lab/llava-onevision-1.5-8b-instruct"
print("⏳ Cargando modelo local con trust_remote_code=True...")
processor = LlavaOneVisionProcessor.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 LLaVA-OneVision cargado correctamente.")
# ============================================
# 🧠 FUNCIONES DE ANÁLISIS NUTRICIONAL
# ============================================
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 (Purple Glassmorphism)
# ============================================
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() |