commit
8f217117a9
8 changed files with 81 additions and 37 deletions
11
debian/changelog
vendored
11
debian/changelog
vendored
|
|
@ -1,3 +1,14 @@
|
||||||
|
howdy (2.5.1) xenial; urgency=medium
|
||||||
|
|
||||||
|
* Removed dismiss_lockscreen as it could lock users out of their system (thanks @ujjwalbe, @ju916 and many others!)
|
||||||
|
* Added option to disable howdy when the laptop lid is closed (thanks @accek!)
|
||||||
|
* Added automatic fallback to default frame color palette (thanks @Ethiarpus!)
|
||||||
|
* Added manual exposure setting (thanks @accek!)
|
||||||
|
* Fixed test command ignoring dark frame threshold (thanks @eduncan911!)
|
||||||
|
* Fixed import error in v4l2 recorder (thanks @timwelch!)
|
||||||
|
|
||||||
|
-- boltgolt <boltgolt@gmail.com> Fri, 29 Mar 2019 23:02:21 +0100
|
||||||
|
|
||||||
howdy (2.5.0) xenial; urgency=medium
|
howdy (2.5.0) xenial; urgency=medium
|
||||||
|
|
||||||
* Added FFmpeg and v4l2 recorders (thanks @timwelch!)
|
* Added FFmpeg and v4l2 recorders (thanks @timwelch!)
|
||||||
|
|
|
||||||
25
debian/postinst
vendored
25
debian/postinst
vendored
|
|
@ -70,6 +70,13 @@ if not os.path.exists("/tmp/howdy_picked_device"):
|
||||||
if key == "timout":
|
if key == "timout":
|
||||||
key = "timeout"
|
key = "timeout"
|
||||||
|
|
||||||
|
# MIGRATION 2.5.0 -> 2.5.1
|
||||||
|
# Remove unsafe automatic dismissal of lock screen
|
||||||
|
if key == "dismiss_lockscreen":
|
||||||
|
if value == "true":
|
||||||
|
print("DEPRECATION: Config falue dismiss_lockscreen is no longer supported because of login loop issues.")
|
||||||
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
newConf.set(section, key, value)
|
newConf.set(section, key, value)
|
||||||
# Add a new section where needed
|
# Add a new section where needed
|
||||||
|
|
@ -152,24 +159,6 @@ log("Building dlib")
|
||||||
|
|
||||||
cmd = ["sudo", "python3", "setup.py", "install"]
|
cmd = ["sudo", "python3", "setup.py", "install"]
|
||||||
cuda_used = False
|
cuda_used = False
|
||||||
flags = ""
|
|
||||||
|
|
||||||
# Get the CPU details
|
|
||||||
with open("/proc/cpuinfo") as info:
|
|
||||||
for line in info:
|
|
||||||
if "flags" in line:
|
|
||||||
flags = line
|
|
||||||
break
|
|
||||||
|
|
||||||
# Use the most efficient instruction set the CPU supports
|
|
||||||
if "avx" in flags:
|
|
||||||
cmd += ["--yes", "USE_AVX_INSTRUCTIONS"]
|
|
||||||
elif "sse4" in flags:
|
|
||||||
cmd += ["--yes", "USE_SSE4_INSTRUCTIONS"]
|
|
||||||
elif "sse3" in flags:
|
|
||||||
cmd += ["--yes", "USE_SSE3_INSTRUCTIONS"]
|
|
||||||
elif "sse2" in flags:
|
|
||||||
cmd += ["--yes", "USE_SSE2_INSTRUCTIONS"]
|
|
||||||
|
|
||||||
# Compile and link dlib
|
# Compile and link dlib
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
2
debian/prerm
vendored
2
debian/prerm
vendored
|
|
@ -12,7 +12,7 @@ from shutil import rmtree
|
||||||
if "remove" not in sys.argv and "purge" not in sys.argv:
|
if "remove" not in sys.argv and "purge" not in sys.argv:
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
# Don't try running this if it's already gome
|
# Don't try running this if it's already gone
|
||||||
if not os.path.exists("/lib/security/howdy/cli"):
|
if not os.path.exists("/lib/security/howdy/cli"):
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Show a windows with the video stream and testing information
|
# Show a window with the video stream and testing information
|
||||||
|
|
||||||
# Import required modules
|
# Import required modules
|
||||||
import configparser
|
import configparser
|
||||||
|
|
@ -37,6 +37,10 @@ if fw != -1:
|
||||||
if fh != -1:
|
if fh != -1:
|
||||||
video_capture.set(cv2.CAP_PROP_FRAME_HEIGHT, fh)
|
video_capture.set(cv2.CAP_PROP_FRAME_HEIGHT, fh)
|
||||||
|
|
||||||
|
# Read exposure and dark_thresholds from config to use in the main loop
|
||||||
|
exposure = config.getint("video", "exposure", fallback=-1)
|
||||||
|
dark_threshold = config.getfloat("video", "dark_threshold")
|
||||||
|
|
||||||
# Let the user know what's up
|
# Let the user know what's up
|
||||||
print("""
|
print("""
|
||||||
Opening a window with a test feed
|
Opening a window with a test feed
|
||||||
|
|
@ -106,7 +110,17 @@ try:
|
||||||
|
|
||||||
# Grab a single frame of video
|
# Grab a single frame of video
|
||||||
ret, frame = video_capture.read()
|
ret, frame = video_capture.read()
|
||||||
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
|
||||||
|
try:
|
||||||
|
# Convert from color to grayscale
|
||||||
|
# First processing of frame, so frame errors show up here
|
||||||
|
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
except cv2.error:
|
||||||
|
print("\nUnknown camera, please check your 'device_path' config value.\n")
|
||||||
|
raise
|
||||||
|
|
||||||
frame = clahe.apply(frame)
|
frame = clahe.apply(frame)
|
||||||
# Make a frame to put overlays in
|
# Make a frame to put overlays in
|
||||||
overlay = frame.copy()
|
overlay = frame.copy()
|
||||||
|
|
@ -133,9 +147,6 @@ try:
|
||||||
# Draw the bar in green
|
# Draw the bar in green
|
||||||
cv2.rectangle(overlay, p1, p2, (0, 200, 0), thickness=cv2.FILLED)
|
cv2.rectangle(overlay, p1, p2, (0, 200, 0), thickness=cv2.FILLED)
|
||||||
|
|
||||||
# Draw a stripe indicating the dark threshold
|
|
||||||
cv2.rectangle(overlay, (8, 35), (20, 36), (255, 0, 0), thickness=cv2.FILLED)
|
|
||||||
|
|
||||||
# Print the statis in the bottom left
|
# Print the statis in the bottom left
|
||||||
print_text(0, "RESOLUTION: %dx%d" % (height, width))
|
print_text(0, "RESOLUTION: %dx%d" % (height, width))
|
||||||
print_text(1, "FPS: %d" % (fps, ))
|
print_text(1, "FPS: %d" % (fps, ))
|
||||||
|
|
@ -147,7 +158,7 @@ try:
|
||||||
cv2.putText(overlay, "SLOW MODE", (width - 66, height - 10), cv2.FONT_HERSHEY_SIMPLEX, .3, (0, 0, 255), 0, cv2.LINE_AA)
|
cv2.putText(overlay, "SLOW MODE", (width - 66, height - 10), cv2.FONT_HERSHEY_SIMPLEX, .3, (0, 0, 255), 0, cv2.LINE_AA)
|
||||||
|
|
||||||
# Ignore dark frames
|
# Ignore dark frames
|
||||||
if hist_perc[0] > 50:
|
if hist_perc[0] > dark_threshold:
|
||||||
# Show that this is an ignored frame in the top right
|
# Show that this is an ignored frame in the top right
|
||||||
cv2.putText(overlay, "DARK FRAME", (width - 68, 16), cv2.FONT_HERSHEY_SIMPLEX, .3, (0, 0, 255), 0, cv2.LINE_AA)
|
cv2.putText(overlay, "DARK FRAME", (width - 68, 16), cv2.FONT_HERSHEY_SIMPLEX, .3, (0, 0, 255), 0, cv2.LINE_AA)
|
||||||
else:
|
else:
|
||||||
|
|
@ -156,7 +167,8 @@ try:
|
||||||
|
|
||||||
rec_tm = time.time()
|
rec_tm = time.time()
|
||||||
# Get the locations of all faces and their locations
|
# Get the locations of all faces and their locations
|
||||||
face_locations = face_detector(frame, 1) # upsample 1 time
|
# Upsample it once
|
||||||
|
face_locations = face_detector(frame, 1)
|
||||||
rec_tm = time.time() - rec_tm
|
rec_tm = time.time() - rec_tm
|
||||||
|
|
||||||
# Loop though all faces and paint a circle around them
|
# Loop though all faces and paint a circle around them
|
||||||
|
|
@ -193,6 +205,15 @@ try:
|
||||||
if slow_mode:
|
if slow_mode:
|
||||||
time.sleep(.5 - frame_time)
|
time.sleep(.5 - frame_time)
|
||||||
|
|
||||||
|
if exposure != -1:
|
||||||
|
# For a strange reason on some cameras (e.g. Lenoxo X1E)
|
||||||
|
# setting manual exposure works only after a couple frames
|
||||||
|
# are captured and even after a delay it does not
|
||||||
|
# always work. Setting exposure at every frame is
|
||||||
|
# reliable though.
|
||||||
|
video_capture.set(cv2.CAP_PROP_AUTO_EXPOSURE, 1.0) # 1 = Manual
|
||||||
|
video_capture.set(cv2.CAP_PROP_EXPOSURE, float(exposure))
|
||||||
|
|
||||||
# On ctrl+C
|
# On ctrl+C
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
# Let the user know we're stopping
|
# Let the user know we're stopping
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,9 @@ if fh != -1:
|
||||||
# This will let the camera adjust its light levels while we're importing for faster scanning
|
# This will let the camera adjust its light levels while we're importing for faster scanning
|
||||||
video_capture.grab()
|
video_capture.grab()
|
||||||
|
|
||||||
|
# Read exposure from config to use in the main loop
|
||||||
|
exposure = config.getint("video", "exposure", fallback=-1)
|
||||||
|
|
||||||
# Note the time it took to open the camera
|
# Note the time it took to open the camera
|
||||||
timings["ic"] = time.time() - timings["ic"]
|
timings["ic"] = time.time() - timings["ic"]
|
||||||
|
|
||||||
|
|
@ -180,10 +183,16 @@ while True:
|
||||||
# Grab a single frame of video
|
# Grab a single frame of video
|
||||||
ret, frame = video_capture.read()
|
ret, frame = video_capture.read()
|
||||||
|
|
||||||
|
if frames == 1 and ret is False:
|
||||||
|
print("Could not read from camera")
|
||||||
|
exit(12)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Convert from color to grayscale
|
# Convert from color to grayscale
|
||||||
# First processing of frame, so frame errors show up here
|
# First processing of frame, so frame errors show up here
|
||||||
gsframe = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
gsframe = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
||||||
|
except RuntimeError:
|
||||||
|
gsframe = frame
|
||||||
except cv2.error:
|
except cv2.error:
|
||||||
print("\nUnknown camera, please check your 'device_path' config value.\n")
|
print("\nUnknown camera, please check your 'device_path' config value.\n")
|
||||||
raise
|
raise
|
||||||
|
|
@ -261,3 +270,12 @@ while True:
|
||||||
|
|
||||||
# End peacefully
|
# End peacefully
|
||||||
stop(0)
|
stop(0)
|
||||||
|
|
||||||
|
if exposure != -1:
|
||||||
|
# For a strange reason on some cameras (e.g. Lenoxo X1E)
|
||||||
|
# setting manual exposure works only after a couple frames
|
||||||
|
# are captured and even after a delay it does not
|
||||||
|
# always work. Setting exposure at every frame is
|
||||||
|
# reliable though.
|
||||||
|
video_capture.set(cv2.CAP_PROP_AUTO_EXPOSURE, 1.0) # 1 = Manual
|
||||||
|
video_capture.set(cv2.CAP_PROP_EXPOSURE, float(exposure))
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,8 @@ suppress_unknown = false
|
||||||
# Disable Howdy in remote shells
|
# Disable Howdy in remote shells
|
||||||
ignore_ssh = true
|
ignore_ssh = true
|
||||||
|
|
||||||
# Auto dismiss lock screen on confirmation
|
# Disable Howdy if lid is closed
|
||||||
# Will run loginctl unlock-sessions after every auth
|
ignore_closed_lid = true
|
||||||
# Experimental, can behave incorrectly on some systems
|
|
||||||
dismiss_lockscreen = false
|
|
||||||
|
|
||||||
# Disable howdy in the PAM
|
# Disable howdy in the PAM
|
||||||
# The howdy command will still function
|
# The howdy command will still function
|
||||||
|
|
@ -70,6 +68,12 @@ device_format = v4l2
|
||||||
# OPENCV only.
|
# OPENCV only.
|
||||||
force_mjpeg = false
|
force_mjpeg = false
|
||||||
|
|
||||||
|
# Specify exposure value explicitly. This disables autoexposure.
|
||||||
|
# Use qv4l2 to determine an appropriate value.
|
||||||
|
# OPENCV only.
|
||||||
|
exposure = -1
|
||||||
|
|
||||||
[debug]
|
[debug]
|
||||||
# Show a short but detailed diagnostic report in console
|
# Show a short but detailed diagnostic report in console
|
||||||
|
# Enabling this can cause some UI apps to fail, only enable it to debug
|
||||||
end_report = false
|
end_report = false
|
||||||
|
|
|
||||||
13
src/pam.py
13
src/pam.py
|
|
@ -4,6 +4,7 @@
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import glob
|
||||||
|
|
||||||
# pam-python is running python 2, so we use the old module here
|
# pam-python is running python 2, so we use the old module here
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
|
|
@ -25,8 +26,13 @@ def doAuth(pamh):
|
||||||
if "SSH_CONNECTION" in os.environ or "SSH_CLIENT" in os.environ or "SSHD_OPTS" in os.environ:
|
if "SSH_CONNECTION" in os.environ or "SSH_CLIENT" in os.environ or "SSHD_OPTS" in os.environ:
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Abort if lid is closed
|
||||||
|
if config.getboolean("core", "ignore_closed_lid"):
|
||||||
|
if any("closed" in open(f).read() for f in glob.glob("/proc/acpi/button/lid/*/state")):
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
# Alert the user that we are doing face detection
|
# Alert the user that we are doing face detection
|
||||||
if config.get("core", "detection_notice") == "true":
|
if config.getboolean("core", "detection_notice"):
|
||||||
pamh.conversation(pamh.Message(pamh.PAM_TEXT_INFO, "Attempting face detection"))
|
pamh.conversation(pamh.Message(pamh.PAM_TEXT_INFO, "Attempting face detection"))
|
||||||
|
|
||||||
# Run compare as python3 subprocess to circumvent python version and import issues
|
# Run compare as python3 subprocess to circumvent python version and import issues
|
||||||
|
|
@ -50,11 +56,6 @@ def doAuth(pamh):
|
||||||
if not config.getboolean("core", "no_confirmation"):
|
if not config.getboolean("core", "no_confirmation"):
|
||||||
pamh.conversation(pamh.Message(pamh.PAM_TEXT_INFO, "Identified face as " + pamh.get_user()))
|
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"):
|
|
||||||
# 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"])
|
|
||||||
|
|
||||||
return pamh.PAM_SUCCESS
|
return pamh.PAM_SUCCESS
|
||||||
|
|
||||||
# Otherwise, we can't discribe what happend but it wasn't successful
|
# Otherwise, we can't discribe what happend but it wasn't successful
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import sys
|
||||||
from cv2 import cvtColor, COLOR_GRAY2BGR, CAP_PROP_FRAME_WIDTH, CAP_PROP_FRAME_HEIGHT
|
from cv2 import cvtColor, COLOR_GRAY2BGR, CAP_PROP_FRAME_WIDTH, CAP_PROP_FRAME_HEIGHT
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from v4l2.frame import Frame
|
from pyv4l2.frame import Frame
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print("Missing pyv4l2 module, please run:")
|
print("Missing pyv4l2 module, please run:")
|
||||||
print(" pip3 install pyv4l2\n")
|
print(" pip3 install pyv4l2\n")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue