fabiosam commited on
Commit
fc2715e
·
verified ·
1 Parent(s): 676c99a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +68 -68
app.py CHANGED
@@ -1,7 +1,5 @@
1
  import os
2
  import json
3
- import io
4
-
5
  import cv2
6
  import numpy as np
7
  import mediapipe as mp
@@ -10,7 +8,6 @@ from tensorflow import keras
10
 
11
  import gradio as gr
12
  import matplotlib.pyplot as plt
13
- from PIL import Image
14
 
15
  print("TensorFlow version:", tf.__version__)
16
 
@@ -30,13 +27,13 @@ with open(LABELS_PATH, "r") as f:
30
  num_classes = len(label_names)
31
  MAX_FRAMES = 20 # mismo valor que usaste al entrenar
32
 
 
33
  # ==== MEDIAPIPE ====
34
  mp_holistic = mp.solutions.holistic
35
  mp_drawing = mp.solutions.drawing_utils
36
  mp_styles = mp.solutions.drawing_styles
37
 
38
 
39
- # ---- 1. EXTRAER LANDMARKS COMO VECTOR (IGUAL QUE EN EL ENTRENAMIENTO) ----
40
  def extract_landmarks_from_results(results):
41
  """
42
  Convierte los resultados de MediaPipe Holistic en un vector 1D (225,)
@@ -61,19 +58,30 @@ def extract_landmarks_from_results(results):
61
  return np.array(all_points, dtype=np.float32).flatten() # (225,)
62
 
63
 
64
- # ---- 2. PROCESAR VIDEO -> SECUENCIA + FRAME CON LANDMARKS ----
65
- def video_to_sequence_and_landmarks_frame(video_path, max_frames=MAX_FRAMES):
66
  """
67
  Procesa un video:
68
  - Devuelve la secuencia (1, max_frames, 225) para el LSTM
69
- - Devuelve una imagen (PIL.Image) con los landmarks dibujados
70
- en el primer frame donde se detecte algo.
71
  """
72
  cap = cv2.VideoCapture(video_path)
73
 
 
 
 
74
  frames_feats = []
75
- frame_for_vis = None
76
- results_for_vis = None
 
 
 
 
 
 
 
 
 
 
77
 
78
  with mp_holistic.Holistic(
79
  static_image_mode=False,
@@ -84,6 +92,7 @@ def video_to_sequence_and_landmarks_frame(video_path, max_frames=MAX_FRAMES):
84
  min_tracking_confidence=0.5
85
  ) as holistic:
86
 
 
87
  while True:
88
  ret, frame = cap.read()
89
  if not ret:
@@ -92,29 +101,48 @@ def video_to_sequence_and_landmarks_frame(video_path, max_frames=MAX_FRAMES):
92
  frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
93
  results = holistic.process(frame_rgb)
94
 
95
- vec = extract_landmarks_from_results(results)
96
- frames_feats.append(vec)
97
-
98
- # Guardamos el primer frame donde se detecta algo para visualizar
99
- if frame_for_vis is None and (
100
- results.pose_landmarks or
101
- results.left_hand_landmarks or
102
- results.right_hand_landmarks
103
- ):
104
- frame_for_vis = frame.copy()
105
- results_for_vis = results
106
-
107
- if len(frames_feats) >= max_frames:
108
- break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
  cap.release()
 
111
 
112
  if len(frames_feats) == 0:
113
  raise ValueError("No se pudieron leer frames válidos del video.")
114
 
115
  seq = np.array(frames_feats, dtype=np.float32)
116
 
117
- # Padding / recorte
118
  if seq.shape[0] < max_frames:
119
  pad_len = max_frames - seq.shape[0]
120
  pad = np.zeros((pad_len, seq.shape[1]), dtype=np.float32)
@@ -124,63 +152,35 @@ def video_to_sequence_and_landmarks_frame(video_path, max_frames=MAX_FRAMES):
124
 
125
  seq = seq.reshape(1, max_frames, seq.shape[1]) # (1, T, 225)
126
 
127
- # --- crear imagen con landmarks ---
128
- landmarks_image = None
129
- if frame_for_vis is not None and results_for_vis is not None:
130
- annotated = frame_for_vis.copy()
131
-
132
- mp_drawing.draw_landmarks(
133
- annotated,
134
- results_for_vis.pose_landmarks,
135
- mp_holistic.POSE_CONNECTIONS,
136
- landmark_drawing_spec=mp_styles.get_default_pose_landmarks_style()
137
- )
138
- mp_drawing.draw_landmarks(
139
- annotated,
140
- results_for_vis.left_hand_landmarks,
141
- mp_holistic.HAND_CONNECTIONS,
142
- landmark_drawing_spec=mp_styles.get_default_hand_landmarks_style()
143
- )
144
- mp_drawing.draw_landmarks(
145
- annotated,
146
- results_for_vis.right_hand_landmarks,
147
- mp_holistic.HAND_CONNECTIONS,
148
- landmark_drawing_spec=mp_styles.get_default_hand_landmarks_style()
149
- )
150
-
151
- annotated_rgb = cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB)
152
- landmarks_image = Image.fromarray(annotated_rgb)
153
-
154
- return seq, landmarks_image
155
-
156
-
157
- # ---- 3. PREDICCIÓN + GRÁFICO ----
158
  def predict_from_video(video):
159
  """
