Compare commits

..

41 Commits

Author SHA1 Message Date
Blake Blackshear
cd057370e1 fallback to opencv to detect resolution and allow config to specify 2020-02-22 09:03:00 -06:00
Blake Blackshear
6263912655 use ffprobe to get frame shape (fixes #87) 2020-02-22 09:03:00 -06:00
Blake Blackshear
af247275cf make timestamp on snapshots configurable (fixes #88) 2020-02-22 09:03:00 -06:00
Blake Blackshear
1198c29dac make watchdog timeout configurable per camera (fixes #95) 2020-02-22 09:03:00 -06:00
Blake Blackshear
169603d3ff attempt to fix regions in process key error 2020-02-22 09:03:00 -06:00
Blake Blackshear
dc7eecebc6 clarify config 2020-02-22 09:03:00 -06:00
Blake Blackshear
0dd4087d5d switch base image back to ubuntu:18.04 2020-02-22 09:03:00 -06:00
Blake Blackshear
6ecf87fc60 update config example 2020-02-22 09:03:00 -06:00
Blake Blackshear
ebcf1482f8 remove region in process when skipping 2020-02-22 09:03:00 -06:00
Blake Blackshear
50bcf60893 switch to opencv headless 2020-02-22 09:03:00 -06:00
Blake Blackshear
38efbd63ea add camera name to ffmpeg log messages 2020-02-22 09:03:00 -06:00
Blake Blackshear
50bcad8b77 skip regions when the queue is too full and add more locks 2020-02-22 09:03:00 -06:00
Blake Blackshear
cfffb219ae switch back to stretch for hwaccel issues 2020-02-22 09:03:00 -06:00
Blake Blackshear
382d7be50a check correct object 2020-02-22 09:03:00 -06:00
Blake Blackshear
f43dc36a37 cleanup 2020-02-22 09:03:00 -06:00
Blake Blackshear
38e7fa07d2 add a label position arg for bounding boxes 2020-02-22 09:03:00 -06:00
Blake Blackshear
e261c20819 let the queues get as big as needed 2020-02-22 09:03:00 -06:00
Blake Blackshear
3a66e672d3 notify mqtt when objects deregistered 2020-02-22 09:03:00 -06:00
Blake Blackshear
2aada930e3 fix multiple object type tracking 2020-02-22 09:03:00 -06:00
Blake Blackshear
d87f4407a0 switch everything to run off of tracked objects 2020-02-22 09:03:00 -06:00
Blake Blackshear
be5a114f6a group by label before tracking objects 2020-02-22 09:03:00 -06:00
Blake Blackshear
32b212c7b6 fix mask filtering 2020-02-22 09:03:00 -06:00
Blake Blackshear
76c8e3a12f make a copy 2020-02-22 09:03:00 -06:00
Blake Blackshear
16f7a361c3 fix object filters 2020-02-22 09:03:00 -06:00
Blake Blackshear
634b87307f group by label before suppressing boxes 2020-02-22 09:03:00 -06:00
Blake Blackshear
1d4fbbdba3 update all obj props 2020-02-22 09:03:00 -06:00
Blake Blackshear
65579e9cbf add thread to write frames to disk 2020-02-22 09:03:00 -06:00
Blake Blackshear
49dc029c43 merge boxes by label 2020-02-22 09:03:00 -06:00
Blake Blackshear
08174d8db2 fix color of best image 2020-02-22 09:03:00 -06:00
Blake Blackshear
5199242a68 remove unused current frame variable 2020-02-22 09:03:00 -06:00
Blake Blackshear
725dd3220c removing pillow-simd for now 2020-02-22 09:03:00 -06:00
Blake Blackshear
10dc56f6ea revamp dockerfile 2020-02-22 09:03:00 -06:00
Blake Blackshear
cc2abe93a6 track objects and add config for tracked objects 2020-02-22 09:03:00 -06:00
Blake Blackshear
0c6717090c implement filtering and switch to NMS with OpenCV 2020-02-22 09:03:00 -06:00
Blake Blackshear
f5a2252b29 cleanup imports 2020-02-22 09:03:00 -06:00
Blake Blackshear
02efb6f415 fixing a few things 2020-02-22 09:03:00 -06:00
Blake Blackshear
5b4c6e50bc dedupe detected objects 2020-02-22 09:03:00 -06:00
Blake Blackshear
9cc46a71cb working dynamic regions, but messy 2020-02-22 09:03:00 -06:00
Blake Blackshear
be1673b00a process detected objects in a queue 2020-02-22 09:03:00 -06:00
Blake Blackshear
b6130e77ff label threads and implements stats endpoint 2020-02-22 09:03:00 -06:00
Blake Blackshear
4180c710cd refactor resizing into generic priority queues 2020-02-22 09:03:00 -06:00
4 changed files with 73 additions and 13 deletions

View File

@@ -1,4 +1,4 @@
FROM debian:stretch-slim FROM ubuntu:18.04
LABEL maintainer "blakeb@blakeshome.com" LABEL maintainer "blakeb@blakeshome.com"
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
@@ -16,7 +16,7 @@ RUN apt -qq update && apt -qq install --no-install-recommends -y \
# pillow-simd # pillow-simd
# zlib1g-dev libjpeg-dev \ # zlib1g-dev libjpeg-dev \
# VAAPI drivers for Intel hardware accel # VAAPI drivers for Intel hardware accel
i965-va-driver vainfo \ libva-drm2 libva2 i965-va-driver vainfo \
&& echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" > /etc/apt/sources.list.d/coral-edgetpu.list \ && echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" > /etc/apt/sources.list.d/coral-edgetpu.list \
&& wget -q -O - https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - \ && wget -q -O - https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - \
&& apt -qq update \ && apt -qq update \

View File

@@ -82,6 +82,12 @@ cameras:
# input_args: [] # input_args: []
# output_args: [] # output_args: []
################
## Optionally specify the resolution of the video feed. Frigate will try to auto detect if not specified
################
# height: 1280
# width: 720
################ ################
## Optional mask. Must be the same dimensions as your video feed. ## 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 ## The mask works by looking at the bottom center of the bounding box for the detected
@@ -100,7 +106,19 @@ cameras:
take_frame: 1 take_frame: 1
################ ################
# Overrides for global object config # The number of seconds frigate will allow a camera to go without sending a frame before
# assuming the ffmpeg process has a problem and restarting.
################
# watchdog_timeout: 300
################
# Configuration for the snapshot sent over mqtt
################
snapshots:
show_timestamp: True
################
# Camera level object config. This config is merged with the global config above.
################ ################
objects: objects:
track: track:

View File

@@ -88,21 +88,30 @@ class DetectedObjectsProcessor(threading.Thread):
obj['clipped'] = True obj['clipped'] = True
# Compute the area # Compute the area
# TODO: +1 right?
obj['area'] = (obj['box']['xmax']-obj['box']['xmin'])*(obj['box']['ymax']-obj['box']['ymin']) obj['area'] = (obj['box']['xmax']-obj['box']['xmin'])*(obj['box']['ymax']-obj['box']['ymin'])
self.camera.detected_objects[frame['frame_time']].append(obj) self.camera.detected_objects[frame['frame_time']].append(obj)
# TODO: use in_process and processed counts instead to avoid lock
with self.camera.regions_in_process_lock: with self.camera.regions_in_process_lock:
self.camera.regions_in_process[frame['frame_time']] -= 1 if frame['frame_time'] in self.camera.regions_in_process:
self.camera.regions_in_process[frame['frame_time']] -= 1
# print(f"{frame['frame_time']} remaining regions {self.camera.regions_in_process[frame['frame_time']]}") # print(f"{frame['frame_time']} remaining regions {self.camera.regions_in_process[frame['frame_time']]}")
if self.camera.regions_in_process[frame['frame_time']] == 0: if self.camera.regions_in_process[frame['frame_time']] == 0:
del self.camera.regions_in_process[frame['frame_time']] del self.camera.regions_in_process[frame['frame_time']]
# print(f"{frame['frame_time']} no remaining regions") # print(f"{frame['frame_time']} no remaining regions")
self.camera.finished_frame_queue.put(frame['frame_time'])
else:
self.camera.finished_frame_queue.put(frame['frame_time']) self.camera.finished_frame_queue.put(frame['frame_time'])
# Thread that checks finished frames for clipped objects and sends back # Thread that checks finished frames for clipped objects and sends back
# for processing if needed # for processing if needed
# TODO: evaluate whether or not i really need separate threads/queues for each step
# given that only 1 thread will really be able to run at a time. you need a
# separate process to actually do things in parallel for when you are CPU bound.
# threads are good when you are waiting and could be processing while you wait
class RegionRefiner(threading.Thread): class RegionRefiner(threading.Thread):
def __init__(self, camera): def __init__(self, camera):
threading.Thread.__init__(self) threading.Thread.__init__(self)
@@ -360,6 +369,9 @@ class ObjectTracker(threading.Thread):
# than the number of existing object centroids we need to # than the number of existing object centroids we need to
# register each new input centroid as a trackable object # register each new input centroid as a trackable object
# if D.shape[0] < D.shape[1]: # if D.shape[0] < D.shape[1]:
# TODO: rather than assuming these are new objects, we could
# look to see if any of the remaining boxes have a large amount
# of overlap...
for col in unusedCols: for col in unusedCols:
self.register(col, group[col]) self.register(col, group[col])
@@ -399,7 +411,8 @@ class BestFrames(threading.Thread):
obj['box']['xmax'], obj['box']['ymax'], obj['name'], "{}% {}".format(int(obj['score']*100), obj['area'])) obj['box']['xmax'], obj['box']['ymax'], obj['name'], "{}% {}".format(int(obj['score']*100), obj['area']))
# print a timestamp # print a timestamp
time_to_show = datetime.datetime.fromtimestamp(obj['frame_time']).strftime("%m/%d/%Y %H:%M:%S") if self.camera.snapshot_config['show_timestamp']:
cv2.putText(best_frame, time_to_show, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, fontScale=.8, color=(255, 255, 255), thickness=2) time_to_show = datetime.datetime.fromtimestamp(obj['frame_time']).strftime("%m/%d/%Y %H:%M:%S")
cv2.putText(best_frame, time_to_show, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, fontScale=.8, color=(255, 255, 255), thickness=2)
self.best_frames[name] = best_frame self.best_frames[name] = best_frame

View File

@@ -11,6 +11,7 @@ import numpy as np
import prctl import prctl
import copy import copy
import itertools import itertools
import json
from collections import defaultdict from collections import defaultdict
from frigate.util import tonumpyarray, LABELS, draw_box_with_label, calculate_region, EventsPerSecond from frigate.util import tonumpyarray, LABELS, draw_box_with_label, calculate_region, EventsPerSecond
from frigate.object_detection import RegionPrepper, RegionRequester from frigate.object_detection import RegionPrepper, RegionRequester
@@ -42,8 +43,29 @@ class FrameTracker(threading.Thread):
del self.recent_frames[k] del self.recent_frames[k]
def get_frame_shape(source): def get_frame_shape(source):
# capture a single frame and check the frame shape so the correct array ffprobe_cmd = " ".join([
# size can be allocated in memory 'ffprobe',
'-v',
'panic',
'-show_error',
'-show_streams',
'-of',
'json',
'"'+source+'"'
])
print(ffprobe_cmd)
p = sp.Popen(ffprobe_cmd, stdout=sp.PIPE, shell=True)
(output, err) = p.communicate()
p_status = p.wait()
info = json.loads(output)
print(info)
video_info = [s for s in info['streams'] if s['codec_type'] == 'video'][0]
if video_info['height'] != 0 and video_info['width'] != 0:
return (video_info['height'], video_info['width'], 3)
# fallback to using opencv if ffprobe didnt succeed
video = cv2.VideoCapture(source) video = cv2.VideoCapture(source)
ret, frame = video.read() ret, frame = video.read()
frame_shape = frame.shape frame_shape = frame.shape
@@ -65,7 +87,7 @@ class CameraWatchdog(threading.Thread):
# wait a bit before checking # wait a bit before checking
time.sleep(10) time.sleep(10)
if self.camera.frame_time.value != 0.0 and (datetime.datetime.now().timestamp() - self.camera.frame_time.value) > 300: if self.camera.frame_time.value != 0.0 and (datetime.datetime.now().timestamp() - self.camera.frame_time.value) > self.camera.watchdog_timeout:
print(self.camera.name + ": last frame is more than 5 minutes old, restarting camera capture...") print(self.camera.name + ": last frame is more than 5 minutes old, restarting camera capture...")
self.camera.start_or_restart_capture() self.camera.start_or_restart_capture()
time.sleep(5) time.sleep(5)
@@ -151,8 +173,15 @@ class Camera:
camera_objects_config = config.get('objects', {}) camera_objects_config = config.get('objects', {})
self.take_frame = self.config.get('take_frame', 1) self.take_frame = self.config.get('take_frame', 1)
self.watchdog_timeout = self.config.get('watchdog_timeout', 300)
self.snapshot_config = {
'show_timestamp': self.config.get('snapshots', {}).get('show_timestamp', True)
}
self.regions = self.config['regions'] self.regions = self.config['regions']
self.frame_shape = get_frame_shape(self.ffmpeg_input) if 'width' in self.config and 'height' in self.config:
self.frame_shape = (self.config['height'], self.config['width'], 3)
else:
self.frame_shape = get_frame_shape(self.ffmpeg_input)
self.frame_size = self.frame_shape[0] * self.frame_shape[1] * self.frame_shape[2] 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)