Commit 40c01470 authored by Kyle Anderson's avatar Kyle Anderson

Merge branch 'initial-user-authentication' into 'master'

Initial user authentication

See merge request !1
parents 29288054 17fc6bae
\ No newline at end of file
Authenticates a user.
Methods for authenticating a user.
from import VideoStream
import face_recognition
import imutils
import pickle
import time
import cv2
# How long to wait before timing out and saying failed authentication.
TIMEOUT: float = 30.0
# Minimum number of frames in which a user must be recognized in order to be authenticated.
image_writer = None
USER_IDS_KEY: str = "names" # TODO change
def load_encodings(file_location: str):
"""Loads the encodings for faces from the given file location."""
with open(file_location, "rb") as encodings_file:
encodings = pickle.loads(
return encodings
def start_video_stream(camera: int):
"""Starts the video stream and returns the created stream.
Also waits for the video stream to open before returning it."""
video_stream = VideoStream(src=camera).start()
return video_stream
def determine_identity(face_encoding, known_faces):
"""Determines the most likely identity of a single face. Returns the user id."""
matches = face_recognition.compare_faces(
known_faces["encodings"], face_encoding)
matched_user = ''
matched_user_id_count = {}
# If there is at least one match to a face in the database, figure out which one it is.
if True in matches:
matched_users = [user_index for (
user_index, is_match) in enumerate(matches) if is_match]
for i in matched_users:
user_id: str = known_faces[USER_IDS_KEY][i]
matched_user_id_count[user_id] = matched_user_id_count.get(user_id, 0) + 1
matched_user: str = max(matched_user_id_count,
return matched_user
def check_recognized_users(recognized_user_counts):
"""Determines if there are recognized users in the dictionary,
and if so returns the list of their IDs"""
recognized_users = []
for user_id, count in recognized_user_counts.items():
return recognized_users
def draw_rectanges_and_user_ids(image_frame, conversion: float, boxes, user_ids: list):
"""Draws the rectangles and user_ids onto the video stream so anyone viewing the stream could see them."""
if boxes and user_ids and len(user_ids) > 0:
for ((top, right, bottom, left), user_id) in zip(boxes, user_ids):
top = round(top * conversion)
right = round(right * conversion)
bottom = round(bottom * conversion)
left = round(left * conversion)
# Draw the rectangle onto the face we've identified
cv2.rectangle(image_frame, (left, top), (right, bottom), (0, 255, 0), 2)
# Find the top so we can put the text there.
y = top - 15 if top - 15 > 15 else top + 15
cv2.putText(image_frame, user_id, (left, y), cv2.FONT_HERSHEY_PLAIN, 0.75, (0, 255, 0), 2)
def display_frame(frame):
"""Displays the frame to the user."""
cv2.imshow("Frame", frame)
def recognize_user(encodings_location: str = "./encodings.pickle", encoding_model: str = "hog", image_flip: int = None,
"""Attempts to recognize a user.
Returns the ID of the user if identified, or None if no users are identified.
Dictionary of the form { "user_id": #frames_recognized } to keep
track of how many times each user was recognized."""
recognized_users_count = {}
recognized_user = None
video_stream = start_video_stream(0)
known_faces = load_encodings(encodings_location)
# Determine the time at which we will time out. Equal to current time + timeout.
timeout_time: float = time.time() + TIMEOUT
while time.time() < timeout_time:
# Read a image_frame from the video stream.
image_frame =
if image_flip is not None:
image_frame = cv2.flip(image_frame, image_flip)
# Convert input from BGR to RGB
cv2.cvtColor(image_frame, cv2.COLOR_BGR2RGB)
# Resize image to width of 750 PX to speed up processing.
rgb_image = imutils.resize(image_frame, width=750)
r = image_frame.shape[1] / float(rgb_image.shape[1])
# Detect the location of each face and determine the boxes in which they lie
boxes = face_recognition.face_locations(
rgb_image, model=encoding_model)
# Computer the facial embeddings (the encoding) at
# each of the locations found in the previous line.
encodings = face_recognition.face_encodings(rgb_image, boxes)
for encoding in encodings:
user_id: str = determine_identity(encoding, known_faces)
if user_id:
if user_id not in recognized_users_count:
recognized_users_count[user_id] = 0
recognized_users_count[user_id] += 1
if draw_rectangles:
draw_rectanges_and_user_ids(image_frame, r, boxes, list(known_faces[USER_IDS_KEY]))
# Now check if we have already positively identified a user enough times
recognized_users = check_recognized_users(recognized_users_count)
if len(recognized_users) > 0:
cv2.waitKey(1) # Required or else video stream doesn't really render.
if recognized_users_count:
recognized_user = max(recognized_users_count,
if recognized_users_count[recognized_user] < MIN_USER_RECOGNITION_COUNT:
recognized_user = None
return recognized_user
# If this program is the main program, authenticate the user.
if __name__ == "__main__":
import argparse;
parser = argparse.ArgumentParser(description="Facial Identification Options")
parser.add_argument("--encodings", "-e", type=str, help="File location of facial encodings.", required=False,
parser.add_argument("--model", "-m", type=str, help="Type of encoding method, either \"hog\" or \"cnn\". HOG is "
"faster, CNN is more accurate.", required=False,
default="hog", choices=["cnn", "hog"])
parser.add_argument("--flip", "-f", type=int,
help="Whether or not to flip the image vertically or horizontally. 0 to flip horizontally, 1 to flip vertically.",
required=False, default=None, choices=[0, 1])
parser.add_argument("--show", "-s", action="store_true",
help="Include this argument to have the image shown to you.", default=False)
args = parser.parse_args()
user = recognize_user(encoding_model=args.model, encodings_location=args.encodings, image_flip=args.flip,
if user:
print(f"Recognized user {user}.")
Creates a facial recognition profile for a new user.
Creates a facial recongnition profile for a new user.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment