Fixed a few small things from PR #109
This commit is contained in:
parent
faa3e28563
commit
f8cfde6f72
6 changed files with 56 additions and 48 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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()))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue