0.30.0
bknd-io/bknd0.30.0Aug 4, 2026by Borda
Upgrading?
AI Summary
v0.30.0 makes OpenCV optional by introducing a NumPy/Pillow-only backend, adds Soft-NMS, LabelMe and CreateML dataset formats, GeoTIFF support, and sv.load_image_from_url. The release includes five breaking changes, most notably dropping Python 3.9 support and changing JSONSink output types.
Key Highlights
- Run supervision without OpenCV (cv2-free backend available).
- Added Soft-NMS (sv.Detections.with_soft_nms).
- Added LabelMe and CreateML dataset format support.
- InferenceSlicer now supports GeoTIFF reading for large aerial/drone datasets.
- Added sv.load_image_from_url to load images directly from HTTP(S).
Breaking Changes
- OpenCV is no longer installed by default.
- Python 3.10+ is now required (3.9 support dropped).
- sv.JSONSink now emits native JSON types (float/bool) instead of strings.
- sv.mask_non_max_merge computes exact mask overlap and ignores the mask_dimension parameter.
- Detections.merge() on mixed dense + CompactMask inputs now returns a CompactMask object.
New Features
- sv.load_image_from_url
- OpenCV-free backend / sv.ImageWindow
- Soft-NMS
- sv.VLM.GOOGLE_GEMINI_3_5
- get_video_frames_generator(prefetch=...)
- PolygonZone(require_all_anchors=...)
- KeyPoints.merge()
- BaseAnnotator.requires_mask
- CompactMask.from_coco_rle / Detections.from_inference(compact_masks=True)
- sv.mask_to_roi
- DetectionDataset.from_labelme / as_labelme
- DetectionDataset.from_createml / as_createml
- InferenceSlicer GeoTIFF support
- InferenceSlicer(batch_size=...)
- ConfusionMatrix.benchmark(save_directory_path=...)
- HeatMapAnnotator.reset(), TraceAnnotator.reset(), DetectionsSmoother.reset()
- AREA_DATA_FIELD
- sv.denormalize_boxes and sv.xyxyxy
Full Release Notes
# 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
```python
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](docs/how_to/opencv_migration.md).
### Soft-NMS
```python
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
```python
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`
```python
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](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:
```python
# 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`:
```python
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-NMS** — `sv.box_soft_non_max_suppression`, `sv.mask_soft_non_max_suppression`, `sv.Detections.with_soft_nms` (#1624)
- **`sv.VLM.GOOGLE_GEMINI_3_5`** — `Detections.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 support** — `sv.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.0` → `0.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](https://www.linkedin.com/in/abhijith-neil-abraham-765165141)) — added `KeyPoints.merge()`; fixed out-of-bucket metric scoring and `key_points` edge cases
- **Agis Kounelis** (@kounelisagis, [LinkedIn](https://linkedin.com/in/kounelisagis)) — made `get_anchors_coordinates` OBB-aware; kept `from_inference` aligned on partial data
- **Andrew Barnes** (@Bortlesboat, [LinkedIn](https://www.linkedin.com/in/andrew-barnes-705a08195/)) — fixed sink state on reopen
- **Arthi Arumugam** (@arthi-arumugam-git, [LinkedIn](https://www.linkedin.com/in/arthiarumugam99/)) — fixed the Recall metric to track prediction-only classes
- **Dylan Parsons** (@dylanparsons, [LinkedIn](https://www.linkedin.com/in/dylanparsons)) — converted `Detections` doctests to runnable examples
- **Erik** (@Erol444) — added `sv.load_image_from_url`
- **Yann Hallouard** (@YHallouard, [LinkedIn](https://www.linkedin.com/in/yann-hallouard/)) — added Soft-NMS
- **Lee Clement** (@leeclemnet) — fixed COCO export to read image sizes from headers
- **Linas Kondrackis** (@LinasKo, [LinkedIn](https://www.linkedin.com/in/LinasKo)) — added batching to `InferenceSlicer`
- **Madhav-C** (@madhavcodez, [LinkedIn](https://www.linkedin.com/in/madhav-s-c)) — 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](https://www.linkedin.com/in/mattvanhorn)) — centralized geometry-aware IoU/area dispatch
- **Murillo Rodrigues** (@murillo-ro-silva, [LinkedIn](https://www.linkedin.com/in/murillo-rodrigues/)) — added `show_progress` to dataset load/save (0.29.1)
- **Nick Herrig** (@NickHerrig, [LinkedIn](https://www.linkedin.com/in/nickherrig/)) — added the face-blurring cookbook
- **Piotr Skalski** (@SkalskiP, [LinkedIn](https://www.linkedin.com/in/skalskip92/)) — added Gemini 3.5 Flash VLM support
- **Ruben** (@RubenHaisma) — perf fixes across mask counting, box IoU, `from_tensorflow`
- **Saif Khan** (@K-saif, [LinkedIn](https://www.linkedin.com/in/saif-khan-396348231/)) — 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](https://www.linkedin.com/in/shaoming-wu/)) — 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](https://www.linkedin.com/in/vikas-saini1/)) — converted fenced examples to doctests; removed defensive asserts in annotators
- **Jirka Borovec** (@Borda, [LinkedIn](https://linkedin.com/in/jirka-borovec)) — 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.0Related Repositories
The AI that really does things. Any OS. Any Platform. The lobster way. 🦞
Interactive roadmaps, guides and other educational content to help developers grow in their careers.
Reverse engineer anything with agents, from app behavior down to native binaries.
The open source coding agent.
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.