Skip to main content

Overview

The Drawing class provides comprehensive visualization for trash detection results. It orchestrates three specialized drawer components to render segmentation masks, bounding boxes with labels, and object tracking trajectories on video frames.
The Drawing class automatically initializes all drawer components and applies them in the optimal order for visual clarity.

Interface Definition

DrawingInterface

Abstract base class defining the contract for drawing systems.
from abc import ABC, abstractmethod
import numpy as np
from typing import Dict
from ultralytics.engine.results import Results

class DrawingInterface(ABC):
    @abstractmethod
    def draw(self, image: np.ndarray, trash_track: Results, trash_classes: Dict[int, str], device):
        raise NotImplementedError

Class Definition

class Drawing(DrawingInterface):
    def __init__(self):
        self.mask_drawer: MaskDrawerInterface = MaskDrawer()
        self.bbox_drawer: BoundingBoxDrawerInterface = BoundingBoxDrawer()
        self.track_drawer: TrackDrawerInterface = TrackDrawer()

Constructor

__init__()

Initializes the Drawing system with three specialized drawer components. Attributes Initialized:
mask_drawer
MaskDrawerInterface
Instance of MaskDrawer for rendering segmentation masks with colored overlays
bbox_drawer
BoundingBoxDrawerInterface
Instance of BoundingBoxDrawer for drawing bounding boxes with class labels
track_drawer
TrackDrawerInterface
Instance of TrackDrawer for visualizing object trajectories and tracking history
Example:
from trash_classificator.drawing.main import Drawing

# Initialize the drawing system
drawer = Drawing()

Methods

draw()

Renders all detection visualizations on the input image.
def draw(self, image: np.ndarray, trash_track: Results, trash_classes: Dict[int, str], device)

Parameters

image
np.ndarray
required
Input image as a NumPy array in BGR format (OpenCV standard). This image will be modified with visualizations.
trash_track
Results
required
YOLO Results object containing detection data:
  • masks.xy: Polygon coordinates for segmentation masks
  • boxes.xyxy: Bounding box coordinates in [x1, y1, x2, y2] format
  • boxes.id: Tracking IDs for each detected object
  • boxes.cls: Class indices for detected trash types
trash_classes
Dict[int, str]
required
Dictionary mapping class indices to human-readable trash type names:
{
    0: "plastic",
    1: "paper",
    2: "metal"
}
device
torch.device
required
PyTorch device object indicating where the model inference was performed. Used for tensor operations during visualization.

Returns

image
np.ndarray
The input image with all visualizations applied:
  1. Colored segmentation masks (semi-transparent overlay)
  2. Bounding boxes with class labels
  3. Tracking trajectories showing object movement history

Drawing Pipeline

Visualizations are applied in the following order:
  1. Mask Drawing: Semi-transparent colored overlays for segmentation
  2. Bounding Box Drawing: Boxes with class labels and confidence scores
  3. Track Drawing: Trajectory lines showing object movement history
Source Code Reference:
drawing/main.py
def draw(self, image: np.ndarray, trash_track: Results, trash_classes: Dict[int, str], device):

    masks = trash_track.masks.xy
    boxes = trash_track.boxes.xyxy.cpu()
    tracks_ids = trash_track.boxes.id.int().cpu().tolist()
    clss = trash_track.boxes.cls.cpu().tolist()

    image = self.mask_drawer.draw(image, masks, clss)
    image = self.bbox_drawer.draw(image, boxes, trash_classes, clss)
    image = self.track_drawer.draw(image, tracks_ids, boxes)
    return image
The draw method modifies the input image in place. Each drawer component receives and returns the progressively modified image.

Drawer Components

The Drawing class integrates three specialized drawer components:

MaskDrawer

Renders segmentation masks with colored semi-transparent overlays.

BoundingBoxDrawer

Draws bounding boxes with class labels using Ultralytics Annotator.

TrackDrawer

Visualizes object trajectories with tracking history.

Usage Examples

Basic Usage

