diff --git a/.vscode/launch.json b/.vscode/launch.json index 5990161..6f0fe56 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,7 @@ "request": "launch", "module": "wyzely_detect", "args": [ - "--run-scale", "0.25", "--view-scale", "0.5", "--no-remove-representations" + "--run-scale", "0.25", "--view-scale", "0.5", "--no-remove-representations", "--fake-second-source" ], "justMyCode": true }, diff --git a/wyzely_detect/__main__.py b/wyzely_detect/__main__.py index dc02114..89005df 100644 --- a/wyzely_detect/__main__.py +++ b/wyzely_detect/__main__.py @@ -59,6 +59,17 @@ def main(): "devices": [cv2.VideoCapture(device) for device in args.capture_device], } + if args.fake_second_source: + try: + video_sources["devices"].append(video_sources["devices"][0]) + except KeyError: + print("No capture device to use as second source. Trying stream.") + try: + video_sources["devices"].append(video_sources["devices"][0]) + except KeyError: + print("No stream to use as a second source") + # When the code tries to resize the nonexistent capture device 1, the program will fail + # Eliminate lag by setting the buffer size to 1 # This makes it so that the video capture will only grab the most recent frame # However, this means that the video may be choppy @@ -74,6 +85,7 @@ def main(): pretty_table = PrettyTable(field_names=["Source Type", "Resolution"]) for source_type, sources in video_sources.items(): for source in sources: + # TODO: Add check to see if resolution is 0x0, and if it is, print a warning or just fail pretty_table.add_row( [source_type, f"{source.get(cv2.CAP_PROP_FRAME_WIDTH)}x{source.get(cv2.CAP_PROP_FRAME_HEIGHT)}"] ) @@ -83,31 +95,36 @@ def main(): print("Beginning video capture...") while True: # Grab a single frame of video - ret, frame = video_capture.read() + frames = [] + # frames = [source.read() for sources in video_sources.values() for source in sources] + for list_of_sources in video_sources.values(): + frames.extend([source.read()[1] for source in list_of_sources]) + frames_to_show = [] + for frame in frames: + frames_to_show.append(utils.process_footage( + frame = frame, + run_scale = args.run_scale, + view_scale = args.view_scale, - frame_to_show = utils.process_footage( - frame = frame, - run_scale = args.run_scale, - view_scale = args.view_scale, + faces_directory=Path(args.faces_directory), + face_confidence_threshold=args.face_confidence_threshold, + no_remove_representations=args.no_remove_representations, - faces_directory=Path(args.faces_directory), - face_confidence_threshold=args.face_confidence_threshold, - no_remove_representations=args.no_remove_representations, + detection_window=args.detection_window, + detection_duration=args.detection_duration, + notification_window=args.notification_window, - detection_window=args.detection_window, - detection_duration=args.detection_duration, - notification_window=args.notification_window, + ntfy_url=args.ntfy_url, - ntfy_url=args.ntfy_url, - - model=model, - detect_object=args.detect_object, - object_confidence_threshold=args.object_confidence_threshold, - ) + model=model, + detect_object=args.detect_object, + object_confidence_threshold=args.object_confidence_threshold, + )) # Display the resulting frame # TODO: When multi-camera support is added, this needs to be changed to allow all feeds if not args.no_display: - cv2.imshow("Video", frame_to_show) + for i, frame_to_show in enumerate(frames_to_show): + cv2.imshow(f"Video {i}", frame_to_show) # Hit 'q' on the keyboard to quit! if cv2.waitKey(1) & 0xFF == ord("q"): @@ -115,7 +132,7 @@ def main(): # Release handle to the webcam print("Releasing video capture") - video_capture.release() + [source.release() for sources in video_sources.values() for source in sources] cv2.destroyAllWindows() diff --git a/wyzely_detect/utils/cli_args.py b/wyzely_detect/utils/cli_args.py index 6337c76..2b9cfad 100644 --- a/wyzely_detect/utils/cli_args.py +++ b/wyzely_detect/utils/cli_args.py @@ -21,7 +21,7 @@ def set_argparse(): argparser = argparse.ArgumentParser( prog="Wyzely Detect", description="Recognize faces/objects in a video stream (from a webcam or a security camera) and send notifications to your devices", # noqa: E501 - epilog=":)", + epilog="For env bool options, setting them to anything except for an empty string will enable them." ) @@ -69,7 +69,9 @@ def set_argparse(): video_options.add_argument( "--no-display", default=os.environ["NO_DISPLAY"] - if "NO_DISPLAY" in os.environ and os.environ["NO_DISPLAY"] != "" + if "NO_DISPLAY" in os.environ + and os.environ["NO_DISPLAY"] != "" + and os.environ["NO_DISPLAY"].lower() != "false" else False, action="store_true", help="Don't display the video feed", @@ -78,7 +80,9 @@ def set_argparse(): '-c', '--force-disable-tensorflow-gpu', default=os.environ["FORCE_DISABLE_TENSORFLOW_GPU"] - if "FORCE_DISABLE_TENSORFLOW_GPU" in os.environ and os.environ["FORCE_DISABLE_TENSORFLOW_GPU"] != "" + if "FORCE_DISABLE_TENSORFLOW_GPU" in os.environ + and os.environ["FORCE_DISABLE_TENSORFLOW_GPU"] != "" + and os.environ["FORCE_DISABLE_TENSORFLOW_GPU"].lower() != "false" else False, action="store_true", help="Force disable tensorflow GPU through env since sometimes it's not worth it to install cudnn and whatnot", @@ -146,6 +150,7 @@ def set_argparse(): default=os.environ["NO_REMOVE_REPRESENTATIONS"] if "NO_REMOVE_REPRESENTATIONS" in os.environ and os.environ["NO_REMOVE_REPRESENTATIONS"] != "" + and os.environ["NO_REMOVE_REPRESENTATIONS"].lower() != "false" else False, action="store_true", help="Don't remove representations_.pkl at the start of the program. Greatly improves startup time, but doesn't take into account changes to the faces directory since it was created", # noqa: E501 @@ -166,12 +171,27 @@ def set_argparse(): "--object-confidence-threshold", default=os.environ["OBJECT_CONFIDENCE_THRESHOLD"] if "OBJECT_CONFIDENCE_THRESHOLD" in os.environ - and os.environ["OBJECT_CONFIDENCE_THRESHOLD"] != "" + and os.environ["OBJECT_CONFIDENCE_THRESHOLD"] != "" + # I think this should always be a str so using lower shouldn't be a problem. + # Also, if the first check fails the rest shouldn't be run + and os.environ["OBJECT_CONFIDENCE_THRESHOLD"].lower() != "false" else 0.6, type=float, help="The confidence threshold to use", ) + debug = argparser.add_argument_group("Debug options") + debug.add_argument( + "--fake-second-source", + help="Duplicate the first source and use it as a second source. Capture device takes priority.", + action="store_true", + default=os.environ["FAKE_SECOND_SOURCE"] + if "FAKE_SECOND_SOURCE" in os.environ + and os.environ["FAKE_SECOND_SOURCE"] != "" + and os.environ["FAKE_SECOND_SOURCE"].lower() != "false" + else False, + ) + # return argparser