JossephVR commited on
Commit
a42dad9
·
1 Parent(s): e34b82b

Feat: add app and models working

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.keras filter=lfs diff=lfs merge=lfs -text
app.py CHANGED
@@ -1,32 +1,186 @@
 
 
 
 
1
  import gradio as gr
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
- def dummy_response(entrada, modelo=None):
4
- if modelo:
5
- return f"🔧 Funcionalidad en desarrollo... Modelo seleccionado: {modelo}"
6
- else:
7
- return "🔧 Funcionalidad en desarrollo..."
 
 
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  with gr.Blocks() as demo:
10
- gr.Markdown("""
11
- # 🤖 Aplicación Multimodal con Deep Learning
12
- Esta aplicación integra modelos de visión e inteligencia artificial para analizar **imágenes** y **audio** en tiempo real.
13
- """)
14
 
15
  with gr.Tab("📷 Clasificación de Imágenes"):
16
- gr.Markdown("Sube una imagen para clasificarla con modelos visuales (ResNet, MobileNet, EfficientNet)")
17
- image_input = gr.Image(type="pil", label="📤 Imagen de entrada")
18
- image_model = gr.Dropdown(
19
- ["ResNet", "EfficientNet"],
20
- label="Selecciona el modelo",
21
- value="ResNet"
22
- )
23
  image_output = gr.Textbox(label="📈 Resultado")
24
- gr.Button("Clasificar Imagen").click(fn=dummy_response, inputs=[image_input, image_model], outputs=image_output)
 
 
 
 
25
 
26
  with gr.Tab("🎙️ Reconocimiento de Voz"):
27
- gr.Markdown("Sube un archivo de audio para convertirlo en texto utilizando un modelo basado en CTC.")
28
- audio_input = gr.Audio(type="filepath", label="🎧 Audio de entrada")
29
  audio_output = gr.Textbox(label="📝 Texto transcrito")
30
- gr.Button("Transcribir Audio").click(fn=dummy_response, inputs=audio_input, outputs=audio_output)
 
 
 
 
31
 
32
  demo.launch()
 
1
+ """
2
+ app.py – Clasificador de huellas (EfficientNet / ResNet) + ASR español
3
+ """
4
+
5
  import gradio as gr
