PranavRatnalikar's picture
Update app.py
6496b13 verified
import os
import streamlit as st
import firebase_admin
from firebase_admin import credentials, db
import face_recognition
import numpy as np
import cv2
import mediapipe as mp
import pandas as pd
from datetime import datetime
import dlib
# Initialize MediaPipe
mp_face_detection = mp.solutions.face_detection
mp_drawing = mp.solutions.drawing_utils
# Firebase setup
current_directory = os.path.dirname(os.path.abspath(__file__))
firebase_cred_path = os.path.join(current_directory, "vierp-attendance-system.json")
if not firebase_admin._apps:
cred = credentials.Certificate(firebase_cred_path)
firebase_admin.initialize_app(cred, {
'databaseURL': 'vierp-attendance-system-firebase-adminsdk-8ld44-89fdcba270'
})
# Dlib setup for face alignment
shape_predictor_path = "FaceNet.dat"
detector = dlib.get_frontal_face_detector()
shape_predictor = dlib.shape_predictor(shape_predictor_path)
def add_to_firebase(prn, name, image_encoding):
"""Add face encoding to Firebase without date structure"""
ref = db.reference(f'FaceData/{prn}')
data = {
'name': name,
'encoding': image_encoding.tolist(),
'last_updated': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
ref.set(data)
return True
def mark_single_attendance(prn, name, confidence, status='Present'):
"""Mark attendance for a single student without affecting others"""
current_date = datetime.now().strftime("%Y-%m-%d")
student_ref = db.reference(f'Attendance/{current_date}/{prn}')
data = {
'name': name,
'time': datetime.now().strftime("%H:%M:%S"),
'confidence': confidence,
'status': status
}
student_ref.set(data)
def mark_attendance(present_prns, all_students):
"""Mark attendance in Firebase for all students"""
current_date = datetime.now().strftime("%Y-%m-%d")
attendance_ref = db.reference(f'Attendance/{current_date}')
# Get existing attendance data for the day
existing_attendance = attendance_ref.get() or {}
attendance_data = existing_attendance.copy()
current_time = datetime.now().strftime("%H:%M:%S")
# Mark present students
for prn in present_prns:
attendance_data[prn] = {
'name': all_students[prn]['name'],
'time': current_time,
'confidence': present_prns[prn]['confidence'],
'status': 'Present'
}
# Mark absent students (only for those not already marked today)
for prn, student_data in all_students.items():
if prn not in attendance_data: # Only add if not already marked
attendance_data[prn] = {
'name': student_data['name'],
'time': current_time,
'confidence': 0.0,
'status': 'Absent'
}
attendance_ref.set(attendance_data)
def get_known_faces():
"""Retrieve known faces from Firebase"""
ref = db.reference('FaceData')
face_data = ref.get()
known_encodings = []
known_prns = []
known_names = []
if face_data:
for prn, data in face_data.items():
if 'encoding' in data:
known_encodings.append(np.array(data['encoding']))
known_prns.append(prn)
known_names.append(data['name'])
return known_encodings, known_prns, known_names, face_data
def detect_and_align_faces(image):
"""Detect and align faces using dlib"""
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = detector(gray)
aligned_faces = []
face_locations = []
for face in faces:
shape = shape_predictor(gray, face)
aligned_face = dlib.get_face_chip(image, shape, size=256)
aligned_faces.append(aligned_face)
face_locations.append((face.top(), face.right(), face.bottom(), face.left()))
return aligned_faces, face_locations
def process_image_for_recognition(image):
"""Process image and recognize faces"""
aligned_faces, face_locations = detect_and_align_faces(image)
known_encodings, known_prns, known_names, all_students = get_known_faces()
verification_needed = []
recognition_results = []
present_students = {}
for idx, aligned_face in enumerate(aligned_faces):
face_encoding = face_recognition.face_encodings(aligned_face)[0]
face_distances = face_recognition.face_distance(known_encodings, face_encoding)
if len(face_distances) > 0:
best_match_index = np.argmin(face_distances)
confidence = (1 - face_distances[best_match_index]) * 100
if confidence >= 50:
prn = known_prns[best_match_index]
name = known_names[best_match_index]
present_students[prn] = {
'name': name,
'confidence': confidence
}
recognition_results.append({
'location': face_locations[idx],
'name': name,
'prn': prn,
'confidence': confidence,
'status': 'Confirmed'
})
else:
verification_needed.append({
'location': face_locations[idx],
'face_image': aligned_face,
'encoding': face_encoding,
'matched_prn': known_prns[best_match_index],
'matched_name': known_names[best_match_index],
'confidence': confidence
})
# Mark attendance for all students (present and absent)
mark_attendance(present_students, all_students)
return recognition_results, verification_needed
def draw_results(image, recognition_results):
"""Draw recognition results on image"""
for result in recognition_results:
top, right, bottom, left = result['location']
color = (36, 255, 12) if result['status'] == 'Confirmed' else (0, 0, 255)
cv2.rectangle(image, (left, top), (right, bottom), color, 2)
text = f"{result['name']} ({result['confidence']:.1f}%)"
cv2.putText(image, text, (left, top - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
return image
# Streamlit UI
st.title("Enhanced Face Recognition Attendance System")
# Sidebar for database management
st.sidebar.header("Database Management")
with st.sidebar.expander("Add New Student"):
prn = st.text_input("Enter PRN")
name = st.text_input("Enter Name")
upload_for_db = st.file_uploader("Upload face image", type=["jpg", "jpeg", "png"])
if st.button("Add to Database") and prn and name and upload_for_db:
file_bytes = np.asarray(bytearray(upload_for_db.read()), dtype=np.uint8)
img = cv2.imdecode(file_bytes, 1)
aligned_faces, _ = detect_and_align_faces(img)
if aligned_faces:
encoding = face_recognition.face_encodings(aligned_faces[0])[0]
if add_to_firebase(prn, name, encoding):
st.success(f"Added {name} (PRN: {prn}) to database!")
else:
st.error("No face detected in the image")
# Main interface
st.header("Take Attendance")
uploaded_file = st.file_uploader("Upload image for attendance", type=["jpg", "jpeg", "png"])
if uploaded_file:
# Load and process image
file_bytes = np.asarray(bytearray(uploaded_file.read()), dtype=np.uint8)
image = cv2.imdecode(file_bytes, 1)
with st.spinner("Processing image..."):
recognition_results, verification_needed = process_image_for_recognition(image)
processed_image = draw_results(image.copy(), recognition_results)
# Display results
st.image(cv2.cvtColor(processed_image, cv2.COLOR_BGR2RGB),
caption="Processed Image",
use_column_width=True)
# Handle verifications needed
if verification_needed:
st.subheader("Verification Needed")
for idx, verify_data in enumerate(verification_needed):
col1, col2 = st.columns([1, 2])
with col1:
st.image(cv2.cvtColor(verify_data['face_image'], cv2.COLOR_BGR2RGB),
caption=f"Unconfirmed Face {idx+1}",
width=200)
with col2:
st.write(f"Possible match: {verify_data['matched_name']}")
st.write(f"PRN: {verify_data['matched_prn']}")
st.write(f"Confidence: {verify_data['confidence']:.1f}%")
if st.button(f"Confirm {verify_data['matched_name']}", key=f"confirm_{idx}"):
# Update only this student's attendance
mark_single_attendance(
verify_data['matched_prn'],
verify_data['matched_name'],
verify_data['confidence']
)
st.success(f"Attendance marked for {verify_data['matched_name']}")
if st.button("Not the same person", key=f"reject_{idx}"):
st.info("Rejected - No attendance marked")
# View Attendance Records
with st.sidebar.expander("View Attendance Records"):
date_to_view = st.date_input("Select Date", datetime.now())
formatted_date = date_to_view.strftime("%Y-%m-%d")
attendance_ref = db.reference(f'Attendance/{formatted_date}')
attendance_data = attendance_ref.get()
if attendance_data:
df = pd.DataFrame.from_dict(attendance_data, orient='index')
# Sort by status to show absent students separately
df = df.sort_values(by=['status', 'name'])
st.dataframe(df)
# Add summary statistics
total_students = len(df)
present_students = len(df[df['status'] == 'Present'])
absent_students = len(df[df['status'] == 'Absent'])
st.write(f"Total Students: {total_students}")
st.write(f"Present: {present_students}")
st.write(f"Absent: {absent_students}")
# Download attendance report
csv = df.to_csv().encode('utf-8')
st.download_button(
"Download Attendance Report",
csv,
f"attendance_{formatted_date}.csv",
"text/csv",
key='download-csv'
)
else:
st.info("No attendance records for selected date")