File size: 4,761 Bytes
66e2a19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
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]}&nbsp; {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)