6
+ import numpy as np
7
+ import tensorflow as tf
8
+ import torch
9
+ import torch.nn as nn
10
+ import torchvision.transforms as T
11
+ import torchvision.models as tv_models
12
+ import librosa
13
+ import soundfile as sf
14
+ import os
15
+
16
+ # -------------------- Configuración general --------------------
17
+ IMG_SIZE = 224
18
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
19
+
20
+ FINGERS_EN = ["index", "little", "middle", "ring", "thumb"]
21
+ FINGERS_ES = ["índice", "meñique", "medio", "anular", "pulgar"]
22
+ FINGER_MAP_ES = dict(zip(FINGERS_EN, FINGERS_ES))
23
+ HANDS_ES = ["izquierda", "derecha"]
24
+
25
+ RESNET_CLASSES = [
26
+ "Left_index", "Left_little", "Left_middle", "Left_ring", "Left_thumb",
27
+ "Right_index", "Right_little", "Right_middle", "Right_ring", "Right_thumb",
28
+ ]
29
+
30
+ TORCH_MEAN = [0.485, 0.456, 0.406]
31
+ TORCH_STD = [0.229, 0.224, 0.225]
32
+ torch_tfms = T.Compose([
33
+ T.Grayscale(num_output_channels=3),
34
+ T.Resize((IMG_SIZE, IMG_SIZE)),
35
+ T.ToTensor(),
36
+ T.Normalize(TORCH_MEAN, TORCH_STD),
37
+ ])
38
+
39
+ # ---------- ASR parámetros (idénticos al entrenamiento) ----------
40
+ FRAME_LENGTH = 256
41
+ FRAME_STEP = 160
42
+ FFT_LENGTH = 384
43
+ TARGET_SR = 16_000
44
+
45
+ CHARS = [c for c in "abcdefghijklmnopqrstuvwxyzáéíóúüñ'?! "]
46
+ char_to_num = tf.keras.layers.StringLookup(vocabulary=CHARS, oov_token="")
47
+ num_to_char = tf.keras.layers.StringLookup(
48
+ vocabulary=char_to_num.get_vocabulary(), oov_token="", invert=True
49
+ )
50
+
51
+ # -------------------- Carga diferida de modelos --------------------
52
+ _models_cache = {}
53
+
54
+ def _load_efficientnet():
55
+ return tf.keras.models.load_model("models/fingerprint_model_EfficientNet.keras")
56
+
57
+ def _load_resnet():
58
+ model = tv_models.resnet18(weights=tv_models.ResNet18_Weights.IMAGENET1K_V1)
59
+ model.fc = nn.Linear(model.fc.in_features, len(RESNET_CLASSES))
60
+ model.load_state_dict(torch.load("models/resnet.pt", map_location=DEVICE))
61
+ model.eval().to(DEVICE)
62
+ return model
63
+
64
+ def CTCLoss(y_true, y_pred):
65
+ b = tf.cast(tf.shape(y_true)[0], dtype="int64")
66
+ t = tf.cast(tf.shape(y_pred)[1], dtype="int64")
67
+ l = tf.cast(tf.shape(y_true)[1], dtype="int64")
68
+ t = t * tf.ones(shape=(b, 1), dtype="int64")
69
+ l = l * tf.ones(shape=(b, 1), dtype="int64")
70
+ return tf.keras.backend.ctc_batch_cost(y_true, y_pred, t, l)
71
+
72
+ def _load_asr():
73
+ return tf.keras.models.load_model("models/audio.keras",
74
+ custom_objects={"CTCLoss": CTCLoss})
75
+
76
+ def _get_model(name):
77
+ if name not in _models_cache:
78
+ _models_cache[name] = (
79
+ _load_efficientnet() if name == "EfficientNet" else
80
+ _load_resnet() if name == "ResNet" else
81
+ _load_asr() if name == "ASR" else None
82
+ )
83
+ return _models_cache[name]
84
+
85
+ # -------------------- Clasificación de imágenes --------------------
86
+ def classify_fingerprint(image, model_name):
87
+ if image is None:
88
+ return "⚠️ Sube una imagen primero."
89
+
90
+ # --- EfficientNet ---
91
+ if model_name == "EfficientNet":
92
+ model = _get_model("EfficientNet")
93
+ img = image.convert("RGB").resize((IMG_SIZE, IMG_SIZE))
94
+ arr = tf.keras.applications.efficientnet.preprocess_input(
95
+ tf.keras.utils.img_to_array(img)
96
+ )[None, ...]
97
+ preds = model.predict(arr, verbose=0)
98
+ finger_probs = np.squeeze(preds[0] if isinstance(preds, (list, tuple)) else preds)
99
+ idx = int(finger_probs.argmax())
100
+ finger_es = FINGERS_ES[idx]
101
+ conf = finger_probs[idx]
102
+ hand_es = "N/A"
103
+ if isinstance(preds, (list, tuple)) and len(preds) > 1:
104
+ hand_es = HANDS_ES[int(np.squeeze(preds[1]).argmax())]
105
+ return f"Dedo: {finger_es}\nMano: {hand_es}\nConfianza: {conf:.2%}"
106
+
107
+ # --- ResNet ---
108
+ if model_name == "ResNet":
109
+ model = _get_model("ResNet")
110
+ tensor = torch_tfms(image).unsqueeze(0).to(DEVICE)
111
+ with torch.no_grad():
112
+ probs = torch.softmax(model(tensor), 1)[0].cpu().numpy()
113
+ idx = int(probs.argmax())
114
+ conf = probs[idx]
115
+ hand_en, finger_en = RESNET_CLASSES[idx].split('_')
116
+ return (
117
+ f"Dedo: {FINGER_MAP_ES[finger_en]}\n"
118
+ f"Mano: {HANDS_ES[0] if hand_en=='Left' else HANDS_ES[1]}\n"
119
+ f"Confianza: {conf:.2%}"
120
+ )
121
+
122
+ return "🔧 Modelo no reconocido"
123
+
124
+ # -------------------- Transcripción de audio --------------------
125
+ def _load_audio_16k(path):
126
+ audio, sr = sf.read(path)
127
+ if audio.ndim > 1:
128
+ audio = audio.mean(axis=1)
129
+ if sr != TARGET_SR:
130
+ audio = librosa.resample(audio, orig_sr=sr, target_sr=TARGET_SR)
131
+ return audio.astype("float32")
132
 
