Spaces:
Sleeping
Sleeping
import os | |
import cv2 | |
import torch | |
import numpy as np | |
import streamlit as st | |
import requests | |
from PIL import Image | |
from glob import glob | |
from insightface.app import FaceAnalysis | |
import torch.nn.functional as F | |
# Set the device | |
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
# Global Variables | |
IMAGE_SHAPE = 640 | |
data_path = 'employees' | |
webcam_path = 'captured_image.jpg' | |
# Set Streamlit title | |
st.title("πΈ AI-Driven Multi-Face Attendance System") | |
# Load employee image paths | |
image_paths = glob(os.path.join(data_path, '*.jpg')) | |
employee_names = [os.path.basename(p).split('.')[0] for p in image_paths] | |
# Initialize Face Analysis | |
app = FaceAnalysis(name="buffalo_l") # ArcFace model | |
app.prepare(ctx_id=0 if torch.cuda.is_available() else -1, det_size=(IMAGE_SHAPE, IMAGE_SHAPE)) | |
# Load all employee embeddings | |
employee_embeddings = [] | |
for path in image_paths: | |
img = cv2.imread(path) | |
faces = app.get(img, max_num=1) | |
if faces: | |
embedding = torch.tensor(faces[0].embedding, dtype=torch.float32) | |
employee_embeddings.append(embedding) | |
else: | |
employee_embeddings.append(None) | |
# Function to compute cosine similarity and find best match | |
def find_best_match(face_emb): | |
similarities = [] | |
for emp_emb in employee_embeddings: | |
if emp_emb is None: | |
similarities.append(torch.tensor(-1.0)) | |
continue | |
score = F.cosine_similarity(emp_emb, face_emb, dim=0) | |
similarities.append(score) | |
scores = torch.stack(similarities) | |
best_idx = torch.argmax(scores) | |
return scores[best_idx].item(), best_idx | |
# Streamlit tabs | |
about_tab, app_tab = st.tabs(["About the app", "Face Recognition"]) | |
with about_tab: | |
st.markdown(""" | |
# π― Multi-Face AI Attendance System | |
- Detects multiple faces in one frame | |
- Matches using ArcFace embeddings | |
- Marks attendance for each recognized student | |
""") | |
st.markdown("### π Students Trained in the System:") | |
if employee_names: | |
for name in employee_names: | |
st.markdown(f"- {name}") | |
else: | |
st.warning("No student images found in the 'employees/' folder.") | |
with app_tab: | |
enable = st.checkbox("Enable camera") | |
picture = st.camera_input("Take a picture", disabled=not enable) | |
if picture is not None: | |
with st.spinner("Analyzing faces..."): | |
image_pil = Image.open(picture) | |
image_np = np.array(image_pil) | |
image_bgr = cv2.cvtColor(image_np, cv2.COLOR_RGB2BGR) | |
detected_faces = app.get(image_bgr) | |
if not detected_faces: | |
st.warning("No face detected in the captured image.") | |
else: | |
marked_names = [] | |
for face in detected_faces: | |
emb = torch.tensor(face.embedding, dtype=torch.float32) | |
score, idx = find_best_match(emb) | |
if score >= 0.6: | |
name = employee_names[idx] | |
# Draw box and name | |
x1, y1, x2, y2 = [int(i) for i in face.bbox] | |
cv2.rectangle(image_bgr, (x1, y1), (x2, y2), (0, 255, 0), 2) | |
cv2.rectangle(image_bgr, (x1, y2 - 35), (x2, y2), (0, 255, 0), cv2.FILLED) | |
cv2.putText(image_bgr, name, (x1 + 6, y2 - 10), cv2.FONT_HERSHEY_COMPLEX, 0.8, (255, 255, 255), 2) | |
# Mark attendance only once | |
if name not in marked_names: | |
marked_names.append(name) | |
# Send attendance via POST | |
url = "https://attendance-system-25.glitch.me/adds" | |
data = {'rno': 15, 'sname': name, 'sclass': 7} | |
try: | |
response = requests.post(url, data=data) | |
if response.status_code == 200: | |
st.success(f"β Attendance marked for {name}") | |
else: | |
st.warning(f"β οΈ Attendance failed for {name}") | |
except Exception as e: | |
st.error(f"Request failed for {name}: {e}") | |
else: | |
x1, y1, x2, y2 = [int(i) for i in face.bbox] | |
cv2.rectangle(image_bgr, (x1, y1), (x2, y2), (0, 0, 255), 2) | |
cv2.putText(image_bgr, "Unknown", (x1 + 6, y2 - 10), cv2.FONT_HERSHEY_COMPLEX, 0.8, (255, 255, 255), 2) | |
# Show final image | |
final_image = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB) | |
st.image(final_image, caption="Detected Faces with Names", use_column_width=True) | |