J
File size: 2,818 Bytes
85fa7d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
"""
Face Processor - Detecci贸n y alineaci贸n de rostros
"""

import cv2
import numpy as np
from mtcnn import MTCNN
from PIL import Image
from loguru import logger


class FaceProcessor:
    """
    Procesa im谩genes para detectar, alinear y normalizar rostros.
    """
    
    def __init__(self):
        """Inicializa el detector MTCNN"""
        logger.info("Inicializando MTCNN...")
        self.detector = MTCNN()
        logger.success("MTCNN inicializado")
    
    def align_face(self, image):
        """
        Detecta y alinea el rostro en la imagen.
        
        Args:
            image: Imagen PIL o numpy array (RGB)
            
        Returns:
            Rostro alineado y normalizado (160x160) o None si no se detecta
        """
        # Convertir PIL a numpy si es necesario
        if isinstance(image, Image.Image):
            image = np.array(image)
        
        # Asegurar que est谩 en RGB
        if len(image.shape) == 2:  # Grayscale
            image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
        elif image.shape[2] == 4:  # RGBA
            image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)
        
        # Detectar rostros
        faces = self.detector.detect_faces(image)
        
        if len(faces) == 0:
            logger.warning("No se detect贸 ning煤n rostro")
            return None
        
        # Tomar el rostro m谩s grande (m谩s probable que sea el principal)
        face = max(faces, key=lambda x: x['box'][2] * x['box'][3])
        
        # Extraer keypoints
        keypoints = face['keypoints']
        left_eye = keypoints['left_eye']
        right_eye = keypoints['right_eye']
        
        # Calcular 谩ngulo de rotaci贸n para alinear horizontalmente
        dY = right_eye[1] - left_eye[1]
        dX = right_eye[0] - left_eye[0]
        angle = np.degrees(np.arctan2(dY, dX))
        
        # Rotar imagen
        h, w = image.shape[:2]
        center = (w // 2, h // 2)
        M = cv2.getRotationMatrix2D(center, angle, 1.0)
        aligned = cv2.warpAffine(image, M, (w, h), flags=cv2.INTER_CUBIC)
        
        # Recortar rostro con margen
        x, y, width, height = face['box']
        margin = int(min(width, height) * 0.2)  # 20% de margen
        
        x1 = max(0, x - margin)
        y1 = max(0, y - margin)
        x2 = min(w, x + width + margin)
        y2 = min(h, y + height + margin)
        
        face_crop = aligned[y1:y2, x1:x2]
        
        # Resize a 160x160 (est谩ndar FaceNet)
        try:
            face_resized = cv2.resize(face_crop, (160, 160), interpolation=cv2.INTER_AREA)
            logger.debug(f"Rostro detectado y alineado: {face_resized.shape}")
            return face_resized
        except Exception as e:
            logger.error(f"Error al resize: {e}")
            return None