Camera.py¶
The Camera.py file serves to be the connection between the HTML dashboard and the OpenCV camera. The Camera.py file controls
all the camera functions.
Imports¶
import os
import sys
import cv2
import face_recognition
from TransferLearning import loadDictionary, loadLists, toList, getLivenessValue, runInParallel, dynamicAdd, \
getFolderSize, checkIfHere
from init import *
import numpy as np
from Excel import *
from LivenessDetection import getModel, getModelPred
import socket
from Sheets import *
from timeit import default_timer as timer
os: Necessary to access file systemssys: Necessary to access the operating systemcv2: Necessary to access computer vision toolsface_recognition: Necessary to access face recognition toolsTransferLearning: Necessary to access helper methods in original programinit: Necessary to access the arraysnumpy: Necessary to access Linear Algebra functionsExcel: Necessary to access Microsoft Excel methodsLivenessDetection: Necessary to access the Liveness Detection modelssocket: Necessary to check internet connectionSheets: Necessary to access Google Sheets methodstimeit: Necessary to take times
Feature Control Variables¶
These variables serve to control different features related to the camera
global dynamicState
global pauseState
global onlineMode
dynamicState = False
pauseState = True
onlineMode = False
dynamicState: Controls whether you want to add a new person or notpauseState: Controls whether the camera will be paused or notonlineState: Controls whether to use google sheets or excel
Static Functions¶
The addPerson() toggles the dynamicState variable from True to False and vice versa.
def addPerson():
global dynamicState
dynamicState = True
The internetCheck() function will use the socket class and try to create a connection with Google.com. If it fails, it will throw an Exception and return False.
def internetCheck():
try:
socket.create_connection(("www.google.com", 80))
return True
except OSError:
pass
return False
Objects¶
The VideoCamera Object initializes with several starting variable amounts including initial arrays, liveness models, timestamps, encodings, and internet connections.
def __init__(self, source):
try:
# Call on OpenCV Video Capture
self.video = cv2.VideoCapture(source)
# Some global variables
global processThisFrame, faceLocations, faceEncodings, faceNames, encodingList, encodingNames
global faceNamesKnown, fullStudentNames, inputFrames, model, start, internetCheck
# Initialize variables
faceLocations = []
faceEncodings = []
faceNames = []
inputFrames = []
processThisFrame = True
# Load List information
fullStudentNames = loadLists("List Information/Full Student Names") # List with full Student Names
faceNamesKnown = loadLists("List Information/Face Names Known") # List With Face Names
encodingNames = loadLists("List Information/Encoding Names") # List With encoding names
loadDictionary("List Information/Face Names Known", faceEncodingsKnown) # Dictionary with Encodings
encodingList = toList(faceEncodingsKnown)
# Load encodings
for x in range(0, int(len(encodingList))):
encodingList[x] = np.load("Encodings/" + str(encodingNames[x]))
# Load Liveness Model
model = getModelPred()
# Start Late timer
start = timer()
# Internet Check
internetCheck = internetCheck()
except Exception as e:
print(e)
When it is destroyed, it deletes the camera.
def __del__(self):
# Delete Video Capture
self.video.release()
Object Functions¶
The addFace() function will apply the dynamicAdd() from TransferLearning.py if and only if a face is found and it is Unknown. It will then reload all of the arrays and encodings, At the end, it will turn the dynamicState variable to False.
def addFace(self):
# Some global variables
global dynamicState, encodingNames, fullStudentNames, faceNamesKnown, encodingList, frame
# Only run Dynamic Addition if a face is found and is unknown
if 'Unknown' in faceNames and len(faceLocations) > 1:
# Run dynamic core addition
dynamicAdd(frame)
# Relaod Lists
fullStudentNames = loadLists("List Information/Full Student Names") # List with full Student Names
faceNamesKnown = loadLists("List Information/Face Names Known") # List With Face Names
encodingNames = loadLists("List Information/Encoding Names") # List With encoding names
loadDictionary("List Information/Face Names Known", faceEncodingsKnown) # Dictionary with Encodings
# Run Encoding Model as necessary
if getFolderSize("Encodings/") != len(encodingNames):
import EncodingModel
# Reload Enecodings
encodingList = toList(faceEncodingsKnown)
for x in range(0, int(len(encodingList))):
encodingList[x] = np.load("Encodings/" + str(encodingNames[x]))
# Turn off dynamic addition once done
dynamicState = False
The getRawFrame() function will return solely the frame the OpenCV camera sees.
def getRawFrame(self):
# Returns the raw frame
_, frameToReturn = self.video.read()
return frameToReturn
The goOnline() function will control the onlineMode feature control variable to control whether to use online or offline mode.
def goOnline(self):
global onlineMode
onlineMode = not onlineMode
The getFrame() function is the core function and has been split into different parts for the purpose of readability and easier to understand documentation.
Here we are declaring some global variables that are used universally throughout Camera.py
def getFrame(self):
try:
# Some global variables
global processThisFrame, faceLocations, faceNames, encodingList, faceNamesKnown, fullStudentNames
global model, inputFrames, frame, dynamicState, start, internetCheck, onlineMode
Next we are reading the frame and converting it into the correct dimensions and formats for our needs. This also includes calculating the elapsed time.
# Read OpenCV video
success, frame = self.video.read()
# Resize as necessary
smallFrame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)
# Change Colors as necessary
rgbSmallFrame = smallFrame[:, :, ::-1]
# End time for Late feature
end = timer()
# Calculate time spent
elapsedTime = end - start
We are then using the processThisFrame variable to process every other frame so that the User Experience is better. We also calcualte the locations and encodings of the faces
in the current frame being analyzed. We declared an empty list that will store all face names in the frame.
# Only process every other frame of video to save time
if processThisFrame:
# Find all the faces and face encodings in the current frame of video
faceLocations = face_recognition.face_locations(rgbSmallFrame)
faceEncodings = face_recognition.face_encodings(rgbSmallFrame, faceLocations)
# Empty Face names for every iteration
faceNames = []
We then calculate the blur amount using a Laplacian function and if the blur is low enough we will perform face recognition to the frame. The face recognition is done through calculating a Frobenius Norm to find the variance between saved encodings and encodings within the frame. The lowest variance is the face that is recognized. If the variance is an outlier, then it will assume the face is not in the database and give it an unknown tag.
# Calculate Blur; if its too blurry it won't do facial recognition
blurAmount = cv2.Laplacian(frame, cv2.CV_64F).var()
if blurAmount > 40:
for faceEncoding in faceEncodings:
# See if the face is a match for the known face(s)
matchesFound = face_recognition.compare_faces(encodingList, faceEncoding)
name = "Unknown"
# Or instead, use the known face with the smallest distance to the new face
faceDistances = face_recognition.face_distance(encodingList, faceEncoding)
matchIndex = np.argmin(faceDistances)
if matchesFound[matchIndex]:
name = faceNamesKnown[matchIndex]
# Add name to the faceNames array
faceNames.append(name)
# Process every other frame
processThisFrame = not processThisFrame
This will calculate the coordinates to draw the faces. It also calculates liveness values and blur amounts once again.
# Display the results
for (top, right, bottom, left), name in zip(faceLocations, faceNames):
# Scale back up face locations since the frame we detected in was scaled to 1/4 size
top *= 4
right *= 4
bottom *= 4
left *= 4
# Draw a box around the face
cv2.rectangle(frame, (left, top), (right, bottom), (255, 0, 0), 2)
# Draw a label with a name below the face
cv2.rectangle(frame, (left, bottom - 35), (right, bottom), (255, 0, 0), cv2.FILLED)
font = cv2.FONT_HERSHEY_DUPLEX
# Recalculate blur
blurAmount = cv2.Laplacian(frame, cv2.CV_64F).var()
# Calculate liveness amount
livenessVal = getLivenessValue(frame, inputFrames, model)
This part will actually draw the box with a name if and only if the image is alive.
# if liveness is over 95% then continue recognition
if livenessVal > 0.95:
# Blur must be over 40 in order to accurately recognize a face
if blurAmount > 40:
cv2.putText(frame, name, (left + 6, bottom - 6), font, 1.0, (255, 255, 255), 1)
This part will check if the User is online or offline and their respective mode. If they are online and in online mode, it will record attendance on Google Sheets. This part will also check if they are late or not.
# Online/Offline Mode
if internetCheck and onlineMode:
for x in range(0, len(fullStudentNames)):
if name in fullStudentNames[x]:
# Check if they are late
if elapsedTime > 300:
updateLatePerson()
else:
updatePresentPerson()
If they are offline it will put it on the Microsoft Excel sheet.
else:
for x in range(0, len(fullStudentNames)):
if name in fullStudentNames[x]:
# Check if they are late
if elapsedTime > 300:
updateLatePersonExcel(fullStudentNames[x])
else:
updatePresentPersonExcel(fullStudentNames[x])
This will record it on the text file
for x in range(0, len(faceNamesKnown)):
checkIfHere(name, faceNamesKnown[x])
If it is a spoof, it will warn the user,
else:
# Do not mark anyone if its a spoof
cv2.putText(frame, "WARNING: SPOOF DETECTED", (100, 75), font, 1.0, (0, 0, 255), 2)
This will encode the frame into a .jpeg file so that it can be displayed on the Flask Dashboard.
# Encode frame so it can be displayed on a webpage
ret, jpeg = cv2.imencode('.jpg', frame)
return jpeg.tobytes()
This will catch any potential errors that may occur.
except Exception as e:
# Enceptions to get file + line numbers errors are on
exceptionType, exceptionObject, exceptionThrowback = sys.exc_info()
fileName = os.path.split(exceptionThrowback.tb_frame.f_code.co_filename)[1]
print(exceptionType, fileName, exceptionThrowback.tb_lineno)
print(e)