harshdhane commited on
Commit
2183d48
·
verified ·
1 Parent(s): b4812cd

Upload 16 files

Browse files
app.py ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, request, jsonify
2
+ import os
3
+ import cv2
4
+ import numpy as np
5
+ import pandas as pd
6
+ from datetime import datetime
7
+ import base64
8
+
9
+ app = Flask(__name__)
10
+
11
+ # Setup
12
+ face_cascade = cv2.CascadeClassifier("haarcascade_frontalface_alt.xml")
13
+ dataset_path = "./face_dataset/"
14
+ os.makedirs(dataset_path, exist_ok=True)
15
+
16
+ # KNN distance
17
+ def distance(v1, v2):
18
+ return np.sqrt(((v1 - v2) ** 2).sum())
19
+
20
+ def knn(train, test, k=5):
21
+ dist = []
22
+ for i in range(train.shape[0]):
23
+ ix = train[i, :-1]
24
+ iy = train[i, -1]
25
+ d = distance(test, ix)
26
+ dist.append([d, iy])
27
+ dk = sorted(dist, key=lambda x: x[0])[:k]
28
+ labels = np.array(dk)[:, -1]
29
+ return np.unique(labels, return_counts=True)[0][0]
30
+
31
+ # Attendance system
32
+ class AttendanceSystem:
33
+ def __init__(self):
34
+ self.file = "attendance.csv"
35
+ self.columns = ["Name", "Date", "Time"]
36
+ if not os.path.exists(self.file):
37
+ pd.DataFrame(columns=self.columns).to_csv(self.file, index=False)
38
+
39
+ def mark(self, name):
40
+ today = datetime.now().strftime("%Y-%m-%d")
41
+ now = datetime.now().strftime("%H:%M:%S")
42
+ df = pd.read_csv(self.file)
43
+ existing = df[(df["Name"] == name) & (df["Date"] == today)]
44
+ if existing.empty:
45
+ new_entry = pd.DataFrame([[name, today, now]], columns=self.columns)
46
+ df = pd.concat([df, new_entry], ignore_index=True)
47
+ df.to_csv(self.file, index=False)
48
+ return True
49
+ return False
50
+
51
+ attendance = AttendanceSystem()
52
+
53
+ # Home page
54
+ @app.route('/')
55
+ def index():
56
+ return render_template('index.html')
57
+
58
+ @app.route('/register')
59
+ def register():
60
+ return render_template('register.html')
61
+
62
+ @app.route('/mark')
63
+ def mark():
64
+ return render_template('mark.html')
65
+
66
+ # API to save face during registration
67
+ @app.route('/api/register_face', methods=['POST'])
68
+ def register_face():
69
+ data = request.json
70
+ name = data['name']
71
+ images = data['images'] # List of base64 images
72
+
73
+ face_data = []
74
+
75
+ for img_data in images:
76
+ img_bytes = base64.b64decode(img_data.split(",")[1])
77
+ np_arr = np.frombuffer(img_bytes, np.uint8)
78
+ img = cv2.imdecode(np_arr, cv2.IMREAD_COLOR)
79
+
80
+ gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
81
+ faces = face_cascade.detectMultiScale(gray, 1.3, 5)
82
+
83
+ for (x, y, w, h) in faces[:1]:
84
+ face = img[y:y+h, x:x+w]
85
+ face = cv2.resize(face, (100, 100))
86
+ face_data.append(face.flatten())
87
+
88
+ if face_data:
89
+ face_data = np.array(face_data)
90
+ np.save(os.path.join(dataset_path, f"{name}.npy"), face_data)
91
+ return jsonify({"status": "success", "message": f"{len(face_data)} faces saved"})
92
+ else:
93
+ return jsonify({"status": "fail", "message": "No faces detected"})
94
+
95
+ # API to mark attendance
96
+ @app.route('/api/mark_attendance', methods=['POST'])
97
+ def mark_attendance():
98
+ img_data = request.json['image']
99
+ img_bytes = base64.b64decode(img_data.split(",")[1])
100
+ np_arr = np.frombuffer(img_bytes, np.uint8)
101
+ img = cv2.imdecode(np_arr, cv2.IMREAD_COLOR)
102
+
103
+ gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
104
+ faces = face_cascade.detectMultiScale(gray, 1.3, 5)
105
+
106
+ # Load training data
107
+ face_data = []
108
+ labels = []
109
+ names = {}
110
+ class_id = 0
111
+
112
+ for file in os.listdir(dataset_path):
113
+ if file.endswith('.npy'):
114
+ data = np.load(os.path.join(dataset_path, file))
115
+ face_data.append(data)
116
+ names[class_id] = file[:-4]
117
+ labels.extend([class_id] * data.shape[0])
118
+ class_id += 1
119
+
120
+ if not face_data:
121
+ return jsonify({"status": "fail", "message": "No trained data found"})
122
+
123
+ X_train = np.concatenate(face_data, axis=0)
124
+ y_train = np.array(labels).reshape(-1, 1)
125
+ trainset = np.hstack((X_train, y_train))
126
+
127
+ for (x, y, w, h) in faces[:1]:
128
+ face = img[y:y+h, x:x+w]
129
+ face = cv2.resize(face, (100, 100)).flatten()
130
+ pred_id = knn(trainset, face)
131
+ name = names.get(pred_id, "Unknown")
132
+
133
+ if name != "Unknown":
134
+ marked = attendance.mark(name)
135
+ msg = "Attendance marked" if marked else "Already marked today"
136
+ return jsonify({"status": "success", "name": name, "message": msg})
137
+
138
+ return jsonify({"status": "fail", "message": "No known face detected"})
139
+
140
+ if __name__ == '__main__':
141
+ app.run(host='0.0.0.0', port=5000, debug=True)
attendance.csv ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Name,Date,Time
2
+ DSA,4/6/2025,15:27:56
3
+ Harshvardhan Dhane,4/6/2025,15:35:33
4
+ Ravi,4/6/2025,15:42:07
5
+ Harsh,4/6/2025,15:43:47
6
+ harshvardhan,4/6/2025,15:47:15
7
+ Ravi,4/7/2025,12:49:02
8
+ Harsh,4/7/2025,12:50:14
9
+ Yashodhan,2025-04-08,02:06:00
10
+ Ravi,2025-04-08,02:06:21
11
+ Harsh,2025-04-08,02:08:18
12
+ Sudhir,2025-04-08,02:11:00
13
+ Tulasi ,2025-04-08,13:39:34
14
+ Aman,2025-04-08,15:00:46
face_dataset/Aman.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8325e3cc868993eb7a85782c47913e5cbf163ae12900e9159233ad5c156b0d91
3
+ size 450128
face_dataset/Harsh.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9e94abd225bedded2ef763ffc097fb0b67ba4ac6003083ac1ca0cdc0ab7bede8
3
+ size 450128
face_dataset/Ravi.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:efa7e14a321cd913118bd25ea983379b29aa27e82e847a70dddf58371342702b
3
+ size 420128
face_dataset/Sudhir.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:53af27e7af2c2eaf629b423594eddb8988d3656f984f204701fc25a0276850a8
3
+ size 690128
face_dataset/Tulasi .npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e23b8fc46e01a4ce67ba7d017505aff046df28ca9fa1f78418924aad75f8c87e
3
+ size 330128
face_dataset/Yashodhan.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f9fdca318514deacf6bbaf1a7392557e0fa86e0c59f4b804a7b02092fc6dbeef
3
+ size 6480128
haarcascade_frontalface_alt.xml ADDED
The diff for this file is too large to render. See raw diff
 
