File size: 7,959 Bytes
c336617
dede013
 
 
8775ba7
dede013
5eaad66
dede013
 
9ca0a51
dede013
9ca0a51
 
dede013
b15f8f4
 
dede013
 
b15f8f4
81a4855
fd4d6b9
b15f8f4
dede013
789804a
dede013
 
 
 
 
 
 
 
0cf156b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fd4d6b9
dede013
 
 
 
4c8602a
c5f95fa
dede013
9178928
151b2c7
c5f95fa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9178928
 
 
 
ecd4291
9178928
ecd4291
4edd752
9178928
c5f95fa
9178928
dede013
 
0b20aa4
dede013
f02d1fe
dede013
 
 
 
 
 
 
 
 
 
89b1493
bd7dcdf
 
4567594
bd7dcdf
 
 
 
817b9ca
bd7dcdf
 
 
 
 
 
15eac66
4edd752
bd7dcdf
4edd752
bd7dcdf
ecd4291
bd7dcdf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fd4d6b9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0cf156b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
import os
import cv2
import torch
import numpy as np
import streamlit as st
import requests
import sqlite3

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'
STUDENT_DB = "students.db"

# Set Streamlit title
st.title("AIML-Student Attendance System")

# Load employee image paths
image_paths = glob(os.path.join(data_path, '*.jpg'))

# 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))

# # Check if roll exists
# def roll_exists(rno):
#     with sqlite3.connect(STUDENT_DB) as conn:
#         cur = conn.execute("SELECT * FROM students WHERE rno=?", (rno,))
#         return cur.fetchone()

# # Register new student
# def register_student(rno, sname, sclass, image):
#     image_path = os.path.join(data_path, f"{rno}.jpg")
#     image.save(image_path)
#     with sqlite3.connect(STUDENT_DB) as conn:
#         conn.execute("INSERT INTO students (rno, sname, sclass, image_path) VALUES (?, ?, ?, ?)",
#                      (rno, sname, sclass, image_path))


# # Load all registered images
# def load_registered_images():
#     with sqlite3.connect(STUDENT_DB) as conn:
        # return [row[3] for row in conn.execute("SELECT * FROM students")]
        
# Define function to match face embeddings
def prod_function(app, prod_path, webcam_img_pil):
    np_webcam = np.array(webcam_img_pil)
    cv2_webcam = cv2.cvtColor(np_webcam, cv2.COLOR_RGB2BGR)
    
    webcam_faces = app.get(cv2_webcam)
    if not webcam_faces:
        return [], cv2_webcam
    
    results = []
    for webcam_face in webcam_faces:
        webcam_emb = torch.tensor(webcam_face.embedding, dtype=torch.float32)
        similarity_scores = []
        for path in prod_path:
            img = cv2.imread(path)
            faces = app.get(img, max_num=1)
            if not faces:
                similarity_scores.append(torch.tensor(-1.0))
                continue

            face_emb = torch.tensor(faces[0].embedding, dtype=torch.float32)
            score = F.cosine_similarity(face_emb, webcam_emb, dim=0)
            similarity_scores.append(score)

        similarity_scores = torch.stack(similarity_scores)
        best_match_idx = torch.argmax(similarity_scores)
        best_score = similarity_scores[best_match_idx].item()
        
        # Get coordinates
        x1, y1, x2, y2 = [int(i) for i in webcam_face.bbox]
        if best_score >= 0.6:
            matched_name = os.path.basename(prod_path[best_match_idx]).split('.')[0]
        else:
            matched_name = "Unknown"
        results.append({'bbox': (x1, y1, x2, y2), 'name': matched_name})
        
    
    return results, cv2_webcam

# Streamlit tabs
about_tab, app_tab, register_tab = st.tabs(["About the app", "Face Recognition", "Register new Student"])

with about_tab:
    st.markdown("""
    # πŸ‘οΈβ€πŸ—¨οΈ AI-Powered Face Recognition Attendance System
    Secure and Accurate Attendance using Vision Transformer + ArcFace Embeddings.
    
    - **Automated, contactless attendance logging**
    - **Uses InsightFace ArcFace embeddings for recognition**
    - **Real-time logging with confidence scoring**
    - **Future Scope: Mask-aware recognition, Group detection, and more**
    """)

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 face..."):
                image_pil = Image.open(picture)
                matches, image_bgr = prod_function(app, image_paths, image_pil)
    
                if not matches:
                    st.warning("No face detected in the captured image.")
                else:
                    # st.write("Similarity Scores:", prediction_scores)
                    for match in matches:
                        x1, y1, x2, y2 = match['bbox']
                        matched_name = match['name']
                        color = (0, 255, 0) if matched_name != "Unknown" else (0, 0, 255)
                        cv2.rectangle(image_bgr, (x1, y1), (x2, y2), color, 2)
                        cv2.putText(image_bgr, matched_name, (x1, y2 + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2)
        
                        if matched_name != "Unknown":
                        # recognized = False
                        # for score, idx in matches:
                        #     if matched_score >= 0.6:
                        #         matched_name = os.path.basename(image_paths[match_idx]).split('.')[0]
                        #         st.success(f"βœ… Welcome: {matched_name}")
                        #         recognizes=True
                            
                            # Send attendance via POST
                            url = "https://nielit-attendance.glitch.me/adds"
                            data = {'rno': 15, 'sname': matched_name, 'sclass': 7}
                            try:
                                response = requests.post(url, data=data)
                                if response.status_code == 200:
                                    st.success(f"Attendance marked successfully for {matched_name}.")
                                else:
                                    st.warning(f"Failed to update attendance for {matched_name}.")
                            except Exception as e:
                                st.error(f"Request failed: {e}")
                        else:
                            st.warning("❌ Face match not found or too low confidence.")
                    
                # Convert back to RGB for display
                image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)
                st.image(image_rgb, caption="Detected Faces", use_container_width=True)

with register_tab:
    upload_mode = st.radio("Choose Method", ["πŸ“€ Upload Image", "πŸ“· Take Photo"])
    if upload_mode == "πŸ“€ Upload Image":
        upload = st.file_uploader("Upload Student Image with plain background(.jpg)", type="jpg")
        if upload:
            image_pil = Image.open(upload)
    else:
        camera_capture = st.camera_input("Capture Image")
        if camera_capture:
            image_pil = Image.open(camera_capture)

    if 'image_pil' in locals():
        st.image(image_pil, caption="Student Image", use_container_width=True)
        rno = st.text_input("Roll Number")
        sname = st.text_input("Student Name")
        sclass = st.text_input("Class")

        if st.button("Register"):
            if not (rno and sname and sclass):
                st.warning("Fill all details.")
            # # Save locally in employees folder
            #     local_path = os.path.join("employees", f"{sname}.jpg")
            #     image_pil.save(local_path)

            #     # Convert image to base64 for sending to Glitch
            #     buffered = BytesIO()
            #     image_pil.save(buffered, format="JPEG")
            #     img_str = base64.b64encode(buffered.getvalue()).decode()

            #     # Prepare data for POST request
            #     glitch_url = "https://your-glitch-app.glitch.me/register"  # Replace this
            #     data = {
            #         "rno": rno,
            #         "sname": sname,
            #         "sclass": sclass,
            #         "image_base64": img_str,
            #     }

            #     response = requests.post(glitch_url, json=data)
            #     result = response.json()

            #     if result["status"] == "success":
            #         st.success(result["message"])
            #     else:
            #         st.error(result["message"])