0.30.9

roboflow/supervision0.30.9Oct 8, 2026by Borda

Upgrading?

AI Summary

Enhances the NumPy fallback to match OpenCV behavior, fixes video reading to the end, and enforces stricter validation on box and classification inputs.

Key Highlights

  • NumPy fallback now draws thick borders and resizes images identically to OpenCV.
  • Video frame generators now read to the end of the stream, fixing browser-recorded WebM support.
  • COCO, Pascal VOC, and CreateML loaders now reject reversed boxes.
  • `get_top_k` now raises a `ValueError` for negative `k` instead of hiding a classification.
  • `DetectionsSmoother` ages history on frames without a tracker_id.

Breaking Changes

  • COCO and CreateML loaders now raise errors on negative box width or height.
  • `get_top_k` now raises `ValueError` for negative `k`.

New Features

  • Browser-recorded WebM and variable frame rate MKV support to the end of stream.
  • NumPy fallback now matches OpenCV's pixel mapping for `cv2.resize`.
  • Stricter validation for `epsilon` in `approxPolyDP` (rejects NaN, infinite, or 1e30+).
  • Return empty `KeyPoints` instead of raising `IndexError` on empty pose results.

Full Release Notes

# v0.30.9 — Closer to OpenCV without OpenCV

Installs without OpenCV now draw and resize like OpenCV, and the COCO and CreateML loaders stop accepting reversed boxes.

- **`BoxAnnotator`** borders paint the same pixels with or without OpenCV installed.
- **`get_video_frames_generator`** reads browser-recorded WebM and variable frame rate MKV to the end.
- **`from_coco`**, `from_pascal_voc` and `from_createml` no longer build boxes with `x_min` past `x_max`.
- **`get_top_k`** rejects a negative `k` instead of dropping one classification.
- **`DetectionsSmoother`** forgets expired tracks on frames without `tracker_id`.

Drop-in upgrade. Without OpenCV, borders and resized regions shift toward OpenCV's output. Negative `k`, negative COCO/CreateML box sizes and an `epsilon` that is `NaN`, infinite or `1e30` and larger are now rejected; without OpenCV, so is rectangle `thickness` above 32767.

## ✨ Spotlights / highlights

### NumPy fallback matches OpenCV

