0.30.5

triggerdotdev/trigger.dev0.30.5Sep 22, 2026by Borda

Upgrading?

AI Summary

A patch release for the supervision library fixing 11 bugs across tracking, metrics, and image drawing, including LineZone trigger miscounting and InferenceSlicer crashes.

Key Highlights

  • `LineZone.trigger` no longer counts flicker as a crossing
  • `MeanAverageRecall` now scores correctly per image's top K predictions
  • `InferenceSlicer` no longer crashes on metadata mismatches
  • OpenCV-free backend achieves parity with OpenCV for rotated videos and CMYK images
  • New public constant `sv.config.SOURCE_IMAGE_METADATA_FIELD`

New Features

  • Regression tests for all fixes
  • Support for rotated video upright reading without OpenCV
  • CMYK JPEG/TIFF image drawing support without OpenCV
  • Fixed `Detections.__eq__` comparison for NaN values and list/ndarray mismatches

Full Release Notes

# 0.30.5: Tracking, metrics, and image-drawing correctness fixes

supervision 0.30.5 is a patch release closing 11 correctness and crash bugs across line-crossing tracking, mAR@K scoring, model connectors, key points, and image drawing/loading — no new public API of note, no breaking changes. The most consequential fixes are silent, not crashes: `LineZone.trigger` miscounted crossings by one per flicker whenever a tracker briefly touched the far side of the line, and `MeanAverageRecall` scored mAR@K against the wrong predictions when a lower-ranked one fit a target more tightly than one within the top K. The remaining fixes close a hard crash in `InferenceSlicer` on conflicting slice metadata (plus two related `Detections.__eq__` bugs), bring the OpenCV-free fallback backend to parity with OpenCV for rotated videos, CMYK images, transparent/1-bit PNGs, and default JPEG/WebP write quality, fix two drawing bugs that reproduce with OpenCV installed (16-bit `draw_image`, grayscale `IconAnnotator` icons), and fix two isolated bugs in `KeyPoints.as_detections` and `plot_images_grid`. Every fix ships a regression test.

## ✨ Spotlights / highlights

### `sv.LineZone.trigger` no longer counts flicker as a crossing

A crossing was confirmed whenever the oldest entry of a `minimum_crossing_threshold + 1` frame history differed from every later entry, which never verified the tracker had actually settled on the side it supposedly came from. With `minimum_crossing_threshold=2` the side sequence `A,A,A,B,A,A,A` counted a crossing *into* `A` — the side the object never left — so counts drifted by one per flicker, in the wrong direction; three separated flickers gave `in_count=3` instead of `0`.

```python
line_zone = sv.LineZone(
    start=sv.Point(0, 0), end=sv.Point(0, 100), minimum_crossing_threshold=2
)
# a tracker that flickers to the far side for one frame and back
# no longer registers a phantom crossing; only a sustained crossing counts
```

Crossings are now measured against the last side a tracker was confirmed on, not against the oldest history entry. Sustained crossings and `minimum_crossing_threshold=1` (the default) are unchanged.

### `sv.metrics.MeanAverageRecall` now scores mAR@K from each image's own top K

The matcher pairs predictions to targets by highest IoU, not confidence, so a prediction ranked below K could take a target away from one ranked within it. Adding a low-confidence duplicate that fit a target more tightly than the top prediction actually *lowered* mAR@1 — one target with a top prediction at IoU `0.71` scored mAR@1 `0.5` alone but `0.0` once a second, unrelated prediction at IoU `1.0` and confidence `0.1` was added. Each detection limit now matches only its own top K predictions.

### `sv.InferenceSlicer` no longer crashes when slices disagree on `metadata`

RF-DETR and `inference`-package connectors attach a `source_image` array per slice; any mismatch across slices previously crashed the merge outright. Any metadata key that isn't identical across every slice is now dropped with a one-time warning naming the dropped keys; `source_image` is reattached afterward as the full input image.

```python
slicer = sv.InferenceSlicer(callback=callback)
detections = slicer(image)  # metadata.source_image is the full input image again
```

Fixing this also closed two `Detections.__eq__` bugs: `NaN` in a float-array metadata value now compares equal to itself, and a list-valued value against an ndarray-valued one now returns `False` instead of raising `ValueError`.

### Rotated videos read upright without OpenCV

OpenCV's FFmpeg backend applies a video's display-rotation matrix to decoded frames and to the reported width/height; the OpenCV-free fallback ignored it, so a portrait phone video came back sideways with its width/height swapped.

### `sv.KeyPoints.as_detections` drops invisible key points from the derived box

`as_detections` stretched each box over every key point that wasn't `[0, 0]` or non-finite, ignoring `visible` — unlike `with_nms`, which already respected it. A pose whose low-confidence joints were predicted off-frame no longer produces an inflated box.

## 🔄 Migration guide

No breaking changes in this release.

No deprecations or removals landed in 0.30.5 either. All scheduled `remove_in` markers in the codebase target `0.31.0` and are untouched by this patch release.

`sv.config.SOURCE_IMAGE_METADATA_FIELD` is a new public constant (added alongside the `InferenceSlicer` fix) naming the `Detections.metadata` key RF-DETR/`inference`-package connectors use for the source image — additive, no existing code needs to change.

## 📝 Notable changes

### 🔧 Fixed

**Tracking / metrics**

