Compare commits

..

45 Commits

Author SHA1 Message Date
Blake Blackshear
dd86e4f317 fix clips path and check for symlinks 2020-10-19 07:01:31 -05:00
Blake Blackshear
4db285a875 remove reference to stable 2020-10-18 14:12:25 -05:00
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 Zelic
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
Radegast
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 71 additions and 31 deletions

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

View File

@@ -31,7 +31,7 @@ docker run --rm \
-v /etc/localtime:/etc/localtime:ro \ -v /etc/localtime:/etc/localtime:ro \
-p 5000:5000 \ -p 5000:5000 \
-e FRIGATE_RTSP_PASSWORD='password' \ -e FRIGATE_RTSP_PASSWORD='password' \
blakeblackshear/frigate:stable blakeblackshear/frigate:0.7.0-amd64
``` ```
Example docker-compose: Example docker-compose:
@@ -41,7 +41,7 @@ Example docker-compose:
restart: unless-stopped restart: unless-stopped
privileged: true privileged: true
shm_size: '100m' # only needed with large numbers of high res cameras shm_size: '100m' # only needed with large numbers of high res cameras
image: blakeblackshear/frigate:stable image: blakeblackshear/frigate:0.7.0-amd64
volumes: volumes:
- /dev/bus/usb:/dev/bus/usb - /dev/bus/usb:/dev/bus/usb
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro

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.

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', '/clips')
if not os.path.exists(CACHE_DIR) and not os.path.islink(CACHE_DIR):
os.makedirs(CACHE_DIR)
if not os.path.exists(CLIPS_DIR) and not os.path.islink(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:

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()