import cv2
import numpy as np
from trash_classificator.drawing.main import Drawing
from trash_classificator.segmentation.main import SegmentationModel

# Initialize components
segmentation = SegmentationModel()
drawer = Drawing()

# Load image
image = cv2.imread('frame.jpg')

# Run inference
results, trash_classes, device = segmentation.inference(image)

# Draw visualizations
for result in results:
    if result.boxes.id is not None:
        visualized_image = drawer.draw(image.copy(), result, trash_classes, device)
        cv2.imshow('Detections', visualized_image)
        cv2.waitKey(0)

Video Processing with Visualization

import cv2
from trash_classificator.drawing.main import Drawing
from trash_classificator.segmentation.main import SegmentationModel

# Initialize
segmentation = SegmentationModel()
drawer = Drawing()

# Open video
cap = cv2.VideoCapture('trash_video.mp4')

while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break
    
    # Process frame
    results, trash_classes, device = segmentation.inference(frame)
    
    # Visualize detections
    for result in results:
        if result.boxes.id is not None:
            frame = drawer.draw(frame, result, trash_classes, device)
    
    # Display
    cv2.imshow('Trash Detection', frame)
    
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

Custom Visualization Pipeline

import cv2
import numpy as np
from trash_classificator.drawing.main import (
    Drawing, MaskDrawer, BoundingBoxDrawer, TrackDrawer
)
from trash_classificator.segmentation.main import SegmentationModel

# Initialize with custom drawer configuration
drawing = Drawing()
segmentation = SegmentationModel()

# Access individual drawers for custom control
mask_drawer = drawing.mask_drawer
bbox_drawer = drawing.bbox_drawer
track_drawer = drawing.track_drawer

image = cv2.imread('scene.jpg')
results, trash_classes, device = segmentation.inference(image)

for result in results:
    if result.boxes.id is not None:
        # Extract data
        masks = result.masks.xy
        boxes = result.boxes.xyxy.cpu()
        track_ids = result.boxes.id.int().cpu().tolist()
        classes = result.boxes.cls.cpu().tolist()
        
        # Apply drawers individually for custom control
        image_with_masks = mask_drawer.draw(image.copy(), masks, classes)
        image_with_boxes = bbox_drawer.draw(image_with_masks, boxes, trash_classes, classes)
        final_image = track_drawer.draw(image_with_boxes, track_ids, boxes)
        
        cv2.imshow('Custom Visualization', final_image)
        cv2.waitKey(0)

Saving Visualized Frames

import cv2
from trash_classificator.drawing.main import Drawing
from trash_classificator.segmentation.main import SegmentationModel

segmentation = SegmentationModel()
drawer = Drawing()

cap = cv2.VideoCapture('input.mp4')
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter('output.mp4', fourcc, 30.0, (640, 480))

frame_count = 0
while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break
    
    results, trash_classes, device = segmentation.inference(frame)
    
    for result in results:
        if result.boxes.id is not None:
            frame = drawer.draw(frame, result, trash_classes, device)
    
    out.write(frame)
    frame_count += 1
    print(f"Processed frame {frame_count}")

cap.release()
out.release()
print(f"Saved {frame_count} frames to output.mp4")

Integration Details


Performance Considerations

  • Sequential Processing: Drawers are applied sequentially, each modifying the image
  • Tensor Operations: Boxes and IDs are moved to CPU before drawing
  • Memory Efficiency: Track history limited to prevent unbounded growth
  • Rendering Speed: Drawing operations are CPU-bound and add ~5-10ms per frame
For real-time applications, consider:
  • Using lower resolution images for display
  • Reducing track history length below 50 positions
  • Simplifying visualizations by disabling some drawer components

  • TrashClassificator - Main orchestrator that uses Drawing
  • SegmentationModel - Provides Results data for visualization
  • MaskDrawerInterface - Interface for mask rendering (drawing/main.py:15)
  • BoundingBoxDrawerInterface - Interface for box rendering (drawing/main.py:37)
  • TrackDrawerInterface - Interface for track rendering (drawing/main.py:54)

Build docs developers (and LLMs) love