I am using YOLOv8 with track
in python. I can get the bounding boxes and classes of each object, but the IDs change every time one of the objects is no longer visible. If current object IDs are 1,2,3,4 and number 2 is not detected, YOLO simply decrements 3 and 4, so new IDs are 1,2,3, forcing me to manually detect which one left, which is a pain. I know YOLO can do this for me, as the show=True
method for track
plots the correct IDs even after objects are missed or leave the frame. I have tried looking in documentation but have not been able to find anything. The object returned by invoking track
has methods sort
, and index
, which might be of help, but I don't know how to use them.
Small example:
from ultralytics import YOLO
model = YOLO("yolov8n-seg.pt")
results = model.track(img, conf=0.20, show = True, persist = True, tracker="botsort.yaml", verbose=False )
print(results[0].boxes.data[:,4])
----------------------------------
1,2,3,4
# when 2 is not detected:
1,2,3
check official documentation about persisting tracks loop.
You've got to use model.track frame by frame in loop with persist=True parameter. Furthermore be shure that you don't use stream=True parameter, it will breake your track's id.
so here is code from documentation with my correction about "finished tracks" (the objects that is no longer visible)
from collections import defaultdict
import cv2
import numpy as np
from ultralytics import YOLO
# Load the YOLOv8 model
model = YOLO('yolov8n.pt')
# Open the video file
video_path = "path/to/video.mp4"
cap = cv2.VideoCapture(video_path)
# Store the track history
track_history = defaultdict(lambda: [])
# Loop through the video frames
while cap.isOpened():
# Read a frame from the video
success, frame = cap.read()
if success:
# Run YOLOv8 tracking on the frame, persisting tracks between frames
results = model.track(frame, persist=True)
# Get the boxes and track IDs
boxes = results[0].boxes.xywh.cpu()
track_ids = results[0].boxes.id.int().cpu().tolist()
# Visualize the results on the frame
annotated_frame = results[0].plot()
# Plot the tracks
for box, track_id in zip(boxes, track_ids):
x, y, w, h = box
track = track_history[track_id]
track.append((float(x), float(y))) # x, y center point
if len(track) > 30: # retain 90 tracks for 90 frames
track.pop(0)
# Draw the tracking lines
points = np.hstack(track).astype(np.int32).reshape((-1, 1, 2))
cv2.polylines(annotated_frame, [points], isClosed=False, color=(230, 230, 230), thickness=10)
# get finished tracks and do some logic with them
finished_tracks = track_history.keys() - track_ids
for ft_id in finished_tracks:
ft = track_history.pop(ft_id)
# do some logic with ft here.........
# Display the annotated frame
cv2.imshow("YOLOv8 Tracking", annotated_frame)
# Break the loop if 'q' is pressed
if cv2.waitKey(1) & 0xFF == ord("q"):
break
else:
# Break the loop if the end of the video is reached
break
# Release the video capture object and close the display window
cap.release()
cv2.destroyAllWindows()