commit
23088907ad
5 changed files with 67 additions and 60 deletions
|
|
@ -4,7 +4,6 @@
|
|||
# Import required modules
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import getpass
|
||||
import argparse
|
||||
import builtins
|
||||
|
|
@ -16,7 +15,7 @@ except:
|
|||
user = os.environ.get("SUDO_USER")
|
||||
|
||||
# If that fails, try to get the direct user
|
||||
if user == "root" or user == None:
|
||||
if user == "root" or user is None:
|
||||
env_user = getpass.getuser().strip()
|
||||
|
||||
# If even that fails, error out
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
# Save the face of the user in encoded form
|
||||
|
||||
# Import required modules
|
||||
import subprocess
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import cv2
|
||||
import configparser
|
||||
import builtins
|
||||
import cv2
|
||||
|
||||
# Try to import face_recognition and give a nice error if we can't
|
||||
# Add should be the first point where import issues show up
|
||||
|
|
@ -56,12 +55,12 @@ print("Adding face model for the user " + user)
|
|||
label = "Initial model"
|
||||
|
||||
# If models already exist, set that default label
|
||||
if len(encodings) > 0:
|
||||
if encodings:
|
||||
label = "Model #" + str(len(encodings) + 1)
|
||||
|
||||
# Keep de default name if we can't ask questions
|
||||
if builtins.howdy_args.y:
|
||||
print("Using default label \"" + label + "\" because of -y flag")
|
||||
print('Using default label "%s" because of -y flag' % (label, ))
|
||||
else:
|
||||
# Ask the user for a custom label
|
||||
label_in = input("Enter a label for this new model [" + label + "]: ")
|
||||
|
|
@ -82,15 +81,18 @@ insert_model = {
|
|||
video_capture = cv2.VideoCapture(config.get("video", "device_path"))
|
||||
|
||||
# Force MJPEG decoding if true
|
||||
if config.get("video", "force_mjpeg") == "true":
|
||||
if config.getboolean("video", "force_mjpeg"):
|
||||
# Set a magic number, will enable MJPEG but is badly documentated
|
||||
video_capture.set(cv2.CAP_PROP_FOURCC, 1196444237)
|
||||
|
||||
# Set the frame width and height if requested
|
||||
if int(config.get("video", "frame_width")) != -1:
|
||||
video_capture.set(cv2.CAP_PROP_FRAME_WIDTH, int(config.get("video", "frame_width")))
|
||||
fw = config.getint("video", "frame_width")
|
||||
fh = config.getint("video", "frame_height")
|
||||
if fw != -1:
|
||||
video_capture.set(cv2.CAP_PROP_FRAME_WIDTH, fw)
|
||||
|
||||
if int(config.get("video", "frame_height")) != -1:
|
||||
video_capture.set(cv2.CAP_PROP_FRAME_HEIGHT, int(config.get("video", "frame_height")))
|
||||
if fh != -1:
|
||||
video_capture.set(cv2.CAP_PROP_FRAME_HEIGHT, fh)
|
||||
|
||||
# Request a frame to wake the camera up
|
||||
video_capture.read()
|
||||
|
|
@ -117,11 +119,9 @@ while frames < 60:
|
|||
enc = face_recognition.face_encodings(frame)
|
||||
|
||||
# If we've found at least one, we can continue
|
||||
if len(enc) > 0:
|
||||
if enc:
|
||||
break
|
||||
|
||||
# If 0 faces are detected we can't continue
|
||||
if len(enc) == 0:
|
||||
else:
|
||||
print("No face detected, aborting")
|
||||
sys.exit(1)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
# Show a windows with the video stream and testing information
|
||||
|
||||
# Import required modules
|
||||
import face_recognition
|
||||
import cv2
|
||||
import configparser
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import numpy
|
||||
import time
|
||||
import cv2
|
||||
import face_recognition
|
||||
|
||||
# Get the absolute path to the current file
|
||||
path = os.path.dirname(os.path.abspath(__file__))
|
||||
|
|
@ -21,15 +18,18 @@ config.read(path + "/../config.ini")
|
|||
video_capture = cv2.VideoCapture(config.get("video", "device_path"))
|
||||
|
||||
# Force MJPEG decoding if true
|
||||
if config.get("video", "force_mjpeg") == "true":
|
||||
if config.getboolean("video", "force_mjpeg"):
|
||||
# Set a magic number, will enable MJPEG but is badly documented
|
||||
video_capture.set(cv2.CAP_PROP_FOURCC, 1196444237)
|
||||
|
||||
# Set the frame width and height if requested
|
||||
if int(config.get("video", "frame_width")) != -1:
|
||||
video_capture.set(cv2.CAP_PROP_FRAME_WIDTH, int(config.get("video", "frame_width")))
|
||||
fw = config.getint("video", "frame_width")
|
||||
fh = config.getint("video", "frame_height")
|
||||
if fw != -1:
|
||||
video_capture.set(cv2.CAP_PROP_FRAME_WIDTH, fw)
|
||||
|
||||
if int(config.get("video", "frame_height")) != -1:
|
||||
video_capture.set(cv2.CAP_PROP_FRAME_HEIGHT, int(config.get("video", "frame_height")))
|
||||
if fh != -1:
|
||||
video_capture.set(cv2.CAP_PROP_FRAME_HEIGHT, fh)
|
||||
|
||||
# Let the user know what's up
|
||||
print("""
|
||||
|
|
@ -47,6 +47,10 @@ def mouse(event, x, y, flags, param):
|
|||
if event == cv2.EVENT_LBUTTONDOWN:
|
||||
slow_mode = not slow_mode
|
||||
|
||||
def print_text(line_number, text):
|
||||
"""Print the status text by line number"""
|
||||
cv2.putText(overlay, text, (10, height - 10 - (10 * line_number)), cv2.FONT_HERSHEY_SIMPLEX, .3, (0, 255, 0), 0, cv2.LINE_AA)
|
||||
|
||||
# Open the window and attach a a mouse listener
|
||||
cv2.namedWindow("Howdy Test")
|
||||
cv2.setMouseCallback("Howdy Test", mouse)
|
||||
|
|
@ -61,11 +65,13 @@ sec_frames = 0
|
|||
fps = 0
|
||||
# The current second we're counting
|
||||
sec = int(time.time())
|
||||
# recognition time
|
||||
rec_tm = 0
|
||||
|
||||
# Wrap everything in an keyboard interupt handler
|
||||
try:
|
||||
while True:
|
||||
# Inclement the frames
|
||||
# Increment the frames
|
||||
total_frames += 1
|
||||
sec_frames += 1
|
||||
|
||||
|
|
@ -109,14 +115,11 @@ try:
|
|||
# Draw a stripe indicating the dark threshold
|
||||
cv2.rectangle(overlay, (8, 35), (20, 36), (255, 0, 0), thickness=cv2.FILLED)
|
||||
|
||||
def print_text(line_number, text):
|
||||
"""Print the status text by line number"""
|
||||
cv2.putText(overlay, text, (10, height - 10 - (10 * line_number)), cv2.FONT_HERSHEY_SIMPLEX, .3, (0, 255, 0), 0, cv2.LINE_AA)
|
||||
|
||||
# Print the statis in the bottom left
|
||||
print_text(0, "RESOLUTION: " + str(height) + "x" + str(width))
|
||||
print_text(1, "FPS: " + str(fps))
|
||||
print_text(2, "FRAMES: " + str(total_frames))
|
||||
print_text(0, "RESOLUTION: %dx%d" % (height, width))
|
||||
print_text(1, "FPS: %d" % (fps, ))
|
||||
print_text(2, "FRAMES: %d" % (total_frames, ))
|
||||
print_text(3, "RECOGNITION: %dms" % (round(rec_tm * 1000), ))
|
||||
|
||||
# Show that slow mode is on, if it's on
|
||||
if slow_mode:
|
||||
|
|
@ -130,8 +133,10 @@ try:
|
|||
# SHow that this is an active frame
|
||||
cv2.putText(overlay, "SCAN FRAME", (width - 68, 16), cv2.FONT_HERSHEY_SIMPLEX, .3, (0, 255, 0), 0, cv2.LINE_AA)
|
||||
|
||||
rec_tm = time.time()
|
||||
# Get the locations of all faces and their locations
|
||||
face_locations = face_recognition.face_locations(frame)
|
||||
rec_tm = time.time() - rec_tm
|
||||
|
||||
# Loop though all faces and paint a circle around them
|
||||
for loc in face_locations:
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import cv2
|
|||
import sys
|
||||
import os
|
||||
import json
|
||||
import math
|
||||
import configparser
|
||||
|
||||
# Read config from disk
|
||||
|
|
@ -43,17 +42,16 @@ dark_tries = 0
|
|||
# Try to load the face model from the models folder
|
||||
try:
|
||||
models = json.load(open(os.path.dirname(os.path.abspath(__file__)) + "/models/" + user + ".dat"))
|
||||
|
||||
# Put all models together into 1 array
|
||||
encodings = [model["data"] for model in models]
|
||||
except FileNotFoundError:
|
||||
sys.exit(10)
|
||||
|
||||
# Check if the file contains a model
|
||||
if len(models) < 1:
|
||||
if not encodings:
|
||||
sys.exit(10)
|
||||
|
||||
# Put all models together into 1 array
|
||||
for model in models:
|
||||
encodings += model["data"]
|
||||
|
||||
# Add the time needed to start the script
|
||||
timings.append(time.time())
|
||||
|
||||
|
|
@ -61,16 +59,18 @@ timings.append(time.time())
|
|||
video_capture = cv2.VideoCapture(config.get("video", "device_path"))
|
||||
|
||||
# Force MJPEG decoding if true
|
||||
if config.get("video", "force_mjpeg") == "true":
|
||||
if config.getboolean("video", "force_mjpeg"):
|
||||
# Set a magic number, will enable MJPEG but is badly documentated
|
||||
video_capture.set(cv2.CAP_PROP_FOURCC, 1196444237)
|
||||
|
||||
# Set the frame width and height if requested
|
||||
if int(config.get("video", "frame_width")) != -1:
|
||||
video_capture.set(cv2.CAP_PROP_FRAME_WIDTH, int(config.get("video", "frame_width")))
|
||||
fw = config.getint("video", "frame_width")
|
||||
fh = config.getint("video", "frame_height")
|
||||
if fw != -1:
|
||||
video_capture.set(cv2.CAP_PROP_FRAME_WIDTH, fw)
|
||||
|
||||
if int(config.get("video", "frame_height")) != -1:
|
||||
video_capture.set(cv2.CAP_PROP_FRAME_HEIGHT, int(config.get("video", "frame_height")))
|
||||
if fh != -1:
|
||||
video_capture.set(cv2.CAP_PROP_FRAME_HEIGHT, fh)
|
||||
|
||||
# Capture a single frame so the camera becomes active
|
||||
# This will let the camera adjust its light levels while we're importing for faster scanning
|
||||
|
|
@ -88,12 +88,15 @@ max_height = int(config.get("video", "max_height"))
|
|||
|
||||
# Start the read loop
|
||||
frames = 0
|
||||
timeout = config.getint("video", "timout")
|
||||
dark_threshold = config.getfloat("video", "dark_threshold")
|
||||
end_report = config.getboolean("debug", "end_report")
|
||||
while True:
|
||||
# Increment the frame count every loop
|
||||
frames += 1
|
||||
|
||||
# Stop if we've exceded the time limit
|
||||
if time.time() - timings[3] > int(config.get("video", "timout")):
|
||||
if time.time() - timings[3] > timeout:
|
||||
stop(11)
|
||||
|
||||
# Grab a single frame of video
|
||||
|
|
@ -111,7 +114,7 @@ while True:
|
|||
continue
|
||||
|
||||
# Scrip the frame if it exceeds the threshold
|
||||
if float(hist[0]) / hist_total * 100 > float(config.get("video", "dark_threshold")):
|
||||
if float(hist[0]) / hist_total * 100 > dark_threshold:
|
||||
dark_tries += 1
|
||||
continue
|
||||
|
||||
|
|
@ -146,31 +149,31 @@ while True:
|
|||
timings.append(time.time())
|
||||
|
||||
# If set to true in the config, print debug text
|
||||
if config.get("debug", "end_report") == "true":
|
||||
if end_report:
|
||||
def print_timing(label, offset):
|
||||
"""Helper function to print a timing from the list"""
|
||||
print(" " + label + ": " + str(round((timings[1 + offset] - timings[offset]) * 1000)) + "ms")
|
||||
print(" %s: %dms" % (label, round((timings[1 + offset] - timings[offset]) * 1000)))
|
||||
|
||||
print("Time spend")
|
||||
print("Time spent")
|
||||
print_timing("Starting up", 0)
|
||||
print_timing("Opening the camera", 1)
|
||||
print_timing("Importing face_recognition", 2)
|
||||
print_timing("Searching for known face", 3)
|
||||
|
||||
print("\nResolution")
|
||||
print(" Native: " + str(height) + "x" + str(width))
|
||||
print(" Used: " + str(scale_height) + "x" + str(scale_width))
|
||||
print(" Native: %dx%d" % (height, width))
|
||||
print(" Used: %dx%d" % (scale_height, scale_width))
|
||||
|
||||
# Show the total number of frames and calculate the FPS by deviding it by the total scan time
|
||||
print("\nFrames searched: " + str(frames) + " (" + str(round(float(frames) / (timings[4] - timings[3]), 2)) + " fps)")
|
||||
print("Dark frames ignored: " + str(dark_tries))
|
||||
print("Certainty of winning frame: " + str(round(match * 10, 3)))
|
||||
print("\nFrames searched: %d (%.2f fps)" % (frames, frames / (timings[4] - timings[3])))
|
||||
print("Dark frames ignored: %d " % (dark_tries, ))
|
||||
print("Certainty of winning frame: %.3f" % (match * 10, ))
|
||||
|
||||
# Catch older 3-encoding models
|
||||
if not match_index in models:
|
||||
match_index = 0
|
||||
|
||||
print("Winning model: " + str(match_index) + " (\"" + models[match_index]["label"] + "\")")
|
||||
print("Winning model: %d (\"%s\")" % (match_index, models[match_index]["label"]))
|
||||
|
||||
# End peacefully
|
||||
stop(0)
|
||||
|
|
|
|||
10
src/pam.py
10
src/pam.py
|
|
@ -16,11 +16,11 @@ def doAuth(pamh):
|
|||
"""Starts authentication in a seperate process"""
|
||||
|
||||
# Abort is Howdy is disabled
|
||||
if config.get("core", "disabled") == "true":
|
||||
if config.getboolean("core", "disabled"):
|
||||
sys.exit(0)
|
||||
|
||||
# Abort if we're in a remote SSH env
|
||||
if config.get("core", "ignore_ssh") == "true":
|
||||
if config.getboolean("core", "ignore_ssh"):
|
||||
if "SSH_CONNECTION" in os.environ or "SSH_CLIENT" in os.environ or "SSHD_OPTS" in os.environ:
|
||||
sys.exit(0)
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ def doAuth(pamh):
|
|||
|
||||
# Status 10 means we couldn't find any face models
|
||||
if status == 10:
|
||||
if config.get("core", "suppress_unknown") != "true":
|
||||
if not config.getboolean("core", "suppress_unknown"):
|
||||
pamh.conversation(pamh.Message(pamh.PAM_ERROR_MSG, "No face model known"))
|
||||
return pamh.PAM_USER_UNKNOWN
|
||||
# Status 11 means we exceded the maximum retry count
|
||||
|
|
@ -39,11 +39,11 @@ def doAuth(pamh):
|
|||
# Status 0 is a successful exit
|
||||
if status == 0:
|
||||
# Show the success message if it isn't suppressed
|
||||
if config.get("core", "no_confirmation") != "true":
|
||||
if not config.getboolean("core", "no_confirmation"):
|
||||
pamh.conversation(pamh.Message(pamh.PAM_TEXT_INFO, "Identified face as " + pamh.get_user()))
|
||||
|
||||
# Try to dismiss the lock screen if enabled
|
||||
if config.get("core", "dismiss_lockscreen") == "true":
|
||||
if config.get("core", "dismiss_lockscreen"):
|
||||
# Run it as root with a timeout of 1s, and never ask for a password through the UI
|
||||
subprocess.Popen(["sudo", "timeout", "1", "loginctl", "unlock-sessions", "--no-ask-password"])
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue