Compare commits

..
43 Commits
Author SHA1 Message Date
Blake Blackshear 939d1ba091 use global and ensure dirs exist 2020-10-18 13:47:13 -05:00
Blake Blackshear 0fe8d486d9 make cache/clips dirs configurable 2020-10-18 13:47:13 -05:00
Blake Blackshear a3cb02af5c sync arch names with hassio 2020-10-18 13:47:13 -05:00
Blake Blackshear 45a6b8452c allow config file to be specified by env var and allow json 2020-10-18 13:47:13 -05:00
Blake Blackshear 9d594cc640 allow setting config file location via env var 2020-10-18 13:47:13 -05:00
Blake Blackshear 59e41ae1ac update sample config 2020-10-18 13:47:13 -05:00
Blake Blackshear c6ed16465b move the timestamp to bottom 2020-10-18 13:47:13 -05:00
Blake Blackshear 8f14b36f5a tweak size 2020-10-18 13:47:13 -05:00
Blake Blackshear b6c2491e3b use the actual original shape 2020-10-18 13:47:13 -05:00
Blake Blackshear 8e31d04d90 scale font of timestamp dynamically 2020-10-18 13:47:13 -05:00
Blake Blackshear bf93fbb357 add ability to draw bounding boxes/timestamps on snapshots 2020-10-18 13:47:13 -05:00
Blake Blackshear c064b244db handle empty best frames 2020-10-18 13:47:13 -05:00
Blake Blackshear 0280610e96 fix detector cleanup 2020-10-18 13:47:13 -05:00
Blake Blackshear 4363623c45 reduce zone filter bouncing 2020-10-18 13:47:13 -05:00
Blake Blackshear c960914ec3 prevent the camera process from hanging 2020-10-18 13:47:13 -05:00
Blake Blackshear 9ecc80b443 syntax error 2020-10-18 13:47:13 -05:00
Blake Blackshear 3e146de0a2 update docs 2020-10-18 13:47:13 -05:00
Blake Blackshear bee54c39dc update default detectors 2020-10-18 13:47:13 -05:00
Blake Blackshear 623d138d60 use dictionary for detectors for sensors 2020-10-18 13:47:13 -05:00
Blake Blackshear 76befc1249 only draw during debug 2020-10-18 13:47:13 -05:00
Dejan ZelicandBlake Blackshear 51251b9fb0 Added Healthcheck to Docker Compose
Frigate provides an HTTP server that can be used to detect if frigate is running or not. Using the docker-compose "healthcheck" feature we can set automations to restart the service if it stops working.
2020-10-18 13:47:13 -05:00
RadegastandBlake Blackshear 8c45076bb6 Fix error in the docker run command
I have very little experience with Docker, but it seems the command in the README has two mistakes in it:

- unknown shorthand flag: 'n' in -name
- docker: Error response from daemon: Invalid container name (blakeblackshear/frigate:stable), only [a-zA-Z0-9][a-zA-Z0-9_.-] are allowed.

