Compare commits

..
1 Commits
Author SHA1 Message Date
blakeblackshear 228d5ed9c1 working odroid build, still needs hwaccel 2019-05-27 10:17:57 -05:00
13 changed files with 115 additions and 289 deletions
+1 -6
View File
@@ -1,6 +1 @@
README.md README.md
diagram.png
.gitignore
debug
config/
*.pyc
-1
View File
@@ -1 +0,0 @@
ko_fi: blakeblackshear
+9 -21
View File
@@ -1,7 +1,5 @@
FROM ubuntu:18.04 FROM ubuntu:18.04
ARG DEVICE
# Install packages for apt repo # Install packages for apt repo
RUN apt-get -qq update && apt-get -qq install --no-install-recommends -y \ RUN apt-get -qq update && apt-get -qq install --no-install-recommends -y \
apt-transport-https \ apt-transport-https \
@@ -10,14 +8,11 @@ RUN apt-get -qq update && apt-get -qq install --no-install-recommends -y \
wget \ wget \
gnupg-agent \ gnupg-agent \
dirmngr \ dirmngr \
software-properties-common \ software-properties-common
&& rm -rf /var/lib/apt/lists/*
COPY scripts/install_odroid_repo.sh . RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys D986B59D
RUN if [ "$DEVICE" = "odroid" ]; then \ RUN echo "deb http://deb.odroid.in/5422-s bionic main" > /etc/apt/sources.list.d/odroid.list
sh /install_odroid_repo.sh; \
fi
RUN apt-get -qq update && apt-get -qq install --no-install-recommends -y \ RUN apt-get -qq update && apt-get -qq install --no-install-recommends -y \
python3 \ python3 \
@@ -49,8 +44,6 @@ RUN apt-get -qq update && apt-get -qq install --no-install-recommends -y \
libc++abi1 \ libc++abi1 \
libunwind8 \ libunwind8 \
libgcc1 \ libgcc1 \
# VAAPI drivers for Intel hardware accel
libva-drm2 libva2 i965-va-driver vainfo \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# Install core packages # Install core packages
@@ -62,9 +55,6 @@ RUN pip install -U pip \
PyYAML PyYAML
# Download & build OpenCV # Download & build OpenCV
# TODO: use multistage build to reduce image size:
# https://medium.com/@denismakogon/pain-and-gain-running-opencv-application-with-golang-and-docker-on-alpine-3-7-435aa11c7aec
# https://www.merixstudio.com/blog/docker-multi-stage-builds-python-development/
RUN wget -q -P /usr/local/src/ --no-check-certificate https://github.com/opencv/opencv/archive/4.0.1.zip RUN wget -q -P /usr/local/src/ --no-check-certificate https://github.com/opencv/opencv/archive/4.0.1.zip
RUN cd /usr/local/src/ \ RUN cd /usr/local/src/ \
&& unzip 4.0.1.zip \ && unzip 4.0.1.zip \
@@ -79,15 +69,14 @@ RUN cd /usr/local/src/ \
&& rm -rf /usr/local/src/opencv-4.0.1 && rm -rf /usr/local/src/opencv-4.0.1
# Download and install EdgeTPU libraries for Coral # Download and install EdgeTPU libraries for Coral
RUN wget https://dl.google.com/coral/edgetpu_api/edgetpu_api_latest.tar.gz -O edgetpu_api.tar.gz --trust-server-names \ RUN wget https://dl.google.com/coral/edgetpu_api/edgetpu_api_latest.tar.gz -O edgetpu_api.tar.gz --trust-server-names
&& tar xzf edgetpu_api.tar.gz
COPY scripts/install_edgetpu_api.sh edgetpu_api/install.sh RUN tar xzf edgetpu_api.tar.gz \
&& cd edgetpu_api \
&& cp -p libedgetpu/libedgetpu_arm32.so /usr/lib/arm-linux-gnueabihf/libedgetpu.so.1.0 \
&& ldconfig \
&& python3 -m pip install --no-deps "$(ls edgetpu-*-py3-none-any.whl 2>/dev/null)"
RUN cd edgetpu_api \
&& /bin/bash install.sh
# Copy a python 3.6 version
RUN cd /usr/local/lib/python3.6/dist-packages/edgetpu/swig/ \ RUN cd /usr/local/lib/python3.6/dist-packages/edgetpu/swig/ \
&& ln -s _edgetpu_cpp_wrapper.cpython-35m-arm-linux-gnueabihf.so _edgetpu_cpp_wrapper.cpython-36m-arm-linux-gnueabihf.so && ln -s _edgetpu_cpp_wrapper.cpython-35m-arm-linux-gnueabihf.so _edgetpu_cpp_wrapper.cpython-36m-arm-linux-gnueabihf.so
@@ -104,6 +93,5 @@ RUN (apt-get autoremove -y; \
WORKDIR /opt/frigate/ WORKDIR /opt/frigate/
ADD frigate frigate/ ADD frigate frigate/
COPY detect_objects.py . COPY detect_objects.py .
COPY benchmark.py .
CMD ["python3", "-u", "detect_objects.py"] CMD ["python3", "-u", "detect_objects.py"]
-2
View File
@@ -1,5 +1,3 @@
<a href='https://ko-fi.com/P5P7XGO9' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://az743702.vo.msecnd.net/cdn/kofi4.png?v=2' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>
# Frigate - Realtime Object Detection for RTSP Cameras # Frigate - Realtime Object Detection for RTSP Cameras
**Note:** This version requires the use of a [Google Coral USB Accelerator](https://coral.withgoogle.com/products/accelerator/) **Note:** This version requires the use of a [Google Coral USB Accelerator](https://coral.withgoogle.com/products/accelerator/)
-20
View File
@@ -1,20 +0,0 @@
import statistics
import numpy as np
from edgetpu.detection.engine import DetectionEngine
# Path to frozen detection graph. This is the actual model that is used for the object detection.
PATH_TO_CKPT = '/frozen_inference_graph.pb'
# Load the edgetpu engine and labels
engine = DetectionEngine(PATH_TO_CKPT)
frame = np.zeros((300,300,3), np.uint8)
flattened_frame = np.expand_dims(frame, axis=0).flatten()
detection_times = []
for x in range(0, 1000):
objects = engine.DetectWithInputTensor(flattened_frame, threshold=0.1, top_k=3)
detection_times.append(engine.get_inference_time())
print("Average inference time: " + str(statistics.mean(detection_times)))
+1 -32
View File
@@ -15,38 +15,7 @@ cameras:
# values that begin with a "$" will be replaced with environment variable # values that begin with a "$" will be replaced with environment variable
password: $RTSP_PASSWORD password: $RTSP_PASSWORD
path: /cam/realmonitor?channel=1&subtype=2 path: /cam/realmonitor?channel=1&subtype=2
mask: back-mask.bmp
################
## Optional mask. Must be the same dimensions as your video feed.
## The mask works by looking at the bottom center of the bounding box for the detected
## person in the image. If that pixel in the mask is a black pixel, it ignores it as a
## false positive. In my mask, the grass and driveway visible from my backdoor camera
## are white. The garage doors, sky, and trees (anywhere it would be impossible for a
## person to stand) are black.
################
# mask: back-mask.bmp
################
# Allows you to limit the framerate within frigate for cameras that do not support
# custom framerates. A value of 1 tells frigate to look at every frame, 2 every 2nd frame,
# 3 every 3rd frame, etc.
################
take_frame: 1
################
# Optional hardware acceleration parameters for ffmpeg. If your hardware supports it, it can
# greatly reduce the CPU power used to decode the video stream. You will need to determine which
# parameters work for your specific hardware. These may work for those with Intel hardware that
# supports QuickSync.
################
# ffmpeg_hwaccel_args:
# - -hwaccel
# - vaapi
# - -hwaccel_device
# - /dev/dri/renderD128
# - -hwaccel_output_format
# - yuv420p
regions: regions:
- size: 350 - size: 350
x_offset: 0 x_offset: 0
+2 -11
View File
@@ -25,18 +25,9 @@ def main():
# connect to mqtt and setup last will # connect to mqtt and setup last will
def on_connect(client, userdata, flags, rc): def on_connect(client, userdata, flags, rc):
print("On connect called") print("On connect called")
if rc != 0:
if rc == 3:
print ("MQTT Server unavailable")
elif rc == 4:
print ("MQTT Bad username or password")
elif rc == 5:
print ("MQTT Not authorized")
else:
print ("Unable to connect to MQTT: Connection refused. Error code: " + str(rc))
# publish a message to signal that the service is running # publish a message to signal that the service is running
client.publish(MQTT_TOPIC_PREFIX+'/available', 'online', retain=True) client.publish(MQTT_TOPIC_PREFIX+'/available', 'online', retain=True)
client = mqtt.Client(client_id="frigate") client = mqtt.Client()
client.on_connect = on_connect client.on_connect = on_connect
client.will_set(MQTT_TOPIC_PREFIX+'/available', payload='offline', qos=1, retain=True) client.will_set(MQTT_TOPIC_PREFIX+'/available', payload='offline', qos=1, retain=True)
if not MQTT_USER is None: if not MQTT_USER is None:
@@ -96,4 +87,4 @@ def main():
camera.join() camera.join()
if __name__ == '__main__': if __name__ == '__main__':
main() main()
+5 -5
View File
@@ -39,8 +39,6 @@ class PreppedQueueProcessor(threading.Thread):
# Actual detection. # Actual detection.
objects = self.engine.DetectWithInputTensor(frame['frame'], threshold=frame['region_threshold'], top_k=3) objects = self.engine.DetectWithInputTensor(frame['frame'], threshold=frame['region_threshold'], top_k=3)
# print(self.engine.get_inference_time())
# parse and pass detected objects back to the camera # parse and pass detected objects back to the camera
parsed_objects = [] parsed_objects = []
for obj in objects: for obj in objects:
@@ -91,11 +89,13 @@ class FramePrepper(threading.Thread):
cropped_frame = self.shared_frame[self.region_y_offset:self.region_y_offset+self.region_size, self.region_x_offset:self.region_x_offset+self.region_size].copy() cropped_frame = self.shared_frame[self.region_y_offset:self.region_y_offset+self.region_size, self.region_x_offset:self.region_x_offset+self.region_size].copy()
frame_time = self.frame_time.value frame_time = self.frame_time.value
# convert to RGB
cropped_frame_rgb = cv2.cvtColor(cropped_frame, cv2.COLOR_BGR2RGB)
# Resize to 300x300 if needed # Resize to 300x300 if needed
if cropped_frame.shape != (300, 300, 3): if cropped_frame_rgb.shape != (300, 300, 3):
cropped_frame = cv2.resize(cropped_frame, dsize=(300, 300), interpolation=cv2.INTER_LINEAR) cropped_frame_rgb = cv2.resize(cropped_frame_rgb, dsize=(300, 300), interpolation=cv2.INTER_LINEAR)
# Expand dimensions since the model expects images to have shape: [1, 300, 300, 3] # Expand dimensions since the model expects images to have shape: [1, 300, 300, 3]
frame_expanded = np.expand_dims(cropped_frame, axis=0) frame_expanded = np.expand_dims(cropped_frame_rgb, axis=0)
# add the frame to the queue # add the frame to the queue
if not self.prepped_frame_queue.full(): if not self.prepped_frame_queue.full():
+7 -5
View File
@@ -2,7 +2,6 @@ import time
import datetime import datetime
import threading import threading
import cv2 import cv2
from . util import draw_box_with_label
class ObjectCleaner(threading.Thread): class ObjectCleaner(threading.Thread):
def __init__(self, objects_parsed, detected_objects): def __init__(self, objects_parsed, detected_objects):
@@ -80,9 +79,12 @@ class BestPersonFrame(threading.Thread):
if not self.best_person is None and self.best_person['frame_time'] in recent_frames: if not self.best_person is None and self.best_person['frame_time'] in recent_frames:
best_frame = recent_frames[self.best_person['frame_time']] best_frame = recent_frames[self.best_person['frame_time']]
best_frame = cv2.cvtColor(best_frame, cv2.COLOR_BGR2RGB)
# draw the bounding box on the frame
color = (255,0,0)
cv2.rectangle(best_frame, (self.best_person['xmin'], self.best_person['ymin']),
(self.best_person['xmax'], self.best_person['ymax']),
color, 2)
label = "{}: {}% {}".format(self.best_person['name'],int(self.best_person['score']*100),int(self.best_person['area'])) # convert back to BGR
draw_box_with_label(best_frame, self.best_person['xmin'], self.best_person['ymin'],
self.best_person['xmax'], self.best_person['ymax'], label)
self.best_frame = cv2.cvtColor(best_frame, cv2.COLOR_RGB2BGR) self.best_frame = cv2.cvtColor(best_frame, cv2.COLOR_RGB2BGR)
+1 -22
View File
@@ -1,26 +1,5 @@
import numpy as np import numpy as np
import cv2
# convert shared memory array into numpy array # convert shared memory array into numpy array
def tonumpyarray(mp_arr): def tonumpyarray(mp_arr):
return np.frombuffer(mp_arr.get_obj(), dtype=np.uint8) return np.frombuffer(mp_arr.get_obj(), dtype=np.uint8)
def draw_box_with_label(frame, x_min, y_min, x_max, y_max, label):
color = (255,0,0)
cv2.rectangle(frame, (x_min, y_min),
(x_max, y_max),
color, 2)
font_scale = 0.5
font = cv2.FONT_HERSHEY_SIMPLEX
# get the width and height of the text box
size = cv2.getTextSize(label, font, fontScale=font_scale, thickness=2)
text_width = size[0][0]
text_height = size[0][1]
line_height = text_height + size[1]
# set the text start position
text_offset_x = x_min
text_offset_y = 0 if y_min < line_height else y_min - (line_height+8)
# make the coords of the box with a small padding of two pixels
textbox_coords = ((text_offset_x, text_offset_y), (text_offset_x + text_width + 2, text_offset_y + line_height))
cv2.rectangle(frame, textbox_coords[0], textbox_coords[1], color, cv2.FILLED)
cv2.putText(frame, label, (text_offset_x, text_offset_y + line_height - 3), font, fontScale=font_scale, color=(0, 0, 0), thickness=2)
+89 -109
View File
@@ -5,13 +5,60 @@ import cv2
import threading import threading
import ctypes import ctypes
import multiprocessing as mp import multiprocessing as mp
import subprocess as sp
import numpy as np import numpy as np
from . util import tonumpyarray, draw_box_with_label from . util import tonumpyarray
from . object_detection import FramePrepper from . object_detection import FramePrepper
from . objects import ObjectCleaner, BestPersonFrame from . objects import ObjectCleaner, BestPersonFrame
from . mqtt import MqttObjectPublisher from . mqtt import MqttObjectPublisher
# fetch the frames as fast a possible and store current frame in a shared memory array
def fetch_frames(shared_arr, shared_frame_time, frame_lock, frame_ready, frame_shape, rtsp_url):
# convert shared memory array into numpy and shape into image array
arr = tonumpyarray(shared_arr).reshape(frame_shape)
# start the video capture
video = cv2.VideoCapture()
video.open(rtsp_url)
print("Opening the RTSP Url...")
# keep the buffer small so we minimize old data
video.set(cv2.CAP_PROP_BUFFERSIZE,1)
bad_frame_counter = 0
while True:
# check if the video stream is still open, and reopen if needed
if not video.isOpened():
success = video.open(rtsp_url)
if not success:
time.sleep(1)
continue
# grab the frame, but dont decode it yet
ret = video.grab()
# snapshot the time the frame was grabbed
frame_time = datetime.datetime.now()
if ret:
# go ahead and decode the current frame
ret, frame = video.retrieve()
if ret:
# Lock access and update frame
with frame_lock:
arr[:] = frame
shared_frame_time.value = frame_time.timestamp()
# Notify with the condition that a new frame is ready
with frame_ready:
frame_ready.notify_all()
bad_frame_counter = 0
else:
print("Unable to decode frame")
bad_frame_counter += 1
else:
print("Unable to grab a frame")
bad_frame_counter += 1
if bad_frame_counter > 100:
video.release()
video.release()
# Stores 2 seconds worth of frames when motion is detected so they can be used for other threads # Stores 2 seconds worth of frames when motion is detected so they can be used for other threads
class FrameTracker(threading.Thread): class FrameTracker(threading.Thread):
def __init__(self, shared_frame, frame_time, frame_ready, frame_lock, recent_frames): def __init__(self, shared_frame, frame_time, frame_ready, frame_lock, recent_frames):
@@ -71,48 +118,13 @@ class CameraWatchdog(threading.Thread):
while True: while True:
# wait a bit before checking # wait a bit before checking
time.sleep(10) time.sleep(60)
if (datetime.datetime.now().timestamp() - self.camera.frame_time.value) > 2: if (datetime.datetime.now().timestamp() - self.camera.shared_frame_time.value) > 2:
print("last frame is more than 2 seconds old, restarting camera capture...") print("last frame is more than 2 seconds old, restarting camera capture...")
self.camera.start_or_restart_capture() self.camera.start_or_restart_capture()
time.sleep(5) time.sleep(5)
# Thread to read the stdout of the ffmpeg process and update the current frame
class CameraCapture(threading.Thread):
def __init__(self, camera):
threading.Thread.__init__(self)
self.camera = camera
def run(self):
frame_num = 0
while True:
if self.camera.ffmpeg_process.poll() != None:
print("ffmpeg process is not running. exiting capture thread...")
break
raw_image = self.camera.ffmpeg_process.stdout.read(self.camera.frame_size)
if len(raw_image) == 0:
print("ffmpeg didnt return a frame. something is wrong. exiting capture thread...")
break
frame_num += 1
if (frame_num % self.camera.take_frame) != 0:
continue
with self.camera.frame_lock:
self.camera.frame_time.value = datetime.datetime.now().timestamp()
self.camera.current_frame[:] = (
np
.frombuffer(raw_image, np.uint8)
.reshape(self.camera.frame_shape)
)
# Notify with the condition that a new frame is ready
with self.camera.frame_ready:
self.camera.frame_ready.notify_all()
class Camera: class Camera:
def __init__(self, name, config, prepped_frame_queue, mqtt_client, mqtt_prefix): def __init__(self, name, config, prepped_frame_queue, mqtt_client, mqtt_prefix):
self.name = name self.name = name
@@ -120,18 +132,17 @@ class Camera:
self.detected_objects = [] self.detected_objects = []
self.recent_frames = {} self.recent_frames = {}
self.rtsp_url = get_rtsp_url(self.config['rtsp']) self.rtsp_url = get_rtsp_url(self.config['rtsp'])
self.take_frame = self.config.get('take_frame', 1)
self.ffmpeg_hwaccel_args = self.config.get('ffmpeg_hwaccel_args', [])
self.regions = self.config['regions'] self.regions = self.config['regions']
self.frame_shape = get_frame_shape(self.rtsp_url) self.frame_shape = get_frame_shape(self.rtsp_url)
self.frame_size = self.frame_shape[0] * self.frame_shape[1] * self.frame_shape[2]
self.mqtt_client = mqtt_client self.mqtt_client = mqtt_client
self.mqtt_topic_prefix = '{}/{}'.format(mqtt_prefix, self.name) self.mqtt_topic_prefix = '{}/{}'.format(mqtt_prefix, self.name)
# create a numpy array for the current frame in initialize to zeros # compute the flattened array length from the shape of the frame
self.current_frame = np.zeros(self.frame_shape, np.uint8) flat_array_length = self.frame_shape[0] * self.frame_shape[1] * self.frame_shape[2]
# create shared array for storing the full frame image data
self.shared_frame_array = mp.Array(ctypes.c_uint8, flat_array_length)
# create shared value for storing the frame_time # create shared value for storing the frame_time
self.frame_time = mp.Value('d', 0.0) self.shared_frame_time = mp.Value('d', 0.0)
# Lock to control access to the frame # Lock to control access to the frame
self.frame_lock = mp.Lock() self.frame_lock = mp.Lock()
# Condition for notifying that a new frame is ready # Condition for notifying that a new frame is ready
@@ -139,8 +150,10 @@ class Camera:
# Condition for notifying that objects were parsed # Condition for notifying that objects were parsed
self.objects_parsed = mp.Condition() self.objects_parsed = mp.Condition()
self.ffmpeg_process = None # shape current frame so it can be treated as a numpy image
self.capture_thread = None self.shared_frame_np = tonumpyarray(self.shared_frame_array).reshape(self.frame_shape)
self.capture_process = None
# for each region, create a separate thread to resize the region and prep for detection # for each region, create a separate thread to resize the region and prep for detection
self.detection_prep_threads = [] self.detection_prep_threads = []
@@ -153,8 +166,8 @@ class Camera:
region['threshold'] = 0.5 region['threshold'] = 0.5
self.detection_prep_threads.append(FramePrepper( self.detection_prep_threads.append(FramePrepper(
self.name, self.name,
self.current_frame, self.shared_frame_np,
self.frame_time, self.shared_frame_time,
self.frame_ready, self.frame_ready,
self.frame_lock, self.frame_lock,
region['size'], region['x_offset'], region['y_offset'], region['threshold'], region['size'], region['x_offset'], region['y_offset'], region['threshold'],
@@ -162,7 +175,7 @@ class Camera:
)) ))
# start a thread to store recent motion frames for processing # start a thread to store recent motion frames for processing
self.frame_tracker = FrameTracker(self.current_frame, self.frame_time, self.frame_tracker = FrameTracker(self.shared_frame_np, self.shared_frame_time,
self.frame_ready, self.frame_lock, self.recent_frames) self.frame_ready, self.frame_lock, self.recent_frames)
self.frame_tracker.start() self.frame_tracker.start()
@@ -185,59 +198,24 @@ class Camera:
if 'mask' in self.config: if 'mask' in self.config:
self.mask = cv2.imread("/config/{}".format(self.config['mask']), cv2.IMREAD_GRAYSCALE) self.mask = cv2.imread("/config/{}".format(self.config['mask']), cv2.IMREAD_GRAYSCALE)
else: else:
self.mask = None
if self.mask is None:
self.mask = np.zeros((self.frame_shape[0], self.frame_shape[1], 1), np.uint8) self.mask = np.zeros((self.frame_shape[0], self.frame_shape[1], 1), np.uint8)
self.mask[:] = 255 self.mask[:] = 255
def start_or_restart_capture(self): def start_or_restart_capture(self):
if not self.ffmpeg_process is None: if not self.capture_process is None:
print("Killing the existing ffmpeg process...") print("Terminating the existing capture process...")
self.ffmpeg_process.kill() self.capture_process.terminate()
self.ffmpeg_process.wait() del self.capture_process
print("Waiting for the capture thread to exit...") self.capture_process = None
self.capture_thread.join()
self.ffmpeg_process = None
self.capture_thread = None
# create the process to capture frames from the RTSP stream and store in a shared array # create the process to capture frames from the RTSP stream and store in a shared array
print("Creating a new ffmpeg process...") print("Creating a new capture process...")
self.start_ffmpeg() self.capture_process = mp.Process(target=fetch_frames, args=(self.shared_frame_array,
self.shared_frame_time, self.frame_lock, self.frame_ready, self.frame_shape, self.rtsp_url))
print("Creating a new capture thread...") self.capture_process.daemon = True
self.capture_thread = CameraCapture(self) print("Starting a new capture process...")
print("Starting a new capture thread...") self.capture_process.start()
self.capture_thread.start()
def start_ffmpeg(self):
ffmpeg_global_args = [
'-hide_banner', '-loglevel', 'panic'
]
ffmpeg_input_args = [
'-avoid_negative_ts', 'make_zero',
'-fflags', 'nobuffer',
'-flags', 'low_delay',
'-strict', 'experimental',
'-fflags', '+genpts',
'-rtsp_transport', 'tcp',
'-stimeout', '5000000',
'-use_wallclock_as_timestamps', '1'
]
ffmpeg_cmd = (['ffmpeg'] +
ffmpeg_global_args +
self.ffmpeg_hwaccel_args +
ffmpeg_input_args +
['-i', self.rtsp_url,
'-f', 'rawvideo',
'-pix_fmt', 'rgb24',
'pipe:'])
print(" ".join(ffmpeg_cmd))
self.ffmpeg_process = sp.Popen(ffmpeg_cmd, stdout = sp.PIPE, bufsize=self.frame_size)
def start(self): def start(self):
self.start_or_restart_capture() self.start_or_restart_capture()
@@ -247,20 +225,18 @@ class Camera:
self.watchdog.start() self.watchdog.start()
def join(self): def join(self):
self.capture_thread.join() self.capture_process.join()
def get_capture_pid(self): def get_capture_pid(self):
return self.ffmpeg_process.pid return self.capture_process.pid
def add_objects(self, objects): def add_objects(self, objects):
if len(objects) == 0: if len(objects) == 0:
return return
for obj in objects: for obj in objects:
# Store object area to use in bounding box labels
obj['area'] = (obj['xmax']-obj['xmin'])*(obj['ymax']-obj['ymin'])
if obj['name'] == 'person': if obj['name'] == 'person':
person_area = (obj['xmax']-obj['xmin'])*(obj['ymax']-obj['ymin'])
# find the matching region # find the matching region
region = None region = None
for r in self.regions: for r in self.regions:
@@ -275,13 +251,13 @@ class Camera:
# if the min person area is larger than the # if the min person area is larger than the
# detected person, don't add it to detected objects # detected person, don't add it to detected objects
if region and 'min_person_area' in region and region['min_person_area'] > obj['area']: if region and region['min_person_area'] > person_area:
continue continue
# compute the coordinates of the person and make sure # compute the coordinates of the person and make sure
# the location isnt outside the bounds of the image (can happen from rounding) # the location isnt outide the bounds of the image (can happen from rounding)
y_location = min(int(obj['ymax']), len(self.mask)-1) y_location = min(int(obj['ymax']), len(self.mask)-1)
x_location = min(int((obj['xmax']-obj['xmin'])/2.0)+obj['xmin'], len(self.mask[0])-1) x_location = min(int((obj['xmax']-obj['xmin'])/2.0), len(self.mask[0])-1)
# if the person is in a masked location, continue # if the person is in a masked location, continue
if self.mask[y_location][x_location] == [0]: if self.mask[y_location][x_location] == [0]:
@@ -300,12 +276,16 @@ class Camera:
detected_objects = self.detected_objects.copy() detected_objects = self.detected_objects.copy()
# lock and make a copy of the current frame # lock and make a copy of the current frame
with self.frame_lock: with self.frame_lock:
frame = self.current_frame.copy() frame = self.shared_frame_np.copy()
# convert to RGB for drawing
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# draw the bounding boxes on the screen # draw the bounding boxes on the screen
for obj in detected_objects: for obj in detected_objects:
label = "{}: {}% {}".format(obj['name'],int(obj['score']*100),int(obj['area'])) color = (255,0,0)
draw_box_with_label(frame, obj['xmin'], obj['ymin'], obj['xmax'], obj['ymax'], label) cv2.rectangle(frame, (obj['xmin'], obj['ymin']),
(obj['xmax'], obj['ymax']),
color, 2)
for region in self.regions: for region in self.regions:
color = (255,255,255) color = (255,255,255)
@@ -313,11 +293,11 @@ class Camera:
(region['x_offset']+region['size'], region['y_offset']+region['size']), (region['x_offset']+region['size'], region['y_offset']+region['size']),
color, 2) color, 2)
# convert to BGR # convert back to BGR
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
return frame return frame
-50
View File
@@ -1,50 +0,0 @@
#!/bin/bash
set -e
CPU_ARCH=$(uname -m)
OS_VERSION=$(uname -v)
echo "CPU_ARCH ${CPU_ARCH}"
echo "OS_VERSION ${OS_VERSION}"
if [[ "${CPU_ARCH}" == "x86_64" ]]; then
echo "Recognized as Linux on x86_64."
LIBEDGETPU_SUFFIX=x86_64
HOST_GNU_TYPE=x86_64-linux-gnu
elif [[ "${CPU_ARCH}" == "armv7l" ]]; then
echo "Recognized as Linux on ARM32 platform."
LIBEDGETPU_SUFFIX=arm32
HOST_GNU_TYPE=arm-linux-gnueabihf
elif [[ "${CPU_ARCH}" == "aarch64" ]]; then
echo "Recognized as generic ARM64 platform."
LIBEDGETPU_SUFFIX=arm64
HOST_GNU_TYPE=aarch64-linux-gnu
fi
if [[ -z "${HOST_GNU_TYPE}" ]]; then
echo "Your platform is not supported."
exit 1
fi
echo "Using maximum operating frequency."
LIBEDGETPU_SRC="libedgetpu/libedgetpu_${LIBEDGETPU_SUFFIX}.so"
LIBEDGETPU_DST="/usr/lib/${HOST_GNU_TYPE}/libedgetpu.so.1.0"
# Runtime library.
echo "Installing Edge TPU runtime library [${LIBEDGETPU_DST}]..."
if [[ -f "${LIBEDGETPU_DST}" ]]; then
echo "File already exists. Replacing it..."
rm -f "${LIBEDGETPU_DST}"
fi
cp -p "${LIBEDGETPU_SRC}" "${LIBEDGETPU_DST}"
ldconfig
echo "Done."
# Python API.
WHEEL=$(ls edgetpu-*-py3-none-any.whl 2>/dev/null)
if [[ $? == 0 ]]; then
echo "Installing Edge TPU Python API..."
python3 -m pip install --no-deps "${WHEEL}"
echo "Done."
fi
-5
View File
@@ -1,5 +0,0 @@
#!/bin/bash
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys D986B59D
echo "deb http://deb.odroid.in/5422-s bionic main" > /etc/apt/sources.list.d/odroid.list