133
+ def _make_spectrogram(path):
134
+ audio = _load_audio_16k(path)
135
+ spec = np.abs(librosa.stft(audio,
136
+ n_fft=FFT_LENGTH,
137
+ hop_length=FRAME_STEP,
138
+ win_length=FRAME_LENGTH)) ** 0.5 # (freq, time)
139
+ spec = spec.T # (time, freq)
140
 
141
+ # 🔑 Normalización por FILA (freq-axis) como en entrenamiento
142
+ means = spec.mean(axis=1, keepdims=True) # (time, 1)
143
+ stds = spec.std(axis=1, keepdims=True) + 1e-10 # (time, 1)
144
+ return ((spec - means) / stds).astype("float32") # (time, freq)
145
+
146
+ def _decode_predictions(pred):
147
+ decoded, _ = tf.keras.backend.ctc_decode(
148
+ pred, input_length=np.ones(pred.shape[0]) * pred.shape[1], greedy=True
149
+ )
150
+ seq = decoded[0][0]
151
+ return tf.strings.reduce_join(num_to_char(seq)).numpy().decode("utf-8").strip()
152
+
153
+ def transcribe_audio(audio_path):
154
+ if not audio_path or not os.path.exists(audio_path):
155
+ return "⚠️ Sube o graba un audio primero."
156
+ model = _get_model("ASR")
157
+ spec = _make_spectrogram(audio_path)
158
+ pred = model.predict(spec[None, ...], verbose=0)
159
+ text = _decode_predictions(pred)
160
+ return text if text else "(vacío)"
161
+
162
+ # -------------------- Interfaz Gradio --------------------
163
  with gr.Blocks() as demo:
164
+ gr.Markdown("# 🤖 Aplicación Multimodal con Deep Learning")
 
 
 
165
 
166
  with gr.Tab("📷 Clasificación de Imágenes"):
167
+ image_input = gr.Image(type="pil", label="📤 Imagen de entrada")
168
+ image_model = gr.Dropdown(["ResNet", "EfficientNet"], value="ResNet",
169
+ label="Selecciona el modelo")
 
 
 
 
170
  image_output = gr.Textbox(label="📈 Resultado")
171
+ gr.Button("Clasificar Imagen").click(
172
+ classify_fingerprint,
173
+ inputs=[image_input, image_model],
174
+ outputs=image_output,
175
+ )
176
 
177
  with gr.Tab("🎙️ Reconocimiento de Voz"):
178
+ audio_input = gr.Audio(type="filepath", label="🎧 Audio de entrada")
 
179
  audio_output = gr.Textbox(label="📝 Texto transcrito")
180
+ gr.Button("Transcribir Audio").click(
181
+ transcribe_audio,
182
+ inputs=audio_input,
183
+ outputs=audio_output,
184
+ )
185
 
186
  demo.launch()
models/audio.keras ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8ec9e4f96bac8275d2b474066f144103e6bb7bc6eede577f32c7b87d42b3ba4c
3
+ size 192282769
models/fingerprint_model_EfficientNet.keras ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fee8c75d1a4bbe80a019cf3eb8f8aa1b29a31b766b96da9f5f4751f4257e2940
3
+ size 49374092
models/resnet.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:587e9e0d23a87469270d1550b43d28e400b9a46df877c257354688d5a0e34bcd
3
+ size 44807328
requirements.txt ADDED
Binary file (3.58 kB). View file