forked from Github/frigate
Compare commits
41 Commits
v0.4.0-bet
...
v0.5.0-rc4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd057370e1 | ||
|
|
6263912655 | ||
|
|
af247275cf | ||
|
|
1198c29dac | ||
|
|
169603d3ff | ||
|
|
dc7eecebc6 | ||
|
|
0dd4087d5d | ||
|
|
6ecf87fc60 | ||
|
|
ebcf1482f8 | ||
|
|
50bcf60893 | ||
|
|
38efbd63ea | ||
|
|
50bcad8b77 | ||
|
|
cfffb219ae | ||
|
|
382d7be50a | ||
|
|
f43dc36a37 | ||
|
|
38e7fa07d2 | ||
|
|
e261c20819 | ||
|
|
3a66e672d3 | ||
|
|
2aada930e3 | ||
|
|
d87f4407a0 | ||
|
|
be5a114f6a | ||
|
|
32b212c7b6 | ||
|
|
76c8e3a12f | ||
|
|
16f7a361c3 | ||
|
|
634b87307f | ||
|
|
1d4fbbdba3 | ||
|
|
65579e9cbf | ||
|
|
49dc029c43 | ||
|
|
08174d8db2 | ||
|
|
5199242a68 | ||
|
|
725dd3220c | ||
|
|
10dc56f6ea | ||
|
|
cc2abe93a6 | ||
|
|
0c6717090c | ||
|
|
f5a2252b29 | ||
|
|
02efb6f415 | ||
|
|
5b4c6e50bc | ||
|
|
9cc46a71cb | ||
|
|
be1673b00a | ||
|
|
b6130e77ff | ||
|
|
4180c710cd |
@@ -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 \
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user