Added getext translating function

This commit is contained in:
boltgolt 2020-12-31 14:15:17 +01:00
parent 473aa9807e
commit 55f3753260
No known key found for this signature in database
GPG key ID: BECEC9937E1AAE26
15 changed files with 152 additions and 115 deletions

View file

@ -8,6 +8,8 @@ import getpass
import argparse import argparse
import builtins import builtins
from i18n import _
# Try to get the original username (not "root") from shell # Try to get the original username (not "root") from shell
try: try:
user = os.getlogin() user = os.getlogin()
@ -20,48 +22,48 @@ if user == "root" or user is None:
# If even that fails, error out # If even that fails, error out
if env_user == "": if env_user == "":
print("Could not determine user, please use the --user flag") print(_("Could not determine user, please use the --user flag"))
sys.exit(1) sys.exit(1)
else: else:
user = env_user user = env_user
# Basic command setup # Basic command setup
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Command line interface for Howdy face authentication.", description=_("Command line interface for Howdy face authentication."),
formatter_class=argparse.RawDescriptionHelpFormatter, formatter_class=argparse.RawDescriptionHelpFormatter,
add_help=False, add_help=False,
prog="howdy", prog="howdy",
epilog="For support please visit\nhttps://github.com/boltgolt/howdy") epilog=_("For support please visit\nhttps://github.com/boltgolt/howdy"))
# Add an argument for the command # Add an argument for the command
parser.add_argument( parser.add_argument(
"command", _("command"),
help="The command option to execute, can be one of the following: add, clear, config, disable, list, remove, snapshot, set, test or version.", help=_("The command option to execute, can be one of the following: add, clear, config, disable, list, remove, snapshot, set, test or version."),
metavar="command", metavar="command",
choices=["add", "clear", "config", "disable", "list", "remove", "set", "snapshot", "test", "version"]) choices=["add", "clear", "config", "disable", "list", "remove", "set", "snapshot", "test", "version"])
# Add an argument for the extra arguments of diable and remove # Add an argument for the extra arguments of diable and remove
parser.add_argument( parser.add_argument(
"arguments", _("arguments"),
help="Optional arguments for the add, disable, remove and set commands.", help=_("Optional arguments for the add, disable, remove and set commands."),
nargs="*") nargs="*")
# Add the user flag # Add the user flag
parser.add_argument( parser.add_argument(
"-U", "--user", "-U", "--user",
default=user, default=user,
help="Set the user account to use.") help=_("Set the user account to use."))
# Add the -y flag # Add the -y flag
parser.add_argument( parser.add_argument(
"-y", "-y",
help="Skip all questions.", help=_("Skip all questions."),
action="store_true") action="store_true")
# Add the --plain flag # Add the --plain flag
parser.add_argument( parser.add_argument(
"--plain", "--plain",
help="Print machine-friendly output.", help=_("Print machine-friendly output."),
action="store_true") action="store_true")
# Overwrite the default help message so we can use a uppercase S # Overwrite the default help message so we can use a uppercase S
@ -69,11 +71,11 @@ parser.add_argument(
"-h", "--help", "-h", "--help",
action="help", action="help",
default=argparse.SUPPRESS, default=argparse.SUPPRESS,
help="Show this help message and exit.") help=_("Show this help message and exit."))
# If we only have 1 argument we print the help text # If we only have 1 argument we print the help text
if len(sys.argv) < 2: if len(sys.argv) < 2:
print("current active user: " + user + "\n") print(_("current active user: ") + user + "\n")
parser.print_help() parser.print_help()
sys.exit(0) sys.exit(0)
@ -87,13 +89,13 @@ builtins.howdy_user = args.user
# Check if we have rootish rights # Check if we have rootish rights
# This is this far down the file so running the command for help is always possible # This is this far down the file so running the command for help is always possible
if os.geteuid() != 0: if os.geteuid() != 0:
print("Please run this command as root:\n") print(_("Please run this command as root:\n"))
print("\tsudo howdy " + " ".join(sys.argv[1:])) print("\tsudo howdy " + " ".join(sys.argv[1:]))
sys.exit(1) sys.exit(1)
# Beond this point the user can't change anymore, if we still have root as user we need to abort # Beond this point the user can't change anymore, if we still have root as user we need to abort
if args.user == "root": if args.user == "root":
print("Can't run howdy commands as root, please run this command with the --user flag") print(_("Can't run howdy commands as root, please run this command with the --user flag"))
sys.exit(1) sys.exit(1)
# Execute the right command # Execute the right command

View file

@ -8,7 +8,9 @@ import json
import configparser import configparser
import builtins import builtins
import numpy as np import numpy as np
from recorders.video_capture import VideoCapture from recorders.video_capture import VideoCapture
from i18n import _
# Try to import dlib and give a nice error if we can't # Try to import dlib and give a nice error if we can't
# Add should be the first point where import issues show up # Add should be the first point where import issues show up
@ -17,7 +19,7 @@ try:
except ImportError as err: except ImportError as err:
print(err) print(err)
print("\nCan't import the dlib module, check the output of") print(_("\nCan't import the dlib module, check the output of"))
print("pip3 show dlib") print("pip3 show dlib")
sys.exit(1) sys.exit(1)
@ -29,7 +31,7 @@ path = os.path.abspath(__file__ + "/..")
# Test if at lest 1 of the data files is there and abort if it's not # Test if at lest 1 of the data files is there and abort if it's not
if not os.path.isfile(path + "/../dlib-data/shape_predictor_5_face_landmarks.dat"): if not os.path.isfile(path + "/../dlib-data/shape_predictor_5_face_landmarks.dat"):
print("Data files have not been downloaded, please run the following commands:") print(_("Data files have not been downloaded, please run the following commands:"))
print("\n\tcd " + os.path.realpath(path + "/../dlib-data")) print("\n\tcd " + os.path.realpath(path + "/../dlib-data"))
print("\tsudo ./install.sh\n") print("\tsudo ./install.sh\n")
sys.exit(1) sys.exit(1)
@ -55,7 +57,7 @@ encodings = []
# Make the ./models folder if it doesn't already exist # Make the ./models folder if it doesn't already exist
if not os.path.exists(path + "/../models"): if not os.path.exists(path + "/../models"):
print("No face model folder found, creating one") print(_("No face model folder found, creating one"))
os.makedirs(path + "/../models") os.makedirs(path + "/../models")
# To try read a premade encodings file if it exists # To try read a premade encodings file if it exists
@ -66,30 +68,30 @@ except FileNotFoundError:
# Print a warning if too many encodings are being added # Print a warning if too many encodings are being added
if len(encodings) > 3: if len(encodings) > 3:
print("NOTICE: Each additional model slows down the face recognition engine slightly") print(_("NOTICE: Each additional model slows down the face recognition engine slightly"))
print("Press Ctrl+C to cancel\n") print(_("Press Ctrl+C to cancel\n"))
# Make clear what we are doing if not human # Make clear what we are doing if not human
if not builtins.howdy_args.plain: if not builtins.howdy_args.plain:
print("Adding face model for the user " + user) print(_("Adding face model for the user " + user))
# Set the default label # Set the default label
label = "Initial model" label = "Initial model"
# Get the label from the cli arguments if provided # Get the label from the cli arguments if provided
if builtins.howdy_args.arguments: if builtins.howdy_args.arguments:
label = builtins.howdy_args.arguments[0] label = builtins.howdy_args.arguments[0]
# If models already exist, set that default label # If models already exist, set that default label
elif encodings: elif encodings:
label = "Model #" + str(len(encodings) + 1) label = _("Model #") + str(len(encodings) + 1)
# Keep de default name if we can't ask questions # Keep de default name if we can't ask questions
if builtins.howdy_args.y: if builtins.howdy_args.y:
print('Using default label "%s" because of -y flag' % (label, )) print(_('Using default label "%s" because of -y flag') % (label, ))
else: else:
# Ask the user for a custom label # Ask the user for a custom label
label_in = input("Enter a label for this new model [" + label + "]: ") label_in = input(_("Enter a label for this new model [{}]: ").format(label))
# Set the custom label (if any) and limit it to 24 characters # Set the custom label (if any) and limit it to 24 characters
if label_in != "": if label_in != "":
@ -97,7 +99,7 @@ else:
# Remove illegal characters # Remove illegal characters
if "," in label: if "," in label:
print("NOTICE: Removing illegal character \",\" from model name") print(_("NOTICE: Removing illegal character \",\" from model name"))
label = label.replace(",", "") label = label.replace(",", "")
# Prepare the metadata for insertion # Prepare the metadata for insertion
@ -111,7 +113,7 @@ insert_model = {
# Set up video_capture # Set up video_capture
video_capture = VideoCapture(config) video_capture = VideoCapture(config)
print("\nPlease look straight into the camera") print(_("\nPlease look straight into the camera"))
# Give the user time to read # Give the user time to read
time.sleep(2) time.sleep(2)
@ -176,17 +178,17 @@ video_capture.release()
# If we've found no faces, try to determine why # If we've found no faces, try to determine why
if face_locations is None or not face_locations: if face_locations is None or not face_locations:
if valid_frames == 0: if valid_frames == 0:
print("Camera saw only black frames - is IR emitter working?") print(_("Camera saw only black frames - is IR emitter working?"))
elif valid_frames == dark_tries: elif valid_frames == dark_tries:
print("All frames were too dark, please check dark_threshold in config") print(_("All frames were too dark, please check dark_threshold in config"))
print("Average darkness: " + str(dark_running_total / valid_frames) + ", Threshold: " + str(dark_threshold)) print(_("Average darkness: {avg}, Threshold: {threshold}").format(avg=str(dark_running_total / valid_frames), threshold=str(dark_threshold)))
else: else:
print("No face detected, aborting") print(_("No face detected, aborting"))
sys.exit(1) sys.exit(1)
# If more than 1 faces are detected we can't know wich one belongs to the user # If more than 1 faces are detected we can't know wich one belongs to the user
elif len(face_locations) > 1: elif len(face_locations) > 1:
print("Multiple faces detected, aborting") print(_("Multiple faces detected, aborting"))
sys.exit(1) sys.exit(1)
face_location = face_locations[0] face_location = face_locations[0]
@ -207,6 +209,5 @@ with open(enc_file, "w") as datafile:
json.dump(encodings, datafile) json.dump(encodings, datafile)
# Give let the user know how it went # Give let the user know how it went
print("""Scan complete print(_("""\nScan complete
Added a new model to """) + user)
Added a new model to """ + user)

View file

@ -5,6 +5,8 @@ import os
import sys import sys
import builtins import builtins
from i18n import _
# Get the full path to this file # Get the full path to this file
path = os.path.dirname(os.path.abspath(__file__)) path = os.path.dirname(os.path.abspath(__file__))
# Get the passed user # Get the passed user
@ -12,25 +14,25 @@ user = builtins.howdy_user
# Check if the models folder is there # Check if the models folder is there
if not os.path.exists(path + "/../models"): if not os.path.exists(path + "/../models"):
print("No models created yet, can't clear them if they don't exist") print(_("No models created yet, can't clear them if they don't exist"))
sys.exit(1) sys.exit(1)
# Check if the user has a models file to delete # Check if the user has a models file to delete
if not os.path.isfile(path + "/../models/" + user + ".dat"): if not os.path.isfile(path + "/../models/" + user + ".dat"):
print(user + " has no models or they have been cleared already") print(_("{} has no models or they have been cleared already").format(user))
sys.exit(1) sys.exit(1)
# Only ask the user if there's no -y flag # Only ask the user if there's no -y flag
if not builtins.howdy_args.y: if not builtins.howdy_args.y:
# Double check with the user # Double check with the user
print("This will clear all models for " + user) print(_("This will clear all models for ") + user)
ans = input("Do you want to continue [y/N]: ") ans = input(_("Do you want to continue [y/N]: "))
# Abort if they don't answer y or Y # Abort if they don't answer y or Y
if (ans.lower() != "y"): if (ans.lower() != "y"):
print('\nInerpeting as a "NO"') print(_('\nInerpeting as a "NO", aborting'))
sys.exit(1) sys.exit(1)
# Delete otherwise # Delete otherwise
os.remove(path + "/../models/" + user + ".dat") os.remove(path + "/../models/" + user + ".dat")
print("\nModels cleared") print(_("\nModels cleared"))

View file

@ -4,8 +4,10 @@
import os import os
import subprocess import subprocess
from i18n import _
# Let the user know what we're doing # Let the user know what we're doing
print("Opening config.ini in the default editor") print(_("Opening config.ini in the default editor"))
# Default to the nano editor # Default to the nano editor
editor = "/bin/nano" editor = "/bin/nano"

View file

@ -7,6 +7,8 @@ import builtins
import fileinput import fileinput
import configparser import configparser
from i18n import _
# Get the absolute filepath # Get the absolute filepath
config_path = os.path.dirname(os.path.abspath(__file__)) + "/../config.ini" config_path = os.path.dirname(os.path.abspath(__file__)) + "/../config.ini"
@ -16,7 +18,7 @@ config.read(config_path)
# Check if enough arguments have been passed # Check if enough arguments have been passed
if not builtins.howdy_args.arguments: if not builtins.howdy_args.arguments:
print("Please add a 0 (enable) or a 1 (disable) as an argument") print(_("Please add a 0 (enable) or a 1 (disable) as an argument"))
sys.exit(1) sys.exit(1)
# Get the cli argument # Get the cli argument
@ -29,12 +31,12 @@ elif argument == "0" or argument.lower() == "false":
out_value = "false" out_value = "false"
else: else:
# Of it's not a 0 or a 1, it's invalid # Of it's not a 0 or a 1, it's invalid
print("Please only use 0 (enable) or 1 (disable) as an argument") print(_("Please only use 0 (enable) or 1 (disable) as an argument"))
sys.exit(1) sys.exit(1)
# Don't do anything when the state is already the requested one # Don't do anything when the state is already the requested one
if out_value == config.get("core", "disabled"): if out_value == config.get("core", "disabled"):
print("The disable option has already been set to " + out_value) print(_("The disable option has already been set to ") + out_value)
sys.exit(1) sys.exit(1)
# Loop though the config file and only replace the line containing the disable config # Loop though the config file and only replace the line containing the disable config
@ -43,6 +45,6 @@ for line in fileinput.input([config_path], inplace=1):
# Print what we just did # Print what we just did
if out_value == "true": if out_value == "true":
print("Howdy has been disabled") print(_("Howdy has been disabled"))
else: else:
print("Howdy has been enabled") print(_("Howdy has been enabled"))

View file

@ -7,13 +7,15 @@ import json
import time import time
import builtins import builtins
from i18n import _
# Get the absolute path and the username # Get the absolute path and the username
path = os.path.dirname(os.path.realpath(__file__)) + "/.." path = os.path.dirname(os.path.realpath(__file__)) + "/.."
user = builtins.howdy_user user = builtins.howdy_user
# Check if the models file has been created yet # Check if the models file has been created yet
if not os.path.exists(path + "/models"): if not os.path.exists(path + "/models"):
print("Face models have not been initialized yet, please run:") print(_("Face models have not been initialized yet, please run:"))
print("\n\tsudo howdy -U " + user + " add\n") print("\n\tsudo howdy -U " + user + " add\n")
sys.exit(1) sys.exit(1)
@ -24,14 +26,14 @@ enc_file = path + "/models/" + user + ".dat"
try: try:
encodings = json.load(open(enc_file)) encodings = json.load(open(enc_file))
except FileNotFoundError: except FileNotFoundError:
print("No face model known for the user " + user + ", please run:") print(_("No face model known for the user {}, please run:").format(user))
print("\n\tsudo howdy -U " + user + " add\n") print("\n\tsudo howdy -U " + user + " add\n")
sys.exit(1) sys.exit(1)
# Print a header if we're not in plain mode # Print a header if we're not in plain mode
if not builtins.howdy_args.plain: if not builtins.howdy_args.plain:
print("Known face models for " + user + ":") print(_("Known face models for {}:").format(user))
print("\n\033[1;29mID Date Label\033[0m") print("\n\033[1;29m" + _("ID Date Label\033[0m"))
# Loop through all encodings and print info about them # Loop through all encodings and print info about them
for enc in encodings: for enc in encodings:

View file

@ -6,22 +6,24 @@ import os
import json import json
import builtins import builtins
from i18n import _
# Get the absolute path and the username # Get the absolute path and the username
path = os.path.dirname(os.path.realpath(__file__)) + "/.." path = os.path.dirname(os.path.realpath(__file__)) + "/.."
user = builtins.howdy_user user = builtins.howdy_user
# Check if enough arguments have been passed # Check if enough arguments have been passed
if not builtins.howdy_args.arguments: if not builtins.howdy_args.arguments:
print("Please add the ID of the model you want to remove as an argument") print(_("Please add the ID of the model you want to remove as an argument"))
print("For example:") print(_("For example:"))
print("\n\thowdy remove 0\n") print("\n\thowdy remove 0\n")
print("You can find the IDs by running:") print(_("You can find the IDs by running:"))
print("\n\thowdy list\n") print("\n\thowdy list\n")
sys.exit(1) sys.exit(1)
# Check if the models file has been created yet # Check if the models file has been created yet
if not os.path.exists(path + "/models"): if not os.path.exists(path + "/models"):
print("Face models have not been initialized yet, please run:") print(_("Face models have not been initialized yet, please run:"))
print("\n\thowdy add\n") print("\n\thowdy add\n")
sys.exit(1) sys.exit(1)
@ -32,7 +34,7 @@ enc_file = path + "/models/" + user + ".dat"
try: try:
encodings = json.load(open(enc_file)) encodings = json.load(open(enc_file))
except FileNotFoundError: except FileNotFoundError:
print("No face model known for the user " + user + ", please run:") print(_("No face model known for the user " + user + ", please run:"))
print("\n\thowdy add\n") print("\n\thowdy add\n")
sys.exit(1) sys.exit(1)
@ -48,12 +50,12 @@ for enc in encodings:
# Only ask the user if there's no -y flag # Only ask the user if there's no -y flag
if not builtins.howdy_args.y: if not builtins.howdy_args.y:
# Double check with the user # Double check with the user
print('This will remove the model called "' + enc["label"] + '" for ' + user) print(_('This will remove the model called "{label}" for {user}').format(label=enc["label"], user=user))
ans = input("Do you want to continue [y/N]: ") ans = input(_("Do you want to continue [y/N]: "))
# Abort if the answer isn't yes # Abort if the answer isn't yes
if (ans.lower() != "y"): if (ans.lower() != "y"):
print('\nInterpreting as a "NO", aborting') print(_('\nInterpreting as a "NO", aborting'))
sys.exit(1) sys.exit(1)
# Add a padding empty line # Add a padding empty line
@ -65,13 +67,13 @@ for enc in encodings:
# Abort if no matching id was found # Abort if no matching id was found
if not found: if not found:
print("No model with ID " + id + " exists for " + user) print(_("No model with ID {id} exists for {user}").format(id=id, user=user))
sys.exit(1) sys.exit(1)
# Remove the entire file if this encoding is the only one # Remove the entire file if this encoding is the only one
if len(encodings) == 1: if len(encodings) == 1:
os.remove(path + "/models/" + user + ".dat") os.remove(path + "/models/" + user + ".dat")
print("Removed last model, howdy disabled for user") print(_("Removed last model, howdy disabled for user"))
else: else:
# A place holder to contain the encodings that will remain # A place holder to contain the encodings that will remain
new_encodings = [] new_encodings = []
@ -85,4 +87,4 @@ else:
with open(enc_file, "w") as datafile: with open(enc_file, "w") as datafile:
json.dump(new_encodings, datafile) json.dump(new_encodings, datafile)
print("Removed model " + id) print(_("Removed model {}").format(id))

View file

@ -6,13 +6,15 @@ import os
import builtins import builtins
import fileinput import fileinput
from i18n import _
# Get the absolute filepath # Get the absolute filepath
config_path = os.path.dirname(os.path.abspath(__file__)) + "/../config.ini" config_path = os.path.dirname(os.path.abspath(__file__)) + "/../config.ini"
# Check if enough arguments have been passed # Check if enough arguments have been passed
if len(builtins.howdy_args.arguments) < 2: if len(builtins.howdy_args.arguments) < 2:
print("Please add a setting you would like to change and the value to set it to") print(_("Please add a setting you would like to change and the value to set it to"))
print("For example:") print(_("For example:"))
print("\n\thowdy set certainty 3\n") print("\n\thowdy set certainty 3\n")
sys.exit(1) sys.exit(1)
@ -31,11 +33,11 @@ for line in fileinput.input([config_path]):
# If we don't have the line it is not in the config file # If we don't have the line it is not in the config file
if not found_line: if not found_line:
print('Could not find a "' + set_name + '" config option to set') print(_('Could not find a "{}" config option to set').format(set_name))
sys.exit(1) sys.exit(1)
# Go through the file again and update the correct line # Go through the file again and update the correct line
for line in fileinput.input([config_path], inplace=1): for line in fileinput.input([config_path], inplace=1):
print(line.replace(found_line, set_name + " = " + set_value + "\n"), end="") print(line.replace(found_line, set_name + " = " + set_value + "\n"), end="")
print("Config option updated") print(_("Config option updated"))

View file

@ -7,6 +7,8 @@ import datetime
import snapshot import snapshot
from recorders.video_capture import VideoCapture from recorders.video_capture import VideoCapture
from i18n import _
# Get the absolute path to the current directory # Get the absolute path to the current directory
path = os.path.abspath(__file__ + "/..") path = os.path.abspath(__file__ + "/..")
@ -40,12 +42,12 @@ while True:
# Generate a snapshot image from the frames # Generate a snapshot image from the frames
file = snapshot.generate(frames, [ file = snapshot.generate(frames, [
"GENERATED SNAPSHOT", _("GENERATED SNAPSHOT"),
"Date: " + datetime.datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S UTC"), _("Date: ") + datetime.datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S UTC"),
"Dark threshold config: " + str(config.getfloat("video", "dark_threshold", fallback=50.0)), _("Dark threshold config: ") + str(config.getfloat("video", "dark_threshold", fallback=50.0)),
"Certainty config: " + str(config.getfloat("video", "certainty", fallback=3.5)) _("Certainty config: ") + str(config.getfloat("video", "certainty", fallback=3.5))
]) ])
# Show the file location in console # Show the file location in console
print("Generated snapshot saved as") print(_("Generated snapshot saved as"))
print(file) print(file)

