""" Face Comparator - Comparación de embeddings con niveles de confianza adaptativos """ import numpy as np from sklearn.metrics.pairwise import cosine_similarity from loguru import logger class FaceComparator: """ Compara embeddings faciales con umbrales adaptativos. Implementa el sistema de 3 niveles: Seguro, Probable, Descartado. """ def __init__(self, threshold=0.75): """ Args: threshold: Umbral base de similitud (0.0-1.0) """ self.threshold = threshold # Umbrales adaptativos self.SECURE_MATCH = 0.85 # >85% = Match Seguro self.PROBABLE_MATCH = 0.72 # 72-85% = Coincidencia Probable # <72% = Descartado def calculate_similarity(self, embedding1, embedding2): """ Calcula la similitud coseno entre dos embeddings. Args: embedding1: Vector de embedding 1 embedding2: Vector de embedding 2 Returns: Similitud entre 0.0 y 1.0 """ emb1 = np.array(embedding1).reshape(1, -1) emb2 = np.array(embedding2).reshape(1, -1) similarity = cosine_similarity(emb1, emb2)[0][0] return float(similarity) def verify_identity(self, source_emb, target_emb): """ Verifica identidad con análisis de confianza adaptativo. Returns: Tupla (nivel_confianza: str, similitud: float) """ similarity = self.calculate_similarity(source_emb, target_emb) if similarity > self.SECURE_MATCH: confidence_level = "Match Seguro" logger.info(f"Match Seguro: {similarity:.3f}") elif similarity > self.PROBABLE_MATCH: confidence_level = "Coincidencia Probable (Requiere revisión)" logger.info(f"Coincidencia Probable: {similarity:.3f}") else: confidence_level = "Descartado" logger.debug(f"Descartado: {similarity:.3f}") return confidence_level, similarity def compare_embeddings(self, query_embedding, candidate_results): """ Compara el embedding query con múltiples candidatos. Args: query_embedding: Embedding de la imagen query candidate_results: Lista de resultados con embeddings Returns: Lista de matches verificados ordenados por similitud """ verified_matches = [] for candidate in candidate_results: if 'embedding' not in candidate: continue # Calcular similitud similarity = self.calculate_similarity( query_embedding, candidate['embedding'] ) # Solo incluir si supera el umbral if similarity >= self.threshold: # Determinar nivel de confianza if similarity > self.SECURE_MATCH: confidence_level = "Match Seguro" elif similarity > self.PROBABLE_MATCH: confidence_level = "Coincidencia Probable" else: confidence_level = "Baja confianza" candidate['similarity'] = similarity candidate['confidence_level'] = confidence_level candidate['embedding_distance'] = 1 - similarity candidate['verified'] = True verified_matches.append(candidate) logger.debug(f"Match verificado: {similarity:.3f} - {confidence_level}") # Ordenar por similitud descendente verified_matches.sort(key=lambda x: x['similarity'], reverse=True) logger.info(f"Comparación completada: {len(verified_matches)}/{len(candidate_results)} verificados") return verified_matches