160
- Función que usa Gradio:
161
- - recibe ruta del video (upload o webcam)
162
- - devuelve: texto con predicción, plot de barras, imagen con landmarks
163
  """
164
  if video is None:
165
  return "Sube un video o grábalo primero.", None, None
166
 
167
- # Gradio pasa un dict con la ruta en 'video'
168
  if isinstance(video, dict) and "video" in video:
169
  video_path = video["video"]
170
  else:
171
  video_path = video
172
 
173
- seq, landmarks_image = video_to_sequence_and_landmarks_frame(video_path, MAX_FRAMES)
174
 
175
  probs = model.predict(seq, verbose=0)[0] # (num_classes,)
176
  idx = int(np.argmax(probs))
177
  label = label_names[idx]
178
  conf = float(probs[idx])
179
 
180
- # Texto de salida
181
  text_pred = f"Predicción: {label} (confianza {conf:.2f})"
182
 
183
- # Gráfico de barras con las probabilidades
184
  fig, ax = plt.subplots(figsize=(6, 3))
185
  ax.bar(range(len(label_names)), probs)
186
  ax.set_xticks(range(len(label_names)))
@@ -190,16 +190,16 @@ def predict_from_video(video):
190
  ax.set_title("Confianza por clase")
191
  plt.tight_layout()
192
 
193
- return text_pred, fig, landmarks_image
 
194
 
195
 
196
- # ---- 4. INTERFAZ GRADIO ----
197
  title = "LSP-EnSeñas - Demo LSTM"
198
  description = (
199
  "Traductor de señas basado en LSTM + MediaPipe Holistic. "
200
  "Sube un video corto o grábalo en vivo haciendo una seña. "
201
- "El modelo procesará el movimiento (cuerpo + manos) y mostrará la predicción. "
202
- "También verás un frame con los puntos (landmarks) detectados por MediaPipe."
203
  )
204
 
205
  demo = gr.Interface(
@@ -208,7 +208,7 @@ demo = gr.Interface(
208
  outputs=[
209
  gr.Textbox(label="Predicción del modelo"),
210
  gr.Plot(label="Confianza por clase"),
211
- gr.Image(type="pil", label="Landmarks detectados (ejemplo de frame)")
212
  ],
213
  title=title,
214
  description=description,
 
1
  import os
2
  import json
 
 
3
  import cv2
4
  import numpy as np
5
  import mediapipe as mp
 
8
 
9
  import gradio as gr
10
  import matplotlib.pyplot as plt
 
11
 
12
  print("TensorFlow version:", tf.__version__)
13
 
 
27
  num_classes = len(label_names)
28
  MAX_FRAMES = 20 # mismo valor que usaste al entrenar
29
 
30
+
31
  # ==== MEDIAPIPE ====
32
  mp_holistic = mp.solutions.holistic
33
  mp_drawing = mp.solutions.drawing_utils
34
  mp_styles = mp.solutions.drawing_styles
35
 
36
 
 
37
  def extract_landmarks_from_results(results):
38
  """
39
  Convierte los resultados de MediaPipe Holistic en un vector 1D (225,)
 
58
  return np.array(all_points, dtype=np.float32).flatten() # (225,)
59
 
60
 
61
+ def video_to_sequence_and_annotated(video_path, max_frames=MAX_FRAMES):
 
62
  """
63
  Procesa un video:
64
  - Devuelve la secuencia (1, max_frames, 225) para el LSTM
65
+ - Devuelve la ruta de un nuevo video con los landmarks dibujados.
 
66
  """
67
  cap = cv2.VideoCapture(video_path)
68
 
69
+ if not cap.isOpened():
70
+ raise ValueError(f"No se pudo abrir el video: {video_path}")
71
+
72
  frames_feats = []
73
+
74
+ # Info del video de entrada
75
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
76
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
77
+ fps = cap.get(cv2.CAP_PROP_FPS)
78
+ if fps is None or fps <= 0:
79
+ fps = 25.0
80
+
81
+ # Ruta temporal para el video anotado
82
+ out_path = os.path.join("/tmp", "annotated_output.mp4")
83
+ fourcc = cv2.VideoWriter_fourcc(*"mp4v")
84
+ writer = cv2.VideoWriter(out_path, fourcc, fps, (width, height))
85
 
