mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
Updates to object processing
Lock updates to tracked objects, current frame time, motion boxes, and regions on `update()`. Directly create Counters using counted values. Don't convert removed_ids, new_ids, or updated_ids sets to lists. Update defaultdict's to remove un-necessary lambdas when possible. When possible, drop un-necessay list comprehensions, such as when calling `any`. Use set comprehension, rather than passing a list comprehension into `set()`. Do the slightly more pythonic `x not in y` rather than `not x in y` to check list inclusion.
This commit is contained in:
parent
9634ec8e31
commit
abbc608ee4
@ -91,9 +91,7 @@ class TrackedObject:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
threshold = self.camera_config.objects.filters[self.obj_data["label"]].threshold
|
threshold = self.camera_config.objects.filters[self.obj_data["label"]].threshold
|
||||||
if self.computed_score < threshold:
|
return self.computed_score < threshold
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def compute_score(self):
|
def compute_score(self):
|
||||||
scores = self.score_history[:]
|
scores = self.score_history[:]
|
||||||
@ -156,31 +154,31 @@ class TrackedObject:
|
|||||||
|
|
||||||
def to_dict(self, include_thumbnail: bool = False):
|
def to_dict(self, include_thumbnail: bool = False):
|
||||||
event = {
|
event = {
|
||||||
'id': self.obj_data['id'],
|
"id": self.obj_data["id"],
|
||||||
'camera': self.camera,
|
"camera": self.camera,
|
||||||
'frame_time': self.obj_data['frame_time'],
|
"frame_time": self.obj_data["frame_time"],
|
||||||
'label': self.obj_data['label'],
|
"label": self.obj_data["label"],
|
||||||
'top_score': self.top_score,
|
"top_score": self.top_score,
|
||||||
'false_positive': self.false_positive,
|
"false_positive": self.false_positive,
|
||||||
'start_time': self.obj_data['start_time'],
|
"start_time": self.obj_data["start_time"],
|
||||||
'end_time': self.obj_data.get('end_time', None),
|
"end_time": self.obj_data.get("end_time", None),
|
||||||
'score': self.obj_data['score'],
|
"score": self.obj_data["score"],
|
||||||
'box': self.obj_data['box'],
|
"box": self.obj_data["box"],
|
||||||
'area': self.obj_data['area'],
|
"area": self.obj_data["area"],
|
||||||
'region': self.obj_data['region'],
|
"region": self.obj_data["region"],
|
||||||
'current_zones': self.current_zones.copy(),
|
"current_zones": self.current_zones.copy(),
|
||||||
'entered_zones': list(self.entered_zones).copy(),
|
"entered_zones": list(self.entered_zones).copy(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if include_thumbnail:
|
if include_thumbnail:
|
||||||
event['thumbnail'] = base64.b64encode(self.get_thumbnail()).decode('utf-8')
|
event["thumbnail"] = base64.b64encode(self.get_thumbnail()).decode("utf-8")
|
||||||
|
|
||||||
return event
|
return event
|
||||||
|
|
||||||
def get_thumbnail(self):
|
def get_thumbnail(self):
|
||||||
if (
|
if (
|
||||||
self.thumbnail_data is None
|
self.thumbnail_data is None
|
||||||
or not self.thumbnail_data["frame_time"] in self.frame_cache
|
or self.thumbnail_data["frame_time"] not in self.frame_cache
|
||||||
):
|
):
|
||||||
ret, jpg = cv2.imencode(".jpg", np.zeros((175, 175, 3), np.uint8))
|
ret, jpg = cv2.imencode(".jpg", np.zeros((175, 175, 3), np.uint8))
|
||||||
|
|
||||||
@ -300,17 +298,17 @@ class CameraState:
|
|||||||
self.camera_config = config.cameras[name]
|
self.camera_config = config.cameras[name]
|
||||||
self.frame_manager = frame_manager
|
self.frame_manager = frame_manager
|
||||||
self.best_objects: Dict[str, TrackedObject] = {}
|
self.best_objects: Dict[str, TrackedObject] = {}
|
||||||
self.object_counts = defaultdict(lambda: 0)
|
self.object_counts = defaultdict(int)
|
||||||
self.tracked_objects: Dict[str, TrackedObject] = {}
|
self.tracked_objects: Dict[str, TrackedObject] = {}
|
||||||
self.frame_cache = {}
|
self.frame_cache = {}
|
||||||
self.zone_objects = defaultdict(lambda: [])
|
self.zone_objects = defaultdict(list)
|
||||||
self._current_frame = np.zeros(self.camera_config.frame_shape_yuv, np.uint8)
|
self._current_frame = np.zeros(self.camera_config.frame_shape_yuv, np.uint8)
|
||||||
self.current_frame_lock = threading.Lock()
|
self.current_frame_lock = threading.Lock()
|
||||||
self.current_frame_time = 0.0
|
self.current_frame_time = 0.0
|
||||||
self.motion_boxes = []
|
self.motion_boxes = []
|
||||||
self.regions = []
|
self.regions = []
|
||||||
self.previous_frame_id = None
|
self.previous_frame_id = None
|
||||||
self.callbacks = defaultdict(lambda: [])
|
self.callbacks = defaultdict(list)
|
||||||
|
|
||||||
def get_current_frame(self, draw_options={}):
|
def get_current_frame(self, draw_options={}):
|
||||||
with self.current_frame_lock:
|
with self.current_frame_lock:
|
||||||
@ -325,10 +323,10 @@ class CameraState:
|
|||||||
if draw_options.get("bounding_boxes"):
|
if draw_options.get("bounding_boxes"):
|
||||||
# draw the bounding boxes on the frame
|
# draw the bounding boxes on the frame
|
||||||
for obj in tracked_objects.values():
|
for obj in tracked_objects.values():
|
||||||
thickness = 2
|
if obj["frame_time"] == frame_time:
|
||||||
color = COLOR_MAP[obj["label"]]
|
thickness = 2
|
||||||
|
color = COLOR_MAP[obj["label"]]
|
||||||
if obj["frame_time"] != frame_time:
|
else:
|
||||||
thickness = 1
|
thickness = 1
|
||||||
color = (255, 0, 0)
|
color = (255, 0, 0)
|
||||||
|
|
||||||
@ -341,7 +339,7 @@ class CameraState:
|
|||||||
box[2],
|
box[2],
|
||||||
box[3],
|
box[3],
|
||||||
obj["label"],
|
obj["label"],
|
||||||
f"{int(obj['score']*100)}% {int(obj['area'])}",
|
f"{obj['score']:.0%} {int(obj['area'])}",
|
||||||
thickness=thickness,
|
thickness=thickness,
|
||||||
color=color,
|
color=color,
|
||||||
)
|
)
|
||||||
@ -361,10 +359,7 @@ class CameraState:
|
|||||||
thickness = (
|
thickness = (
|
||||||
8
|
8
|
||||||
if any(
|
if any(
|
||||||
[
|
name in obj["current_zones"] for obj in tracked_objects.values()
|
||||||
name in obj["current_zones"]
|
|
||||||
for obj in tracked_objects.values()
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
else 2
|
else 2
|
||||||
)
|
)
|
||||||
@ -407,23 +402,21 @@ class CameraState:
|
|||||||
self.callbacks[event_type].append(callback)
|
self.callbacks[event_type].append(callback)
|
||||||
|
|
||||||
def update(self, frame_time, current_detections, motion_boxes, regions):
|
def update(self, frame_time, current_detections, motion_boxes, regions):
|
||||||
self.current_frame_time = frame_time
|
|
||||||
self.motion_boxes = motion_boxes
|
|
||||||
self.regions = regions
|
|
||||||
# get the new frame
|
# get the new frame
|
||||||
frame_id = f"{self.name}{frame_time}"
|
frame_id = f"{self.name}{frame_time}"
|
||||||
current_frame = self.frame_manager.get(
|
current_frame = self.frame_manager.get(
|
||||||
frame_id, self.camera_config.frame_shape_yuv
|
frame_id, self.camera_config.frame_shape_yuv
|
||||||
)
|
)
|
||||||
|
|
||||||
current_ids = current_detections.keys()
|
tracked_objects = self.tracked_objects.copy()
|
||||||
previous_ids = self.tracked_objects.keys()
|
current_ids = set(current_detections.keys())
|
||||||
removed_ids = list(set(previous_ids).difference(current_ids))
|
previous_ids = set(tracked_objects.keys())
|
||||||
new_ids = list(set(current_ids).difference(previous_ids))
|
removed_ids = previous_ids.difference(current_ids)
|
||||||
updated_ids = list(set(current_ids).intersection(previous_ids))
|
new_ids = current_ids.difference(previous_ids)
|
||||||
|
updated_ids = current_ids.intersection(previous_ids)
|
||||||
|
|
||||||
for id in new_ids:
|
for id in new_ids:
|
||||||
new_obj = self.tracked_objects[id] = TrackedObject(
|
new_obj = tracked_objects[id] = TrackedObject(
|
||||||
self.name, self.camera_config, self.frame_cache, current_detections[id]
|
self.name, self.camera_config, self.frame_cache, current_detections[id]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -432,7 +425,7 @@ class CameraState:
|
|||||||
c(self.name, new_obj, frame_time)
|
c(self.name, new_obj, frame_time)
|
||||||
|
|
||||||
for id in updated_ids:
|
for id in updated_ids:
|
||||||
updated_obj = self.tracked_objects[id]
|
updated_obj = tracked_objects[id]
|
||||||
significant_update = updated_obj.update(frame_time, current_detections[id])
|
significant_update = updated_obj.update(frame_time, current_detections[id])
|
||||||
|
|
||||||
if significant_update:
|
if significant_update:
|
||||||
@ -458,7 +451,7 @@ class CameraState:
|
|||||||
|
|
||||||
for id in removed_ids:
|
for id in removed_ids:
|
||||||
# publish events to mqtt
|
# publish events to mqtt
|
||||||
removed_obj = self.tracked_objects[id]
|
removed_obj = tracked_objects[id]
|
||||||
if not "end_time" in removed_obj.obj_data:
|
if not "end_time" in removed_obj.obj_data:
|
||||||
removed_obj.obj_data["end_time"] = frame_time
|
removed_obj.obj_data["end_time"] = frame_time
|
||||||
for c in self.callbacks["end"]:
|
for c in self.callbacks["end"]:
|
||||||
@ -466,13 +459,10 @@ class CameraState:
|
|||||||
|
|
||||||
# TODO: can i switch to looking this up and only changing when an event ends?
|
# TODO: can i switch to looking this up and only changing when an event ends?
|
||||||
# maintain best objects
|
# maintain best objects
|
||||||
for obj in self.tracked_objects.values():
|
for obj in tracked_objects.values():
|
||||||
object_type = obj.obj_data["label"]
|
object_type = obj.obj_data["label"]
|
||||||
# if the object's thumbnail is not from the current frame
|
# if the object's thumbnail is not from the current frame
|
||||||
if (
|
if obj.false_positive or obj.thumbnail_data["frame_time"] != frame_time:
|
||||||
obj.false_positive
|
|
||||||
or obj.thumbnail_data["frame_time"] != self.current_frame_time
|
|
||||||
):
|
|
||||||
continue
|
continue
|
||||||
if object_type in self.best_objects:
|
if object_type in self.best_objects:
|
||||||
current_best = self.best_objects[object_type]
|
current_best = self.best_objects[object_type]
|
||||||
@ -497,10 +487,11 @@ class CameraState:
|
|||||||
c(self.name, self.best_objects[object_type], frame_time)
|
c(self.name, self.best_objects[object_type], frame_time)
|
||||||
|
|
||||||
# update overall camera state for each object type
|
# update overall camera state for each object type
|
||||||
obj_counter = Counter()
|
obj_counter = Counter(
|
||||||
for obj in self.tracked_objects.values():
|
obj.obj_data["label"]
|
||||||
if not obj.false_positive:
|
for obj in tracked_objects.values()
|
||||||
obj_counter[obj.obj_data["label"]] += 1
|
if not obj.false_positive
|
||||||
|
)
|
||||||
|
|
||||||
# report on detected objects
|
# report on detected objects
|
||||||
for obj_name, count in obj_counter.items():
|
for obj_name, count in obj_counter.items():
|
||||||
@ -513,7 +504,7 @@ class CameraState:
|
|||||||
expired_objects = [
|
expired_objects = [
|
||||||
obj_name
|
obj_name
|
||||||
for obj_name, count in self.object_counts.items()
|
for obj_name, count in self.object_counts.items()
|
||||||
if count > 0 and not obj_name in obj_counter
|
if count > 0 and obj_name not in obj_counter
|
||||||
]
|
]
|
||||||
for obj_name in expired_objects:
|
for obj_name in expired_objects:
|
||||||
self.object_counts[obj_name] = 0
|
self.object_counts[obj_name] = 0
|
||||||
@ -523,27 +514,29 @@ class CameraState:
|
|||||||
c(self.name, self.best_objects[obj_name], frame_time)
|
c(self.name, self.best_objects[obj_name], frame_time)
|
||||||
|
|
||||||
# cleanup thumbnail frame cache
|
# cleanup thumbnail frame cache
|
||||||
current_thumb_frames = set(
|
current_thumb_frames = {
|
||||||
[
|
obj.thumbnail_data["frame_time"]
|
||||||
obj.thumbnail_data["frame_time"]
|
for obj in tracked_objects.values()
|
||||||
for obj in self.tracked_objects.values()
|
if not obj.false_positive
|
||||||
if not obj.false_positive
|
}
|
||||||
]
|
current_best_frames = {
|
||||||
)
|
obj.thumbnail_data["frame_time"] for obj in self.best_objects.values()
|
||||||
current_best_frames = set(
|
}
|
||||||
[obj.thumbnail_data["frame_time"] for obj in self.best_objects.values()]
|
|
||||||
)
|
|
||||||
thumb_frames_to_delete = [
|
thumb_frames_to_delete = [
|
||||||
t
|
t
|
||||||
for t in self.frame_cache.keys()
|
for t in self.frame_cache.keys()
|
||||||
if not t in current_thumb_frames and not t in current_best_frames
|
if t not in current_thumb_frames and t not in current_best_frames
|
||||||
]
|
]
|
||||||
for t in thumb_frames_to_delete:
|
for t in thumb_frames_to_delete:
|
||||||
del self.frame_cache[t]
|
del self.frame_cache[t]
|
||||||
|
|
||||||
with self.current_frame_lock:
|
with self.current_frame_lock:
|
||||||
|
self.tracked_objects = tracked_objects
|
||||||
|
self.current_frame_time = frame_time
|
||||||
|
self.motion_boxes = motion_boxes
|
||||||
|
self.regions = regions
|
||||||
self._current_frame = current_frame
|
self._current_frame = current_frame
|
||||||
if not self.previous_frame_id is None:
|
if self.previous_frame_id is not 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
|
||||||
|
|
||||||
@ -665,7 +658,7 @@ class TrackedObjectProcessor(threading.Thread):
|
|||||||
# }
|
# }
|
||||||
# }
|
# }
|
||||||
# }
|
# }
|
||||||
self.zone_data = defaultdict(lambda: defaultdict(lambda: {}))
|
self.zone_data = defaultdict(lambda: defaultdict(dict))
|
||||||
|
|
||||||
def should_save_snapshot(self, camera, obj: TrackedObject):
|
def should_save_snapshot(self, camera, obj: TrackedObject):
|
||||||
# if there are required zones and there is no overlap
|
# if there are required zones and there is no overlap
|
||||||
@ -728,15 +721,14 @@ class TrackedObjectProcessor(threading.Thread):
|
|||||||
# for each zone in the current camera
|
# for each zone in the current camera
|
||||||
for zone in self.config.cameras[camera].zones.keys():
|
for zone in self.config.cameras[camera].zones.keys():
|
||||||
# count labels for the camera in the zone
|
# count labels for the camera in the zone
|
||||||
obj_counter = Counter()
|
obj_counter = Counter(
|
||||||
for obj in camera_state.tracked_objects.values():
|
obj.obj_data["label"]
|
||||||
if zone in obj.current_zones and not obj.false_positive:
|
for obj in camera_state.tracked_objects.values()
|
||||||
obj_counter[obj.obj_data["label"]] += 1
|
if zone in obj.current_zones and not obj.false_positive
|
||||||
|
)
|
||||||
|
|
||||||
# update counts and publish status
|
# update counts and publish status
|
||||||
for label in set(
|
for label in set(self.zone_data[zone].keys()) | set(obj_counter.keys()):
|
||||||
list(self.zone_data[zone].keys()) + list(obj_counter.keys())
|
|
||||||
):
|
|
||||||
# if we have previously published a count for this zone/label
|
# if we have previously published a count for this zone/label
|
||||||
zone_label = self.zone_data[zone][label]
|
zone_label = self.zone_data[zone][label]
|
||||||
if camera in zone_label:
|
if camera in zone_label:
|
||||||
|
Loading…
Reference in New Issue
Block a user