mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +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