From e79991965ed2ccd94ff4fa86414725f744663b0f Mon Sep 17 00:00:00 2001 From: jetsonhacks Date: Fri, 21 Jan 2022 19:56:36 -0800 Subject: [PATCH] Initial Commit --- .github/ISSUE_TEMPLATE/bug_report.md | 30 +++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 +++++ README.md | 96 ++++++++++++++++++++++- face-detect-usb.py | 80 +++++++++++++++++++ usb-camera-gst.py | 67 ++++++++++++++++ usb-camera-simple.py | 64 +++++++++++++++ 6 files changed, 355 insertions(+), 2 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100755 face-detect-usb.py create mode 100755 usb-camera-gst.py create mode 100755 usb-camera-simple.py diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..ed9b007 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,30 @@ +--- +name: Issue report +about: Create a report to help us improve +title: "[BUG]" +labels: bug +assignees: '' + +--- + +**Describe the issue** +Please describe the issue + +**What version of L4T/JetPack** +L4T/JetPack version: + +**What version of OpenCV** +OpenCV version: + +**Python Version** +Python version if applicable: + +**To Reproduce** +Steps to reproduce the behavior: +For example, what command line did you run? + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bfcb29d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[FEATURE REQUEST]" +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/README.md b/README.md index dfcd78b..eb64992 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,96 @@ # USB-Camera -Code example for running V4L2 USB Cameras on NVIDIA Jetson Developer Kits +Code examples for running V4L2 USB Cameras on NVIDIA Jetson Developer Kits -# WORK IN PROGRESS +Here are a few examples on how to interface a USB Camera with a Jetson Developer Kit through V4L2 and GStreamer. + +Typically "plug and play" USB cameras on Linux use the kernel module uvcvideo to interface with the Video 4 Linux subsystem (v4l2). Several specialty cameras such as the Stereolabs ZED camera and Intel RealSense cameras also use uvcvideo. + +In the Jetson Camera Architecture, you can use V4L2 or GStreamer (which runs on top of V4L2) to interface with such devices. OpenCV is used in these samples to demonstrate how to read the cameras. OpenCV works with multiple camera front ends, including V4L2 and GStreamer. OpenCV is included in the default Jetson software install, and supports V4L2 and GStreamer in the default configuration. + + There are other ways to interface through the same camera interfaces. For example, here are samples from NVIDIA on how to interface with V4L2 cameras using Input/Output Control (ioctl) + +``` +https://github.com/dusty-nv/jetson-utils +``` +Many people use OpenCV for the ease of use to open a camera, read frames, and then display the frames without having to worry bout configuring a video capture interface or GUI display interface. + +## Samples +The intent of the samples is to give a minimal sample script to be able to work with a USB camera. You will need to configure the scripts to meet your needs, specifially you will need to assign the correct video address, i.e. /dev/videoX must match your camera address, where X is a number. Make sure you read through the code. + +#### usb-camera-simple.py +usb-camera-simple.py uses the V4L2 backend for OpenCV to interface with the camera (cv2.CAP_V4L2). You are able to use the V4L2 camera properties to configure the camera, once created. Note that some properties are only available when integrated properly with OpenCV. For example, even though V4L2 has a fourcc description for H.264 video, the default OpenCV is not compiled with libx264. This means that the video will not be decoded correctly. + +To run: +``` +$ python3 usb-camera-simple.py +``` + +#### usb-camera-gst.py +usb-camera-gst.py uses the GStreamer backend for OpenCV to interface with the camera (cv2.CAP_GSTREAMER). You are *not* able to use the V4l2 camera properties when using the GStreamer backend. The sample has a H.264 pipeline described which is commented out. + +To run: +``` +$ python3 usb-camera-gst.py +``` +GStreamer is a very flexible framework, which in practice means there may be some challenges in configuring the pipelines for maximum performance. Also, different cameras may have different results on the same pipelines. However, much of this is covered online. + +Note that OpenCV requires a 'BGR' pixel output input. This is usually placed before the appsink element. + +#### face-detect-usb.py +face-detect-usb.py is an example of reading frames and manipulating them in OpenCV. The face and eye detection is performes using Haar cascades. The sample Haar cascades are in the default Jetson distribution. +To run: +``` +$ python3 face-detect-usb.py +``` + +### OpenCV +The samples use OpenCV to render frames. The standard OpenCV release on the Jetson currently uses GTK+ as its graphic output. Also, OpenCV is compiled here with support for GStreamer and V4L2 Camera input. If you encounter issues and do not have the standard OpenCV installed, check to make sure that these are loaded and selected. + +In Python3, to examine the options that were selected when OpenCV was built: +``` +>>> import cv2 +>>> print(cv2.getBuildInformation()) + +The GUI sections lists the graphics backend, the Video I/O sections lists the video front ends. + +### Tools +A valuable tool for working with V4L2 cameras is v4l2-ctl. To install: +``` +$ sudo apt install v4l-utils +``` +Useful commands: +#### Return a List of Cameras +``` +$ v4l2-ctl --list-devices +``` +#### Get Camera Pixel Formats +Get the list of supported pixel formats. /dev/videoX for camera address, e.g. +``` +$ v4l2-ctl --list-formats-ext -d /dev/video0 +``` + +#### Get All Information about a Camera +Get the list of supported pixel formats. /dev/videoX for camera address, e.g. +``` +$ v4l2-ctl --all -d /dev/video0 +``` +Lists driver, pixel formats, frame sizes, controls + + +### canberra-gtk-module +If you see the error: +``` +Failed to load module "canberra-gtk-module" +``` +Generally, this will not cause issues. However, you can remove the error with: +``` +$ sudo apt install libcanberra-gtk-module +``` + +## Release + +### January, 20200 +* Initial Release +* JetPack 4.6.1, L4T 32.6.1 +* Tested on Jetson Nano and Jetson Xavier NX +* Cameras tested: Logitech C920, Stereolabs ZED, Intel Realsense D435 \ No newline at end of file diff --git a/face-detect-usb.py b/face-detect-usb.py new file mode 100755 index 0000000..213c953 --- /dev/null +++ b/face-detect-usb.py @@ -0,0 +1,80 @@ +# MIT License +# Copyright (c) 2019 JetsonHacks +# See LICENSE for OpenCV license and additional information + +# https://docs.opencv.org/3.3.1/d7/d8b/tutorial_py_face_detection.html +# On the Jetson Nano, OpenCV comes preinstalled +# Data files are in /usr/sharc/OpenCV + +import cv2 + +window_title = "Face Detect" + +def face_detect(): + face_cascade = cv2.CascadeClassifier( + "/usr/share/opencv4/haarcascades/haarcascade_frontalface_default.xml" + ) + eye_cascade = cv2.CascadeClassifier( + "/usr/share/opencv4/haarcascades/haarcascade_eye.xml" + ) + # ASSIGN CAMERA ADDRESS HERE + camera_id="/dev/video0" + # Full list of Video Capture APIs (video backends): https://docs.opencv.org/3.4/d4/d15/group__videoio__flags__base.html + # For webcams, we use V4L2 + video_capture = cv2.VideoCapture(camera_id, cv2.CAP_V4L2) + """ + # How to set video capture properties using V4L2: + # Full list of Video Capture Properties for OpenCV: https://docs.opencv.org/3.4/d4/d15/group__videoio__flags__base.html + #Select Pixel Format: + # video_capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'YUYV')) + # Two common formats, MJPG and H264 + # video_capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG')) + # Default libopencv on the Jetson is not linked against libx264, so H.264 is not available + # video_capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'H264')) + # Select frame size, FPS: + video_capture.set(cv2.CAP_PROP_FRAME_WIDTH, 640) + video_capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) + video_capture.set(cv2.CAP_PROP_FPS, 30) + """ + + if video_capture.isOpened(): + try: + cv2.namedWindow(window_title, cv2.WINDOW_AUTOSIZE) + # Start counting the number of frames read and displayed + # left_camera.start_counting_fps() + while True: + _, frame = video_capture.read() + gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + faces = face_cascade.detectMultiScale(gray, 1.3, 5) + + for (x, y, w, h) in faces: + cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2) + roi_gray = gray[y : y + h, x : x + w] + roi_color = frame[y : y + h, x : x + w] + eyes = eye_cascade.detectMultiScale(roi_gray) + for (ex, ey, ew, eh) in eyes: + cv2.rectangle( + roi_color, (ex, ey), (ex + ew, ey + eh), (0, 255, 0), 2 + ) + + # Check to see if the user closed the window + # Under GTK+, WND_PROP_VISIBLE does not work correctly. Under Qt it does + # GTK - Substitute WND_PROP_AUTOSIZE to detect if window has been closed by user + if cv2.getWindowProperty(window_title, cv2.WND_PROP_AUTOSIZE) >= 0: + cv2.imshow(window_title, frame) + else: + break + + keyCode = cv2.waitKey(30) & 0xFF + # Stop the program on the ESC key or 'q' + if keyCode == 27 or keyCode == ord('q'): + break + finally: + video_capture.release() + cv2.destroyAllWindows() + else: + print("Unable to open camera") + + +if __name__ == "__main__": + face_detect() diff --git a/usb-camera-gst.py b/usb-camera-gst.py new file mode 100755 index 0000000..257b5ca --- /dev/null +++ b/usb-camera-gst.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# +# USB Camera - Simple +# +# Copyright (C) 2021-22 JetsonHacks (info@jetsonhacks.com) +# +# MIT License +# + +import sys + +import cv2 + +window_title = "USB Camera" + +# ASSIGN CAMERA ADRESS to DEVICE HERE! +pipeline = " ! ".join(["v4l2src device=/dev/video0", + "video/x-raw, width=640, height=480, framerate=30/1", + "videoconvert", + "video/x-raw, format=(string)BGR", + "appsink" + ]) + +# Sample pipeline for H.264 video, tested on Logitech C920 +h264_pipeline = " ! ".join(["v4l2src device=/dev/video0", + "video/x-h264, width=1280, height=720, framerate=30/1, format=H264", + "avdec_h264", + "videoconvert", + "video/x-raw, format=(string)BGR", + "appsink sync=false" + ]) + +def show_camera(): + + # Full list of Video Capture APIs (video backends): https://docs.opencv.org/3.4/d4/d15/group__videoio__flags__base.html + # For webcams, we use V4L2 + video_capture = cv2.VideoCapture(pipeline, cv2.CAP_GSTREAMER) + + if video_capture.isOpened(): + try: + window_handle = cv2.namedWindow( + window_title, cv2.WINDOW_AUTOSIZE) + # Window + while True: + ret_val, frame = video_capture.read() + # Check to see if the user closed the window + # Under GTK+ (Jetson Default), WND_PROP_VISIBLE does not work correctly. Under Qt it does + # GTK - Substitute WND_PROP_AUTOSIZE to detect if window has been closed by user + if cv2.getWindowProperty(window_title, cv2.WND_PROP_AUTOSIZE) >= 0: + cv2.imshow(window_title, frame) + else: + break + keyCode = cv2.waitKey(10) & 0xFF + # Stop the program on the ESC key or 'q' + if keyCode == 27 or keyCode == ord('q'): + break + + finally: + video_capture.release() + cv2.destroyAllWindows() + else: + print("Error: Unable to open camera") + + +if __name__ == "__main__": + + show_camera() diff --git a/usb-camera-simple.py b/usb-camera-simple.py new file mode 100755 index 0000000..44f1bf8 --- /dev/null +++ b/usb-camera-simple.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# +# USB Camera - Simple +# +# Copyright (C) 2021-22 JetsonHacks (info@jetsonhacks.com) +# +# MIT License +# + +import sys + +import cv2 + +window_title = "USB Camera" + +def show_camera(): + # ASSIGN CAMERA ADDRESS HERE + camera_id = "/dev/video0" + # Full list of Video Capture APIs (video backends): https://docs.opencv.org/3.4/d4/d15/group__videoio__flags__base.html + # For webcams, we use V4L2 + video_capture = cv2.VideoCapture(camera_id, cv2.CAP_V4L2) + """ + # How to set video capture properties using V4L2: + # Full list of Video Capture Properties for OpenCV: https://docs.opencv.org/3.4/d4/d15/group__videoio__flags__base.html + #Select Pixel Format: + # video_capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'YUYV')) + # Two common formats, MJPG and H264 + # video_capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG')) + # Default libopencv on the Jetson is not linked against libx264, so H.264 is not available + # video_capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'H264')) + # Select frame size, FPS: + video_capture.set(cv2.CAP_PROP_FRAME_WIDTH, 640) + video_capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) + video_capture.set(cv2.CAP_PROP_FPS, 30) + """ + if video_capture.isOpened(): + try: + window_handle = cv2.namedWindow( + window_title, cv2.WINDOW_AUTOSIZE ) + # Window + while True: + ret_val, frame = video_capture.read() + # Check to see if the user closed the window + # Under GTK+ (Jetson Default), WND_PROP_VISIBLE does not work correctly. Under Qt it does + # GTK - Substitute WND_PROP_AUTOSIZE to detect if window has been closed by user + if cv2.getWindowProperty(window_title, cv2.WND_PROP_AUTOSIZE) >= 0: + cv2.imshow(window_title, frame) + else: + break + keyCode = cv2.waitKey(10) & 0xFF + # Stop the program on the ESC key or 'q' + if keyCode == 27 or keyCode == ord('q'): + break + + finally: + video_capture.release() + cv2.destroyAllWindows() + else: + print("Unable to open camera") + + +if __name__ == "__main__": + + show_camera()