I am running Docker version 19.03.13-ce, build 4484c46d9d on Arch linux.
2020-10-18 13:47:13 -05:00
Blake Blackshear 7d683ef399 cleanup frame queue 2020-10-18 13:47:13 -05:00
Blake Blackshear e4da3822b1 cleanup detection shms 2020-10-18 13:47:13 -05:00
Blake Blackshear 12c4cd77c5 only convert pix_fmt when necessary 2020-10-18 13:47:13 -05:00
Blake Blackshear a611cbb942 use yuv420p pixel format for motion 2020-10-18 13:47:13 -05:00
Blake Blackshear f946813ccb support multiple coral devices (fixes #100) 2020-10-18 13:47:13 -05:00
Blake Blackshear 49fca1b839 print stacktraceon segfaults 2020-10-18 13:47:13 -05:00
Blake Blackshear 54cb4a2180 prevent frame from being deleted while in use 2020-10-18 13:47:13 -05:00
Blake Blackshear 9954e3b11e build ffmpeg in separate container 2020-10-18 13:47:13 -05:00
Blake Blackshear 82692b0ddc arm64 ffmpeg cleanup 2020-10-18 13:47:13 -05:00
Blake Blackshear 9d4fdec12f arm64 ffmpeg build 2020-10-18 13:47:13 -05:00
Blake Blackshear ed72c995ef ffmpeg 4.3.1 build for amd64 2020-10-18 13:47:13 -05:00
Blake Blackshear 66c77d1157 base image build cleanup 2020-10-18 13:47:13 -05:00
Blake Blackshear 40c322ad47 arm64 support 2020-10-18 13:47:13 -05:00
Blake Blackshear 83f1e0d713 add rpi dockerfile 2020-10-18 13:47:13 -05:00
Blake Blackshear 2d89044bd3 update dockerfiles for amd64 2020-10-18 13:47:13 -05:00
Blake Blackshear dc4d24c2b9 Base dockerfile for building wheels 2020-10-18 13:47:13 -05:00
Blake Blackshear d5fb20c524 refactor dockerfile 2020-10-18 13:47:13 -05:00
Blake Blackshear 7e92e8bfe8 fix shared memory store usage for events 2020-10-18 13:47:13 -05:00
Blake Blackshear efdcfcef97 cleanup 2020-10-18 13:47:13 -05:00
Blake Blackshear 574ee2a46f update detection handoff to use shared memory 2020-10-18 13:47:13 -05:00
Blake Blackshear ec4d048905 upgrade to python3.8 and switch from plasma store to shared_memory 2020-10-18 13:47:13 -05:00
10 changed files with 74 additions and 30 deletions
+16 -16
View File
@@ -12,26 +12,26 @@ amd64_frigate:
amd64_all: amd64_wheels amd64_ffmpeg amd64_frigate amd64_all: amd64_wheels amd64_ffmpeg amd64_frigate
arm64_wheels: aarch64_wheels:
docker build --tag blakeblackshear/frigate-wheels:arm64 --file docker/Dockerfile.wheels.arm64 . docker build --tag blakeblackshear/frigate-wheels:aarch64 --file docker/Dockerfile.wheels.aarch64 .
arm64_ffmpeg: aarch64_ffmpeg:
docker build --tag blakeblackshear/frigate-ffmpeg:arm64 --file docker/Dockerfile.ffmpeg.arm64 . docker build --tag blakeblackshear/frigate-ffmpeg:aarch64 --file docker/Dockerfile.ffmpeg.aarch64 .
arm64_frigate: aarch64_frigate:
docker build --tag frigate-base --build-arg ARCH=arm64 --file docker/Dockerfile.base . docker build --tag frigate-base --build-arg ARCH=aarch64 --file docker/Dockerfile.base .
docker build --tag frigate --file docker/Dockerfile.arm64 . docker build --tag frigate --file docker/Dockerfile.aarch64 .
armv7hf_all: arm64_wheels arm64_ffmpeg arm64_frigate armv7_all: armv7_wheels armv7_ffmpeg armv7_frigate
armv7hf_wheels: armv7_wheels:
docker build --tag blakeblackshear/frigate-wheels:armv7hf --file docker/Dockerfile.wheels . docker build --tag blakeblackshear/frigate-wheels:armv7 --file docker/Dockerfile.wheels .
armv7hf_ffmpeg: armv7_ffmpeg:
docker build --tag blakeblackshear/frigate-ffmpeg:armv7hf --file docker/Dockerfile.ffmpeg.armv7hf . docker build --tag blakeblackshear/frigate-ffmpeg:armv7 --file docker/Dockerfile.ffmpeg.armv7 .
armv7hf_frigate: armv7_frigate:
docker build --tag frigate-base --build-arg ARCH=armv7hf --file docker/Dockerfile.base . docker build --tag frigate-base --build-arg ARCH=armv7 --file docker/Dockerfile.base .
docker build --tag frigate --file docker/Dockerfile.armv7hf . docker build --tag frigate --file docker/Dockerfile.armv7 .
armv7hf_all: armv7hf_wheels armv7hf_ffmpeg armv7hf_frigate armv7_all: armv7_wheels armv7_ffmpeg armv7_frigate
+3
View File
@@ -31,6 +31,8 @@ save_clips:
# will begin to expire and the resulting clip will be the last x seconds of the event. # will begin to expire and the resulting clip will be the last x seconds of the event.
########### ###########
max_seconds: 300 max_seconds: 300
clips_dir: /clips
cache_dir: /cache
################# #################
# Default ffmpeg args. Optional and can be overwritten per camera. # Default ffmpeg args. Optional and can be overwritten per camera.
@@ -215,6 +217,7 @@ cameras:
snapshots: snapshots:
show_timestamp: True show_timestamp: True
draw_zones: False draw_zones: False
draw_bounding_boxes: True
################ ################
# Camera level object config. If defined, this is used instead of the global config. # Camera level object config. If defined, this is used instead of the global config.
+27 -9
View File
@@ -9,6 +9,7 @@ import time
import datetime import datetime
import queue import queue
import yaml import yaml
import json
import threading import threading
import multiprocessing as mp import multiprocessing as mp
import subprocess as sp import subprocess as sp
@@ -25,8 +26,22 @@ from frigate.edgetpu import EdgeTPUProcess
FRIGATE_VARS = {k: v for k, v in os.environ.items() if k.startswith('FRIGATE_')} FRIGATE_VARS = {k: v for k, v in os.environ.items() if k.startswith('FRIGATE_')}
with open('/config/config.yml') as f: CONFIG_FILE = os.environ.get('CONFIG_FILE', '/config/config.yml')
CONFIG = yaml.safe_load(f)
if CONFIG_FILE.endswith(".yml"):
with open(CONFIG_FILE) as f:
CONFIG = yaml.safe_load(f)
elif CONFIG_FILE.endswith(".json"):
with open(CONFIG_FILE) as f:
CONFIG = json.load(f)
CACHE_DIR = CONFIG.get('save_clips', {}).get('cache_dir', '/cache')
CLIPS_DIR = CONFIG.get('save_clips', {}).get('clips_dir', '/cache')
if not os.path.exists(CACHE_DIR):
os.makedirs(CACHE_DIR)
if not os.path.exists(CLIPS_DIR):
os.makedirs(CLIPS_DIR)
MQTT_HOST = CONFIG['mqtt']['host'] MQTT_HOST = CONFIG['mqtt']['host']
MQTT_PORT = CONFIG.get('mqtt', {}).get('port', 1883) MQTT_PORT = CONFIG.get('mqtt', {}).get('port', 1883)
@@ -164,7 +179,8 @@ def main():
for name, config in CONFIG['cameras'].items(): for name, config in CONFIG['cameras'].items():
config['snapshots'] = { config['snapshots'] = {
'show_timestamp': config.get('snapshots', {}).get('show_timestamp', True), 'show_timestamp': config.get('snapshots', {}).get('show_timestamp', True),
'draw_zones': config.get('snapshots', {}).get('draw_zones', False) 'draw_zones': config.get('snapshots', {}).get('draw_zones', False),
'draw_bounding_boxes': config.get('snapshots', {}).get('draw_bounding_boxes', True)
} }
config['zones'] = config.get('zones', {}) config['zones'] = config.get('zones', {})
@@ -222,7 +238,7 @@ def main():
"-an", "-an",
"-map", "-map",
"0", "0",
f"/cache/{name}-%Y%m%d%H%M%S.mp4" f"{os.path.join(CACHE_DIR, name)}-%Y%m%d%H%M%S.mp4"
] + ffmpeg_output_args ] + ffmpeg_output_args
ffmpeg_cmd = (['ffmpeg'] + ffmpeg_cmd = (['ffmpeg'] +
ffmpeg_global_args + ffmpeg_global_args +
@@ -288,7 +304,7 @@ def main():
camera_process['process'].start() camera_process['process'].start()
print(f"Camera_process started for {name}: {camera_process['process'].pid}") print(f"Camera_process started for {name}: {camera_process['process'].pid}")
event_processor = EventProcessor(CONFIG, camera_processes, '/cache', '/clips', event_queue, stop_event) event_processor = EventProcessor(CONFIG, camera_processes, CACHE_DIR, CLIPS_DIR, event_queue, stop_event)
event_processor.start() event_processor.start()
object_processor = TrackedObjectProcessor(CONFIG['cameras'], client, MQTT_TOPIC_PREFIX, tracked_objects_queue, event_queue, stop_event) object_processor = TrackedObjectProcessor(CONFIG['cameras'], client, MQTT_TOPIC_PREFIX, tracked_objects_queue, event_queue, stop_event)
@@ -312,7 +328,7 @@ def main():
shm.close() shm.close()
shm.unlink() shm.unlink()
for detector in detectors: for detector in detectors.values():
detector.stop() detector.stop()
for shm in camera_shms: for shm in camera_shms:
shm.close() shm.close()
@@ -388,9 +404,11 @@ def main():
def best(camera_name, label): def best(camera_name, label):
if camera_name in CONFIG['cameras']: if camera_name in CONFIG['cameras']:
best_object = object_processor.get_best(camera_name, label) best_object = object_processor.get_best(camera_name, label)
best_frame = best_object.get('frame', np.zeros((720,1280,3), np.uint8)) best_frame = best_object.get('frame')
if best_frame is None:
best_frame = cv2.cvtColor(best_frame, cv2.COLOR_YUV2BGR_I420) best_frame = np.zeros((720,1280,3), np.uint8)
else:
best_frame = cv2.cvtColor(best_frame, cv2.COLOR_YUV2BGR_I420)
crop = bool(request.args.get('crop', 0, type=int)) crop = bool(request.args.get('crop', 0, type=int))
if crop: if crop:
+5 -1
View File
@@ -177,7 +177,11 @@ class RemoteObjectDetector():
self.np_shm[:] = tensor_input[:] self.np_shm[:] = tensor_input[:]
self.event.clear() self.event.clear()
self.detection_queue.put(self.name) self.detection_queue.put(self.name)
self.event.wait() result = self.event.wait(timeout=10.0)
# if it timed out
if result is None:
return detections
for d in self.out_np_shm: for d in self.out_np_shm:
if d[1] < threshold: if d[1] < threshold:
+23 -4
View File
@@ -181,10 +181,12 @@ class CameraState():
# check each zone # check each zone
for name, zone in self.config['zones'].items(): for name, zone in self.config['zones'].items():
contour = zone['contour'] contour = zone['contour']
# check if the object is in the zone and not filtered # check if the object is in the zone
if (cv2.pointPolygonTest(contour, bottom_center, False) >= 0 if (cv2.pointPolygonTest(contour, bottom_center, False) >= 0):
and not zone_filtered(obj, zone.get('filters', {}))): # if the object passed the filters once, dont apply again
current_zones.append(name) if name in obj.get('zones', []) or not zone_filtered(obj, zone.get('filters', {})):
current_zones.append(name)
obj['zones'] = current_zones obj['zones'] = current_zones
# maintain best objects # maintain best objects
@@ -266,7 +268,14 @@ class TrackedObjectProcessor(threading.Thread):
def snapshot(camera, obj): def snapshot(camera, obj):
if not 'frame' in obj: if not 'frame' in obj:
return return
best_frame = cv2.cvtColor(obj['frame'], cv2.COLOR_YUV2BGR_I420) best_frame = cv2.cvtColor(obj['frame'], cv2.COLOR_YUV2BGR_I420)
if self.camera_config[camera]['snapshots']['draw_bounding_boxes']:
thickness = 2
color = COLOR_MAP[obj['label']]
box = obj['box']
draw_box_with_label(best_frame, box[0], box[1], box[2], box[3], obj['label'], f"{int(obj['score']*100)}% {int(obj['area'])}", thickness=thickness, color=color)
mqtt_config = self.camera_config[camera].get('mqtt', {'crop_to_region': False}) mqtt_config = self.camera_config[camera].get('mqtt', {'crop_to_region': False})
if mqtt_config.get('crop_to_region'): if mqtt_config.get('crop_to_region'):
region = obj['region'] region = obj['region']
@@ -275,6 +284,16 @@ class TrackedObjectProcessor(threading.Thread):
height = int(mqtt_config['snapshot_height']) height = int(mqtt_config['snapshot_height'])
width = int(height*best_frame.shape[1]/best_frame.shape[0]) width = int(height*best_frame.shape[1]/best_frame.shape[0])
best_frame = cv2.resize(best_frame, dsize=(width, height), interpolation=cv2.INTER_AREA) best_frame = cv2.resize(best_frame, dsize=(width, height), interpolation=cv2.INTER_AREA)
if self.camera_config[camera]['snapshots']['show_timestamp']:
time_to_show = datetime.datetime.fromtimestamp(obj['frame_time']).strftime("%m/%d/%Y %H:%M:%S")
size = cv2.getTextSize(time_to_show, cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, thickness=2)
text_width = size[0][0]
text_height = size[0][1]
desired_size = max(200, 0.33*best_frame.shape[1])
font_scale = desired_size/text_width
cv2.putText(best_frame, time_to_show, (5, best_frame.shape[0]-7), cv2.FONT_HERSHEY_SIMPLEX, fontScale=font_scale, color=(255, 255, 255), thickness=2)
ret, jpg = cv2.imencode('.jpg', best_frame) ret, jpg = cv2.imencode('.jpg', best_frame)
if ret: if ret:
jpg_bytes = jpg.tobytes() jpg_bytes = jpg.tobytes()