Spaces:
Sleeping
Sleeping
Commit ·
fc85f1e
1
Parent(s): f504d9a
Mejorado detector facial y control de flujo para imágenes con demasiados rostros
Browse files- app.py +92 -89
- services/face_service.py +3 -3
- utils/face_validation.py +2 -1
app.py
CHANGED
|
@@ -302,100 +302,103 @@ elif page == "Visual Analysis":
|
|
| 302 |
display_face_validation_result(face_validation_result, img_array)
|
| 303 |
|
| 304 |
# Only continue if we don't have too many faces
|
| 305 |
-
if should_continue_processing():
|
| 306 |
-
#
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
# Verificar si es óptimo basado en las dimensiones (si no existe ya la clave)
|
| 320 |
-
if "is_optimal" not in dimension_result:
|
| 321 |
-
width = dimension_result["width"]
|
| 322 |
-
height = dimension_result["height"]
|
| 323 |
-
dimension_result["is_optimal"] = width >= 640 and height >= 480
|
| 324 |
-
except Exception as e:
|
| 325 |
-
st.error(f"Error validating dimensions: {str(e)}")
|
| 326 |
-
# Crear un resultado predeterminado si hay un error
|
| 327 |
height, width = img_array.shape[:2]
|
| 328 |
-
dimension_result =
|
| 329 |
-
|
| 330 |
-
"height": height,
|
| 331 |
-
"is_optimal": width >= 640 and height >= 480
|
| 332 |
-
}
|
| 333 |
|
| 334 |
-
#
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
quality_result["
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
quality_result["recommendations"] = []
|
| 369 |
-
except Exception as e:
|
| 370 |
-
st.error(f"Error checking quality: {str(e)}")
|
| 371 |
-
# Crear un resultado predeterminado si hay un error
|
| 372 |
-
quality_result = {
|
| 373 |
-
"score": 50,
|
| 374 |
-
"label": "Fair",
|
| 375 |
-
"brightness": 50,
|
| 376 |
-
"contrast": 30,
|
| 377 |
-
"sharpness_label": "Fair",
|
| 378 |
-
"recommendations": ["Use a clearer image for better analysis."]
|
| 379 |
-
}
|
| 380 |
|
| 381 |
-
|
| 382 |
-
|
|
|
|
|
|
|
| 383 |
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
|
|
|
| 387 |
|
| 388 |
-
if
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 399 |
|
| 400 |
except Exception as e:
|
| 401 |
st.error(f"Error processing image: {str(e)}")
|
|
|
|
| 302 |
display_face_validation_result(face_validation_result, img_array)
|
| 303 |
|
| 304 |
# Only continue if we don't have too many faces
|
| 305 |
+
if not should_continue_processing():
|
| 306 |
+
# Si hay demasiados rostros, terminamos la ejecución aquí
|
| 307 |
+
st.stop()
|
| 308 |
+
|
| 309 |
+
# 2. Validar dimensiones (como en la versión local)
|
| 310 |
+
try:
|
| 311 |
+
dimension_result = img_service.validate_image_dimensions(img_array)
|
| 312 |
+
# Asegurarnos de que dimension_result contiene las claves necesarias
|
| 313 |
+
if not isinstance(dimension_result, dict):
|
| 314 |
+
dimension_result = {}
|
| 315 |
+
|
| 316 |
+
# Asegurarnos de que contiene las claves necesarias
|
| 317 |
+
if "width" not in dimension_result:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 318 |
height, width = img_array.shape[:2]
|
| 319 |
+
dimension_result["width"] = width
|
| 320 |
+
dimension_result["height"] = height
|
|
|
|
|
|
|
|
|
|
| 321 |
|
| 322 |
+
# Verificar si es óptimo basado en las dimensiones (si no existe ya la clave)
|
| 323 |
+
if "is_optimal" not in dimension_result:
|
| 324 |
+
width = dimension_result["width"]
|
| 325 |
+
height = dimension_result["height"]
|
| 326 |
+
dimension_result["is_optimal"] = width >= 640 and height >= 480
|
| 327 |
+
except Exception as e:
|
| 328 |
+
st.error(f"Error validating dimensions: {str(e)}")
|
| 329 |
+
# Crear un resultado predeterminado si hay un error
|
| 330 |
+
height, width = img_array.shape[:2]
|
| 331 |
+
dimension_result = {
|
| 332 |
+
"width": width,
|
| 333 |
+
"height": height,
|
| 334 |
+
"is_optimal": width >= 640 and height >= 480
|
| 335 |
+
}
|
| 336 |
+
|
| 337 |
+
# 3. Evaluar calidad (como en la versión local)
|
| 338 |
+
try:
|
| 339 |
+
quality_result = img_service.check_image_quality(img_array)
|
| 340 |
+
# Asegurarnos de que quality_result contiene las claves necesarias
|
| 341 |
+
if not isinstance(quality_result, dict):
|
| 342 |
+
quality_result = {}
|
| 343 |
+
|
| 344 |
+
# Añadir claves faltantes si es necesario
|
| 345 |
+
if "score" not in quality_result:
|
| 346 |
+
quality_result["score"] = 50 # Valor predeterminado
|
| 347 |
+
|
| 348 |
+
if "label" not in quality_result:
|
| 349 |
+
score = quality_result["score"]
|
| 350 |
+
if score >= 70:
|
| 351 |
+
quality_result["label"] = "Good"
|
| 352 |
+
elif score >= 40:
|
| 353 |
+
quality_result["label"] = "Fair"
|
| 354 |
+
else:
|
| 355 |
+
quality_result["label"] = "Poor"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 356 |
|
| 357 |
+
if "brightness" not in quality_result:
|
| 358 |
+
# Calcular brillo si no existe
|
| 359 |
+
gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
|
| 360 |
+
quality_result["brightness"] = np.mean(gray) / 2.55 # Convertir a porcentaje
|
| 361 |
|
| 362 |
+
if "contrast" not in quality_result:
|
| 363 |
+
# Calcular contraste si no existe
|
| 364 |
+
gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
|
| 365 |
+
quality_result["contrast"] = np.std(gray) / 2.55 # Convertir a porcentaje
|
| 366 |
|
| 367 |
+
if "sharpness_label" not in quality_result:
|
| 368 |
+
quality_result["sharpness_label"] = "Good" # Valor predeterminado
|
| 369 |
+
|
| 370 |
+
if "recommendations" not in quality_result:
|
| 371 |
+
quality_result["recommendations"] = []
|
| 372 |
+
except Exception as e:
|
| 373 |
+
st.error(f"Error checking quality: {str(e)}")
|
| 374 |
+
# Crear un resultado predeterminado si hay un error
|
| 375 |
+
quality_result = {
|
| 376 |
+
"score": 50,
|
| 377 |
+
"label": "Fair",
|
| 378 |
+
"brightness": 50,
|
| 379 |
+
"contrast": 30,
|
| 380 |
+
"sharpness_label": "Fair",
|
| 381 |
+
"recommendations": ["Use a clearer image for better analysis."]
|
| 382 |
+
}
|
| 383 |
+
|
| 384 |
+
# Guardar la imagen en session_state para mantener consistencia
|
| 385 |
+
st.session_state.original_image = img_array
|
| 386 |
+
|
| 387 |
+
# 4. Verificar si se debe usar imagen mejorada
|
| 388 |
+
use_improved = "use_improved_image" in st.session_state and st.session_state["use_improved_image"]
|
| 389 |
+
img_to_process = img_array # Por defecto usamos la imagen original
|
| 390 |
+
|
| 391 |
+
if use_improved:
|
| 392 |
+
try:
|
| 393 |
+
# Convertir a BGR para procesamiento OpenCV
|
| 394 |
+
img_bgr = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR)
|
| 395 |
+
# Aplicar técnicas avanzadas de preprocesamiento
|
| 396 |
+
enhanced_bgr = img_service.enhance_image_for_facial_detection(img_bgr)
|
| 397 |
+
# Convertir de vuelta a RGB
|
| 398 |
+
img_to_process = cv2.cvtColor(enhanced_bgr, cv2.COLOR_BGR2RGB)
|
| 399 |
+
except Exception as e:
|
| 400 |
+
st.warning(f"Could not apply advanced image enhancements: {str(e)}")
|
| 401 |
+
# Ya tenemos img_to_process = img_array por defecto, no necesitamos asignarlo de nuevo
|
| 402 |
|
| 403 |
except Exception as e:
|
| 404 |
st.error(f"Error processing image: {str(e)}")
|
services/face_service.py
CHANGED
|
@@ -64,9 +64,9 @@ class FaceDetectionService:
|
|
| 64 |
# Detect faces with optimized parameters for better detection
|
| 65 |
faces = self.face_cascade.detectMultiScale(
|
| 66 |
gray,
|
| 67 |
-
scaleFactor=1.
|
| 68 |
-
minNeighbors=
|
| 69 |
-
minSize=(
|
| 70 |
flags=cv2.CASCADE_SCALE_IMAGE
|
| 71 |
)
|
| 72 |
|
|
|
|
| 64 |
# Detect faces with optimized parameters for better detection
|
| 65 |
faces = self.face_cascade.detectMultiScale(
|
| 66 |
gray,
|
| 67 |
+
scaleFactor=1.2, # Aumentado de 1.1 a 1.2 para reducir falsos positivos
|
| 68 |
+
minNeighbors=6, # Aumentado de 5 a 6 para ser más selectivo
|
| 69 |
+
minSize=(50, 50), # Aumentado tamaño mínimo de 30x30 a 50x50
|
| 70 |
flags=cv2.CASCADE_SCALE_IMAGE
|
| 71 |
)
|
| 72 |
|
utils/face_validation.py
CHANGED
|
@@ -97,4 +97,5 @@ def should_continue_processing() -> bool:
|
|
| 97 |
Returns:
|
| 98 |
Boolean indicating whether to continue processing
|
| 99 |
"""
|
| 100 |
-
|
|
|
|
|
|
| 97 |
Returns:
|
| 98 |
Boolean indicating whether to continue processing
|
| 99 |
"""
|
| 100 |
+
# Si la validación no ha ocurrido aún, asumimos que debemos continuar
|
| 101 |
+
return not st.session_state.get("too_many_faces", False)
|