View file

@ -9,6 +9,8 @@ import dlib
import cv2 import cv2
from recorders.video_capture import VideoCapture from recorders.video_capture import VideoCapture
from i18n import _
# Get the absolute path to the current file # Get the absolute path to the current file
path = os.path.dirname(os.path.abspath(__file__)) path = os.path.dirname(os.path.abspath(__file__))
@ -17,8 +19,7 @@ config = configparser.ConfigParser()
config.read(path + "/../config.ini") config.read(path + "/../config.ini")
if config.get("video", "recording_plugin") != "opencv": if config.get("video", "recording_plugin") != "opencv":
print("Howdy has been configured to use a recorder which doesn't support the test command yet") print(_("Howdy has been configured to use a recorder which doesn't support the test command yet, aborting"))
print("Aborting")
sys.exit(12) sys.exit(12)
video_capture = VideoCapture(config) video_capture = VideoCapture(config)
@ -28,12 +29,12 @@ exposure = config.getint("video", "exposure", fallback=-1)
dark_threshold = config.getfloat("video", "dark_threshold") 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
Press ctrl+C in this terminal to quit Press ctrl+C in this terminal to quit
Click on the image to enable or disable slow mode Click on the image to enable or disable slow mode
""") """))
def mouse(event, x, y, flags, param): def mouse(event, x, y, flags, param):
@ -97,7 +98,7 @@ try:
sec_frames = 0 sec_frames = 0
# Grab a single frame of video # Grab a single frame of video
_, frame = video_capture.read_frame() ret, frame = video_capture.read_frame()
frame = clahe.apply(frame) frame = clahe.apply(frame)
# Make a frame to put overlays in # Make a frame to put overlays in
@ -127,22 +128,22 @@ try:
cv2.rectangle(overlay, p1, p2, (0, 200, 0), thickness=cv2.FILLED) cv2.rectangle(overlay, p1, p2, (0, 200, 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, ))
print_text(2, "FRAMES: %d" % (total_frames, )) print_text(2, _("FRAMES: %d") % (total_frames, ))
print_text(3, "RECOGNITION: %dms" % (round(rec_tm * 1000), )) print_text(3, _("RECOGNITION: %dms") % (round(rec_tm * 1000), ))
# Show that slow mode is on, if it's on # Show that slow mode is on, if it's on
if slow_mode: if slow_mode:
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] > dark_threshold: 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:
# SHow that this is an active frame # 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) cv2.putText(overlay, _("SCAN FRAME"), (width - 68, 16), cv2.FONT_HERSHEY_SIMPLEX, .3, (0, 255, 0), 0, cv2.LINE_AA)
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
@ -197,7 +198,7 @@ try:
# On ctrl+C # On ctrl+C
except KeyboardInterrupt: except KeyboardInterrupt:
# Let the user know we're stopping # Let the user know we're stopping
print("\nClosing window") print(_("\nClosing window"))
# Release handle to the webcam # Release handle to the webcam
cv2.destroyAllWindows() cv2.destroyAllWindows()

