Fix facial recognition

Scaling still seems to be imperfect
This commit is contained in:
slashtechno 2023-10-14 17:37:42 -05:00
parent b6948aded2
commit b2f10280b3
Signed by: slashtechno
GPG Key ID: 8EC1D9D9286C2B17
4 changed files with 92 additions and 40 deletions

2
.vscode/launch.json vendored
View File

@ -8,7 +8,7 @@
"name": "Python: Module", "name": "Python: Module",
"type": "python", "type": "python",
"request": "launch", "request": "launch",
"module": "set-detect-notify", "module": "set_detect_notify",
"justMyCode": true "justMyCode": true
} }
] ]

View File

@ -46,7 +46,8 @@ def main():
# Set it to the env RUN_SCALE if it isn't blank, otherwise set it to 0.25 # Set it to the env RUN_SCALE if it isn't blank, otherwise set it to 0.25
default=os.environ["RUN_SCALE"] default=os.environ["RUN_SCALE"]
if "RUN_SCALE" in os.environ and os.environ["RUN_SCALE"] != "" if "RUN_SCALE" in os.environ and os.environ["RUN_SCALE"] != ""
else 0.25, # noqa: E501 # else 0.25,
else 1,
type=float, type=float,
help="The scale to run the detection at, default is 0.25", help="The scale to run the detection at, default is 0.25",
) )
@ -55,7 +56,8 @@ def main():
# Set it to the env VIEW_SCALE if it isn't blank, otherwise set it to 0.75 # Set it to the env VIEW_SCALE if it isn't blank, otherwise set it to 0.75
default=os.environ["VIEW_SCALE"] default=os.environ["VIEW_SCALE"]
if "VIEW_SCALE" in os.environ and os.environ["VIEW_SCALE"] != "" if "VIEW_SCALE" in os.environ and os.environ["VIEW_SCALE"] != ""
else 0.75, # noqa: E501 # else 0.75,
else 1,
type=float, type=float,
help="The scale to view the detection at, default is 0.75", help="The scale to view the detection at, default is 0.75",
) )
@ -191,6 +193,26 @@ def main():
for i, r in enumerate(results): for i, r in enumerate(results):
# list of dicts with each dict containing a label, x1, y1, x2, y2 # list of dicts with each dict containing a label, x1, y1, x2, y2
plot_boxes = [] plot_boxes = []
# The following is stuff for people
# This is still in the for loop as each result, no matter if anything is detected, will be present.
# Thus, there will always be one result (r)
if face_details := utils.recognize_face(path_to_directory=Path(args.faces_directory), run_frame=run_frame):
plot_boxes.append( face_details )
objects_and_peoples=notify.thing_detected(
thing_name=face_details["label"],
objects_and_peoples=objects_and_peoples,
detection_type="peoples",
detection_window=args.detection_window,
detection_duration=args.detection_duration,
notification_window=args.notification_window,
ntfy_url=args.ntfy_url,
)
# The following is stuff for objects
# Setup dictionary of object names # Setup dictionary of object names
if objects_and_peoples["objects"] == {} or objects_and_peoples["objects"] is None: if objects_and_peoples["objects"] == {} or objects_and_peoples["objects"] is None:
for name in r.names.values(): for name in r.names.values():

View File

