Supervision: Computer Vision Toolkit by Roboflow

# Supervision: Toolkit Computer Vision dari Roboflow Dalam proyek computer vision, setelah model mendeteksi objek, Anda masih perlu melakukan banyak hal: **menggambar bounding box, memfilter deteksi,...

By Ruby Abdullah · · tutorial
SupervisionComputer VisionObject DetectionRoboflowPython

Supervision: Computer Vision Toolkit by Roboflow

In computer vision projects, after a model detects objects, there is still much to do: drawing bounding boxes, filtering detections, counting objects, and tracking movement. All of this requires substantial and repetitive code if written from scratch.

Supervision by Roboflow is a Python toolkit that simplifies all post-detection tasks. With a consistent API, Supervision works with various detection models like YOLO, Detectron2, and SAM, allowing you to focus on application logic rather than boilerplate code.

What Is Supervision?

Supervision is an open-source Python library by Roboflow that simplifies computer vision workflows. Its key features include:

  • Detections Object: Universal representation for detection results from various models
  • Annotators: Various visual annotator types (bounding box, mask, label, trace, halo)
  • Filtering: Filter detections by confidence, class, zone, and other criteria
  • Polygon Zones: Define polygon zones for counting and area analysis
  • Line Counters: Count objects crossing virtual lines
  • Object Tracking: ByteTrack integration for cross-frame object tracking
  • Video Processing: Utilities for frame-by-frame video processing
  • FPS Monitor: Real-time performance monitoring

Installation

Basic Installation

pip install supervision

Installation with Model Dependencies

# With YOLO (Ultralytics)

pip install supervision ultralytics

With Detectron2

pip install supervision detectron2

With SAM (Segment Anything)

pip install supervision segment-anything

Verify Installation

import supervision as sv

print(f"Supervision version: {sv.version}")

Detections Object

sv.Detections is the core object in Supervision that universally represents detection results.

Creating Detections from Various Models

import supervision as sv

import numpy as np

From Ultralytics YOLO

from ultralytics import YOLO

model = YOLO("yolov8n.pt")

results = model("image.jpg")

detections = sv.Detections.fromultralytics(results[0])

print(f"Number of detections: {len(detections)}")

print(f"Bounding boxes: {detections.xyxy}")

print(f"Confidence: {detections.confidence}")

print(f"Class IDs: {detections.classid}")

From Detectron2

from detectron2.engine import DefaultPredictor

from detectron2.config import getcfg

cfg = getcfg()

cfg.mergefromfile("config.yaml")

predictor = DefaultPredictor(cfg)

outputs = predictor(image)

detections = sv.Detections.fromdetectron2(outputs)

From SAM (Segment Anything)

from segmentanything import sammodelregistry, SamAutomaticMaskGenerator

sam = sammodelregistryvith.pth"">"vith"

maskgenerator = SamAutomaticMaskGenerator(sam)

masks = maskgenerator.generate(image)

detections = sv.Detections.fromsam(masks)

Creating Detections Manually

# Create detections manually

detections = sv.Detections(

xyxy=np.array([

[100, 200, 300, 400],

[150, 250, 350, 450]

]),

confidence=np.array([0.95, 0.87]),

classid=np.array([0, 1])

)

Annotators: Detection Visualization

Supervision provides various annotators for visualizing detection results.

Bounding Box Annotator

import supervision as sv

import cv2

image = cv2.imread("image.jpg")

Bounding box annotator

bboxannotator = sv.BoxAnnotator(

thickness=2,

color=sv.ColorPalette.fromhex(["#FF0000", "#00FF00", "#0000FF"])

)

annotatedimage = bboxannotator.annotate(

scene=image.copy(),

detections=detections

)

cv2.imwrite("annotatedbbox.jpg", annotatedimage)

Label Annotator

# Label annotator for displaying text

labelannotator = sv.LabelAnnotator(

textscale=0.5,

textthickness=1,

textpadding=5

)

Create labels with class names and confidence

labels = [

f"{classnames[classid]} {confidence:.2f}"

for classid, confidence

in zip(detections.classid, detections.confidence)

]

annotatedimage = labelannotator.annotate(

scene=annotatedimage,

detections=detections,

labels=labels

)

Mask Annotator

# For models that produce segmentation masks

maskannotator = sv.MaskAnnotator(

opacity=0.4

)

annotatedimage = maskannotator.annotate(

scene=image.copy(),

detections=detections

)

Trace Annotator (for Tracking)

# Trace annotator displays object movement trails

traceannotator = sv.TraceAnnotator(

thickness=2,

tracelength=50 # Trail length in frames

)

annotatedimage = traceannotator.annotate(

scene=image.copy(),

detections=detections

)

Halo Annotator

# Halo annotator creates a halo effect around objects

haloannotator = sv.HaloAnnotator(

opacity=0.5,

kernelsize=40

)

annotatedimage = haloannotator.annotate(

scene=image.copy(),

detections=detections

)

Combining Multiple Annotators

# Combine multiple annotators

bboxannotator = sv.BoxAnnotator()

labelannotator = sv.LabelAnnotator()

maskannotator = sv.MaskAnnotator(opacity=0.3)

Apply sequentially

annotated = image.copy()

annotated = maskannotator.annotate(scene=annotated, detections=detections)

annotated = bboxannotator.annotate(scene=annotated, detections=detections)

annotated = labelannotator.annotate(

scene=annotated,

detections=detections,

labels=labels

)

cv2.imwrite("fullyannotated.jpg", annotated)

Filtering Detections

Filter by Confidence

# Filter detections with confidence > 0.5

highconfidence = detections[detections.confidence > 0.5]

print(f"High confidence detections: {len(highconfidence)}")

Filter with confidence range

medium = detections[

(detections.confidence > 0.3) & (detections.confidence < 0.7)

]

Filter by Class

# Filter specific classes only (e.g.: person = 0, car = 2)

persons = detections[detections.classid == 0]

cars = detections[detections.classid == 2]

Filter multiple classes at once

vehicles = detections[np.isin(detections.classid, [2, 3, 5, 7])]

Filter by Area

# Filter by bounding box area

areas = detections.boxarea

largeobjects = detections[areas > 10000]

smallobjects = detections[areas < 1000]

Filter by Zone

# Define a polygon zone

zonepolygon = np.array([

[100, 100],

[500, 100],

[500, 400],

[100, 400]

])

zone = sv.PolygonZone(polygon=zonepolygon)

Filter detections within the zone

inzone = detections[zone.trigger(detections=detections)]

print(f"Objects in zone: {len(inzone)}")

Polygon Zones

Polygon Zones allow you to define custom areas for monitoring and counting.

import supervision as sv

import numpy as np

import cv2

Define zone as polygon

parkingzone = np.array([

[200, 300], [600, 300], [650, 500], [150, 500]

])

Create polygon zone

zone = sv.PolygonZone(polygon=parkingzone)

zoneannotator = sv.PolygonZoneAnnotator(

zone=zone,

color=sv.Color.GREEN,

thickness=2,

textthickness=2,

textscale=1

)

Process frame

isinzone = zone.trigger(detections=detections)

countinzone = np.sum(isinzone)

Annotate

annotated = zoneannotator.annotate(scene=image.copy())

print(f"Objects in parking: {countinzone}")

Multiple Zones

# Define multiple zones

zonespolygons = [

np.array([[100, 100], [400, 100], [400, 300], [100, 300]]), # Zone A

np.array([[500, 100], [800, 100], [800, 300], [500, 300]]), # Zone B

np.array([[100, 400], [800, 400], [800, 600], [100, 600]]), # Zone C

]

colors = [sv.Color.RED, sv.Color.GREEN, sv.Color.BLUE]

zones = []

zoneannotators = []

for polygon, color in zip(zonespolygons, colors):

zone = sv.PolygonZone(polygon=polygon)

zones.append(zone)

zoneannotators.append(

sv.PolygonZoneAnnotator(zone=zone, color=color)

)

Process each zone

for i, zone in enumerate(zones):

zone.trigger(detections=detections)

annotated = zoneannotators[i].annotate(scene=annotated)

Line Counters

Line counters count objects crossing a virtual line.

import supervision as sv

import numpy as np

Define virtual line

linestart = sv.Point(x=0, y=400)

lineend = sv.Point(x=800, y=400)

linezone = sv.LineZone(

start=linestart,

end=lineend

)

linezoneannotator = sv.LineZoneAnnotator(

thickness=2,

textthickness=2,

textscale=1

)

Process frame (in video loop)

linezone.trigger(detections=detections)

Get counts

print(f"In: {linezone.incount}")

print(f"Out: {linezone.outcount}")

Annotate

annotated = linezoneannotator.annotate(

frame=image.copy(),

linecounter=linezone

)

ByteTrack Object Tracking

ByteTrack enables consistent object tracking across video frames.

import supervision as sv

Initialize tracker

tracker = sv.ByteTrack(

trackactivationthreshold=0.25,

losttrackbuffer=30,

minimummatchingthreshold=0.8,

framerate=30

)

In video processing loop

def processframe(frame, model):

results = model(frame)

detections = sv.Detections.fromultralytics(results[0])

# Update tracker

detections = tracker.updatewithdetections(detections)

# Now detections have trackerid

print(f"Tracker IDs: {detections.trackerid}")

return detections

Tracking with Trace

tracker = sv.ByteTrack()

traceannotator = sv.TraceAnnotator(

thickness=2,

tracelength=50

)

bboxannotator = sv.BoxAnnotator()

labelannotator = sv.LabelAnnotator()

def processframe(frame, model):

results = model(frame)

detections = sv.Detections.fromultralytics(results[0])

detections = tracker.updatewithdetections(detections)

# Create labels with tracker ID

labels = [

f"#{trackerid} {classnames[classid]}"

for trackerid, classid

in zip(detections.trackerid, detections.classid)

]

# Annotate

annotated = frame.copy()

annotated = traceannotator.annotate(scene=annotated, detections=detections)

annotated = bboxannotator.annotate(scene=annotated, detections=detections)

annotated = labelannotator.annotate(

scene=annotated, detections=detections, labels=labels

)

return annotated

Integration with YOLO

import supervision as sv

from ultralytics import YOLO

import cv2

Load model

model = YOLO("yolov8n.pt")

Setup annotators

bboxannotator = sv.BoxAnnotator()

labelannotator = sv.LabelAnnotator()

Process image

image = cv2.imread("street.jpg")

results = model(image)

detections = sv.Detections.fromultralytics(results[0])

Filter vehicles only

vehicleclasses = [2, 3, 5, 7] # car, motorcycle, bus, truck

vehicles = detections[np.isin(detections.classid, vehicleclasses)]

Create labels

labels = [

f"{model.names[classid]} {conf:.2f}"

for classid, conf in zip(vehicles.classid, vehicles.confidence)

]

Annotate

annotated = bboxannotator.annotate(scene=image.copy(), detections=vehicles)

annotated = labelannotator.annotate(

scene=annotated, detections=vehicles, labels=labels

)

cv2.imwrite("vehiclesdetected.jpg", annotated)

print(f"Total vehicles detected: {len(vehicles)}")

Video Processing

Processing Video with VideoSink

import supervision as sv

from ultralytics import YOLO

model = YOLO("yolov8n.pt")

tracker = sv.ByteTrack()

bboxannotator = sv.BoxAnnotator()

labelannotator = sv.LabelAnnotator()

Video information

videoinfo = sv.VideoInfo.fromvideopath("inputvideo.mp4")

print(f"Resolution: {videoinfo.width}x{videoinfo.height}")

print(f"FPS: {videoinfo.fps}")

print(f"Total frames: {videoinfo.totalframes}")

Process video

with sv.VideoSink(

targetpath="outputvideo.mp4",

videoinfo=videoinfo

) as sink:

for frame in sv.getvideoframesgenerator(

sourcepath="inputvideo.mp4"

):

# Detection

results = model(frame)

detections = sv.Detections.fromultralytics(results[0])

detections = tracker.updatewithdetections(detections)

# Annotate

labels = [

f"#{tid} {model.names[cid]}"

for tid, cid in zip(

detections.trackerid, detections.classid

)

]

annotated = bboxannotator.annotate(

scene=frame.copy(), detections=detections

)

annotated = labelannotator.annotate(

scene=annotated, detections=detections, labels=labels

)

sink.writeframe(annotated)

Callback-Based Video Processing

def callback(frame: np.ndarray, index: int) -> np.ndarray:

results = model(frame)

detections = sv.Detections.fromultralytics(results[0])

detections = tracker.updatewithdetections(detections)

annotated = bboxannotator.annotate(

scene=frame.copy(), detections=detections

)

return annotated

sv.processvideo(

sourcepath="input.mp4",

targetpath="output.mp4",

callback=callback

)

FPS Monitor

import supervision as sv

import time

fpsmonitor = sv.FPSMonitor(samplesize=30)

for frame in sv.getvideoframesgenerator("video.mp4"):

fpsmonitor.tick()

# Process frame...

results = model(frame)

detections = sv.Detections.fromultralytics(results[0])

# Display FPS

fps = fpsmonitor.fps

print(f"FPS: {fps:.1f}")

# Add FPS to frame

cv2.putText(

frame,

f"FPS: {fps:.1f}",

(10, 30),

cv2.FONTHERSHEYSIMPLEX,

1,

(0, 255, 0),

2

)

Practical Example: Vehicle Counting System with Zone-Based Tracking

Here is a complete example of building a vehicle counting system that uses polygon zones and object tracking.

import supervision as sv

from ultralytics import YOLO

import numpy as np

import cv2

Configuration

MODELPATH = "yolov8n.pt"

VIDEOPATH = "trafficvideo.mp4"

OUTPUTPATH = "trafficcounted.mp4"

Vehicle classes in COCO dataset

VEHICLECLASSES = {

2: "car",

3: "motorcycle",

5: "bus",

7: "truck"

}

class VehicleCounter:

def init(self, videopath: str, modelpath: str):

self.model = YOLO(modelpath)

self.videoinfo = sv.VideoInfo.fromvideopath(videopath)

# Initialize tracker

self.tracker = sv.ByteTrack(

trackactivationthreshold=0.25,

losttrackbuffer=30,

minimummatchingthreshold=0.8,

framerate=self.videoinfo.fps

)

# Define counting zones (adjust to your video)

# Entry zone (top)

self.entryzonepolygon = np.array([

[100, 200], [700, 200], [700, 300], [100, 300]

])

# Exit zone (bottom)

self.exitzonepolygon = np.array([

[100, 400], [700, 400], [700, 500], [100, 500]

])

# Create zones

self.entryzone = sv.PolygonZone(

polygon=self.entryzonepolygon

)

self.exitzone = sv.PolygonZone(

polygon=self.exitzonepolygon

)

# Line counter (horizontal line in the middle)

self.linezone = sv.LineZone(

start=sv.Point(x=50, y=350),

end=sv.Point(x=750, y=350)

)

# Annotators

self.bboxannotator = sv.BoxAnnotator(thickness=2)

self.labelannotator = sv.LabelAnnotator(

textscale=0.5,

textthickness=1

)

self.traceannotator = sv.TraceAnnotator(

thickness=2,

tracelength=60

)

self.entryzoneannotator = sv.PolygonZoneAnnotator(

zone=self.entryzone,

color=sv.Color.GREEN,

thickness=2

)

self.exitzoneannotator = sv.PolygonZoneAnnotator(

zone=self.exitzone,

color=sv.Color.RED,

thickness=2

)

self.lineannotator = sv.LineZoneAnnotator(

thickness=2,

textthickness=2,

textscale=0.8

)

# Statistics

self.vehiclecounts = {name: 0 for name in VEHICLECLASSES.values()}

self.trackedids = set()

def processframe(self, frame: np.ndarray, frameindex: int) -> np.ndarray:

# Object detection

results = self.model(frame, verbose=False)

detections = sv.Detections.fromultralytics(results[0])

# Filter vehicles only

vehiclemask = np.isin(

detections.classid,

list(VEHICLECLASSES.keys())

)

detections = detections[vehiclemask]

# Filter low confidence

detections = detections[detections.confidence > 0.3]

# Update tracker

detections = self.tracker.updatewithdetections(detections)

# Update zones and line counter

self.entryzone.trigger(detections=detections)

self.exitzone.trigger(detections=detections)

self.linezone.trigger(detections=detections)

# Count unique vehicles

if detections.trackerid is not None:

for tid, cid in zip(detections.trackerid, detections.classid):

if tid not in self.trackedids:

self.trackedids.add(tid)

vehicletype = VEHICLECLASSES.get(cid, "unknown")

self.vehiclecounts[vehicletype] += 1

# Create labels

labels = []

if detections.trackerid is not None:

labels = [

f"#{tid} {VEHICLECLASSES.get(cid, '?')} {conf:.2f}"

for tid, cid, conf in zip(

detections.trackerid,

detections.classid,

detections.confidence

)

]

# Annotate frame

annotated = frame.copy()

annotated = self.traceannotator.annotate(

scene=annotated, detections=detections

)

annotated = self.bboxannotator.annotate(

scene=annotated, detections=detections

)

annotated = self.labelannotator.annotate(

scene=annotated, detections=detections, labels=labels

)

annotated = self.entryzoneannotator.annotate(scene=annotated)

annotated = self.exitzoneannotator.annotate(scene=annotated)

annotated = self.lineannotator.annotate(

frame=annotated, linecounter=self.linezone

)

# Overlay statistics

self.drawstats(annotated, frameindex)

return annotated

def drawstats(self, frame: np.ndarray, frameindex: int):

"""Draw statistics overlay on frame."""

# Semi-transparent background

overlay = frame.copy()

cv2.rectangle(overlay, (10, 10), (300, 180), (0, 0, 0), -1)

cv2.addWeighted(overlay, 0.6, frame, 0.4, 0, frame)

# Title

cv2.putText(

frame, "Vehicle Counter",

(20, 35), cv2.FONTHERSHEYSIMPLEX, 0.7, (255, 255, 255), 2

)

# Per-vehicle details

yoffset = 60

total = 0

for vehicletype, count in self.vehiclecounts.items():

cv2.putText(

frame,

f"{vehicletype}: {count}",

(20, yoffset),

cv2.FONTHERSHEYSIMPLEX,

0.5,

(200, 200, 200),

1

)

yoffset += 25

total += count

# Total

cv2.putText(

frame,

f"Total: {total}",

(20, yoffset + 5),

cv2.FONTHERSHEYSIMPLEX,

0.6,

(0, 255, 0),

2

)

# Line counter

cv2.putText(

frame,

f"In: {self.linezone.incount} | Out: {self.linezone.outcount}",

(20, yoffset + 30),

cv2.FONTHERSHEYSIMPLEX,

0.5,

(255, 255, 0),

1

)

def run(self, outputpath: str):

"""Run the counting process on video."""

print(f"Video: {self.videoinfo.width}x{self.videoinfo.height}")

print(f"FPS: {self.videoinfo.fps}")

print(f"Total frames: {self.videoinfo.totalframes}")

print("Processing video...")

fpsmonitor = sv.FPSMonitor(samplesize=30)

with sv.VideoSink(

targetpath=outputpath,

videoinfo=self.videoinfo

) as sink:

for index, frame in enumerate(

sv.getvideoframesgenerator(VIDEOPATH)

):

fpsmonitor.tick()

annotated = self.processframe(frame, index)

sink.writeframe(annotated)

# Progress report every 100 frames

if index % 100 == 0:

fps = fpsmonitor.fps

progress = (index / self.videoinfo.totalframes) * 100

print(

f"Frame {index}/{self.videoinfo.totalframes} "

f"({progress:.1f}%) - FPS: {fps:.1f}"

)

# Final report

print("\n=== Counting Results ===")

total = 0

for vehicletype, count in self.vehiclecounts.items():

print(f" {vehicletype}: {count}")

total += count

print(f" Total vehicles: {total}")

print(f" Unique tracked IDs: {len(self.trackedids)}")

print(f" Line In: {self.linezone.incount}")

print(f" Line Out: {self.linezone.outcount}")

Run

if name == "main":

counter = VehicleCounter(

videopath=VIDEOPATH,

modelpath=MODELPATH

)

counter.run(outputpath=OUTPUTPATH)

Running the Vehicle Counter

# Install dependencies

pip install supervision ultralytics opencv-python numpy

Download YOLO model (automatic on first run)

Prepare your traffic video

Run

python vehiclecounter.py

Configuring Zones for Your Video

To define zones that match your video, use Supervision utilities:

import supervision as sv

import cv2

Get the first frame from video

framegenerator = sv.getvideoframesgenerator("trafficvideo.mp4")

firstframe = next(framegenerator)

Display and note zone coordinates

cv2.imshow("First Frame", firstframe)

cv2.waitKey(0)

cv2.destroyAllWindows()

Use the noted coordinates for polygon zones

Tips and Best Practices

  • Choose the Right Model: Use lighter models (YOLOv8n) for real-time and larger models (YOLOv8x) for accuracy
  • Tune the Tracker: Adjust ByteTrack parameters like trackactivationthreshold and losttrack_buffer based on your scenario
  • Early Filtering: Always filter detections by confidence and class before tracking to reduce noise
  • Accurate Zones: Define polygon zones carefully to match the monitored area
  • Batch Processing: For long videos, use batch processing and save results periodically
  • Conclusion

    Supervision by Roboflow is an extremely useful toolkit for simplifying computer vision workflows. With consistent abstractions for various detection models, rich visual annotators, and powerful tracking features, Supervision enables you to build production-ready computer vision applications with clean and maintainable code. Seamless integration with YOLO, Detectron2, and SAM makes it the go-to choice for modern computer vision projects.

    Related Articles

    Florence-2: Microsoft's Multi-Task Vision Foundation Model

    Florence-2: Model Vision Multi-Task dari Microsoft Daftar Isi Pendahuluan Prasyarat Instalasi Memuat Model Florence-2

    Complete Ultralytics Tutorial: Object Detection with YOLO

    Tutorial Lengkap Ultralytics: Object Detection dengan YOLO Ultralytics adalah framework Python yang menyediakan implemen...

    Complete Guide to DETR: Object Detection with Transformers

    Panduan Lengkap DETR: Object Detection dengan Transformers DETR (DEtection TRansformer) adalah pendekatan revolusioner u...

    Surya OCR Tutorial: Document AI for Text Detection, OCR, and Layout Analysis

    Tutorial Surya OCR: Document AI untuk Text Detection, OCR, dan Layout Analysis Surya adalah library Document AI open-sou...