import os import logging from typing import List, Tuple, Optional import face_recognition import numpy as np logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') class AttendanceSystem: def __init__(self, database_dir: str): self.database_dir = database_dir self.database = self._load_database() def _load_database(self) -> dict: database = {} for filename in os.listdir(self.database_dir): if filename.endswith(('.jpg', '.jpeg', '.png')): roll_number = os.path.splitext(filename)[0] image_path = os.path.join(self.database_dir, filename) try: image = face_recognition.load_image_file(image_path) encodings = face_recognition.face_encodings(image) if encodings: database[roll_number] = encodings[0] else: logging.warning(f"No face found in {filename}. Skipping.") except Exception as e: logging.error(f"Error processing {filename}: {str(e)}") return database def process_classroom_image(self, image_path: str) -> List[Tuple[str, float]]: try: classroom_image = face_recognition.load_image_file(image_path) except Exception as e: logging.error(f"Error loading classroom image: {str(e)}") return [] face_locations = face_recognition.face_locations(classroom_image) if not face_locations: logging.warning("No faces detected in the classroom image.") return [] face_encodings = face_recognition.face_encodings(classroom_image, face_locations) matches = [] for face_encoding in face_encodings: match = self._find_best_match(face_encoding) if match: matches.append(match) return matches def _find_best_match(self, face_encoding: np.ndarray) -> Optional[Tuple[str, float]]: best_match = None best_distance = float('inf') for roll_number, known_encoding in self.database.items(): distance = face_recognition.face_distance([known_encoding], face_encoding)[0] if distance < best_distance: best_distance = distance best_match = (roll_number, distance) if best_match and best_match[1] < 0.6: # Adjust this threshold as needed return best_match return None def record_attendance(self, course: str, date: str, classroom_image_path: str) -> List[str]: matches = self.process_classroom_image(classroom_image_path) present_students = [roll_number for roll_number, _ in matches] logging.info(f"Recorded attendance for {len(present_students)} students in {course} on {date}") return present_students def add_to_database(self, roll_number: str, image_path: str) -> bool: try: image = face_recognition.load_image_file(image_path) encodings = face_recognition.face_encodings(image) if encodings: self.database[roll_number] = encodings[0] logging.info(f"Added {roll_number} to the database successfully.") return True else: logging.warning(f"No face found in the image for {roll_number}. Not added to database.") return False except Exception as e: logging.error(f"Error adding {roll_number} to database: {str(e)}") return False