""" 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()