- `sv.LineZone.trigger` no longer counts a spurious crossing in the opposite direction when a tracker flickers to the far side of the line for fewer than `minimum_crossing_threshold` frames. Crossings are now measured against the last side a tracker was confirmed on, and once a tracker has a confirmed side, a new one replaces it only after being held for the full threshold. Sustained crossings, `minimum_crossing_threshold=1` (the default), and per-tracker isolation are unchanged. ([#2600](https://github.com/roboflow/supervision/pull/2600))
- `sv.metrics.MeanAverageRecall` now scores mAR@K from each image's K most confident predictions alone, instead of matching every prediction first and only then keeping the top K by confidence. mAR@1 and mAR@10 now equal the recall of the top 1 and top 10 predictions per image, as documented; mAR@100 changes only for images with more than 100 predictions. ([#2604](https://github.com/roboflow/supervision/pull/2604))

**Model connectors**

- `sv.InferenceSlicer` no longer raises when slices disagree on `metadata` (e.g. a `source_image` NumPy array attached per-slice by RF-DETR/`inference`-package connectors), which previously crashed the merge outright. Any metadata key that isn't identical across every slice is now dropped from the merged result, with a `SupervisionWarnings` warning naming the dropped keys, emitted once per slicer instance. `source_image` is a special case: removed from each slice before the lenient merge, then reattached afterward as the full input image. Fixing this also closed two related `Detections.__eq__` bugs: metadata holding `NaN` in a float array now compares equal to itself instead of always reading unequal, and comparing a list-valued metadata value against an ndarray-valued one now returns `False` instead of raising `ValueError`. ([#2596](https://github.com/roboflow/supervision/pull/2596))

**OpenCV-free fallback backend**

- `sv.VideoInfo.from_video_path`, `sv.get_video_frames_generator` and `sv.process_video` now turn a rotated video upright when OpenCV is not installed, as they already do with OpenCV. The fallback now applies quarter and half turns, the same angles OpenCV applies, using the container's display-rotation matrix. Videos without a display rotation, and every read with OpenCV installed, are unchanged. ([#2601](https://github.com/roboflow/supervision/pull/2601))
- `sv.IconAnnotator` and `sv.draw_image` now draw CMYK JPEG and TIFF images in their colors when OpenCV is not installed, instead of returning the four ink channels as if they were blue/green/red/alpha. Other images, and every read with OpenCV installed, are unchanged. ([#2602](https://github.com/roboflow/supervision/pull/2602))
- `sv.ImageSink` and the dataset exports that encode in-memory images now write JPEG and WebP files at OpenCV's default quality (JPEG 95, lossless WebP) when OpenCV is not installed, instead of Pillow's defaults (JPEG 75, lossy WebP). The fallback's in-memory encoder also now accepts `.jpe`, `.tif`, `.jp2`, and `.pgm`, which it previously rejected even though file writes already accepted them. PNG and every write with OpenCV installed are unchanged. ([#2592](https://github.com/roboflow/supervision/pull/2592))
- `sv.IconAnnotator` and `sv.draw_image` now keep the transparency of grayscale PNGs with alpha, RGB PNGs with a transparent color, and 1-bit PNGs, when OpenCV is not installed — the fallback previously returned Pillow's pixel layout for `IMREAD_UNCHANGED` rather than OpenCV's, causing shape-mismatch `ValueError`s or black-drawn transparent pixels. Other images, and every read with OpenCV installed, are unchanged. ([#2588](https://github.com/roboflow/supervision/pull/2588))

**Annotators / drawing**

- `sv.draw_image` now scales a 16-bit PNG down to 8 bits on load, instead of blending it into an 8-bit scene and clipping channel values above 255 — this reproduces with OpenCV installed, since `IMREAD_UNCHANGED` keeps a 16-bit PNG at 16 bits. Eight-bit images, and images passed as arrays, are drawn as before. ([#2603](https://github.com/roboflow/supervision/pull/2603))
- `sv.IconAnnotator` now draws grayscale PNG icons instead of failing with `IndexError` — the overlay only handled BGR and BGRA arrays, and `cv2.imread(..., IMREAD_UNCHANGED)` returns a 2-D array for a grayscale PNG without alpha, with or without OpenCV installed. A grayscale icon is now expanded to BGR on load, and a 16-bit icon is scaled to 8 bits, as `cv2.imread` does by default. Eight-bit color icons are drawn as before. ([#2591](https://github.com/roboflow/supervision/pull/2591))

**Key points / utils**

- `sv.KeyPoints.as_detections` now leaves key points marked not visible out of the box it derives for each skeleton, as `sv.KeyPoints.with_nms` already does. A skeleton with no visible key point is now dropped, as one with only missing key points already was. Key points without `visible` convert as before. ([#2605](https://github.com/roboflow/supervision/pull/2605))
- `sv.plot_images_grid` now plots a single image in a `grid_size=(1, 1)` grid, instead of failing with `AttributeError` — `plt.subplots` returns a lone `Axes` rather than an array for a 1x1 grid. Grids with more than one cell plot as before. ([#2590](https://github.com/roboflow/supervision/pull/2590))

## 🏆 Contributors

- **kevin** (@kevin9327) — the OpenCV-free fallback backend fixes for rotated video, CMYK, transparent/1-bit images and default JPEG/WebP quality, the 16-bit `draw_image` and grayscale `IconAnnotator` drawing fixes, plus the `MeanAverageRecall`/`plot_images_grid`/`KeyPoints.as_detections` fixes
- **Souptik Chakraborty** (@Souptik96, [LinkedIn](https://www.linkedin.com/in/souptikchakraborty/)) — the `LineZone` flicker-crossing fix
- **RAJVARDHAN** (@vardhans07) — the `InferenceSlicer` conflicting-metadata fix and the two `Detections.__eq__` bugs it uncovered

______________________________________________________________________

**Full changelog**: https://github.com/roboflow/supervision/compare/0.30.4...0.30.5

Related Repositories