mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-10-30 18:46:38 +08:00
Compare commits
29 Commits
v0.7.1
...
v0.7.0-rc2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45e9f84f6c | ||
|
|
20cff853e8 | ||
|
|
d28d5e04a9 | ||
|
|
26f4e27df0 | ||
|
|
106d513e0b | ||
|
|
223ec76601 | ||
|
|
405837de22 | ||
|
|
51bd107536 | ||
|
|
08c43e7918 | ||
|
|
6f070502b5 | ||
|
|
61081b91a3 | ||
|
|
2a84d0afb9 | ||
|
|
2c17f27ab4 | ||
|
|
5bb9838c4f | ||
|
|
d59018a14c | ||
|
|
5f2b8bb6ad | ||
|
|
6554640a61 | ||
|
|
5fbb092212 | ||
|
|
dbb4ca7c87 | ||
|
|
e506931830 | ||
|
|
feaf63c15f | ||
|
|
a94179be4d | ||
|
|
7837de8bc8 | ||
|
|
0366781728 | ||
|
|
e898fca70a | ||
|
|
d788ceb1d3 | ||
|
|
90a48fc761 | ||
|
|
de57c79bf9 | ||
|
|
af8c4e7eac |
32
Makefile
32
Makefile
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user