Spaces:
Build error
Build error
Upload app.py
Browse files
app.py
ADDED
@@ -0,0 +1,175 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import cv2
|
3 |
+
import face_recognition
|
4 |
+
import os
|
5 |
+
from datetime import datetime
|
6 |
+
import sqlite3
|
7 |
+
import pandas as pd
|
8 |
+
import numpy as np
|
9 |
+
from PIL import Image
|
10 |
+
|
11 |
+
# --- App Configuration ---
|
12 |
+
st.set_page_config(
|
13 |
+
page_title="Face Recognition Attendance",
|
14 |
+
page_icon="✅",
|
15 |
+
layout="wide"
|
16 |
+
)
|
17 |
+
|
18 |
+
# --- Database Connection ---
|
19 |
+
conn = sqlite3.connect('attendance.db')
|
20 |
+
cursor = conn.cursor()
|
21 |
+
|
22 |
+
# --- Create Attendance Table (if not exists) ---
|
23 |
+
cursor.execute('''
|
24 |
+
CREATE TABLE IF NOT EXISTS attendance (
|
25 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
26 |
+
name TEXT,
|
27 |
+
roll_no TEXT,
|
28 |
+
date TEXT,
|
29 |
+
time TEXT,
|
30 |
+
status TEXT
|
31 |
+
)
|
32 |
+
''')
|
33 |
+
conn.commit()
|
34 |
+
|
35 |
+
# --- Load Known Faces ---
|
36 |
+
def load_known_faces():
|
37 |
+
"""Loads known faces from the 'Photos' directory."""
|
38 |
+
images = []
|
39 |
+
classnames = []
|
40 |
+
directory = "Photos"
|
41 |
+
|
42 |
+
for cls in os.listdir(directory):
|
43 |
+
if os.path.splitext(cls)[1] in [".jpg", ".jpeg"]:
|
44 |
+
img_path = os.path.join(directory, cls)
|
45 |
+
curImg = cv2.imread(img_path)
|
46 |
+
images.append(curImg)
|
47 |
+
classnames.append(os.path.splitext(cls)[0])
|
48 |
+
|
49 |
+
return images, classnames
|
50 |
+
|
51 |
+
# --- Encode Known Faces ---
|
52 |
+
def find_encodings(images):
|
53 |
+
"""Encodes the given images."""
|
54 |
+
encodeList = []
|
55 |
+
for img in images:
|
56 |
+
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
57 |
+
encode = face_recognition.face_encodings(img)[0]
|
58 |
+
encodeList.append(encode)
|
59 |
+
return encodeList
|
60 |
+
|
61 |
+
# --- Load and Encode Known Faces ---
|
62 |
+
Images, classnames = load_known_faces()
|
63 |
+
encodeListKnown = find_encodings(Images)
|
64 |
+
|
65 |
+
# --- Add New Face ---
|
66 |
+
def add_new_face():
|
67 |
+
"""Adds a new face to the system."""
|
68 |
+
new_name = st.text_input("Enter your name:")
|
69 |
+
roll_no = st.text_input("Enter your roll number:")
|
70 |
+
|
71 |
+
img_file_buffer = st.camera_input("Take a picture")
|
72 |
+
if img_file_buffer is not None and new_name and roll_no:
|
73 |
+
image = np.array(Image.open(img_file_buffer)) # Convert to numpy array
|
74 |
+
img_path = os.path.join("Photos", f"{new_name}_{roll_no}.jpg")
|
75 |
+
cv2.imwrite(img_path, image)
|
76 |
+
|
77 |
+
# Update known faces and encodings
|
78 |
+
global Images, classnames, encodeListKnown
|
79 |
+
Images, classnames = load_known_faces()
|
80 |
+
encodeListKnown = find_encodings(Images)
|
81 |
+
|
82 |
+
# Add to database (without updating attendance)
|
83 |
+
date = datetime.now().strftime('%Y-%m-%d')
|
84 |
+
time = datetime.now().strftime('%H:%M:%S')
|
85 |
+
cursor.execute("INSERT INTO attendance (name, roll_no, date, time, status) VALUES (?, ?, ?, ?, 'Registered')",
|
86 |
+
(new_name, roll_no, date, time))
|
87 |
+
conn.commit()
|
88 |
+
|
89 |
+
st.success(f"New face added for {new_name} ({roll_no}).")
|
90 |
+
|
91 |
+
# --- Face Recognition ---
|
92 |
+
def recognize_face():
|
93 |
+
"""Performs face recognition and updates attendance."""
|
94 |
+
img_file_buffer = st.camera_input("Take a picture")
|
95 |
+
if img_file_buffer is not None:
|
96 |
+
with st.spinner("Recognizing face..."):
|
97 |
+
image = np.array(Image.open(img_file_buffer)) # Convert to numpy array
|
98 |
+
imgS = cv2.resize(image, (0, 0), None, 0.25, 0.25)
|
99 |
+
imgS = cv2.cvtColor(imgS, cv2.COLOR_BGR2RGB)
|
100 |
+
facesCurFrame = face_recognition.face_locations(imgS)
|
101 |
+
encodesCurFrame = face_recognition.face_encodings(imgS, facesCurFrame)
|
102 |
+
|
103 |
+
name = "Unknown"
|
104 |
+
roll_no = "Unknown"
|
105 |
+
|
106 |
+
if len(encodesCurFrame) > 0:
|
107 |
+
for encodeFace, faceLoc in zip(encodesCurFrame, facesCurFrame):
|
108 |
+
matches = face_recognition.compare_faces(encodeListKnown, encodeFace)
|
109 |
+
faceDis = face_recognition.face_distance(encodeListKnown, encodeFace)
|
110 |
+
matchIndex = np.argmin(faceDis)
|
111 |
+
|
112 |
+
if matches[matchIndex]:
|
113 |
+
name = classnames[matchIndex].split("_")[0]
|
114 |
+
roll_no = classnames[matchIndex].split("_")[1]
|
115 |
+
|
116 |
+
y1, x2, y2, x1 = faceLoc
|
117 |
+
y1, x2, y2, x1 = y1 * 4, x2 * 4, y2 * 4, x1 * 4
|
118 |
+
cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2)
|
119 |
+
cv2.rectangle(image, (x1, y2 - 35), (x2, y2), (0, 255, 0), cv2.FILLED)
|
120 |
+
cv2.putText(image, name, (x1 + 6, y2 - 6), cv2.FONT_HERSHEY_COMPLEX, 1, (255, 255, 255), 2)
|
121 |
+
|
122 |
+
# Check for duplicate attendance entries
|
123 |
+
date = datetime.now().strftime('%Y-%m-%d')
|
124 |
+
time = datetime.now().strftime('%H:%M:%S')
|
125 |
+
cursor.execute("SELECT * FROM attendance WHERE name=? AND date=? AND time=?", (name, date, time))
|
126 |
+
existing_attendance = cursor.fetchone()
|
127 |
+
|
128 |
+
if existing_attendance:
|
129 |
+
st.info(f"Attendance already recorded for {name} at {time}.")
|
130 |
+
else:
|
131 |
+
# Allow manual marking of attendance
|
132 |
+
status = st.radio("Mark Attendance:", ("Present", "Absent"))
|
133 |
+
cursor.execute("INSERT INTO attendance (name, roll_no, date, time, status) VALUES (?, ?, ?, ?, ?)",
|
134 |
+
(name, roll_no, date, time, status))
|
135 |
+
conn.commit()
|
136 |
+
st.success(f"Attendance updated for {name} at {time} ({status}).")
|
137 |
+
|
138 |
+
st.image(image, caption="Detected Face", use_container_width=True)
|
139 |
+
if name == "Unknown":
|
140 |
+
st.info("Face not recognized.")
|
141 |
+
|
142 |
+
# --- View Attendance Records ---
|
143 |
+
def view_attendance_records():
|
144 |
+
"""Displays attendance records."""
|
145 |
+
st.subheader("Attendance Records")
|
146 |
+
cursor.execute("SELECT * FROM attendance ORDER BY date DESC, time DESC")
|
147 |
+
records = cursor.fetchall()
|
148 |
+
|
149 |
+
if records:
|
150 |
+
df = pd.DataFrame(records, columns=["ID", "Name", "Roll No", "Date", "Time", "Status"])
|
151 |
+
st.table(df)
|
152 |
+
else:
|
153 |
+
st.info("No attendance records available.")
|
154 |
+
|
155 |
+
# --- Main App Logic ---
|
156 |
+
if __name__ == "__main__":
|
157 |
+
st.title("Face Recognition Attendance")
|
158 |
+
|
159 |
+
# --- Password Protection (Optional) ---
|
160 |
+
password = st.text_input("Enter password", type="password")
|
161 |
+
if password != "123":
|
162 |
+
st.stop()
|
163 |
+
|
164 |
+
# --- App Sections ---
|
165 |
+
app_mode = st.sidebar.selectbox("Select Mode", ["Recognize", "Add New Face", "View Records"])
|
166 |
+
|
167 |
+
if app_mode == "Recognize":
|
168 |
+
recognize_face()
|
169 |
+
elif app_mode == "Add New Face":
|
170 |
+
add_new_face()
|
171 |
+
elif app_mode == "View Records":
|
172 |
+
view_attendance_records()
|
173 |
+
|
174 |
+
# --- Close Database Connection ---
|
175 |
+
conn.close()
|