| """
|
| 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
|
|
|
|
|
| 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:
|
|
|
| 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."
|
|
|
|
|
| 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
|
|
|
|
|
| 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)
|
|
|
|
|
| face_distance = face_recognition.face_distance([stored_encoding], captured_encoding)[0]
|
|
|
|
|
| confidence = (1 - face_distance) * 100
|
|
|
|
|
| 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:
|
|
|
| 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
|
|
|
|
|
| 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 []
|
|
|
|
|
|
|
| face_recognition_module = FaceRecognitionModule()
|
|
|