Spaces:
Build error
Build error
Commit
·
2c2574b
1
Parent(s):
ca08464
Mejora deteccion facial
Browse files- streamlit_app.py +96 -48
streamlit_app.py
CHANGED
|
@@ -117,6 +117,7 @@ def main():
|
|
| 117 |
def detect_face_dnn(net, frame, conf_threshold=0.3):
|
| 118 |
"""
|
| 119 |
Detecta rostros en una imagen utilizando un modelo DNN pre-entrenado.
|
|
|
|
| 120 |
|
| 121 |
Args:
|
| 122 |
net: Modelo DNN cargado
|
|
@@ -134,7 +135,7 @@ def main():
|
|
| 134 |
log_info += f"Forma de la imagen: {frame.shape}\n"
|
| 135 |
|
| 136 |
# Forzar un umbral muy bajo para aumentar la sensibilidad
|
| 137 |
-
internal_threshold = 0.
|
| 138 |
|
| 139 |
# Añadir impresión de depuración para el umbral usado
|
| 140 |
print(f"Detecting faces with original threshold: {conf_threshold}, using internal threshold: {internal_threshold}")
|
|
@@ -156,7 +157,7 @@ def main():
|
|
| 156 |
with open("diagnostico_deteccion.txt", "a") as f:
|
| 157 |
f.write(log_info)
|
| 158 |
print(log_info)
|
| 159 |
-
return
|
| 160 |
|
| 161 |
# Pasar el blob a través de la red
|
| 162 |
try:
|
|
@@ -167,7 +168,7 @@ def main():
|
|
| 167 |
with open("diagnostico_deteccion.txt", "a") as f:
|
| 168 |
f.write(log_info)
|
| 169 |
print(log_info)
|
| 170 |
-
return
|
| 171 |
|
| 172 |
# Realizar la detección (forward pass)
|
| 173 |
try:
|
|
@@ -241,7 +242,7 @@ def main():
|
|
| 241 |
# Verificar si hay detecciones con umbral más bajo para depuración
|
| 242 |
for i in range(detections.shape[2]):
|
| 243 |
confidence = detections[0, 0, i, 2]
|
| 244 |
-
if confidence > 0.
|
| 245 |
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
|
| 246 |
x1, y1, x2, y2 = box.astype("int")
|
| 247 |
log_info += f"Detección de baja confianza: {confidence:.3f} en [{x1},{y1},{x2},{y2}]\n"
|
|
@@ -295,38 +296,48 @@ def main():
|
|
| 295 |
# Ecualizar el histograma para mejorar contraste
|
| 296 |
gray = cv2.equalizeHist(gray)
|
| 297 |
|
| 298 |
-
#
|
| 299 |
-
|
| 300 |
-
min_neighbors =
|
|
|
|
|
|
|
|
|
|
| 301 |
faces = st.session_state.haar_face_cascade.detectMultiScale(
|
| 302 |
gray,
|
| 303 |
-
scaleFactor=
|
| 304 |
minNeighbors=min_neighbors,
|
| 305 |
-
minSize=
|
| 306 |
flags=cv2.CASCADE_SCALE_IMAGE
|
| 307 |
)
|
| 308 |
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
# Convertir al formato [x1, y1, x2, y2, confidence]
|
| 312 |
-
# Note que Haar Cascades no proporciona un valor de confianza real
|
| 313 |
bboxes = []
|
| 314 |
for (x, y, w, h) in faces:
|
| 315 |
-
#
|
| 316 |
-
#
|
| 317 |
-
bboxes.append([x, y, x+w, y+h,
|
| 318 |
|
| 319 |
-
return bboxes
|
| 320 |
|
| 321 |
except Exception as e:
|
| 322 |
-
print(f"Error en
|
| 323 |
-
return
|
| 324 |
|
| 325 |
# Function for processing face detections
|
| 326 |
def process_face_detections(frame, detections, conf_threshold=0.5, bbox_color=(0, 255, 0)):
|
| 327 |
# Create a copy for drawing on
|
| 328 |
result_frame = frame.copy()
|
| 329 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 330 |
# Procesar detecciones si son del formato original
|
| 331 |
if isinstance(detections, np.ndarray) and len(detections.shape) == 4:
|
| 332 |
bboxes = []
|
|
@@ -335,7 +346,12 @@ def main():
|
|
| 335 |
|
| 336 |
for i in range(detections.shape[2]):
|
| 337 |
confidence = detections[0, 0, i, 2]
|
| 338 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 339 |
x1 = int(detections[0, 0, i, 3] * frame_w)
|
| 340 |
y1 = int(detections[0, 0, i, 4] * frame_h)
|
| 341 |
x2 = int(detections[0, 0, i, 5] * frame_w)
|
|
@@ -347,29 +363,40 @@ def main():
|
|
| 347 |
x2 = max(0, min(x2, frame_w - 1))
|
| 348 |
y2 = max(0, min(y2, frame_h - 1))
|
| 349 |
|
| 350 |
-
#
|
| 351 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 352 |
|
| 353 |
# Añadir texto con la confianza
|
| 354 |
label = f"{confidence:.2f}"
|
| 355 |
-
cv2.putText(result_frame, label, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.
|
| 356 |
|
| 357 |
# Añadir a la lista de bounding boxes
|
| 358 |
bboxes.append([x1, y1, x2, y2, confidence])
|
| 359 |
else:
|
| 360 |
# Si ya es una lista de bounding boxes (formato nuevo)
|
| 361 |
-
bboxes = detections
|
|
|
|
| 362 |
# Dibujar bounding boxes
|
| 363 |
for bbox in bboxes:
|
| 364 |
if len(bbox) == 5: # Asegurarse de que el bounding box tiene el formato correcto
|
| 365 |
x1, y1, x2, y2, confidence = bbox
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 373 |
|
| 374 |
return result_frame, bboxes
|
| 375 |
|
|
@@ -840,6 +867,19 @@ def main():
|
|
| 840 |
# File uploader for images
|
| 841 |
file_buffer = st.file_uploader("Upload an image", type=['jpg', 'jpeg', 'png'])
|
| 842 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 843 |
if file_buffer is not None:
|
| 844 |
# Read the file and convert it to OpenCV format
|
| 845 |
raw_bytes = np.asarray(bytearray(file_buffer.read()), dtype=np.uint8)
|
|
@@ -874,29 +914,18 @@ def main():
|
|
| 874 |
st.subheader("Processed Image")
|
| 875 |
st.image(processed_image, channels='BGR', use_container_width=True)
|
| 876 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 877 |
# Convert OpenCV image to PIL for download
|
| 878 |
pil_img = Image.fromarray(processed_image[:, :, ::-1])
|
| 879 |
st.markdown(
|
| 880 |
get_image_download_link(pil_img, "face_detection_result.jpg", "📥 Download Processed Image"),
|
| 881 |
unsafe_allow_html=True
|
| 882 |
)
|
| 883 |
-
|
| 884 |
-
# Show metrics if enabled
|
| 885 |
-
if show_metrics:
|
| 886 |
-
st.subheader("Processing Metrics")
|
| 887 |
-
col1, col2, col3 = st.columns(3)
|
| 888 |
-
col1.metric("Processing Time", f"{processing_time:.4f} seconds")
|
| 889 |
-
col2.metric("Faces Detected", len(bboxes))
|
| 890 |
-
col3.metric("Confidence Threshold", f"{conf_threshold:.2f}")
|
| 891 |
-
|
| 892 |
-
# Display detailed metrics in an expandable section
|
| 893 |
-
with st.expander("Detailed Detection Information"):
|
| 894 |
-
if bboxes:
|
| 895 |
-
st.write("Detected faces with confidence scores:")
|
| 896 |
-
for i, bbox in enumerate(bboxes):
|
| 897 |
-
st.write(f"Face #{i+1}: Confidence = {bbox[4]:.4f}")
|
| 898 |
-
else:
|
| 899 |
-
st.write("No faces detected in the image.")
|
| 900 |
|
| 901 |
else: # Video mode
|
| 902 |
# Video mode options
|
|
@@ -1082,6 +1111,19 @@ def main():
|
|
| 1082 |
# File uploader for images
|
| 1083 |
file_buffer = st.file_uploader("Upload an image", type=['jpg', 'jpeg', 'png'])
|
| 1084 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1085 |
if file_buffer is not None:
|
| 1086 |
# Read the file and convert it to OpenCV format
|
| 1087 |
raw_bytes = np.asarray(bytearray(file_buffer.read()), dtype=np.uint8)
|
|
@@ -1131,6 +1173,12 @@ def main():
|
|
| 1131 |
st.subheader("Processed Image")
|
| 1132 |
st.image(processed_image, channels='BGR', use_container_width=True)
|
| 1133 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1134 |
# Convert OpenCV image to PIL for download
|
| 1135 |
pil_img = Image.fromarray(processed_image[:, :, ::-1])
|
| 1136 |
st.markdown(
|
|
|
|
| 117 |
def detect_face_dnn(net, frame, conf_threshold=0.3):
|
| 118 |
"""
|
| 119 |
Detecta rostros en una imagen utilizando un modelo DNN pre-entrenado.
|
| 120 |
+
Si no se detectan rostros, usa automáticamente Haar Cascades como respaldo.
|
| 121 |
|
| 122 |
Args:
|
| 123 |
net: Modelo DNN cargado
|
|
|
|
| 135 |
log_info += f"Forma de la imagen: {frame.shape}\n"
|
| 136 |
|
| 137 |
# Forzar un umbral muy bajo para aumentar la sensibilidad
|
| 138 |
+
internal_threshold = 0.05 # Usar este umbral internamente para mayor sensibilidad
|
| 139 |
|
| 140 |
# Añadir impresión de depuración para el umbral usado
|
| 141 |
print(f"Detecting faces with original threshold: {conf_threshold}, using internal threshold: {internal_threshold}")
|
|
|
|
| 157 |
with open("diagnostico_deteccion.txt", "a") as f:
|
| 158 |
f.write(log_info)
|
| 159 |
print(log_info)
|
| 160 |
+
return detect_face_haar(frame, conf_threshold)
|
| 161 |
|
| 162 |
# Pasar el blob a través de la red
|
| 163 |
try:
|
|
|
|
| 168 |
with open("diagnostico_deteccion.txt", "a") as f:
|
| 169 |
f.write(log_info)
|
| 170 |
print(log_info)
|
| 171 |
+
return detect_face_haar(frame, conf_threshold)
|
| 172 |
|
| 173 |
# Realizar la detección (forward pass)
|
| 174 |
try:
|
|
|
|
| 242 |
# Verificar si hay detecciones con umbral más bajo para depuración
|
| 243 |
for i in range(detections.shape[2]):
|
| 244 |
confidence = detections[0, 0, i, 2]
|
| 245 |
+
if confidence > 0.01: # Umbral extremadamente bajo para depuración
|
| 246 |
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
|
| 247 |
x1, y1, x2, y2 = box.astype("int")
|
| 248 |
log_info += f"Detección de baja confianza: {confidence:.3f} en [{x1},{y1},{x2},{y2}]\n"
|
|
|
|
| 296 |
# Ecualizar el histograma para mejorar contraste
|
| 297 |
gray = cv2.equalizeHist(gray)
|
| 298 |
|
| 299 |
+
# Parámetros más sensibles para la detección con Haar
|
| 300 |
+
scale_factor = 1.05 # Más lento pero más preciso (era 1.1)
|
| 301 |
+
min_neighbors = 3 # Valor más bajo, más detecciones pero más falsos positivos (era 5)
|
| 302 |
+
min_size = (20, 20) # Tamaño mínimo más pequeño (era 30, 30)
|
| 303 |
+
|
| 304 |
+
# Detectar rostros con clasificador Haar
|
| 305 |
faces = st.session_state.haar_face_cascade.detectMultiScale(
|
| 306 |
gray,
|
| 307 |
+
scaleFactor=scale_factor,
|
| 308 |
minNeighbors=min_neighbors,
|
| 309 |
+
minSize=min_size,
|
| 310 |
flags=cv2.CASCADE_SCALE_IMAGE
|
| 311 |
)
|
| 312 |
|
| 313 |
+
# Convertir a formato bounding box [x1, y1, x2, y2, confianza]
|
|
|
|
|
|
|
|
|
|
| 314 |
bboxes = []
|
| 315 |
for (x, y, w, h) in faces:
|
| 316 |
+
# Usar un valor de confianza fijo para las detecciones Haar
|
| 317 |
+
confidence = 0.8 # Valor arbitrario alto
|
| 318 |
+
bboxes.append([x, y, x + w, y + h, confidence])
|
| 319 |
|
| 320 |
+
return bboxes
|
| 321 |
|
| 322 |
except Exception as e:
|
| 323 |
+
print(f"Error en detección Haar: {e}")
|
| 324 |
+
return []
|
| 325 |
|
| 326 |
# Function for processing face detections
|
| 327 |
def process_face_detections(frame, detections, conf_threshold=0.5, bbox_color=(0, 255, 0)):
|
| 328 |
# Create a copy for drawing on
|
| 329 |
result_frame = frame.copy()
|
| 330 |
|
| 331 |
+
# Asegurar que bbox_color sea una tupla de 3 elementos para BGR
|
| 332 |
+
if isinstance(bbox_color, tuple) and len(bbox_color) == 3:
|
| 333 |
+
bbox_color_bgr = bbox_color
|
| 334 |
+
else:
|
| 335 |
+
# Usar verde como color predeterminado
|
| 336 |
+
bbox_color_bgr = (0, 255, 0)
|
| 337 |
+
|
| 338 |
+
# Definir grosor para los rectángulos (más grueso para mejor visibilidad)
|
| 339 |
+
thickness = 3
|
| 340 |
+
|
| 341 |
# Procesar detecciones si son del formato original
|
| 342 |
if isinstance(detections, np.ndarray) and len(detections.shape) == 4:
|
| 343 |
bboxes = []
|
|
|
|
| 346 |
|
| 347 |
for i in range(detections.shape[2]):
|
| 348 |
confidence = detections[0, 0, i, 2]
|
| 349 |
+
print(f"Confidence: {confidence}, Threshold: {conf_threshold}")
|
| 350 |
+
|
| 351 |
+
# Usar un umbral muy bajo para mejorar la detección
|
| 352 |
+
effective_threshold = max(0.05, conf_threshold)
|
| 353 |
+
|
| 354 |
+
if confidence > effective_threshold:
|
| 355 |
x1 = int(detections[0, 0, i, 3] * frame_w)
|
| 356 |
y1 = int(detections[0, 0, i, 4] * frame_h)
|
| 357 |
x2 = int(detections[0, 0, i, 5] * frame_w)
|
|
|
|
| 363 |
x2 = max(0, min(x2, frame_w - 1))
|
| 364 |
y2 = max(0, min(y2, frame_h - 1))
|
| 365 |
|
| 366 |
+
# Verificar que el rectángulo es válido
|
| 367 |
+
if x2 <= x1 or y2 <= y1:
|
| 368 |
+
continue
|
| 369 |
+
|
| 370 |
+
# Dibujar el bounding box con línea más gruesa
|
| 371 |
+
cv2.rectangle(result_frame, (x1, y1), (x2, y2), bbox_color_bgr, thickness)
|
| 372 |
|
| 373 |
# Añadir texto con la confianza
|
| 374 |
label = f"{confidence:.2f}"
|
| 375 |
+
cv2.putText(result_frame, label, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, bbox_color_bgr, 2)
|
| 376 |
|
| 377 |
# Añadir a la lista de bounding boxes
|
| 378 |
bboxes.append([x1, y1, x2, y2, confidence])
|
| 379 |
else:
|
| 380 |
# Si ya es una lista de bounding boxes (formato nuevo)
|
| 381 |
+
bboxes = detections if detections is not None else []
|
| 382 |
+
|
| 383 |
# Dibujar bounding boxes
|
| 384 |
for bbox in bboxes:
|
| 385 |
if len(bbox) == 5: # Asegurarse de que el bounding box tiene el formato correcto
|
| 386 |
x1, y1, x2, y2, confidence = bbox
|
| 387 |
+
|
| 388 |
+
# Usar un umbral bajo para la visualización
|
| 389 |
+
effective_threshold = max(0.05, conf_threshold)
|
| 390 |
+
|
| 391 |
+
if confidence >= effective_threshold:
|
| 392 |
+
# Verificar que las coordenadas son válidas
|
| 393 |
+
if x1 >= 0 and y1 >= 0 and x2 > x1 and y2 > y1:
|
| 394 |
+
# Dibujar el bounding box con línea más gruesa
|
| 395 |
+
cv2.rectangle(result_frame, (x1, y1), (x2, y2), bbox_color_bgr, thickness)
|
| 396 |
+
|
| 397 |
+
# Añadir texto con la confianza
|
| 398 |
+
label = f"{confidence:.2f}"
|
| 399 |
+
cv2.putText(result_frame, label, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, bbox_color_bgr, 2)
|
| 400 |
|
| 401 |
return result_frame, bboxes
|
| 402 |
|
|
|
|
| 867 |
# File uploader for images
|
| 868 |
file_buffer = st.file_uploader("Upload an image", type=['jpg', 'jpeg', 'png'])
|
| 869 |
|
| 870 |
+
# Umbral de confianza ajustable
|
| 871 |
+
conf_threshold = st.slider(
|
| 872 |
+
"Umbral de confianza",
|
| 873 |
+
min_value=0.05,
|
| 874 |
+
max_value=0.95,
|
| 875 |
+
value=0.2, # Valor por defecto más bajo (era 0.5)
|
| 876 |
+
step=0.05,
|
| 877 |
+
help="Ajusta este valor para controlar la sensibilidad de la detección facial. Un valor más bajo detecta más rostros pero puede tener falsos positivos."
|
| 878 |
+
)
|
| 879 |
+
|
| 880 |
+
# Color del bounding box
|
| 881 |
+
bbox_color_bgr = (0, 255, 0) # Verde brillante para mejor visibilidad
|
| 882 |
+
|
| 883 |
if file_buffer is not None:
|
| 884 |
# Read the file and convert it to OpenCV format
|
| 885 |
raw_bytes = np.asarray(bytearray(file_buffer.read()), dtype=np.uint8)
|
|
|
|
| 914 |
st.subheader("Processed Image")
|
| 915 |
st.image(processed_image, channels='BGR', use_container_width=True)
|
| 916 |
|
| 917 |
+
# Mostrar mensaje sobre lo que se está viendo
|
| 918 |
+
if len(bboxes) > 0:
|
| 919 |
+
st.success(f"Se detectaron {len(bboxes)} rostros en la imagen.")
|
| 920 |
+
else:
|
| 921 |
+
st.warning("No se detectaron rostros. Prueba ajustar el umbral de confianza o usar otra imagen.")
|
| 922 |
+
|
| 923 |
# Convert OpenCV image to PIL for download
|
| 924 |
pil_img = Image.fromarray(processed_image[:, :, ::-1])
|
| 925 |
st.markdown(
|
| 926 |
get_image_download_link(pil_img, "face_detection_result.jpg", "📥 Download Processed Image"),
|
| 927 |
unsafe_allow_html=True
|
| 928 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 929 |
|
| 930 |
else: # Video mode
|
| 931 |
# Video mode options
|
|
|
|
| 1111 |
# File uploader for images
|
| 1112 |
file_buffer = st.file_uploader("Upload an image", type=['jpg', 'jpeg', 'png'])
|
| 1113 |
|
| 1114 |
+
# Umbral de confianza ajustable
|
| 1115 |
+
conf_threshold = st.slider(
|
| 1116 |
+
"Umbral de confianza",
|
| 1117 |
+
min_value=0.05,
|
| 1118 |
+
max_value=0.95,
|
| 1119 |
+
value=0.2, # Valor por defecto más bajo (era 0.5)
|
| 1120 |
+
step=0.05,
|
| 1121 |
+
help="Ajusta este valor para controlar la sensibilidad de la detección facial. Un valor más bajo detecta más rostros pero puede tener falsos positivos."
|
| 1122 |
+
)
|
| 1123 |
+
|
| 1124 |
+
# Color del bounding box
|
| 1125 |
+
bbox_color_bgr = (0, 255, 0) # Verde brillante para mejor visibilidad
|
| 1126 |
+
|
| 1127 |
if file_buffer is not None:
|
| 1128 |
# Read the file and convert it to OpenCV format
|
| 1129 |
raw_bytes = np.asarray(bytearray(file_buffer.read()), dtype=np.uint8)
|
|
|
|
| 1173 |
st.subheader("Processed Image")
|
| 1174 |
st.image(processed_image, channels='BGR', use_container_width=True)
|
| 1175 |
|
| 1176 |
+
# Mostrar mensaje sobre lo que se está viendo
|
| 1177 |
+
if len(bboxes) > 0:
|
| 1178 |
+
st.success(f"Se detectaron {len(bboxes)} rostros en la imagen.")
|
| 1179 |
+
else:
|
| 1180 |
+
st.warning("No se detectaron rostros. Prueba ajustar el umbral de confianza o usar otra imagen.")
|
| 1181 |
+
|
| 1182 |
# Convert OpenCV image to PIL for download
|
| 1183 |
pil_img = Image.fromarray(processed_image[:, :, ::-1])
|
| 1184 |
st.markdown(
|