Skip to content

Commit 55e40d1

Browse files
committed
Updated links in README + Added train_classifier.py & few improvements
1 parent 395d788 commit 55e40d1

File tree

7 files changed

+95
-152
lines changed

7 files changed

+95
-152
lines changed

README.md

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
</p>
1010

1111
> A simple, modern and scalable facial recognition based attendance system
12-
> built with Python back-end & Angular front-end
12+
> built with Python back-end & Angular front-end.
1313
1414
![UI](sample/ui.gif)
1515

@@ -25,14 +25,13 @@
2525

2626
## Prerequisites
2727

28-
* Python v3.7+
29-
* Miniconda3 (optional) (recommended)
30-
* CMake
31-
* Visual Studio Build Tools
32-
* Nvidia CUDA (optional - for nvidia gpus)
33-
* Node.js LTS v12.8.0+ (npm v6.14.4+)
34-
* Angular CLI v9.1.8+
3528
* Windows or Linux (macOS not officially supported, but might work)
29+
* Nvidia CUDA (optional - for nvidia gpus)
30+
* [CMake](https://cmake.org/download/)
31+
* [Visual Studio Build Tools](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2019)
32+
* [Miniconda](https://docs.conda.io/en/latest/miniconda.html) for Python v3.7+
33+
* [Node.js LTS](https://nodejs.org/en/) v12.8.0+ (npm v6.14.4+)
34+
* [Angular CLI](https://cli.angular.io/) v9.1.8+
3635

3736
## Installation
3837

@@ -94,21 +93,10 @@ $ cd frontend
9493
$ ng serve -o
9594
```
9695

97-
## Authors
98-
99-
👤 [**@codeglitchz**](https://github.com/codeglitchz)
100-
101-
👤 [**@codewhizz**](https://github.com/codewhizz)
102-
10396
## 🤝 Contributing
10497

105-
Contributions, issues and feature requests are welcome!<br />Feel free to check [issues page](https://github.com/codeglitchz/attendance-system/issues). You can also take a look at the [contributing guide](https://github.com/codeglitchz/attendance-system/blob/master/CONTRIBUTING.md).
98+
Contributions, issues and feature requests are welcome!<br />Feel free to check [issues page](https://github.com/codeglitchz/attendance-system/issues).
10699

107100
## Show your support
108101

109-
Give a ⭐️ if this project helped you!
110-
111-
## 📝 License
112-
113-
Copyright © 2020 [CodeGlitchz](https://github.com/codeglitchz). <br />
114-
This project is [MIT](https://github.com/codeglitchz/attendance-system/blob/master/LICENSE) licensed.
102+
Give a ⭐️ if this project helped you!

backend/run_cli.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import pyfiglet
44

55
from src.db import engine
6+
from src.libs.train_classifier import TrainClassifier
67
from src.models import Base
78
from src.settings import VIDEO_SOURCE
89
from src.libs.cli_utils import CliAppUtils
@@ -53,7 +54,7 @@ def main_menu():
5354
face_util.detect_n_capture()
5455
break
5556
elif choice == 3:
56-
face_util.train_classifier()
57+
TrainClassifier.train()
5758
break
5859
elif choice == 4:
5960
face_util.recognize_n_attendance()

backend/src/app.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from src.libs.image_helper import IMAGE_SET
99
from src.resources.dashboard import Dashboard
1010
from src.resources.teacher import Teacher, TeacherRegister, TeacherLogin
11-
from src.resources.student import StudentList, StudentAdd, StudentCapture, StudentDelete, TrainClassifier
11+
from src.resources.student import StudentList, StudentAdd, StudentCapture, StudentDelete
1212
from src.resources.attendance import AttendanceList
1313
from src.resources.video_feed import (
1414
VideoFeedList, VideoFeedAdd, VideoFeed, VideoFeedPreview, VideoFeedStop, VideoFeedStart, VideoFeedDelete
@@ -51,7 +51,6 @@ def handle_marshmallow_validation(err):
5151
api.add_resource(StudentAdd, "/students/add")
5252
api.add_resource(StudentCapture, "/students/capture/<int:student_id>")
5353
api.add_resource(StudentDelete, "/students/delete/<int:student_id>")
54-
api.add_resource(TrainClassifier, "/students/train")
5554

5655
# /attendance
5756
api.add_resource(AttendanceList, "/attendance")

backend/src/libs/cli_utils.py

Lines changed: 1 addition & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@
44
from datetime import date as dt
55

66
import cv2
7-
import numpy as np
87
import face_recognition
98

109
from src.models import StudentModel, AttendanceModel
1110
from src.settings import (
1211
DATASET_PATH,
13-
HAAR_CASCADE_PATH, PROTOTXT_PATH, CAFFEMODEL_PATH,
12+
HAAR_CASCADE_PATH,
1413
DLIB_MODEL, DLIB_TOLERANCE, ENCODINGS_FILE
1514
)
1615

@@ -93,66 +92,6 @@ def detect_n_capture(self):
9392
cap.release()
9493
cv2.destroyAllWindows()
9594

96-
@classmethod
97-
def train_classifier(cls):
98-
"""
99-
Train KNN Classifier by storing results in `files/encodings.pickle` file
100-
"""
101-
# TODO: Store encodings in SQL database rather than `files/encodings.pickle` file
102-
try:
103-
print("[INFO] loading encodings...")
104-
data = pickle.loads(open(ENCODINGS_FILE, "rb").read())
105-
# initialize the list of known encodings and known names
106-
known_encodings = data["encodings"]
107-
known_ids = data["ids"]
108-
except FileNotFoundError:
109-
# initialize the list of known encodings and known names
110-
known_encodings = []
111-
known_ids = []
112-
113-
# get single unique ids by converting into set
114-
# for each _id convert it into int
115-
unique_ids = [int(_id) for _id in set(known_ids)]
116-
117-
# get all id_paths and join the path of the parent folder to each id_path
118-
id_paths = [os.path.join(DATASET_PATH, f) for f in os.listdir(DATASET_PATH)]
119-
# print(">>> ID paths:", id_paths)
120-
121-
# now looping through all the id_paths and loading the images in that id_path
122-
for id_path in id_paths:
123-
# getting the ID from the image
124-
_id = int(os.path.split(id_path)[1])
125-
if _id in unique_ids:
126-
continue
127-
# grab the paths to the input images of that ID
128-
image_paths = [os.path.join(id_path, f) for f in os.listdir(id_path)]
129-
for i, image_path in enumerate(image_paths):
130-
print(f"[INFO] ID: {_id}, processing image {i + 1}/{len(image_paths)}")
131-
# load the input image and convert it from RGB (OpenCV ordering)
132-
# to dlib ordering (RGB)
133-
image = cv2.imread(image_path)
134-
rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
135-
136-
# detect the (x, y)-coordinates of the bounding boxes
137-
# corresponding to each face in the input frame, then compute
138-
# the facial embeddings for each face
139-
boxes = face_recognition.face_locations(rgb, model=DLIB_MODEL)
140-
# compute the facial embedding for the face
141-
encodings = face_recognition.face_encodings(rgb, boxes)
142-
# loop over the encodings
143-
for encoding in encodings:
144-
# add each encoding + name to our set of known names and
145-
# encodings
146-
known_encodings.append(encoding)
147-
known_ids.append(_id)
148-
149-
# dump the facial encodings + names to disk
150-
print("[INFO] serializing encodings...")
151-
data = {"encodings": known_encodings, "ids": known_ids}
152-
f = open(ENCODINGS_FILE, "wb")
153-
f.write(pickle.dumps(data))
154-
f.close()
155-
15695
def recognize_n_attendance(self):
15796
print("[INFO] loading encodings...")
15897
data = pickle.loads(open(ENCODINGS_FILE, "rb").read())
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import os
2+
import pickle
3+
4+
import cv2
5+
import face_recognition
6+
7+
from src.settings import DATASET_PATH, ENCODINGS_FILE, DLIB_MODEL
8+
9+
10+
class TrainClassifier:
11+
"""Train KNN Classifier by storing results in `files/encodings.pickle` file"""
12+
# TODO: Store encodings in SQL database rather than `files/encodings.pickle` file
13+
@classmethod
14+
def train(cls):
15+
try:
16+
print("[INFO] loading encodings...")
17+
with open(ENCODINGS_FILE, "rb") as ef:
18+
data = pickle.loads(ef.read())
19+
# initialize the list of known encodings and known names
20+
known_encodings = data["encodings"]
21+
known_ids = data["ids"]
22+
except FileNotFoundError:
23+
# initialize the list of known encodings and known names
24+
known_encodings = []
25+
known_ids = []
26+
27+
# get single unique ids by converting into set
28+
# for each _id convert it into int
29+
unique_ids = [int(_id) for _id in set(known_ids)]
30+
31+
# get all id_paths and join the path of the parent folder to each id_path
32+
id_paths = [os.path.join(DATASET_PATH, f) for f in os.listdir(DATASET_PATH)]
33+
# print(">>> ID paths:", id_paths)
34+
35+
# now looping through all the id_paths and loading the images in that id_path
36+
for id_path in id_paths:
37+
# getting the ID from the image
38+
_id = int(os.path.split(id_path)[1])
39+
if _id in unique_ids:
40+
continue
41+
# grab the paths to the input images of that ID
42+
image_paths = [os.path.join(id_path, f) for f in os.listdir(id_path)]
43+
for i, image_path in enumerate(image_paths):
44+
print(f"[INFO] ID: {_id}, processing image {i + 1}/{len(image_paths)}")
45+
# load the input image and convert it from RGB (OpenCV ordering)
46+
# to dlib ordering (RGB)
47+
image = cv2.imread(image_path)
48+
try:
49+
rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
50+
except cv2.error:
51+
# delete image that cannot be processed
52+
try:
53+
os.remove(image_path)
54+
except (FileNotFoundError, PermissionError):
55+
pass
56+
continue
57+
58+
# detect the (x, y)-coordinates of the bounding boxes
59+
# corresponding to each face in the input frame, then compute
60+
# the facial embeddings for each face
61+
boxes = face_recognition.face_locations(rgb, model=DLIB_MODEL)
62+
# compute the facial embedding for the face
63+
encodings = face_recognition.face_encodings(rgb, boxes)
64+
# loop over the encodings
65+
for encoding in encodings:
66+
# add each encoding + name to our set of known names and
67+
# encodings
68+
known_encodings.append(encoding)
69+
known_ids.append(_id)
70+
71+
# dump the facial encodings + names to disk
72+
print("[INFO] serializing encodings...")
73+
data = {"encodings": known_encodings, "ids": known_ids}
74+
f = open(ENCODINGS_FILE, "wb")
75+
f.write(pickle.dumps(data))
76+
f.close()

backend/src/resources/student.py

Lines changed: 5 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,18 @@
22
import shutil
33
import pickle
44

5-
import cv2
6-
import face_recognition
75
from flask import request
86
from flask_restful import Resource
97
from flask_uploads import UploadNotAllowed
108
from flask_jwt_extended import jwt_required
119

1210
from src.db import Session
1311
from src.libs import image_helper
12+
from src.libs.train_classifier import TrainClassifier
1413
from src.libs.strings import gettext
1514
from src.models import StudentModel
1615
from src.schemas import StudentSchema, ImageSchema
17-
from src.settings import DATASET_PATH, ENCODINGS_FILE, DLIB_MODEL
16+
from src.settings import DATASET_PATH, ENCODINGS_FILE
1817

1918

2019
student_schema = StudentSchema()
@@ -101,68 +100,10 @@ def post(cls, student_id: int):
101100
save_as_filename = filename + extension
102101
image_path = image_helper.save_image(data["image"], folder=folder, name=save_as_filename)
103102
basename = image_helper.get_basename(image_path)
104-
return {"message": gettext("image_uploaded").format(basename)}, 201
105103
except UploadNotAllowed:
106104
extension = image_helper.get_extension(data["image"])
107105
return {"message": gettext("image_illegal_extension").format(extension)}, 400
108106

109-
110-
class TrainClassifier(Resource):
111-
"""Train KNN Classifier by storing results in `files/encodings.pickle` file"""
112-
# TODO: Store encodings in SQL database rather than `files/encodings.pickle` file
113-
@classmethod
114-
@jwt_required
115-
def get(cls):
116-
try:
117-
print("[INFO] loading encodings...")
118-
data = pickle.loads(open(ENCODINGS_FILE, "rb").read())
119-
# initialize the list of known encodings and known names
120-
known_encodings = data["encodings"]
121-
known_ids = data["ids"]
122-
except FileNotFoundError:
123-
# initialize the list of known encodings and known names
124-
known_encodings = []
125-
known_ids = []
126-
127-
# get single unique ids by converting into set
128-
# for each _id convert it into int
129-
unique_ids = [int(_id) for _id in set(known_ids)]
130-
131-
# get all id_paths and join the path of the parent folder to each id_path
132-
id_paths = [os.path.join(DATASET_PATH, f) for f in os.listdir(DATASET_PATH)]
133-
# print(">>> ID paths:", id_paths)
134-
135-
# now looping through all the id_paths and loading the images in that id_path
136-
for id_path in id_paths:
137-
# getting the ID from the image
138-
_id = int(os.path.split(id_path)[1])
139-
if _id in unique_ids:
140-
continue
141-
# grab the paths to the input images of that ID
142-
image_paths = [os.path.join(id_path, f) for f in os.listdir(id_path)]
143-
for i, image_path in enumerate(image_paths):
144-
print(f"[INFO] ID: {_id}, processing image {i + 1}/{len(image_paths)}")
145-
# load the input image and convert it from RGB (OpenCV ordering)
146-
# to dlib ordering (RGB)
147-
image = cv2.imread(image_path)
148-
rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
149-
150-
# detect the (x, y)-coordinates of the bounding boxes
151-
# corresponding to each face in the input frame, then compute
152-
# the facial embeddings for each face
153-
boxes = face_recognition.face_locations(rgb, model=DLIB_MODEL)
154-
# compute the facial embedding for the face
155-
encodings = face_recognition.face_encodings(rgb, boxes)
156-
# loop over the encodings
157-
for encoding in encodings:
158-
# add each encoding + name to our set of known names and
159-
# encodings
160-
known_encodings.append(encoding)
161-
known_ids.append(_id)
162-
163-
# dump the facial encodings + names to disk
164-
print("[INFO] serializing encodings...")
165-
data = {"encodings": known_encodings, "ids": known_ids}
166-
f = open(ENCODINGS_FILE, "wb")
167-
f.write(pickle.dumps(data))
168-
f.close()
107+
# train images when submitted successfully
108+
TrainClassifier.train()
109+
return {"message": gettext("image_uploaded").format(basename)}, 201

frontend/src/app/components/video-feed-list/video-feed-list.component.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,9 @@
5353
<th scope="row">{{ feed.id }}</th>
5454
<td>
5555
<div *ngIf="!feed.is_active">
56-
<fa-icon [icon]="circleIcon" style="color:red" size="xs"></fa-icon>&nbsp;
5756
Inactive
5857
</div>
59-
<div *ngIf="feed.is_active" style="color:green">
58+
<div *ngIf="feed.is_active" style="color:red">
6059
<fa-icon [icon]="circleIcon" size="xs"></fa-icon>&nbsp;
6160
<a class="nav-link" style="padding:0rem;display:inline" [routerLink]="generatePreviewUrl(feed.id)">Live</a>
6261
</div>

0 commit comments

Comments
 (0)