@ -44,42 +44,51 @@ def thing_detected(
respective_type = objects_and_peoples[detection_type] respective_type = objects_and_peoples[detection_type]
# (re)start cycle # (re)start cycle
if ( try:
# If the object has not been detected before
respective_type[thing_name]["last_detection_time"] is None
# If the last detection was more than 15 seconds ago
or time.time() - respective_type[thing_name]["last_detection_time"]
> detection_window
):
# Set the last detection time to now
respective_type[thing_name]["last_detection_time"] = time.time()
print(f"First detection of {thing_name} in this detection window")
# This line is important. It resets the detection duration when the object hasn't been detected for a while
# If detection duration is None, don't print anything.
# Otherwise, print that the detection duration is being reset due to inactivity
if respective_type[thing_name]["detection_duration"] is not None:
print(
f"Resetting detection duration for {thing_name} since it hasn't been detected for {detection_window} seconds" # noqa: E501
)
respective_type[thing_name]["detection_duration"] = 0
else:
# Check if the last notification was less than 15 seconds ago
# If it was, then don't do anything
if ( if (
time.time() - respective_type[thing_name]["last_detection_time"] # If the object has not been detected before
<= notification_window respective_type[thing_name]["last_detection_time"] is None
# If the last detection was more than 15 seconds ago
or time.time() - respective_type[thing_name]["last_detection_time"]
> detection_window
): ):
pass # Set the last detection time to now
# If it was more than 15 seconds ago, reset the detection duration respective_type[thing_name]["last_detection_time"] = time.time()
# This effectively resets the notification timer print(f"First detection of {thing_name} in this detection window")
else: # This line is important. It resets the detection duration when the object hasn't been detected for a while
print("Notification timer has expired - resetting") # If detection duration is None, don't print anything.
# Otherwise, print that the detection duration is being reset due to inactivity
if respective_type[thing_name]["detection_duration"] is not None:
print(
f"Resetting detection duration for {thing_name} since it hasn't been detected for {detection_window} seconds" # noqa: E501
)
respective_type[thing_name]["detection_duration"] = 0 respective_type[thing_name]["detection_duration"] = 0
respective_type[thing_name]["detection_duration"] += ( else:
time.time() - respective_type[thing_name]["last_detection_time"] # Check if the last NOTIFICATION was less than 15 seconds ago
) # If it was, then don't do anything
# print("Updating detection duration") if (
respective_type[thing_name]["last_detection_time"] = time.time() time.time() - respective_type[thing_name]["last_detection_time"]
<= notification_window
):
pass
# If it was more than 15 seconds ago, reset the detection duration
# This effectively resets the notification timer
else:
print("Notification timer has expired - resetting")
respective_type[thing_name]["detection_duration"] = 0
respective_type[thing_name]["detection_duration"] += (
time.time() - respective_type[thing_name]["last_detection_time"]
)
# print("Updating detection duration")
respective_type[thing_name]["last_detection_time"] = time.time()
except KeyError:
# If the object has not been detected before
respective_type[thing_name] = {
"last_detection_time": time.time(),
"detection_duration": 0,
"last_notification_time": None,
}
print(f"First detection of {thing_name} ever")
# (re)send notification # (re)send notification
# Check if detection has been ongoing for 2 seconds or more in the past 15 seconds # Check if detection has been ongoing for 2 seconds or more in the past 15 seconds

View File

@ -3,6 +3,8 @@ import numpy as np
from pathlib import Path from pathlib import Path
from deepface import DeepFace from deepface import DeepFace
first_face_try = True
def plot_label( def plot_label(
# list of dicts with each dict containing a label, x1, y1, x2, y2 # list of dicts with each dict containing a label, x1, y1, x2, y2
boxes: list = None, boxes: list = None,
@ -67,7 +69,7 @@ def recognize_face(
Accepts a path to a directory of images of faces to be used as a refference Accepts a path to a directory of images of faces to be used as a refference
In addition, accepts an opencv image to be used as the frame to be searched In addition, accepts an opencv image to be used as the frame to be searched
Returns a list of dictionaries, containing a single dictonary as currently only 1 face can be detected in each frame Returns a single dictonary as currently only 1 face can be detected in each frame
dict contains the following keys: label, x1, y1, x2, y2 dict contains the following keys: label, x1, y1, x2, y2
The directory should be structured as follows: The directory should be structured as follows:
faces/ faces/
@ -84,8 +86,23 @@ def recognize_face(
Point is, `name` is the name of the person in the images in the directory `name` Point is, `name` is the name of the person in the images in the directory `name`
That name will be used as the label for the face in the frame That name will be used as the label for the face in the frame
''' '''
global first_face_try
# If it's the first time the function is being run, remove representations_vgg_face.pkl, if it exists
if first_face_try:
try:
Path("representations_vgg_face.pkl").unlink()
print("Removing representations_vgg_face.pkl")
except FileNotFoundError:
pass
first_face_try = False
# face_dataframes is a vanilla list of dataframes # face_dataframes is a vanilla list of dataframes
face_dataframes = DeepFace.find(run_frame, db_path=str(path_to_directory)) try:
face_dataframes = DeepFace.find(run_frame, db_path=str(path_to_directory), enforce_detection=True, silent=True)
except ValueError as e:
if str(e) == "Face could not be detected. Please confirm that the picture is a face photo or consider to set enforce_detection param to False.":
return None
# Iteate over the dataframes # Iteate over the dataframes
for df in face_dataframes: for df in face_dataframes:
# The last row is the highest confidence # The last row is the highest confidence
@ -103,8 +120,12 @@ def recognize_face(
"x2": df.iloc[-1]["source_x"] + df.iloc[-1]["source_w"], "x2": df.iloc[-1]["source_x"] + df.iloc[-1]["source_w"],
"y2": df.iloc[-1]["source_y"] + df.iloc[-1]["source_h"], "y2": df.iloc[-1]["source_y"] + df.iloc[-1]["source_h"],
} }
distance = df.iloc[-1]["VGG-Face_cosine"]
return [dict(label=label, **coordinates)] # if 0.5 < distance < 0.7:
# label = "Unknown"
to_return = dict(label=label, **coordinates)
print(f'Confindence: {distance}, filname: {path_to_image.name}, to_return: {to_return}')
return to_return
''' '''
Example dataframe, for reference Example dataframe, for reference