This commit is contained in:
boltgolt 2018-01-05 16:37:00 +01:00
parent 47acb8f729
commit 542549934b
4 changed files with 58 additions and 2 deletions

View file

@ -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 face_recognition
import cv2 import cv2
import sys import sys
import os import os
import json import json
# Import config
import config import config
def stop(status): def stop(status):
"""Stop the execution and close video stream"""
video_capture.release() video_capture.release()
sys.exit(status) sys.exit(status)
# Make sure we were given an username to tast against
try: try:
if not isinstance(sys.argv[1], str): if not isinstance(sys.argv[1], str):
sys.exit(1) sys.exit(1)
except IndexError: except IndexError:
sys.exit(1) sys.exit(1)
# The username of the authenticating user
user = sys.argv[1] user = sys.argv[1]
# List of known faces, encoded by face_recognition
encodings = [] encodings = []
# Amount of frames already matched
tries = 0 tries = 0
# Try to load the face model from the models folder
try: try:
encodings = json.load(open(os.path.dirname(__file__) + "/models/" + user + ".dat")) encodings = json.load(open(os.path.dirname(__file__) + "/models/" + user + ".dat"))
except FileNotFoundError: except FileNotFoundError:
stop(10) stop(10)
# Verify that we have a valid model file
if len(encodings) < 3: if len(encodings) < 3:
stop(1) stop(1)
# Start video capture on the IR camera
video_capture = cv2.VideoCapture(config.device_id) video_capture = cv2.VideoCapture(config.device_id)
while True: while True:
# Grab a single frame of video # Grab a single frame of video
ret, frame = video_capture.read() ret, frame = video_capture.read()
# Get all faces from that frame as encodings
face_encodings = face_recognition.face_encodings(frame) 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: for face_encoding in face_encodings:
# Match this found face against a known face
matches = face_recognition.face_distance(encodings, face_encoding) 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: for match in matches:
if match < config.certainty: if match < config.certainty and match > 0:
stop(0) stop(0)
# Stop if we've exceded the maximum retry count
if tries > config.frame_count: if tries > config.frame_count:
stop(11) stop(11)

View file

@ -1,3 +1,6 @@
# Save the face of the user in encoded form
# Import required modules
import face_recognition import face_recognition
import subprocess import subprocess
import time import time
@ -5,58 +8,77 @@ import os
import sys import sys
import json import json
# Import config and extra functions
import config import config
import utils import utils
def captureFrame(delay): 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")) 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) ref = face_recognition.load_image_file(tmp_file)
enc = face_recognition.face_encodings(ref) enc = face_recognition.face_encodings(ref)
# If 0 faces are detected we can't continue
if len(enc) == 0: if len(enc) == 0:
print("No face detected, aborting") print("No face detected, aborting")
sys.exit() sys.exit()
# If more than 1 faces are detected we can't know wich one belongs to the user
if len(enc) > 1: if len(enc) > 1:
print("Multiple faces detected, aborting") print("Multiple faces detected, aborting")
sys.exit() sys.exit()
clean_enc = [] clean_enc = []
# Copy the values into a clean array so we can export it as JSON later on
for point in enc[0]: for point in enc[0]:
clean_enc.append(point) clean_enc.append(point)
encodings.append(clean_enc) encodings.append(clean_enc)
# The current user
user = os.environ.get("USER") user = os.environ.get("USER")
# The name of the tmp frame file to user
tmp_file = "/tmp/howdy_" + user + ".jpg" tmp_file = "/tmp/howdy_" + user + ".jpg"
# The permanent file to store the encoded model in
enc_file = "./models/" + user + ".dat" enc_file = "./models/" + user + ".dat"
# Known encodings
encodings = [] encodings = []
# Make the ./models folder if it doesn't already exist
if not os.path.exists("models"): if not os.path.exists("models"):
print("No face model folder found, creating one") print("No face model folder found, creating one")
os.makedirs("models") os.makedirs("models")
# To try read a premade encodings file if it exists
try: try:
encodings = json.load(open(enc_file)) encodings = json.load(open(enc_file))
except FileNotFoundError: except FileNotFoundError:
encodings = False encodings = False
# If a file does exist, ask the user what needs to be done
if encodings != False: if encodings != False:
encodings = utils.print_menu(encodings) encodings = utils.print_menu(encodings)
print("\nLearning face for the user account " + user) print("\nLearning face for the user account " + user)
print("Please look straight into the camera for 5 seconds") print("Please look straight into the camera for 5 seconds")
# Give the user time to read
time.sleep(2) time.sleep(2)
# Capture with 3 different delays to simulate different camera exposures
for delay in [30, 6, 0]: for delay in [30, 6, 0]:
time.sleep(.3) time.sleep(.3)
captureFrame(delay) captureFrame(delay)
# Save the new encodings to disk
with open(enc_file, "w") as datafile: with open(enc_file, "w") as datafile:
json.dump(encodings, datafile) json.dump(encodings, datafile)
# Remove any left over temp files
os.remove(tmp_file) os.remove(tmp_file)
print("Done.") print("Done.")

14
pam.py
View file

@ -1,31 +1,45 @@
# PAM interface in python, launches compair.py
# Import required modules
import subprocess import subprocess
import sys import sys
import os import os
def doAuth(pamh): 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 = 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: if status == 10:
print("No face model is known for this user, skiping") print("No face model is known for this user, skiping")
return pamh.PAM_SYSTEM_ERR return pamh.PAM_SYSTEM_ERR
# Status 11 means we exceded the maximum retry count
if status == 11: if status == 11:
print("Timeout reached, could not find a known face") print("Timeout reached, could not find a known face")
return pamh.PAM_SYSTEM_ERR return pamh.PAM_SYSTEM_ERR
# Status 0 is a successful exit
if status == 0: if status == 0:
print("Identified face as " + os.environ.get("USER")) print("Identified face as " + os.environ.get("USER"))
return pamh.PAM_SUCCESS return pamh.PAM_SUCCESS
# Otherwise, we can't discribe what happend but it wasn't successful
print("Unknown error: " + str(status)) print("Unknown error: " + str(status))
return pamh.PAM_SYSTEM_ERR return pamh.PAM_SYSTEM_ERR
def pam_sm_authenticate(pamh, flags, args): def pam_sm_authenticate(pamh, flags, args):
"""Called by PAM when the user wants to authenticate, in sudo for example"""
return doAuth(pamh) return doAuth(pamh)
def pam_sm_open_session(pamh, flags, args): def pam_sm_open_session(pamh, flags, args):
"""Called when starting a session, such as su"""
return doAuth(pamh) return doAuth(pamh)
def pam_sm_close_session(pamh, flags, argv): 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 return pamh.PAM_SUCCESS
def pam_sm_setcred(pamh, flags, argv): def pam_sm_setcred(pamh, flags, argv):
"""We don't need set any credentials, so return true"""
return pamh.PAM_SUCCESS return pamh.PAM_SUCCESS

View file

@ -1,4 +1,7 @@
# Useful support functions
def print_menu(encodings): def print_menu(encodings):
"""Show a menu asking the user what he wants to do"""
if len(encodings) == 3: if len(encodings) == 3:
print("There is 1 existing face model for this user") print("There is 1 existing face model for this user")
else: else: