Fixed a few small things from PR #109

This commit is contained in:
boltgolt 2018-12-13 20:10:08 +01:00
parent faa3e28563
commit f8cfde6f72
No known key found for this signature in database
GPG key ID: BECEC9937E1AAE26
6 changed files with 56 additions and 48 deletions

View file

@ -77,14 +77,15 @@ insert_model = {
"data": [] "data": []
} }
# Check if the user explicitly set ffmpeg as recorder
if config.get("video", "recording_plugin") == "ffmpeg": if config.get("video", "recording_plugin") == "ffmpeg":
from ffmpeg_reader import ffmpeg_reader from ffmpeg_reader import ffmpeg_reader
# Set the capture source for ffmpeg # Set the capture source for ffmpeg
video_capture = ffmpeg_reader(config.get("video", "device_name"), config.get("video", "device_format"), 10) video_capture = ffmpeg_reader(config.get("video", "device_path"), config.get("video", "device_format"))
else: else:
# Start video capture on the IR camera # Start video capture on the IR camera through OpenCV
video_capture = cv2.VideoCapture(config.get("video", "device_path")) video_capture = cv2.VideoCapture(config.get("video", "device_path"))
# Force MJPEG decoding if true # Force MJPEG decoding if true

View file

@ -3,6 +3,7 @@
# Import required modules # Import required modules
import configparser import configparser
import os import os
import sys
import time import time
import cv2 import cv2
import face_recognition import face_recognition
@ -14,6 +15,11 @@ path = os.path.dirname(os.path.abspath(__file__))
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read(path + "/../config.ini") config.read(path + "/../config.ini")
if config.get("video", "recording_plugin") == "ffmpeg":
print("Howdy has been configured to use ffmpeg as recorder, which doesn't support the test command yet")
print("Aborting")
sys.exit(12)
# Start capturing from the configured webcam # Start capturing from the configured webcam
video_capture = cv2.VideoCapture(config.get("video", "device_path")) video_capture = cv2.VideoCapture(config.get("video", "device_path"))

View file

@ -28,7 +28,7 @@ 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(12)
# The username of the authenticating user # The username of the authenticating user
user = sys.argv[1] user = sys.argv[1]
@ -56,14 +56,15 @@ for model in models:
# Add the time needed to start the script # Add the time needed to start the script
timings.append(time.time()) timings.append(time.time())
# Check if the user explicitly set ffmpeg as recorder
if config.get("video", "recording_plugin") == "ffmpeg": if config.get("video", "recording_plugin") == "ffmpeg":
from ffmpeg_reader import ffmpeg_reader from ffmpeg_reader import ffmpeg_reader
# Set the capture source for ffmpeg # Set the capture source for ffmpeg
video_capture = ffmpeg_reader(config.get("video", "device_name"), config.get("video", "device_format"), 10) video_capture = ffmpeg_reader(config.get("video", "device_path"), config.get("video", "device_format"))
else: else:
# Start video capture on the IR camera # Start video capture on the IR camera through OpenCV
video_capture = cv2.VideoCapture(config.get("video", "device_path")) video_capture = cv2.VideoCapture(config.get("video", "device_path"))
# Force MJPEG decoding if true # Force MJPEG decoding if true

View file

