Spaces:
Build error
Build error
Commit
·
8aa5820
1
Parent(s):
ffc87b0
Agregar herramientas de diagnóstico y script de detección de rostros independiente
Browse files- detect_face_continuous.py +104 -0
- streamlit_app.py +45 -0
detect_face_continuous.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import cv2
|
| 2 |
+
import numpy as np
|
| 3 |
+
|
| 4 |
+
def detect_face_dnn(net, frame, conf_threshold=0.5):
|
| 5 |
+
"""
|
| 6 |
+
Detecta rostros en una imagen utilizando un modelo DNN pre-entrenado
|
| 7 |
+
y devuelve los cuadros delimitadores.
|
| 8 |
+
"""
|
| 9 |
+
frameHeight = frame.shape[0]
|
| 10 |
+
frameWidth = frame.shape[1]
|
| 11 |
+
|
| 12 |
+
# Crear un blob a partir de la imagen
|
| 13 |
+
blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), [104, 117, 123], False, False)
|
| 14 |
+
|
| 15 |
+
# Establecer la entrada para la red neuronal
|
| 16 |
+
net.setInput(blob)
|
| 17 |
+
|
| 18 |
+
# Ejecutar la detección
|
| 19 |
+
detections = net.forward()
|
| 20 |
+
|
| 21 |
+
bboxes = []
|
| 22 |
+
|
| 23 |
+
# Procesar las detecciones
|
| 24 |
+
for i in range(detections.shape[2]):
|
| 25 |
+
confidence = detections[0, 0, i, 2]
|
| 26 |
+
|
| 27 |
+
# Filtrar por umbral de confianza
|
| 28 |
+
if confidence > conf_threshold:
|
| 29 |
+
x1 = int(detections[0, 0, i, 3] * frameWidth)
|
| 30 |
+
y1 = int(detections[0, 0, i, 4] * frameHeight)
|
| 31 |
+
x2 = int(detections[0, 0, i, 5] * frameWidth)
|
| 32 |
+
y2 = int(detections[0, 0, i, 6] * frameHeight)
|
| 33 |
+
|
| 34 |
+
# Asegurarse de que las coordenadas están dentro de los límites de la imagen
|
| 35 |
+
x1 = max(0, min(x1, frameWidth - 1))
|
| 36 |
+
y1 = max(0, min(y1, frameHeight - 1))
|
| 37 |
+
x2 = max(0, min(x2, frameWidth - 1))
|
| 38 |
+
y2 = max(0, min(y2, frameHeight - 1))
|
| 39 |
+
|
| 40 |
+
# Agregar los cuadros delimitadores y la confianza
|
| 41 |
+
bboxes.append([x1, y1, x2, y2, confidence])
|
| 42 |
+
|
| 43 |
+
return np.array(bboxes)
|
| 44 |
+
|
| 45 |
+
def main():
|
| 46 |
+
# Cargar el modelo de detección de rostros
|
| 47 |
+
modelFile = "models/opencv_face_detector_uint8.pb"
|
| 48 |
+
configFile = "models/opencv_face_detector.pbtxt"
|
| 49 |
+
net = cv2.dnn.readNetFromTensorflow(modelFile, configFile)
|
| 50 |
+
|
| 51 |
+
# Iniciar la cámara
|
| 52 |
+
cap = cv2.VideoCapture(0)
|
| 53 |
+
|
| 54 |
+
if not cap.isOpened():
|
| 55 |
+
print("Error: No se pudo abrir la cámara.")
|
| 56 |
+
return
|
| 57 |
+
|
| 58 |
+
print("Cámara iniciada correctamente. Presione 'q' para salir.")
|
| 59 |
+
|
| 60 |
+
while True:
|
| 61 |
+
# Leer un frame de la cámara
|
| 62 |
+
ret, frame = cap.read()
|
| 63 |
+
|
| 64 |
+
if not ret:
|
| 65 |
+
print("Error al capturar el frame.")
|
| 66 |
+
break
|
| 67 |
+
|
| 68 |
+
# Detectar rostros con umbrales de confianza diferentes
|
| 69 |
+
for threshold in [0.5, 0.3, 0.1]:
|
| 70 |
+
bboxes = detect_face_dnn(net, frame, threshold)
|
| 71 |
+
|
| 72 |
+
# Crear copia del frame para dibujar en ella
|
| 73 |
+
display_frame = frame.copy()
|
| 74 |
+
|
| 75 |
+
# Dibujar los cuadros delimitadores
|
| 76 |
+
for i, bbox in enumerate(bboxes):
|
| 77 |
+
x1, y1, x2, y2, conf = bbox
|
| 78 |
+
# Usar color verde
|
| 79 |
+
color = (0, 255, 0)
|
| 80 |
+
# Dibujar el rectángulo
|
| 81 |
+
cv2.rectangle(display_frame, (int(x1), int(y1)), (int(x2), int(y2)), color, 2)
|
| 82 |
+
# Mostrar la confianza
|
| 83 |
+
label = f"Rostro: {conf:.2f}"
|
| 84 |
+
cv2.putText(display_frame, label, (int(x1), int(y1) - 10),
|
| 85 |
+
cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
|
| 86 |
+
|
| 87 |
+
# Mostrar el número de rostros detectados en la esquina
|
| 88 |
+
cv2.putText(display_frame, f"Umbral: {threshold} - Rostros: {len(bboxes)}",
|
| 89 |
+
(10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
|
| 90 |
+
|
| 91 |
+
# Mostrar el resultado
|
| 92 |
+
window_name = f"Detección de Rostros (Umbral: {threshold})"
|
| 93 |
+
cv2.imshow(window_name, display_frame)
|
| 94 |
+
|
| 95 |
+
# Salir si se presiona 'q'
|
| 96 |
+
if cv2.waitKey(1) & 0xFF == ord('q'):
|
| 97 |
+
break
|
| 98 |
+
|
| 99 |
+
# Liberar recursos
|
| 100 |
+
cap.release()
|
| 101 |
+
cv2.destroyAllWindows()
|
| 102 |
+
|
| 103 |
+
if __name__ == "__main__":
|
| 104 |
+
main()
|
streamlit_app.py
CHANGED
|
@@ -2443,22 +2443,28 @@ def main():
|
|
| 2443 |
const displayCtx = display.getContext('2d');
|
| 2444 |
let captureInterval;
|
| 2445 |
|
|
|
|
|
|
|
|
|
|
| 2446 |
// Configuración dinámica del FPS (desde Streamlit)
|
| 2447 |
const captureDelay = 1000 / %s;
|
| 2448 |
|
| 2449 |
// Iniciar la cámara
|
| 2450 |
async function setupCamera() {
|
| 2451 |
try {
|
|
|
|
| 2452 |
const stream = await navigator.mediaDevices.getUserMedia({
|
| 2453 |
'video': { width: 640, height: 480 },
|
| 2454 |
'audio': false
|
| 2455 |
});
|
| 2456 |
video.srcObject = stream;
|
|
|
|
| 2457 |
|
| 2458 |
// Esperar a que la cámara esté lista
|
| 2459 |
return new Promise((resolve) => {
|
| 2460 |
video.onloadedmetadata = () => {
|
| 2461 |
video.play();
|
|
|
|
| 2462 |
resolve(video);
|
| 2463 |
};
|
| 2464 |
});
|
|
@@ -2489,6 +2495,11 @@ def main():
|
|
| 2489 |
}
|
| 2490 |
}
|
| 2491 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2492 |
// Recibir datos de detección de rostros y dibujarlos
|
| 2493 |
window.addEventListener('message', function(event) {
|
| 2494 |
try {
|
|
@@ -2504,6 +2515,7 @@ def main():
|
|
| 2504 |
|
| 2505 |
// Dibujar cada caja
|
| 2506 |
if (boxes && boxes.length > 0) {
|
|
|
|
| 2507 |
boxes.forEach(box => {
|
| 2508 |
if (box && box.length >= 5) {
|
| 2509 |
const [x1, y1, x2, y2, confidence] = box;
|
|
@@ -2518,8 +2530,12 @@ def main():
|
|
| 2518 |
displayCtx.fillStyle = '#00FF00';
|
| 2519 |
displayCtx.font = '16px Arial';
|
| 2520 |
displayCtx.fillText(`Rostro: ${confidence.toFixed(2)}`, x1, y1-5);
|
|
|
|
|
|
|
| 2521 |
}
|
| 2522 |
});
|
|
|
|
|
|
|
| 2523 |
}
|
| 2524 |
}
|
| 2525 |
} catch (error) {
|
|
@@ -2527,11 +2543,25 @@ def main():
|
|
| 2527 |
}
|
| 2528 |
});
|
| 2529 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2530 |
// Arrancar todo
|
| 2531 |
async function initCapture() {
|
| 2532 |
await setupCamera();
|
|
|
|
|
|
|
| 2533 |
// Empezar a capturar frames periódicamente
|
| 2534 |
captureInterval = setInterval(captureFrame, captureDelay);
|
|
|
|
| 2535 |
}
|
| 2536 |
|
| 2537 |
// Limpiar al salir
|
|
@@ -2540,6 +2570,7 @@ def main():
|
|
| 2540 |
if (video.srcObject) {
|
| 2541 |
video.srcObject.getTracks().forEach(track => track.stop());
|
| 2542 |
}
|
|
|
|
| 2543 |
}
|
| 2544 |
|
| 2545 |
// Iniciar captura automáticamente
|
|
@@ -2590,6 +2621,9 @@ def main():
|
|
| 2590 |
# Convertir a lista si es un array numpy
|
| 2591 |
bbox_list = bboxes.tolist() if isinstance(bboxes, np.ndarray) else bboxes
|
| 2592 |
|
|
|
|
|
|
|
|
|
|
| 2593 |
# Crear script para enviar datos al componente JavaScript
|
| 2594 |
face_boxes_js = f"""
|
| 2595 |
<script>
|
|
@@ -2600,6 +2634,17 @@ def main():
|
|
| 2600 |
type: 'faceBoxes',
|
| 2601 |
boxes: {json.dumps(bbox_list)}
|
| 2602 |
}}, '*');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2603 |
}})();
|
| 2604 |
</script>
|
| 2605 |
"""
|
|
|
|
| 2443 |
const displayCtx = display.getContext('2d');
|
| 2444 |
let captureInterval;
|
| 2445 |
|
| 2446 |
+
// Para depuración
|
| 2447 |
+
console.log('Componente de cámara inicializado');
|
| 2448 |
+
|
| 2449 |
// Configuración dinámica del FPS (desde Streamlit)
|
| 2450 |
const captureDelay = 1000 / %s;
|
| 2451 |
|
| 2452 |
// Iniciar la cámara
|
| 2453 |
async function setupCamera() {
|
| 2454 |
try {
|
| 2455 |
+
console.log('Intentando acceder a la cámara...');
|
| 2456 |
const stream = await navigator.mediaDevices.getUserMedia({
|
| 2457 |
'video': { width: 640, height: 480 },
|
| 2458 |
'audio': false
|
| 2459 |
});
|
| 2460 |
video.srcObject = stream;
|
| 2461 |
+
console.log('Cámara iniciada correctamente');
|
| 2462 |
|
| 2463 |
// Esperar a que la cámara esté lista
|
| 2464 |
return new Promise((resolve) => {
|
| 2465 |
video.onloadedmetadata = () => {
|
| 2466 |
video.play();
|
| 2467 |
+
console.log('Video iniciado');
|
| 2468 |
resolve(video);
|
| 2469 |
};
|
| 2470 |
});
|
|
|
|
| 2495 |
}
|
| 2496 |
}
|
| 2497 |
|
| 2498 |
+
// Debug para todos los mensajes recibidos
|
| 2499 |
+
window.addEventListener('message', function(event) {
|
| 2500 |
+
console.log('Mensaje recibido:', event.data);
|
| 2501 |
+
});
|
| 2502 |
+
|
| 2503 |
// Recibir datos de detección de rostros y dibujarlos
|
| 2504 |
window.addEventListener('message', function(event) {
|
| 2505 |
try {
|
|
|
|
| 2515 |
|
| 2516 |
// Dibujar cada caja
|
| 2517 |
if (boxes && boxes.length > 0) {
|
| 2518 |
+
console.log(`Dibujando ${boxes.length} cajas de rostros`);
|
| 2519 |
boxes.forEach(box => {
|
| 2520 |
if (box && box.length >= 5) {
|
| 2521 |
const [x1, y1, x2, y2, confidence] = box;
|
|
|
|
| 2530 |
displayCtx.fillStyle = '#00FF00';
|
| 2531 |
displayCtx.font = '16px Arial';
|
| 2532 |
displayCtx.fillText(`Rostro: ${confidence.toFixed(2)}`, x1, y1-5);
|
| 2533 |
+
} else {
|
| 2534 |
+
console.warn('Formato de caja inválido:', box);
|
| 2535 |
}
|
| 2536 |
});
|
| 2537 |
+
} else {
|
| 2538 |
+
console.log('No hay cajas para dibujar o formato inválido');
|
| 2539 |
}
|
| 2540 |
}
|
| 2541 |
} catch (error) {
|
|
|
|
| 2543 |
}
|
| 2544 |
});
|
| 2545 |
|
| 2546 |
+
// Dibujar un rectángulo de prueba para verificar que el canvas funciona
|
| 2547 |
+
function drawTestRect() {
|
| 2548 |
+
displayCtx.strokeStyle = '#FF0000';
|
| 2549 |
+
displayCtx.lineWidth = 3;
|
| 2550 |
+
displayCtx.strokeRect(50, 50, 100, 100);
|
| 2551 |
+
displayCtx.fillStyle = '#FF0000';
|
| 2552 |
+
displayCtx.font = '16px Arial';
|
| 2553 |
+
displayCtx.fillText('Test Rectangle', 50, 40);
|
| 2554 |
+
console.log('Rectángulo de prueba dibujado');
|
| 2555 |
+
}
|
| 2556 |
+
|
| 2557 |
// Arrancar todo
|
| 2558 |
async function initCapture() {
|
| 2559 |
await setupCamera();
|
| 2560 |
+
// Dibujar rectángulo de prueba
|
| 2561 |
+
setTimeout(drawTestRect, 1000);
|
| 2562 |
// Empezar a capturar frames periódicamente
|
| 2563 |
captureInterval = setInterval(captureFrame, captureDelay);
|
| 2564 |
+
console.log(`Captura iniciada con intervalo de ${captureDelay}ms`);
|
| 2565 |
}
|
| 2566 |
|
| 2567 |
// Limpiar al salir
|
|
|
|
| 2570 |
if (video.srcObject) {
|
| 2571 |
video.srcObject.getTracks().forEach(track => track.stop());
|
| 2572 |
}
|
| 2573 |
+
console.log('Captura detenida');
|
| 2574 |
}
|
| 2575 |
|
| 2576 |
// Iniciar captura automáticamente
|
|
|
|
| 2621 |
# Convertir a lista si es un array numpy
|
| 2622 |
bbox_list = bboxes.tolist() if isinstance(bboxes, np.ndarray) else bboxes
|
| 2623 |
|
| 2624 |
+
# Imprimir en el servidor para depuración
|
| 2625 |
+
print(f"Cajas de rostros detectadas: {bbox_list}")
|
| 2626 |
+
|
| 2627 |
# Crear script para enviar datos al componente JavaScript
|
| 2628 |
face_boxes_js = f"""
|
| 2629 |
<script>
|
|
|
|
| 2634 |
type: 'faceBoxes',
|
| 2635 |
boxes: {json.dumps(bbox_list)}
|
| 2636 |
}}, '*');
|
| 2637 |
+
|
| 2638 |
+
// Intentar también con window.parent para casos en iframe
|
| 2639 |
+
try {{
|
| 2640 |
+
window.parent.postMessage({{
|
| 2641 |
+
type: 'faceBoxes',
|
| 2642 |
+
boxes: {json.dumps(bbox_list)}
|
| 2643 |
+
}}, '*');
|
| 2644 |
+
console.log('Mensaje también enviado a window.parent');
|
| 2645 |
+
}} catch(e) {{
|
| 2646 |
+
console.error('Error enviando a window.parent:', e);
|
| 2647 |
+
}}
|
| 2648 |
}})();
|
| 2649 |
</script>
|
| 2650 |
"""
|