File size: 4,796 Bytes
d6a3687
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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)