@ -22,21 +22,6 @@ dismiss_lockscreen = false
disabled = false disabled = false
[video] [video]
#
# opencv or ffmpeg. Choose ffmpeg if your IR device is a grayscale only.
# Run this: v4l2-ctl -d /dev/video2 --list-formats
recording_plugin = opencv
#
# format to use for ffmpeg. Could be vfwcap or v4l2 or some other thing...
#
device_format = v4l2
#
# device name if using ffmpeg. ex: /dev/video2
#
device_name = none
# The certainty of the detected face belonging to the user of the account # The certainty of the detected face belonging to the user of the account
# On a scale from 1 to 10, values above 5 are not recommended # On a scale from 1 to 10, values above 5 are not recommended
certainty = 3.5 certainty = 3.5
@ -45,17 +30,13 @@ certainty = 3.5
timeout = 4 timeout = 4
# The path of the device to capture frames from # The path of the device to capture frames from
# Should be set automatically by the installer # Should be set automatically by an installer if your distro has one
device_path = none device_path = none
# Scale down the video feed to this maximum height # Scale down the video feed to this maximum height
# Speeds up face recognition but can make it less precise # Speeds up face recognition but can make it less precise
max_height = 320 max_height = 320
# Force the use of Motion JPEG when decoding frames, fixes issues with
# YUYV raw frame decoding
force_mjpeg = false
# Set the camera input profile to this width and height # Set the camera input profile to this width and height
# The largest profile will be used if set to -1 # The largest profile will be used if set to -1
# Automatically ignored if not a valid profile # Automatically ignored if not a valid profile
@ -68,6 +49,19 @@ frame_height = -1
# The lower this setting is, the more dark frames are ignored # The lower this setting is, the more dark frames are ignored
dark_threshold = 50 dark_threshold = 50
# The recorder to use. Can be either opencv or ffmpeg.
# Switching from the default opencv to ffmpeg can help with grayscale issues.
recording_plugin = opencv
# Video format used by ffmpeg. Options include vfwcap or v4l2.
# FFMPEG only.
device_format = v4l2
# Force the use of Motion JPEG when decoding frames, fixes issues with YUYV
# raw frame decoding.
# OPENCV only.
force_mjpeg = false
[debug] [debug]
# Show a short but detailed diagnostic report in console # Show a short but detailed diagnostic report in console
end_report = false end_report = false

View file

