jarondon82 commited on
Commit
048e62d
·
1 Parent(s): b854a11

Mejorar el procesamiento de video en tiempo real y la cámara alternativa

Browse files
Files changed (1) hide show
  1. streamlit_app.py +184 -81
streamlit_app.py CHANGED
@@ -2213,10 +2213,16 @@ def main():
2213
  {"iceServers": [
2214
  {"urls": ["stun:stun.l.google.com:19302"]},
2215
  {"urls": ["stun:stun1.l.google.com:19302"]},
2216
- {"urls": ["stun:stun2.l.google.com:19302"]}
2217
  ]}
2218
  )
2219
 
 
 
 
 
 
 
2220
  # Define callback to update session state with frames processed
2221
  class VideoProcessor(VideoProcessorBase):
2222
  def __init__(self):
@@ -2224,17 +2230,29 @@ def main():
2224
  self.face_count = 0
2225
  self.start_time = time.time()
2226
  self.processing = True
2227
- self.frame_skip = 3 # Solo procesar cada 3 frames para reducir carga
 
 
2228
 
2229
  def recv(self, frame):
2230
- img = frame.to_ndarray(format="bgr24")
2231
- self.frame_count += 1
2232
-
2233
- # Solo procesar algunos frames para reducir carga
2234
- if self.frame_count % self.frame_skip != 0:
2235
- return av.VideoFrame.from_ndarray(img, format="bgr24")
2236
-
2237
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2238
  # Verificar que la imagen no sea nula
2239
  if img is None or img.size == 0 or img.shape[0] == 0 or img.shape[1] == 0:
2240
  # Si la imagen es inválida, devolver un frame en blanco
