supervision

AIMIT

supervision release notes.

Latest 0.30.2 · by supervisionWritten in PythonWebsiteroboflow/supervisionRSS

Release activity

Release activity — 10 releases across 10 days in the last year. Each cell is one day; darker means more releases that day. Older weeks are hidden at this screen width.
JunJulAugSep
SundayNo releases on May 24, 2026No releases on May 31, 2026No releases on Jun 7, 2026No releases on Jun 14, 2026No releases on Jun 21, 2026No releases on Jun 28, 2026No releases on Jul 5, 2026No releases on Jul 12, 2026No releases on Jul 19, 2026No releases on Jul 26, 2026No releases on Aug 2, 2026No releases on Aug 9, 2026No releases on Aug 16, 2026No releases on Aug 23, 2026No releases on Aug 30, 2026No releases on Sep 6, 2026
MondayNo releases on May 25, 2026No releases on Jun 1, 2026No releases on Jun 8, 20261 release on Jun 15, 2026No releases on Jun 22, 2026No releases on Jun 29, 2026No releases on Jul 6, 2026No releases on Jul 13, 2026No releases on Jul 20, 2026No releases on Jul 27, 2026No releases on Aug 3, 2026No releases on Aug 10, 2026No releases on Aug 17, 20261 release on Aug 24, 2026No releases on Aug 31, 2026No releases on Sep 7, 2026
TuesdayNo releases on May 26, 2026No releases on Jun 2, 2026No releases on Jun 9, 2026No releases on Jun 16, 20261 release on Jun 23, 2026No releases on Jun 30, 2026No releases on Jul 7, 2026No releases on Jul 14, 2026No releases on Jul 21, 2026No releases on Jul 28, 20261 release on Aug 4, 2026No releases on Aug 11, 2026No releases on Aug 18, 2026No releases on Aug 25, 2026No releases on Sep 1, 2026No releases on Sep 8, 2026
WednesdayNo releases on May 27, 2026No releases on Jun 3, 2026No releases on Jun 10, 20261 release on Jun 17, 2026No releases on Jun 24, 2026No releases on Jul 1, 2026No releases on Jul 8, 2026No releases on Jul 15, 2026No releases on Jul 22, 2026No releases on Jul 29, 2026No releases on Aug 5, 2026No releases on Aug 12, 2026No releases on Aug 19, 2026No releases on Aug 26, 2026No releases on Sep 2, 2026No releases on Sep 9, 2026
ThursdayNo releases on May 28, 2026No releases on Jun 4, 20261 release on Jun 11, 2026No releases on Jun 18, 2026No releases on Jun 25, 2026No releases on Jul 2, 2026No releases on Jul 9, 2026No releases on Jul 16, 2026No releases on Jul 23, 2026No releases on Jul 30, 2026No releases on Aug 6, 2026No releases on Aug 13, 2026No releases on Aug 20, 2026No releases on Aug 27, 2026No releases on Sep 3, 2026
FridayNo releases on May 29, 2026No releases on Jun 5, 2026No releases on Jun 12, 2026No releases on Jun 19, 2026No releases on Jun 26, 2026No releases on Jul 3, 2026No releases on Jul 10, 2026No releases on Jul 17, 2026No releases on Jul 24, 2026No releases on Jul 31, 2026No releases on Aug 7, 2026No releases on Aug 14, 2026No releases on Aug 21, 2026No releases on Aug 28, 20261 release on Sep 4, 2026
SaturdayNo releases on May 30, 2026No releases on Jun 6, 2026No releases on Jun 13, 2026No releases on Jun 20, 2026No releases on Jun 27, 2026No releases on Jul 4, 2026No releases on Jul 11, 2026No releases on Jul 18, 2026No releases on Jul 25, 2026No releases on Aug 1, 2026No releases on Aug 8, 2026No releases on Aug 15, 2026No releases on Aug 22, 2026No releases on Aug 29, 2026No releases on Sep 5, 2026

10 releases in the last year

Changelog

0.30.2Latest

supervision-0.30.2

Fixed 9
  • sv.Detections.box_area and sv.Detections.area now compute integer-coordinate box areas in float64, preventing integer overflow for large boxes
  • sv.xcycwh_to_xyxy no longer truncates coordinates for integer input arrays
  • sv.denormalize_boxes no longer truncates coordinates for integer input arrays
  • sv.InferenceSlicer now merges slice results in source order when thread_workers > 1, restoring the documented ordering guarantee
  • Versioned documentation deployment for latest no longer fails with error: version 'latest' already exists when latest exists as an alias of a released version
  • Versioned documentation builds now emit a valid /latest/search/ SearchAction URL when Mike removes the trailing slash from site_url

From supervision

0.30.2: Detection numeric-correctness fixes

supervision 0.30.2 fixes three silent numeric-correctness bugs in the detection utilities — integer box areas that could wrap negative on large boxes, and two coordinate converters that truncated fractional values on integer input — plus an InferenceSlicer determinism fix that restores its documented source-order result guarantee under multithreading. A set of versioned-docs reliability fixes rounds out the release. No breaking API changes, no new public API.

✨ Spotlights / highlights
sv.Detections.box_area no longer overflows to a negative number

Integer-coordinate box area now computes in float64. A large int32 box (50000 x 50000) previously wrapped to a negative area.

detections = sv.Detections(xyxy=np.array([[0, 0, 50000, 50000]], dtype=np.int32))
detections.box_area  # array([2.5e+09]) — was negative before the fix
xcycwh_to_xyxy / denormalize_boxes stop truncating integer boxes

Both converters wrote fractional half-extent or scaled coordinates into a copy of the integer input, silently truncating toward zero — relevant when converting quantized VLM output (e.g. boxes on a 0..1000 grid).

xcycwh_to_xyxy(np.array([[10, 10, 5, 5]], dtype=np.int32))
# array([[ 7.5,  7.5, 12.5, 12.5]]) — the fractional coordinate 7.5 is no longer truncated to 7
InferenceSlicer merges results in source order under multithreading

Slice results now merge in source order under thread_workers > 1, restoring the ordering guarantee its docstring documents. Row order — and, for tied confidences, which overlapping box survives with_nms/with_nmm — no longer varies between runs on identical input.

🔄 Migration guide

No breaking API changes, but three fixes above change return dtype for integer input:

  • sv.Detections.box_area / .area — integer xyxy now returns float64 (was the input's integer dtype, which could silently overflow)
  • sv.xcycwh_to_xyxy — integer input now returns float64 (was truncated integer output)
  • sv.denormalize_boxes — integer input now returns float64 (was truncated integer output)

If your code indexes arrays with these outputs (e.g. image[y1:y2, x1:x2]), a float64 result raises TypeError: slice indices must be integers. Cast explicitly where integer indices are required: xcycwh_to_xyxy(boxes).astype(int).

📝 Notable changes
🔧 Fixed
  • sv.Detections.box_area (and sv.Detections.area for axis-aligned boxes) now computes integer-coordinate box areas in float64, preventing integer overflow for large boxes. (#2514)
  • sv.xcycwh_to_xyxy no longer truncates coordinates for integer input arrays. (#2515)
  • sv.denormalize_boxes no longer truncates coordinates for integer input arrays. (#2516)
  • sv.InferenceSlicer now merges slice results in source order when thread_workers > 1, restoring the ordering guarantee its docstring documents. (#2517)
  • Docs deployment for latest no longer fails with error: version 'latest' already exists when latest exists as an alias of a released version. (#2512, #2513)
  • Versioned documentation builds now emit a valid /latest/search/ SearchAction URL when Mike removes the trailing slash from site_url; docs CI renders the custom theme under Mike version contexts to protect the URL, version banners, and star JSON-LD. Applies to future builds going forward. (#2529)
  • Versioned documentation banners now adjust MkDocs Material's desktop sidebar inline layout and scroll height without shifting the mobile navigation drawer. (#2532)
  • Versioned documentation deploys now export the version being built, so the outdated-version banner reaches readers of the develop tree; the workflow also now backs up the pre-rewrite gh-pages tip to a timestamped branch before committing over it. Applies to future builds going forward. (#2533)
  • The canonical-backfill workflow now reports (in the job summary) rewritten canonicals whose target page does not exist under latest/, and backfills the outdated-version banner itself into already-published archive trees, patching the empty banner markup those pages already carry rather than rebuilding them. (#2534)
🏆 Contributors
  • Advait Shukla (@AdvaitS) — fixed integer overflow in box_area
  • Guillaume Flambard (@guillaume-flambard, LinkedIn) — fixed integer truncation in denormalize_boxes and xcycwh_to_xyxy
  • Roshan Sharma (@roshaninfordham, LinkedIn) — fixed InferenceSlicer result ordering under multithreading
  • Jirka Borovec (@Borda, LinkedIn) — versioned-docs banner layout, MIKE_DOCS_VERSION export, gh-pages backup, and canonical-backfill fixes (#2529, #2532, #2533, #2534)

Full changelog: https://github.com/roboflow/supervision/compare/0.30.1...0.30.2

View originalPermalink
How 0.30.2 went
0.30.1

supervision-0.30.1

Added 1
  • Add RF-DETR example scripts to count_people_in_zone, heatmap_and_track, speed_estimation, tracking, and traffic_analysis bundled examples
Changed 3
  • sv.box_iou now raises TypeError for complex-valued box coordinates instead of silently discarding the imaginary part
  • DetectionsSmoother.update_with_detections now checks active tracker IDs via set membership instead of scanning per tracked object
  • Documentation and API-reference examples now default to RF-DETR instead of Ultralytics YOLO
Fixed 8
  • RF-DETR speed estimation now measures elapsed source-frame intervals, including gaps when tracked detections are temporarily missed
  • sv.get_polygon_center now calculates polygon centroids in translated float64 coordinates, preventing integer overflow and precision loss for large-coordinate polygons
  • sv.Detections.area and sv.oriented_box_iou_batch now translate oriented-box coordinates to local origins before floating-point math, preventing self-IoU collapse for large-coordinate inputs
  • DetectionsSmoother now keeps oriented-box corners aligned with smoothed xyxy geometry, including rotated tracks and mixed metadata windows
  • sv.box_iou now calculates overlap in float64, preventing int32 area overflow for large boxes and matching sv.box_iou_batch for realistic coordinate magnitudes
  • sv.list_files_with_extensions no longer includes directories when listing all files without an extension filter
  • sv.pillow_to_cv2 now accepts RGBA images when the cv2-free fallback backend is active, matching OpenCV by dropping alpha and returning BGR channels
  • import supervision no longer loads PyAV's native libraries when the OpenCV backend is selected; PyAV is now imported lazily on first use, preventing duplicate libavdevice warning and possible crash on macOS when both av and opencv-python are installed

From supervision

0.30.1: Numeric-precision and stability fixes

supervision 0.30.1 is a bug-fix patch release. It corrects numeric-precision issues that only surface on specific inputs — large-coordinate oriented boxes (geospatial data, stitched frames), large integer boxes for box_iou, and rotated tracks in DetectionsSmoother — where prior versions could silently return imprecise or self-inconsistent results instead of erroring. It also fixes a duplicate-libavdevice-load crash risk on macOS when both av and opencv-python are installed, plus smaller fixes to list_files_with_extensions and the cv2-free RGBA fallback. No public API was added or removed, and no signature changed — a drop-in upgrade from 0.30.0 for virtually all users. See Migration guide below for the one narrow exception (box_iou on complex-valued coordinates) and for the precision caveats on the numeric fixes.

✨ Spotlights / highlights
1. Oriented-box area/IoU precision fix for large coordinates

sv.Detections.area and sv.oriented_box_iou_batch now translate OBB coordinates to a local origin before floating-point math. Previously, large-coordinate inputs could lose enough precision that a box's IoU with itself collapsed below 1.0.

pair_origin = np.minimum(origin_i, origin_j)
offset_i = (origin_i - pair_origin).astype(np.float32, copy=False)
offset_j = (origin_j - pair_origin).astype(np.float32, copy=False)
2. sv.box_iou no longer overflows on large integer boxes

Area computation now takes coordinate differences before casting to float, avoiding int32 overflow. For realistic coordinate magnitudes, box_iou's scalar result now matches box_iou_batch.

3. Duplicate libavdevice crash fixed on macOS

import supervision no longer loads PyAV's native libraries when the OpenCV backend is active — PyAV is now imported lazily, only where it's used, preventing a duplicate libavdevice warning (and possible crash) when both av and opencv-python are installed.

4. DetectionsSmoother keeps oriented-box corners consistent

Smoothed OBB corners are now aligned (start index + winding) to a reference before averaging, so rotated tracks smooth correctly instead of averaging mismatched corner orderings.

5. sv.get_polygon_center precision fix for large-coordinate polygons

Centroid calculation now translates to the first vertex and computes in float64 before adding the origin back, preventing integer overflow and precision loss for realistic coordinate magnitudes.

🔄 Migration guide

No public signature changed. One item below (box_iou on complex coordinates) does make one specific previously-succeeding call now raise — narrow and deliberate, not classified as breaking since complex-valued box coordinates were never a documented/supported input. The rest only change output values for inputs that were already edge cases:

  • sv.box_iou on complex-valued coordinates: previously silently discarded the imaginary part and returned a real number. Now raises TypeError("box coordinates must be real-valued").
  • OBB precision fixes: results for oriented boxes with large coordinates or rotated tracks may differ slightly from 0.30.0 — the new values are the corrected ones. Re-calibrate any hardcoded IoU/area thresholds tuned against the old (imprecise) output.
  • sv.box_iou / box_iou_batch agreement: for realistic integer coordinate magnitudes (below 2^53), box_iou's scalar result now matches box_iou_batch. Not a universal guarantee — box_iou subtracts before casting to float, box_iou_batch still casts to float64 before subtracting, so the two can diverge at coordinates ≥ 2^53 (~9 quadrillion), far outside any real use case.
📝 Notable changes
🚀 Added
  • RF-DETR example scripts (rfdetr_example.py) added to the count_people_in_zone, heatmap_and_track, speed_estimation, tracking, and traffic_analysis bundled examples. (#2497)
🌱 Changed
  • sv.box_iou now raises TypeError for complex-valued box coordinates instead of silently discarding the imaginary part. (#2485)
  • Performance: DetectionsSmoother.update_with_detections now checks active tracker IDs via set membership instead of scanning per tracked object. No output changes. (#2496)
  • Documentation and API-reference examples now default to RF-DETR instead of Ultralytics YOLO. (#2493, #2494, #2497)
🔧 Fixed
  • RF-DETR speed estimation now measures elapsed source-frame intervals, including gaps when tracked detections are temporarily missed. (#2497)
  • sv.get_polygon_center now calculates polygon centroids in translated float64 coordinates, preventing integer overflow and precision loss for realistic-magnitude large-coordinate polygons. (#2491)
  • sv.Detections.area and sv.oriented_box_iou_batch now translate oriented-box coordinates to local origins before floating-point math, preventing self-IoU collapse for large-coordinate inputs. (#2492)
  • DetectionsSmoother now keeps oriented-box corners aligned with smoothed xyxy geometry, including rotated tracks and mixed metadata windows. (#2489)
  • sv.box_iou now calculates overlap in float64, preventing int32 area overflow for large boxes; its scalar result now matches sv.box_iou_batch for realistic coordinate magnitudes. (#2485)
  • sv.list_files_with_extensions no longer includes directories when listing all files without an extension filter. (#2486)
  • sv.pillow_to_cv2 now accepts RGBA images when the cv2-free fallback backend is active, matching OpenCV by dropping alpha and returning BGR channels. (#2488)
  • import supervision no longer loads PyAV's native libraries when the OpenCV backend is selected; PyAV is now imported lazily on first use, preventing a duplicate libavdevice warning (and possible crash) on macOS when both av and opencv-python are installed. (#2509)
  • Docstring examples converted to executed doctests across annotators/core.py, dataset/formats/coco.py, dataset/formats/createml.py, and Detections.from_vlm; previously wrong documented outputs corrected for several VLM examples. (#2474, #2475, #2479, #2484)

Also in this release: routine dependency bumps (dependabot: astral-sh/setup-uv, wheel, pymdown-extensions x2, pypa/gh-action-pypi-publish, twine, cryptography), CI/docs-workflow maintenance, and test-only additions (geometry contract test, sklearn parity test) — none change installed package behavior. (#2028, #2470, #2472, #2473, #2480, #2481, #2482, #2483, #2499, #2501, #2506, #2507, #2508)

🏆 Contributors
  • lawliet (@lawliet206) — oriented-box area/IoU precision fix at large coordinate origins
  • Zhewen Tan (@tandede) — polygon centroid overflow/precision fix
  • Tamil Adhavan S K (@adhavan18, LinkedIn) — DetectionsSmoother oriented-box corner alignment fix; added geometry contract test
  • NIKHIL (@Nikhi00718) — fixed box_iou int32 overflow; added complex-coordinate TypeError guard
  • shao (@shaoming11, LinkedIn) — DetectionsSmoother tracker-ID lookup performance improvement
  • Tyyyy (@uczltw6) — RGBA image support in the cv2-free fallback conversion
  • ZZZZZ (@BruceWae) — fixed list_files_with_extensions to exclude directories
  • FootysHands (@ayo0la) — converted docstring examples to doctests; corrected wrong documented VLM example outputs
  • Swapnil Gautam (@Swapnil-gautam) — converted docstring examples to doctests in dataset format modules
  • Daniiiil1 (@Daniiiil1) — added sklearn parity test for metrics
  • Christoph Deil (@cdeil, LinkedIn) — repo maintenance docs

Full changelog: https://github.com/roboflow/supervision/compare/0.30.0...0.30.1

View originalPermalink
How 0.30.1 went
0.30.0

supervision-0.30.0

Added 7
  • Add sv.Detections.with_soft_nms, sv.box_soft_non_max_suppression, and sv.mask_soft_non_max_suppression for rescaling overlapping detections instead of discarding them
  • Add DetectionDataset.from_labelme and as_labelme for LabelMe dataset format support
  • Add DetectionDataset.from_createml and as_createml for CreateML dataset format support
  • Add sv.InferenceSlicer support for reading open rasterio dataset windows for GeoTIFF files without loading the entire image
  • Add batch_size parameter to sv.InferenceSlicer for batched-callback inference
  • Add sv.load_image_from_url to load images from HTTP(S) URLs with optional on-disk caching
  • Add sv.ImageWindow as replacement for cv2.imshow and cv2.waitKey
Changed 6
  • Make OpenCV optional; supervision now runs without opencv-python installed by requiring only NumPy, Pillow, and PyAV for video handling
  • Update sv.JSONSink to emit native JSON types (float, bool) instead of strings
  • Update sv.mask_non_max_merge to compute exact mask overlap instead of downscaled approximation
  • Update Detections.merge() on mixed dense and CompactMask inputs to return CompactMask instead of plain ndarray
  • Make av>=14.2 a required install-time dependency for PyAV video path
  • Update sv.CSVSink per-row custom-data slicing to match JSONSink behavior
Removed 2
  • Drop Python 3.9 support; Python 3.10 is now the minimum required version
  • Stop installing opencv-python by default; users must explicitly install opencv-python or opencv-python-headless if needed
Deprecated 1
  • Deprecate mask_dimension parameter in sv.mask_non_max_merge; removal planned for version 0.33.0

From supervision

v0.30.0: Run supervision without OpenCV

supervision 0.30.0 makes OpenCV optional. A new private _cv2/ backend (NumPy and Pillow, with PyAV for the video path) reimplements every OpenCV call the library needs, so supervision now runs on opencv-python-headless — or no OpenCV wheel at all — instead of crashing on import. This release also adds Soft-NMS, LabelMe and CreateML dataset formats, GeoTIFF-aware windowed reads for InferenceSlicer, and ships five breaking changes, most notably OpenCV no longer being installed by default, JSONSink switching to native JSON types, and mask_non_max_merge computing exact mask overlap instead of a downscaled approximation. Python 3.9 support is dropped — 3.10 is now the minimum.

✨ Spotlights / highlights
Run supervision without OpenCV
import supervision as sv

window = sv.ImageWindow("frame")
for frame in sv.get_video_frames_generator("input.mp4"):
    window.show(frame)
    if window.wait_key(1) == "q":
        break

The largest change in this release: OpenCV stays the default backend when installed, but supervision no longer requires it — there's no opencv-python extra anymore either. sv.ImageWindow replaces cv2.imshow/cv2.waitKey for display. av>=14.2 is now a required dependency for the PyAV video path during this transition. See the OpenCV migration guide.

Soft-NMS
detections = sv.Detections.from_ultralytics(result)
softened = detections.with_soft_nms(sigma=0.5)
filtered = detections.with_soft_nms(sigma=0.5, score_threshold=0.3)

sv.Detections.with_soft_nms (plus sv.box_soft_non_max_suppression / sv.mask_soft_non_max_suppression) rescales overlapping detections' confidence instead of discarding them outright — useful in crowded scenes where hard NMS drops valid overlapping objects.

New dataset formats + GeoTIFF-aware, batched slicing
dataset = sv.DetectionDataset.from_labelme(
    images_directory_path="images/",
    annotations_directory_path="annotations/",
)

import rasterio

with rasterio.open("RGB.byte.tif") as raster:
    slicer = sv.InferenceSlicer(callback=my_model_callback, batch_size=4)
    detections = slicer(raster)

DetectionDataset.from_labelme/as_labelme and from_createml/as_createml join the existing COCO/YOLO/Pascal-VOC converters. sv.InferenceSlicer can now read an open rasterio dataset window-by-window for multi-GB aerial/drone GeoTIFFs without loading the whole image (pip install "supervision[geotiff]"), and accepts batch_size for batched-callback inference.

sv.load_image_from_url
image = sv.load_image_from_url("https://media.roboflow.com/notebooks/examples/dog.jpeg")

Load an image straight from an HTTP(S) URL as an OpenCV array, with optional on-disk caching.

🔄 Migration guide

Five breaking changes. Most require no code changes beyond a type check or threshold recalibration. The two that need action from most users: the OpenCV install change below, and the Python 3.10 floor.

OpenCV is no longer installed by default. If a compatible cv2 is already importable in your environment, nothing changes for you — it's still preferred automatically. Otherwise install one wheel family yourself (pip install opencv-python or opencv-python-headless) if you need OpenCV-specific behavior, then restart the process — cv2 is detected once at import time. sv.ImageWindow replaces cv2.imshow/cv2.waitKey. Full guide: docs/how_to/opencv_migration.md.

Python 3.10+ is now required — 3.9 reached end-of-life in October 2025.

sv.JSONSink now emits native JSON types, not strings:

# before 0.30.0
row["score"] == "0.85"  # str
row["is_valid"] == "True"  # str

# after 0.30.0
row["score"] == 0.85  # float
row["is_valid"] is True  # bool

sv.CSVSink stays textual, but its per-row custom-data slicing now matches JSONSink.

sv.mask_non_max_merge computes exact mask overlap, not a downscaled approximation, and ignores the now-deprecated mask_dimension parameter (kept for signature compatibility, removal in 0.33.0). Re-tune your overlap threshold after upgrading. Passing overlap_metric/mask_dimension positionally still works — the values are still honored — but now emits a DeprecationWarning; pass them by keyword to silence it. More than five positional arguments raises TypeError.

Detections.merge() on mixed dense + CompactMask inputs now returns a CompactMask, not a plain ndarray:

merged = sv.Detections.merge([dense_detections, compact_mask_detections])
isinstance(merged.mask, np.ndarray)  # was True, now False — it's a CompactMask

Only affects code that explicitly merges a CompactMask-carrying Detections object with a dense-mask one yourself — InferenceSlicer, DetectionsSmoother, and with_nms/with_nmm always merge type-homogeneous lists internally, so they're unaffected. The all-dense merge path is also unchanged. This is a substantial performance win: ~2500× less peak memory, ~13× faster on a 1080p frame with 40 detections. If you need the old return type without touching every call site: call merged.mask = merged.mask.to_dense() right after merge(), or avoid producing CompactMask in the first place (Detections.from_inference(compact_masks=False), the default).

supervision also now requires av>=14.2 as an install-time dependency for the PyAV cv2-free video path — this doesn't change any API, so it isn't counted as breaking, but pinned/vendored environments should account for it.

Deprecation removals pushed back one release: ByteTrack, supervision.keypoint, normalized_xyxy, and supervision.dataset.utils RLE compatibility shims — originally scheduled for removal in 0.30.0 — are now scheduled for 0.31.0 instead, giving a full transition window.

📝 Notable changes
🚀 Added
  • sv.load_image_from_url — load an HTTP(S) image as an OpenCV array, with optional on-disk caching (#2372)
  • cv2-free PyAV video fallback + private _cv2 backend facade — image/geometry/drawing/text/video without OpenCV (#2430, #2431, #2432, #2433, #2435, #2438, #2439, #2440, #2441, #2443)
  • sv.ImageWindow — tkinter+Pillow desktop window replacing cv2.imshow/cv2.waitKey (#2320)
  • Soft-NMSsv.box_soft_non_max_suppression, sv.mask_soft_non_max_suppression, sv.Detections.with_soft_nms (#1624)
  • sv.VLM.GOOGLE_GEMINI_3_5Detections.from_vlm parses Gemini 3.5 output (#2449)
  • get_video_frames_generator(prefetch=...) — background-thread decode into a bounded queue (#2273)
  • PolygonZone(require_all_anchors=...) — toggle all-anchors vs. any-anchor containment (#2272)
  • KeyPoints.merge() — combine a list of KeyPoints, mirroring Detections.merge (#2412)
  • BaseAnnotator.requires_mask — class-level flag on all annotators (#2370)
  • CompactMask.from_coco_rle + Detections.from_inference(compact_masks=True) (#2367)
  • CompactMask.image_shape property (#2383)
  • sv.mask_to_roi — exclusive mask-bound helper for slicing/crops (#2416)
  • DetectionDataset.from_labelme/as_labelme (#2299)
  • DetectionDataset.from_createml/as_createml (#2284)
  • InferenceSlicer GeoTIFF supportsv.WindowedRasterDataset, pip install "supervision[geotiff]" (#2281)
  • InferenceSlicer(batch_size=...) — batched callback contract (#1239)
  • ConfusionMatrix.benchmark(save_directory_path=...) — adaptive TP/FP/FN validation-mosaic export (#2271)
  • HeatMapAnnotator.reset(), TraceAnnotator.reset(), DetectionsSmoother.reset() — clear accumulated per-stream state, so a single instance can be reused across independent streams (#2418)
  • AREA_DATA_FIELD config constant (#2428)
  • sv.denormalize_boxes and sv.xyxyxyxy_to_xyxy now exported at the top level
⚠️ Breaking Changes
  • OpenCV no longer installed by default; no OpenCV extra (#2443)
  • Python 3.10+ required — 3.9 dropped (#2260, #2381)
  • sv.JSONSink emits native JSON types instead of strings; sv.CSVSink custom-data slicing now matches JSONSink (#2400)
  • sv.mask_non_max_merge computes exact overlap, ignores mask_dimension, positional overlap_metric/mask_dimension deprecated (#2400)
  • Detections.merge() on mixed dense + CompactMask inputs returns CompactMask (#2383)
🌱 Changed
  • DetectionDataset/ClassificationDataset equality now compares ordered classes lists, not an unordered set
  • supervision now requires av>=14.2 as an install-time dependency for the cv2-free video fallback — no API change (#2438)
  • Deprecation-window delays: ByteTrack, supervision.keypoint, normalized_xyxy, dataset-utils RLE compat removals moved 0.30.00.31.0
  • Perf: count_nonzero mask pixel counts (#2361), vectorized box_iou_batch_with_jaccard (#2359), faster mask-annotation ROI blending (#2368), fewer corner circles on square label backgrounds (#2346), less compact-mask materialization in the polygon annotator (#2369)
  • Geometry-aware IoU/area dispatch centralized (#2374)
🔧 Fixed
  • sv.Recall tracks prediction-only classes, matching Precision/F1Score (#2467, #2468)
  • DetectionDataset.from_pascal_voc no longer raises on background images, with or without force_masks=True (#2463, #2469)
  • import supervision no longer surfaces the deprecated ByteTrack warning
  • Reopening sv.CSVSink/sv.JSONSink starts a fresh session — no stale rows or header (#2459)
  • from_vlm Gemini 2.0/2.5/3.5 salvages valid entries from partially malformed JSON arrays (#2449)
  • save_coco_annotations/as_coco read image sizes from headers, no pixel decode for labels-only export (#2442)
  • sv.F1Score no longer emits a spurious div-by-zero RuntimeWarning (#2437)
  • Size-bucketed Precision/Recall/F1Score no longer miscount out-of-bucket detections (#2427, #2428, #2408)
  • sv.box_iou_batch upcasts corners to float64, fixing int32-coordinate overflow into a wrong 0.0 IoU (#2418)
  • from_tensorflow scales boxes by correct axes (#2360); from_inference stays aligned on partial masks (#2362) and partial tracker_id (#2353)
  • get_anchors_coordinates is OBB-aware (#2382)
  • Annotator clipping: CropAnnotator (#2391), HeatMapAnnotator uint8 wrap (#2393), BackgroundOverlayAnnotator negative coords (#2396); get_video_frames_generator releases capture via try/finally (#2393)
  • ByteTrack no longer mutates input Detections; hardened edge cases (#2413)
  • KeyPoints.as_detections accepts numpy/tuple/generator indices (#2402)
  • hex_to_rgba rejects multiple leading # (#2421); Color(...) validates RGBA range (#2407)
  • ColorPalette.by_idx() on empty palette raises ValueError, not ZeroDivisionError (#2407)
  • Metrics scoring hardening: greedy matching (#2380), COCO 101-point AP, ConfusionMatrix rejects invalid class ids, per-class recall per max-det cutoff, user ignore flags preserved; FP counted on empty-GT images (#2397)
  • Dataset IO hardening — no caller mutation, class-id validation, optional COCO fields, VOC determinized, basename-collision preflight, RGBA/palette PNG support (#2394, #2410, #2416)
  • Classifications.from_timm softmaxes logits; download_assets verifies MD5 + retries once (#2414)
  • ImageSink.save_image() raises OSError on write failure (#2416)
  • Replaced deprecated 2-D np.cross with explicit determinant (#2386); removed defensive asserts in image annotators (#2354)
  • cv2-free fallback correctness fixes across border/blend/polygon/text/color operations (#2431, #2433, #2439, #2440, #2441)

🏆 Contributors
  • Abhijith Neil Abraham (@abhijithneilabraham, LinkedIn) — added KeyPoints.merge(); fixed out-of-bucket metric scoring and key_points edge cases
  • Agis Kounelis (@kounelisagis, LinkedIn) — made get_anchors_coordinates OBB-aware; kept from_inference aligned on partial data
  • Andrew Barnes (@Bortlesboat, LinkedIn) — fixed sink state on reopen
  • Arthi Arumugam (@arthi-arumugam-git, LinkedIn) — fixed the Recall metric to track prediction-only classes
  • Dylan Parsons (@dylanparsons, LinkedIn) — converted Detections doctests to runnable examples
  • Erik (@Erol444) — added sv.load_image_from_url
  • Yann Hallouard (@YHallouard, LinkedIn) — added Soft-NMS
  • Lee Clement (@leeclemnet) — fixed COCO export to read image sizes from headers
  • Linas Kondrackis (@LinasKo, LinkedIn) — added batching to InferenceSlicer
  • Madhav-C (@madhavcodez, LinkedIn) — added LabelMe and CreateML dataset formats, GeoTIFF InferenceSlicer support
  • Mahbod (@Ace3Z) — added prefetch to get_video_frames_generator, require_all_anchors to PolygonZone
  • Matt Van Horn (@mvanhorn, LinkedIn) — centralized geometry-aware IoU/area dispatch
  • Murillo Rodrigues (@murillo-ro-silva, LinkedIn) — added show_progress to dataset load/save (0.29.1)
  • Nick Herrig (@NickHerrig, LinkedIn) — added the face-blurring cookbook
  • Piotr Skalski (@SkalskiP, LinkedIn) — added Gemini 3.5 Flash VLM support
  • Ruben (@RubenHaisma) — perf fixes across mask counting, box IoU, from_tensorflow
  • Saif Khan (@K-saif, LinkedIn) — added the adaptive TP/FP/FN validation mosaic export
  • Shadow_Lu (@LuShadowX) — fixed class_id to stay integral for VOC background images
  • shao (@shaoming11, LinkedIn) — improved draw/utils.py doctests
  • Shehzad Waseem (@Shehzad3684) — fixed a division-by-zero warning in F1Score
  • Teïlo M (@teilomillet) — fixed the hex parser accepting multiple leading prefixes
  • Vikas Saini (@vikassaini77, LinkedIn) — converted fenced examples to doctests; removed defensive asserts in annotators
  • Jirka Borovec (@Borda, LinkedIn) — built the cv2-free OpenCV-optional backend (image, geometry, drawing, text, and PyAV video fallback) end to end, plus various hardening fixes across detection, dataset, and metrics modules; release maintainer

Full changelog: https://github.com/roboflow/supervision/compare/0.29.1...0.30.0

View originalPermalink
How 0.30.0 went
0.29.1

supervision-0.29.1

Added 1
  • Add `KeyPoints.with_nms()` method to remove duplicate skeletons using non-maximum suppression on axis-aligned bounding boxes derived from keypoints, supporting `class_agnostic` mode and any `OverlapMetric`
Changed 3
  • Optimize `sv.HaloAnnotator` to use the same optimized CompactMask paint path as `MaskAnnotator`, reducing memory materialization
  • Optimize mask IoU computation to use matrix multiplication on flattened masks instead of explicit tensor allocation
  • Vectorize `sv.mask_to_xyxy` and `sv.KeyPoints.as_detections` to use batched NumPy operations instead of per-element loops
Fixed 7
  • Fix `sv.DetectionDataset.as_pascal_voc` mutating bounding boxes by shifting them +1 px in-place on each export
  • Fix `sv.Precision` and `sv.F1Score` to correctly count background false positives under `MICRO` and `MACRO` averaging modes
  • Fix `sv.DetectionsSmoother` raising when detections have no confidence scores
  • Fix `sv.Detections.from_vlm` to degrade gracefully on malformed Gemini and Qwen output instead of raising `TypeError`
  • Fix `sv.JSONSink` serializing NumPy scalars in `custom_data` by converting them to JSON-compatible types
  • Fix `sv.approximate_polygon` exceeding the point-count budget and validate `epsilon_step` to be positive
  • Fix COCO export to preserve all polygon segments for multi-part masks instead of writing only the first polygon

From supervision

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 skeletons

Derives 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.

https://github.com/user-attachments/assets/ed7bd310-4868-4275-ae04-c88e7a0c2561


Notable changes
Bug fixes
  • sv.DetectionDataset.as_pascal_voc no 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.Precision and sv.F1Score correctly count background false positives (#2331) Predictions on images with no ground-truth objects, and predictions of classes absent from any annotation, were previously ignored. Under MICRO and MACRO averaging they are now counted as false positives. WEIGHTED averaging is unchanged. Users should re-evaluate existing metric results after upgrading.

  • sv.DetectionsSmoother works 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 produce None.

  • sv.Detections.from_vlm is robust to malformed Gemini/Qwen output (#2342) Valid JSON that is not a list, or whose elements are not dicts, now degrades to empty Detections instead of raising TypeError. A malformed mask value in Gemini 2.5 responses no longer misaligns the xyxy/confidence/masks arrays.

  • sv.JSONSink serializes NumPy scalars in custom_data (#2334) np.int64 frame indices and other NumPy scalars in custom_data no longer raise TypeError at flush time. NumPy arrays are serialized as lists. The file handle closes even when serialization fails.

  • sv.approximate_polygon respects the point-count budget (#2332) The function now returns at most floor(N * (1 - percentage)) points (minimum 3). Previously it could return more points than requested. epsilon_step is 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.HaloAnnotator is ~4× faster with CompactMask detections (#2339) HaloAnnotator now uses the same optimized CompactMask paint path as MaskAnnotator. 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_xyxy and sv.KeyPoints.as_detections vectorized (#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: https://github.com/roboflow/supervision/compare/0.29.0...0.29.1

View originalPermalink
How 0.29.1 went
0.29.0

supervision-0.29.0

Added 5
  • Added sv.VertexEllipseAreaAnnotator, sv.VertexEllipseOutlineAnnotator, and sv.VertexEllipseHaloAnnotator for visualizing keypoint uncertainty as covariance ellipses
  • Added sv.oriented_box_non_max_suppression and sv.oriented_box_non_max_merge for performing NMS and NMM directly on oriented bounding boxes
  • Added OBB (Oriented Bounding Box) support to sv.ConfusionMatrix via MetricTarget.ORIENTED_BOUNDING_BOXES
  • Added preserve_audio parameter to sv.process_video to mux the audio stream from the source video into the output using ffmpeg
  • Added is_obb parameter to sv.DetectionDataset.as_yolo for exporting oriented bounding box annotations in the YOLO OBB format
Changed 3
  • sv.EdgeAnnotator and sv.VertexAnnotator now respect the visible mask, skipping invisible keypoints and their edges during rendering
  • sv.EdgeAnnotator and sv.VertexLabelAnnotator now support per-class skeleton definitions for correct rendering with multiple skeleton topologies
  • sv.Detections.with_nms and sv.Detections.with_nmm are now OBB-aware, using oriented-box IoU automatically when oriented box coordinates are present

From supervision

🚀 Added
🌱 Changed
  • sv.Detections.area is 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.InferenceSlicer now detects OBB outputs from callbacks and automatically falls back to sequential processing to avoid thread-safety issues when thread_workers > 1. (#2256)

  • Fixed sv.oriented_box_iou_batch to 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_video audio 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_vlm returning None for class_id on empty VLM parses. Now returns an empty int ndarray. (#2239)

  • Fixed sv.Detections.from_inference to preserve class_name as a string-dtype array when predictions are empty. Previously it returned an untyped empty array. (#2270)

  • Fixed sv.HeatMapAnnotator divide-by-zero crash when called with empty detections. (#2269)

  • Fixed COCO export emitting 0-indexed category_id values. 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_yolo losing OBB rotation when exporting oriented bounding boxes. (#2289)

  • Fixed YOLO dataset loading to sort class names by numeric keys when the data.yaml uses integer class IDs. (#2296)

  • Fixed letterbox utility to support grayscale images. (#2297)

  • Fixed file extension filters to normalize casing (e.g. .JPG now matches .jpg). (#2298)

⚠️ Deprecated
DeprecatedRemovalReplacement
KeyPoints.confidence0.32.0KeyPoints.keypoint_confidence
merge_inner_detection_object_pair0.32.0None (internal use only)
merge_inner_detections_objects0.32.0None (internal use only)
merge_inner_detections_objects_without_iou0.32.0None (internal use only)
validate_detections_fields0.32.0None (internal use only)
validate_vlm_parameters0.32.0None (internal use only)
validate_fields_both_defined_or_none0.32.0None (internal use only)
validate_xyxy0.32.0None (internal use only)
validate_mask0.32.0None (internal use only)
validate_class_id0.32.0None (internal use only)
validate_confidence0.32.0None (internal use only)
validate_tracker_id0.32.0None (internal use only)
validate_data0.32.0None (internal use only)
validate_xy0.32.0None (internal use only)
validate_key_point_confidence0.32.0None (internal use only)
validate_key_points_fields0.32.0None (internal use only)
validate_resolution0.32.0None (internal use only)
validate_custom_values0.32.0None (internal use only)
validate_input_tensors0.32.0None (internal use only)
validate_labels0.32.0None (internal use only)
🏆 Contributors

@SkalskiP (Piotr Skalski), @Borda (Jirka Borovec), @kounelisagis (Agis Kounelis), @RitwijParmar (Ritwij Aryan Parmar), @Khanz9664 (Shahid Ul Islam), @satishkc7 (SATISH K C), @Ace3Z (Mahbod Tajdini), @Madhav-C, @RubenHaisma (Ruben Haisma), @adhavan18 (Tamil Adhavan), @Bortlesboat (Andrew Barnes), @Lourdhu02, @tarunbommawar27, @YousefZahran1 (Youssef Ibrahim), @JFrench-Enterprise, @Patel-Prem (Premkumar Patel)

View originalPermalink
How 0.29.0 went
0.28.0

supervision-0.28.0

Added 7
  • Add sv.CompactMask for memory-efficient storage of segmentation masks using tight bounding-box crops and RLE encoding, reducing memory usage by 10–100× while maintaining API compatibility with existing mask operations
  • Add sv.Detections.from_sam3() to parse SAM3 text-prompted segmentation responses in both PCS (multi-prompt) and PVS (video) formats into standard Detections objects
  • Add SAM3 detection and point-video-segmentation output parsing to sv.Detections.from_inference() for both local inference package and Roboflow-hosted server responses
  • Add support for compressed COCO RLE masks in sv.Detections.from_inference() to decode RLE or rle_mask fields directly into binary masks
  • Replace print-based diagnostic output with standard logging module under the supervision logger
  • Add RGBA hex code support to sv.Color.from_hex() for 8-digit hex values and add sv.hex_to_rgba(), sv.rgba_to_hex(), and sv.is_valid_hex() helper functions
  • Add dynamic kernel sizing to BlurAnnotator and PixelateAnnotator that computes kernel size per detection as a fraction of the bounding-box side for consistent results across object scales
Changed 1
  • Change sv.VideoInfo.fps from int to float to preserve true NTSC frame rates (23.976, 29.97, 59.94) instead of truncating them
Deprecated 1
  • Deprecate sv.ByteTrack in favor of ByteTrackTracker from the dedicated trackers package, with removal planned for version 0.30.0

From supervision

🔦 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.

info = sv.VideoInfo.from_video_path("clip.mp4")
buf = collections.deque(maxlen=info.fps)
trace = sv.TraceAnnotator(trace_length=info.fps)
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.

tracker = sv.ByteTrack()
detections = tracker.update_with_detections(detections)
# 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, and area all keep working without materialising the full array. (#2159)

  • SAM3 detection and PVS support in from_inference. sv.Detections.from_inference now parses SAM3 detection and point-video-segmentation outputs, both from the local inference package and from Roboflow-hosted server responses. (#2103, #2152)

  • Compressed COCO RLE masks in from_inference. Inference responses with rle or rle_mask fields containing a compressed counts string (as produced by pycocotools) are decoded directly into binary masks, skipping the lossy polygon round-trip. (#2178)

  • Standard logging module instead of print. Diagnostic output is now emitted under the supervision logger, so applications can capture, filter, or silence it through standard logging configuration. (#2154)

  • RGBA hex codes in sv.Color. sv.Color.from_hex accepts 8-digit hex (#ff00ff80), and Color.as_hex() round-trips alpha when not fully opaque. New top-level helpers: sv.hex_to_rgba, sv.rgba_to_hex, and sv.is_valid_hex. (#2004)

  • Dynamic kernel sizing in blur and pixelate annotators. BlurAnnotator(kernel_size=None) and PixelateAnnotator(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.ImageAssets for 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.fps is now float, not int. Frame rates like 23.976, 29.97, and 59.94 are no longer truncated. If you pass fps to APIs that require an integer (deque(maxlen=...), TraceAnnotator(trace_length=...)), wrap with int(...). (#2210)

  • sv.rle_to_mask returns bool, not uint8. This matches the long-declared signature. Code that does mask * 255 still works via NumPy broadcasting, but explicit casts like mask.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 float32 instead of float64. sv.MeanAveragePrecisionResult and related arrays (mAP_scores, ap_per_class, iou_thresholds, precision/recall) drop to float32, reducing memory and speeding up computation. Numerical results may differ in the last few digits. (#2169)

  • rle_to_mask and mask_to_rle moved. New canonical path: supervision.detection.utils.converters. The old supervision.dataset.utils import still works but is deprecated. (#2178)

🗑️ Deprecated
  • normalized_xyxy argument renamed to xyxy in denormalize_boxes. sv.denormalize_boxes(normalized_xyxy=...) still works but emits a FutureWarning; switch to xyxy=. Scheduled for removal in 0.30.0.

  • sv.ByteTrackByteTrackTracker (external trackers package). Install with pip install trackers; the method renames from update_with_detections() to update(). Scheduled for removal in 0.30.0. (#2215)

  • supervision.keypointsupervision.key_points. Also deprecated: the LMM enum (use VLM), from_lmm (use from_vlm), create_tiles in supervision.utils.image, ensure_cv2_image_for_processing in supervision.utils.conversion, and the keypoint validators in supervision.validators. (#2214)

🔧 Fixed
  • PolygonZone no 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)

  • LineZone respects class identity across reused tracker IDs. Trackers that recycle tracker_id across classes no longer leak crossing state from one object to another. (#1868)

  • process_video raises immediately on callback errors. Previously the exception was swallowed and the process hung until the writer was flushed. (#2022)

  • DetectionDataset populates class_name. Loaded annotations now carry data["class_name"], matching what model connectors produce. (#2156)

  • ByteTrack preserves externally assigned tracker_id. No longer overwrites caller-assigned IDs on the first update. (#1364)

  • Confusion matrix double-counting fixed. evaluate_detection_batch now correctly matches multiple predictions to the same target, so FP/FN counts match expectations. (#1853)

  • MeanAverageRecall mAR@K is now COCO-compliant. Computed using top-K detections per image; previous values were inflated relative to pycocotools. (#2136)

  • Detections.is_empty() handles empty tracker_id. Returns True for zero-row detections regardless of whether tracker_id is None or an empty array. (#2209)

  • CSVSink and JSONSink slice custom_data per row. NumPy arrays, lists, and tuples whose length matches the detection count are now indexed per row, instead of being written whole for every detection. (#2199, #2216)

  • TraceAnnotator smooth mode handles stationary tracks. Deduplicates anchor points and falls back to a raw polyline when splprep cannot fit fewer than 4 unique points. (#2217)

  • load_coco_annotations rejects path-traversal annotations. Refuses file_name entries that escape the images directory via ../ or absolute paths. (#2218)

  • OBB datasets no longer blow up memory. Loading oriented-bounding-box datasets stopped allocating full-image masks per box. (#2187)

  • KeyPoints boolean mask indexing fixed. Uniform-count selection now works correctly when all instances share the same keypoint count. (#2188)

  • DetectionDataset.as_coco() preserves area and iscrowd. No longer dropped silently in the round-trip. (#2185)

  • force_mask=True precision and COCO empty-polygon export. Annotation conversion no longer loses precision, and COCO export tolerates empty polygons across formats. (#1746, #1086, #265)


🏆 Contributors

A huge thank you to everyone who shipped this release:

  • @Erol444 — SAM3 detection and PVS parsing
  • @leeclemnet (LinkedIn) — compressed COCO RLE masks and rle_to_mask correctness
  • @abritton2002 — VideoInfo.fps as float and Detections.is_empty() fix
  • @shaun0927 (LinkedIn) — sink slicing, trace annotator, COCO path-traversal hardening
  • @happyhj (LinkedIn) — class_name in DetectionDataset
  • @farukalamai (LinkedIn) — CSVSink NumPy slicing
  • @stop1one (LinkedIn) — COCO-compliant MeanAverageRecall
  • @Adithi-Sreenath (LinkedIn) — PolygonZone overlap fix
  • @JESUSROYETH — LineZone class-aware tracker IDs
  • @realh4m — process_video error propagation
  • @rolson24 (LinkedIn) — ByteTrack preserves external tracker IDs
  • @panagiotamoraiti (LinkedIn) — confusion matrix correctness
  • @Youho99, @kirilllzaitsev — COCO empty polygons and force_masks consistency
  • @aza-ali — RGBA hex support in sv.Color
  • @Clemens-E — dynamic kernel sizing for blur and pixelate annotators
  • @NickHerrig (LinkedIn) — sv.ImageAssets
  • @0xD4rky — force_mask=True precision fix
  • @Borda (LinkedIn) — CompactMask, metrics float32, deprecations

Full changelog: https://github.com/roboflow/supervision/compare/0.27.0...0.28.0

View originalPermalink
How 0.28.0 went
0.27.0

supervision-0.27.0

Added 7
  • Added sv.filter_segments_by_distance to keep the largest connected component and any nearby components within an absolute or relative distance threshold
  • Added sv.edit_distance for Levenshtein distance between two strings supporting insert, delete, and substitute operations
  • Added sv.fuzzy_match_index to find the first close match in a list using edit distance
  • Added sv.get_image_resolution_wh as a unified way to read image width and height from NumPy and PIL inputs
  • Added sv.tint_image to apply a solid color overlay to an image at a specified opacity, supporting both NumPy and PIL inputs
  • Added sv.grayscale_image to convert an image to 3-channel grayscale for compatibility with color-based drawing utilities
  • Added sv.xyxy_to_mask to convert bounding boxes into 2D boolean masks
Changed 4
  • Added Qwen3-VL support in sv.Detections.from_vlm and legacy from_lmm mapping
  • Added DeepSeek-VL2 support in sv.Detections.from_vlm and legacy from_lmm mapping
  • Improved sv.Detections.from_vlm parsing for Qwen 2.5 VL outputs to handle incomplete or truncated JSON responses
  • sv.InferenceSlicer now uses new offset generation logic that removes redundant tiles and ensures clean border aligned slicing, reducing the number of tiles processed and lowering inference time

From supervision

Description

🚀 Added
  • Added sv.filter_segments_by_distance to 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)

https://github.com/user-attachments/assets/2bdfd45d-b235-414b-91a3-6544d7c2b4ec

  • Added sv.edit_distance for 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_index to 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_wh as a unified way to read image width and height from NumPy and PIL inputs. (#2014)

  • Added sv.tint_image to apply a solid color overlay to an image at a specified opacity. Works with both NumPy and PIL inputs. (#1943)

  • Added sv.grayscale_image to convert an image to 3-channel grayscale for compatibility with color-based drawing utilities. (#1943)

  • Added sv.xyxy_to_mask to convert bounding boxes into 2D boolean masks. Each mask corresponds to one bounding box. (#2006)

🌱 Changed
  • Added Qwen3-VL support in sv.Detections.from_vlm and legacy from_lmm mapping. Use vlm=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_vlm and legacy from_lmm mapping. Use vlm=sv.VLM.DEEPSEEK_VL_2. (#1884)

  • Improved sv.Detections.from_vlm parsing for Qwen 2.5 VL outputs. The function now handles incomplete or truncated JSON responses. (#2015)

  • sv.InferenceSlicer now 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)

https://github.com/user-attachments/assets/0141ff44-0269-472c-900d-610f47330d57

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.Detections now includes a box_aspect_ratio property 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_video now 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_boxes now supports batch conversion of bounding boxes. The function now accepts arrays of shape (N, 4) and returns a batch of absolute pixel coordinates.

  • sv.LabelAnnotator and sv.RichLabelAnnotator now accepts text_offset=(x, y) to shift the label relative to text_position. Works with smart label position and line wrapping. (#1917)

❌ Removed
  • Removed the deprecated overlap_ratio_wh argument from sv.InferenceSlicer. Use the pixel based overlap_wh argument 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)

View originalPermalink
How 0.27.0 went
0.26.1

supervision-0.26.1

Fixed 5
  • Fixed error in sv.MeanAveragePrecision where the area used for size-specific evaluation (small / medium / large) was always zero unless explicitly provided in sv.Detections.data
  • Fixed ID=0 bug in sv.MeanAveragePrecision where objects were getting 0.0 mAP despite perfect IoU matches due to a bug in annotation ID assignment
  • Fixed issue where sv.MeanAveragePrecision could return negative values when certain object size categories have no data
  • Fixed match_metric support for sv.Detections.with_nms
  • Fixed border_thickness parameter usage for sv.PercentageBarAnnotator

From supervision

🔧 Fixed
🏆 Contributors

@balthazur (Balthasar Huber), @onuralpszr (Onuralp SEZER), @rafaelpadilla (Rafael Padilla), @soumik12345 (Soumik Rakshit), @SkalskiP (Piotr Skalski)

View originalPermalink
How 0.26.1 went
0.26.0

supervision-0.26.0

Added 6
  • Add support for creating sv.KeyPoints objects from ViTPose and ViTPose++ inference results via sv.KeyPoints.from_transformers
  • Add support for the IOS (Intersection over Smallest) overlap metric in sv.Detections.with_nms, sv.Detections.with_nmm, sv.box_iou_batch, and sv.mask_iou_batch
  • Add sv.box_iou function to efficiently compute the Intersection over Union between two individual bounding boxes
  • Add support for frame limitations and progress bar in sv.process_video
  • Add sv.xyxy_to_xcycarh function to convert bounding box coordinates from (x_min, y_min, x_max, y_max) to (center x, center y, aspect ratio, height) format
  • Add sv.xyxy_to_xywh function to convert bounding box coordinates from (x_min, y_min, x_max, y_max) format to (x, y, width, height) format
Changed 4
  • Upgrade all code to Python 3.9 syntax style
  • sv.LabelAnnotator now supports the smart_position parameter to automatically keep labels within frame boundaries and the max_line_length parameter to control text wrapping
  • sv.LabelAnnotator now supports non-string labels
  • sv.Detections.from_vlm now supports parsing bounding boxes and segmentation masks from responses generated by Google Gemini models
Removed 1
  • Drop Python 3.8 support

From supervision

[!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.KeyPoints objects from ViTPose and ViTPose++ inference results via sv.KeyPoints.from_transformers. (#1788)

    https://github.com/user-attachments/assets/f1917032-29d8-4b88-b871-65c2e28a756e

  • 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, and sv.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_iou that 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_xcycarh function 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 is width / height. (#1823)

  • Added sv.xyxy_to_xywh function to convert bounding box coordinates from (x_min, y_min, x_max, y_max) format to (x, y, width, height) format. (#1788)

🌱 Changed
  • sv.LabelAnnotator now supports the smart_position parameter to automatically keep labels within frame boundaries, and the max_line_length parameter to control text wrapping for long or multi-line labels. (#1820)

    https://github.com/user-attachments/assets/361c17c7-0810-466d-907d-c752e91bc6f7

  • sv.LabelAnnotator now supports non-string labels. (#1825)

  • sv.Detections.from_vlm now 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_vlm now 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_vlm now 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.MeanAveragePrecision is 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 ] = 0.497
    # Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.629
    

[!TIP] The updated mAP implementation enabled us to build an updated version of the Computer Vision Model Leaderboard.

  • Fix #1767: Fixed losing sv.Detections.data when detections filtering.
⚠️ Deprecated
❌ Removed
  • The sv.DetectionDataset.images property has been removed in supervision-0.26.0. Please loop over images with for path, image, annotation in dataset:, as that does not require loading all images into memory.
  • Cconstructing sv.DetectionDataset with parameter images as Dict[str, np.ndarray] is deprecated and has been removed in supervision-0.26.0. Please pass a list of paths List[str] instead.
  • The name sv.BoundingBoxAnnotator is deprecated and has been removed in supervision-0.26.0. It has been renamed to sv.BoxAnnotator.
🏆 Contributors

@onuralpszr (Onuralp SEZER), @SkalskiP (Piotr Skalski), @SunHao-AI (Hao Sun), @rafaelpadilla Rafael Padilla, @Ashp116 (Ashp116), @capjamesg (James Gallagher), @blakeburch (Blake Burch), @hidara2000 (hidara2000), @Armaggheddon (Alessandro Brunello), @soumik12345 (Soumik Rakshit).

View originalPermalink
How 0.26.0 went
0.25.0

supervision-0.25.0

Added 9
  • Add `minimum_crossing_threshold` argument to `LineZone` to confirm crossings over multiple frames and reduce false positives from jittering detections
  • Enable tracking of objects detected as `KeyPoints` by converting them to `Detections`
  • Add `is_empty` method to `KeyPoints` to check if there are any keypoints in the object
  • Add `as_detections` method to `KeyPoints` to convert `KeyPoints` to `Detections`
  • Add new skiing video asset to supervision[assets]
  • Support Python 3.13 compatibility including execution without Global Interpreter Lock (GIL)
Changed 1
  • All Metrics now support Oriented Bounding Boxes (OBB)

From supervision

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 the minimum_crossing_threshold argument. If you set it to 2 or more, extra frames will be used to confirm the crossing, improving the accuracy significantly. (#1540)

https://github.com/user-attachments/assets/89ca2ee6-93c9-41e6-a432-e16c4c69c695

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
)

https://github.com/user-attachments/assets/4c3bdf54-391e-4633-9164-f15878ddfb33

See the guide for the full code used to make the video

  • Added is_empty method to KeyPoints to check if there are any keypoints in the object. (#1658)

  • Added as_detections method to KeyPoints that converts KeyPoints to Detections. (#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)

py3-13

  • Added Mean Average Recall mAR 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()

mAR_plot_example

  • Added Precision and Recall metrics, 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()

recall-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()

OBB example

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)

https://github.com/user-attachments/assets/ef768db4-867d-4305-b905-80e690bb1ea7

  • Added the metadata variable to Detections. It allows you to store custom data per-image, rather than per-detected-object as was possible with data variable. For example, metadata could 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.typed type hints metafile. It should provide a stronger signal to type annotators and IDEs that type support is available. (#1586)
🌱 Changed
  • ByteTrack no longer requires detections to have a class_id (#1637)
  • draw_line, draw_rectangle, draw_filled_rectangle, draw_polygon, draw_filled_polygon and PolygonZoneAnnotator now 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 of ByteTrack would share some date, requiring liberal use of tracker.reset(). (#1603), (#1528)
  • Fixed a bug where class_agnostic setting in MeanAveragePrecision would 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 BaseTrack class
    • Removed unused variables
  • Large refactor of RichLabelAnnotator, matching its contents with LabelAnnotator. (#1625)

🏆 Contributors

@onuralpszr (Onuralp SEZER), @kshitijaucharmal (KshitijAucharmal), @grzegorz-roboflow (Grzegorz Klimaszewski), @Kadermiyanyedi (Kader Miyanyedi), @PrakharJain1509 (Prakhar Jain), @DivyaVijay1234 (Divya Vijay), @souhhmm (Soham Kalburgi), @joaomarcoscrs (João Marcos Cardoso Ramos da Silva), @AHuzail (Ahmad Huzail Khan), @DemyCode (DemyCode), @ablazejuk (Andrey Blazejuk), @LinasKo (Linas Kondrackis)

A special thanks goes out to everyone who joined us for Hacktoberfest! We hope it was a rewarding experience and look forward to seeing you continue contributing and growing with our community. Keep building, keep innovating—your efforts make a difference! 🚀

View originalPermalink
How 0.25.0 went
View all

Discussion

If you publish supervision, you can claim this product by proving you administer its repository.