@ -1,18 +1,26 @@
import ffmpeg # Class that simulates the functionality of opencv so howdy can use ffmpeg seamlessly
# Import required modules
import numpy import numpy
import sys
import re
from subprocess import Popen, PIPE
from cv2 import CAP_PROP_FRAME_WIDTH from cv2 import CAP_PROP_FRAME_WIDTH
from cv2 import CAP_PROP_FRAME_HEIGHT from cv2 import CAP_PROP_FRAME_HEIGHT
from cv2 import CAP_PROP_FOURCC from cv2 import CAP_PROP_FOURCC
import sys
from subprocess import Popen, PIPE try:
import re import ffmpeg
except ImportError:
print("Missing ffmpeg module, please run:")
print(" pip3 install ffmpeg-python\n")
sys.exit(12)
class ffmpeg_reader: class ffmpeg_reader:
""" This class was created to look as similar to the openCV features used in Howdy as possible for overall code cleanliness. """ """ This class was created to look as similar to the openCV features used in Howdy as possible for overall code cleanliness. """
# Init def __init__(self, device_path, device_format, numframes=10):
def __init__(self, device_name, device_format, numframes): self.device_path = device_path
self.device_name = device_name
self.device_format = device_format self.device_format = device_format
self.numframes = numframes self.numframes = numframes
self.video = () self.video = ()
@ -20,7 +28,6 @@ class ffmpeg_reader:
self.height = 0 self.height = 0
self.width = 0 self.width = 0
self.init_camera = True self.init_camera = True
self.force_jpeg = 0
def set(self, prop, setting): def set(self, prop, setting):
""" Setter method for height and width """ """ Setter method for height and width """
@ -28,9 +35,6 @@ class ffmpeg_reader:
self.width = setting self.width = setting
elif prop == CAP_PROP_FRAME_HEIGHT: elif prop == CAP_PROP_FRAME_HEIGHT:
self.height = setting self.height = setting
elif prop == CAP_PROP_FOURCC:
self.force_jpeg = setting
def get(self, prop): def get(self, prop):
""" Getter method for height and width """ """ Getter method for height and width """
@ -38,34 +42,32 @@ class ffmpeg_reader:
return self.width return self.width
elif prop == CAP_PROP_FRAME_HEIGHT: elif prop == CAP_PROP_FRAME_HEIGHT:
return self.height return self.height
elif prop == CAP_PROP_FOURCC:
return self.force_jpeg
def probe(self): def probe(self):
""" Probe the video device to get height and width info """ """ Probe the video device to get height and width info """
# Running this command on ffmpeg unfortunatly returns with an exit code of 1, which is silly. # Running this command on ffmpeg unfortunatly returns with an exit code of 1, which is silly.
# Returns an error code of 1 and this text: "/dev/video2: Immediate exit requested" # Returns an error code of 1 and this text: "/dev/video2: Immediate exit requested"
args = ["ffmpeg", "-f", self.device_format, "-list_formats", "all", "-i", self.device_name] args = ["ffmpeg", "-f", self.device_format, "-list_formats", "all", "-i", self.device_path]
process = Popen(args, stdout=PIPE, stderr=PIPE) process = Popen(args, stdout=PIPE, stderr=PIPE)
out, err = process.communicate() out, err = process.communicate()
return_code = process.poll() return_code = process.poll()
# worst case scenario, err will equal en empty byte string, b'', so probe will get set to [] here. # Worst case scenario, err will equal en empty byte string, b'', so probe will get set to [] here.
regex = re.compile('\s\d{3,4}x\d{3,4}') regex = re.compile('\s\d{3,4}x\d{3,4}')
probe = regex.findall(str(err.decode("utf-8"))) probe = regex.findall(str(err.decode("utf-8")))
if not return_code == 1 or len(probe) < 1: if not return_code == 1 or len(probe) < 1:
# Could not determine the resolution from ffmpeg call. Reverting to ffmpeg.probe() # Could not determine the resolution from ffmpeg call. Reverting to ffmpeg.probe()
probe = ffmpeg.probe(self.device_name) probe = ffmpeg.probe(self.device_path)
height = probe['streams'][0]['height'] height = probe['streams'][0]['height']
width = probe['streams'][0]['width'] width = probe['streams'][0]['width']
else: else:
(height, width) = [x.strip() for x in probe[0].split('x')] (height, width) = [x.strip() for x in probe[0].split('x')]
# Set height and width from probe if they haven't been set already
if height.isdigit() and self.get(CAP_PROP_FRAME_HEIGHT) == 0: if height.isdigit() and self.get(CAP_PROP_FRAME_HEIGHT) == 0:
self.set(CAP_PROP_FRAME_HEIGHT, int(height)) self.set(CAP_PROP_FRAME_HEIGHT, int(height))
if width.isdigit() and self.get(CAP_PROP_FRAME_WIDTH) == 0: if width.isdigit() and self.get(CAP_PROP_FRAME_WIDTH) == 0:
self.set(CAP_PROP_FRAME_WIDTH, int(width)) self.set(CAP_PROP_FRAME_WIDTH, int(width))
@ -80,9 +82,10 @@ class ffmpeg_reader:
# Ensure num_frames_read is reset to 0 # Ensure num_frames_read is reset to 0
self.num_frames_read = 0 self.num_frames_read = 0
# Record a predetermined amount of frames from the camera
stream, ret = ( stream, ret = (
ffmpeg ffmpeg
.input(self.device_name, format=self.device_format) .input(self.device_path, format=self.device_format)
.output('pipe:', format='rawvideo', pix_fmt='rgb24', vframes=numframes) .output('pipe:', format='rawvideo', pix_fmt='rgb24', vframes=numframes)
.run(capture_stdout=True, quiet=True) .run(capture_stdout=True, quiet=True)
) )

View file

@ -33,11 +33,14 @@ def doAuth(pamh):
pamh.conversation(pamh.Message(pamh.PAM_ERROR_MSG, "No face model known")) pamh.conversation(pamh.Message(pamh.PAM_ERROR_MSG, "No face model known"))
return pamh.PAM_USER_UNKNOWN return pamh.PAM_USER_UNKNOWN
# Status 11 means we exceded the maximum retry count # Status 11 means we exceded the maximum retry count
if status == 11: elif status == 11:
pamh.conversation(pamh.Message(pamh.PAM_ERROR_MSG, "Face detection timeout reached")) pamh.conversation(pamh.Message(pamh.PAM_ERROR_MSG, "Face detection timeout reached"))
return pamh.PAM_AUTH_ERR return pamh.PAM_AUTH_ERR
# Status 12 means we aborted
elif status == 12:
return pamh.PAM_AUTH_ERR
# Status 0 is a successful exit # Status 0 is a successful exit
if status == 0: elif status == 0:
# Show the success message if it isn't suppressed # Show the success message if it isn't suppressed
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()))