Download app.py from ProyectoDeepLearning/Deep_Learning_Proyect: direct link, hf CLI and curl.
- Browser
- Download file 6.99 kB
-
https://huggingface.co/spaces/ProyectoDeepLearning/Deep_Learning_Proyect/resolve/e790f59e23cbde54f4ccc29ea7686ed23ba7fde6/app.py
- Command line
-
hf download hf://spaces/ProyectoDeepLearning/Deep_Learning_Proyect@e790f59e23cbde54f4ccc29ea7686ed23ba7fde6/app.py
-
curl -L -o app.py https://huggingface.co/spaces/ProyectoDeepLearning/Deep_Learning_Proyect/resolve/e790f59e23cbde54f4ccc29ea7686ed23ba7fde6/app.py
6.99 kB
| """ | |
| 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() | |