A utility class for smoothing detections over multiple frames in video tracking.
It maintains a history of detections for each track and provides smoothed
predictions based on these histories.
Warning
DetectionsSmoother requires the tracker_id for each detection. Refer to
Roboflow Trackers for
information on integrating tracking into your inference pipeline.
This class is not compatible with segmentation models.
classDetectionsSmoother:""" A utility class for smoothing detections over multiple frames in video tracking. It maintains a history of detections for each track and provides smoothed predictions based on these histories. <video controls> <source src="https://media.roboflow.com/supervision-detection-smoothing.mp4" type="video/mp4"> </video> !!! warning - `DetectionsSmoother` requires the `tracker_id` for each detection. Refer to [Roboflow Trackers](/latest/trackers/) for information on integrating tracking into your inference pipeline. - This class is not compatible with segmentation models. Example: ```python import supervision as sv from ultralytics import YOLO video_info = sv.VideoInfo.from_video_path(video_path=<SOURCE_FILE_PATH>) frame_generator = sv.get_video_frames_generator(source_path=<SOURCE_FILE_PATH>) model = YOLO(<MODEL_PATH>) tracker = sv.ByteTrack(frame_rate=video_info.fps) smoother = sv.DetectionsSmoother() annotator = sv.BoundingBoxAnnotator() with sv.VideoSink(<TARGET_FILE_PATH>, video_info=video_info) as sink: for frame in frame_generator: result = model(frame)[0] detections = sv.Detections.from_ultralytics(result) detections = tracker.update_with_detections(detections) detections = smoother.update_with_detections(detections) annotated_frame = bounding_box_annotator.annotate(frame.copy(), detections) sink.write_frame(annotated_frame) ``` """# noqa: E501 // docsdef__init__(self,length:int=5)->None:""" Args: length (int): The maximum number of frames to consider for smoothing detections. Defaults to 5. """self.tracks=defaultdict(lambda:deque(maxlen=length))defupdate_with_detections(self,detections:Detections)->Detections:""" Updates the smoother with a new set of detections from a frame. Args: detections (Detections): The detections to add to the smoother. """ifdetections.tracker_idisNone:print("Smoothing skipped. DetectionsSmoother requires tracker_id. Refer to ""https://supervision.roboflow.com/latest/trackers for more information.")returndetectionsfordetection_idxinrange(len(detections)):tracker_id=detections.tracker_id[detection_idx]iftracker_idisNone:continueself.tracks[tracker_id].append(detections[detection_idx])fortrack_idinself.tracks.keys():iftrack_idnotindetections.tracker_id:self.tracks[track_id].append(None)fortrack_idinlist(self.tracks.keys()):ifall([disNonefordinself.tracks[track_id]]):delself.tracks[track_id]returnself.get_smoothed_detections()defget_track(self,track_id:int)->Optional[Detections]:track=self.tracks.get(track_id,None)iftrackisNone:returnNonetrack=[dfordintrackifdisnotNone]iflen(track)==0:returnNoneret=deepcopy(track[0])ret.xyxy=np.mean([d.xyxyfordintrack],axis=0)ret.confidence=np.mean([d.confidencefordintrack],axis=0)returnretdefget_smoothed_detections(self)->Detections:tracked_detections=[]fortrack_idinself.tracks:track=self.get_track(track_id)iftrackisnotNone:tracked_detections.append(track)detections=Detections.merge(tracked_detections)iflen(detections)==0:detections.tracker_id=np.array([],dtype=int)returndetections
def__init__(self,length:int=5)->None:""" Args: length (int): The maximum number of frames to consider for smoothing detections. Defaults to 5. """self.tracks=defaultdict(lambda:deque(maxlen=length))
defupdate_with_detections(self,detections:Detections)->Detections:""" Updates the smoother with a new set of detections from a frame. Args: detections (Detections): The detections to add to the smoother. """ifdetections.tracker_idisNone:print("Smoothing skipped. DetectionsSmoother requires tracker_id. Refer to ""https://supervision.roboflow.com/latest/trackers for more information.")returndetectionsfordetection_idxinrange(len(detections)):tracker_id=detections.tracker_id[detection_idx]iftracker_idisNone:continueself.tracks[tracker_id].append(detections[detection_idx])fortrack_idinself.tracks.keys():iftrack_idnotindetections.tracker_id:self.tracks[track_id].append(None)fortrack_idinlist(self.tracks.keys()):ifall([disNonefordinself.tracks[track_id]]):delself.tracks[track_id]returnself.get_smoothed_detections()