diff --git a/scripts/leap-view b/scripts/leap-view new file mode 100755 index 0000000..27b3e06 --- /dev/null +++ b/scripts/leap-view @@ -0,0 +1,80 @@ +#! /usr/bin/env python3 + +import argparse +import sys +import logging +import cv2 +import numpy as np + +logger = logging.getLogger(__name__) + + +def main() -> int: + # Handle program arguments + ap = argparse.ArgumentParser( + prog="leap-view", + description="View the camera feeds from a Leap Motion controller", + ) + ap.add_argument("device", help="Path to the video device") + ap.add_argument( + "-r", + "--resolution", + help="Resolution of the camera", + choices=["640x120", "640x240", "640x480", "752x120", "752x240", "752x480"], + default="640x480", + ) + ap.add_argument("--only", help="Only show the left or right camera", choices=["left", "right"]) + ap.add_argument( + "-v", "--verbose", help="Enable verbose logging", action="store_true" + ) + args = ap.parse_args() + + # Configure logging + logging.basicConfig( + level=logging.DEBUG if args.verbose else logging.INFO, + format="%(levelname)s: %(message)s", + ) + + # Open the video device + cap = cv2.VideoCapture(args.device) + + if not cap.isOpened(): + logger.error("Failed to open video device") + return 1 + + # Set the resolution + width, height = map(int, args.resolution.split("x")) + cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height) + cap.set(cv2.CAP_PROP_CONVERT_RGB, 0) + + # Read frames + while True: + ret, frame = cap.read() + + if not ret: + logger.error("Failed to read frame") + break + + # Reshape the frame + frame = np.reshape(frame, (height, width * 2)) + + # Split into left and right frames (every other byte) + left_frame = frame[:, 0::2] + right_frame = frame[:, 1::2] + + # Show the frame + if not args.only or args.only == "left": + cv2.imshow("Left", left_frame) + if not args.only or args.only == "right": + cv2.imshow("Right", right_frame) + + # Check if one of the windows was closed + if cv2.waitKey(1) & 0xFF == ord("q"): + break + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/leap-virtual-camera b/scripts/leap-virtual-camera new file mode 100755 index 0000000..81cba5a --- /dev/null +++ b/scripts/leap-virtual-camera @@ -0,0 +1,111 @@ +#! /usr/bin/env python3 + +import argparse +import sys +import logging +import cv2 +import pyvirtualcam +import subprocess +import numpy as np + +logger = logging.getLogger(__name__) + + +def main() -> int: + # Handle program arguments + ap = argparse.ArgumentParser( + prog="leap-view", + description="View the camera feeds from a Leap Motion controller", + ) + ap.add_argument("device", help="Path to the video device") + ap.add_argument( + "-r", + "--resolution", + help="Resolution of the camera", + choices=["640x120", "640x240", "640x480", "752x120", "752x240", "752x480"], + default="640x480", + ) + ap.add_argument( + "-v", "--verbose", help="Enable verbose logging", action="store_true" + ) + args = ap.parse_args() + + # Configure logging + logging.basicConfig( + level=logging.DEBUG if args.verbose else logging.INFO, + format="%(levelname)s: %(message)s", + ) + + # Open the video device + cap = cv2.VideoCapture(args.device) + + if not cap.isOpened(): + logger.error("Failed to open video device") + return 1 + + # Set the resolution + width, height = map(int, args.resolution.split("x")) + cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height) + cap.set(cv2.CAP_PROP_CONVERT_RGB, 0) + + # Call v4l2loopback to create two virtual cameras + subprocess.run( + [ + "sudo", + "modprobe", + "v4l2loopback", + "video_nr=31,32", + 'card_label="Leap Motion Left","Leap Motion Right"', + ], + check=True, + ) + + # Create left and right virtual cameras + virtual_cam_left = pyvirtualcam.Camera( + width=width, + height=height, + fps=30, + fmt=pyvirtualcam.PixelFormat.GRAY, + device="/dev/video31", + ) + virtual_cam_right = pyvirtualcam.Camera( + width=width, + height=height, + fps=30, + fmt=pyvirtualcam.PixelFormat.GRAY, + device="/dev/video32", + ) + + # Read frames + try: + while True: + ret, frame = cap.read() + + if not ret: + logger.error("Failed to read frame") + break + + # Reshape the frame + frame = np.reshape(frame, (height, width * 2)) + + # Split into left and right frames (every other byte) + left_frame = frame[:, 0::2] + right_frame = frame[:, 1::2] + + # Write the frames to the virtual cameras + virtual_cam_left.send(left_frame) + virtual_cam_right.send(right_frame) + + except KeyboardInterrupt: + pass + finally: + cap.release() + virtual_cam_left.close() + virtual_cam_right.close() + + return 0 + + +if __name__ == "__main__": + sys.exit(main())