BasitAliii's picture
Update app.py
4ff793f verified
import gradio as gr
import sqlite3
import numpy as np
import cv2
from PIL import Image
import io
import os
# Global variable for FaceNet availability
FACE_NET_AVAILABLE = False
# Try to import FaceNet with error handling
try:
from facenet_pytorch import MTCNN, InceptionResnetV1
FACE_NET_AVAILABLE = True
print("βœ… FaceNet models imported successfully")
except ImportError as e:
print(f"❌ FaceNet import error: {e}")
FACE_NET_AVAILABLE = False
class FaceRecognitionSystem:
def __init__(self):
self.setup_database()
global FACE_NET_AVAILABLE
if FACE_NET_AVAILABLE:
try:
# Initialize MTCNN with simpler settings
self.mtcnn = MTCNN(
keep_all=False, # Only detect one face for simplicity
min_face_size=40,
thresholds=[0.6, 0.7, 0.7],
device='cpu'
)
self.resnet = InceptionResnetV1(pretrained='vggface2', classify=False, device='cpu').eval()
print("βœ… FaceNet models loaded successfully")
except Exception as e:
print(f"❌ Error loading FaceNet models: {e}")
FACE_NET_AVAILABLE = False
def setup_database(self):
"""Initialize SQLite database for students and attendance"""
self.conn = sqlite3.connect('face_attendance.db', check_same_thread=False)
self.cursor = self.conn.cursor()
# Students table with face embeddings
self.cursor.execute('''
CREATE TABLE IF NOT EXISTS students (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
student_id TEXT UNIQUE NOT NULL,
class TEXT,
face_embedding BLOB,
registration_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# Attendance table
self.cursor.execute('''
CREATE TABLE IF NOT EXISTS attendance (
id INTEGER PRIMARY KEY AUTOINCREMENT,
student_id TEXT,
name TEXT,
class TEXT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
status TEXT DEFAULT 'Present'
)
''')
self.conn.commit()
print("βœ… Database initialized successfully")
def extract_face_embedding(self, image):
"""
Extract face embedding from image using FaceNet
"""
global FACE_NET_AVAILABLE
if not FACE_NET_AVAILABLE:
return None, "Face recognition not available"
try:
# Convert image to PIL format
if isinstance(image, str):
# File path
pil_image = Image.open(image).convert('RGB')
elif isinstance(image, np.ndarray):
# numpy array
if len(image.shape) == 3:
pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
else:
return None, "Invalid image format"
else:
# Handle Gradio file object
pil_image = Image.open(image).convert('RGB')
# Detect face and get cropped face
face_tensor, prob = self.mtcnn(pil_image, return_prob=True)
if face_tensor is None:
return None, "No face detected in the image"
if prob < 0.9: # Confidence threshold
return None, "Low confidence face detection"
# Ensure proper tensor dimensions [3, 160, 160]
if face_tensor.dim() == 4:
face_tensor = face_tensor.squeeze(0) # Remove batch dimension if present
# Get embedding
with torch.no_grad():
embedding = self.resnet(face_tensor.unsqueeze(0)) # Add batch dimension
embedding_np = embedding.detach().numpy().flatten()
return embedding_np, "Face embedding extracted successfully"
except Exception as e:
return None, f"Error processing image: {str(e)}"
def register_student(self, name, student_id, class_name, image):
"""
Register a new student with face embedding
"""
if not name or not student_id:
return "❌ Please provide name and student ID"
if image is None:
return "❌ Please upload a face image"
# Extract face embedding
embedding, message = self.extract_face_embedding(image)
if embedding is None:
return f"❌ {message}"
# Store in database
try:
self.cursor.execute('''
INSERT INTO students (name, student_id, class, face_embedding)
VALUES (?, ?, ?, ?)
''', (name, student_id, class_name, embedding.tobytes()))
self.conn.commit()
return f"βœ… Student {name} registered successfully with face recognition!"
except sqlite3.IntegrityError:
return f"❌ Student ID {student_id} already exists"
except Exception as e:
return f"❌ Database error: {str(e)}"
def recognize_face(self, image):
"""
Recognize face from image and mark attendance
"""
global FACE_NET_AVAILABLE
if not FACE_NET_AVAILABLE:
return image, "Face recognition not available"
if image is None:
return None, "Please upload an image"
# Extract face embedding from input image
embedding, message = self.extract_face_embedding(image)
if embedding is None:
return image, f"❌ {message}"
# Get all registered students
self.cursor.execute('SELECT student_id, name, class, face_embedding FROM students')
students = self.cursor.fetchall()
if not students:
return image, "❌ No students registered in the system"
# Find the best match
best_match = None
min_distance = float('inf')
threshold = 1.0 # Similarity threshold
for student_id, name, class_name, embedding_blob in students:
known_embedding = np.frombuffer(embedding_blob, dtype=np.float32)
# Calculate Euclidean distance
distance = np.linalg.norm(embedding - known_embedding)
if distance < min_distance and distance < threshold:
min_distance = distance
best_match = (student_id, name, class_name, distance)
if best_match:
student_id, name, class_name, distance = best_match
# Check if already marked today
self.cursor.execute('''
SELECT * FROM attendance
WHERE student_id = ? AND DATE(timestamp) = DATE('now')
''', (student_id,))
existing = self.cursor.fetchone()
if not existing:
# Mark attendance
self.cursor.execute('''
INSERT INTO attendance (student_id, name, class, status)
VALUES (?, ?, ?, 'Present')
''', (student_id, name, class_name))
self.conn.commit()
result_message = f"βœ… Attendance marked for {name} (ID: {student_id})"
result_message += f"\nSimilarity: {1-distance:.3f}"
else:
result_message = f"⚠️ {name} already marked today (ID: {student_id})"
# Convert image for display
display_image = self.prepare_display_image(image)
# Add text to image
if display_image is not None:
cv2.putText(display_image, f"Recognized: {name}", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
cv2.putText(display_image, f"ID: {student_id}", (10, 60),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
cv2.putText(display_image, f"Class: {class_name}", (10, 90),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
cv2.putText(display_image, f"Similarity: {1-distance:.3f}", (10, 120),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
return display_image, result_message
return image, "❌ No matching student found"
def prepare_display_image(self, image):
"""Convert image to display format"""
try:
if isinstance(image, str):
display_image = cv2.imread(image)
elif isinstance(image, np.ndarray):
display_image = image.copy()
if len(display_image.shape) == 3 and display_image.shape[2] == 3:
display_image = cv2.cvtColor(display_image, cv2.COLOR_RGB2BGR)
else:
# Handle Gradio file object
display_image = np.array(Image.open(image))
if len(display_image.shape) == 3 and display_image.shape[2] == 3:
display_image = cv2.cvtColor(display_image, cv2.COLOR_RGB2BGR)
return display_image
except:
return None
def get_all_students(self):
"""Get all registered students"""
self.cursor.execute('SELECT name, student_id, class FROM students ORDER BY name')
students = self.cursor.fetchall()
return students
def get_attendance_records(self, time_range="Today"):
"""Get attendance records"""
if time_range == "Today":
self.cursor.execute('''
SELECT name, student_id, class, timestamp
FROM attendance
WHERE DATE(timestamp) = DATE('now')
ORDER BY timestamp DESC
''')
else:
self.cursor.execute('''
SELECT name, student_id, class, timestamp
FROM attendance
ORDER BY timestamp DESC
LIMIT 50
''')
return self.cursor.fetchall()
# Import torch after FaceNet imports to avoid conflicts
try:
import torch
except ImportError:
torch = None
# Initialize the system
face_system = FaceRecognitionSystem()
def register_student_interface(name, student_id, class_name, image):
return face_system.register_student(name, student_id, class_name, image)
def recognize_face_interface(image):
return face_system.recognize_face(image)
def view_students_interface():
students = face_system.get_all_students()
if not students:
return "No students registered yet"
result = "## πŸ“‹ Registered Students\n\n"
for name, student_id, class_name in students:
result += f"**{name}**\n"
result += f"- ID: {student_id}\n"
result += f"- Class: {class_name}\n\n"
return result
def view_attendance_interface(time_range):
records = face_system.get_attendance_records(time_range)
if not records:
return f"No attendance records for {time_range.lower()}"
result = f"## πŸ“Š {time_range}'s Attendance\n\n"
for name, student_id, class_name, timestamp in records:
time_str = timestamp[:16] if timestamp else "Unknown"
result += f"**{name}** ({student_id})\n"
result += f"- Class: {class_name}\n"
result += f"- Time: {time_str}\n\n"
return result
# Create Gradio interface
with gr.Blocks(title="Face Recognition Attendance System") as demo:
gr.Markdown("# 🎯 Face Recognition Attendance System")
gr.Markdown("AI-powered facial recognition for automated attendance marking")
with gr.Tab("πŸ‘€ Register Student"):
gr.Markdown("### Register New Student with Face Recognition")
with gr.Row():
with gr.Column():
name_input = gr.Textbox(
label="Full Name",
placeholder="Enter student's full name"
)
student_id_input = gr.Textbox(
label="Student ID",
placeholder="Enter unique student ID"
)
class_input = gr.Textbox(
label="Class",
placeholder="Enter class name"
)
image_input = gr.Image(
label="Upload Face Image",
type="filepath"
)
register_btn = gr.Button("Register Student with Face", variant="primary")
with gr.Column():
register_output = gr.Textbox(
label="Registration Status",
lines=5
)
register_btn.click(
register_student_interface,
inputs=[name_input, student_id_input, class_input, image_input],
outputs=register_output
)
with gr.Tab("πŸ“· Mark Attendance"):
gr.Markdown("### Mark Attendance using Face Recognition")
with gr.Row():
with gr.Column():
attendance_image = gr.Image(
label="Upload Photo for Attendance",
type="filepath"
)
recognize_btn = gr.Button("Recognize Faces & Mark Attendance", variant="primary")
with gr.Column():
processed_image = gr.Image(
label="Processed Image"
)
recognition_output = gr.Textbox(
label="Recognition Results",
lines=4
)
recognize_btn.click(
recognize_face_interface,
inputs=[attendance_image],
outputs=[processed_image, recognition_output]
)
with gr.Tab("πŸ“Š View Records"):
gr.Markdown("### View Students and Attendance Records")
with gr.Row():
with gr.Column():
gr.Markdown("#### Registered Students")
students_btn = gr.Button("View All Students", variant="secondary")
students_output = gr.Markdown()
with gr.Column():
gr.Markdown("#### Attendance Records")
time_range = gr.Radio(
choices=["Today", "All Time"],
value="Today",
label="Select Time Range"
)
attendance_btn = gr.Button("View Attendance Records", variant="secondary")
attendance_output = gr.Markdown()
students_btn.click(view_students_interface, outputs=students_output)
attendance_btn.click(
view_attendance_interface,
inputs=[time_range],
outputs=attendance_output
)
with gr.Tab("ℹ️ System Info"):
status_msg = "βœ… **System Status: FULLY OPERATIONAL**" if FACE_NET_AVAILABLE else "⚠️ **System Status: FACE RECOGNITION UNAVAILABLE**"
gr.Markdown(f"## System Status\n{status_msg}")
gr.Markdown("""
## About Face Recognition Attendance System
This system uses state-of-the-art facial recognition technology to automate attendance marking.
### Features:
- **Face Registration**: Register students with their facial data
- **Automatic Recognition**: Recognize students from photos and mark attendance
- **Attendance Management**: View and manage attendance records
### How to Use:
1. **Register Students**: Go to 'Register Student' tab, enter details and upload face photo
2. **Mark Attendance**: Go to 'Mark Attendance' tab, upload photo with faces
3. **View Records**: Check registered students and attendance records
### Technology:
- FaceNet for face recognition
- Gradio for web interface
- SQLite for data storage
""")
if __name__ == "__main__":
demo.launch()