Spaces:
Build error
Build error
Commit
·
b22d5df
1
Parent(s):
3617555
Mejorar la comunicación entre backend y frontend para la detección facial
Browse files- streamlit_app.py +292 -107
streamlit_app.py
CHANGED
|
@@ -131,66 +131,132 @@ def main():
|
|
| 131 |
print(f"Detecting faces with confidence threshold: {conf_threshold}")
|
| 132 |
|
| 133 |
# Obtener dimensiones de la imagen
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
h, w = frame.shape[:2]
|
| 135 |
|
| 136 |
-
#
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
(300, 300), (104.0, 177.0, 123.0))
|
| 141 |
-
|
| 142 |
-
# Pasar el blob a través de la red
|
| 143 |
-
net.setInput(blob)
|
| 144 |
|
| 145 |
-
# Realizar la detección (forward pass)
|
| 146 |
try:
|
| 147 |
-
|
| 148 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
except Exception as e:
|
| 150 |
-
print(f"Error
|
| 151 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
|
| 153 |
-
|
| 154 |
-
bboxes = []
|
| 155 |
-
|
| 156 |
-
# Procesar cada detección
|
| 157 |
-
detection_count = 0
|
| 158 |
-
for i in range(detections.shape[2]):
|
| 159 |
-
# Extraer la confianza (probabilidad) de la detección
|
| 160 |
-
confidence = detections[0, 0, i, 2]
|
| 161 |
|
| 162 |
-
#
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
#
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
# Imprimir información de depuración
|
| 177 |
-
print(f"Detección #{detection_count}: confianza={confidence:.3f}, bbox=[{x1},{y1},{x2},{y2}]")
|
| 178 |
-
|
| 179 |
-
# Saltar cajas inválidas (por ejemplo, con ancho o alto negativo)
|
| 180 |
-
width, height = x2 - x1, y2 - y1
|
| 181 |
-
if width <= 0 or height <= 0:
|
| 182 |
-
print(f"Saltando caja inválida con dimensiones: {width}x{height}")
|
| 183 |
-
continue
|
| 184 |
-
|
| 185 |
-
# Añadir la caja y la confianza a la lista de resultados
|
| 186 |
-
bboxes.append([x1, y1, x2, y2, confidence])
|
| 187 |
-
|
| 188 |
-
# Dar feedback sobre el número de detecciones
|
| 189 |
-
print(f"Total de detecciones con confianza > {conf_threshold}: {detection_count}")
|
| 190 |
-
print(f"Total de cajas válidas: {len(bboxes)}")
|
| 191 |
-
|
| 192 |
-
# Devolver None si no hay detecciones, o la lista de bboxes en caso contrario
|
| 193 |
-
return bboxes if bboxes else None
|
| 194 |
|
| 195 |
# Function for processing face detections
|
| 196 |
def process_face_detections(frame, detections, conf_threshold=0.5, bbox_color=(0, 255, 0)):
|
|
@@ -2490,6 +2556,9 @@ def main():
|
|
| 2490 |
displayCtx.fillText('Rectángulo de Prueba', 210, 90);
|
| 2491 |
console.log('Rectángulo inicial de prueba dibujado');
|
| 2492 |
|
|
|
|
|
|
|
|
|
|
| 2493 |
// Configuración dinámica del FPS (desde Streamlit)
|
| 2494 |
const captureDelay = 1000 / %s;
|
| 2495 |
|
|
@@ -2528,6 +2597,9 @@ def main():
|
|
| 2528 |
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
| 2529 |
displayCtx.drawImage(video, 0, 0, display.width, display.height);
|
| 2530 |
|
|
|
|
|
|
|
|
|
|
| 2531 |
// Dibujar un rectángulo de prueba en cada captura
|
| 2532 |
drawTestRect();
|
| 2533 |
|
|
@@ -2542,26 +2614,62 @@ def main():
|
|
| 2542 |
}
|
| 2543 |
}
|
| 2544 |
|
| 2545 |
-
//
|
| 2546 |
-
|
| 2547 |
-
|
| 2548 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2549 |
|
| 2550 |
-
//
|
| 2551 |
window.addEventListener('message', function(event) {
|
| 2552 |
-
|
|
|
|
|
|
|
|
|
|
| 2553 |
const message = event.data;
|
|
|
|
| 2554 |
|
| 2555 |
-
|
| 2556 |
-
if (
|
| 2557 |
-
|
| 2558 |
-
|
| 2559 |
|
| 2560 |
// Limpiar el canvas y dibujar el frame actual
|
| 2561 |
displayCtx.drawImage(video, 0, 0, display.width, display.height);
|
| 2562 |
|
| 2563 |
// Dibujar cada caja
|
| 2564 |
-
if (boxes
|
| 2565 |
console.log(`Dibujando ${boxes.length} cajas de rostros`);
|
| 2566 |
boxes.forEach(box => {
|
| 2567 |
if (box && box.length >= 5) {
|
|
@@ -2604,13 +2712,20 @@ def main():
|
|
| 2604 |
displayCtx.font = 'bold 18px Arial';
|
| 2605 |
displayCtx.fillText('No se detectan rostros', 20, 30);
|
| 2606 |
console.log('No hay cajas para dibujar o formato inválido');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2607 |
}
|
| 2608 |
|
| 2609 |
// Dibuja un rectángulo de prueba siempre, para verificar que el canvas funciona
|
| 2610 |
drawTestRect();
|
| 2611 |
}
|
| 2612 |
-
}
|
| 2613 |
-
|
|
|
|
|
|
|
| 2614 |
}
|
| 2615 |
});
|
| 2616 |
|
|
@@ -2697,60 +2812,130 @@ def main():
|
|
| 2697 |
# Imprimir en el servidor para depuración
|
| 2698 |
print(f"Cajas de rostros detectadas: {bbox_list}")
|
| 2699 |
|
| 2700 |
-
# Crear script para enviar datos al componente JavaScript
|
| 2701 |
face_boxes_js = f"""
|
| 2702 |
<script>
|
| 2703 |
-
|
| 2704 |
-
|
| 2705 |
-
|
| 2706 |
-
|
| 2707 |
-
|
| 2708 |
-
|
| 2709 |
-
|
| 2710 |
-
|
| 2711 |
-
|
| 2712 |
-
'*'
|
| 2713 |
-
);
|
| 2714 |
|
| 2715 |
-
//
|
| 2716 |
-
|
| 2717 |
-
|
| 2718 |
-
|
| 2719 |
-
|
| 2720 |
-
|
| 2721 |
-
|
| 2722 |
-
|
| 2723 |
-
|
| 2724 |
-
|
| 2725 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2726 |
}}
|
| 2727 |
-
console.log("Mensaje enviado a todos los iframes");
|
| 2728 |
-
}} catch(e) {{
|
| 2729 |
-
console.error("Error enviando mensaje directo a iframes:", e);
|
| 2730 |
}}
|
| 2731 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2732 |
</script>
|
| 2733 |
"""
|
| 2734 |
|
| 2735 |
# Inyectar el script en la página
|
| 2736 |
-
|
| 2737 |
|
| 2738 |
-
# Agregar un script para
|
| 2739 |
-
#
|
| 2740 |
-
|
| 2741 |
<script>
|
| 2742 |
-
|
| 2743 |
-
|
| 2744 |
-
|
| 2745 |
-
|
| 2746 |
-
|
| 2747 |
-
|
| 2748 |
-
|
| 2749 |
-
|
| 2750 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2751 |
</script>
|
| 2752 |
"""
|
| 2753 |
-
|
| 2754 |
except Exception as e:
|
| 2755 |
st.error(f"Error processing camera frame: {str(e)}")
|
| 2756 |
st.info("Camera continues to run. Processing will be attempted on next frame.")
|
|
|
|
| 131 |
print(f"Detecting faces with confidence threshold: {conf_threshold}")
|
| 132 |
|
| 133 |
# Obtener dimensiones de la imagen
|
| 134 |
+
if frame is None or frame.size == 0:
|
| 135 |
+
print("Error: Empty frame received in detect_face_dnn")
|
| 136 |
+
return None
|
| 137 |
+
|
| 138 |
h, w = frame.shape[:2]
|
| 139 |
|
| 140 |
+
# Verificar que el modelo esté cargado correctamente
|
| 141 |
+
if net is None:
|
| 142 |
+
print("Error: Face detection model not loaded")
|
| 143 |
+
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
|
|
|
|
| 145 |
try:
|
| 146 |
+
# Intentamos con el DNN
|
| 147 |
+
# Crear un blob de la imagen (redimensionada a 300x300 y normalizada)
|
| 148 |
+
# IMPORTANTE: Los valores de media (104.0, 177.0, 123.0) son específicos
|
| 149 |
+
# para el modelo res10_300x300_ssd_iter_140000.caffemodel entrenado en Caffe
|
| 150 |
+
blob = cv2.dnn.blobFromImage(cv2.resize(frame, (300, 300)), 1.0,
|
| 151 |
+
(300, 300), (104.0, 177.0, 123.0))
|
| 152 |
+
|
| 153 |
+
# Pasar el blob a través de la red
|
| 154 |
+
net.setInput(blob)
|
| 155 |
+
|
| 156 |
+
# Realizar la detección (forward pass)
|
| 157 |
+
try:
|
| 158 |
+
detections = net.forward()
|
| 159 |
+
print(f"Shape of detection output: {detections.shape}")
|
| 160 |
+
except Exception as e:
|
| 161 |
+
print(f"Error al procesar la imagen con el modelo DNN: {str(e)}")
|
| 162 |
+
# Intentar con Haar Cascades como alternativa
|
| 163 |
+
return detect_face_haar(frame, conf_threshold)
|
| 164 |
+
|
| 165 |
+
# Variable para almacenar las cajas delimitadoras
|
| 166 |
+
bboxes = []
|
| 167 |
+
|
| 168 |
+
# Procesar cada detección
|
| 169 |
+
detection_count = 0
|
| 170 |
+
for i in range(detections.shape[2]):
|
| 171 |
+
# Extraer la confianza (probabilidad) de la detección
|
| 172 |
+
confidence = detections[0, 0, i, 2]
|
| 173 |
+
|
| 174 |
+
# Filtrar detecciones débiles por confianza
|
| 175 |
+
if confidence > conf_threshold:
|
| 176 |
+
detection_count += 1
|
| 177 |
+
# La red da las coordenadas de la caja normalizadas entre 0 y 1
|
| 178 |
+
# Multiplicamos por ancho y alto para obtener coordenadas en píxeles
|
| 179 |
+
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
|
| 180 |
+
|
| 181 |
+
# Convertir a enteros
|
| 182 |
+
x1, y1, x2, y2 = box.astype("int")
|
| 183 |
+
|
| 184 |
+
# Garantizar que las coordenadas estén dentro de los límites de la imagen
|
| 185 |
+
x1, y1 = max(0, x1), max(0, y1)
|
| 186 |
+
x2, y2 = min(w, x2), min(h, y2)
|
| 187 |
+
|
| 188 |
+
# Imprimir información de depuración
|
| 189 |
+
print(f"Detección #{detection_count}: confianza={confidence:.3f}, bbox=[{x1},{y1},{x2},{y2}]")
|
| 190 |
+
|
| 191 |
+
# Saltar cajas inválidas (por ejemplo, con ancho o alto negativo)
|
| 192 |
+
width, height = x2 - x1, y2 - y1
|
| 193 |
+
if width <= 0 or height <= 0:
|
| 194 |
+
print(f"Saltando caja inválida con dimensiones: {width}x{height}")
|
| 195 |
+
continue
|
| 196 |
+
|
| 197 |
+
# Añadir la caja y la confianza a la lista de resultados
|
| 198 |
+
bboxes.append([x1, y1, x2, y2, confidence])
|
| 199 |
+
|
| 200 |
+
# Dar feedback sobre el número de detecciones
|
| 201 |
+
print(f"Total de detecciones con confianza > {conf_threshold}: {detection_count}")
|
| 202 |
+
print(f"Total de cajas válidas: {len(bboxes)}")
|
| 203 |
+
|
| 204 |
+
# Si no se encontraron rostros con DNN, intentar con Haar cascades como fallback
|
| 205 |
+
if not bboxes:
|
| 206 |
+
print("No se encontraron rostros con DNN, intentando con Haar cascades...")
|
| 207 |
+
return detect_face_haar(frame, conf_threshold)
|
| 208 |
+
|
| 209 |
+
# Devolver None si no hay detecciones, o la lista de bboxes en caso contrario
|
| 210 |
+
return bboxes if bboxes else None
|
| 211 |
+
|
| 212 |
except Exception as e:
|
| 213 |
+
print(f"Error general en detect_face_dnn: {str(e)}")
|
| 214 |
+
# En caso de error, usar Haar cascades como fallback
|
| 215 |
+
return detect_face_haar(frame, conf_threshold)
|
| 216 |
+
|
| 217 |
+
# Función alternativa para detectar rostros usando Haar Cascades
|
| 218 |
+
def detect_face_haar(frame, conf_threshold=0.3):
|
| 219 |
+
"""Detecta rostros usando Haar Cascades como método de respaldo"""
|
| 220 |
+
try:
|
| 221 |
+
# Carga el clasificador Haar Cascade para rostros (debería estar cargado globalmente,
|
| 222 |
+
# pero lo hacemos aquí para asegurar que esté disponible)
|
| 223 |
+
if 'haar_face_cascade' not in st.session_state:
|
| 224 |
+
cascade_path = cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
|
| 225 |
+
st.session_state.haar_face_cascade = cv2.CascadeClassifier(cascade_path)
|
| 226 |
+
print(f"Haar cascade loaded from {cascade_path}")
|
| 227 |
+
|
| 228 |
+
# Convertir a escala de grises
|
| 229 |
+
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
| 230 |
+
|
| 231 |
+
# Ecualizar el histograma para mejorar contraste
|
| 232 |
+
gray = cv2.equalizeHist(gray)
|
| 233 |
+
|
| 234 |
+
# Detectar rostros - la confianza se controla con minNeighbors
|
| 235 |
+
# Usamos el umbral de confianza como guía para minNeighbors
|
| 236 |
+
min_neighbors = max(3, int(conf_threshold * 10))
|
| 237 |
+
faces = st.session_state.haar_face_cascade.detectMultiScale(
|
| 238 |
+
gray,
|
| 239 |
+
scaleFactor=1.1,
|
| 240 |
+
minNeighbors=min_neighbors,
|
| 241 |
+
minSize=(30, 30),
|
| 242 |
+
flags=cv2.CASCADE_SCALE_IMAGE
|
| 243 |
+
)
|
| 244 |
|
| 245 |
+
print(f"Haar cascade found {len(faces)} faces")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 246 |
|
| 247 |
+
# Convertir al formato [x1, y1, x2, y2, confidence]
|
| 248 |
+
# Note que Haar Cascades no proporciona un valor de confianza real
|
| 249 |
+
bboxes = []
|
| 250 |
+
for (x, y, w, h) in faces:
|
| 251 |
+
# Usamos un valor fijo de confianza de 0.8 para Haar detections
|
| 252 |
+
# Esto es arbitrario pero útil para el procesamiento posterior
|
| 253 |
+
bboxes.append([x, y, x+w, y+h, 0.8])
|
| 254 |
+
|
| 255 |
+
return bboxes if bboxes else None
|
| 256 |
+
|
| 257 |
+
except Exception as e:
|
| 258 |
+
print(f"Error en detect_face_haar: {str(e)}")
|
| 259 |
+
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 260 |
|
| 261 |
# Function for processing face detections
|
| 262 |
def process_face_detections(frame, detections, conf_threshold=0.5, bbox_color=(0, 255, 0)):
|
|
|
|
| 2556 |
displayCtx.fillText('Rectángulo de Prueba', 210, 90);
|
| 2557 |
console.log('Rectángulo inicial de prueba dibujado');
|
| 2558 |
|
| 2559 |
+
// Arreglo para almacenar las últimas cajas recibidas
|
| 2560 |
+
let lastBoxes = [];
|
| 2561 |
+
|
| 2562 |
// Configuración dinámica del FPS (desde Streamlit)
|
| 2563 |
const captureDelay = 1000 / %s;
|
| 2564 |
|
|
|
|
| 2597 |
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
| 2598 |
displayCtx.drawImage(video, 0, 0, display.width, display.height);
|
| 2599 |
|
| 2600 |
+
// Dibujar cajas almacenadas (para mantener las cajas entre frames)
|
| 2601 |
+
drawStoredBoxes();
|
| 2602 |
+
|
| 2603 |
// Dibujar un rectángulo de prueba en cada captura
|
| 2604 |
drawTestRect();
|
| 2605 |
|
|
|
|
| 2614 |
}
|
| 2615 |
}
|
| 2616 |
|
| 2617 |
+
// Función para dibujar las cajas almacenadas
|
| 2618 |
+
function drawStoredBoxes() {
|
| 2619 |
+
if (lastBoxes && lastBoxes.length > 0) {
|
| 2620 |
+
console.log(`Redibujando ${lastBoxes.length} cajas almacenadas`);
|
| 2621 |
+
lastBoxes.forEach(box => {
|
| 2622 |
+
if (box && box.length >= 5) {
|
| 2623 |
+
const [x1, y1, x2, y2, confidence] = box;
|
| 2624 |
+
|
| 2625 |
+
// Dibujar rectángulo
|
| 2626 |
+
displayCtx.strokeStyle = '#00FF00'; // Verde brillante
|
| 2627 |
+
displayCtx.lineWidth = 6; // Línea más gruesa
|
| 2628 |
+
displayCtx.strokeRect(x1, y1, x2-x1, y2-y1);
|
| 2629 |
+
|
| 2630 |
+
// Añadir un relleno semitransparente para mayor visibilidad
|
| 2631 |
+
displayCtx.fillStyle = 'rgba(0, 255, 0, 0.2)';
|
| 2632 |
+
displayCtx.fillRect(x1, y1, x2-x1, y2-y1);
|
| 2633 |
+
|
| 2634 |
+
// Añadir un fondo para el texto
|
| 2635 |
+
displayCtx.fillStyle = 'rgba(0, 0, 0, 0.7)';
|
| 2636 |
+
displayCtx.fillRect(x1, y1-25, 140, 25);
|
| 2637 |
+
|
| 2638 |
+
// Dibujar etiqueta con fuente más grande
|
| 2639 |
+
displayCtx.fillStyle = '#FFFF00'; // Amarillo brillante
|
| 2640 |
+
displayCtx.font = 'bold 18px Arial';
|
| 2641 |
+
displayCtx.fillText(`Rostro: ${confidence.toFixed(2)}`, x1+5, y1-5);
|
| 2642 |
+
}
|
| 2643 |
+
});
|
| 2644 |
+
|
| 2645 |
+
// Añadir contador de rostros
|
| 2646 |
+
displayCtx.fillStyle = 'rgba(0, 0, 0, 0.7)';
|
| 2647 |
+
displayCtx.fillRect(10, 10, 200, 30);
|
| 2648 |
+
displayCtx.fillStyle = '#FFFFFF';
|
| 2649 |
+
displayCtx.font = 'bold 18px Arial';
|
| 2650 |
+
displayCtx.fillText(`Rostros: ${lastBoxes.length}`, 20, 30);
|
| 2651 |
+
}
|
| 2652 |
+
}
|
| 2653 |
|
| 2654 |
+
// Debug para todos los mensajes recibidos
|
| 2655 |
window.addEventListener('message', function(event) {
|
| 2656 |
+
console.log('>>> Mensaje recibido:', event.data);
|
| 2657 |
+
|
| 2658 |
+
// Verificar si es un mensaje con cajas de rostros
|
| 2659 |
+
if (event.data && event.data.type === 'faceBoxes') {
|
| 2660 |
const message = event.data;
|
| 2661 |
+
console.log('🔴 Recibido mensaje de cajas faciales:', message);
|
| 2662 |
|
| 2663 |
+
const boxes = message.boxes;
|
| 2664 |
+
if (boxes && Array.isArray(boxes)) {
|
| 2665 |
+
// Guardar cajas para redibujar en cada frame
|
| 2666 |
+
lastBoxes = boxes;
|
| 2667 |
|
| 2668 |
// Limpiar el canvas y dibujar el frame actual
|
| 2669 |
displayCtx.drawImage(video, 0, 0, display.width, display.height);
|
| 2670 |
|
| 2671 |
// Dibujar cada caja
|
| 2672 |
+
if (boxes.length > 0) {
|
| 2673 |
console.log(`Dibujando ${boxes.length} cajas de rostros`);
|
| 2674 |
boxes.forEach(box => {
|
| 2675 |
if (box && box.length >= 5) {
|
|
|
|
| 2712 |
displayCtx.font = 'bold 18px Arial';
|
| 2713 |
displayCtx.fillText('No se detectan rostros', 20, 30);
|
| 2714 |
console.log('No hay cajas para dibujar o formato inválido');
|
| 2715 |
+
|
| 2716 |
+
// Limpiar cajas almacenadas si se recibe explícitamente un array vacío
|
| 2717 |
+
if (!message.forceDisplay) {
|
| 2718 |
+
lastBoxes = [];
|
| 2719 |
+
}
|
| 2720 |
}
|
| 2721 |
|
| 2722 |
// Dibuja un rectángulo de prueba siempre, para verificar que el canvas funciona
|
| 2723 |
drawTestRect();
|
| 2724 |
}
|
| 2725 |
+
} else if (event.data && event.data.type === 'testRect') {
|
| 2726 |
+
// Si solo recibimos una señal de prueba, dibujar el rectángulo
|
| 2727 |
+
console.log('Recibido señal para dibujar rectángulo de prueba');
|
| 2728 |
+
drawTestRect();
|
| 2729 |
}
|
| 2730 |
});
|
| 2731 |
|
|
|
|
| 2812 |
# Imprimir en el servidor para depuración
|
| 2813 |
print(f"Cajas de rostros detectadas: {bbox_list}")
|
| 2814 |
|
| 2815 |
+
# Crear script para enviar datos al componente JavaScript - Versión mejorada con múltiples métodos
|
| 2816 |
face_boxes_js = f"""
|
| 2817 |
<script>
|
| 2818 |
+
// Versión mejorada para envío garantizado de detecciones faciales
|
| 2819 |
+
(function() {{
|
| 2820 |
+
console.log("DETECCIÓN FACIAL: Enviando datos de {len(bbox_list)} rostros");
|
| 2821 |
+
|
| 2822 |
+
const message = {{
|
| 2823 |
+
type: 'faceBoxes',
|
| 2824 |
+
boxes: {json.dumps(bbox_list)},
|
| 2825 |
+
timestamp: Date.now()
|
| 2826 |
+
}};
|
|
|
|
|
|
|
| 2827 |
|
| 2828 |
+
// Función para enviar mensaje a todos los destinos posibles
|
| 2829 |
+
function broadcastMessage() {{
|
| 2830 |
+
console.log("Intentando enviar mensaje a todos los destinos posibles");
|
| 2831 |
+
|
| 2832 |
+
// 1. Enviar directo al window
|
| 2833 |
+
try {{
|
| 2834 |
+
window.postMessage(message, '*');
|
| 2835 |
+
console.log("Mensaje enviado a window");
|
| 2836 |
+
}} catch(e) {{
|
| 2837 |
+
console.error("Error enviando a window:", e);
|
| 2838 |
+
}}
|
| 2839 |
+
|
| 2840 |
+
// 2. Enviar al parent (desde iframe)
|
| 2841 |
+
try {{
|
| 2842 |
+
window.parent.postMessage(message, '*');
|
| 2843 |
+
console.log("Mensaje enviado a parent");
|
| 2844 |
+
}} catch(e) {{
|
| 2845 |
+
console.error("Error enviando a parent:", e);
|
| 2846 |
+
}}
|
| 2847 |
+
|
| 2848 |
+
// 3. Enviar a todos los iframes en la página
|
| 2849 |
+
try {{
|
| 2850 |
+
const frames = document.getElementsByTagName('iframe');
|
| 2851 |
+
console.log(`Encontrados ${frames.length} iframes`);
|
| 2852 |
+
for(let i = 0; i < frames.length; i++) {{
|
| 2853 |
+
try {{
|
| 2854 |
+
frames[i].contentWindow.postMessage(message, '*');
|
| 2855 |
+
console.log(`Mensaje enviado a iframe[${i}]`);
|
| 2856 |
+
}} catch(e) {{
|
| 2857 |
+
console.error(`Error enviando a iframe[${i}]:`, e);
|
| 2858 |
+
}}
|
| 2859 |
+
}}
|
| 2860 |
+
}} catch(e) {{
|
| 2861 |
+
console.error("Error accediendo a iframes:", e);
|
| 2862 |
+
}}
|
| 2863 |
+
|
| 2864 |
+
// 4. Enviar a todos los iframes en el parent
|
| 2865 |
+
try {{
|
| 2866 |
+
const parentFrames = window.parent.document.getElementsByTagName('iframe');
|
| 2867 |
+
console.log(`Encontrados ${parentFrames.length} iframes en parent`);
|
| 2868 |
+
for(let i = 0; i < parentFrames.length; i++) {{
|
| 2869 |
+
try {{
|
| 2870 |
+
parentFrames[i].contentWindow.postMessage(message, '*');
|
| 2871 |
+
console.log(`Mensaje enviado a parent.iframe[${i}]`);
|
| 2872 |
+
}} catch(e) {{
|
| 2873 |
+
console.error(`Error enviando a parent.iframe[${i}]:`, e);
|
| 2874 |
+
}}
|
| 2875 |
+
}}
|
| 2876 |
+
}} catch(e) {{
|
| 2877 |
+
console.error("Error accediendo a iframes de parent:", e);
|
| 2878 |
}}
|
|
|
|
|
|
|
|
|
|
| 2879 |
}}
|
| 2880 |
+
|
| 2881 |
+
// Llamar inmediatamente
|
| 2882 |
+
broadcastMessage();
|
| 2883 |
+
|
| 2884 |
+
// Reintentar varias veces para asegurar la entrega
|
| 2885 |
+
setTimeout(broadcastMessage, 100);
|
| 2886 |
+
setTimeout(broadcastMessage, 500);
|
| 2887 |
+
setTimeout(broadcastMessage, 1000);
|
| 2888 |
+
}})();
|
| 2889 |
</script>
|
| 2890 |
"""
|
| 2891 |
|
| 2892 |
# Inyectar el script en la página
|
| 2893 |
+
components.html(face_boxes_js, height=0, width=0)
|
| 2894 |
|
| 2895 |
+
# Agregar un script para forzar la visualización de los rectángulos
|
| 2896 |
+
# y actualizar el estado del canvas incluso si no hay detecciones
|
| 2897 |
+
force_display_js = """
|
| 2898 |
<script>
|
| 2899 |
+
(function() {
|
| 2900 |
+
console.log("Forzando actualización del rectángulo de prueba");
|
| 2901 |
+
|
| 2902 |
+
const noDetectionMessage = {
|
| 2903 |
+
type: 'faceBoxes',
|
| 2904 |
+
boxes: [],
|
| 2905 |
+
timestamp: Date.now(),
|
| 2906 |
+
forceDisplay: true
|
| 2907 |
+
};
|
| 2908 |
+
|
| 2909 |
+
// Enviar a todos los destinos posibles
|
| 2910 |
+
try { window.parent.postMessage(noDetectionMessage, '*'); } catch(e) {}
|
| 2911 |
+
try { window.postMessage(noDetectionMessage, '*'); } catch(e) {}
|
| 2912 |
+
|
| 2913 |
+
try {
|
| 2914 |
+
const frames = document.getElementsByTagName('iframe');
|
| 2915 |
+
for(let i = 0; i < frames.length; i++) {
|
| 2916 |
+
try { frames[i].contentWindow.postMessage(noDetectionMessage, '*'); } catch(e) {}
|
| 2917 |
+
}
|
| 2918 |
+
} catch(e) {}
|
| 2919 |
+
|
| 2920 |
+
// Enviar señal de prueba para verificar comunicación
|
| 2921 |
+
const testMessage = {
|
| 2922 |
+
type: 'testRect',
|
| 2923 |
+
timestamp: Date.now()
|
| 2924 |
+
};
|
| 2925 |
+
|
| 2926 |
+
try { window.parent.postMessage(testMessage, '*'); } catch(e) {}
|
| 2927 |
+
try { window.postMessage(testMessage, '*'); } catch(e) {}
|
| 2928 |
+
|
| 2929 |
+
try {
|
| 2930 |
+
const frames = document.getElementsByTagName('iframe');
|
| 2931 |
+
for(let i = 0; i < frames.length; i++) {
|
| 2932 |
+
try { frames[i].contentWindow.postMessage(testMessage, '*'); } catch(e) {}
|
| 2933 |
+
}
|
| 2934 |
+
} catch(e) {}
|
| 2935 |
+
})();
|
| 2936 |
</script>
|
| 2937 |
"""
|
| 2938 |
+
components.html(force_display_js, height=0, width=0)
|
| 2939 |
except Exception as e:
|
| 2940 |
st.error(f"Error processing camera frame: {str(e)}")
|
| 2941 |
st.info("Camera continues to run. Processing will be attempted on next frame.")
|