Without `opencv-python`, thick rectangle borders were drawn entirely inside the rectangle. They now extend `(thickness + 1) // 2` pixels outside it, like OpenCV. The same release fixes `cv2.resize` with `fx`/`fy` sampling the wrong source pixels (it hit `PixelateAnnotator`) and `approxPolyDP` dropping vertices that sit beyond a segment endpoint. For polygons, the fallback now follows OpenCV 4.13 and later; older OpenCV keeps the infinite-line rule, so results differ by installed backend. (#2675, #2690, #2673)

```python
import numpy as np
import supervision as sv

scene = np.zeros((100, 100, 3), dtype=np.uint8)
detections = sv.Detections(xyxy=np.array([[20, 20, 80, 60]]), class_id=np.array([0]))
annotated = sv.BoxAnnotator(thickness=2).annotate(scene, detections)
# before (no OpenCV): 392 painted pixels, border drawn inside the box
# now:                596 painted pixels, same as with OpenCV
```

### Video reads to the end of the stream

With no `end`, `sv.get_video_frames_generator` stopped at OpenCV's frame count. A WebM without a duration reports a huge negative count, so browser recordings yielded no frames. It now reads until the stream ends, and `sv.process_video` without `max_frames` does the same. (#2672)

```python
for frame in sv.get_video_frames_generator("recording.webm"):
    ...
# before: no frames yielded
# now:    every frame
```

### No more reversed boxes from COCO, Pascal VOC and CreateML

This finishes the `from_yolo` fix from 0.30.8. COCO and CreateML name an extent, so a negative width or height is rejected. Pascal VOC names two corners, so a reversed pair is ordered. The three exporters order corners first, so a reversed `Detections` box is written as the same rectangle. (#2683)

### A negative `k` no longer hides a classification

`get_top_k(-1)` returned every classification except the lowest-confidence one. It now raises. (#2695)

```python
classifications.get_top_k(-1)
# before: all but the lowest-confidence classification
# now:    ValueError: k must be non-negative
```

### Empty and untracked frames behave

`InferenceSlicer` keeps its oriented-box sequential fallback and warning when the first batch is empty. `DetectionsSmoother` ages its history on frames without `tracker_id`, so expired boxes stop affecting a returning track. `KeyPoints.from_transformers` returns an empty `KeyPoints` instead of raising `IndexError` when pose post-processing finds nothing. (#2685, #2676, #2671)

## 🔄 Migration guide

No migration required for this release. COCO and CreateML annotation files with a negative box width or height now fail to load; correct or remove those boxes.

## 📝 Notable changes

### 🌱 Changed

- `sv.DetectionDataset.from_coco` and `from_createml` raise on a negative box width or height; `from_pascal_voc` orders a reversed corner pair. The three exporters order the corners of a reversed `Detections` box first and the COCO exporter writes the ordered origin; normal boxes are unchanged. (#2683)
- `sv.Classifications.get_top_k` raises `ValueError` for a negative `k`; `k=0` and `k` larger than the number of classifications are unchanged. (#2695)
- Without OpenCV, rectangle `thickness` must be an integer of at most 32767, as in OpenCV: a non-integer raises `TypeError`, a larger value `ValueError`. (#2675)

### 🔧 Fixed

- `sv.BoxAnnotator`, `sv.CropAnnotator`, `sv.PercentageBarAnnotator` and `sv.draw_rectangle` draw a border of `thickness` 2 or more identically with and without `opencv-python`. Filled rectangles and `thickness=1` borders are unchanged, except zero-height rectangles, which no longer draw 1 or 2 extra pixels. (#2675)
- The NumPy fallback for `cv2.resize` maps pixels with `fx`/`fy` when `dsize` is not given, as OpenCV does. `sv.PixelateAnnotator` could sample the wrong source pixels without OpenCV. (#2690)
- The NumPy fallback for `cv2.approxPolyDP`, used by `sv.approximate_polygon` and YOLO/COCO polygon export, measures distance to the finite segment as OpenCV 4.13+ does, and raises `ValueError` for a `NaN`, infinite or `1e30`-and-larger `epsilon`. (#2673)
- `sv.get_video_frames_generator` and `sv.process_video` read to the end of the stream when no `end` or `max_frames` is given. A positive frame count still rejects an `end` past it. (#2672)
- `sv.InferenceSlicer` probes past leading empty batches before choosing threaded or sequential execution, so batched callbacks producing oriented boxes keep the sequential fallback and warning. (#2685)
- `sv.DetectionsSmoother` ages cached track history on frames without `tracker_id`; short gaps still preserve smoothing. (#2676)
- `sv.KeyPoints.from_transformers` returns an empty `KeyPoints` when pose post-processing returns no instances for an image. (#2671)

## 🏆 Contributors

- **Atikul Islam Munna** (@atikulmunna, [LinkedIn](https://www.linkedin.com/in/aimunna/)) — centered thick rectangle borders in the NumPy fallback.
- **Mohammad Hijjawi** (@MohammadHijjawi97, [LinkedIn](https://www.linkedin.com/in/mohammadhijjawi)) — fixed video reading past an unreliable frame count.
- **A Aswanth Raj** (@aswanth-07, [LinkedIn](https://www.linkedin.com/in/a-aswanth-raj/)) — fixed `InferenceSlicer` empty-batch probing and `DetectionsSmoother` history aging.
- **Miral Amin** (@aminmiral) — made COCO, Pascal VOC and CreateML reject or order reversed boxes.
- **NIKHIL** (@Nikhi00718) — rejected negative `get_top_k` counts and handled empty Transformers pose results.
- **Raashish Aggarwal** (@raashish1601) — fixed `cv2.resize` pixel mapping with `fx` and `fy`.
- **kevin** (@kevin9327) — fixed `approxPolyDP` distance measurement in the NumPy fallback.

*Automated contributions: @dependabot, @pre-commit-ci*

______________________________________________________________________

**Full changelog**: <https://github.com/roboflow/supervision/compare/0.30.8...0.30.9>

Related Repositories