mirror of
https://github.com/boltgolt/howdy.git
synced 2024-09-19 09:51:19 +02:00
Added the option to save snapshots
This commit is contained in:
parent
6e9169e87c
commit
51e17420d7
5 changed files with 115 additions and 7 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -103,6 +103,9 @@ ENV/
|
||||||
# generated models
|
# generated models
|
||||||
/src/models
|
/src/models
|
||||||
|
|
||||||
|
# snapshots
|
||||||
|
/src/snapshots
|
||||||
|
|
||||||
# build files
|
# build files
|
||||||
debian/howdy.substvars
|
debian/howdy.substvars
|
||||||
debian/files
|
debian/files
|
||||||
|
|
|
@ -16,6 +16,8 @@ import json
|
||||||
import configparser
|
import configparser
|
||||||
import cv2
|
import cv2
|
||||||
import dlib
|
import dlib
|
||||||
|
import datetime
|
||||||
|
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
|
||||||
|
@ -48,6 +50,18 @@ def init_detector(lock):
|
||||||
lock.release()
|
lock.release()
|
||||||
|
|
||||||
|
|
||||||
|
def make_snapshot(type):
|
||||||
|
"""Generate snapshot after detection"""
|
||||||
|
snapshot.generate(snapframes, [
|
||||||
|
type + " LOGIN",
|
||||||
|
"Date: " + datetime.datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S UTC"),
|
||||||
|
"Scan time: " + str(round(time.time() - timings["fr"], 2)) + "s",
|
||||||
|
"Frames: " + str(frames) + " (" + str(round(frames / (time.time() - timings["fr"]), 2)) + "FPS)",
|
||||||
|
"Hostname: " + os.uname().nodename,
|
||||||
|
"Best certainty value: " + str(round(lowest_certainty * 10, 1))
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
# Make sure we were given an username to tast against
|
# Make sure we were given an username to tast against
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
sys.exit(12)
|
sys.exit(12)
|
||||||
|
@ -67,7 +81,11 @@ black_tries = 0
|
||||||
dark_tries = 0
|
dark_tries = 0
|
||||||
# Total amount of frames captured
|
# Total amount of frames captured
|
||||||
frames = 0
|
frames = 0
|
||||||
# face recognition/detection instances
|
# Captured frames for snapshot capture
|
||||||
|
snapframes = []
|
||||||
|
# Tracks the lowest certainty value in the loop
|
||||||
|
lowest_certainty = 10
|
||||||
|
# Face recognition/detection instances
|
||||||
face_detector = None
|
face_detector = None
|
||||||
pose_predictor = None
|
pose_predictor = None
|
||||||
face_encoder = None
|
face_encoder = None
|
||||||
|
@ -95,6 +113,8 @@ timeout = config.getint("video", "timeout", fallback=5)
|
||||||
dark_threshold = config.getfloat("video", "dark_threshold", fallback=50.0)
|
dark_threshold = config.getfloat("video", "dark_threshold", fallback=50.0)
|
||||||
video_certainty = config.getfloat("video", "certainty", fallback=3.5) / 10
|
video_certainty = config.getfloat("video", "certainty", fallback=3.5) / 10
|
||||||
end_report = config.getboolean("debug", "end_report", fallback=False)
|
end_report = config.getboolean("debug", "end_report", fallback=False)
|
||||||
|
capture_failed = config.getboolean("snapshots", "capture_failed", fallback=False)
|
||||||
|
capture_successful = config.getboolean("snapshots", "capture_successful", fallback=False)
|
||||||
|
|
||||||
# Save the time needed to start the script
|
# Save the time needed to start the script
|
||||||
timings["in"] = time.time() - timings["st"]
|
timings["in"] = time.time() - timings["st"]
|
||||||
|
@ -150,7 +170,11 @@ while True:
|
||||||
|
|
||||||
# Stop if we've exceded the time limit
|
# Stop if we've exceded the time limit
|
||||||
if time.time() - timings["fr"] > timeout:
|
if time.time() - timings["fr"] > timeout:
|
||||||
if (dark_tries == valid_frames):
|
# Create a timeout snapshot if enabled
|
||||||
|
if capture_failed:
|
||||||
|
make_snapshot("FAILED")
|
||||||
|
|
||||||
|
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 / valid_frames) + ", Threshold: " + str(dark_threshold))
|
print("Average darkness: " + str(dark_running_total / valid_frames) + ", Threshold: " + str(dark_threshold))
|
||||||
sys.exit(13)
|
sys.exit(13)
|
||||||
|
@ -159,9 +183,14 @@ while True:
|
||||||
|
|
||||||
# Grab a single frame of video
|
# Grab a single frame of video
|
||||||
frame, gsframe = video_capture.read_frame()
|
frame, gsframe = video_capture.read_frame()
|
||||||
|
|
||||||
gsframe = clahe.apply(gsframe)
|
gsframe = clahe.apply(gsframe)
|
||||||
|
|
||||||
|
# If snapshots have been turned on
|
||||||
|
if capture_failed or capture_successful:
|
||||||
|
# Start capturing frames for the snapshot
|
||||||
|
if len(snapframes) < 3:
|
||||||
|
snapframes.append(frame)
|
||||||
|
|
||||||
# Create a histogram of the image with 8 values
|
# Create a histogram of the image with 8 values
|
||||||
hist = cv2.calcHist([gsframe], [0], None, [8], [0, 256])
|
hist = cv2.calcHist([gsframe], [0], None, [8], [0, 256])
|
||||||
# All values combined for percentage calculation
|
# All values combined for percentage calculation
|
||||||
|
@ -210,10 +239,14 @@ while True:
|
||||||
match_index = np.argmin(matches)
|
match_index = np.argmin(matches)
|
||||||
match = matches[match_index]
|
match = matches[match_index]
|
||||||
|
|
||||||
|
# Update certainty if we have a new low
|
||||||
|
if lowest_certainty > match:
|
||||||
|
lowest_certainty = match
|
||||||
|
|
||||||
# Check if a match that's confident enough
|
# Check if a match that's confident enough
|
||||||
if 0 < match < video_certainty:
|
if 0 < match < video_certainty:
|
||||||
timings["tt"] = time.time() - timings["st"]
|
timings["tt"] = time.time() - timings["st"]
|
||||||
timings["fr"] = time.time() - timings["fr"]
|
timings["fl"] = time.time() - timings["fr"]
|
||||||
|
|
||||||
# If set to true in the config, print debug text
|
# If set to true in the config, print debug text
|
||||||
if end_report:
|
if end_report:
|
||||||
|
@ -227,7 +260,7 @@ while True:
|
||||||
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", "fr")
|
print_timing("Searching for known face", "fl")
|
||||||
print_timing("Total time", "tt")
|
print_timing("Total time", "tt")
|
||||||
|
|
||||||
print("\nResolution")
|
print("\nResolution")
|
||||||
|
@ -238,13 +271,17 @@ while True:
|
||||||
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["fr"]))
|
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
|
||||||
|
if capture_successful:
|
||||||
|
make_snapshot("SUCCESSFUL")
|
||||||
|
|
||||||
# End peacefully
|
# End peacefully
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ use_cnn = false
|
||||||
[video]
|
[video]
|
||||||
# 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
|
||||||
|
# Lower is better
|
||||||
certainty = 3.5
|
certainty = 3.5
|
||||||
|
|
||||||
# The number of seconds to search before timing out
|
# The number of seconds to search before timing out
|
||||||
|
@ -37,7 +38,7 @@ 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 an installer if your distro has one
|
# Should be set automatically by an installer if your distro has one
|
||||||
device_path = "/dev/video1"
|
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
|
||||||
|
@ -73,6 +74,14 @@ force_mjpeg = false
|
||||||
# OPENCV only.
|
# OPENCV only.
|
||||||
exposure = -1
|
exposure = -1
|
||||||
|
|
||||||
|
[snapshots]
|
||||||
|
# Capture snapshots of failed login attempts and save them to disk with metadata
|
||||||
|
# Snapshots are saved to the "snapshots" folder
|
||||||
|
capture_failed = true
|
||||||
|
|
||||||
|
# Do the same as the option above but for successful attemtps
|
||||||
|
capture_successful = true
|
||||||
|
|
||||||
[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
|
# Enabling this can cause some UI apps to fail, only enable it to debug
|
||||||
|
|
BIN
src/logo.png
Normal file
BIN
src/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3 KiB |
59
src/snapshot.py
Normal file
59
src/snapshot.py
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
# Create and save snapshots of auth attemtps
|
||||||
|
|
||||||
|
# Import modules
|
||||||
|
import cv2
|
||||||
|
import os
|
||||||
|
import datetime
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
def generate(frames, text_lines):
|
||||||
|
"""Generate a shapshot from given frames"""
|
||||||
|
|
||||||
|
# Don't execute if no frames were given
|
||||||
|
if len(frames) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get the path to the containing folder
|
||||||
|
abpath = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
# Get frame dimentions
|
||||||
|
frame_height, frame_width, cc = frames[0].shape
|
||||||
|
# Spread the given frames out horizontally
|
||||||
|
snap = np.concatenate(frames, axis=1)
|
||||||
|
|
||||||
|
# Create colors
|
||||||
|
pad_color = [44, 44, 44]
|
||||||
|
text_color = [255, 255, 255]
|
||||||
|
|
||||||
|
# Add a gray square at the bottom of the image
|
||||||
|
snap = cv2.copyMakeBorder(snap, 0, len(text_lines) * 20 + 40, 0, 0, cv2.BORDER_CONSTANT, value=pad_color)
|
||||||
|
|
||||||
|
# Add the Howdy logo if there's space to do so
|
||||||
|
if len(frames) > 1:
|
||||||
|
# Load the logo from file
|
||||||
|
logo = cv2.imread(abpath + "/logo.png")
|
||||||
|
# Calculate the position of the logo
|
||||||
|
logo_y = frame_height + 20
|
||||||
|
logo_x = frame_width * len(frames) - 210
|
||||||
|
|
||||||
|
# Overlay the logo on top of the image
|
||||||
|
snap[logo_y:logo_y+57, logo_x:logo_x+180] = logo
|
||||||
|
|
||||||
|
# Go through each line
|
||||||
|
line_number = 0
|
||||||
|
for line in text_lines:
|
||||||
|
# Calculate how far the line should be from the top
|
||||||
|
padding_top = frame_height + 30 + (line_number * 20)
|
||||||
|
# Print the line onto the image
|
||||||
|
cv2.putText(snap, line, (30, padding_top), cv2.FONT_HERSHEY_SIMPLEX, .4, text_color, 0, cv2.LINE_AA)
|
||||||
|
|
||||||
|
line_number += 1
|
||||||
|
|
||||||
|
# Made sure a snapshot folder exist
|
||||||
|
if not os.path.exists(abpath + "/snapshots"):
|
||||||
|
os.makedirs(abpath + "/snapshots")
|
||||||
|
|
||||||
|
# Generate a filename based on the current time
|
||||||
|
filename = datetime.datetime.utcnow().strftime("%Y%m%dT%H%M%S.jpg")
|
||||||
|
# Write the image to that file
|
||||||
|
cv2.imwrite(abpath + "/snapshots/" + filename, snap)
|
Loading…
Reference in a new issue