@@ -2242,16 +2260,23 @@ def main():
2242
  cv2.putText(blank_frame, "Error: Invalid frame", (50, 240),
2243
  cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
2244
  return av.VideoFrame.from_ndarray(blank_frame, format="bgr24")
2245
-
2246
  # Reducir tamaño del frame para procesamiento más rápido
2247
  scale_factor = 0.5
2248
  h, w = img.shape[:2]
2249
- small_img = safe_resize(img, (int(w * scale_factor), int(h * scale_factor)))
2250
- if small_img is None:
 
 
 
2251
  return av.VideoFrame.from_ndarray(img, format="bgr24")
2252
 
2253
  # Detect faces - la función ahora devuelve directamente los bboxes
2254
- bboxes = detect_face_dnn(face_net, small_img, confidence_threshold)
 
 
 
 
2255
 
2256
  # Ajustar bounding boxes al tamaño original
2257
  original_bboxes = []
@@ -2268,7 +2293,7 @@ def main():
2268
  self.face_count = len(original_bboxes)
2269
  current_time = time.time()
2270
  elapsed_time = current_time - self.start_time
2271
- fps = self.frame_count / elapsed_time if elapsed_time > 0 else 0
2272
 
2273
  # Actualizar métricas en session_state para que sean accesibles fuera
2274
  st.session_state.faces_detected = self.face_count
@@ -2278,83 +2303,85 @@ def main():
2278
  result_img = img.copy()
2279
  for i, (x1, y1, x2, y2, conf) in enumerate(original_bboxes):
2280
  cv2.rectangle(result_img, (x1, y1), (x2, y2), (0, 255, 0), 2)
2281
- cv2.putText(result_img, f"Face {i+1}: {conf:.2f}", (x1, y1-10),
2282
  cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
2283
 
2284
  # Añadir información FPS y rostros
2285
  cv2.putText(result_img, f"FPS: {fps:.1f}", (10, 30),
2286
  cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
2287
- cv2.putText(result_img, f"Faces: {self.face_count}", (10, 60),
2288
  cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
2289
 
2290
  return av.VideoFrame.from_ndarray(result_img, format="bgr24")
2291
  except Exception as e:
2292
- print(f"Error en procesamiento de video: {str(e)}")
2293
- # En caso de error, devolver el frame original
2294
- return av.VideoFrame.from_ndarray(img, format="bgr24")
 
 
 
 
 
2295
 
2296
- # Display WebRTC streamer
 
2297
  webrtc_ctx = webrtc_streamer(
2298
  key="face-recognition",
2299
  mode=WebRtcMode.SENDRECV,
2300
  rtc_configuration=rtc_configuration,
2301
- video_processor_factory=VideoProcessor,
2302
  media_stream_constraints={"video": {"width": 640, "height": 480}, "audio": False},
 
2303
  async_processing=True,
2304
- video_html_attrs={
2305
- "style": {"width": "100%", "margin": "0 auto", "border": "2px solid"},
2306
- "controls": False,
2307
- "autoPlay": True,
2308
- },
2309
  )
2310
 
2311
- # Update metrics if WebRTC is running
2312
  if webrtc_ctx.state.playing:
2313
- # Use a separate thread to update metrics
2314
  faces_metric.metric("Faces detected", st.session_state.get('faces_detected', 0))
2315
  fps_metric.metric("FPS", f"{st.session_state.get('fps', 0):.1f}")
2316
  time_metric.metric("Status", "Running")
2317
 
2318
- # Add instructions
2319
- st.info("WebRTC camera is active. Face recognition is being processed in real-time.")
2320
- st.warning("Note: For better performance, make sure you have good lighting and face the camera directly.")
2321
  else:
2322
  faces_metric.metric("Faces detected", 0)
2323
- fps_metric.metric("FPS", 0)
2324
  time_metric.metric("Status", "Stopped")
2325
 
2326
- st.info("Haga clic en el botón 'START' para activar la cámara y comenzar el reconocimiento en tiempo real.")
2327
- st.warning("Nota: Si está ejecutando esto en Hugging Face Spaces, puede que WebRTC no funcione correctamente. Utilice las opciones alternativas a continuación.")
 
 
 
 
 
2328
 
2329
- # WebRTC troubleshooting
2330
- with st.expander("Ayuda: Problemas con WebRTC"):
2331
- st.markdown("""
2332
- ### Solución de problemas con WebRTC
2333
-
2334
- Si el reconocimiento en tiempo real no funciona, puede deberse a las siguientes razones:
2335
-
2336
- 1. **Restricciones de seguridad en Hugging Face Spaces**: Algunos navegadores restringen el acceso a la cámara en entornos como este.
2337
- 2. **Problemas de conexión**: WebRTC requiere establecer una conexión que puede ser bloqueada por firewalls o proxies.
2338
- 3. **Permisos de cámara**: Es posible que deba conceder permisos explícitos al navegador para acceder a su cámara.
2339
-
2340
- ### Qué hacer:
2341
-
2342
- 1. Intente usar otro navegador (Chrome suele funcionar mejor)
2343
- 2. Asegúrese de que ha concedido permisos de cámara cuando el navegador los solicita
2344
- 3. Si sigue sin funcionar, use las opciones alternativas que se muestran a continuación
2345
- """)
2346
 
2347
  # Añadir opción de cámara alternativa para entornos donde WebRTC no funciona bien
2348
  st.markdown("---")
2349
  st.markdown("### Modo de cámara alternativo")
2350
 
2351
  col1, col2 = st.columns(2)
2352
- if col1.button("Usar cámara simple", key="simple_camera_button1"):
 
 
 
2353
  st.session_state.simple_camera = True
2354
  st.session_state.demo_running = False
2355
  st.session_state.upload_mode = False
2356
 
2357
- if col2.button("Detener cámara simple", key="stop_camera_button1"):
2358
  st.session_state.simple_camera = False
2359
 
2360
  if st.session_state.get('simple_camera', False):
@@ -2363,44 +2390,120 @@ def main():
2363
 
2364
  # Configurar métricas
2365
  faces_metric.metric("Faces detected", 0)
2366
- fps_metric.metric("FPS", "0.0")
2367
  time_metric.metric("Status", "Running")
2368
 
2369
  # Cámara simple que toma una imagen a la vez
2370
  with camera_container:
2371
- st.info("Cámara simple activada. Cada imagen se procesa individualmente.")
2372
 
2373
  # Usar imagen de la cámara
2374
  captured_image = st.camera_input("Tomar foto para reconocimiento", key="camera_simple_input")
2375
 
2376
  # Procesar la imagen si está disponible
2377
  if captured_image is not None:
2378
- # Leer imagen
2379
- image_bytes = captured_image.getvalue()
2380
- image = cv2.imdecode(np.frombuffer(image_bytes, np.uint8), cv2.IMREAD_COLOR)
2381
-
2382
- # Detectar rostros
2383
- bboxes = detect_face_dnn(face_net, image, confidence_threshold)
2384
-
2385
- # Actualizar métricas
2386
- faces_metric.metric("Faces detected", len(bboxes))
2387
- fps_metric.metric("FPS", "N/A")
2388
-
2389
- # Dibujar resultados
2390
- result_img = image.copy()
2391
- for i, bbox in enumerate(bboxes):
2392
- x1, y1, x2, y2, _ = bbox
2393
- cv2.rectangle(result_img, (x1, y1), (x2, y2), (0, 255, 0), 2)
2394
- cv2.putText(result_img, f"Face {i+1}", (x1, y1-10),
2395
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
2396
-
2397
- # Mostrar resultado
2398
- st.image(result_img, channels="BGR", caption="Rostros detectados", use_column_width=True)
2399
-
2400
- if len(bboxes) > 0:
2401
- st.success(f"Se detectaron {len(bboxes)} rostros")
2402
- else:
2403
- st.warning("No se detectaron rostros. Intente con una iluminación mejor o una posición diferente.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2404
 
2405
  # Opción alternativa en caso de problemas con WebRTC (mantenemos esta opción también)
2406
  st.markdown("---")
 
2213
  {"iceServers": [
2214
  {"urls": ["stun:stun.l.google.com:19302"]},
2215
  {"urls": ["stun:stun1.l.google.com:19302"]},
2216
+ {"urls": ["stun2.l.google.com:19302"]}
2217
  ]}
2218
  )
2219
 
2220
+ # Initialize session state variables if they don't exist
2221
+ if 'faces_detected' not in st.session_state:
2222
+ st.session_state.faces_detected = 0
2223
+ if 'fps' not in st.session_state:
2224
+ st.session_state.fps = 0
2225
+
2226
  # Define callback to update session state with frames processed
2227
  class VideoProcessor(VideoProcessorBase):
2228
  def __init__(self):
 
2230
  self.face_count = 0
2231
  self.start_time = time.time()
2232
  self.processing = True
2233
+ self.frame_skip = 2 # Process every other frame to reduce load
2234
+ self.frames_processed = 0
2235
+ self.last_log_time = time.time()
2236
 
2237
  def recv(self, frame):
 
 
 
 
 
 
 
2238
  try:
2239
+ img = frame.to_ndarray(format="bgr24")
2240
+ self.frame_count += 1
2241
+
2242
+ # Solo procesar algunos frames para reducir carga
2243
+ if self.frame_count % self.frame_skip != 0:
2244
+ return av.VideoFrame.from_ndarray(img, format="bgr24")
2245
+
2246
+ self.frames_processed += 1
2247
+ now = time.time()
2248
+
2249
+ # Registro de diagnóstico cada 5 segundos
2250
+ if now - self.last_log_time > 5:
2251
+ print(f"Frames procesados: {self.frames_processed}, " +
2252
+ f"Tiempo transcurrido: {now - self.start_time:.1f}s, " +
2253
+ f"FPS: {self.frames_processed/(now - self.start_time):.1f}")
2254
+ self.last_log_time = now
2255
+
2256
  # Verificar que la imagen no sea nula
2257
  if img is None or img.size == 0 or img.shape[0] == 0 or img.shape[1] == 0:
2258
  # Si la imagen es inválida, devolver un frame en blanco
 
2260
  cv2.putText(blank_frame, "Error: Invalid frame", (50, 240),
2261
  cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
2262
  return av.VideoFrame.from_ndarray(blank_frame, format="bgr24")
2263
+
2264
  # Reducir tamaño del frame para procesamiento más rápido
2265
  scale_factor = 0.5
2266
  h, w = img.shape[:2]
2267
+
2268
+ try:
2269
+ small_img = cv2.resize(img, (int(w * scale_factor), int(h * scale_factor)))
2270
+ except Exception as e:
2271
+ print(f"Error al redimensionar: {e}")
2272
  return av.VideoFrame.from_ndarray(img, format="bgr24")
2273
 
2274
  # Detect faces - la función ahora devuelve directamente los bboxes
2275
+ try:
2276
+ bboxes = detect_face_dnn(face_net, small_img, confidence_threshold)
2277
+ except Exception as e:
2278
+ print(f"Error al detectar rostros: {e}")
2279
+ bboxes = []
2280
 
2281
  # Ajustar bounding boxes al tamaño original
2282
  original_bboxes = []
 
2293
  self.face_count = len(original_bboxes)
2294
  current_time = time.time()
2295
  elapsed_time = current_time - self.start_time
2296
+ fps = self.frames_processed / elapsed_time if elapsed_time > 0 else 0
2297
 
2298
  # Actualizar métricas en session_state para que sean accesibles fuera
2299
  st.session_state.faces_detected = self.face_count
 
2303
  result_img = img.copy()
2304
  for i, (x1, y1, x2, y2, conf) in enumerate(original_bboxes):
2305
  cv2.rectangle(result_img, (x1, y1), (x2, y2), (0, 255, 0), 2)
2306
+ cv2.putText(result_img, f"Rostro {i+1}: {conf:.2f}", (x1, y1-10),
2307
  cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
2308
 
2309
  # Añadir información FPS y rostros
2310
  cv2.putText(result_img, f"FPS: {fps:.1f}", (10, 30),
2311
  cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
2312
+ cv2.putText(result_img, f"Rostros: {self.face_count}", (10, 60),
2313
  cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
2314
 
2315
  return av.VideoFrame.from_ndarray(result_img, format="bgr24")
2316
  except Exception as e:
2317
+ print(f"Error general en procesamiento de video: {str(e)}")
2318
+ try:
2319
+ # Intentar devolver el frame original
2320
+ return av.VideoFrame.from_ndarray(img, format="bgr24")
2321
+ except:
2322
+ # Si eso falla, devolver un frame en blanco como último recurso
2323
+ blank = np.ones((480, 640, 3), dtype=np.uint8) * 255
2324
+ return av.VideoFrame.from_ndarray(blank, format="bgr24")
2325
 
2326
+ # Display WebRTC streamer con opciones simplificadas para mejorar compatibilidad
2327
+ st.info("⚠️ Si el video no carga: Intente usar Chrome, recargar la página o pruebe las opciones alternativas abajo.")
2328
  webrtc_ctx = webrtc_streamer(
2329
  key="face-recognition",
2330
  mode=WebRtcMode.SENDRECV,
2331
  rtc_configuration=rtc_configuration,
 
2332
  media_stream_constraints={"video": {"width": 640, "height": 480}, "audio": False},
2333
+ video_processor_factory=VideoProcessor,
2334
  async_processing=True,
 
 
 
 
 
2335
  )
2336
 
2337
+ # Establecer y actualizar métricas
2338
  if webrtc_ctx.state.playing:
 
2339
  faces_metric.metric("Faces detected", st.session_state.get('faces_detected', 0))
2340
  fps_metric.metric("FPS", f"{st.session_state.get('fps', 0):.1f}")
2341
  time_metric.metric("Status", "Running")
2342
 
2343
+ # Mostrar instrucciones de uso
2344
+ st.success("Cámara web activada. Los rostros detectados serán identificados en tiempo real.")
 
2345
  else:
2346
  faces_metric.metric("Faces detected", 0)
2347
+ fps_metric.metric("FPS", "0")
2348
  time_metric.metric("Status", "Stopped")
2349
 
2350
+ # Mostrar instrucciones de activación
2351
+ st.warning("Haga clic en START para activar la cámara web. Esta función puede no estar disponible en entornos como Hugging Face Spaces debido a restricciones de seguridad.")
2352
+
2353
+ # WebRTC troubleshooting
2354
+ with st.expander("Ayuda: Problemas con WebRTC"):
2355
+ st.markdown("""
2356
+ ### Solución de problemas con WebRTC
2357
 
2358
+ Si el reconocimiento en tiempo real no funciona, puede deberse a las siguientes razones:
2359
+
2360
+ 1. **Restricciones de seguridad en Hugging Face Spaces**: Algunos navegadores restringen el acceso a la cámara en entornos como este.
2361
+ 2. **Problemas de conexión**: WebRTC requiere establecer una conexión que puede ser bloqueada por firewalls o proxies.
2362
+ 3. **Permisos de cámara**: Es posible que deba conceder permisos explícitos al navegador para acceder a su cámara.
2363
+
2364
+ ### Qué hacer:
2365
+
2366
+ 1. Intente usar otro navegador (Chrome suele funcionar mejor)
2367
+ 2. Asegúrese de que ha concedido permisos de cámara cuando el navegador los solicita
2368
+ 3. Si sigue sin funcionar, use las opciones alternativas que se muestran a continuación
2369
+ """)
 
 
 
 
 
2370
 
2371
  # Añadir opción de cámara alternativa para entornos donde WebRTC no funciona bien
2372
  st.markdown("---")
2373
  st.markdown("### Modo de cámara alternativo")
2374
 
2375
  col1, col2 = st.columns(2)
2376
+ simple_camera = col1.button("Usar cámara simple", key="simple_camera_button1", use_container_width=True)
2377
+ stop_simple_camera = col2.button("Detener cámara simple", key="stop_camera_button1", use_container_width=True)
2378
+
2379
+ if simple_camera:
2380
  st.session_state.simple_camera = True
2381
  st.session_state.demo_running = False
2382
  st.session_state.upload_mode = False
2383
 
2384
+ if stop_simple_camera:
2385
  st.session_state.simple_camera = False
2386
 
2387
  if st.session_state.get('simple_camera', False):
 
2390
 
2391
  # Configurar métricas
2392
  faces_metric.metric("Faces detected", 0)
2393
+ fps_metric.metric("FPS", "N/A")
2394
  time_metric.metric("Status", "Running")
2395
 
2396
  # Cámara simple que toma una imagen a la vez
2397
  with camera_container:
2398
+ st.info("Cámara simple activada. Cada imagen se procesa individualmente. Tome una foto con su cámara para detectar rostros.")
2399
 
2400
  # Usar imagen de la cámara
2401
  captured_image = st.camera_input("Tomar foto para reconocimiento", key="camera_simple_input")
2402
 
2403
  # Procesar la imagen si está disponible
2404
  if captured_image is not None:
2405
+ try:
2406
+ # Leer imagen
2407
+ image_bytes = captured_image.getvalue()
2408
+ image = cv2.imdecode(np.frombuffer(image_bytes, np.uint8), cv2.IMREAD_COLOR)
2409
+
2410
+ if image is not None and image.size > 0:
2411
+ # Detectar rostros
2412
+ bboxes = detect_face_dnn(face_net, image, confidence_threshold)
2413
+
2414
+ # Actualizar métricas
2415
+ faces_metric.metric("Faces detected", len(bboxes))
2416
+
2417
+ # Dibujar resultados
2418
+ result_img = image.copy()
2419
+ for i, bbox in enumerate(bboxes):
2420
+ x1, y1, x2, y2, conf = bbox
2421
+ cv2.rectangle(result_img, (x1, y1), (x2, y2), (0, 255, 0), 2)
2422
+ cv2.putText(result_img, f"Rostro {i+1}: {conf:.2f}", (x1, y1-10),
2423
+ cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
2424
+
2425
+ # Mostrar resultado
2426
+ st.image(result_img, channels="BGR", caption="Rostros detectados", use_column_width=True)
2427
+
2428
+ if len(bboxes) > 0:
2429
+ # Si hay rostros registrados, intentar reconocerlos
2430
+ if st.session_state.face_database and len(st.session_state.face_database) > 0:
2431
+ st.subheader("Reconocimiento de rostros:")
2432
+
2433
+ recognition_results = []
2434
+ for i, bbox in enumerate(bboxes):
2435
+ x1, y1, x2, y2, _ = bbox
2436
+ face_img = image[y1:y2, x1:x2]
2437
+
2438
+ # Extraer el embedding del rostro
2439
+ if model_choice == "VGG-Face":
2440
+ embedding = vggface_model(face_img)
2441
+ elif model_choice == "Facenet":
2442
+ embedding = facenet_model(face_img)
2443
+ elif model_choice == "OpenFace":
2444
+ embedding = openface_model(face_img)
2445
+ elif model_choice == "ArcFace":
2446
+ embedding = arcface_model(face_img)
2447
+ else: # Default to VGG-Face
2448
+ embedding = vggface_model(face_img)
2449
+
2450
+ # Comparar con rostros registrados
2451
+ best_match = None
2452
+ best_similarity = -1
2453
+
2454
+ for name, info in st.session_state.face_database.items():
2455
+ if 'embeddings' in info and info['embeddings']:
2456
+ for emb_info in info['embeddings']:
2457
+ if emb_info['model'] == model_choice:
2458
+ stored_emb = emb_info['embedding']
2459
+ similarity = cosine_similarity(embedding, stored_emb)
2460
+
2461
+ if similarity > similarity_threshold and similarity > best_similarity:
2462
+ best_similarity = similarity
2463
+ best_match = name
2464
+
2465
+ if best_match is not None:
2466
+ recognition_results.append({
2467
+ 'bbox': bbox,
2468
+ 'name': best_match,
2469
+ 'similarity': best_similarity
2470
+ })
2471
+
2472
+ # Mostrar resultados de reconocimiento
2473
+ if recognition_results:
2474
+ result_with_names = result_img.copy()
2475
+ for result in recognition_results:
2476
+ x1, y1, x2, y2, _ = result['bbox']
2477
+ name = result['name']
2478
+ similarity = result['similarity']
2479
+
2480
+ # Dibujar nombre y similitud
2481
+ cv2.rectangle(result_with_names, (x1, y1), (x2, y2), (0, 255, 0), 2)
2482
+ label = f"{name}: {similarity:.2f}"
2483
+ cv2.putText(result_with_names, label, (x1, y1-10),
2484
+ cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
2485
+
2486
+ st.image(result_with_names, channels="BGR", caption="Rostros reconocidos", use_column_width=True)
2487
+
2488
+ # Mostrar tabla de resultados
2489
+ results_df = pd.DataFrame([
2490
+ {"Nombre": r['name'], "Confianza": f"{r['similarity']:.2f}"}
2491
+ for r in recognition_results
2492
+ ])
2493
+ st.table(results_df)
2494
+ else:
2495
+ st.warning("No se pudo reconocer ninguno de los rostros detectados.")
2496
+ else:
2497
+ st.info("No hay rostros registrados para comparar. Registre rostros en la sección 'Face Registration'.")
2498
+
2499
+ st.success(f"Se detectaron {len(bboxes)} rostros")
2500
+ else:
2501
+ st.warning("No se detectaron rostros. Intente con una iluminación mejor o una posición diferente.")
2502
+ else:
2503
+ st.error("No se pudo procesar la imagen. Intente tomar otra foto.")
2504
+ except Exception as e:
2505
+ st.error(f"Error al procesar la imagen: {str(e)}")
2506
+ st.info("Intente tomar otra foto o use otra opción.")
2507
 
2508
  # Opción alternativa en caso de problemas con WebRTC (mantenemos esta opción también)
2509
  st.markdown("---")