0.31.1

roboflow/supervision0.31.1Oct 20, 2026by Borda

Upgrading?

Full Release Notes

# v0.31.1 — Recall that matches COCO, boxes that don't overflow

A bug-fix release: recall scores now agree with COCO, and half-precision boxes stop overflowing.

- **`MeanAverageRecall`** skips crowd regions and matches predictions in confidence order, like `pycocotools`.
- COCO segmentations loaded without an `area` field no longer make mAP return `-1`.
- `float16` boxes keep finite areas, and mAP and `with_nmm` stop overflowing on narrow coordinate types.
- Mask and halo annotators draw on grayscale scenes, and vertex annotators accept `(x, y, z)` points.
- Without OpenCV, ellipses and thick circles render like OpenCV 5, and grayscale images save correctly.

Drop-in upgrade, no code changes. mAR values can move: up on COCO data with crowd annotations, and in either direction where confidence order now decides which prediction claims a target.

## ✨ Spotlights / highlights

### Recall that matches COCO

`sv.metrics.MeanAverageRecall` used to count COCO crowd regions as missed objects and to pair predictions with the highest-IoU target first. It now leaves out targets with `data["iscrowd"]` or `data["ignore"]` set, the same rule `MeanAveragePrecision` uses. It also matches predictions in descending confidence order with COCO's tie rule, and counts an oriented-box IoU of exactly 0.55, 0.6, 0.8 or 0.85 as a match at that threshold. One target, one crowd target and a perfect prediction now score 1.0 instead of 0.5. (#2750, #2758, #2752)

```python
import numpy as np
import supervision as sv
from supervision.metrics import MeanAverageRecall

targets = sv.Detections(
    xyxy=np.array([[0, 0, 10, 10], [20, 20, 30, 30]], dtype=float),
    class_id=np.array([0, 0]),
    data={"iscrowd": np.array([0, 1])},
)
predictions = sv.Detections(
    xyxy=np.array([[0, 0, 10, 10]], dtype=float),
    class_id=np.array([0]),
    confidence=np.array([0.9]),
)
MeanAverageRecall().update(predictions, targets).compute().mAR_at_100  # 1.0
```

### Half-precision boxes that stay finite

A 256×256 box in `float16` has an area of 65,536, which is past `float16`'s maximum, so `Detections.box_area` returned `inf` and area filters dropped it. Box areas of `float16` detections are now computed and returned in `float32`, and `MeanAveragePrecision` and `with_nmm` no longer overflow on `float16` or narrow integer coordinates either. COCO segmentations loaded with masks but without an `area` field now get it from the decoded mask, so those targets stay in their size bucket instead of turning mAP into `-1`. (#2764, #2743, #2753, #2763)

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

detections = sv.Detections(
    xyxy=np.array([[0, 0, 256, 256], [0, 0, 0.01, 0.01]], dtype=np.float16)
)
detections.box_area  # array([6.5536000e+04, 1.0004273e-04], dtype=float32)
```

### Grayscale scenes and 3D keypoints in annotators

`sv.MaskAnnotator` and `sv.HaloAnnotator` raised `ValueError` on single-channel images. They now accept `(H, W)`, `(H, W, 1)` and Pillow `"L"` scenes, painting the gray value OpenCV would write for the same color. `VertexAnnotator` and the vertex-ellipse annotators draw `(x, y, z)` keypoints from their x and y, where they used to fail on tuple unpacking. (#2755, #2759)

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

scene = np.zeros((100, 100), dtype=np.uint8)
mask = np.zeros((1, 100, 100), dtype=bool)
mask[0, 20:60, 20:60] = True
detections = sv.Detections(
    xyxy=np.array([[20, 20, 60, 60]], dtype=float), mask=mask, class_id=np.array([0])
)
sv.MaskAnnotator().annotate(scene.copy(), detections).shape  # (100, 100)

key_points = sv.KeyPoints(xy=np.array([[[10, 10, 0.5], [50, 50, 1.0]]], dtype=np.float32))
sv.VertexAnnotator().annotate(np.zeros((64, 64, 3), dtype=np.uint8), key_points)
```

### Drawing without OpenCV, closer to OpenCV

