authenticate_user.py 5.36 KB
Newer Older
Kyle Anderson's avatar
Kyle Anderson committed
1
"""
2
Methods for authenticating a user.
Kyle Anderson's avatar
Kyle Anderson committed
3
"""
4 5 6 7 8 9 10 11 12 13 14 15 16 17

from imutils.video 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
# The encoding to use. Hog is faster. Other one is "cnn", which will only really be doable with a GPU.
ENCODING_MODEL: str = "hog"
# Minimum number of frames in which a user must be recognized in order to be authenticated.
MIN_USER_RECOGNITION_COUNT = 10
Kyle Anderson's avatar
Kyle Anderson committed
18 19
draw_rectangles: bool = False
image_writer = None
20 21 22


def load_encodings(file_location: str):
Kyle Anderson's avatar
Kyle Anderson committed
23
    """Loads the encodings for faces from the given file location."""
24 25 26 27 28 29
    with open(file_location, "rb") as encodings_file:
        encodings = pickle.loads(encodings_file.read())
    return encodings


def start_video_stream(camera: int):
Kyle Anderson's avatar
Kyle Anderson committed
30 31
    """Starts the video stream and returns the created stream. 
    Also waits for the video stream to open before returning it."""
32 33 34 35
    video_stream = VideoStream(src=camera)
    time.sleep(2.0)
    return video_stream

Kyle Anderson's avatar
Kyle Anderson committed
36

37
def determine_identity(face_encoding, known_faces):
Kyle Anderson's avatar
Kyle Anderson committed
38 39 40
    """Determines the most likely identity of a single face. Returns the user id."""
    matches = face_recognition.compare_faces(
        known_faces["encodings"], face_encoding)
41
    matched_user = None
Kyle Anderson's avatar
Kyle Anderson committed
42

43 44
    # If there is at least one match to a face in the database, figure out which one it is.
    if True in matches:
Kyle Anderson's avatar
Kyle Anderson committed
45 46 47 48 49 50 51 52 53 54 55
        matched_user_id_count = {}
        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"][i]
            matched_user_id_count[user_id] = matched_user_id_count.get(
                user_id, 0) + 1

    matched_user: str = max(matched_user_id_count,
                            keys=matched_user_id_count.get())
56 57
    return matched_user

Kyle Anderson's avatar
Kyle Anderson committed
58

59
def check_recognized_users(recognized_user_counts):
Kyle Anderson's avatar
Kyle Anderson committed
60 61
    """Determines if there are recognized users in the dictionary,
    and if so returns the list of their IDs"""
62
    recognized_users = []
Kyle Anderson's avatar
Kyle Anderson committed
63
    for user_id, count in recognized_user_counts.items():
64 65 66
        if count >= MIN_USER_RECOGNITION_COUNT:
            recognized_users.append(user_id)
    return recognized_users
Kyle Anderson's avatar
Kyle Anderson committed
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87

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."""
    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)
    display_frame(image_frame)

def display_frame(frame):
    """Displays the frame to the user."""
    cv2.imshow("Frame", frame)


88
def recognize_user():
Kyle Anderson's avatar
Kyle Anderson committed
89 90 91 92
    """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."""
93 94 95
    recognized_users_count = {}
    recognized_user = None
    video_stream = start_video_stream(0)
Kyle Anderson's avatar
Kyle Anderson committed
96
    known_faces = load_encodings("./encodings.pickle")
97 98 99 100 101
    user_recognized: bool = False

    # 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 and not user_recognized):
Kyle Anderson's avatar
Kyle Anderson committed
102 103
        # Read a image_frame from the videostream.
        image_frame = video_stream.read()
104 105

        # Convert input from BGR to RGB
Kyle Anderson's avatar
Kyle Anderson committed
106
        rgb_image = cv2.cvtColor(image_frame, cv2.COLOR_BGR2RGB)
107
        # Resize image to width of 750 PX to speed up processing.
Kyle Anderson's avatar
Kyle Anderson committed
108 109
        rgb_image = imutils.resize(image_frame, width=750)
        r = image_frame.shape[1] / float(rgb_image.shape[1])
110 111 112 113

        # Detect the location of each face and put a rectangle around it
        boxes = face_recognition.face_locations(
            rgb_image, model=ENCODING_MODEL)
Kyle Anderson's avatar
Kyle Anderson committed
114 115
        # Computer the facial embeddings (the encoding) at
        # each of the locations found in the previous line.
116 117 118
        encodings = face_recognition.face_encodings(rgb_image, boxes)

        for encoding in encodings:
Kyle Anderson's avatar
Kyle Anderson committed
119 120 121 122 123 124 125
            user_id: str = determine_identity(encoding, known_faces)
            if user_id:
                recognized_users_count[user_id] += 1

        if draw_rectangles:
            draw_rectanges_and_user_ids(image_frame, r, boxes, known_faces.keys)

126 127 128 129 130
        # 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:
            user_recognized = True
            break
Kyle Anderson's avatar
Kyle Anderson committed
131 132 133

    recognized_user = max(recognized_users_count,
                          keys=recognized_users_count.get())
134 135 136 137 138 139 140 141
    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__":
    recognize_user()