Skip to main content

Overview

The TrackDrawer class implements the TrackDrawerInterface to visualize object tracking by drawing motion trails on images. It maintains a history of object centroids and renders connected lines to show movement paths.

Class Definition

TrackDrawer

Draws tracking lines for moving objects by maintaining and visualizing centroid history.
from trash_classificator.drawing.main import TrackDrawer

track_drawer = TrackDrawer()

Attributes

track_history
defaultdict(list)
Dictionary storing the tracking history for each tracked object. Keys are track IDs (integers), and values are lists of centroid coordinates (tuples of floats).Structure:
{
    1: [(x1, y1), (x2, y2), ..., (xn, yn)],
    2: [(x1, y1), (x2, y2), ..., (xn, yn)],
    ...
}
Each track maintains up to 50 historical points.
thickness
int
Line thickness for drawing track trails. Default is 2 pixels.

Methods

draw

Draws tracking lines on an image based on object movement history.
result = track_drawer.draw(image, tracks_ids, boxes)

Parameters

image
np.ndarray
required
Input image in BGR format (OpenCV format). The image will be annotated with tracking lines.
tracks_ids
List[int]
required
List of tracking IDs for detected objects. Each ID uniquely identifies an object across frames.
boxes
np.ndarray
required
Array of bounding boxes in xyxy format, where each box is [x1, y1, x2, y2]:
  • x1, y1: Top-left corner coordinates
  • x2, y2: Bottom-right corner coordinates
Shape: (N, 4) where N is the number of boxes. Each box corresponds to a track ID in tracks_ids.

Returns

result
np.ndarray
The annotated image with:
  • Colored tracking lines connecting historical centroids
  • Lines drawn with 2px thickness
  • Colors determined by track ID for consistent visualization

Centroid Calculation

For each bounding box, the centroid (center point) is calculated as:
centroid_x = (x1 + x2) / 2
centroid_y = (y1 + y2) / 2
centroid = (centroid_x, centroid_y)
Where (x1, y1) is the top-left corner and (x2, y2) is the bottom-right corner of the bounding box.

History Management

The tracker maintains a sliding window of the last 50 centroid positions:
track_line.append(centroid)  # Add new position

if len(track_line) > 50:
    track_line.pop(0)  # Remove oldest position
This ensures:
  • Memory efficiency by limiting history size
  • Smooth visualization of recent movement
  • Automatic cleanup of old tracking data

Track Line Visualization

Lines are drawn connecting consecutive centroid positions:
for i in range(1, len(track_line)):
    cv2.line(image, 
             tuple(map(int, track_line[i - 1])), 
             tuple(map(int, track_line[i])),
             colors(track_id, True), 
             self.thickness)
Each track ID gets a consistent color from Ultralytics’ colors() function.

Usage Example

import cv2
import numpy as np
from trash_classificator.drawing.main import TrackDrawer

# Initialize drawer
track_drawer = TrackDrawer()

# Process video frames
cap = cv2.VideoCapture("trash_video.mp4")

while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break
    
    # Get tracking results from detector
    # tracks_ids: [1, 2, 3]
    # boxes: [[100, 100, 200, 200], [300, 150, 400, 250], [500, 100, 600, 180]]
    
    tracks_ids = [1, 2, 3]
    boxes = np.array([
        [100, 100, 200, 200],
        [300, 150, 400, 250],
        [500, 100, 600, 180]
    ])
    
    # Draw tracking lines
    annotated_frame = track_drawer.draw(frame, tracks_ids, boxes)
    
    cv2.imshow("Tracking", annotated_frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

Customization

To change the line thickness:
track_drawer = TrackDrawer()
track_drawer.thickness = 3  # Thicker trails
To adjust the history length, modify the limit:
# In the draw method, change:
if len(track_line) > 100:  # Keep 100 points instead of 50
    track_line.pop(0)

Interface

TrackDrawerInterface

Abstract base class defining the contract for track drawing implementations.
from abc import ABC, abstractmethod
import numpy as np
from typing import List

class TrackDrawerInterface(ABC):
    def draw(self, image: np.ndarray, tracks_ids: List[int], boxes: np.ndarray) -> np.ndarray:
        """draw and save track in image"""
        raise NotImplementedError

Integration with Detection Pipeline

Typically used in the main drawing pipeline after mask and bounding box visualization:
from trash_classificator.drawing.main import Drawing

drawing = Drawing()
image = drawing.draw(image, trash_track, trash_classes, device)
The Drawing class orchestrates:
  1. Mask drawing (semi-transparent overlays)
  2. Bounding box drawing (labeled boxes)
  3. Track drawing (motion trails) - Final step

Tracking Behavior

  • New objects: When a new track ID appears, a new entry is created in track_history
  • Existing objects: Centroids are appended to the existing history
  • Lost objects: If an object is not detected in subsequent frames, its history persists but stops growing
  • Reappearing objects: If a track ID reappears, it continues from its existing history

Performance Considerations

  • The 50-point limit keeps memory usage constant regardless of video length
  • Line drawing is optimized by OpenCV’s native cv2.line() function
  • History is stored per track ID using defaultdict for automatic initialization

Build docs developers (and LLMs) love