static/js/webcam.js ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function initWebcam(videoId) {
2
+ const video = document.getElementById(videoId);
3
+ navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
4
+ video.srcObject = stream;
5
+ });
6
+ }
7
+
8
+ function captureFrame(frameArray, single = false) {
9
+ return new Promise((resolve) => {
10
+ const video = document.getElementById("video");
11
+ const canvas = document.createElement("canvas");
12
+ canvas.width = video.videoWidth;
13
+ canvas.height = video.videoHeight;
14
+ const ctx = canvas.getContext("2d");
15
+ ctx.drawImage(video, 0, 0);
16
+ const image = canvas.toDataURL("image/jpeg");
17
+ if (!single) {
18
+ frameArray.push(image);
19
+ resolve();
20
+ } else {
21
+ resolve(image);
22
+ }
23
+ });
24
+ }
templates/index.html ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Face Attendance</title>
7
+ <style>
8
+ body {
9
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
10
+ margin: 0;
11
+ padding: 0;
12
+ background-color: #f5f5f5;
13
+ color: #333;
14
+ line-height: 1.6;
15
+ }
16
+ .container {
17
+ width: 80%;
18
+ max-width: 800px;
19
+ margin: 50px auto;
20
+ padding: 30px;
21
+ background-color: #fff;
22
+ border-radius: 8px;
23
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
24
+ text-align: center;
25
+ }
26
+ h2 {
27
+ color: #2c3e50;
28
+ margin-bottom: 30px;
29
+ font-size: 32px;
30
+ }
31
+ .nav-links {
32
+ display: flex;
33
+ flex-direction: column;
34
+ gap: 15px;
35
+ margin-top: 25px;
36
+ }
37
+ .btn {
38
+ display: inline-block;
39
+ padding: 12px 24px;
40
+ background-color: #3498db;
41
+ color: white;
42
+ text-decoration: none;
43
+ border-radius: 5px;
44
+ font-weight: 500;
45
+ transition: background-color 0.3s;
46
+ border: none;
47
+ font-size: 16px;
48
+ cursor: pointer;
49
+ }
50
+ .btn:hover {
51
+ background-color: #2980b9;
52
+ }
53
+ .logo {
54
+ font-size: 36px;
55
+ color: #3498db;
56
+ margin-bottom: 15px;
57
+ }
58
+ </style>
59
+ </head>
60
+ <body>
61
+ <div class="container">
62
+ <div class="logo">
63
+ <i class="fas fa-user-check"></i>
64
+ </div>
65
+ <h2>Welcome to Face Attendance System</h2>
66
+ <div class="nav-links">
67
+ <a href="/register" class="btn">Register Face</a>
68
+ <a href="/mark" class="btn">Mark Attendance</a>
69
+ </div>
70
+ </div>
71
+ <!-- Add Font Awesome for icons -->
72
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/js/all.min.js"></script>
73
+ </body>
74
+ </html>
templates/index.html.bak ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!-- index.html -->
2
+
3
+
4
+ <!DOCTYPE html>
5
+ <html>
6
+ <head>
7
+ <title>Face Attendance</title>
8
+ </head>
9
+ <body>
10
+ <h2>Welcome to Face Attendance System</h2>
11
+ <a href="/register">Register Face</a><br>
12
+ <a href="/mark">Mark Attendance</a>
13
+ </body>
14
+ </html>
templates/mark.html ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Mark Attendance</title>
7
+ <style>
8
+ body {
9
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
10
+ margin: 0;
11
+ padding: 0;
12
+ background-color: #f5f5f5;
13
+ color: #333;
14
+ line-height: 1.6;
15
+ }
16
+ .container {
17
+ width: 80%;
18
+ max-width: 800px;
19
+ margin: 30px auto;
20
+ padding: 30px;
21
+ background-color: #fff;
22
+ border-radius: 8px;
23
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
24
+ text-align: center;
25
+ }
26
+ h2 {
27
+ color: #2c3e50;
28
+ margin-bottom: 25px;
29
+ font-size: 28px;
30
+ }
31
+ .btn {
32
+ display: inline-block;
33
+ padding: 12px 24px;
34
+ background-color: #27ae60;
35
+ color: white;
36
+ text-decoration: none;
37
+ border-radius: 5px;
38
+ font-weight: 500;
39
+ transition: background-color 0.3s;
40
+ border: none;
41
+ font-size: 16px;
42
+ cursor: pointer;
43
+ margin: 15px 0;
44
+ }
45
+ .btn:hover {
46
+ background-color: #219653;
47
+ }
48
+ video {
49
+ margin: 20px auto;
50
+ border-radius: 8px;
51
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
52
+ background-color: #eee;
53
+ max-width: 100%;
54
+ }
55
+ #status {
56
+ margin-top: 20px;
57
+ font-size: 18px;
58
+ padding: 15px;
59
+ border-radius: 5px;
60
+ background-color: #f8f9fa;
61
+ display: inline-block;
62
+ min-width: 60%;
63
+ }
64
+ .success {
65
+ color: #27ae60;
66
+ font-weight: 600;
67
+ }
68
+ .error {
69
+ color: #e74c3c;
70
+ font-weight: 600;
71
+ }
72
+ .header {
73
+ display: flex;
74
+ justify-content: space-between;
75
+ align-items: center;
76
+ margin-bottom: 20px;
77
+ }
78
+ .back-link {
79
+ color: #3498db;
80
+ text-decoration: none;
81
+ display: flex;
82
+ align-items: center;
83
+ font-weight: 500;
84
+ }
85
+ .back-link:hover {
86
+ text-decoration: underline;
87
+ }
88
+ .video-container {
89
+ position: relative;
90
+ margin: 0 auto;
91
+ width: 320px;
92
+ max-width: 100%;
93
+ }
94
+ .camera-overlay {
95
+ position: absolute;
96
+ top: 0;
97
+ left: 0;
98
+ right: 0;
99
+ bottom: 0;
100
+ border: 2px solid rgba(52, 152, 219, 0.5);
101
+ border-radius: 8px;
102
+ pointer-events: none;
103
+ }
104
+ .loading {
105
+ display: none;
106
+ margin: 20px auto;
107
+ border: 5px solid #f3f3f3;
108
+ border-radius: 50%;
109
+ border-top: 5px solid #3498db;
110
+ width: 40px;
111
+ height: 40px;
112
+ animation: spin 1s linear infinite;
113
+ }
114
+ @keyframes spin {
115
+ 0% { transform: rotate(0deg); }
116
+ 100% { transform: rotate(360deg); }
117
+ }
118
+ </style>
119
+ </head>
120
+ <body>
121
+ <div class="container">
122
+ <div class="header">
123
+ <a href="/" class="back-link">
124
+ <i class="fas fa-arrow-left"></i> &nbsp;Back to Home
125
+ </a>
126
+ <h2>Mark Attendance</h2>
127
+ </div>
128
+
129
+ <div class="video-container">
130
+ <video id="video" width="300" autoplay></video>
131
+ <div class="camera-overlay"></div>
132
+ </div>
133
+
134
+ <button onclick="markAttendance()" class="btn">
135
+ <i class="fas fa-user-check"></i> Mark Attendance
136
+ </button>
137
+
138
+ <div class="loading" id="loading"></div>
139
+ <p id="status">Position your face in the camera and click the button</p>
140
+ </div>
141
+
142
+ <script src="/static/js/webcam.js"></script>
143
+ <script>
144
+ async function markAttendance() {
145
+ document.getElementById("loading").style.display = "block";
146
+ document.getElementById("status").innerText = "Processing...";
147
+
148
+ captureFrame([], true).then(image => {
149
+ fetch('/api/mark_attendance', {
150
+ method: 'POST',
151
+ headers: { 'Content-Type': 'application/json' },
152
+ body: JSON.stringify({ image: image })
153
+ })
154
+ .then(res => res.json())
155
+ .then(data => {
156
+ document.getElementById("loading").style.display = "none";
157
+
158
+ const statusElem = document.getElementById("status");
159
+ if (data.name) {
160
+ statusElem.innerHTML = `<span class="success">Welcome, ${data.name}!</span><br>${data.message}`;
161
+ } else {
162
+ statusElem.innerHTML = `<span class="error">Face not recognized</span><br>${data.message}`;
163
+ }
164
+ })
165
+ .catch(err => {
166
+ document.getElementById("loading").style.display = "none";
167
+ document.getElementById("status").innerHTML = `<span class="error">Error: ${err.message}</span>`;
168
+ });
169
+ });
170
+ }
171
+ initWebcam("video");
172
+ </script>
173
+
174
+ <!-- Add Font Awesome for icons -->
175
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/js/all.min.js"></script>
176
+ </body>
177
+ </html>
templates/mark.html.bak ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!-- mark.html -->
2
+
3
+
4
+ <!DOCTYPE html>
5
+ <html>
6
+ <head>
7
+ <title>Face Attendance</title>
8
+ </head>
9
+ <body>
10
+ <h2>Welcome to Face Attendance System</h2>
11
+ <a href="/register">Register Face</a><br>
12
+ <a href="/mark">Mark Attendance</a>
13
+ </body>
14
+ </html>
15
+
16
+
17
+ <!DOCTYPE html>
18
+ <html>
19
+ <head>
20
+ <title>Mark Attendance</title>
21
+ </head>
22
+ <body>
23
+ <h2>Mark Attendance</h2>
24
+ <button onclick="markAttendance()">Mark</button>
25
+ <video id="video" width="300" autoplay></video>
26
+ <p id="status"></p>
27
+
28
+ <script src="/static/js/webcam.js"></script>
29
+ <script>
30
+ async function markAttendance() {
31
+ captureFrame([], true).then(image => {
32
+ fetch('/api/mark_attendance', {
33
+ method: 'POST',
34
+ headers: { 'Content-Type': 'application/json' },
35
+ body: JSON.stringify({ image: image })
36
+ })
37
+ .then(res => res.json())
38
+ .then(data => {
39
+ document.getElementById("status").innerText = data.name + ": " + data.message;
40
+ });
41
+ });
42
+ }
43
+
44
+ initWebcam("video");
45
+ </script>
46
+ </body>
47
+ </html>
templates/register.html ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Register Face</title>
7
+ <style>
8
+ body {
9
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
10
+ margin: 0;
11
+ padding: 0;
12
+ background-color: #f5f5f5;
13
+ color: #333;
14
+ line-height: 1.6;
15
+ }
16
+ .container {
17
+ width: 80%;
18
+ max-width: 800px;
19
+ margin: 30px auto;
20
+ padding: 30px;
21
+ background-color: #fff;
22
+ border-radius: 8px;
23
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
24
+ text-align: center;
25
+ }
26
+ h2 {
27
+ color: #2c3e50;
28
+ margin-bottom: 25px;
29
+ font-size: 28px;
30
+ }
31
+ .btn {
32
+ display: inline-block;
33
+ padding: 12px 24px;
34
+ background-color: #3498db;
35
+ color: white;
36
+ text-decoration: none;
37
+ border-radius: 5px;
38
+ font-weight: 500;
39
+ transition: background-color 0.3s;
40
+ border: none;
41
+ font-size: 16px;
42
+ cursor: pointer;
43
+ margin-top: 15px;
44
+ }
45
+ .btn:hover {
46
+ background-color: #2980b9;
47
+ }
48
+ input[type="text"] {
49
+ width: 100%;
50
+ padding: 12px;
51
+ margin: 10px 0;
52
+ border: 1px solid #ddd;
53
+ border-radius: 4px;
54
+ box-sizing: border-box;
55
+ font-size: 16px;
56
+ }
57
+ video {
58
+ margin: 20px auto;
59
+ border-radius: 8px;
60
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
61
+ background-color: #eee;
62
+ max-width: 100%;
63
+ }
64
+ #status {
65
+ margin-top: 15px;
66
+ font-weight: 500;
67
+ color: #2c3e50;
68
+ }
69
+ .header {
70
+ display: flex;
71
+ justify-content: space-between;
72
+ align-items: center;
73
+ margin-bottom: 20px;
74
+ }
75
+ .back-link {
76
+ color: #3498db;
77
+ text-decoration: none;
78
+ display: flex;
79
+ align-items: center;
80
+ font-weight: 500;
81
+ }
82
+ .back-link:hover {
83
+ text-decoration: underline;
84
+ }
85
+ .form-container {
86
+ margin-top: 20px;
87
+ }
88
+ .progress {
89
+ margin-top: 10px;
90
+ height: 10px;
91
+ background-color: #eee;
92
+ border-radius: 5px;
93
+ overflow: hidden;
94
+ position: relative;
95
+ margin-bottom: 15px;
96
+ }
97
+ .progress-bar {
98
+ height: 100%;
99
+ background-color: #3498db;
100
+ width: 0%;
101
+ transition: width 0.5s ease;
102
+ }
103
+ </style>
104
+ </head>
105
+ <body>
106
+ <div class="container">
107
+ <div class="header">
108
+ <a href="/" class="back-link">
109
+ <i class="fas fa-arrow-left"></i> &nbsp;Back to Home
110
+ </a>
111
+ <h2>Register Face</h2>
112
+ </div>
113
+
114
+ <div class="form-container">
115
+ <input type="text" id="name" placeholder="Enter your name" required>
116
+ <button onclick="startCapture()" class="btn">
117
+ <i class="fas fa-camera"></i> Start Capture
118
+ </button>
119
+
120
+ <div class="video-container">
121
+ <video id="video" width="300" autoplay></video>
122
+ </div>
123
+
124
+ <div class="progress">
125
+ <div class="progress-bar" id="progress-bar"></div>
126
+ </div>
127
+
128
+ <p id="status">Enter your name and click "Start Capture"</p>
129
+ </div>
130
+ </div>
131
+
132
+ <script src="/static/js/webcam.js"></script>
133
+ <script>
134
+ let frames = [];
135
+ let interval;
136
+ async function startCapture() {
137
+ frames = [];
138
+ const name = document.getElementById("name").value;
139
+ if (!name) return alert("Please enter your name");
140
+
141
+ document.getElementById("status").innerText = "Capturing...";
142
+ const progressBar = document.getElementById("progress-bar");
143
+ progressBar.style.width = "0%";
144
+
145
+ interval = setInterval(() => {
146
+ captureFrame(frames);
147
+ const progress = (frames.length / 15) * 100;
148
+ progressBar.style.width = progress + "%";
149
+
150
+ if (frames.length >= 15) {
151
+ clearInterval(interval);
152
+ document.getElementById("status").innerText = "Processing...";
153
+
154
+ fetch('/api/register_face', {
155
+ method: 'POST',
156
+ headers: { 'Content-Type': 'application/json' },
157
+ body: JSON.stringify({ name: name, images: frames })
158
+ })
159
+ .then(res => res.json())
160
+ .then(data => {
161
+ document.getElementById("status").innerText = data.message;
162
+ });
163
+ }
164
+ }, 500);
165
+ }
166
+ initWebcam("video");
167
+ </script>
168
+
169
+ <!-- Add Font Awesome for icons -->
170
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/js/all.min.js"></script>
171
+ </body>
172
+ </html>
templates/register.html.bak ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!-- register.html -->
2
+
3
+
4
+ <!DOCTYPE html>
5
+ <html>
6
+ <head>
7
+ <title>Face Attendance</title>
8
+ </head>
9
+ <body>
10
+ <h2>Welcome to Face Attendance System</h2>
11
+ <a href="/register">Register Face</a><br>
12
+ <a href="/mark">Mark Attendance</a>
13
+ </body>
14
+ </html>
15
+
16
+
17
+ <!DOCTYPE html>
18
+ <html>
19
+ <head>
20
+ <title>Register Face</title>
21
+ </head>
22
+ <body>
23
+ <h2>Register Face</h2>
24
+ <input type="text" id="name" placeholder="Enter name" required>
25
+ <button onclick="startCapture()">Start Capture</button>
26
+ <video id="video" width="300" autoplay></video>
27
+ <p id="status"></p>
28
+
29
+ <script src="/static/js/webcam.js"></script>
30
+ <script>
31
+ let frames = [];
32
+ let interval;
33
+ async function startCapture() {
34
+ frames = [];
35
+ const name = document.getElementById("name").value;
36
+ if (!name) return alert("Please enter name");
37
+ document.getElementById("status").innerText = "Capturing...";
38
+
39
+ interval = setInterval(() => {
40
+ captureFrame(frames);
41
+ if (frames.length >= 15) {
42
+ clearInterval(interval);
43
+ fetch('/api/register_face', {
44
+ method: 'POST',
45
+ headers: { 'Content-Type': 'application/json' },
46
+ body: JSON.stringify({ name: name, images: frames })
47
+ })
48
+ .then(res => res.json())
49
+ .then(data => {
50
+ document.getElementById("status").innerText = data.message;
51
+ });
52
+ }
53
+ }, 500);
54
+ }
55
+
56
+ initWebcam("video");
57
+ </script>
58
+ </body>
59
+ </html>