Compare commits

..

29 Commits

Author SHA1 Message Date
Blake Blackshear
45e9f84f6c prevent the camera process from hanging 2020-10-11 21:28:58 -05:00
Blake Blackshear
20cff853e8 syntax error 2020-10-11 13:07:00 -05:00
Blake Blackshear
d28d5e04a9 update docs 2020-10-11 12:58:41 -05:00
Blake Blackshear
26f4e27df0 update default detectors 2020-10-11 12:52:50 -05:00
Blake Blackshear
106d513e0b use dictionary for detectors for sensors 2020-10-11 12:49:08 -05:00
Blake Blackshear
223ec76601 only draw during debug 2020-10-11 12:16:57 -05:00
Dejan Zelic
405837de22 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-11 11:49:29 -05:00
Radegast
51bd107536 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-11 11:49:29 -05:00
Blake Blackshear
08c43e7918 cleanup frame queue 2020-10-11 11:49:29 -05:00
Blake Blackshear
6f070502b5 cleanup detection shms 2020-10-11 11:49:29 -05:00
Blake Blackshear
61081b91a3 only convert pix_fmt when necessary 2020-10-11 11:49:29 -05:00
Blake Blackshear
2a84d0afb9 use yuv420p pixel format for motion 2020-10-11 11:49:29 -05:00
Blake Blackshear
2c17f27ab4 support multiple coral devices (fixes #100) 2020-10-11 11:49:29 -05:00
Blake Blackshear
5bb9838c4f print stacktraceon segfaults 2020-10-11 11:49:29 -05:00
Blake Blackshear
d59018a14c prevent frame from being deleted while in use 2020-10-11 11:49:29 -05:00
Blake Blackshear
5f2b8bb6ad build ffmpeg in separate container 2020-10-11 11:49:29 -05:00
Blake Blackshear
6554640a61 arm64 ffmpeg cleanup 2020-10-11 11:49:29 -05:00
Blake Blackshear
5fbb092212 arm64 ffmpeg build 2020-10-11 11:49:29 -05:00
Blake Blackshear
dbb4ca7c87 ffmpeg 4.3.1 build for amd64 2020-10-11 11:49:29 -05:00
Blake Blackshear
e506931830 base image build cleanup 2020-10-11 11:49:29 -05:00
Blake Blackshear
feaf63c15f arm64 support 2020-10-11 11:49:29 -05:00
Blake Blackshear
a94179be4d add rpi dockerfile 2020-10-11 11:49:29 -05:00
Blake Blackshear
7837de8bc8 update dockerfiles for amd64 2020-10-11 11:49:29 -05:00
Blake Blackshear
0366781728 Base dockerfile for building wheels 2020-10-11 11:49:29 -05:00
Blake Blackshear
e898fca70a refactor dockerfile 2020-10-11 11:49:29 -05:00
Blake Blackshear
d788ceb1d3 fix shared memory store usage for events 2020-10-11 11:49:29 -05:00
Blake Blackshear
90a48fc761 cleanup 2020-10-11 11:49:29 -05:00
Blake Blackshear
de57c79bf9 update detection handoff to use shared memory 2020-10-11 11:49:29 -05:00
Blake Blackshear
af8c4e7eac upgrade to python3.8 and switch from plasma store to shared_memory 2020-10-11 11:49:29 -05:00
10 changed files with 31 additions and 71 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
aarch64_wheels: arm64_wheels:
docker build --tag blakeblackshear/frigate-wheels:aarch64 --file docker/Dockerfile.wheels.aarch64 . docker build --tag blakeblackshear/frigate-wheels:arm64 --file docker/Dockerfile.wheels.arm64 .
aarch64_ffmpeg: arm64_ffmpeg:
docker build --tag blakeblackshear/frigate-ffmpeg:aarch64 --file docker/Dockerfile.ffmpeg.aarch64 . docker build --tag blakeblackshear/frigate-ffmpeg:arm64 --file docker/Dockerfile.ffmpeg.arm64 .
aarch64_frigate: arm64_frigate:
docker build --tag frigate-base --build-arg ARCH=aarch64 --file docker/Dockerfile.base . docker build --tag frigate-base --build-arg ARCH=arm64 --file docker/Dockerfile.base .
docker build --tag frigate --file docker/Dockerfile.aarch64 . docker build --tag frigate --file docker/Dockerfile.arm64 .
armv7_all: armv7_wheels armv7_ffmpeg armv7_frigate armv7hf_all: arm64_wheels arm64_ffmpeg arm64_frigate
armv7_wheels: armv7hf_wheels:
docker build --tag blakeblackshear/frigate-wheels:armv7 --file docker/Dockerfile.wheels . docker build --tag blakeblackshear/frigate-wheels:armv7hf --file docker/Dockerfile.wheels .
armv7_ffmpeg: armv7hf_ffmpeg:
docker build --tag blakeblackshear/frigate-ffmpeg:armv7 --file docker/Dockerfile.ffmpeg.armv7 . docker build --tag blakeblackshear/frigate-ffmpeg:armv7hf --file docker/Dockerfile.ffmpeg.armv7hf .
armv7_frigate: armv7hf_frigate:
docker build --tag frigate-base --build-arg ARCH=armv7 --file docker/Dockerfile.base . docker build --tag frigate-base --build-arg ARCH=armv7hf --file docker/Dockerfile.base .
docker build --tag frigate --file docker/Dockerfile.armv7 . docker build --tag frigate --file docker/Dockerfile.armv7hf .
armv7_all: armv7_wheels armv7_ffmpeg armv7_frigate armv7hf_all: armv7hf_wheels armv7hf_ffmpeg armv7hf_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:0.7.0-amd64 blakeblackshear/frigate:stable
``` ```
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:0.7.0-amd64 image: blakeblackshear/frigate:stable
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,8 +31,6 @@ 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.
@@ -217,7 +215,6 @@ 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,7 +9,6 @@ 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
@@ -26,22 +25,8 @@ 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_')}
CONFIG_FILE = os.environ.get('CONFIG_FILE', '/config/config.yml') with open('/config/config.yml') as f:
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)
@@ -179,8 +164,7 @@ 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', {})
@@ -238,7 +222,7 @@ def main():
"-an", "-an",
"-map", "-map",
"0", "0",
f"{os.path.join(CACHE_DIR, name)}-%Y%m%d%H%M%S.mp4" f"/cache/{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 +
@@ -304,7 +288,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_DIR, CLIPS_DIR, event_queue, stop_event) event_processor = EventProcessor(CONFIG, camera_processes, '/cache', '/clips', 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)
@@ -328,7 +312,7 @@ def main():
shm.close() shm.close()
shm.unlink() shm.unlink()
for detector in detectors.values(): for detector in detectors:
detector.stop() detector.stop()
for shm in camera_shms: for shm in camera_shms:
shm.close() shm.close()
@@ -404,11 +388,9 @@ 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') best_frame = best_object.get('frame', np.zeros((720,1280,3), np.uint8))
if best_frame is None:
best_frame = np.zeros((720,1280,3), np.uint8) best_frame = cv2.cvtColor(best_frame, cv2.COLOR_YUV2BGR_I420)
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,12 +181,10 @@ 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 # check if the object is in the zone and not filtered
if (cv2.pointPolygonTest(contour, bottom_center, False) >= 0): if (cv2.pointPolygonTest(contour, bottom_center, False) >= 0
# if the object passed the filters once, dont apply again and not zone_filtered(obj, zone.get('filters', {}))):
if name in obj.get('zones', []) or not zone_filtered(obj, zone.get('filters', {})): current_zones.append(name)
current_zones.append(name)
obj['zones'] = current_zones obj['zones'] = current_zones
# maintain best objects # maintain best objects
@@ -268,14 +266,7 @@ 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']
@@ -284,16 +275,6 @@ 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()