├── .gitignore ├── .vscode └── launch.json ├── README.md ├── docker ├── Dockerfile.cpu └── Dockerfile.gpu ├── keys └── id_rsa.pub ├── models └── haarcascade_frontalface_default.xml └── src ├── cpudetector.py ├── gpudetector.py ├── gpudetectorasync.py ├── loader ├── tfloader.py └── trtloader.py ├── multithreading ├── objectdetectasync.py └── videocaptureasync.py └── xtest.py /.gitignore: -------------------------------------------------------------------------------- 1 | /models/ 2 | **/__pycache__ -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Python: Current File", 9 | "type": "python", 10 | "request": "launch", 11 | "program": "${file}", 12 | "console": "integratedTerminal", 13 | "cwd": "${workspaceFolder}", 14 | "args": [ 15 | "--model-name=ssd_inception_v2_coco", 16 | "--camera-id=1", 17 | "--trt-optimize"], 18 | "env": 19 | { 20 | "DISPLAY": "10.135.62.79:0.0", 21 | } 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # High Throuput Object detector for the Nvidia Jetson device family 2 | 3 | This is the code used in [this blog post](http://havedatawilltrain.com/three-threads-to-perdido) to demonstrate different ways of improving the performance of a simple Object Detection program. 4 | 5 | ## Clone the example repo 6 | ``` 7 | https://github.com/paloukari/jetson-detectors 8 | cd jetson-detectors 9 | ``` 10 | 11 | ## To build and run the CPU accelerated container 12 | ``` 13 | sudo docker build . -f ./docker/Dockerfile.cpu -t object-detection-cpu 14 | sudo docker run --rm --runtime nvidia --privileged -ti -e DISPLAY=$DISPLAY -v "$PWD":/src -p 32001:22 object-detection-cpu 15 | ``` 16 | 17 | 18 | ## To build and run the GPU accelerated container 19 | ``` 20 | sudo docker build . -f ./docker/Dockerfile.gpu -t object-detection-gpu 21 | sudo docker run --rm --runtime nvidia --privileged -ti -e DISPLAY=$DISPLAY -v "$PWD":/src -p 32001:22 object-detection-gpu 22 | ``` 23 | 24 | > Run from the root folder of the repo 25 | -------------------------------------------------------------------------------- /docker/Dockerfile.cpu: -------------------------------------------------------------------------------- 1 | FROM nvcr.io/nvidia/l4t-base:r32.2 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | 5 | # Install Python3, Git and OpenCV 6 | RUN apt-get update && apt-get --yes install openssh-server python3-dev python3-pip python3-opencv git 7 | RUN pip3 install --upgrade pip 8 | 9 | RUN pip3 install click 10 | 11 | ENV LC_ALL C.UTF-8 12 | ENV LANG C.UTF-8 13 | 14 | # Set the WORKDIR 15 | WORKDIR /src 16 | 17 | ENTRYPOINT service ssh restart && bash 18 | 19 | # Install the ssh public key - Remove this in a production deployment 20 | COPY ./keys/id_rsa.pub /tmp/tmp.pub 21 | RUN mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat /tmp/tmp.pub >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys && rm -f /tmp/tmp.pub 22 | -------------------------------------------------------------------------------- /docker/Dockerfile.gpu: -------------------------------------------------------------------------------- 1 | FROM nvcr.io/nvidia/l4t-base:r32.2 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | 5 | # Install Python3, Git and OpenCV 6 | RUN apt-get update && apt-get --yes install openssh-server python3-dev python3-pip python3-opencv git 7 | RUN pip3 install --upgrade pip 8 | 9 | # Install system packages as required by TensorFlow 10 | RUN apt-get --yes install libhdf5-serial-dev hdf5-tools libhdf5-dev zlib1g-dev zip libjpeg8-dev pkg-config dh-autoreconf 11 | 12 | RUN pip3 install setuptools -U 13 | RUN pip3 install 'numpy<1.17,>=1.14.5' 14 | RUN pip3 install h5py 15 | 16 | # Install TensorFlow prebuilt wheel fron nvidia 17 | RUN pip3 install --pre --extra-index-url https://developer.download.nvidia.com/compute/redist/jp/v42 tensorflow-gpu 18 | 19 | # Install the cpp protobuf 20 | RUN set -e 21 | RUN mkdir -p /tmp/protoc 22 | WORKDIR /tmp/protoc 23 | RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v3.6.1/protobuf-python-3.6.1.zip 24 | RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v3.6.1/protoc-3.6.1-linux-aarch_64.zip 25 | 26 | RUN unzip protobuf-python-3.6.1.zip 27 | RUN unzip protoc-3.6.1-linux-aarch_64.zip -d protoc-3.6.1 28 | RUN cp protoc-3.6.1/bin/protoc /usr/local/bin/protoc 29 | RUN export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp 30 | WORKDIR protobuf-3.6.1/ 31 | 32 | RUN ./autogen.sh 33 | RUN ./configure --prefix=/usr/local 34 | RUN make 35 | RUN make check 36 | RUN make install 37 | RUN ldconfig 38 | 39 | RUN pip3 uninstall -y protobuf 40 | RUN pip3 install Cython 41 | WORKDIR python/ 42 | RUN sed -i '205s/if v:/if True:/' setup.py 43 | RUN python3 setup.py build --cpp_implementation 44 | RUN python3 setup.py test --cpp_implementation 45 | RUN python3 setup.py install --cpp_implementation 46 | 47 | # Install the dependencies for the nvidia tf_trt_models repo. This repo helps downloading the models dynamically. 48 | RUN sudo apt-get install --yes libfreetype6-dev 49 | RUN pip install matplotlib Pillow click 50 | 51 | WORKDIR / 52 | RUN git clone --recursive https://github.com/NVIDIA-Jetson/tf_trt_models.git 53 | WORKDIR /tf_trt_models 54 | RUN ./install.sh python3 55 | 56 | ENV LC_ALL C.UTF-8 57 | ENV LANG C.UTF-8 58 | 59 | # Set the WORKDIR 60 | WORKDIR /src 61 | 62 | ENTRYPOINT service ssh restart && bash 63 | 64 | # Install the ssh public key - Remove this in a production deployment 65 | COPY ./keys/id_rsa.pub /tmp/tmp.pub 66 | RUN mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat /tmp/tmp.pub >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys && rm -f /tmp/tmp.pub -------------------------------------------------------------------------------- /keys/id_rsa.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6lr8DHViUeyEqZHeRnrHt7gUqOdBQpBPj5+w1mpaAFzgpKoHfpuknfwCaxKmfs5NtsQUjMZPSA8CQ6mg8H0cXDu9WnlWaze8dJVOzdNPoXmvcV99HMbaDtXVXtACFFt/sgN6B/ByFpniDWaOhV8mRp3qbd/zg9kj1BQoYLRyYdTjJ7CECYvMO6/e0I/hPZuGBB4DCvqSesuH+Ufs1DSCLvRfd5m7wrWQxkY3ffAQAHnu+35UwBLeWf9XodCcF2A4N8EK7VcgB7AAJMC8fRHWrnYKJvVPc1gUruKSsleIHwTK+lmn0vTxutICmGAvj8Baf/185p/8spoJZzhivVD0v root@MININT-EGFEHCF -------------------------------------------------------------------------------- /src/cpudetector.py: -------------------------------------------------------------------------------- 1 | import cv2 as cv 2 | import os 3 | import time 4 | import click 5 | 6 | @click.command() 7 | @click.option('--model-name', default='haarcascade_frontalface_default.xml', 8 | help='The name of the pre-trained model to load. Download more from https://github.com/opencv/opencv/tree/master/data/haarcascades') 9 | @click.option('--camera-id', default=1, 10 | help='The id of the camera to use.. You can discover the connected cameras by runnimg: ls -ltrh /dev/video*.') 11 | @click.option('--trt-optimize', default=False, 12 | help='Setting this to True, the downloaded TF model will be converted to TensorRT model.', is_flag=True) 13 | def detector(model_name, camera_id, trt_optimize): 14 | 15 | detector_model = f'./models/{model_name}' 16 | classifier = cv.CascadeClassifier() 17 | if not classifier.load(detector_model): 18 | raise ValueError(f'Could not find {detector_model}') 19 | 20 | video_capture = cv.VideoCapture(camera_id) 21 | start_time = time.time() 22 | 23 | while(True): 24 | # Capture frame-by-frame 25 | video_capture_result, frame = video_capture.read() 26 | 27 | if video_capture_result == False: 28 | raise ValueError(f'Error reading the frame from camera {camera_id}') 29 | 30 | # face detection and other logic goes here 31 | faces = classifier.detectMultiScale(frame, 1.3, 5) 32 | 33 | for (x, y, w, h) in faces: 34 | # send each face in mqtt topic 35 | cv.rectangle(frame, (x, y), (x+w, y+h), color=(0, 255, 0), thickness=2) 36 | 37 | cv.putText(frame, f"FPS:{ 1.0 / (time.time() - start_time):0.1f}", 38 | (10, 30), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) 39 | start_time = time.time() 40 | 41 | cv.imshow('Input', frame) 42 | if cv.waitKey(1) == 27: 43 | break 44 | 45 | 46 | if __name__ == "__main__": 47 | detector() 48 | -------------------------------------------------------------------------------- /src/gpudetector.py: -------------------------------------------------------------------------------- 1 | import os 2 | os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 3 | 4 | from loader.trtloader import load_model as trt_loader 5 | from loader.tfloader import load_model as tf_loader 6 | import time 7 | import click 8 | 9 | import tensorflow as tf 10 | import cv2 as cv 11 | import numpy as np 12 | 13 | 14 | @click.command() 15 | @click.option('--model-name', default='ssd_inception_v2_coco', 16 | help='The name of the pre-trained TF model to load. See: https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md') 17 | @click.option('--camera-id', default=1, 18 | help='The id of the camera to use.. You can discover the connected cameras by runnimg: ls -ltrh /dev/video*.') 19 | @click.option('--trt-optimize', default=False, 20 | help='Setting this to True, the downloaded TF model will be converted to TensorRT model.', is_flag=True) 21 | def detector(model_name, camera_id, trt_optimize): 22 | 23 | trt_graph = None 24 | 25 | if trt_optimize: 26 | trt_graph = trt_loader(model_name) 27 | else: 28 | trt_graph = tf_loader(model_name) 29 | 30 | tf_config = tf.ConfigProto() 31 | tf_config.gpu_options.allow_growth = True 32 | tf_sess = tf.Session(config=tf_config) 33 | tf.import_graph_def(trt_graph, name='') 34 | 35 | tf_input = tf_sess.graph.get_tensor_by_name('image_tensor:0') 36 | tf_scores = tf_sess.graph.get_tensor_by_name('detection_scores:0') 37 | tf_boxes = tf_sess.graph.get_tensor_by_name('detection_boxes:0') 38 | tf_classes = tf_sess.graph.get_tensor_by_name('detection_classes:0') 39 | tf_num_detections = tf_sess.graph.get_tensor_by_name('num_detections:0') 40 | 41 | video_capture = cv.VideoCapture(camera_id) 42 | video_capture_result, frame = video_capture.read() 43 | camera_height, camera_width, channels = frame.shape 44 | 45 | start_time = time.time() 46 | 47 | while(video_capture_result): 48 | # Capture frame-by-frame 49 | video_capture_result, frame = video_capture.read() 50 | 51 | if video_capture_result == False: 52 | raise ValueError( 53 | f'Error reading the frame from camera {camera_id}') 54 | 55 | # face detection and other logic goes here 56 | image_resized = cv.resize(frame, (300, 300)) 57 | 58 | scores, boxes, classes, num_detections = tf_sess.run( 59 | [tf_scores, tf_boxes, tf_classes, tf_num_detections], 60 | feed_dict={tf_input: image_resized[None, ...]}) 61 | 62 | boxes = boxes[0] # index by 0 to remove batch dimension 63 | scores = scores[0] 64 | classes = classes[0] 65 | num_detections = num_detections[0] 66 | 67 | for i in range(int(num_detections)): 68 | box = boxes[i] * np.array([camera_height, 69 | camera_width, camera_height, camera_width]) 70 | box = box.astype(int) 71 | 72 | cv.rectangle(frame, (box[1], box[0]), (box[3], 73 | box[2]), color=(0, 255, 0), thickness=1) 74 | text = f"{scores[i]*100:.0f} | {str(int(classes[i]))}" 75 | cv.putText(frame, text, (box[3]+10, box[2]), 76 | cv.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2) 77 | 78 | cv.putText(frame, f"FPS:{ 1.0 / (time.time() - start_time):0.1f}", 79 | (10, 30), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) 80 | start_time = time.time() 81 | 82 | cv.imshow('Input', frame) 83 | if cv.waitKey(1) == 27: 84 | break 85 | 86 | cv.destroyAllWindows() 87 | 88 | 89 | if __name__ == "__main__": 90 | detector() 91 | -------------------------------------------------------------------------------- /src/gpudetectorasync.py: -------------------------------------------------------------------------------- 1 | import time 2 | import numpy as np 3 | import os 4 | os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 5 | import cv2 as cv 6 | import click 7 | import time 8 | 9 | from multithreading.videocaptureasync import VideoCaptureAsync 10 | from multithreading.objectdetectasync import ObjectDetectionAsync 11 | 12 | import threading 13 | 14 | 15 | @click.command() 16 | @click.option('--model-name', default='ssd_inception_v2_coco', 17 | help='The name of the pre-trained TF model to load. See: https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md') 18 | @click.option('--camera-id', default=1, 19 | help='The id of the camera to use.. You can discover the connected cameras by runnimg: ls -ltrh /dev/video*.') 20 | @click.option('--trt-optimize', default=False, 21 | help='Setting this to True, the downloaded TF model will be converted to TensorRT model.', is_flag=True) 22 | def detector(model_name, camera_id, trt_optimize): 23 | 24 | read_lock = threading.Lock() 25 | 26 | video_capture = VideoCaptureAsync(camera_id) 27 | video_capture.start() 28 | 29 | video_capture_result, frame, frame_resized = video_capture.read() 30 | if video_capture_result == False: 31 | raise ValueError(f'Error reading the frame from camera {camera_id}') 32 | camera_height, camera_width, channels = frame.shape 33 | 34 | detector_frame = None 35 | 36 | def frame_callback(): 37 | with read_lock: 38 | if detector_frame is not None and len(detector_frame) > 0: 39 | return detector_frame.copy() 40 | return None 41 | 42 | object_detection = ObjectDetectionAsync( 43 | model_name, trt_optimize, frame_callback) 44 | object_detection.start() 45 | start_time = time.time() 46 | 47 | while(video_capture_result): 48 | # Capture frame-by-frame 49 | video_capture_result, frame, frame_resized = video_capture.read() 50 | with read_lock: 51 | detector_frame = frame_resized 52 | 53 | if video_capture_result == False: 54 | print(f'Error reading the frame from camera {camera_id}') 55 | 56 | if object_detection.ready == True: 57 | scores, boxes, classes, num_detections = object_detection.read() 58 | if scores is not None and boxes is not None and classes is not None and num_detections is not None: 59 | scores=scores[0] 60 | boxes=boxes[0] 61 | classes=classes[0] 62 | num_detections=num_detections[0] 63 | 64 | for i in range(int(num_detections)): 65 | box = boxes[i] * np.array([camera_height, 66 | camera_width, camera_height, camera_width]) 67 | box = box.astype(int) 68 | 69 | cv.rectangle(frame, (box[1], box[0]), (box[3], 70 | box[2]), color=(0, 255, 0), thickness=1) 71 | text = f"{scores[i]*100:.0f} | {str(int(classes[i]))}" 72 | cv.putText( 73 | frame, text, (box[3]+10, box[2]), cv.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2) 74 | 75 | cv.putText(frame, f"FPS:{ 1.0 / (time.time() - start_time):0.1f}", 76 | (10, 30), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) 77 | start_time = time.time() 78 | else: 79 | cv.putText(frame, f"Loading detector"+int(time.time() % 4)*".", 80 | (10, 30), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) 81 | time.sleep(0.1) 82 | 83 | cv.imshow('Input', frame) 84 | if cv.waitKey(1) == 27: 85 | break 86 | 87 | cv.destroyAllWindows() 88 | video_capture.stop() 89 | object_detection.stop() 90 | 91 | 92 | if __name__ == "__main__": 93 | detector() 94 | -------------------------------------------------------------------------------- /src/loader/tfloader.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import tensorflow.contrib.tensorrt as trt 3 | 4 | sys.path.insert(1, '/') 5 | from tf_trt_models.detection import download_detection_model 6 | from tf_trt_models.detection import build_detection_graph 7 | 8 | def load_model(model_name): 9 | 10 | # Download and load the model 11 | config_path, checkpoint_path = download_detection_model( 12 | model_name, './models/') 13 | 14 | tr_graph, input_names, output_names = build_detection_graph( 15 | config=config_path, 16 | checkpoint=checkpoint_path 17 | ) 18 | 19 | print(f'Input names: {input_names}') 20 | print(f'Output names: {output_names}') 21 | 22 | return tr_graph -------------------------------------------------------------------------------- /src/loader/trtloader.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import os 3 | import cv2 as cv 4 | import tensorflow.contrib.tensorrt as trt 5 | import tensorflow as tf 6 | 7 | def load_model(model_name): 8 | 9 | trt_output_file = f'./models/{model_name}_trt.pb' 10 | 11 | trt_graph = tf.compat.v1.GraphDef() 12 | 13 | if os.path.exists(trt_output_file): 14 | print(f'Loading model {trt_output_file}...') 15 | with tf.io.gfile.GFile(trt_output_file, 'rb') as f: 16 | trt_graph.ParseFromString(f.read()) 17 | print(f'{trt_output_file} loaded.') 18 | else: 19 | # Lazy load these dependencies 20 | import sys 21 | sys.path.insert(1, '/') 22 | from tf_trt_models.detection import download_detection_model 23 | from tf_trt_models.detection import build_detection_graph 24 | 25 | config_path, checkpoint_path = download_detection_model( 26 | model_name, './models/') 27 | 28 | frozen_graph, input_names, output_names = build_detection_graph( 29 | config=config_path, 30 | checkpoint=checkpoint_path 31 | ) 32 | 33 | print(f'Converting {model_name} to trt..') 34 | trt_graph = trt.create_inference_graph( 35 | input_graph_def=frozen_graph, 36 | outputs=output_names, 37 | max_batch_size=1, 38 | max_workspace_size_bytes=1 << 25, 39 | precision_mode='FP16', 40 | minimum_segment_size=50 41 | ) 42 | with open(trt_output_file, 'wb') as f: 43 | f.write(trt_graph.SerializeToString()) 44 | print(f'{trt_output_file} saved.') 45 | 46 | return trt_graph -------------------------------------------------------------------------------- /src/multithreading/objectdetectasync.py: -------------------------------------------------------------------------------- 1 | import time 2 | import threading 3 | import cv2 4 | 5 | 6 | class ObjectDetectionAsync: 7 | def __init__(self, model_name, trt_optimize, frame_callback): 8 | self.started = False 9 | self.model_name = model_name 10 | self.trt_optimize = trt_optimize 11 | self.frame_callback = frame_callback 12 | 13 | self.scores = None 14 | self.boxes = None 15 | self.classes = None 16 | self.num_detections = None 17 | 18 | self._ready = False 19 | 20 | def start(self): 21 | if self.started: 22 | print('[!] Asynchronous detector already started.') 23 | return None 24 | self.started = True 25 | self.thread = threading.Thread(target=self.update, args=()) 26 | self.thread.start() 27 | return self 28 | 29 | def _create(self): 30 | import tensorflow as tf 31 | from loader.tfloader import load_model as tf_loader 32 | from loader.trtloader import load_model as trt_loader 33 | 34 | trt_graph = None 35 | if self.trt_optimize: 36 | trt_graph = trt_loader(self.model_name) 37 | else: 38 | trt_graph = tf_loader(self.model_name) 39 | 40 | tf_config = tf.ConfigProto() 41 | tf_config.gpu_options.allow_growth = True 42 | self.tf_sess = tf.Session(config=tf_config) 43 | tf.import_graph_def(trt_graph, name='') 44 | 45 | self.tf_input = self.tf_sess.graph.get_tensor_by_name('image_tensor:0') 46 | self.tf_scores = self.tf_sess.graph.get_tensor_by_name( 47 | 'detection_scores:0') 48 | self.tf_boxes = self.tf_sess.graph.get_tensor_by_name( 49 | 'detection_boxes:0') 50 | self.tf_classes = self.tf_sess.graph.get_tensor_by_name( 51 | 'detection_classes:0') 52 | self.tf_num_detections = self.tf_sess.graph.get_tensor_by_name( 53 | 'num_detections:0') 54 | 55 | self.frame = None 56 | # wait for the video feed to activate 57 | while self.frame is None: 58 | time.sleep(0.1) 59 | self.frame = self.frame_callback() 60 | 61 | # run once to load all cuda libs 62 | self.scores, self.boxes, self.classes, self.num_detections = self.tf_sess.run( 63 | [self.tf_scores, self.tf_boxes, self.tf_classes, self.tf_num_detections], feed_dict={self.tf_input: self.frame[None, ...]}) 64 | self._ready = True 65 | 66 | print("Detector ready") 67 | 68 | def update(self): 69 | self._create() 70 | while self._ready: 71 | self.frame = self.frame_callback() 72 | 73 | if self.frame is not None: 74 | try: 75 | self.scores, self.boxes, self.classes, self.num_detections = self.tf_sess.run( 76 | [self.tf_scores, self.tf_boxes, self.tf_classes, self.tf_num_detections], feed_dict={self.tf_input: self.frame[None, ...]}) 77 | except Exception as ex: 78 | print(ex) 79 | 80 | @property 81 | def ready(self): 82 | return self._ready 83 | 84 | def read(self): 85 | return self.scores, self.boxes, self.classes, self.num_detections 86 | 87 | def stop(self): 88 | self.started = False 89 | self.thread.join() 90 | 91 | def __exit__(self, exec_type, exc_value, traceback): 92 | self.cap.release() 93 | -------------------------------------------------------------------------------- /src/multithreading/videocaptureasync.py: -------------------------------------------------------------------------------- 1 | # from http://blog.blitzblit.com/2017/12/24/asynchronous-video-capture-in-python-with-opencv/ 2 | 3 | import threading 4 | import cv2 5 | 6 | 7 | class VideoCaptureAsync: 8 | def __init__(self, src=0, width=640, height=480, resize_width=300, resize_height=300): 9 | self.src = src 10 | self.cap = cv2.VideoCapture(self.src) 11 | self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) 12 | self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height) 13 | self.grabbed, self.frame = self.cap.read() 14 | self.frame_resized = [] 15 | self.started = False 16 | self.read_lock = threading.Lock() 17 | self.resize_width = resize_width 18 | self.resize_height = resize_height 19 | 20 | def set(self, var1, var2): 21 | self.cap.set(var1, var2) 22 | 23 | def start(self): 24 | if self.started: 25 | print('[!] Asynchronous video capturing has already been started.') 26 | return None 27 | self.started = True 28 | self.thread = threading.Thread(target=self.update, args=()) 29 | self.thread.start() 30 | return self 31 | 32 | def update(self): 33 | while self.started: 34 | grabbed, frame = self.cap.read() 35 | with self.read_lock: 36 | self.grabbed = grabbed 37 | self.frame = frame 38 | self.frame_resized = cv2.resize( 39 | frame, (self.resize_width, self.resize_height)) 40 | 41 | def read(self): 42 | with self.read_lock: 43 | frame = self.frame.copy() 44 | frame_resized = self.frame_resized.copy() 45 | grabbed = self.grabbed 46 | 47 | return grabbed, frame, frame_resized 48 | 49 | def test(self): 50 | with self.read_lock: 51 | frame = self.frame.copy() 52 | grabbed = self.grabbed 53 | return grabbed, frame 54 | 55 | def stop(self): 56 | self.started = False 57 | self.thread.join() 58 | 59 | def __exit__(self, exec_type, exc_value, traceback): 60 | self.cap.release() 61 | -------------------------------------------------------------------------------- /src/xtest.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | subprocess.run(["eog"]) --------------------------------------------------------------------------------