upgrade to python3.8 and switch from plasma store to shared_memory

This commit is contained in:
Blake Blackshear 2020-09-21 21:02:00 -05:00
parent b063099b2a
commit ec4d048905
7 changed files with 128 additions and 154 deletions

View File

@ -1,4 +1,4 @@
FROM ubuntu:18.04 FROM ubuntu:20.04
LABEL maintainer "blakeb@blakeshome.com" LABEL maintainer "blakeb@blakeshome.com"
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
@ -11,27 +11,26 @@ RUN apt -qq update && apt -qq install --no-install-recommends -y \
# libcap-dev \ # libcap-dev \
&& add-apt-repository ppa:deadsnakes/ppa -y \ && add-apt-repository ppa:deadsnakes/ppa -y \
&& apt -qq install --no-install-recommends -y \ && apt -qq install --no-install-recommends -y \
python3.7 \ python3.8 \
python3.7-dev \ python3.8-dev \
python3-pip \ python3-pip \
ffmpeg \ ffmpeg \
# VAAPI drivers for Intel hardware accel # VAAPI drivers for Intel hardware accel
libva-drm2 libva2 i965-va-driver vainfo \ libva-drm2 libva2 i965-va-driver vainfo \
&& python3.7 -m pip install -U pip \ && python3.8 -m pip install -U pip \
&& python3.7 -m pip install -U wheel setuptools \ && python3.8 -m pip install -U wheel setuptools \
&& python3.7 -m pip install -U \ && python3.8 -m pip install -U \
opencv-python-headless \ opencv-python-headless \
# python-prctl \ # python-prctl \
numpy \ numpy \
imutils \ imutils \
scipy \ scipy \
psutil \ psutil \
&& python3.7 -m pip install -U \ && python3.8 -m pip install -U \
Flask \ Flask \
paho-mqtt \ paho-mqtt \
PyYAML \ PyYAML \
matplotlib \ matplotlib \
pyarrow \
click \ click \
&& 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 - \
@ -39,10 +38,10 @@ RUN apt -qq update && apt -qq install --no-install-recommends -y \
&& echo "libedgetpu1-max libedgetpu/accepted-eula boolean true" | debconf-set-selections \ && echo "libedgetpu1-max libedgetpu/accepted-eula boolean true" | debconf-set-selections \
&& apt -qq install --no-install-recommends -y \ && apt -qq install --no-install-recommends -y \
libedgetpu1-max \ libedgetpu1-max \
## Tensorflow lite (python 3.7 only) ## Tensorflow lite
&& wget -q https://dl.google.com/coral/python/tflite_runtime-2.1.0.post1-cp37-cp37m-linux_x86_64.whl \ && wget -q https://dl.google.com/coral/python/tflite_runtime-2.1.0.post1-cp38-cp38-linux_x86_64.whl \
&& python3.7 -m pip install tflite_runtime-2.1.0.post1-cp37-cp37m-linux_x86_64.whl \ && python3.8 -m pip install tflite_runtime-2.1.0.post1-cp38-cp38-linux_x86_64.whl \
&& rm tflite_runtime-2.1.0.post1-cp37-cp37m-linux_x86_64.whl \ && rm tflite_runtime-2.1.0.post1-cp38-cp38-linux_x86_64.whl \
&& rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/apt/lists/* \
&& (apt-get autoremove -y; apt-get autoclean -y) && (apt-get autoremove -y; apt-get autoclean -y)
@ -60,4 +59,4 @@ COPY detect_objects.py .
COPY benchmark.py . COPY benchmark.py .
COPY process_clip.py . COPY process_clip.py .
CMD ["python3.7", "-u", "detect_objects.py"] CMD ["python3.8", "-u", "detect_objects.py"]

View File

@ -63,23 +63,13 @@ WEB_PORT = CONFIG.get('web_port', 5000)
DEBUG = (CONFIG.get('debug', '0') == '1') DEBUG = (CONFIG.get('debug', '0') == '1')
TENSORFLOW_DEVICE = CONFIG.get('tensorflow_device') TENSORFLOW_DEVICE = CONFIG.get('tensorflow_device')
def start_plasma_store():
plasma_cmd = ['plasma_store', '-m', '400000000', '-s', '/tmp/plasma']
plasma_process = sp.Popen(plasma_cmd, stdout=sp.DEVNULL, stderr=sp.DEVNULL)
time.sleep(1)
rc = plasma_process.poll()
if rc is not None:
return None
return plasma_process
class CameraWatchdog(threading.Thread): class CameraWatchdog(threading.Thread):
def __init__(self, camera_processes, config, tflite_process, tracked_objects_queue, plasma_process, stop_event): def __init__(self, camera_processes, config, tflite_process, tracked_objects_queue, stop_event):
threading.Thread.__init__(self) threading.Thread.__init__(self)
self.camera_processes = camera_processes self.camera_processes = camera_processes
self.config = config self.config = config
self.tflite_process = tflite_process self.tflite_process = tflite_process
self.tracked_objects_queue = tracked_objects_queue self.tracked_objects_queue = tracked_objects_queue
self.plasma_process = plasma_process
self.stop_event = stop_event self.stop_event = stop_event
def run(self): def run(self):
@ -94,12 +84,6 @@ class CameraWatchdog(threading.Thread):
now = datetime.datetime.now().timestamp() now = datetime.datetime.now().timestamp()
# check the plasma process
rc = self.plasma_process.poll()
if rc != None:
print(f"plasma_process exited unexpectedly with {rc}")
self.plasma_process = start_plasma_store()
# check the detection process # check the detection process
detection_start = self.tflite_process.detection_start.value detection_start = self.tflite_process.detection_start.value
if (detection_start > 0.0 and if (detection_start > 0.0 and
@ -172,8 +156,6 @@ def main():
client.connect(MQTT_HOST, MQTT_PORT, 60) client.connect(MQTT_HOST, MQTT_PORT, 60)
client.loop_start() client.loop_start()
plasma_process = start_plasma_store()
## ##
# Setup config defaults for cameras # Setup config defaults for cameras
## ##
@ -190,10 +172,15 @@ def main():
# Queue for clip processing # Queue for clip processing
event_queue = mp.Queue() event_queue = mp.Queue()
# Start the shared tflite process # create the detection pipes
tflite_process = EdgeTPUProcess(TENSORFLOW_DEVICE) detection_pipes = {}
for name in CONFIG['cameras'].keys():
detection_pipes[name] = mp.Pipe(duplex=False)
# start the camera processes # Start the shared tflite process
tflite_process = EdgeTPUProcess(result_connections={ key:value[1] for (key,value) in detection_pipes.items() }, tf_device=TENSORFLOW_DEVICE)
# create the camera processes
camera_processes = {} camera_processes = {}
for name, config in CONFIG['cameras'].items(): for name, config in CONFIG['cameras'].items():
# Merge the ffmpeg config with the global config # Merge the ffmpeg config with the global config
@ -237,6 +224,8 @@ def main():
else: else:
frame_shape = get_frame_shape(ffmpeg_input) frame_shape = get_frame_shape(ffmpeg_input)
config['frame_shape'] = frame_shape
frame_size = frame_shape[0] * frame_shape[1] * frame_shape[2] frame_size = frame_shape[0] * frame_shape[1] * frame_shape[2]
take_frame = config.get('take_frame', 1) take_frame = config.get('take_frame', 1)
@ -275,12 +264,13 @@ def main():
} }
camera_process = mp.Process(target=track_camera, args=(name, config, frame_queue, frame_shape, camera_process = mp.Process(target=track_camera, args=(name, config, frame_queue, frame_shape,
tflite_process.detection_queue, tracked_objects_queue, camera_processes[name]['process_fps'], tflite_process.detection_queue, detection_pipes[name][0], tracked_objects_queue, camera_processes[name]['process_fps'],
camera_processes[name]['detection_fps'], camera_processes[name]['detection_fps'],
camera_processes[name]['read_start'], camera_processes[name]['detection_frame'], stop_event)) camera_processes[name]['read_start'], camera_processes[name]['detection_frame'], stop_event))
camera_process.daemon = True camera_process.daemon = True
camera_processes[name]['process'] = camera_process camera_processes[name]['process'] = camera_process
# start the camera_processes
for name, camera_process in camera_processes.items(): for name, camera_process in camera_processes.items():
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}")
@ -291,7 +281,7 @@ def main():
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)
object_processor.start() object_processor.start()
camera_watchdog = CameraWatchdog(camera_processes, CONFIG['cameras'], tflite_process, tracked_objects_queue, plasma_process, stop_event) camera_watchdog = CameraWatchdog(camera_processes, CONFIG['cameras'], tflite_process, tracked_objects_queue, stop_event)
camera_watchdog.start() camera_watchdog.start()
def receiveSignal(signalNumber, frame): def receiveSignal(signalNumber, frame):
@ -300,11 +290,9 @@ def main():
event_processor.join() event_processor.join()
object_processor.join() object_processor.join()
camera_watchdog.join() camera_watchdog.join()
for name, camera_process in camera_processes.items(): for camera_process in camera_processes.values():
camera_process['capture_thread'].join() camera_process['capture_thread'].join()
rc = camera_watchdog.plasma_process.poll() tflite_process.stop()
if rc == None:
camera_watchdog.plasma_process.terminate()
sys.exit() sys.exit()
signal.signal(signal.SIGTERM, receiveSignal) signal.signal(signal.SIGTERM, receiveSignal)
@ -368,9 +356,6 @@ def main():
'pid': tflite_process.detect_process.pid 'pid': tflite_process.detect_process.pid
} }
rc = camera_watchdog.plasma_process.poll()
stats['plasma_store_rc'] = rc
return jsonify(stats) return jsonify(stats)
@app.route('/<camera_name>/<label>/best.jpg') @app.route('/<camera_name>/<label>/best.jpg')
@ -449,7 +434,5 @@ def main():
object_processor.join() object_processor.join()
plasma_process.terminate()
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@ -2,12 +2,14 @@ import os
import datetime import datetime
import hashlib import hashlib
import multiprocessing as mp import multiprocessing as mp
import queue
from multiprocessing.connection import Connection
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Dict
import numpy as np import numpy as np
import pyarrow.plasma as plasma
import tflite_runtime.interpreter as tflite import tflite_runtime.interpreter as tflite
from tflite_runtime.interpreter import load_delegate from tflite_runtime.interpreter import load_delegate
from frigate.util import EventsPerSecond, listen from frigate.util import EventsPerSecond, listen, SharedMemoryFrameManager
def load_labels(path, encoding='utf-8'): def load_labels(path, encoding='utf-8'):
"""Loads labels from file (with or without index numbers). """Loads labels from file (with or without index numbers).
@ -100,32 +102,33 @@ class LocalObjectDetector(ObjectDetector):
return detections return detections
def run_detector(detection_queue, avg_speed, start, tf_device): def run_detector(detection_queue, result_connections: Dict[str, Connection], avg_speed, start, tf_device):
print(f"Starting detection process: {os.getpid()}") print(f"Starting detection process: {os.getpid()}")
listen() listen()
plasma_client = plasma.connect("/tmp/plasma") frame_manager = SharedMemoryFrameManager()
object_detector = LocalObjectDetector(tf_device=tf_device) object_detector = LocalObjectDetector(tf_device=tf_device)
while True: while True:
object_id_str = detection_queue.get() connection_id = detection_queue.get()
object_id_hash = hashlib.sha1(str.encode(object_id_str)) input_frame = frame_manager.get(connection_id, (1,300,300,3))
object_id = plasma.ObjectID(object_id_hash.digest())
object_id_out = plasma.ObjectID(hashlib.sha1(str.encode(f"out-{object_id_str}")).digest())
input_frame = plasma_client.get(object_id, timeout_ms=0)
if input_frame is plasma.ObjectNotAvailable: if input_frame is None:
continue continue
# detect and put the output in the plasma store # detect and put the output in the plasma store
start.value = datetime.datetime.now().timestamp() start.value = datetime.datetime.now().timestamp()
plasma_client.put(object_detector.detect_raw(input_frame), object_id_out) # TODO: what is the overhead for pickling this result vs writing back to shared memory?
# I could try using an Event() and waiting in the other process before looking in memory...
detections = object_detector.detect_raw(input_frame)
result_connections[connection_id].send(detections)
duration = datetime.datetime.now().timestamp()-start.value duration = datetime.datetime.now().timestamp()-start.value
start.value = 0.0 start.value = 0.0
avg_speed.value = (avg_speed.value*9 + duration)/10 avg_speed.value = (avg_speed.value*9 + duration)/10
class EdgeTPUProcess(): class EdgeTPUProcess():
def __init__(self, tf_device=None): def __init__(self, result_connections, tf_device=None):
self.result_connections = result_connections
self.detection_queue = mp.Queue() self.detection_queue = mp.Queue()
self.avg_inference_speed = mp.Value('d', 0.01) self.avg_inference_speed = mp.Value('d', 0.01)
self.detection_start = mp.Value('d', 0.0) self.detection_start = mp.Value('d', 0.0)
@ -133,40 +136,43 @@ class EdgeTPUProcess():
self.tf_device = tf_device self.tf_device = tf_device
self.start_or_restart() self.start_or_restart()
def stop(self):
self.detect_process.terminate()
print("Waiting for detection process to exit gracefully...")
self.detect_process.join(timeout=30)
if self.detect_process.exitcode is None:
print("Detection process didnt exit. Force killing...")
self.detect_process.kill()
self.detect_process.join()
def start_or_restart(self): def start_or_restart(self):
self.detection_start.value = 0.0 self.detection_start.value = 0.0
if (not self.detect_process is None) and self.detect_process.is_alive(): if (not self.detect_process is None) and self.detect_process.is_alive():
self.detect_process.terminate() self.stop()
print("Waiting for detection process to exit gracefully...") self.detect_process = mp.Process(target=run_detector, args=(self.detection_queue, self.result_connections, self.avg_inference_speed, self.detection_start, self.tf_device))
self.detect_process.join(timeout=30)
if self.detect_process.exitcode is None:
print("Detection process didnt exit. Force killing...")
self.detect_process.kill()
self.detect_process.join()
self.detect_process = mp.Process(target=run_detector, args=(self.detection_queue, self.avg_inference_speed, self.detection_start, self.tf_device))
self.detect_process.daemon = True self.detect_process.daemon = True
self.detect_process.start() self.detect_process.start()
class RemoteObjectDetector(): class RemoteObjectDetector():
def __init__(self, name, labels, detection_queue): def __init__(self, name, labels, detection_queue, result_connection: Connection):
self.labels = load_labels(labels) self.labels = load_labels(labels)
self.name = name self.name = name
self.fps = EventsPerSecond() self.fps = EventsPerSecond()
self.plasma_client = plasma.connect("/tmp/plasma")
self.detection_queue = detection_queue self.detection_queue = detection_queue
self.result_connection = result_connection
self.shm = mp.shared_memory.SharedMemory(name=self.name, create=True, size=300*300*3)
self.np_shm = np.ndarray((1,300,300,3), dtype=np.uint8, buffer=self.shm.buf)
def detect(self, tensor_input, threshold=.4): def detect(self, tensor_input, threshold=.4):
detections = [] detections = []
now = f"{self.name}-{str(datetime.datetime.now().timestamp())}" # copy input to shared memory
object_id_frame = plasma.ObjectID(hashlib.sha1(str.encode(now)).digest()) # TODO: what if I just write it there in the first place?
object_id_detections = plasma.ObjectID(hashlib.sha1(str.encode(f"out-{now}")).digest()) self.np_shm[:] = tensor_input[:]
self.plasma_client.put(tensor_input, object_id_frame) self.detection_queue.put(self.name)
self.detection_queue.put(now) if self.result_connection.poll(10):
raw_detections = self.plasma_client.get(object_id_detections, timeout_ms=10000) raw_detections = self.result_connection.recv()
else:
if raw_detections is plasma.ObjectNotAvailable:
self.plasma_client.delete([object_id_frame])
return detections return detections
for d in raw_detections: for d in raw_detections:
@ -177,6 +183,5 @@ class RemoteObjectDetector():
float(d[1]), float(d[1]),
(d[2], d[3], d[4], d[5]) (d[2], d[3], d[4], d[5])
)) ))
self.plasma_client.delete([object_id_frame, object_id_detections])
self.fps.update() self.fps.update()
return detections return detections

View File

@ -10,9 +10,8 @@ import copy
import numpy as np import numpy as np
from collections import Counter, defaultdict from collections import Counter, defaultdict
import itertools import itertools
import pyarrow.plasma as plasma
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from frigate.util import draw_box_with_label, PlasmaFrameManager from frigate.util import draw_box_with_label, SharedMemoryFrameManager
from frigate.edgetpu import load_labels from frigate.edgetpu import load_labels
from typing import Callable, Dict from typing import Callable, Dict
from statistics import mean, median from statistics import mean, median
@ -59,7 +58,7 @@ class CameraState():
self.object_status = defaultdict(lambda: 'OFF') self.object_status = defaultdict(lambda: 'OFF')
self.tracked_objects = {} self.tracked_objects = {}
self.zone_objects = defaultdict(lambda: []) self.zone_objects = defaultdict(lambda: [])
self.current_frame = np.zeros((720,1280,3), np.uint8) self.current_frame = np.zeros(self.config['frame_shape'], np.uint8)
self.current_frame_time = 0.0 self.current_frame_time = 0.0
self.previous_frame_id = None self.previous_frame_id = None
self.callbacks = defaultdict(lambda: []) self.callbacks = defaultdict(lambda: [])
@ -88,7 +87,7 @@ class CameraState():
self.current_frame_time = frame_time self.current_frame_time = frame_time
# get the new frame and delete the old frame # get the new frame and delete the old frame
frame_id = f"{self.name}{frame_time}" frame_id = f"{self.name}{frame_time}"
self.current_frame = self.frame_manager.get(frame_id) self.current_frame = self.frame_manager.get(frame_id, self.config['frame_shape'])
if not self.previous_frame_id is None: if not self.previous_frame_id is None:
self.frame_manager.delete(self.previous_frame_id) self.frame_manager.delete(self.previous_frame_id)
self.previous_frame_id = frame_id self.previous_frame_id = frame_id
@ -238,7 +237,7 @@ class TrackedObjectProcessor(threading.Thread):
self.event_queue = event_queue self.event_queue = event_queue
self.stop_event = stop_event self.stop_event = stop_event
self.camera_states: Dict[str, CameraState] = {} self.camera_states: Dict[str, CameraState] = {}
self.plasma_client = PlasmaFrameManager(self.stop_event) self.frame_manager = SharedMemoryFrameManager()
def start(camera, obj): def start(camera, obj):
# publish events to mqtt # publish events to mqtt
@ -273,7 +272,7 @@ class TrackedObjectProcessor(threading.Thread):
self.client.publish(f"{self.topic_prefix}/{camera}/{object_name}", status, retain=False) self.client.publish(f"{self.topic_prefix}/{camera}/{object_name}", status, retain=False)
for camera in self.camera_config.keys(): for camera in self.camera_config.keys():
camera_state = CameraState(camera, self.camera_config[camera], self.plasma_client) camera_state = CameraState(camera, self.camera_config[camera], self.frame_manager)
camera_state.on('start', start) camera_state.on('start', start)
camera_state.on('update', update) camera_state.on('update', update)
camera_state.on('end', end) camera_state.on('end', end)

View File

@ -9,7 +9,8 @@ import cv2
import threading import threading
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import hashlib import hashlib
import pyarrow.plasma as plasma from multiprocessing import shared_memory
from typing import AnyStr
def draw_box_with_label(frame, x_min, y_min, x_max, y_max, label, info, thickness=2, color=None, position='ul'): def draw_box_with_label(frame, x_min, y_min, x_max, y_max, label, info, thickness=2, color=None, position='ul'):
if color is None: if color is None:
@ -148,12 +149,16 @@ def listen():
signal.signal(signal.SIGUSR1, print_stack) signal.signal(signal.SIGUSR1, print_stack)
class FrameManager(ABC): class FrameManager(ABC):
@abstractmethod
def create(self, name, size) -> AnyStr:
pass
@abstractmethod @abstractmethod
def get(self, name, timeout_ms=0): def get(self, name, timeout_ms=0):
pass pass
@abstractmethod @abstractmethod
def put(self, name, frame): def close(self, name):
pass pass
@abstractmethod @abstractmethod
@ -164,66 +169,45 @@ class DictFrameManager(FrameManager):
def __init__(self): def __init__(self):
self.frames = {} self.frames = {}
def get(self, name, timeout_ms=0): def create(self, name, size) -> AnyStr:
return self.frames.get(name) mem = bytearray(size)
self.frames[name] = mem
return mem
def put(self, name, frame): def get(self, name, shape):
self.frames[name] = frame mem = self.frames[name]
return np.ndarray(shape, dtype=np.uint8, buffer=mem)
def close(self, name):
pass
def delete(self, name): def delete(self, name):
del self.frames[name] del self.frames[name]
class PlasmaFrameManager(FrameManager): class SharedMemoryFrameManager(FrameManager):
def __init__(self, stop_event=None): def __init__(self):
self.stop_event = stop_event self.shm_store = {}
self.connect()
def connect(self): def create(self, name, size) -> AnyStr:
while True: shm = shared_memory.SharedMemory(name=name, create=True, size=size)
if self.stop_event != None and self.stop_event.is_set(): self.shm_store[name] = shm
return return shm.buf
try:
self.plasma_client = plasma.connect("/tmp/plasma")
return
except:
print(f"TrackedObjectProcessor: unable to connect plasma client")
time.sleep(10)
def get(self, name, timeout_ms=0): def get(self, name, shape):
object_id = plasma.ObjectID(hashlib.sha1(str.encode(name)).digest()) if name in self.shm_store:
while True: shm = self.shm_store[name]
if self.stop_event != None and self.stop_event.is_set(): else:
return shm = shared_memory.SharedMemory(name=name)
try: self.shm_store[name] = shm
frame = self.plasma_client.get(object_id, timeout_ms=timeout_ms) return np.ndarray(shape, dtype=np.uint8, buffer=shm.buf)
if frame is plasma.ObjectNotAvailable:
return None
return frame
except:
self.connect()
time.sleep(1)
def put(self, name, frame): def close(self, name):
object_id = plasma.ObjectID(hashlib.sha1(str.encode(name)).digest()) if name in self.shm_store:
while True: self.shm_store[name].close()
if self.stop_event != None and self.stop_event.is_set(): del self.shm_store[name]
return
try:
self.plasma_client.put(frame, object_id)
return
except Exception as e:
print(f"Failed to put in plasma: {e}")
self.connect()
time.sleep(1)
def delete(self, name): def delete(self, name):
object_id = plasma.ObjectID(hashlib.sha1(str.encode(name)).digest()) if name in self.shm_store:
while True: self.shm_store[name].close()
if self.stop_event != None and self.stop_event.is_set(): self.shm_store[name].unlink()
return del self.shm_store[name]
try:
self.plasma_client.delete([object_id])
return
except:
self.connect()
time.sleep(1)

View File

@ -5,7 +5,6 @@ import cv2
import queue import queue
import threading import threading
import ctypes import ctypes
import pyarrow.plasma as plasma
import multiprocessing as mp import multiprocessing as mp
import subprocess as sp import subprocess as sp
import numpy as np import numpy as np
@ -15,7 +14,7 @@ import json
import base64 import base64
from typing import Dict, List from typing import Dict, List
from collections import defaultdict from collections import defaultdict
from frigate.util import draw_box_with_label, area, calculate_region, clipped, intersection_over_union, intersection, EventsPerSecond, listen, FrameManager, PlasmaFrameManager from frigate.util import draw_box_with_label, area, calculate_region, clipped, intersection_over_union, intersection, EventsPerSecond, listen, FrameManager, SharedMemoryFrameManager
from frigate.objects import ObjectTracker from frigate.objects import ObjectTracker
from frigate.edgetpu import RemoteObjectDetector from frigate.edgetpu import RemoteObjectDetector
from frigate.motion import MotionDetector from frigate.motion import MotionDetector
@ -154,11 +153,10 @@ def capture_frames(ffmpeg_process, camera_name, frame_shape, frame_manager: Fram
continue continue
# put the frame in the frame manager # put the frame in the frame manager
frame_manager.put(f"{camera_name}{current_frame.value}", frame_buffer = frame_manager.create(f"{camera_name}{current_frame.value}", frame_size)
np frame_buffer[:] = frame_bytes[:]
.frombuffer(frame_bytes, np.uint8) frame_manager.close(f"{camera_name}{current_frame.value}")
.reshape(frame_shape)
)
# add to the queue # add to the queue
frame_queue.put(current_frame.value) frame_queue.put(current_frame.value)
last_frame = current_frame.value last_frame = current_frame.value
@ -173,7 +171,7 @@ class CameraCapture(threading.Thread):
self.take_frame = take_frame self.take_frame = take_frame
self.fps = fps self.fps = fps
self.skipped_fps = EventsPerSecond() self.skipped_fps = EventsPerSecond()
self.plasma_client = PlasmaFrameManager(stop_event) self.frame_manager = SharedMemoryFrameManager()
self.ffmpeg_process = ffmpeg_process self.ffmpeg_process = ffmpeg_process
self.current_frame = mp.Value('d', 0.0) self.current_frame = mp.Value('d', 0.0)
self.last_frame = 0 self.last_frame = 0
@ -182,10 +180,10 @@ class CameraCapture(threading.Thread):
def run(self): def run(self):
self.skipped_fps.start() self.skipped_fps.start()
capture_frames(self.ffmpeg_process, self.name, self.frame_shape, self.plasma_client, self.frame_queue, self.take_frame, capture_frames(self.ffmpeg_process, self.name, self.frame_shape, self.frame_manager, self.frame_queue, self.take_frame,
self.fps, self.skipped_fps, self.stop_event, self.detection_frame, self.current_frame) self.fps, self.skipped_fps, self.stop_event, self.detection_frame, self.current_frame)
def track_camera(name, config, frame_queue, frame_shape, detection_queue, detected_objects_queue, fps, detection_fps, read_start, detection_frame, stop_event): def track_camera(name, config, frame_queue, frame_shape, detection_queue, result_connection, detected_objects_queue, fps, detection_fps, read_start, detection_frame, stop_event):
print(f"Starting process for {name}: {os.getpid()}") print(f"Starting process for {name}: {os.getpid()}")
listen() listen()
@ -218,13 +216,13 @@ def track_camera(name, config, frame_queue, frame_shape, detection_queue, detect
mask[:] = 255 mask[:] = 255
motion_detector = MotionDetector(frame_shape, mask, resize_factor=6) motion_detector = MotionDetector(frame_shape, mask, resize_factor=6)
object_detector = RemoteObjectDetector(name, '/labelmap.txt', detection_queue) object_detector = RemoteObjectDetector(name, '/labelmap.txt', detection_queue, result_connection)
object_tracker = ObjectTracker(10) object_tracker = ObjectTracker(10)
plasma_client = PlasmaFrameManager() frame_manager = SharedMemoryFrameManager()
process_frames(name, frame_queue, frame_shape, plasma_client, motion_detector, object_detector, process_frames(name, frame_queue, frame_shape, frame_manager, motion_detector, object_detector,
object_tracker, detected_objects_queue, fps, detection_fps, detection_frame, objects_to_track, object_filters, mask, stop_event) object_tracker, detected_objects_queue, fps, detection_fps, detection_frame, objects_to_track, object_filters, mask, stop_event)
print(f"{name}: exiting subprocess") print(f"{name}: exiting subprocess")
@ -281,7 +279,7 @@ def process_frames(camera_name: str, frame_queue: mp.Queue, frame_shape,
current_frame_time.value = frame_time current_frame_time.value = frame_time
frame = frame_manager.get(f"{camera_name}{frame_time}") frame = frame_manager.get(f"{camera_name}{frame_time}", frame_shape)
if frame is None: if frame is None:
print(f"{camera_name}: frame {frame_time} is not in memory store.") print(f"{camera_name}: frame {frame_time} is not in memory store.")
@ -364,3 +362,5 @@ def process_frames(camera_name: str, frame_queue: mp.Queue, frame_shape,
detected_objects_queue.put((camera_name, frame_time, object_tracker.tracked_objects)) detected_objects_queue.put((camera_name, frame_time, object_tracker.tracked_objects))
detection_fps.value = object_detector.fps.eps() detection_fps.value = object_detector.fps.eps()
frame_manager.close(f"{camera_name}{frame_time}")

View File

@ -4,7 +4,7 @@ import os
import datetime import datetime
from unittest import TestCase, main from unittest import TestCase, main
from frigate.video import process_frames, start_or_restart_ffmpeg, capture_frames, get_frame_shape from frigate.video import process_frames, start_or_restart_ffmpeg, capture_frames, get_frame_shape
from frigate.util import DictFrameManager, EventsPerSecond, draw_box_with_label from frigate.util import DictFrameManager, SharedMemoryFrameManager, EventsPerSecond, draw_box_with_label
from frigate.motion import MotionDetector from frigate.motion import MotionDetector
from frigate.edgetpu import LocalObjectDetector from frigate.edgetpu import LocalObjectDetector
from frigate.objects import ObjectTracker from frigate.objects import ObjectTracker
@ -19,6 +19,7 @@ class ProcessClip():
self.frame_shape = frame_shape self.frame_shape = frame_shape
self.camera_name = 'camera' self.camera_name = 'camera'
self.frame_manager = DictFrameManager() self.frame_manager = DictFrameManager()
# self.frame_manager = SharedMemoryFrameManager()
self.frame_queue = mp.Queue() self.frame_queue = mp.Queue()
self.detected_objects_queue = mp.Queue() self.detected_objects_queue = mp.Queue()
self.camera_state = CameraState(self.camera_name, config, self.frame_manager) self.camera_state = CameraState(self.camera_name, config, self.frame_manager)
@ -72,13 +73,15 @@ class ProcessClip():
for obj in self.camera_state.tracked_objects.values(): for obj in self.camera_state.tracked_objects.values():
print(f"{frame_time}: {obj['id']} - {obj['computed_score']} - {obj['score_history']}") print(f"{frame_time}: {obj['id']} - {obj['computed_score']} - {obj['score_history']}")
self.frame_manager.delete(self.camera_state.previous_frame_id)
return { return {
'object_detected': obj_detected, 'object_detected': obj_detected,
'top_score': top_computed_score 'top_score': top_computed_score
} }
def save_debug_frame(self, debug_path, frame_time, tracked_objects): def save_debug_frame(self, debug_path, frame_time, tracked_objects):
current_frame = self.frame_manager.get(f"{self.camera_name}{frame_time}") current_frame = self.frame_manager.get(f"{self.camera_name}{frame_time}", self.frame_shape)
# draw the bounding boxes on the frame # draw the bounding boxes on the frame
for obj in tracked_objects: for obj in tracked_objects:
thickness = 2 thickness = 2
@ -132,6 +135,7 @@ def process(path, label, threshold, debug_path):
results = [] results = []
for c in clips: for c in clips:
frame_shape = get_frame_shape(c) frame_shape = get_frame_shape(c)
config['frame_shape'] = frame_shape
process_clip = ProcessClip(c, frame_shape, config) process_clip = ProcessClip(c, frame_shape, config)
process_clip.load_frames() process_clip.load_frames()
process_clip.process_frames(objects_to_track=config['objects']['track']) process_clip.process_frames(objects_to_track=config['objects']['track'])