engine / face_classifier.py
VeuReu's picture
Upload 2 files
6e65ae2 verified
raw
history blame
8.41 kB
"""
Face Classifier Module
Valida caras y detecta género usando DeepFace para filtrar falsos positivos
y asignar nombres automáticos según el género detectado.
"""
import logging
from typing import Optional, Dict, Any
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Configuración de thresholds
# FACE_CONFIDENCE_THRESHOLD: Confianza mínima para aceptar una cara
# Valores: 0.3 = permisivo (acepta muchos falsos positivos)
# 0.6 = balanceado
# 0.8 = estricto (elimina falsos positivos pero puede perder caras reales)
# 0.85 = MUY estricto (solo caras muy claras)
FACE_CONFIDENCE_THRESHOLD = 0.85 # MUY ESTRICTO: eliminar camisetas, letreros, etc.
GENDER_NEUTRAL_THRESHOLD = 0.2 # Diferencia mínima para género neutro
def validate_and_classify_face(image_path: str) -> Optional[Dict[str, Any]]:
"""
Valida si és una cara real i detecta el gènere usant DeepFace.
Args:
image_path: Ruta a la imagen de la cara
Returns:
Dict amb: {
'is_valid_face': bool, # True si és una cara amb confiança alta
'face_confidence': float, # Score de detecció de cara (0-1)
'gender': 'Man' | 'Woman' | 'Neutral',
'gender_confidence': float, # Score de confiança del gènere (0-1)
'man_prob': float,
'woman_prob': float
}
o None si falla completament
"""
try:
import cv2
import numpy as np
from deepface import DeepFace
print(f"[DeepFace] Analitzant: {image_path}")
# PREPROCESAMIENTO: Normalizar iluminación y mejorar contraste
# Esto reduce el impacto de luz/oscuridad en la detección
img = cv2.imread(str(image_path))
if img is None:
print(f"[DeepFace] No se pudo cargar la imagen: {image_path}")
return None
# Convertir a escala de grises (más robusto para detección)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# CLAHE: Adaptive Histogram Equalization
# Normaliza el contraste de forma local, reduciendo efectos de luz
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
normalized = clahe.apply(gray)
# Volver a BGR para DeepFace
normalized_bgr = cv2.cvtColor(normalized, cv2.COLOR_GRAY2BGR)
# Guardar imagen preprocesada temporalmente
import tempfile
import os
temp_dir = tempfile.gettempdir()
temp_path = os.path.join(temp_dir, f"normalized_{os.path.basename(image_path)}")
cv2.imwrite(temp_path, normalized_bgr)
print(f"[DeepFace] Imagen preprocesada con CLAHE: {temp_path}")
# Analitzar gènere amb detecció de cara (usando imagen normalizada)
result = DeepFace.analyze(
img_path=temp_path,
actions=['gender'],
enforce_detection=True, # Intentar detectar cara
detector_backend='opencv',
silent=True
)
# Limpiar archivo temporal
try:
os.remove(temp_path)
except:
pass
# DeepFace pot retornar llista si detecta múltiples cares
if isinstance(result, list):
print(f"[DeepFace] Resultado es lista con {len(result)} elementos")
result = result[0] if result else None
if not result:
print(f"[DeepFace] No s'ha detectat cap cara")
return {
'is_valid_face': False,
'face_confidence': 0.0,
'gender': 'Neutral',
'gender_confidence': 0.0,
'man_prob': 0.0,
'woman_prob': 0.0
}
# LOG: Ver estructura completa del resultado
print(f"[DeepFace] Resultado completo de analyze: {result}")
# Extreure informació de gènere
gender_info = result.get('gender', {})
print(f"[DeepFace] gender_info type: {type(gender_info)}, value: {gender_info}")
if isinstance(gender_info, dict):
# DeepFace retorna percentatges, convertir a 0-1
man_prob = gender_info.get('Man', 0) / 100.0
woman_prob = gender_info.get('Woman', 0) / 100.0
print(f"[DeepFace] Extraído de dict - Man: {man_prob:.3f}, Woman: {woman_prob:.3f}")
else:
# Fallback si el format és diferent
print(f"[DeepFace] gender_info NO es dict, usando fallback 0.5/0.5")
man_prob = 0.5
woman_prob = 0.5
# Determinar gènere basat en les probabilitats
gender_diff = abs(man_prob - woman_prob)
print(f"[DeepFace] Diferencia Man-Woman: {gender_diff:.3f} (threshold neutral={GENDER_NEUTRAL_THRESHOLD})")
# Si la diferència és petita (< threshold), considerar neutre
if gender_diff < GENDER_NEUTRAL_THRESHOLD:
gender = 'Neutral'
gender_confidence = 0.5
print(f"[DeepFace] → Asignado NEUTRAL (diferencia {gender_diff:.3f} < {GENDER_NEUTRAL_THRESHOLD})")
else:
gender = 'Man' if man_prob > woman_prob else 'Woman'
gender_confidence = max(man_prob, woman_prob)
print(f"[DeepFace] → Asignado {gender.upper()} (man_prob={man_prob:.3f}, woman_prob={woman_prob:.3f})")
# Confiança de detecció de cara
# DeepFace no proporciona score directamente en analyze(), pero si retornó resultado
# asumimos que es cara válida con confianza alta
face_confidence = result.get('face_confidence', 0.9) # Default alto si detecta
# Si DeepFace va retornar resultat, assumir que és cara vàlida
is_valid_face = True
print(f"[DeepFace] ===== RESUMEN FINAL =====")
print(f"[DeepFace] is_valid_face: {is_valid_face}")
print(f"[DeepFace] face_confidence: {face_confidence:.3f}")
print(f"[DeepFace] gender: {gender}")
print(f"[DeepFace] gender_confidence: {gender_confidence:.3f}")
print(f"[DeepFace] man_prob: {man_prob:.3f}")
print(f"[DeepFace] woman_prob: {woman_prob:.3f}")
print(f"[DeepFace] ==========================")
return {
'is_valid_face': is_valid_face,
'face_confidence': face_confidence,
'gender': gender,
'gender_confidence': gender_confidence,
'man_prob': man_prob,
'woman_prob': woman_prob
}
except ValueError as e:
# ValueError significa que no es va detectar cara
print(f"[DeepFace] No s'ha detectat cara (ValueError): {e}")
return {
'is_valid_face': False,
'face_confidence': 0.0,
'gender': 'Neutral',
'gender_confidence': 0.0,
'man_prob': 0.0,
'woman_prob': 0.0
}
except Exception as e:
print(f"[DeepFace] Error validant cara: {e}")
return None
def get_random_catalan_name_by_gender(gender: str, seed_value: str = "") -> str:
"""
Genera un nom català aleatori basat en el gènere.
Args:
gender: 'Man', 'Woman', o 'Neutral'
seed_value: Valor per fer el random determinista (opcional)
Returns:
Nom català
"""
noms_home = [
"Jordi", "Marc", "Pau", "Pere", "Joan", "Josep", "David", "Guillem", "Albert",
"Arnau", "Martí", "Bernat", "Oriol", "Roger", "Pol", "Lluís", "Sergi", "Carles", "Xavier"
]
noms_dona = [
"Maria", "Anna", "Laura", "Marta", "Cristina", "Núria", "Montserrat", "Júlia", "Sara", "Carla",
"Alba", "Elisabet", "Rosa", "Gemma", "Sílvia", "Teresa", "Irene", "Laia", "Marina", "Bet"
]
noms_neutre = ["Àlex", "Andrea", "Francis", "Cris", "Noa"]
# Seleccionar llista segons gènere
if gender == 'Woman':
noms = noms_dona
elif gender == 'Man':
noms = noms_home
else: # Neutral
noms = noms_neutre
# Usar hash del seed per seleccionar nom de forma determinista
if seed_value:
hash_val = hash(seed_value)
return noms[abs(hash_val) % len(noms)]
else:
import random
return random.choice(noms)