File size: 6,991 Bytes
a42dad9 63f8ad8 a42dad9 63f8ad8 a42dad9 63f8ad8 a42dad9 63f8ad8 a42dad9 63f8ad8 a42dad9 63f8ad8 a42dad9 63f8ad8 a42dad9 63f8ad8 a42dad9 63f8ad8 | 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 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | """
app.py – Clasificador de huellas (EfficientNet / ResNet) + ASR español
"""
import gradio as gr
import numpy as np
import tensorflow as tf
import torch
import torch.nn as nn
import torchvision.transforms as T
import torchvision.models as tv_models
import librosa
import soundfile as sf
import os
# -------------------- Configuración general --------------------
IMG_SIZE = 224
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
FINGERS_EN = ["index", "little", "middle", "ring", "thumb"]
FINGERS_ES = ["índice", "meñique", "medio", "anular", "pulgar"]
FINGER_MAP_ES = dict(zip(FINGERS_EN, FINGERS_ES))
HANDS_ES = ["izquierda", "derecha"]
RESNET_CLASSES = [
"Left_index", "Left_little", "Left_middle", "Left_ring", "Left_thumb",
"Right_index", "Right_little", "Right_middle", "Right_ring", "Right_thumb",
]
TORCH_MEAN = [0.485, 0.456, 0.406]
TORCH_STD = [0.229, 0.224, 0.225]
torch_tfms = T.Compose([
T.Grayscale(num_output_channels=3),
T.Resize((IMG_SIZE, IMG_SIZE)),
T.ToTensor(),
T.Normalize(TORCH_MEAN, TORCH_STD),
])
# ---------- ASR parámetros (idénticos al entrenamiento) ----------
FRAME_LENGTH = 256
FRAME_STEP = 160
FFT_LENGTH = 384
TARGET_SR = 16_000
CHARS = [c for c in "abcdefghijklmnopqrstuvwxyzáéíóúüñ'?! "]
char_to_num = tf.keras.layers.StringLookup(vocabulary=CHARS, oov_token="")
num_to_char = tf.keras.layers.StringLookup(
vocabulary=char_to_num.get_vocabulary(), oov_token="", invert=True
)
# -------------------- Carga diferida de modelos --------------------
_models_cache = {}
def _load_efficientnet():
return tf.keras.models.load_model("models/fingerprint_model_EfficientNet.keras")
def _load_resnet():
model = tv_models.resnet18(weights=tv_models.ResNet18_Weights.IMAGENET1K_V1)
model.fc = nn.Linear(model.fc.in_features, len(RESNET_CLASSES))
model.load_state_dict(torch.load("models/resnet.pt", map_location=DEVICE))
model.eval().to(DEVICE)
return model
def CTCLoss(y_true, y_pred):
b = tf.cast(tf.shape(y_true)[0], dtype="int64")
t = tf.cast(tf.shape(y_pred)[1], dtype="int64")
l = tf.cast(tf.shape(y_true)[1], dtype="int64")
t = t * tf.ones(shape=(b, 1), dtype="int64")
l = l * tf.ones(shape=(b, 1), dtype="int64")
return tf.keras.backend.ctc_batch_cost(y_true, y_pred, t, l)
def _load_asr():
return tf.keras.models.load_model("models/audio.keras",
custom_objects={"CTCLoss": CTCLoss})
def _get_model(name):
if name not in _models_cache:
_models_cache[name] = (
_load_efficientnet() if name == "EfficientNet" else
_load_resnet() if name == "ResNet" else
_load_asr() if name == "ASR" else None
)
return _models_cache[name]
# -------------------- Clasificación de imágenes --------------------
def classify_fingerprint(image, model_name):
if image is None:
return "⚠️ Sube una imagen primero."
# --- EfficientNet ---
if model_name == "EfficientNet":
model = _get_model("EfficientNet")
img = image.convert("RGB").resize((IMG_SIZE, IMG_SIZE))
arr = tf.keras.applications.efficientnet.preprocess_input(
tf.keras.utils.img_to_array(img)
)[None, ...]
preds = model.predict(arr, verbose=0)
finger_probs = np.squeeze(preds[0] if isinstance(preds, (list, tuple)) else preds)
idx = int(finger_probs.argmax())
finger_es = FINGERS_ES[idx]
conf = finger_probs[idx]
hand_es = "N/A"
if isinstance(preds, (list, tuple)) and len(preds) > 1:
hand_es = HANDS_ES[int(np.squeeze(preds[1]).argmax())]
return f"Dedo: {finger_es}\nMano: {hand_es}\nConfianza: {conf:.2%}"
# --- ResNet ---
if model_name == "ResNet":
model = _get_model("ResNet")
tensor = torch_tfms(image).unsqueeze(0).to(DEVICE)
with torch.no_grad():
probs = torch.softmax(model(tensor), 1)[0].cpu().numpy()
idx = int(probs.argmax())
conf = probs[idx]
hand_en, finger_en = RESNET_CLASSES[idx].split('_')
return (
f"Dedo: {FINGER_MAP_ES[finger_en]}\n"
f"Mano: {HANDS_ES[0] if hand_en=='Left' else HANDS_ES[1]}\n"
f"Confianza: {conf:.2%}"
)
return "🔧 Modelo no reconocido"
# -------------------- Transcripción de audio --------------------
def _load_audio_16k(path):
audio, sr = sf.read(path)
if audio.ndim > 1:
audio = audio.mean(axis=1)
if sr != TARGET_SR:
audio = librosa.resample(audio, orig_sr=sr, target_sr=TARGET_SR)
return audio.astype("float32")
def _make_spectrogram(path):
audio = _load_audio_16k(path)
spec = np.abs(librosa.stft(audio,
n_fft=FFT_LENGTH,
hop_length=FRAME_STEP,
win_length=FRAME_LENGTH)) ** 0.5 # (freq, time)
spec = spec.T # (time, freq)
# 🔑 Normalización por FILA (freq-axis) como en entrenamiento
means = spec.mean(axis=1, keepdims=True) # (time, 1)
stds = spec.std(axis=1, keepdims=True) + 1e-10 # (time, 1)
return ((spec - means) / stds).astype("float32") # (time, freq)
def _decode_predictions(pred):
decoded, _ = tf.keras.backend.ctc_decode(
pred, input_length=np.ones(pred.shape[0]) * pred.shape[1], greedy=True
)
seq = decoded[0][0]
return tf.strings.reduce_join(num_to_char(seq)).numpy().decode("utf-8").strip()
def transcribe_audio(audio_path):
if not audio_path or not os.path.exists(audio_path):
return "⚠️ Sube o graba un audio primero."
model = _get_model("ASR")
spec = _make_spectrogram(audio_path)
pred = model.predict(spec[None, ...], verbose=0)
text = _decode_predictions(pred)
return text if text else "(vacío)"
# -------------------- Interfaz Gradio --------------------
with gr.Blocks() as demo:
gr.Markdown("# 🤖 Aplicación Multimodal con Deep Learning")
with gr.Tab("📷 Clasificación de Imágenes"):
image_input = gr.Image(type="pil", label="📤 Imagen de entrada")
image_model = gr.Dropdown(["ResNet", "EfficientNet"], value="ResNet",
label="Selecciona el modelo")
image_output = gr.Textbox(label="📈 Resultado")
gr.Button("Clasificar Imagen").click(
classify_fingerprint,
inputs=[image_input, image_model],
outputs=image_output,
)
with gr.Tab("🎙️ Reconocimiento de Voz"):
audio_input = gr.Audio(type="filepath", label="🎧 Audio de entrada")
audio_output = gr.Textbox(label="📝 Texto transcrito")
gr.Button("Transcribir Audio").click(
transcribe_audio,
inputs=audio_input,
outputs=audio_output,
)
demo.launch()
|