The NumPy fallback used when OpenCV is not installed now ports OpenCV's ellipse rasterization, and thick circle outlines are centred on the radius instead of shrinking inward by half their thickness. Both match OpenCV 5.0 pixel for pixel in the reference tests. Without OpenCV, this changes the pixels drawn by `EllipseAnnotator`, `RoundBoxAnnotator`, the vertex-ellipse keypoint annotators, `CircleAnnotator` and `DotAnnotator` outlines. Image writing now accepts `(H, W, 1)` grayscale arrays and saves BGRA images as JPEG, and video reading handles files whose audio stream comes before the video stream. (#2740, #2765, #2754, #2746)

## 🔄 Migration guide

No migration required for this release.

## 📝 Notable changes

### 🔧 Fixed

**`MeanAverageRecall` and COCO**

- `sv.metrics.MeanAverageRecall` counted COCO crowd and ignored targets as missed objects and paired predictions by highest IoU first; it now skips those targets and matches in descending confidence order with COCO's last-target tie rule, as COCO does. (#2750, #2758)
- Oriented-box `MeanAverageRecall`, `Precision`, `Recall` and `F1Score` missed matches whose IoU was exactly 0.55, 0.6, 0.8 or 0.85, because the IoU was `float64` and the thresholds `float32`. (#2752)

**Overflow-safe boxes**

- `Detections.box_area` and box-based `Detections.area` overflowed to infinity for ordinary `float16` boxes; they now compute and return `float32`. (#2764)
- `sv.metrics.MeanAveragePrecision` overflowed box extents and areas for `float16` or narrow integer coordinates, so perfect large boxes scored `-1` or landed in the wrong size bucket. (#2743)
- `sv.Detections.with_nmm` overflowed its area-weighted confidence for `float16` boxes and, at extreme coordinates, for integer and `float32` boxes. (#2753)
- COCO loading with masks left segmented annotations without an `area` field with a `NaN` area, which pushed them out of every mAP size bucket; the area now comes from the decoded mask. (#2763)

**Masks and polygons**

- `resize_masks` returned an empty array when downscaling very narrow masks; it now keeps at least one pixel per axis. (#2762)
- `sv.filter_polygons_by_area` and `sv.polygon_to_xyxy` raised `ValueError` on empty polygons; they now treat them as zero area and an all-zero box. (#2761)

**Annotators**

- `sv.MaskAnnotator` and `sv.HaloAnnotator` raised `ValueError` on grayscale scenes. (#2755)
- `VertexAnnotator` and the vertex-ellipse annotators raised a tuple-unpacking error on three-coordinate keypoints; they now draw from x and y. (#2759)
- `sv.MaskAnnotator` widened its blend region to the boxes of all-empty masks, including `CompactMask`, copying and blending outside the foreground. (#2741)

**Without OpenCV**

- The fallback `ellipse` painted different pixels from OpenCV for thick strokes, fills and arcs; it now ports OpenCV's rasterization. (#2740)
- The fallback `circle` grew thick outlines inward from the bounding box, shrinking them by half their thickness; they are now centred on the radius. (#2765)
- Image writing rejected `(H, W, 1)` grayscale arrays and lost BGRA images saved as JPEG. (#2754)
- Video reading failed on files whose audio stream comes before the first video stream. (#2746)

## 🏆 Contributors

Welcome to our first-time contributors, Ghanashyam V Narayan and jorge!

- **NIKHIL** (@Nikhi00718) — fixed overflow in box areas, mAP and `with_nmm`, derived missing COCO areas, and fixed image writing and video reading without OpenCV.
- **Kaushik Reddy Lingireddy** (@Lingikaushikreddy) — made `MeanAverageRecall` skip crowd targets and match by confidence, and fixed oriented-box threshold matching.
- **peng xiang** (@px12811) — ported OpenCV's ellipse rasterization to the fallback and fixed mAR tie-breaking.
- **A Aswanth Raj** (@aswanth-07) — fixed three-coordinate keypoint drawing and narrow-mask resizing.
- **Mohammad Hijjawi** (@MohammadHijjawi97) — made `MaskAnnotator` and `HaloAnnotator` work on grayscale scenes.
- **kevin** (@kevin9327) — centred thick fallback circles on their radius.
- **Ghanashyam V Narayan** (@ghanashyam03) — made the polygon helpers accept empty polygons.
- **jorge** (@jorge-asenjo) — kept empty compact masks out of the mask blend region.
- **Jirka Borovec** (@Borda) — prepared the release.

______________________________________________________________________

**Full changelog**: <https://github.com/roboflow/supervision/compare/0.31.0...0.31.1>

Related Repositories