Spaces:
Sleeping
Sleeping
| """ | |
| app.py — Identificador Caribicus warrenii (Hugging Face Spaces) | |
| Versión para servidor: con login (usuario/contraseña vía secretos del Space) | |
| y el modelo afinado. El cómputo ocurre en el Space, no en tu PC. | |
| """ | |
| import os | |
| import pandas as pd | |
| import gradio as gr | |
| import caribicus_finetuned as cf | |
| print("Cargando modelo afinado Caribicus...") | |
| try: | |
| cf._cargar_modelo() | |
| BASE = cf.cargar_base() | |
| U_CONF, U_DUD = cf.cargar_umbrales() | |
| N = len(BASE) | |
| OK = True | |
| print(f"Base: {N} individuos | Umbrales: {U_CONF}/{U_DUD}") | |
| except Exception as exc: | |
| BASE = {}; N = 0; OK = False; ERR = str(exc); U_CONF, U_DUD = 65.0, 50.0 | |
| print("ADVERTENCIA:", exc) | |
| COLOR = {'CONFIRMADO': '#27ae60', 'DUDOSO': '#e67e22', 'NUEVO': '#e74c3c'} | |
| ICONO = {'CONFIRMADO': '✅', 'DUDOSO': '⚠️', 'NUEVO': '❌'} | |
| ETIQ = {'CONFIRMADO': 'Confirmado', | |
| 'DUDOSO': 'Dudoso — requiere revisión manual', | |
| 'NUEVO': 'Nuevo individuo no registrado'} | |
| def _barra(pct, color, alto=10): | |
| return (f"<div style='background:#e9ecef;border-radius:6px;height:{alto}px;margin:4px 0 8px'>" | |
| f"<div style='width:{min(pct,100):.1f}%;background:{color};height:{alto}px;" | |
| f"border-radius:6px'></div></div>") | |
| def _html_resultado(estado, c1, p1, c2, p2): | |
| color = COLOR[estado] | |
| bloque2 = "" | |
| if c2: | |
| bloque2 = (f"<div style='font-size:.8em;color:#888;margin:10px 0 2px'>SEGUNDO CANDIDATO</div>" | |
| f"<div style='display:flex;justify-content:space-between'>" | |
| f"<span style='font-weight:600'>{c2}</span><span style='color:#555'>{p2:.1f} %</span></div>" | |
| f"{_barra(p2,'#95a5a6',7)}") | |
| return (f"<div style='font-family:sans-serif;padding:20px;border-radius:10px;" | |
| f"border-left:5px solid {color};background:#fafafa;box-shadow:0 1px 4px rgba(0,0,0,.08)'>" | |
| f"<div style='font-size:1.4em;font-weight:700;color:{color}'>{ICONO[estado]} {ETIQ[estado]}</div>" | |
| f"<hr style='border:none;border-top:1px solid #eee;margin:12px 0'>" | |
| f"<div style='font-size:.8em;color:#888;margin-bottom:2px'>MEJOR CANDIDATO</div>" | |
| f"<div style='display:flex;justify-content:space-between;align-items:baseline'>" | |
| f"<span style='font-size:1.25em;font-weight:700'>{c1}</span>" | |
| f"<span style='font-size:1.25em;font-weight:700;color:{color}'>{p1:.1f} %</span></div>" | |
| f"{_barra(p1,color)}{bloque2}</div>") | |
| def predecir(imagen_path): | |
| if imagen_path is None: | |
| return "<div style='color:#aaa;padding:40px;text-align:center'>Sube una foto y presiona Identificar.</div>", None | |
| if not OK: | |
| return f"<div style='color:#c0392b;padding:16px'>Error: {ERR}</div>", None | |
| try: | |
| emb = cf.foto_a_embedding(imagen_path, recortar=True) | |
| ranking = cf.identificar_embedding(emb, BASE) | |
| c1, p1 = ranking[0] | |
| c2, p2 = ranking[1] if len(ranking) > 1 else (None, 0.0) | |
| estado = cf.interpretar(p1, U_CONF, U_DUD) | |
| df = pd.DataFrame(ranking, columns=['Individuo', 'Confianza (%)']) | |
| df['Confianza (%)'] = df['Confianza (%)'].round(1) | |
| return _html_resultado(estado, c1, p1, c2, p2), df | |
| except Exception as exc: | |
| return f"<div style='color:#c0392b;padding:16px'>Error: {exc}</div>", None | |
| _sub = (f"**{N} individuos** · modelo afinado + recorte automático · " | |
| f"✅ ≥{U_CONF:.0f}% ⚠️ {U_DUD:.0f}–{U_CONF:.0f}% ❌ <{U_DUD:.0f}%" | |
| if OK else "⚠️ Error al cargar el modelo/base.") | |
| with gr.Blocks(title="Identificador Caribicus", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown("# Identificador de Individuos — *Caribicus warrenii*") | |
| gr.Markdown(_sub) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| img_input = gr.Image(label="Foto a identificar", type="filepath", height=340) | |
| btn = gr.Button("Identificar", variant="primary", size="lg") | |
| with gr.Column(scale=1): | |
| html_out = gr.HTML(value="<div style='color:#aaa;padding:40px;text-align:center'>Sube una foto y presiona Identificar.</div>") | |
| tabla_out = gr.Dataframe(headers=["Individuo", "Confianza (%)"], | |
| label="Ranking completo", interactive=False, wrap=False) | |
| btn.click(fn=predecir, inputs=img_input, outputs=[html_out, tabla_out]) | |
| gr.Markdown("<sub>La foto se recorta automáticamente (quita el fondo) antes de identificar.</sub>") | |
| # --- Login (usuario/contraseña desde los secretos del Space) --- | |
| _USER = os.environ.get("APP_USER", "equipo") | |
| _PASS = os.environ.get("APP_PASSWORD") # definido como secreto del Space | |
| _auth = (_USER, _PASS) if _PASS else None | |
| if __name__ == "__main__": | |
| demo.launch(auth=_auth) | |