View file

@ -20,7 +20,9 @@ import datetime
import snapshot import snapshot
import numpy as np import numpy as np
import _thread as thread import _thread as thread
from recorders.video_capture import VideoCapture from recorders.video_capture import VideoCapture
from i18n import _
def init_detector(lock): def init_detector(lock):
@ -29,7 +31,7 @@ def init_detector(lock):
# Test if at lest 1 of the data files is there and abort if it's not # Test if at lest 1 of the data files is there and abort if it's not
if not os.path.isfile(PATH + "/dlib-data/shape_predictor_5_face_landmarks.dat"): if not os.path.isfile(PATH + "/dlib-data/shape_predictor_5_face_landmarks.dat"):
print("Data files have not been downloaded, please run the following commands:") print(_("Data files have not been downloaded, please run the following commands:"))
print("\n\tcd " + PATH + "/dlib-data") print("\n\tcd " + PATH + "/dlib-data")
print("\tsudo ./install.sh\n") print("\tsudo ./install.sh\n")
lock.release() lock.release()
@ -53,12 +55,12 @@ def init_detector(lock):
def make_snapshot(type): def make_snapshot(type):
"""Generate snapshot after detection""" """Generate snapshot after detection"""
snapshot.generate(snapframes, [ snapshot.generate(snapframes, [
type + " LOGIN", type + _(" LOGIN"),
"Date: " + datetime.datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S UTC"), _("Date: ") + datetime.datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S UTC"),
"Scan time: " + str(round(time.time() - timings["fr"], 2)) + "s", _("Scan time: ") + str(round(time.time() - timings["fr"], 2)) + "s",
"Frames: " + str(frames) + " (" + str(round(frames / (time.time() - timings["fr"]), 2)) + "FPS)", _("Frames: ") + str(frames) + " (" + str(round(frames / (time.time() - timings["fr"]), 2)) + "FPS)",
"Hostname: " + os.uname().nodename, _("Hostname: ") + os.uname().nodename,
"Best certainty value: " + str(round(lowest_certainty * 10, 1)) _("Best certainty value: ") + str(round(lowest_certainty * 10, 1))
]) ])
@ -172,11 +174,11 @@ while True:
if time.time() - timings["fr"] > timeout: if time.time() - timings["fr"] > timeout:
# Create a timeout snapshot if enabled # Create a timeout snapshot if enabled
if capture_failed: if capture_failed:
make_snapshot("FAILED") make_snapshot(_("FAILED"))
if dark_tries == valid_frames: if dark_tries == valid_frames:
print("All frames were too dark, please check dark_threshold in config") print(_("All frames were too dark, please check dark_threshold in config"))
print("Average darkness: " + str(dark_running_total / max(1, valid_frames)) + ", Threshold: " + str(dark_threshold)) print(_("Average darkness: {avg}, Threshold: {threshold}").format(avg=str(dark_running_total / max(1, valid_frames)), threshold=str(dark_threshold)))
sys.exit(13) sys.exit(13)
else: else:
sys.exit(11) sys.exit(11)
@ -255,32 +257,32 @@ while True:
print(" %s: %dms" % (label, round(timings[k] * 1000))) print(" %s: %dms" % (label, round(timings[k] * 1000)))
# Print a nice timing report # Print a nice timing report
print("Time spent") print(_("Time spent"))
print_timing("Starting up", "in") print_timing(_("Starting up"), "in")
print(" Open cam + load libs: %dms" % (round(max(timings["ll"], timings["ic"]) * 1000, ))) print(_(" Open cam + load libs: %dms") % (round(max(timings["ll"], timings["ic"]) * 1000, )))
print_timing(" Opening the camera", "ic") print_timing(_(" Opening the camera"), "ic")
print_timing(" Importing recognition libs", "ll") print_timing(_(" Importing recognition libs"), "ll")
print_timing("Searching for known face", "fl") print_timing(_("Searching for known face"), "fl")
print_timing("Total time", "tt") print_timing(_("Total time"), "tt")
print("\nResolution") print(_("\nResolution"))
width = video_capture.fw or 1 width = video_capture.fw or 1
print(" Native: %dx%d" % (height, width)) print(_(" Native: %dx%d") % (height, width))
# Save the new size for diagnostics # Save the new size for diagnostics
scale_height, scale_width = frame.shape[:2] scale_height, scale_width = frame.shape[:2]
print(" Used: %dx%d" % (scale_height, scale_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 # Show the total number of frames and calculate the FPS by deviding it by the total scan time
print("\nFrames searched: %d (%.2f fps)" % (frames, frames / timings["fl"])) print(_("\nFrames searched: %d (%.2f fps)") % (frames, frames / timings["fl"]))
print("Black frames ignored: %d " % (black_tries, )) print(_("Black frames ignored: %d ") % (black_tries, ))
print("Dark frames ignored: %d " % (dark_tries, )) print(_("Dark frames ignored: %d ") % (dark_tries, ))
print("Certainty of winning frame: %.3f" % (match * 10, )) print(_("Certainty of winning frame: %.3f") % (match * 10, ))
print("Winning model: %d (\"%s\")" % (match_index, models[match_index]["label"])) print(_("Winning model: %d (\"%s\")") % (match_index, models[match_index]["label"]))
# Make snapshot if enabled # Make snapshot if enabled
if capture_successful: if capture_successful:
make_snapshot("SUCCESSFUL") make_snapshot(_("SUCCESSFUL"))
# Run rubberstamps if enabled # Run rubberstamps if enabled
if config.getboolean("rubberstamps", "enabled", fallback=False): if config.getboolean("rubberstamps", "enabled", fallback=False):

12
src/i18n.py Normal file
View file

@ -0,0 +1,12 @@
# Support file for translations
# Import modules
import gettext
import os
# Get the right translation based on locale, falling back to base if none found
translation = gettext.translation("core", localedir=os.path.join(os.path.dirname(__file__), 'locales'), fallback=True)
translation.install()
# Export translation function as _
_ = translation.gettext

View file

@ -4,14 +4,16 @@
import numpy import numpy
import sys import sys
import re import re
from subprocess import Popen, PIPE 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 i18n import _
try: try:
import ffmpeg import ffmpeg
except ImportError: except ImportError:
print("Missing ffmpeg module, please run:") print(_("Missing ffmpeg module, please run:"))
print(" pip3 install ffmpeg-python\n") print(" pip3 install ffmpeg-python\n")
sys.exit(12) sys.exit(12)

View file

@ -1,16 +1,18 @@
# Class that simulates the functionality of opencv so howdy can use v4l2 devices seamlessly # Class that simulates the functionality of opencv so howdy can use v4l2 devices seamlessly
# Import required modules. lib4l-dev package is also required. # Import required modules. lib4l-dev package is also required.
from recorders import v4l2
import fcntl import fcntl
import numpy import numpy
import sys import sys
from recorders import v4l2
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
from i18n import _
try: try:
from pyv4l2.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")
sys.exit(13) sys.exit(13)

View file

@ -7,6 +7,7 @@ import cv2
import os import os
import sys import sys
from i18n import _
# Class to provide boilerplate code to build a video recorder with the # Class to provide boilerplate code to build a video recorder with the
# correct settings from the config file. # correct settings from the config file.
@ -33,8 +34,8 @@ class VideoCapture:
# Check device path # Check device path
if not os.path.exists(self.config.get("video", "device_path")): if not os.path.exists(self.config.get("video", "device_path")):
if self.config.getboolean("video", "warn_no_device"): if self.config.getboolean("video", "warn_no_device"):
print("Howdy could not find a camera device at the path specified in the config file.") print(_("Howdy could not find a camera device at the path specified in the config file."))
print("It is very likely that the path is not configured correctly, please edit the 'device_path' config value by running:") print(_("It is very likely that the path is not configured correctly, please edit the 'device_path' config value by running:"))
print("\n\tsudo howdy config\n") print("\n\tsudo howdy config\n")
sys.exit(1) sys.exit(1)
@ -81,7 +82,7 @@ class VideoCapture:
# Don't remove ret, it doesn't work without it # Don't remove ret, it doesn't work without it
ret, frame = self.internal.read() ret, frame = self.internal.read()
if not ret: if not ret:
print("Failed to read camera specified in your 'device_path', aborting") print(_("Failed to read camera specified in the 'device_path' config option, aborting"))
sys.exit(1) sys.exit(1)
try: try: