mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
Refactor with a working false positive test
This commit is contained in:
parent
a8556a729b
commit
ea4ecae27c
@ -3,7 +3,7 @@ from statistics import mean
|
|||||||
import multiprocessing as mp
|
import multiprocessing as mp
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import datetime
|
import datetime
|
||||||
from frigate.edgetpu import ObjectDetector, EdgeTPUProcess, RemoteObjectDetector, load_labels
|
from frigate.edgetpu import LocalObjectDetector, EdgeTPUProcess, RemoteObjectDetector, load_labels
|
||||||
|
|
||||||
my_frame = np.expand_dims(np.full((300,300,3), 1, np.uint8), axis=0)
|
my_frame = np.expand_dims(np.full((300,300,3), 1, np.uint8), axis=0)
|
||||||
labels = load_labels('/labelmap.txt')
|
labels = load_labels('/labelmap.txt')
|
||||||
|
@ -262,7 +262,7 @@ def main():
|
|||||||
camera_process = mp.Process(target=track_camera, args=(name, config, GLOBAL_OBJECT_CONFIG, frame_queue, frame_shape,
|
camera_process = mp.Process(target=track_camera, args=(name, config, GLOBAL_OBJECT_CONFIG, frame_queue, frame_shape,
|
||||||
tflite_process.detection_queue, tracked_objects_queue, camera_processes[name]['process_fps'],
|
tflite_process.detection_queue, 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']))
|
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
|
||||||
|
|
||||||
|
0
frigate/__init__.py
Normal file
0
frigate/__init__.py
Normal file
@ -2,6 +2,7 @@ import os
|
|||||||
import datetime
|
import datetime
|
||||||
import hashlib
|
import hashlib
|
||||||
import multiprocessing as mp
|
import multiprocessing as mp
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pyarrow.plasma as plasma
|
import pyarrow.plasma as plasma
|
||||||
import tflite_runtime.interpreter as tflite
|
import tflite_runtime.interpreter as tflite
|
||||||
@ -27,8 +28,18 @@ def load_labels(path, encoding='utf-8'):
|
|||||||
else:
|
else:
|
||||||
return {index: line.strip() for index, line in enumerate(lines)}
|
return {index: line.strip() for index, line in enumerate(lines)}
|
||||||
|
|
||||||
class ObjectDetector():
|
class ObjectDetector(ABC):
|
||||||
def __init__(self):
|
@abstractmethod
|
||||||
|
def detect(self, tensor_input, threshold = .4):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class LocalObjectDetector(ObjectDetector):
|
||||||
|
def __init__(self, labels=None):
|
||||||
|
if labels is None:
|
||||||
|
self.labels = {}
|
||||||
|
else:
|
||||||
|
self.labels = load_labels(labels)
|
||||||
|
|
||||||
edge_tpu_delegate = None
|
edge_tpu_delegate = None
|
||||||
try:
|
try:
|
||||||
edge_tpu_delegate = load_delegate('libedgetpu.so.1.0', {"device": "usb"})
|
edge_tpu_delegate = load_delegate('libedgetpu.so.1.0', {"device": "usb"})
|
||||||
@ -53,6 +64,21 @@ class ObjectDetector():
|
|||||||
self.tensor_input_details = self.interpreter.get_input_details()
|
self.tensor_input_details = self.interpreter.get_input_details()
|
||||||
self.tensor_output_details = self.interpreter.get_output_details()
|
self.tensor_output_details = self.interpreter.get_output_details()
|
||||||
|
|
||||||
|
def detect(self, tensor_input, threshold=.4):
|
||||||
|
detections = []
|
||||||
|
|
||||||
|
raw_detections = self.detect_raw(tensor_input)
|
||||||
|
|
||||||
|
for d in raw_detections:
|
||||||
|
if d[1] < threshold:
|
||||||
|
break
|
||||||
|
detections.append((
|
||||||
|
self.labels[int(d[0])],
|
||||||
|
float(d[1]),
|
||||||
|
(d[2], d[3], d[4], d[5])
|
||||||
|
))
|
||||||
|
return detections
|
||||||
|
|
||||||
def detect_raw(self, tensor_input):
|
def detect_raw(self, tensor_input):
|
||||||
self.interpreter.set_tensor(self.tensor_input_details[0]['index'], tensor_input)
|
self.interpreter.set_tensor(self.tensor_input_details[0]['index'], tensor_input)
|
||||||
self.interpreter.invoke()
|
self.interpreter.invoke()
|
||||||
@ -70,7 +96,7 @@ def run_detector(detection_queue, avg_speed, start):
|
|||||||
print(f"Starting detection process: {os.getpid()}")
|
print(f"Starting detection process: {os.getpid()}")
|
||||||
listen()
|
listen()
|
||||||
plasma_client = plasma.connect("/tmp/plasma")
|
plasma_client = plasma.connect("/tmp/plasma")
|
||||||
object_detector = ObjectDetector()
|
object_detector = LocalObjectDetector()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
object_id_str = detection_queue.get()
|
object_id_str = detection_queue.get()
|
||||||
|
@ -11,7 +11,7 @@ from collections import Counter, defaultdict
|
|||||||
import itertools
|
import itertools
|
||||||
import pyarrow.plasma as plasma
|
import pyarrow.plasma as plasma
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
from frigate.util import draw_box_with_label, PlasmaManager
|
from frigate.util import draw_box_with_label, PlasmaFrameManager
|
||||||
from frigate.edgetpu import load_labels
|
from frigate.edgetpu import load_labels
|
||||||
|
|
||||||
PATH_TO_LABELS = '/labelmap.txt'
|
PATH_TO_LABELS = '/labelmap.txt'
|
||||||
@ -91,7 +91,7 @@ class TrackedObjectProcessor(threading.Thread):
|
|||||||
for i, zone in enumerate(self.zone_data.values()):
|
for i, zone in enumerate(self.zone_data.values()):
|
||||||
zone['color'] = tuple(int(round(255 * c)) for c in colors(i)[:3])
|
zone['color'] = tuple(int(round(255 * c)) for c in colors(i)[:3])
|
||||||
|
|
||||||
self.plasma_client = PlasmaManager(self.stop_event)
|
self.plasma_client = PlasmaFrameManager(self.stop_event)
|
||||||
|
|
||||||
def get_best(self, camera, label):
|
def get_best(self, camera, label):
|
||||||
if label in self.camera_data[camera]['best_objects']:
|
if label in self.camera_data[camera]['best_objects']:
|
||||||
|
0
frigate/test/__init__.py
Normal file
0
frigate/test/__init__.py
Normal file
71
frigate/test/test_false_positives.py
Normal file
71
frigate/test/test_false_positives.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import datetime
|
||||||
|
from unittest import TestCase, main
|
||||||
|
from frigate.video import process_frames, start_or_restart_ffmpeg, capture_frames
|
||||||
|
from frigate.util import DictFrameManager, EventsPerSecond, draw_box_with_label
|
||||||
|
from frigate.motion import MotionDetector
|
||||||
|
from frigate.edgetpu import LocalObjectDetector
|
||||||
|
from frigate.objects import ObjectTracker
|
||||||
|
import multiprocessing as mp
|
||||||
|
import numpy as np
|
||||||
|
import cv2
|
||||||
|
from frigate.object_processing import COLOR_MAP
|
||||||
|
|
||||||
|
class FalsePositiveTests(TestCase):
|
||||||
|
|
||||||
|
def test_back_1594395958_675351_0(self):
|
||||||
|
### load in frames
|
||||||
|
frame_shape = (1080,1920,3)
|
||||||
|
frame_manager = DictFrameManager()
|
||||||
|
frame_queue = mp.Queue()
|
||||||
|
fps = EventsPerSecond()
|
||||||
|
skipped_fps = EventsPerSecond()
|
||||||
|
stop_event = mp.Event()
|
||||||
|
detection_frame = mp.Value('d', datetime.datetime.now().timestamp()+100000)
|
||||||
|
ffmpeg_cmd = "ffmpeg -hide_banner -loglevel panic -i /debug/false_positives/back-1595647759.228381-0.mp4 -f rawvideo -pix_fmt rgb24 pipe:".split(" ")
|
||||||
|
ffmpeg_process = start_or_restart_ffmpeg(ffmpeg_cmd, frame_shape[0]*frame_shape[1]*frame_shape[2])
|
||||||
|
capture_frames(ffmpeg_process, "back", frame_shape, frame_manager, frame_queue, 1, fps, skipped_fps, stop_event, detection_frame)
|
||||||
|
ffmpeg_process.wait()
|
||||||
|
ffmpeg_process.communicate()
|
||||||
|
assert(frame_queue.qsize() > 0)
|
||||||
|
|
||||||
|
### process frames
|
||||||
|
mask = np.zeros((frame_shape[0], frame_shape[1], 1), np.uint8)
|
||||||
|
mask[:] = 255
|
||||||
|
motion_detector = MotionDetector(frame_shape, mask)
|
||||||
|
|
||||||
|
object_detector = LocalObjectDetector(labels='/labelmap.txt')
|
||||||
|
object_tracker = ObjectTracker(10)
|
||||||
|
detected_objects_queue = mp.Queue()
|
||||||
|
process_fps = EventsPerSecond()
|
||||||
|
current_frame = mp.Value('d', 0.0)
|
||||||
|
|
||||||
|
process_frames("back", frame_queue, frame_shape, frame_manager, motion_detector, object_detector, object_tracker, detected_objects_queue,
|
||||||
|
process_fps, current_frame, ['person'], {}, mask, stop_event, exit_on_empty=True)
|
||||||
|
assert(detected_objects_queue.qsize() > 0)
|
||||||
|
|
||||||
|
### check result
|
||||||
|
while(not detected_objects_queue.empty()):
|
||||||
|
camera_name, frame_time, current_tracked_objects = detected_objects_queue.get()
|
||||||
|
|
||||||
|
current_frame = frame_manager.get(f"{camera_name}{frame_time}")
|
||||||
|
# draw the bounding boxes on the frame
|
||||||
|
for obj in current_tracked_objects.values():
|
||||||
|
thickness = 2
|
||||||
|
color = COLOR_MAP[obj['label']]
|
||||||
|
|
||||||
|
if obj['frame_time'] != frame_time:
|
||||||
|
thickness = 1
|
||||||
|
color = (255,0,0)
|
||||||
|
|
||||||
|
# draw the bounding boxes on the frame
|
||||||
|
box = obj['box']
|
||||||
|
draw_box_with_label(current_frame, box[0], box[1], box[2], box[3], obj['label'], f"{int(obj['score']*100)}% {int(obj['area'])}", thickness=thickness, color=color)
|
||||||
|
# draw the regions on the frame
|
||||||
|
region = obj['region']
|
||||||
|
draw_box_with_label(current_frame, region[0], region[1], region[2], region[3], 'region', f"{region[2]-region[0]}", thickness=1, color=(0,255,0))
|
||||||
|
|
||||||
|
cv2.imwrite(f"/debug/frames/{int(frame_time*1000000)}.jpg", cv2.cvtColor(current_frame, cv2.COLOR_RGB2BGR))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -1,3 +1,4 @@
|
|||||||
|
from abc import ABC, abstractmethod
|
||||||
import datetime
|
import datetime
|
||||||
import time
|
import time
|
||||||
import signal
|
import signal
|
||||||
@ -139,7 +140,33 @@ def print_stack(sig, frame):
|
|||||||
def listen():
|
def listen():
|
||||||
signal.signal(signal.SIGUSR1, print_stack)
|
signal.signal(signal.SIGUSR1, print_stack)
|
||||||
|
|
||||||
class PlasmaManager:
|
class FrameManager(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def get(self, name, timeout_ms=0):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def put(self, name, frame):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def delete(self, name):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class DictFrameManager(FrameManager):
|
||||||
|
def __init__(self):
|
||||||
|
self.frames = {}
|
||||||
|
|
||||||
|
def get(self, name, timeout_ms=0):
|
||||||
|
return self.frames.get(name)
|
||||||
|
|
||||||
|
def put(self, name, frame):
|
||||||
|
self.frames[name] = frame
|
||||||
|
|
||||||
|
def delete(self, name):
|
||||||
|
del self.frames[name]
|
||||||
|
|
||||||
|
class PlasmaFrameManager(FrameManager):
|
||||||
def __init__(self, stop_event=None):
|
def __init__(self, stop_event=None):
|
||||||
self.stop_event = stop_event
|
self.stop_event = stop_event
|
||||||
self.connect()
|
self.connect()
|
||||||
@ -161,18 +188,21 @@ class PlasmaManager:
|
|||||||
if self.stop_event != None and self.stop_event.is_set():
|
if self.stop_event != None and self.stop_event.is_set():
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
return self.plasma_client.get(object_id, timeout_ms=timeout_ms)
|
frame = self.plasma_client.get(object_id, timeout_ms=timeout_ms)
|
||||||
|
if frame is plasma.ObjectNotAvailable:
|
||||||
|
return None
|
||||||
|
return frame
|
||||||
except:
|
except:
|
||||||
self.connect()
|
self.connect()
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
def put(self, name, obj):
|
def put(self, name, frame):
|
||||||
object_id = plasma.ObjectID(hashlib.sha1(str.encode(name)).digest())
|
object_id = plasma.ObjectID(hashlib.sha1(str.encode(name)).digest())
|
||||||
while True:
|
while True:
|
||||||
if self.stop_event != None and self.stop_event.is_set():
|
if self.stop_event != None and self.stop_event.is_set():
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
self.plasma_client.put(obj, object_id)
|
self.plasma_client.put(frame, object_id)
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to put in plasma: {e}")
|
print(f"Failed to put in plasma: {e}")
|
||||||
|
291
frigate/video.py
291
frigate/video.py
@ -13,8 +13,9 @@ import copy
|
|||||||
import itertools
|
import itertools
|
||||||
import json
|
import json
|
||||||
import base64
|
import base64
|
||||||
|
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, PlasmaManager
|
from frigate.util import draw_box_with_label, area, calculate_region, clipped, intersection_over_union, intersection, EventsPerSecond, listen, FrameManager, PlasmaFrameManager
|
||||||
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
|
||||||
@ -53,7 +54,7 @@ def get_ffmpeg_input(ffmpeg_input):
|
|||||||
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_')}
|
||||||
return ffmpeg_input.format(**frigate_vars)
|
return ffmpeg_input.format(**frigate_vars)
|
||||||
|
|
||||||
def filtered(obj, objects_to_track, object_filters, mask):
|
def filtered(obj, objects_to_track, object_filters, mask=None):
|
||||||
object_name = obj[0]
|
object_name = obj[0]
|
||||||
|
|
||||||
if not object_name in objects_to_track:
|
if not object_name in objects_to_track:
|
||||||
@ -82,7 +83,7 @@ def filtered(obj, objects_to_track, object_filters, mask):
|
|||||||
x_location = min(int((obj[2][2]-obj[2][0])/2.0)+obj[2][0], len(mask[0])-1)
|
x_location = min(int((obj[2][2]-obj[2][0])/2.0)+obj[2][0], len(mask[0])-1)
|
||||||
|
|
||||||
# if the object is in a masked location, don't add it to detected objects
|
# if the object is in a masked location, don't add it to detected objects
|
||||||
if mask[y_location][x_location] == [0]:
|
if mask != None and mask[y_location][x_location] == [0]:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
@ -115,6 +116,53 @@ def start_or_restart_ffmpeg(ffmpeg_cmd, frame_size, ffmpeg_process=None):
|
|||||||
process = sp.Popen(ffmpeg_cmd, stdout = sp.PIPE, stdin = sp.DEVNULL, bufsize=frame_size*10, start_new_session=True)
|
process = sp.Popen(ffmpeg_cmd, stdout = sp.PIPE, stdin = sp.DEVNULL, bufsize=frame_size*10, start_new_session=True)
|
||||||
return process
|
return process
|
||||||
|
|
||||||
|
def capture_frames(ffmpeg_process, camera_name, frame_shape, frame_manager: FrameManager,
|
||||||
|
frame_queue, take_frame: int, fps:EventsPerSecond, skipped_fps: EventsPerSecond,
|
||||||
|
stop_event: mp.Event, detection_frame: mp.Value):
|
||||||
|
|
||||||
|
frame_num = 0
|
||||||
|
last_frame = 0
|
||||||
|
frame_size = frame_shape[0] * frame_shape[1] * frame_shape[2]
|
||||||
|
skipped_fps.start()
|
||||||
|
while True:
|
||||||
|
if stop_event.is_set():
|
||||||
|
print(f"{camera_name}: stop event set. exiting capture thread...")
|
||||||
|
break
|
||||||
|
|
||||||
|
frame_bytes = ffmpeg_process.stdout.read(frame_size)
|
||||||
|
current_frame = datetime.datetime.now().timestamp()
|
||||||
|
|
||||||
|
if len(frame_bytes) == 0:
|
||||||
|
print(f"{camera_name}: ffmpeg didnt return a frame. something is wrong.")
|
||||||
|
|
||||||
|
if ffmpeg_process.poll() != None:
|
||||||
|
print(f"{camera_name}: ffmpeg process is not running. exiting capture thread...")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
fps.update()
|
||||||
|
|
||||||
|
frame_num += 1
|
||||||
|
if (frame_num % take_frame) != 0:
|
||||||
|
skipped_fps.update()
|
||||||
|
continue
|
||||||
|
|
||||||
|
# if the detection process is more than 1 second behind, skip this frame
|
||||||
|
if detection_frame.value > 0.0 and (last_frame - detection_frame.value) > 1:
|
||||||
|
skipped_fps.update()
|
||||||
|
continue
|
||||||
|
|
||||||
|
# put the frame in the frame manager
|
||||||
|
frame_manager.put(f"{camera_name}{current_frame}",
|
||||||
|
np
|
||||||
|
.frombuffer(frame_bytes, np.uint8)
|
||||||
|
.reshape(frame_shape)
|
||||||
|
)
|
||||||
|
# add to the queue
|
||||||
|
frame_queue.put(current_frame)
|
||||||
|
last_frame = current_frame
|
||||||
|
|
||||||
class CameraCapture(threading.Thread):
|
class CameraCapture(threading.Thread):
|
||||||
def __init__(self, name, ffmpeg_process, frame_shape, frame_queue, take_frame, fps, detection_frame, stop_event):
|
def __init__(self, name, ffmpeg_process, frame_shape, frame_queue, take_frame, fps, detection_frame, stop_event):
|
||||||
threading.Thread.__init__(self)
|
threading.Thread.__init__(self)
|
||||||
@ -125,7 +173,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 = PlasmaManager(stop_event)
|
self.plasma_client = PlasmaFrameManager(stop_event)
|
||||||
self.ffmpeg_process = ffmpeg_process
|
self.ffmpeg_process = ffmpeg_process
|
||||||
self.current_frame = 0
|
self.current_frame = 0
|
||||||
self.last_frame = 0
|
self.last_frame = 0
|
||||||
@ -133,47 +181,11 @@ class CameraCapture(threading.Thread):
|
|||||||
self.stop_event = stop_event
|
self.stop_event = stop_event
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
frame_num = 0
|
|
||||||
self.skipped_fps.start()
|
self.skipped_fps.start()
|
||||||
while True:
|
capture_frames(self.ffmpeg_process, self.name, self.frame_shape, self.plasma_client, self.frame_queue, self.take_frame,
|
||||||
if self.stop_event.is_set():
|
self.fps, self.skipped_fps, self.stop_event, self.detection_frame)
|
||||||
print(f"{self.name}: stop event set. exiting capture thread...")
|
|
||||||
break
|
|
||||||
|
|
||||||
if self.ffmpeg_process.poll() != None:
|
def track_camera(name, config, global_objects_config, frame_queue, frame_shape, detection_queue, detected_objects_queue, fps, detection_fps, read_start, detection_frame, stop_event):
|
||||||
print(f"{self.name}: ffmpeg process is not running. exiting capture thread...")
|
|
||||||
break
|
|
||||||
|
|
||||||
frame_bytes = self.ffmpeg_process.stdout.read(self.frame_size)
|
|
||||||
self.current_frame = datetime.datetime.now().timestamp()
|
|
||||||
|
|
||||||
if len(frame_bytes) == 0:
|
|
||||||
print(f"{self.name}: ffmpeg didnt return a frame. something is wrong.")
|
|
||||||
continue
|
|
||||||
|
|
||||||
self.fps.update()
|
|
||||||
|
|
||||||
frame_num += 1
|
|
||||||
if (frame_num % self.take_frame) != 0:
|
|
||||||
self.skipped_fps.update()
|
|
||||||
continue
|
|
||||||
|
|
||||||
# if the detection process is more than 1 second behind, skip this frame
|
|
||||||
if self.detection_frame.value > 0.0 and (self.last_frame - self.detection_frame.value) > 1:
|
|
||||||
self.skipped_fps.update()
|
|
||||||
continue
|
|
||||||
|
|
||||||
# put the frame in the plasma store
|
|
||||||
self.plasma_client.put(f"{self.name}{self.current_frame}",
|
|
||||||
np
|
|
||||||
.frombuffer(frame_bytes, np.uint8)
|
|
||||||
.reshape(self.frame_shape)
|
|
||||||
)
|
|
||||||
# add to the queue
|
|
||||||
self.frame_queue.put(self.current_frame)
|
|
||||||
self.last_frame = self.current_frame
|
|
||||||
|
|
||||||
def track_camera(name, config, global_objects_config, frame_queue, frame_shape, detection_queue, detected_objects_queue, fps, detection_fps, read_start, detection_frame):
|
|
||||||
print(f"Starting process for {name}: {os.getpid()}")
|
print(f"Starting process for {name}: {os.getpid()}")
|
||||||
listen()
|
listen()
|
||||||
|
|
||||||
@ -191,8 +203,6 @@ def track_camera(name, config, global_objects_config, frame_queue, frame_shape,
|
|||||||
for obj in objects_with_config:
|
for obj in objects_with_config:
|
||||||
object_filters[obj] = {**global_object_filters.get(obj, {}), **camera_object_filters.get(obj, {})}
|
object_filters[obj] = {**global_object_filters.get(obj, {}), **camera_object_filters.get(obj, {})}
|
||||||
|
|
||||||
frame = np.zeros(frame_shape, np.uint8)
|
|
||||||
|
|
||||||
# load in the mask for object detection
|
# load in the mask for object detection
|
||||||
if 'mask' in config:
|
if 'mask' in config:
|
||||||
if config['mask'].startswith('base64,'):
|
if config['mask'].startswith('base64,'):
|
||||||
@ -213,109 +223,96 @@ def track_camera(name, config, global_objects_config, frame_queue, frame_shape,
|
|||||||
|
|
||||||
object_tracker = ObjectTracker(10)
|
object_tracker = ObjectTracker(10)
|
||||||
|
|
||||||
plasma_client = PlasmaManager()
|
plasma_client = PlasmaFrameManager()
|
||||||
avg_wait = 0.0
|
|
||||||
|
process_frames(name, frame_queue, frame_shape, plasma_client, motion_detector, object_detector,
|
||||||
|
object_tracker, detected_objects_queue, fps, detection_frame, objects_to_track, object_filters, mask, stop_event)
|
||||||
|
|
||||||
|
print(f"{name}: exiting subprocess")
|
||||||
|
|
||||||
|
def reduce_boxes(boxes):
|
||||||
|
if len(boxes) == 0:
|
||||||
|
return []
|
||||||
|
reduced_boxes = cv2.groupRectangles([list(b) for b in itertools.chain(boxes, boxes)], 1, 0.2)[0]
|
||||||
|
return [tuple(b) for b in reduced_boxes]
|
||||||
|
|
||||||
|
def detect(object_detector, frame, region, objects_to_track, object_filters, mask):
|
||||||
|
tensor_input = create_tensor_input(frame, region)
|
||||||
|
|
||||||
|
detections = []
|
||||||
|
region_detections = object_detector.detect(tensor_input)
|
||||||
|
for d in region_detections:
|
||||||
|
box = d[2]
|
||||||
|
size = region[2]-region[0]
|
||||||
|
x_min = int((box[1] * size) + region[0])
|
||||||
|
y_min = int((box[0] * size) + region[1])
|
||||||
|
x_max = int((box[3] * size) + region[0])
|
||||||
|
y_max = int((box[2] * size) + region[1])
|
||||||
|
det = (d[0],
|
||||||
|
d[1],
|
||||||
|
(x_min, y_min, x_max, y_max),
|
||||||
|
(x_max-x_min)*(y_max-y_min),
|
||||||
|
region)
|
||||||
|
# apply object filters
|
||||||
|
if filtered(det, objects_to_track, object_filters, mask):
|
||||||
|
continue
|
||||||
|
detections.append(det)
|
||||||
|
return detections
|
||||||
|
|
||||||
|
def process_frames(camera_name: str, frame_queue: mp.Queue, frame_shape,
|
||||||
|
frame_manager: FrameManager, motion_detector: MotionDetector,
|
||||||
|
object_detector: RemoteObjectDetector, object_tracker: ObjectTracker,
|
||||||
|
detected_objects_queue: mp.Queue, fps: mp.Value, current_frame_time: mp.Value,
|
||||||
|
objects_to_track: List[str], object_filters: Dict, mask, stop_event: mp.Event,
|
||||||
|
exit_on_empty: bool = False):
|
||||||
|
|
||||||
fps_tracker = EventsPerSecond()
|
fps_tracker = EventsPerSecond()
|
||||||
fps_tracker.start()
|
fps_tracker.start()
|
||||||
object_detector.fps.start()
|
|
||||||
while True:
|
|
||||||
read_start.value = datetime.datetime.now().timestamp()
|
|
||||||
frame_time = frame_queue.get()
|
|
||||||
duration = datetime.datetime.now().timestamp()-read_start.value
|
|
||||||
read_start.value = 0.0
|
|
||||||
avg_wait = (avg_wait*99+duration)/100
|
|
||||||
detection_frame.value = frame_time
|
|
||||||
|
|
||||||
# Get frame from plasma store
|
|
||||||
frame = plasma_client.get(f"{name}{frame_time}")
|
|
||||||
|
|
||||||
if frame is plasma.ObjectNotAvailable:
|
while True:
|
||||||
|
if stop_event.is_set() or (exit_on_empty and frame_queue.empty()):
|
||||||
|
print(f"Exiting track_objects...")
|
||||||
|
break
|
||||||
|
|
||||||
|
try:
|
||||||
|
frame_time = frame_queue.get(True, 10)
|
||||||
|
except queue.Empty:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
current_frame_time.value = frame_time
|
||||||
|
|
||||||
|
frame = frame_manager.get(f"{camera_name}{frame_time}")
|
||||||
|
|
||||||
fps_tracker.update()
|
fps_tracker.update()
|
||||||
fps.value = fps_tracker.eps()
|
fps.value = fps_tracker.eps()
|
||||||
detection_fps.value = object_detector.fps.eps()
|
|
||||||
|
|
||||||
# look for motion
|
# look for motion
|
||||||
motion_boxes = motion_detector.detect(frame)
|
motion_boxes = motion_detector.detect(frame)
|
||||||
|
|
||||||
tracked_objects = object_tracker.tracked_objects.values()
|
tracked_object_boxes = [obj['box'] for obj in object_tracker.tracked_objects.values()]
|
||||||
|
|
||||||
# merge areas of motion that intersect with a known tracked object into a single area to look at
|
# combine motion boxes with known locations of existing objects
|
||||||
areas_of_interest = []
|
combined_boxes = reduce_boxes(motion_boxes + tracked_object_boxes)
|
||||||
used_motion_boxes = []
|
|
||||||
for obj in tracked_objects:
|
|
||||||
x_min, y_min, x_max, y_max = obj['box']
|
|
||||||
for m_index, motion_box in enumerate(motion_boxes):
|
|
||||||
if intersection_over_union(motion_box, obj['box']) > .2:
|
|
||||||
used_motion_boxes.append(m_index)
|
|
||||||
x_min = min(obj['box'][0], motion_box[0])
|
|
||||||
y_min = min(obj['box'][1], motion_box[1])
|
|
||||||
x_max = max(obj['box'][2], motion_box[2])
|
|
||||||
y_max = max(obj['box'][3], motion_box[3])
|
|
||||||
areas_of_interest.append((x_min, y_min, x_max, y_max))
|
|
||||||
unused_motion_boxes = set(range(0, len(motion_boxes))).difference(used_motion_boxes)
|
|
||||||
|
|
||||||
# compute motion regions
|
|
||||||
motion_regions = [calculate_region(frame_shape, motion_boxes[i][0], motion_boxes[i][1], motion_boxes[i][2], motion_boxes[i][3], 1.2)
|
|
||||||
for i in unused_motion_boxes]
|
|
||||||
|
|
||||||
# compute tracked object regions
|
|
||||||
object_regions = [calculate_region(frame_shape, a[0], a[1], a[2], a[3], 1.2)
|
|
||||||
for a in areas_of_interest]
|
|
||||||
|
|
||||||
# merge regions with high IOU
|
|
||||||
merged_regions = motion_regions+object_regions
|
|
||||||
while True:
|
|
||||||
max_iou = 0.0
|
|
||||||
max_indices = None
|
|
||||||
region_indices = range(len(merged_regions))
|
|
||||||
for a, b in itertools.combinations(region_indices, 2):
|
|
||||||
iou = intersection_over_union(merged_regions[a], merged_regions[b])
|
|
||||||
if iou > max_iou:
|
|
||||||
max_iou = iou
|
|
||||||
max_indices = (a, b)
|
|
||||||
if max_iou > 0.1:
|
|
||||||
a = merged_regions[max_indices[0]]
|
|
||||||
b = merged_regions[max_indices[1]]
|
|
||||||
merged_regions.append(calculate_region(frame_shape,
|
|
||||||
min(a[0], b[0]),
|
|
||||||
min(a[1], b[1]),
|
|
||||||
max(a[2], b[2]),
|
|
||||||
max(a[3], b[3]),
|
|
||||||
1
|
|
||||||
))
|
|
||||||
del merged_regions[max(max_indices[0], max_indices[1])]
|
|
||||||
del merged_regions[min(max_indices[0], max_indices[1])]
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
|
|
||||||
|
# compute regions
|
||||||
|
regions = [calculate_region(frame_shape, a[0], a[1], a[2], a[3], 1.2)
|
||||||
|
for a in combined_boxes]
|
||||||
|
|
||||||
|
# combine overlapping regions
|
||||||
|
combined_regions = reduce_boxes(regions)
|
||||||
|
|
||||||
|
# re-compute regions
|
||||||
|
regions = [calculate_region(frame_shape, a[0], a[1], a[2], a[3], 1.0)
|
||||||
|
for a in combined_regions]
|
||||||
|
|
||||||
# resize regions and detect
|
# resize regions and detect
|
||||||
detections = []
|
detections = []
|
||||||
for region in merged_regions:
|
for region in regions:
|
||||||
|
detections.extend(detect(object_detector, frame, region, objects_to_track, object_filters, mask))
|
||||||
tensor_input = create_tensor_input(frame, region)
|
|
||||||
|
|
||||||
region_detections = object_detector.detect(tensor_input)
|
|
||||||
|
|
||||||
for d in region_detections:
|
|
||||||
box = d[2]
|
|
||||||
size = region[2]-region[0]
|
|
||||||
x_min = int((box[1] * size) + region[0])
|
|
||||||
y_min = int((box[0] * size) + region[1])
|
|
||||||
x_max = int((box[3] * size) + region[0])
|
|
||||||
y_max = int((box[2] * size) + region[1])
|
|
||||||
det = (d[0],
|
|
||||||
d[1],
|
|
||||||
(x_min, y_min, x_max, y_max),
|
|
||||||
(x_max-x_min)*(y_max-y_min),
|
|
||||||
region)
|
|
||||||
if filtered(det, objects_to_track, object_filters, mask):
|
|
||||||
continue
|
|
||||||
detections.append(det)
|
|
||||||
|
|
||||||
#########
|
#########
|
||||||
# merge objects, check for clipped objects and look again up to N times
|
# merge objects, check for clipped objects and look again up to 4 times
|
||||||
#########
|
#########
|
||||||
refining = True
|
refining = True
|
||||||
refine_count = 0
|
refine_count = 0
|
||||||
@ -345,40 +342,20 @@ def track_camera(name, config, global_objects_config, frame_queue, frame_shape,
|
|||||||
box[0], box[1],
|
box[0], box[1],
|
||||||
box[2], box[3])
|
box[2], box[3])
|
||||||
|
|
||||||
tensor_input = create_tensor_input(frame, region)
|
selected_objects.extend(detect(object_detector, frame, region, objects_to_track, object_filters, mask))
|
||||||
# run detection on new region
|
|
||||||
refined_detections = object_detector.detect(tensor_input)
|
|
||||||
for d in refined_detections:
|
|
||||||
box = d[2]
|
|
||||||
size = region[2]-region[0]
|
|
||||||
x_min = int((box[1] * size) + region[0])
|
|
||||||
y_min = int((box[0] * size) + region[1])
|
|
||||||
x_max = int((box[3] * size) + region[0])
|
|
||||||
y_max = int((box[2] * size) + region[1])
|
|
||||||
det = (d[0],
|
|
||||||
d[1],
|
|
||||||
(x_min, y_min, x_max, y_max),
|
|
||||||
(x_max-x_min)*(y_max-y_min),
|
|
||||||
region)
|
|
||||||
if filtered(det, objects_to_track, object_filters, mask):
|
|
||||||
continue
|
|
||||||
selected_objects.append(det)
|
|
||||||
|
|
||||||
refining = True
|
refining = True
|
||||||
else:
|
else:
|
||||||
selected_objects.append(obj)
|
selected_objects.append(obj)
|
||||||
|
|
||||||
# set the detections list to only include top, complete objects
|
# set the detections list to only include top, complete objects
|
||||||
# and new detections
|
# and new detections
|
||||||
detections = selected_objects
|
detections = selected_objects
|
||||||
|
|
||||||
if refining:
|
if refining:
|
||||||
refine_count += 1
|
refine_count += 1
|
||||||
|
|
||||||
# now that we have refined our detections, we need to track objects
|
# now that we have refined our detections, we need to track objects
|
||||||
object_tracker.match_and_update(frame_time, detections)
|
object_tracker.match_and_update(frame_time, detections)
|
||||||
|
|
||||||
# add to the queue
|
# add to the queue
|
||||||
detected_objects_queue.put((name, frame_time, object_tracker.tracked_objects))
|
detected_objects_queue.put((camera_name, frame_time, object_tracker.tracked_objects))
|
||||||
|
|
||||||
print(f"{name}: exiting subprocess")
|
|
||||||
|
Loading…
Reference in New Issue
Block a user