File size: 7,215 Bytes
f742815
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
"""

Production Face Recognition Module for Attendr

This is the REAL implementation using the face_recognition library

Use this file after installing dlib and face_recognition



To activate:

1. Install dlib and face_recognition

2. Rename face_recognition_module.py to face_recognition_module_MOCK.py

3. Rename this file to face_recognition_module.py

4. Restart the Flask application

"""

import face_recognition
import numpy as np
from config import Config
import logging

# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


class FaceRecognitionModule:
    """Handles all face recognition operations using production library"""
    
    def __init__(self, tolerance=None, model='hog'):
        """

        Initialize face recognition module

        

        Args:

            tolerance: Face matching tolerance (lower = more strict)

            model: Detection model ('hog' or 'cnn')

        """
        self.tolerance = tolerance or Config.FACE_RECOGNITION_TOLERANCE
        self.model = model or Config.FACE_DETECTION_MODEL
        logger.info(f"PRODUCTION Face Recognition Module initialized with tolerance={self.tolerance}, model={self.model}")
    
    def detect_faces(self, image_array):
        """

        Detect faces in an image

        

        Args:

            image_array: numpy array of the image

            

        Returns:

            List of face locations [(top, right, bottom, left), ...]

        """
        try:
            face_locations = face_recognition.face_locations(image_array, model=self.model)
            logger.info(f"Detected {len(face_locations)} face(s) in image")
            return face_locations
        except Exception as e:
            logger.error(f"Face detection error: {str(e)}")
            return []
    
    def generate_face_encoding(self, image_array):
        """

        Generate face encoding from an image

        Implements: CapturedFace(x) - captures and encodes the face

        

        Args:

            image_array: numpy array of the image

            

        Returns:

            tuple: (success, encoding or error_message)

        """
        try:
            # Detect faces first
            face_locations = self.detect_faces(image_array)
            
            if len(face_locations) == 0:
                return False, "No face detected in the image. Please ensure your face is clearly visible."
            
            if len(face_locations) > 1:
                return False, "Multiple faces detected. Please ensure only one person is in the frame."
            
            # Generate encoding
            encodings = face_recognition.face_encodings(image_array, face_locations)
            
            if len(encodings) == 0:
                return False, "Failed to generate face encoding. Please try again with better lighting."
            
            encoding = encodings[0]
            logger.info("Face encoding generated successfully")
            return True, encoding
            
        except Exception as e:
            logger.error(f"Face encoding error: {str(e)}")
            return False, f"Face encoding failed: {str(e)}"
    
    def verify_face(self, captured_encoding, stored_encoding):
        """

        Verify if captured face matches stored encoding

        Implements: MatchStored(x) → FaceMatch(x)

        

        Args:

            captured_encoding: Face encoding from live capture

            stored_encoding: Stored face encoding from database

            

        Returns:

            tuple: (is_match: bool, confidence: float)

        """
        try:
            if captured_encoding is None or stored_encoding is None:
                logger.warning("One or both encodings are None")
                return False, 0.0
            
            # Convert to numpy arrays if needed
            if not isinstance(captured_encoding, np.ndarray):
                captured_encoding = np.array(captured_encoding)
            if not isinstance(stored_encoding, np.ndarray):
                stored_encoding = np.array(stored_encoding)
            
            # Calculate face distance (lower = more similar)
            face_distance = face_recognition.face_distance([stored_encoding], captured_encoding)[0]
            
            # Convert distance to confidence percentage
            confidence = (1 - face_distance) * 100
            
            # Check if match is within tolerance
            is_match = face_distance <= self.tolerance
            
            logger.info(f"Face verification: match={is_match}, confidence={confidence:.2f}%, distance={face_distance:.4f}")
            
            return is_match, confidence
            
        except Exception as e:
            logger.error(f"Face verification error: {str(e)}")
            return False, 0.0
    
    def register_face(self, image_array):
        """

        Complete face registration process

        

        Args:

            image_array: numpy array of the image

            

        Returns:

            tuple: (success, encoding or error_message, face_location)

        """
        try:
            # Detect faces
            face_locations = self.detect_faces(image_array)
            
            if len(face_locations) == 0:
                return False, "No face detected. Please ensure your face is clearly visible and well-lit.", None
            
            if len(face_locations) > 1:
                return False, "Multiple faces detected. Please ensure only one person is in the frame.", None
            
            # Generate encoding
            success, result = self.generate_face_encoding(image_array)
            
            if success:
                return True, result, face_locations[0]
            else:
                return False, result, None
                
        except Exception as e:
            logger.error(f"Face registration error: {str(e)}")
            return False, f"Registration failed: {str(e)}", None
    
    def compare_faces_batch(self, known_encodings, face_encoding_to_check):
        """

        Compare a face encoding against multiple known encodings

        Useful for identifying which student from a list

        

        Args:

            known_encodings: List of known face encodings

            face_encoding_to_check: Face encoding to compare

            

        Returns:

            List of boolean matches

        """
        try:
            if not isinstance(face_encoding_to_check, np.ndarray):
                face_encoding_to_check = np.array(face_encoding_to_check)
            
            matches = face_recognition.compare_faces(
                known_encodings, 
                face_encoding_to_check, 
                tolerance=self.tolerance
            )
            
            return matches
            
        except Exception as e:
            logger.error(f"Batch face comparison error: {str(e)}")
            return []


# Singleton instance
face_recognition_module = FaceRecognitionModule()