From 542549934b9b9f86a6dbdc3bf90ff37905cfb589 Mon Sep 17 00:00:00 2001 From: boltgolt Date: Fri, 5 Jan 2018 16:37:00 +0100 Subject: [PATCH] Cleanup --- compair.py | 21 +++++++++++++++++++-- learn.py | 22 ++++++++++++++++++++++ pam.py | 14 ++++++++++++++ utils.py | 3 +++ 4 files changed, 58 insertions(+), 2 deletions(-) diff --git a/compair.py b/compair.py index c48aec5..ba7a6c5 100644 --- a/compair.py +++ b/compair.py @@ -1,49 +1,66 @@ +# Compair incomming video with known faces +# Running in a local python instance to get around PATH issues + +# Import required modules import face_recognition import cv2 import sys import os import json +# Import config import config def stop(status): + """Stop the execution and close video stream""" video_capture.release() sys.exit(status) +# Make sure we were given an username to tast against try: if not isinstance(sys.argv[1], str): sys.exit(1) except IndexError: sys.exit(1) +# The username of the authenticating user user = sys.argv[1] +# List of known faces, encoded by face_recognition encodings = [] +# Amount of frames already matched tries = 0 +# Try to load the face model from the models folder try: encodings = json.load(open(os.path.dirname(__file__) + "/models/" + user + ".dat")) except FileNotFoundError: stop(10) +# Verify that we have a valid model file if len(encodings) < 3: stop(1) +# Start video capture on the IR camera video_capture = cv2.VideoCapture(config.device_id) while True: # Grab a single frame of video ret, frame = video_capture.read() + # Get all faces from that frame as encodings face_encodings = face_recognition.face_encodings(frame) - # Loop through each face in this frame of video + # Loop through each face for face_encoding in face_encodings: + # Match this found face against a known face matches = face_recognition.face_distance(encodings, face_encoding) + # Check if any match is certain enough to be the user we're looking for for match in matches: - if match < config.certainty: + if match < config.certainty and match > 0: stop(0) + # Stop if we've exceded the maximum retry count if tries > config.frame_count: stop(11) diff --git a/learn.py b/learn.py index 2b1e3e8..0d50ad5 100644 --- a/learn.py +++ b/learn.py @@ -1,3 +1,6 @@ +# Save the face of the user in encoded form + +# Import required modules import face_recognition import subprocess import time @@ -5,58 +8,77 @@ import os import sys import json +# Import config and extra functions import config import utils def captureFrame(delay): + """Capture and encode 1 frame of video""" + + # Call fswebcam to save a frame to /tmp with a set delay subprocess.call(["fswebcam", "-S", str(delay), "--no-banner", "-d", "/dev/video" + str(config.device_id), tmp_file], stderr=open(os.devnull, "wb")) + # Get the faces in htat image ref = face_recognition.load_image_file(tmp_file) enc = face_recognition.face_encodings(ref) + # If 0 faces are detected we can't continue if len(enc) == 0: print("No face detected, aborting") sys.exit() + # If more than 1 faces are detected we can't know wich one belongs to the user if len(enc) > 1: print("Multiple faces detected, aborting") sys.exit() clean_enc = [] + # Copy the values into a clean array so we can export it as JSON later on for point in enc[0]: clean_enc.append(point) encodings.append(clean_enc) +# The current user user = os.environ.get("USER") +# The name of the tmp frame file to user tmp_file = "/tmp/howdy_" + user + ".jpg" +# The permanent file to store the encoded model in enc_file = "./models/" + user + ".dat" +# Known encodings encodings = [] +# Make the ./models folder if it doesn't already exist if not os.path.exists("models"): print("No face model folder found, creating one") os.makedirs("models") +# To try read a premade encodings file if it exists try: encodings = json.load(open(enc_file)) except FileNotFoundError: encodings = False +# If a file does exist, ask the user what needs to be done if encodings != False: encodings = utils.print_menu(encodings) print("\nLearning face for the user account " + user) print("Please look straight into the camera for 5 seconds") +# Give the user time to read time.sleep(2) +# Capture with 3 different delays to simulate different camera exposures for delay in [30, 6, 0]: time.sleep(.3) captureFrame(delay) +# Save the new encodings to disk with open(enc_file, "w") as datafile: json.dump(encodings, datafile) +# Remove any left over temp files os.remove(tmp_file) print("Done.") diff --git a/pam.py b/pam.py index 16a90cd..25a87ff 100644 --- a/pam.py +++ b/pam.py @@ -1,31 +1,45 @@ +# PAM interface in python, launches compair.py + +# Import required modules import subprocess import sys import os def doAuth(pamh): + """Start authentication in a seperate process""" + + # Run compair as python3 subprocess to circumvent python version and import issues status = subprocess.call(["python3", os.path.dirname(__file__) + "/compair.py", pamh.get_user()]) + # Status 10 means we couldn't find any face models if status == 10: print("No face model is known for this user, skiping") return pamh.PAM_SYSTEM_ERR + # Status 11 means we exceded the maximum retry count if status == 11: print("Timeout reached, could not find a known face") return pamh.PAM_SYSTEM_ERR + # Status 0 is a successful exit if status == 0: print("Identified face as " + os.environ.get("USER")) return pamh.PAM_SUCCESS + # Otherwise, we can't discribe what happend but it wasn't successful print("Unknown error: " + str(status)) return pamh.PAM_SYSTEM_ERR def pam_sm_authenticate(pamh, flags, args): + """Called by PAM when the user wants to authenticate, in sudo for example""" return doAuth(pamh) def pam_sm_open_session(pamh, flags, args): + """Called when starting a session, such as su""" return doAuth(pamh) def pam_sm_close_session(pamh, flags, argv): + """We don't need to clean anyting up at the end of a session, so return true""" return pamh.PAM_SUCCESS def pam_sm_setcred(pamh, flags, argv): + """We don't need set any credentials, so return true""" return pamh.PAM_SUCCESS diff --git a/utils.py b/utils.py index 1ed3ac4..848edac 100644 --- a/utils.py +++ b/utils.py @@ -1,4 +1,7 @@ +# Useful support functions + def print_menu(encodings): + """Show a menu asking the user what he wants to do""" if len(encodings) == 3: print("There is 1 existing face model for this user") else: