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, menghitung objek, dan melacak pergerakan. Semua ini membutuhkan kode yang cukup banyak dan repetitif jika ditulis dari nol.
Supervision dari Roboflow hadir sebagai toolkit Python yang menyederhanakan semua pekerjaan pasca-deteksi ini. Dengan API yang konsisten, Supervision bekerja dengan berbagai model deteksi seperti YOLO, Detectron2, dan SAM, memungkinkan Anda fokus pada logika aplikasi bukan boilerplate code.Apa Itu Supervision?
Supervision adalah library Python open-source dari Roboflow untuk memudahkan pekerjaan computer vision. Fitur utamanya meliputi:
- Detections Object: Representasi universal untuk hasil deteksi dari berbagai model
- Annotators: Berbagai tipe annotator visual (bounding box, mask, label, trace, halo)
- Filtering: Filter deteksi berdasarkan confidence, kelas, zona, dan kriteria lainnya
- Polygon Zones: Definisi zona poligon untuk counting dan analisis area
- Line Counters: Penghitung objek yang melewati garis virtual
- Object Tracking: Integrasi ByteTrack untuk pelacakan objek lintas frame
- Video Processing: Utilitas untuk memproses video frame-by-frame
- FPS Monitor: Monitoring performa real-time
Instalasi
Instalasi Dasar
pip install supervision
Instalasi dengan Dependensi Model
# Dengan YOLO (Ultralytics)
pip install supervision ultralytics
Dengan Detectron2
pip install supervision detectron2
Dengan SAM (Segment Anything)
pip install supervision segment-anything
Verifikasi Instalasi
import supervision as sv
print(f"Supervision version: {sv.version}")
Detections Object
sv.Detections adalah objek inti di Supervision yang merepresentasikan hasil deteksi secara universal.
Membuat Detections dari Berbagai Model
import supervision as sv
import numpy as np
Dari Ultralytics YOLO
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
results = model("image.jpg")
detections = sv.Detections.fromultralytics(results[0])
print(f"Jumlah deteksi: {len(detections)}")
print(f"Bounding boxes: {detections.xyxy}")
print(f"Confidence: {detections.confidence}")
print(f"Class IDs: {detections.classid}")
Dari 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)
Dari SAM (Segment Anything)
from segmentanything import sammodelregistry, SamAutomaticMaskGenerator
sam = sammodelregistryvith.pth"">"vith"
maskgenerator = SamAutomaticMaskGenerator(sam)
masks = maskgenerator.generate(image)
detections = sv.Detections.fromsam(masks)
Membuat Detections Manual
# Buat detections secara manual
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: Visualisasi Deteksi
Supervision menyediakan berbagai annotator untuk memvisualisasikan hasil deteksi.
Bounding Box Annotator
import supervision as sv
import cv2
image = cv2.imread("image.jpg")
Annotator bounding box
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 untuk menampilkan teks
labelannotator = sv.LabelAnnotator(
textscale=0.5,
textthickness=1,
textpadding=5
)
Buat label dengan nama kelas dan 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
# Untuk model yang menghasilkan segmentation mask
maskannotator = sv.MaskAnnotator(
opacity=0.4
)
annotatedimage = maskannotator.annotate(
scene=image.copy(),
detections=detections
)
Trace Annotator (untuk Tracking)
# Trace annotator menampilkan jejak pergerakan objek
traceannotator = sv.TraceAnnotator(
thickness=2,
tracelength=50 # Panjang jejak dalam frame
)
annotatedimage = traceannotator.annotate(
scene=image.copy(),
detections=detections
)
Halo Annotator
# Halo annotator memberikan efek halo di sekitar objek
haloannotator = sv.HaloAnnotator(
opacity=0.5,
kernelsize=40
)
annotatedimage = haloannotator.annotate(
scene=image.copy(),
detections=detections
)
Menggabungkan Beberapa Annotator
# Gabungkan beberapa annotator
bboxannotator = sv.BoxAnnotator()
labelannotator = sv.LabelAnnotator()
maskannotator = sv.MaskAnnotator(opacity=0.3)
Terapkan berurutan
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 Berdasarkan Confidence
# Filter deteksi dengan confidence > 0.5
highconfidence = detections[detections.confidence > 0.5]
print(f"Deteksi high confidence: {len(highconfidence)}")
Filter dengan range confidence
medium = detections[
(detections.confidence > 0.3) & (detections.confidence < 0.7)
]
Filter Berdasarkan Kelas
# Filter hanya kelas tertentu (misalnya: person = 0, car = 2)
persons = detections[detections.classid == 0]
cars = detections[detections.classid == 2]
Filter beberapa kelas sekaligus
vehicles = detections[np.isin(detections.classid, [2, 3, 5, 7])]
Filter Berdasarkan Area
# Filter berdasarkan luas bounding box
areas = detections.boxarea
largeobjects = detections[areas > 10000]
smallobjects = detections[areas < 1000]
Filter Berdasarkan Zona
# Definisikan zona poligon
zonepolygon = np.array([
[100, 100],
[500, 100],
[500, 400],
[100, 400]
])
zone = sv.PolygonZone(polygon=zonepolygon)
Filter deteksi yang berada dalam zona
inzone = detections[zone.trigger(detections=detections)]
print(f"Objek dalam zona: {len(inzone)}")
Polygon Zones
Polygon Zones memungkinkan Anda mendefinisikan area kustom untuk monitoring dan counting.
import supervision as sv
import numpy as np
import cv2
Definisikan zona sebagai poligon
parkingzone = np.array([
[200, 300], [600, 300], [650, 500], [150, 500]
])
Buat polygon zone
zone = sv.PolygonZone(polygon=parkingzone)
zoneannotator = sv.PolygonZoneAnnotator(
zone=zone,
color=sv.Color.GREEN,
thickness=2,
textthickness=2,
textscale=1
)
Proses frame
isinzone = zone.trigger(detections=detections)
countinzone = np.sum(isinzone)
Annotate
annotated = zoneannotator.annotate(scene=image.copy())
print(f"Objek di parkiran: {countinzone}")
Multiple Zones
# Definisikan beberapa zona
zonespolygons = [
np.array([[100, 100], [400, 100], [400, 300], [100, 300]]), # Zona A
np.array([[500, 100], [800, 100], [800, 300], [500, 300]]), # Zona B
np.array([[100, 400], [800, 400], [800, 600], [100, 600]]), # Zona 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)
)
Proses setiap zona
for i, zone in enumerate(zones):
zone.trigger(detections=detections)
annotated = zoneannotators[i].annotate(scene=annotated)
Line Counters
Line counters menghitung objek yang melewati garis virtual.
import supervision as sv
import numpy as np
Definisikan garis virtual
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
)
Proses frame (dalam loop video)
linezone.trigger(detections=detections)
Dapatkan hitungan
print(f"Masuk: {linezone.incount}")
print(f"Keluar: {linezone.outcount}")
Annotate
annotated = linezoneannotator.annotate(
frame=image.copy(),
linecounter=linezone
)
ByteTrack Object Tracking
ByteTrack memungkinkan pelacakan objek konsisten antar frame video.
import supervision as sv
Inisialisasi tracker
tracker = sv.ByteTrack(
trackactivationthreshold=0.25,
losttrackbuffer=30,
minimummatchingthreshold=0.8,
framerate=30
)
Dalam loop video processing
def processframe(frame, model):
results = model(frame)
detections = sv.Detections.fromultralytics(results[0])
# Update tracker
detections = tracker.updatewithdetections(detections)
# Sekarang detections memiliki trackerid
print(f"Tracker IDs: {detections.trackerid}")
return detections
Tracking dengan 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)
# Buat label dengan 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
Integrasi dengan YOLO
import supervision as sv
from ultralytics import YOLO
import cv2
Muat model
model = YOLO("yolov8n.pt")
Setup annotators
bboxannotator = sv.BoxAnnotator()
labelannotator = sv.LabelAnnotator()
Proses gambar
image = cv2.imread("street.jpg")
results = model(image)
detections = sv.Detections.fromultralytics(results[0])
Filter hanya kendaraan
vehicleclasses = [2, 3, 5, 7] # car, motorcycle, bus, truck
vehicles = detections[np.isin(detections.classid, vehicleclasses)]
Buat label
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 kendaraan terdeteksi: {len(vehicles)}")
Video Processing
Memproses Video dengan VideoSink
import supervision as sv
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
tracker = sv.ByteTrack()
bboxannotator = sv.BoxAnnotator()
labelannotator = sv.LabelAnnotator()
Informasi video
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}")
Proses video
with sv.VideoSink(
targetpath="outputvideo.mp4",
videoinfo=videoinfo
) as sink:
for frame in sv.getvideoframesgenerator(
sourcepath="inputvideo.mp4"
):
# Deteksi
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()
# Proses frame...
results = model(frame)
detections = sv.Detections.fromultralytics(results[0])
# Tampilkan FPS
fps = fpsmonitor.fps
print(f"FPS: {fps:.1f}")
# Tambahkan FPS ke frame
cv2.putText(
frame,
f"FPS: {fps:.1f}",
(10, 30),
cv2.FONTHERSHEYSIMPLEX,
1,
(0, 255, 0),
2
)
Contoh Praktis: Sistem Penghitung Kendaraan dengan Zone-Based Tracking
Berikut adalah contoh lengkap membangun sistem penghitung kendaraan yang menggunakan polygon zone dan object tracking.
import supervision as sv
from ultralytics import YOLO
import numpy as np
import cv2
Konfigurasi
MODELPATH = "yolov8n.pt"
VIDEOPATH = "trafficvideo.mp4"
OUTPUTPATH = "trafficcounted.mp4"
Kelas kendaraan dalam 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)
# Inisialisasi tracker
self.tracker = sv.ByteTrack(
trackactivationthreshold=0.25,
losttrackbuffer=30,
minimummatchingthreshold=0.8,
framerate=self.videoinfo.fps
)
# Definisikan zona counting (sesuaikan dengan video Anda)
# Zona masuk (atas)
self.entryzonepolygon = np.array([
[100, 200], [700, 200], [700, 300], [100, 300]
])
# Zona keluar (bawah)
self.exitzonepolygon = np.array([
[100, 400], [700, 400], [700, 500], [100, 500]
])
# Buat zona
self.entryzone = sv.PolygonZone(
polygon=self.entryzonepolygon
)
self.exitzone = sv.PolygonZone(
polygon=self.exitzonepolygon
)
# Line counter (garis horizontal di tengah)
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
)
# Statistik
self.vehiclecounts = {name: 0 for name in VEHICLECLASSES.values()}
self.trackedids = set()
def processframe(self, frame: np.ndarray, frameindex: int) -> np.ndarray:
# Deteksi objek
results = self.model(frame, verbose=False)
detections = sv.Detections.fromultralytics(results[0])
# Filter hanya kendaraan
vehiclemask = np.isin(
detections.classid,
list(VEHICLECLASSES.keys())
)
detections = detections[vehiclemask]
# Filter confidence rendah
detections = detections[detections.confidence > 0.3]
# Update tracker
detections = self.tracker.updatewithdetections(detections)
# Update zona dan line counter
self.entryzone.trigger(detections=detections)
self.exitzone.trigger(detections=detections)
self.linezone.trigger(detections=detections)
# Hitung kendaraan unik
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
# Buat label
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 statistik
self.drawstats(annotated, frameindex)
return annotated
def drawstats(self, frame: np.ndarray, frameindex: int):
"""Gambar statistik di atas frame."""
# Background semi-transparan
overlay = frame.copy()
cv2.rectangle(overlay, (10, 10), (300, 180), (0, 0, 0), -1)
cv2.addWeighted(overlay, 0.6, frame, 0.4, 0, frame)
# Judul
cv2.putText(
frame, "Vehicle Counter",
(20, 35), cv2.FONTHERSHEYSIMPLEX, 0.7, (255, 255, 255), 2
)
# Detail per kendaraan
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):
"""Jalankan proses counting pada 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("Memproses 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 setiap 100 frame
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}"
)
# Laporan final
print("\n=== Hasil Penghitungan ===")
total = 0
for vehicletype, count in self.vehiclecounts.items():
print(f" {vehicletype}: {count}")
total += count
print(f" Total kendaraan: {total}")
print(f" Unique tracked IDs: {len(self.trackedids)}")
print(f" Line In: {self.linezone.incount}")
print(f" Line Out: {self.linezone.outcount}")
Jalankan
if name == "main":
counter = VehicleCounter(
videopath=VIDEOPATH,
modelpath=MODELPATH
)
counter.run(outputpath=OUTPUTPATH)
Menjalankan Vehicle Counter
# Install dependensi
pip install supervision ultralytics opencv-python numpy
Download model YOLO (otomatis saat pertama kali)
Siapkan video traffic Anda
Jalankan
python vehiclecounter.py
Konfigurasi Zona untuk Video Anda
Untuk mendefinisikan zona yang sesuai dengan video Anda, gunakan utilitas dari Supervision:
import supervision as sv
import cv2
Ambil frame pertama dari video
framegenerator = sv.getvideoframesgenerator("trafficvideo.mp4")
firstframe = next(framegenerator)
Tampilkan dan catat koordinat zona
cv2.imshow("First Frame", firstframe)
cv2.waitKey(0)
cv2.destroyAllWindows()
Gunakan koordinat yang dicatat untuk polygon zona
Tips dan Best Practices
trackactivationthreshold dan losttrack_buffer berdasarkan skenarioKesimpulan
Supervision dari Roboflow adalah toolkit yang sangat berguna untuk menyederhanakan workflow computer vision. Dengan abstraksi yang konsisten untuk berbagai model deteksi, annotator visual yang kaya, dan fitur tracking yang powerful, Supervision memungkinkan Anda membangun aplikasi computer vision production-ready dengan kode yang bersih dan maintainable. Integrasi seamless dengan YOLO, Detectron2, dan SAM membuatnya menjadi pilihan utama untuk proyek computer vision modern.