Releases: roboflow/supervision
Release list
supervision-0.29.1
What's new
π KeyPoints.with_nms() β NMS for pose estimation
import supervision as sv
key_points = model.predict(image) # sv.KeyPoints
key_points = key_points.with_nms(threshold=0.5) # removes duplicate skeletonsDerives axis-aligned bounding boxes from each skeleton's valid (non-zero and visible) keypoints, then applies standard box NMS. Supports class_agnostic mode and any OverlapMetric (IOU, IOS). Raises ValueError if detection_confidence is not set.
feat-keypoints-with-nms-compressed.mp4
Notable changes
Bug fixes
-
sv.DetectionDataset.as_pascal_vocno longer mutates bounding boxes (#2341) Previously, every export shifted every bounding box by +1 px in-place. A second call compounded the shift. Fixed by rebinding to a new array; on-disk XML output is unchanged. -
sv.Precisionandsv.F1Scorecorrectly count background false positives (#2331) Predictions on images with no ground-truth objects, and predictions of classes absent from any annotation, were previously ignored. UnderMICROandMACROaveraging they are now counted as false positives.WEIGHTEDaveraging is unchanged. Users should re-evaluate existing metric results after upgrading. -
sv.DetectionsSmootherworks with confidence-free detections (#2333) The smoother no longer raises when detections have no confidence scores. Confidence is averaged over the frames that carry it; tracks without any confidence produceNone. -
sv.Detections.from_vlmis robust to malformed Gemini/Qwen output (#2342) Valid JSON that is not a list, or whose elements are not dicts, now degrades to emptyDetectionsinstead of raisingTypeError. A malformed mask value in Gemini 2.5 responses no longer misaligns thexyxy/confidence/masksarrays. -
sv.JSONSinkserializes NumPy scalars incustom_data(#2334)np.int64frame indices and other NumPy scalars incustom_datano longer raiseTypeErrorat flush time. NumPy arrays are serialized as lists. The file handle closes even when serialization fails. -
sv.approximate_polygonrespects the point-count budget (#2332) The function now returns at mostfloor(N * (1 - percentage))points (minimum 3). Previously it could return more points than requested.epsilon_stepis now validated to be positive. -
COCO export preserves all segments for multi-part masks (#2322) Previously, only the first polygon was written when a non-crowd detection had disjoint mask segments. All polygon parts are now written.
Performance
-
sv.HaloAnnotatoris ~4Γ faster withCompactMaskdetections (#2339)HaloAnnotatornow uses the same optimized CompactMask paint path asMaskAnnotator. Previously it materialized each mask full-frame; now it operates on the bounding-box crop. Annotated output is unchanged. -
Mask IoU uses less peak memory (#2323) Mask IoU computation now uses matrix multiplication on flattened masks instead of an explicit
(N, M, H, W)tensor. For masks larger than 4096Γ4096 px, computation promotes to float64 automatically. Results are numerically identical. -
sv.mask_to_xyxyandsv.KeyPoints.as_detectionsvectorized (#2330) Both functions now use batched NumPy operations instead of per-element loops. Outputs are bit-identical.
Contributors
- Ruben Haisma (@RubenHaisma, LinkedIn) β VLM robustness, Pascal VOC export fix, DetectionsSmoother, JSONSink, metrics correctness, polygon budgeting, vectorization
- Agis Kounelis (@kounelisagis, LinkedIn) β HaloAnnotator perf, mask IoU matmul, mask_to_xyxy/KeyPoints.as_detections vectorization, OBB cookbook
- Piotr Skalski (@SkalskiP, LinkedIn) β
KeyPoints.with_nms() - Abdelrahman Gomaa (@abdogomaa201099, LinkedIn) β COCO multi-polygon export
Full Changelog: 0.29.0...0.29.1
supervision-0.29.0.post
Full Changelog: 0.29.0...0.29.0.post0
supervision-0.29.0
π Added
-
Added
sv.VertexEllipseAreaAnnotator,sv.VertexEllipseOutlineAnnotator, andsv.VertexEllipseHaloAnnotatorfor visualizing keypoint uncertainty as covariance ellipses. (#2277, #2286)import cv2 import supervision as sv from rfdetr import RFDETRKeypointPreview image = cv2.imread("<SOURCE_IMAGE_PATH>") model = RFDETRKeypointPreview() key_points = model.predict(image) annotator = sv.VertexEllipseAreaAnnotator( sigma=[1.0, 2.0, 3.0], color=[sv.Color.GREEN, sv.Color.YELLOW, sv.Color.RED], opacity=0.4, ) annotated = annotator.annotate(image.copy(), key_points)
rf-detr-ellipse-promo-2.mp4
import cv2 import supervision as sv from rfdetr import RFDETRKeypointPreview image = cv2.imread("<SOURCE_IMAGE_PATH>") model = RFDETRKeypointPreview() key_points = model.predict(image) annotator = sv.VertexEllipseOutlineAnnotator( sigma=[1.0, 2.0, 3.0], color=[sv.Color.GREEN, sv.Color.YELLOW, sv.Color.RED], thickness=2, ) annotated = annotator.annotate(image.copy(), key_points)
rf-detr-ellipse-promo-3.mp4
import cv2 import supervision as sv from rfdetr import RFDETRKeypointPreview image = cv2.imread("<SOURCE_IMAGE_PATH>") model = RFDETRKeypointPreview() key_points = model.predict(image) annotator = sv.VertexEllipseHaloAnnotator( sigma=[1.0, 2.0, 3.0], color=[sv.Color.GREEN, sv.Color.YELLOW, sv.Color.RED], opacity=0.6, ) annotated = annotator.annotate(image.copy(), key_points)
rf-detr-ellipse-promo-1.mp4
-
Added
sv.oriented_box_non_max_suppressionandsv.oriented_box_non_max_mergefor performing NMS and NMM directly on oriented bounding boxes. (#2303) -
Added OBB (Oriented Bounding Box) support to
sv.ConfusionMatrixviaMetricTarget.ORIENTED_BOUNDING_BOXES. (#2247) -
Added
preserve_audioparameter tosv.process_video. When enabled, the audio stream from the source video is muxed into the output using ffmpeg. (#2252) -
Added
is_obbparameter tosv.DetectionDataset.as_yolofor exporting oriented bounding box annotations in the YOLO OBB format (9-token lines with 4 corner coordinates). (#2302, #2289)
π± Changed
-
sv.EdgeAnnotatorandsv.VertexAnnotatornow respect thevisiblemask. Invisible keypoints and their edges are skipped during rendering. (#2286) -
sv.EdgeAnnotatorandsv.VertexLabelAnnotatornow support per-class skeleton definitions, enabling correct rendering when multiple skeleton topologies (e.g. person + animal) coexist in one frame. (#2286) -
sv.Detections.with_nmsandsv.Detections.with_nmmare now OBB-aware. Whendata[ORIENTED_BOX_COORDINATES]is present, oriented-box IoU is used automatically instead of axis-aligned box IoU. (#2303)
-
sv.Detections.areais now OBB-aware. When oriented box coordinates are present, the property returns the polygon area of the rotated bounding box (via the shoelace formula) instead of the axis-aligned box area. (#2306) -
sv.InferenceSlicernow detects OBB outputs from callbacks and automatically falls back to sequential processing to avoid thread-safety issues whenthread_workers > 1. (#2256) -
Fixed
sv.oriented_box_iou_batchto correctly handle non-square canvases. Previously, rasterization assumed square dimensions, leading to incorrect IoU values for tall or wide images. (#2282)
π§ Fixed
-
Fixed
sv.process_videoaudio stream handling. The audio muxing path now correctly creates temp files on the same filesystem, decodes ffmpeg errors, and avoids muxing incomplete output. (#2252) -
Fixed
sv.Detections.from_vlmreturningNoneforclass_idon empty VLM parses. Now returns an empty int ndarray. (#2239) -
Fixed
sv.Detections.from_inferenceto preserveclass_nameas a string-dtype array when predictions are empty. Previously it returned an untyped empty array. (#2270) -
Fixed
sv.HeatMapAnnotatordivide-by-zero crash when called with empty detections. (#2269) -
Fixed COCO export emitting 0-indexed
category_idvalues. Now correctly emits 1-indexed IDs as per the COCO specification. (#2276) -
Fixed COCO annotation and image IDs not being chainable across dataset splits. IDs are now sequential across train/val/test. (#2267)
-
Fixed
sv.DetectionDataset.as_yololosing OBB rotation when exporting oriented bounding boxes. (#2289) -
Fixed YOLO dataset loading to sort class names by numeric keys when the
data.yamluses integer class IDs. (#2296) -
Fixed letterbox utility to support grayscale images. (#2297)
-
Fixed file extension filters to normalize casing (e.g.
.JPGnow matches.jpg). (#2298)
β οΈ Deprecated
| Deprecated | Removal | Replacement |
|---|---|---|
KeyPoints.confidence |
0.32.0 |
KeyPoints.keypoint_confidence |
merge_inner_detection_object_pair |
0.32.0 |
None (internal use only) |
merge_inner_detections_objects |
0.32.0 |
None (internal use only) |
merge_inner_detections_objects_without_iou |
0.32.0 |
None (internal use only) |
validate_detections_fields |
0.32.0 |
None (internal use only) |
validate_vlm_parameters |
0.32.0 |
None (internal use only) |
validate_fields_both_defined_or_none |
0.32.0 |
None (internal use only) |
validate_xyxy |
0.32.0 |
None (in... |
[RC] supervision
Full Changelog: 0.28.0...0.29.0rc0
supervision-0.28.0
π¦ Spotlight
Memory-efficient masks with sv.CompactMask
Segmentation models produce one full-resolution bitmap per instance. On a 1920Γ1080 image with 28 detections that is ~55 MB of mask data. Most pixels are background. sv.CompactMask stores only the tight bounding-box crop, RLE-encoded β the same 28 masks drop to ~237 KB of crops, a 240Γ reduction before RLE kicks in.
It's a drop-in replacement: annotators, filters, and area all work unchanged.
import supervision as sv
# any segmentation model β RF-DETR Seg, YOLO-Seg, SAM3
detections = model.predict(image) # sv.Detections with dense masks
dense_mb = detections.mask.nbytes / 1024 / 1024
compact = sv.CompactMask.from_dense(
masks=detections.mask,
xyxy=detections.xyxy,
image_shape=image.shape[:2],
)
detections.mask = compact # swap in β API unchanged
# filter by pixel area without materialising dense masks
large = detections[compact.area > 1000]
# annotators call .to_dense() internally
annotated = sv.MaskAnnotator().annotate(image.copy(), detections)SAM3 text-prompted segmentation
SAM3 segments objects by free-text prompt β no class list, no bounding boxes. sv.Detections.from_sam3() parses both PCS (multi-prompt) and PVS (video) response formats into a standard sv.Detections, with class_id set to the prompt index.
import requests, base64
import supervision as sv
PROMPTS = ["person", "bag"]
with open("image.jpg", "rb") as f:
img_b64 = base64.b64encode(f.read()).decode()
response = requests.post(
f"https://api.roboflow.com/inferenceproxy/seg-preview?api_key={API_KEY}",
json={
"image": {"type": "base64", "value": img_b64},
"prompts": [{"type": "text", "text": p} for p in PROMPTS],
},
headers={"Content-Type": "application/json"},
)
sam3_result = response.json()
h, w = cv2.imread("image.jpg").shape[:2]
detections = sv.Detections.from_sam3(sam3_result=sam3_result, resolution_wh=(w, h))
# class_id == 0 β "person", class_id == 1 β "bag"π Migration
VideoInfo.fps is now float
NTSC frame rates (23.976, 29.97, 59.94) were silently truncated. fps is now the true float β cast at call sites that need an integer.
Before
info = sv.VideoInfo.from_video_path("clip.mp4")
buf = collections.deque(maxlen=info.fps)
trace = sv.TraceAnnotator(trace_length=info.fps)After
info = sv.VideoInfo.from_video_path("clip.mp4")
buf = collections.deque(maxlen=int(info.fps))
trace = sv.TraceAnnotator(trace_length=int(info.fps))sv.ByteTrack deprecated β use ByteTrackTracker
Tracker implementations now live in the dedicated trackers package. sv.ByteTrack remains available in 0.28β0.29 with DeprecationWarning; removal in 0.30.0.
Before
tracker = sv.ByteTrack()
detections = tracker.update_with_detections(detections)After
# pip install trackers
from trackers import ByteTrackTracker
tracker = ByteTrackTracker()
detections = tracker.update(detections)π Added
-
Memory-efficient masks with
sv.CompactMask. Sparse segmentation masks are now stored as a crop region plus RLE-encoded data instead of full-resolution bitmaps, cutting memory use by 10β100Γ for typical instance-segmentation outputs. It's a drop-in change βsv.Detections.mask, filtering, merging, andareaall keep working without materialising the full array. (#2159) -
SAM3 detection and PVS support in
from_inference.sv.Detections.from_inferencenow parses SAM3 detection and point-video-segmentation outputs, both from the localinferencepackage and from Roboflow-hosted server responses. (#2103, #2152) -
Compressed COCO RLE masks in
from_inference. Inference responses withrleorrle_maskfields containing a compressed counts string (as produced bypycocotools) are decoded directly into binary masks, skipping the lossy polygon round-trip. (#2178) -
Standard
loggingmodule instead ofprint. Diagnostic output is now emitted under thesupervisionlogger, so applications can capture, filter, or silence it through standardloggingconfiguration. (#2154) -
RGBA hex codes in
sv.Color.sv.Color.from_hexaccepts 8-digit hex (#ff00ff80), andColor.as_hex()round-trips alpha when not fully opaque. New top-level helpers:sv.hex_to_rgba,sv.rgba_to_hex, andsv.is_valid_hex. (#2004) -
Dynamic kernel sizing in blur and pixelate annotators.
BlurAnnotator(kernel_size=None)andPixelateAnnotator(pixel_size=None)(the new default) compute the kernel per detection as a fraction of the shorter bounding-box side, giving visually consistent results across object scales. (#709) -
sv.ImageAssetsfor sample images. A counterpart to the existing video assets β downloads sample images for examples and tutorials. (#932) -
Boundary warnings in
InferenceSlicer. Emits a warning when callback detections fall outside tile boundaries, helping you spot coordinate-system bugs in custom callbacks early. (#2186)
β οΈ Breaking Changes
-
sv.VideoInfo.fpsis nowfloat, notint. Frame rates like 23.976, 29.97, and 59.94 are no longer truncated. If you passfpsto APIs that require an integer (deque(maxlen=...),TraceAnnotator(trace_length=...)), wrap withint(...). (#2210) -
sv.rle_to_maskreturnsbool, notuint8. This matches the long-declared signature. Code that doesmask * 255still works via NumPy broadcasting, but explicit casts likemask.view(np.uint8)will break. Add.astype(np.uint8)if you relied on the undocumented integer output. (#2178)
See the migration guide below for before/after snippets.
π± Changed
-
Metric arrays use
float32instead offloat64.sv.MeanAveragePrecisionResultand related arrays (mAP_scores,ap_per_class,iou_thresholds, precision/recall) drop tofloat32, reducing memory and speeding up computation. Numerical results may differ in the last few digits. (#2169) -
rle_to_maskandmask_to_rlemoved. New canonical path:supervision.detection.utils.converters. The oldsupervision.dataset.utilsimport still works but is deprecated. (#2178)
ποΈ Deprecated
-
normalized_xyxyargument renamed toxyxyindenormalize_boxes.sv.denormalize_boxes(normalized_xyxy=...)still works but emits aFutureWarning; switch toxyxy=. Scheduled for removal in 0.30.0. -
sv.ByteTrackβByteTrackTracker(externaltrackerspackage). Install withpip install trackers; the method renames fromupdate_with_detections()toupdate(). Scheduled for removal in 0.30.0. (#2215) -
supervision.keypointβsupervision.key_points. Also deprecated: theLMMenum (useVLM),from_lmm(usefrom_vlm),create_tilesinsupervision.utils.image,ensure_cv2_image_for_processinginsupervision.utils.conversion, and the keypoint validators insupervision.validators. (#2214)
π§ Fixed
-
PolygonZoneno longer double-counts overlapping zones. When two polygons contain the same anchor, each zone now reflects its own containment instead of every zone claiming the detection. (#1991) -
LineZonerespects class identity across reused tracker IDs. Trackers that recycletracker_idacross classes no longer leak crossing state from one object to another. (#1868) -
process_videoraises immediately on callback errors. Previously the exception was swallowed and the process hung until the writer was flushed. (#2022) -
DetectionDatasetpopulatesclass_name. Loaded annotations now carrydata["class_name"], matching what model connectors produce. (#2156) -
ByteTrackpreserves externally assignedtracker_id. No longer overwrites caller-assigned IDs on the first update. (#1364) -
Confusion matrix double-counting fixed.
evaluate_detection_batchnow correctly matches multiple predictions to the same target, so FP/FN counts match expectations. (#1853) -
MeanAverageRecallmAR@K is now COCO-compliant. Computed using top-K detections per image; previous values were...
supervision-0.27.0.post
Full Changelog: 0.27.0...0.27.0.post2
supervision-0.27.0
Description
π Added
- Added
sv.filter_segments_by_distanceto keep the largest connected component and any nearby components within an absolute or relative distance threshold. This helps you clean up predictions from segmentation models like SAM, SAM2, YOLO segmentation, and RF-DETR segmentation. (#2008)
supervision-0.27.0-filter-segments-by-distance.mp4
-
Added
sv.edit_distancefor Levenshtein distance between two strings. Supports insert, delete, substitute. (#1912)import supervision as sv sv.edit_distance("hello", "hello") # 0 sv.edit_distance("hello world", "helloworld") # 1 sv.edit_distance("YOLO", "yolo", case_sensitive=True) # 4
-
Added
sv.fuzzy_match_indexto find the first close match in a list using edit distance. (#1912)import supervision as sv sv.fuzzy_match_index(["cat", "dog", "rat"], "dat", threshold=1) # 0 sv.fuzzy_match_index(["alpha", "beta", "gamma"], "bata", threshold=1) # 1 sv.fuzzy_match_index(["one", "two", "three"], "ten", threshold=2) # None
-
Added
sv.get_image_resolution_whas a unified way to read image width and height from NumPy and PIL inputs. (#2014) -
Added
sv.tint_imageto apply a solid color overlay to an image at a specified opacity. Works with both NumPy and PIL inputs. (#1943) -
Added
sv.grayscale_imageto convert an image to 3-channel grayscale for compatibility with color-based drawing utilities. (#1943) -
Added
sv.xyxy_to_maskto convert bounding boxes into 2D boolean masks. Each mask corresponds to one bounding box. (#2006)
π± Changed
-
Added Qwen3-VL support in
sv.Detections.from_vlmand legacyfrom_lmmmapping. Usevlm=sv.QWEN_3_VL. (#2015)import supervision as sv response = """```json [ {"bbox_2d": [220, 102, 341, 206], "label": "taxi"}, {"bbox_2d": [30, 606, 171, 743], "label": "taxi"}, {"bbox_2d": [192, 451, 318, 581], "label": "taxi"}, {"bbox_2d": [358, 908, 506, 1000], "label": "taxi"}, {"bbox_2d": [735, 359, 873, 480], "label": "taxi"}, {"bbox_2d": [758, 508, 885, 617], "label": "taxi"}, {"bbox_2d": [857, 263, 988, 374], "label": "taxi"}, {"bbox_2d": [735, 243, 838, 351], "label": "taxi"}, {"bbox_2d": [303, 291, 434, 417], "label": "taxi"}, {"bbox_2d": [426, 273, 552, 382], "label": "taxi"} ] ```""" detections = sv.Detections.from_vlm( vlm=sv.VLM.QWEN_3_VL, result=response, resolution_wh=(1023, 682) ) detections.xyxy # array([[ 225.06 , 69.564, 348.843, 140.492], # [ 30.69 , 413.292, 174.933, 506.726], # [ 196.416, 307.582, 325.314, 396.242], # [ 366.234, 619.256, 517.638, 682. ], # [ 751.905, 244.838, 893.079, 327.36 ], # [ 775.434, 346.456, 905.355, 420.794], # [ 876.711, 179.366, 1010.724, 255.068], # [ 751.905, 165.726, 857.274, 239.382], # [ 309.969, 198.462, 443.982, 284.394], # [ 435.798, 186.186, 564.696, 260.524]])
-
Added DeepSeek-VL2 support in
sv.Detections.from_vlmand legacyfrom_lmmmapping. Usevlm=sv.VLM.DEEPSEEK_VL_2. (#1884) -
Improved
sv.Detections.from_vlmparsing for Qwen 2.5 VL outputs. The function now handles incomplete or truncated JSON responses. (#2015) -
sv.InferenceSlicernow uses a new offset generation logic that removes redundant tiles and ensures clean border aligned slicing. This reduces the number of tiles processed, lowering inference time without hurting detection quality. (#2014)
supervision-0.27.0-new-offset-generation-logic.mp4
import supervision as sv
from PIL import Image
from rfdetr import RFDETRMedium
model = RFDETRMedium()
def callback(tile):
return model.predict(tile)
slicer = sv.InferenceSlicer(callback, slice_wh=512, overlap_wh=128)
image = Image.open("example.png")
detections = slicer(image)sv.Detectionsnow includes abox_aspect_ratioproperty for vectorized aspect ratio computation. You use it to filter detections based on box shape. (#2016)
import numpy as np
import supervision as sv
xyxy = np.array([
[10, 10, 50, 50],
[60, 10, 180, 50],
[10, 60, 50, 180],
])
detections = sv.Detections(xyxy=xyxy)
ar = detections.box_aspect_ratio
# array([1.0, 3.0, 0.33333333])
detections[(ar < 2.0) & (ar > 0.5)].xyxy
# array([[10., 10., 50., 50.]])-
Improved the performance of
sv.box_iou_batch. Processing runs about 2x to 5x faster. (#2001) -
sv.process_videonow uses a threaded reader, processor, and writer pipeline. This removes I/O stalls and improves throughput while keeping the callback single threaded and safe for stateful models. (#1997) -
sv.denormalize_boxesnow supports batch conversion of bounding boxes. The function now accepts arrays of shape(N, 4)and returns a batch of absolute pixel coordinates. -
sv.LabelAnnotatorandsv.RichLabelAnnotatornow acceptstext_offset=(x, y)to shift the label relative totext_position. Works with smart label position and line wrapping. (#1917)
β Removed
- Removed the deprecated
overlap_ratio_whargument fromsv.InferenceSlicer. Use the pixel basedoverlap_whargument to control slice overlap. (#2014)
Tip
Convert your old ratio based overlap to pixel based overlap. Multiply each ratio by the slice dimensions.
# before
slice_wh = (640, 640)
overlap_ratio_wh = (0.25, 0.25)
slicer = sv.InferenceSlicer(
callback=callback,
slice_wh=slice_wh,
overlap_ratio_wh=overlap_ratio_wh,
overlap_filter=sv.OverlapFilter.NON_MAX_SUPPRESSION,
)
# after
overlap_wh = (
int(overlap_ratio_wh[0] * slice_wh[0]),
int(overlap_ratio_wh[1] * slice_wh[1]),
)
slicer = sv.InferenceSlicer(
callback=callback,
slice_wh=slice_wh,
overlap_wh=overlap_wh,
overlap_filter=sv.OverlapFilter.NON_MAX_SUPPRESSION,
)π Contributors
@SkalskiP (Piotr Skalski), @onuralpszr (Onuralp SEZER), @soumik12345 (Soumik Rakshit), @rcvsq, @AlexBodner (Alex Bodner), @Ashp116, @kshitijaucharmal (Kshitij Aucharmal), @ernestlwt, @AnonymDevOSS, @jackiehimel (Jackie Himel ), @dominikWin (Dominik Winecki)
supervision-0.26.1
π§ Fixed
- Fixed error in
sv.MeanAveragePrecisionwhere the area used for size-specific evaluation (small / medium / large) was always zero unless explicitly provided insv.Detections.data. (#1894) - Fixed
ID=0bug insv.MeanAveragePrecisionwhere objects were getting0.0mAP despite perfect IoU matches due to a bug in annotation ID assignment. (#1895) - Fixed issue where
sv.MeanAveragePrecisioncould return negative values when certain object size categories have no data. (#1898) - Fixed
match_metricsupport forsv.Detections.with_nms. (#1901) - Fixed
border_thicknessparameter usage forsv.PercentageBarAnnotator. (#1906)
π Contributors
@balthazur (Balthasar Huber), @onuralpszr (Onuralp SEZER), @rafaelpadilla (Rafael Padilla), @soumik12345 (Soumik Rakshit), @SkalskiP (Piotr Skalski)
supervision-0.26.0
Warning
supervision-0.26.0 drops python3.8 support and upgrade all codes to python3.9 syntax style.
Tip
Our docs page now has a fresh look that is consistent with the documentations of all Roboflow open-source projects. (#1858)
π Added
-
Added support for creating
sv.KeyPointsobjects from ViTPose and ViTPose++ inference results viasv.KeyPoints.from_transformers. (#1788)vitpose-plus-large.mp4
-
Added support for the IOS (Intersection over Smallest) overlap metric that measures how much of the smaller object is covered by the larger one in
sv.Detections.with_nms,sv.Detections.with_nmm,sv.box_iou_batch, andsv.mask_iou_batch. (#1774)import numpy as np import supervision as sv boxes_true = np.array([ [100, 100, 200, 200], [300, 300, 400, 400] ]) boxes_detection = np.array([ [150, 150, 250, 250], [320, 320, 420, 420] ]) sv.box_iou_batch( boxes_true=boxes_true, boxes_detection=boxes_detection, overlap_metric=sv.OverlapMetric.IOU ) # array([[0.14285714, 0. ], # [0. , 0.47058824]]) sv.box_iou_batch( boxes_true=boxes_true, boxes_detection=boxes_detection, overlap_metric=sv.OverlapMetric.IOS ) # array([[0.25, 0. ], # [0. , 0.64]])
-
Added
sv.box_iouthat efficiently computes the Intersection over Union (IoU) between two individual bounding boxes. (#1874) -
Added support for frame limitations and progress bar in
sv.process_video. (#1816) -
Added
sv.xyxy_to_xcycarhfunction to convert bounding box coordinates from(x_min, y_min, x_max, y_max)into measurement space to format(center x, center y, aspect ratio, height), where the aspect ratio iswidth / height. (#1823) -
AddedΒ
sv.xyxy_to_xywhfunction to convert bounding box coordinates from(x_min, y_min, x_max, y_max)format to(x, y, width, height)format. (#1788)
π± Changed
-
sv.LabelAnnotatornow supports thesmart_positionparameter to automatically keep labels within frame boundaries, and themax_line_lengthparameter to control text wrapping for long or multi-line labels. (#1820)supervision-0.26.0-2.mp4
-
sv.LabelAnnotatornow supports non-string labels. (#1825) -
sv.Detections.from_vlmnow supports parsing bounding boxes and segmentation masks from responses generated by Google Gemini models. You can test Gemini prompting, result parsing, and visualization with Supervision using this example notebook. (#1792)import supervision as sv gemini_response_text = """```json [ {"box_2d": [543, 40, 728, 200], "label": "cat", "id": 1}, {"box_2d": [653, 352, 820, 522], "label": "dog", "id": 2} ] ```""" detections = sv.Detections.from_vlm( sv.VLM.GOOGLE_GEMINI_2_5, gemini_response_text, resolution_wh=(1000, 1000), classes=['cat', 'dog'], ) detections.xyxy # array([[543., 40., 728., 200.], [653., 352., 820., 522.]]) detections.data # {'class_name': array(['cat', 'dog'], dtype='<U26')} detections.class_id # array([0, 1])
-
sv.Detections.from_vlmnow supports parsing bounding boxes from responses generated by Moondream. (#1878)import supervision as sv moondream_result = { 'objects': [ { 'x_min': 0.5704046934843063, 'y_min': 0.20069346576929092, 'x_max': 0.7049859315156937, 'y_max': 0.3012596592307091 }, { 'x_min': 0.6210969910025597, 'y_min': 0.3300672620534897, 'x_max': 0.8417936339974403, 'y_max': 0.4961046129465103 } ] } detections = sv.Detections.from_vlm( sv.VLM.MOONDREAM, moondream_result, resolution_wh=(1000, 1000), ) detections.xyxy # array([[1752.28, 818.82, 2165.72, 1229.14], # [1908.01, 1346.67, 2585.99, 2024.11]])
-
sv.Detections.from_vlmnow supports parsing bounding boxes from responses generated by Qwen-2.5 VL. You can test Qwen2.5-VL prompting, result parsing, and visualization with Supervision using this example notebook. (#1709)import supervision as sv qwen_2_5_vl_result = """```json [ {"bbox_2d": [139, 768, 315, 954], "label": "cat"}, {"bbox_2d": [366, 679, 536, 849], "label": "dog"} ] ```""" detections = sv.Detections.from_vlm( sv.VLM.QWEN_2_5_VL, qwen_2_5_vl_result, input_wh=(1000, 1000), resolution_wh=(1000, 1000), classes=['cat', 'dog'], ) detections.xyxy # array([[139., 768., 315., 954.], [366., 679., 536., 849.]]) detections.class_id # array([0, 1]) detections.data # {'class_name': array(['cat', 'dog'], dtype='<U10')} detections.class_id # array([0, 1])
-
Significantly improved the speed of HSV color mapping in
sv.HeatMapAnnotator, achieving approximately 28x faster performance on 1920x1080 frames. (#1786)
π§ Fixed
-
Supervisionβs
sv.MeanAveragePrecisionis now fully aligned with pycocotools, the official COCO evaluation tool, ensuring accurate and standardized metrics. (#1834)import supervision as sv from supervision.metrics import MeanAveragePrecision predictions = sv.Detections(...) targets = sv.Detections(...) map_metric = MeanAveragePrecision() map_metric.update(predictions, targets).compute() # Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.464 # Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.637 # Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.203 # Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.284 # Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ...
supervision-0.25.0
Supervision 0.25.0 is here! Featuring a more robust LineZone crossing counter, support for tracking KeyPoints, Python 3.13 compatibility, and 3 new metrics: Precision, Recall and Mean Average Recall. The update also includes smart label positioning, improved Oriented Bounding Box support, and refined error handling. Thank you to all contributors - especially those who answered the call of Hacktoberfest!
Changelog
π Added
- Essential update to the
LineZone: when computing line crossings, detections that jitter might be counted twice (or more!). This can now be solved with theminimum_crossing_thresholdargument. If you set it to2or more, extra frames will be used to confirm the crossing, improving the accuracy significantly. (#1540)
hb-final.mp4
- It is now possible to track objects detected as
KeyPoints. See the complete step-by-step guide in the Object Tracking Guide. (#1658)
import numpy as np
import supervision as sv
from ultralytics import YOLO
model = YOLO("yolov8m-pose.pt")
tracker = sv.ByteTrack()
trace_annotator = sv.TraceAnnotator()
def callback(frame: np.ndarray, _: int) -> np.ndarray:
results = model(frame)[0]
key_points = sv.KeyPoints.from_ultralytics(results)
detections = key_points.as_detections()
detections = tracker.update_with_detections(detections)
annotated_image = trace_annotator.annotate(frame.copy(), detections)
return annotated_image
sv.process_video(
source_path="input_video.mp4",
target_path="output_video.mp4",
callback=callback
)track-keypoints-with-smoothing.mp4
See the guide for the full code used to make the video
-
Added
is_emptymethod toKeyPointsto check if there are any keypoints in the object. (#1658) -
Added
as_detectionsmethod toKeyPointsthat convertsKeyPointstoDetections. (#1658) -
Added a new video to
supervision[assets]. (#1657)
from supervision.assets import download_assets, VideoAssets
path_to_video = download_assets(VideoAssets.SKIING)- Supervision can now be used with
Python 3.13. The most renowned update is the ability to run Python without Global Interpreter Lock (GIL). We expect support for this among our dependencies to be inconsistent, but if you do attempt it - let us know the results! (#1595)
- Added
Mean Average RecallmAR metric, which returns a recall score, averaged over IoU thresholds, detected object classes, and limits imposed on maximum considered detections. (#1661)
import supervision as sv
from supervision.metrics import MeanAverageRecall
predictions = sv.Detections(...)
targets = sv.Detections(...)
map_metric = MeanAverageRecall()
map_result = map_metric.update(predictions, targets).compute()
map_result.plot()- Added
PrecisionandRecallmetrics, providing a baseline for comparing model outputs to ground truth or another model (#1609)
import supervision as sv
from supervision.metrics import Recall
predictions = sv.Detections(...)
targets = sv.Detections(...)
recall_metric = Recall()
recall_result = recall_metric.update(predictions, targets).compute()
recall_result.plot()- All Metrics now support Oriented Bounding Boxes (OBB) (#1593)
import supervision as sv
from supervision.metrics import F1_Score
predictions = sv.Detections(...)
targets = sv.Detections(...)
f1_metric = MeanAverageRecall(metric_target=sv.MetricTarget.ORIENTED_BOUNDING_BOXES)
f1_result = f1_metric.update(predictions, targets).compute()- Introducing Smart Labels! When
smart_positionis set forLabelAnnotator,RichLabelAnnotatororVertexLabelAnnotator, the labels will move around to avoid overlapping others. (#1625)
import supervision as sv
from ultralytics import YOLO
image = cv2.imread("image.jpg")
label_annotator = sv.LabelAnnotator(smart_position=True)
model = YOLO("yolo11m.pt")
results = model(image)[0]
detections = sv.Detections.from_ultralytics(results)
annotated_frame = label_annotator.annotate(first_frame.copy(), detections)
sv.plot_image(annotated_frame)fish-z.mp4
- Added the
metadatavariable toDetections. It allows you to store custom data per-image, rather than per-detected-object as was possible withdatavariable. For example,metadatacould be used to store the source video path, camera model or camera parameters. (#1589)
import supervision as sv
from ultralytics import YOLO
model = YOLO("yolov8m")
result = model("image.png")[0]
detections = sv.Detections.from_ultralytics(result)
# Items in `data` must match length of detections
object_ids = [num for num in range(len(detections))]
detections.data["object_number"] = object_ids
# Items in `metadata` can be of any length.
detections.metadata["camera_model"] = "Luxonis OAK-D"- Added a
py.typedtype hints metafile. It should provide a stronger signal to type annotators and IDEs that type support is available. (#1586)
π± Changed
ByteTrackno longer requiresdetectionsto have aclass_id(#1637)draw_line,draw_rectangle,draw_filled_rectangle,draw_polygon,draw_filled_polygonandPolygonZoneAnnotatornow comes with a default color (#1591)- Dataset classes are treated as case-sensitive when merging multiple datasets. (#1643)
- Expanded metrics documentation with example plots and printed results (#1660)
- Added usage example for polygon zone (#1608)
- Small improvements to error handling in polygons: (#1602)
π§ Fixed
- Updated
ByteTrack, removing shared variables. Previously, multiple instances ofByteTrackwould share some date, requiring liberal use oftracker.reset(). (#1603), (#1528) - Fixed a bug where
class_agnosticsetting inMeanAveragePrecisionwould not work. (#1577) hacktoberfest - Removed welcome workflow from our CI system. (#1596)
β No removals or deprecations this time!
βοΈ Internal Changes
- Large refactor of
ByteTrack(#1603)- STrack moved to separate class
- Remove superfluous
BaseTrackclass - Removed unused variables
- Large refactor of
RichLabelAnnotator, matching its contents withLabelAnnotator. (#1625)
π Contributors
@onuralpszr (Onuralp SEZER), @kshitijaucharmal (KshitijAucharmal), @grzegorz-roboflow (Grzegorz Klimaszewski), @Kadermiyanyedi (Kader Miyanyedi), @PrakharJain1509 ([Pra...



