From b2f10280b30959990a4e72aa7a434e7b267d4840 Mon Sep 17 00:00:00 2001 From: slashtechno <77907286+slashtechno@users.noreply.github.com> Date: Sat, 14 Oct 2023 17:37:42 -0500 Subject: [PATCH] Fix facial recognition Scaling still seems to be imperfect --- .vscode/launch.json | 2 +- set_detect_notify/__main__.py | 26 ++++++++++- set_detect_notify/utils/notify.py | 75 +++++++++++++++++-------------- set_detect_notify/utils/utils.py | 29 ++++++++++-- 4 files changed, 92 insertions(+), 40 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 6fe5232..f843587 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "name": "Python: Module", "type": "python", "request": "launch", - "module": "set-detect-notify", + "module": "set_detect_notify", "justMyCode": true } ] diff --git a/set_detect_notify/__main__.py b/set_detect_notify/__main__.py index 549c5ce..f975af3 100644 --- a/set_detect_notify/__main__.py +++ b/set_detect_notify/__main__.py @@ -46,7 +46,8 @@ def main(): # Set it to the env RUN_SCALE if it isn't blank, otherwise set it to 0.25 default=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, 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 default=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, help="The scale to view the detection at, default is 0.75", ) @@ -191,6 +193,26 @@ def main(): for i, r in enumerate(results): # list of dicts with each dict containing a label, x1, y1, x2, y2 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 if objects_and_peoples["objects"] == {} or objects_and_peoples["objects"] is None: for name in r.names.values(): diff --git a/set_detect_notify/utils/notify.py b/set_detect_notify/utils/notify.py index f3a37aa..2d8756a 100644 --- a/set_detect_notify/utils/notify.py +++ b/set_detect_notify/utils/notify.py @@ -44,42 +44,51 @@ def thing_detected( respective_type = objects_and_peoples[detection_type] # (re)start cycle - if ( - # 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 + try: if ( - time.time() - respective_type[thing_name]["last_detection_time"] - <= notification_window + # 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 ): - 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") + # 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 - 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() + else: + # Check if the last NOTIFICATION was less than 15 seconds ago + # If it was, then don't do anything + if ( + 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 # Check if detection has been ongoing for 2 seconds or more in the past 15 seconds diff --git a/set_detect_notify/utils/utils.py b/set_detect_notify/utils/utils.py index 7f168c1..f08a223 100644 --- a/set_detect_notify/utils/utils.py +++ b/set_detect_notify/utils/utils.py @@ -3,6 +3,8 @@ import numpy as np from pathlib import Path from deepface import DeepFace +first_face_try = True + def plot_label( # list of dicts with each dict containing a label, x1, y1, x2, y2 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 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 The directory should be structured as follows: faces/ @@ -84,8 +86,23 @@ def recognize_face( 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 ''' + 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 = 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 for df in face_dataframes: # 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"], "y2": df.iloc[-1]["source_y"] + df.iloc[-1]["source_h"], } - - return [dict(label=label, **coordinates)] + distance = df.iloc[-1]["VGG-Face_cosine"] + # 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