howdy/src/cli/add.py
Tim Welch 6a73634ac2 Adding support for v4l2 devices directly in python
v4l2l.py is included in src dir. The version you get from pip install does not work with python3 (at least not on my system), so I had to modify a bunch of the functions returning a list() of range() instead of the original range().
2018-12-23 12:08:18 -05:00

195 lines
5.5 KiB
Python

# Save the face of the user in encoded form
# Import required modules
import time
import os
import sys
import json
import configparser
import builtins
import cv2
import numpy as np
# Try to import dlib and give a nice error if we can't
# Add should be the first point where import issues show up
try:
import dlib
except ImportError as err:
print(err)
print("\nCan't import the dlib module, check the output of")
print("pip3 show dlib")
sys.exit(1)
# Get the absolute path to the current directory
path = os.path.abspath(__file__ + '/..')
# Read config from disk
config = configparser.ConfigParser()
config.read(path + "/../config.ini")
use_cnn = config.getboolean('core', 'use_cnn', fallback=False)
if use_cnn:
face_detector = dlib.cnn_face_detection_model_v1(
path + '/../dlib-data/mmod_human_face_detector.dat'
)
else:
face_detector = dlib.get_frontal_face_detector()
pose_predictor = dlib.shape_predictor(
path + '/../dlib-data/shape_predictor_5_face_landmarks.dat'
)
face_encoder = dlib.face_recognition_model_v1(
path + '/../dlib-data/dlib_face_recognition_resnet_model_v1.dat'
)
user = builtins.howdy_user
# The permanent file to store the encoded model in
enc_file = path + "/../models/" + user + ".dat"
# Known encodings
encodings = []
# Make the ./models folder if it doesn't already exist
if not os.path.exists(path + "/../models"):
print("No face model folder found, creating one")
os.makedirs(path + "/../models")
# To try read a premade encodings file if it exists
try:
encodings = json.load(open(enc_file))
except FileNotFoundError:
encodings = []
# Print a warning if too many encodings are being added
if len(encodings) > 3:
print("NOTICE: Each additional model slows down the face recognition engine slightly")
print("Press Ctrl+C to cancel\n")
print("Adding face model for the user " + user)
# Set the default label
label = "Initial model"
# If models already exist, set that default label
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 "%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 + "] (max 24 characters): ")
# Set the custom label (if any) and limit it to 24 characters
if label_in != "":
label = label_in[:24]
# Prepare the metadata for insertion
insert_model = {
"time": int(time.time()),
"label": label,
"id": len(encodings),
"data": []
}
# Check if the user explicitly set ffmpeg as recorder
if config.get("video", "recording_plugin") == "ffmpeg":
# Set the capture source for ffmpeg
from ffmpeg_reader import ffmpeg_reader
video_capture = ffmpeg_reader(config.get("video", "device_path"), config.get("video", "device_format"))
elif config.get("video", "recording_plugin") == "pyv4l2":
# Set the capture source for pyv4l2
from pyv4l2_reader import pyv4l2_reader
video_capture = pyv4l2_reader(config.get("video", "device_path"), config.get("video", "device_format"))
else:
# Start video capture on the IR camera through OpenCV
video_capture = cv2.VideoCapture(config.get("video", "device_path"))
# Force MJPEG decoding if true
if config.getboolean("video", "force_mjpeg", fallback=False):
# 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
fw = config.getint("video", "frame_width", fallback=-1)
fh = config.getint("video", "frame_height", fallback=-1)
if fw != -1:
video_capture.set(cv2.CAP_PROP_FRAME_WIDTH, fw)
if fh != -1:
video_capture.set(cv2.CAP_PROP_FRAME_HEIGHT, fh)
# Request a frame to wake the camera up
video_capture.grab()
print("\nPlease look straight into the camera")
# Give the user time to read
time.sleep(2)
# Will contain found face encodings
enc = []
# Count the amount or read frames
frames = 0
dark_threshold = config.getfloat("video", "dark_threshold")
# Loop through frames till we hit a timeout
while frames < 60:
# Grab a single frame of video
# Don't remove ret, it doesn't work without it
ret, frame = video_capture.read()
gsframe = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Create a histogram of the image with 8 values
hist = cv2.calcHist([gsframe], [0], None, [8], [0, 256])
# All values combined for percentage calculation
hist_total = np.sum(hist)
# If the image is fully black or the frame exceeds threshold,
# skip to the next frame
if hist_total == 0 or (hist[0] / hist_total * 100 > dark_threshold):
continue
frames += 1
# Get all faces from that frame as encodings
face_locations = face_detector(gsframe, 1) # upsample 1 time
# If we've found at least one, we can continue
if face_locations:
break
video_capture.release()
# If more than 1 faces are detected we can't know wich one belongs to the user
if len(face_locations) > 1:
print("Multiple faces detected, aborting")
sys.exit(1)
elif not face_locations:
print("No face detected, aborting")
sys.exit(1)
face_location = face_locations[0]
if use_cnn:
face_location = face_location.rect
# Get the encodings in the frame
face_landmark = pose_predictor(frame, face_location)
face_encoding = np.array(
face_encoder.compute_face_descriptor(frame, face_landmark, 1) # num_jitters=1
)
insert_model["data"].append(face_encoding.tolist())
# Insert full object into the list
encodings.append(insert_model)
# Save the new encodings to disk
with open(enc_file, "w") as datafile:
json.dump(encodings, datafile)
# Give let the user know how it went
print("""Scan complete
Added a new model to """ + user)