86
  with mp_holistic.Holistic(
87
  static_image_mode=False,
 
92
  min_tracking_confidence=0.5
93
  ) as holistic:
94
 
95
+ frame_idx = 0
96
  while True:
97
  ret, frame = cap.read()
98
  if not ret:
 
101
  frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
102
  results = holistic.process(frame_rgb)
103
 
104
+ # Features para el modelo (solo guardamos hasta max_frames)
105
+ if len(frames_feats) < max_frames:
106
+ vec = extract_landmarks_from_results(results)
107
+ frames_feats.append(vec)
108
+
109
+ # DIBUJAR LANDMARKS SOBRE EL FRAME
110
+ annotated = frame.copy()
111
+
112
+ if results.pose_landmarks:
113
+ mp_drawing.draw_landmarks(
114
+ annotated,
115
+ results.pose_landmarks,
116
+ mp_holistic.POSE_CONNECTIONS,
117
+ landmark_drawing_spec=mp_styles.get_default_pose_landmarks_style()
118
+ )
119
+ if results.left_hand_landmarks:
120
+ mp_drawing.draw_landmarks(
121
+ annotated,
122
+ results.left_hand_landmarks,
123
+ mp_holistic.HAND_CONNECTIONS,
124
+ landmark_drawing_spec=mp_styles.get_default_hand_landmarks_style()
125
+ )
126
+ if results.right_hand_landmarks:
127
+ mp_drawing.draw_landmarks(
128
+ annotated,
129
+ results.right_hand_landmarks,
130
+ mp_holistic.HAND_CONNECTIONS,
131
+ landmark_drawing_spec=mp_styles.get_default_hand_landmarks_style()
132
+ )
133
+
134
+ writer.write(annotated)
135
+ frame_idx += 1
136
 
137
  cap.release()
138
+ writer.release()
139
 
140
  if len(frames_feats) == 0:
141
  raise ValueError("No se pudieron leer frames válidos del video.")
142
 
143
  seq = np.array(frames_feats, dtype=np.float32)
144
 
145
+ # Padding / recorte para el LSTM
146
  if seq.shape[0] < max_frames:
147
  pad_len = max_frames - seq.shape[0]
148
  pad = np.zeros((pad_len, seq.shape[1]), dtype=np.float32)
 
152
 
153
  seq = seq.reshape(1, max_frames, seq.shape[1]) # (1, T, 225)
154
 
155
+ return seq, out_path
156
+
157
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  def predict_from_video(video):
159
  """
160
+ Función llamada por Gradio.
161
+ - Recibe ruta del video (upload o webcam).
162
+ - Devuelve: texto, gráfico de barras, video con landmarks.
163
  """
164
  if video is None:
165
  return "Sube un video o grábalo primero.", None, None
166
 
167
+ # Gradio a veces pasa dict con 'video'
168
  if isinstance(video, dict) and "video" in video:
169
  video_path = video["video"]
170
  else:
171
  video_path = video
172
 
173
+ seq, annotated_path = video_to_sequence_and_annotated(video_path, MAX_FRAMES)
174
 
175
  probs = model.predict(seq, verbose=0)[0] # (num_classes,)
176
  idx = int(np.argmax(probs))
177
  label = label_names[idx]
178
  conf = float(probs[idx])
179
 
180
+ # Texto
181
  text_pred = f"Predicción: {label} (confianza {conf:.2f})"
182
 
183
+ # Gráfico de barras
184
  fig, ax = plt.subplots(figsize=(6, 3))
185
  ax.bar(range(len(label_names)), probs)
186
  ax.set_xticks(range(len(label_names)))
 
190
  ax.set_title("Confianza por clase")
191
  plt.tight_layout()
192
 
193
+ # annotated_path es la ruta del video con landmarks
194
+ return text_pred, fig, annotated_path
195
 
196
 
 
197
  title = "LSP-EnSeñas - Demo LSTM"
198
  description = (
199
  "Traductor de señas basado en LSTM + MediaPipe Holistic. "
200
  "Sube un video corto o grábalo en vivo haciendo una seña. "
201
+ "El modelo procesará el movimiento (cuerpo + manos), mostrará la predicción "
202
+ "y devolverá tu video con los puntos (landmarks) dibujados."
203
  )
204
 
205
  demo = gr.Interface(
 
208
  outputs=[
209
  gr.Textbox(label="Predicción del modelo"),
210
  gr.Plot(label="Confianza por clase"),
211
+ gr.Video(label="Video con landmarks detectados"),
212
  ],
213
  title=title,
214
  description=description,