0.30.4

roboflow/supervision0.30.4Sep 17, 2026by Borda

Upgrading?

AI Summary

This patch release closes 14 correctness and crash bugs across dataset loaders, exporters, and model connectors. Key fixes include correcting COCO polygon mask alignment, fixing EXIF orientation handling, and ensuring proper UTF-8 encoding for non-ASCII class names on Windows. The release also resolves hard crashes in Transformers connectors and various annotators.

Key Highlights

  • COCO polygon masks no longer shift by a pixel due to integer truncation during loading.
  • EXIF-rotated photos no longer result in swapped width/height dimensions in YOLO and COCO formats.
  • Dataset loaders now explicitly use UTF-8 encoding to prevent mangling of non-ASCII class names on Windows.
  • Transformers connectors now correctly load binary maps for overlapping instance segmentation.
  • Hard crashes in Transformers v4/v5 connectors and TraceAnnotator have been resolved.

Full Release Notes

# 0.30.4: Dataset, connector, and key point correctness fixes

supervision 0.30.4 is a patch release closing 14 correctness and crash bugs across dataset loaders/exporters, model connectors, and key point/annotator/video handling — no new public API, no breaking changes. The most consequential fixes are silent, not crashes: COCO polygon masks loaded shifted by up to a pixel, EXIF-rotated photos loaded with swapped width/height across two loaders and both image backends, and non-ASCII class names were mangled or rejected on Windows across four loaders and one writer. The remaining fixes close hard crashes in the Transformers v4/v5 connectors, Ultralytics pose loading, `TraceAnnotator`, `iterative_seek`, and three more dataset-format edge cases. Every fix ships a regression test.

## ✨ Spotlights / highlights

### `sv.DetectionDataset.from_coco` no longer shifts mask polygons by up to a pixel

COCO polygons commonly hold sub-pixel float coordinates, but the loader cast them straight to `int32`, which truncates rather than rounds. Every polygon mask loaded shifted up and to the left by up to a pixel — the same polygon loaded one pixel apart depending on the format it was stored in (a square with corners at `2.6`/`7.6` covered pixels 2–7 from COCO but 3–8 from LabelMe, an IoU of 0.53 between the two masks). Silent, not a crash — training data was quietly misaligned.

```python
dataset = sv.DetectionDataset.from_coco(
    images_directory_path="images",
    annotations_path="annotations.json",
)  # polygon vertices now rounded to nearest pixel, matching from_yolo/from_labelme/from_pascal_voc
```

A non-finite vertex now raises `ValueError` naming the annotation id, instead of silently propagating.

### `from_yolo`/`as_coco` no longer swap width and height on EXIF-rotated photos

Photos from phones are often stored sideways with an EXIF orientation tag. `cv2.imread` applies that tag, but the size read used Pillow's file-header read, which doesn't. For a quarter-turned photo, `from_yolo` scaled normalized boxes and polygons by the swapped width/height, so they landed outside the image, and `as_coco` wrote the swapped dimensions. The OpenCV-free fallback backend now also applies the orientation tag, matching how OpenCV itself handles every read except `IMREAD_UNCHANGED` — the same file previously loaded with a different shape depending on whether `opencv-python` was installed.

### Dataset loaders/writers no longer mangle non-ASCII class names on Windows

`from_coco`, `from_labelme`, `from_createml`, `from_yolo` read JSON/YAML, and `as_pascal_voc` wrote XML, using the platform's default encoding — cp1252 on Windows. A class name like `café` loaded as `café`; `고양이` raised `UnicodeDecodeError`. All five now read/write UTF-8 explicitly.

### `sv.Detections.from_transformers` now loads Mask2Former/MaskFormer overlap-safe binary maps

`post_process_instance_segmentation(return_binary_maps=True)` — the option Transformers recommends when instances can overlap — returns a `(num_instances, H, W)` stack of binary maps, but the v5 instance path compared it against each segment's `id` as if it were an id-map, producing a 4-D array `mask_to_xyxy` rejected outright. Each segment now indexes the stack at its own `id`, so overlapping instances keep their full masks.

```python
detections = sv.Detections.from_transformers(
    transformers_results=processor.post_process_instance_segmentation(
        result, target_sizes=[image.size[::-1]], return_binary_maps=True
    )[0],
    id2label=model.config.id2label,
)  # overlapping-instance results now load instead of crashing
```

### `sv.DetectionDataset.from_pascal_voc` no longer silently drops bmp/tif/webp images

The loader only listed `.jpg`, `.jpeg`, `.png`, so every other image was left out without a warning — even though `from_yolo`/`from_folder_structure` load those formats and `as_pascal_voc` writes annotations for them. A dataset exported to Pascal VOC and read back came back smaller than it went out.

## 🔄 Migration guide

No breaking changes in this release.

No deprecations or removals landed in 0.30.4 either. Eight scheduled-removal commits (`ByteTrack`, `supervision.keypoint`, `create_tiles`/`overlay_image`, keypoint validators, the legacy `MeanAveragePrecision`, `LMM`/`from_lmm`, and others) plus a new `MetricResult` ABC exist on `develop` but are not part of this cherry-picked patch release — they target 0.31.0 and will get their own migration guide when that release ships.

Two internal (non-underscored but unexported) helper functions changed signature as part of fixes in this release: `parse_polygon_points` (`dataset/formats/pascal_voc.py`) now returns `float64` instead of `int`, and `detections_from_xml_obj`'s `xyxy` is now `list[list[float]]` instead of `list[list[int]]`. Neither is re-exported from `supervision.__init__` or `supervision.dataset.__init__`, so this is not a public API change — no action needed unless you import these paths directly.

## 📝 Notable changes

### 🔧 Fixed

**Dataset loaders / exporters**

- `sv.DetectionDataset.from_coco` now rounds polygon vertices to the nearest pixel before rasterising masks, instead of truncating them to `int32`, as `from_yolo`/`from_labelme`/`from_pascal_voc` already do. Truncation shifted every mask up and to the left by up to a pixel — a polygon with corners at `2.6`/`7.6` covered pixels 2 to 7 from COCO but 3 to 8 from LabelMe, an IoU of 0.53 between the two masks. A non-finite vertex now raises `ValueError` naming the annotation id; integer vertices and RLE masks load as before. ([#2587](https://github.com/roboflow/supervision/pull/2587))
- `sv.DetectionDataset.from_coco`/`from_labelme`/`from_createml`/`from_yolo` now read JSON/YAML as UTF-8, and `as_pascal_voc` writes XML as UTF-8, instead of the platform default (cp1252 on Windows). A COCO category or YOLO `data.yaml` name outside ASCII broke on Windows: `café` loaded as `café`, `고양이` failed with `UnicodeDecodeError`, and `as_pascal_voc` either failed with `UnicodeEncodeError` or wrote a file `from_pascal_voc` rejected with `ParseError: not well-formed (invalid token)` — Linux/macOS, already UTF-8 by default, are unaffected. ([#2585](https://github.com/roboflow/supervision/pull/2585))
- `sv.ClassificationDataset.as_folder_structure` now copies source image files unchanged instead of round-tripping them through `cv2.imread`/`cv2.imwrite`, which dropped alpha channels, downcast 16-bit PNGs to 8-bit, and recompressed JPEGs on every export — matching `sv.DetectionDataset`'s exports (`as_yolo`/`as_pascal_voc`/`as_coco`), which already copied; exporting into the folder the dataset was loaded from previously rewrote its own images the same lossy way. Images held in memory are still encoded with `cv2.imwrite`. ([#2584](https://github.com/roboflow/supervision/pull/2584))
- `sv.DetectionDataset.from_labelme` now finds images for LabelMe files saved on Windows when loading on Linux or macOS. `imagePath` is written with `\`, but the loader took only `Path(...).name`, which doesn't split on `\` on POSIX — the whole value became the filename and reading failed with `ValueError: Could not read image from path`. The filename is now taken with either separator on every system, as LabelMe itself does when reading its own files; forward-slash paths, and every file on Windows, are unchanged. ([#2581](https://github.com/roboflow/supervision/pull/2581))
- `sv.DetectionDataset.from_yolo` now loads label files whose class ids are written as decimals (e.g. `1.0 0.5 0.5 0.2 0.4`), which previously aborted the whole load with `ValueError: invalid literal for int()` — `np.savetxt` writes floats by default and Ultralytics tolerates them. Whole numbers load in any notation now; fractional, non-finite, or non-numeric ids still raise, naming the offending id. ([#2580](https://github.com/roboflow/supervision/pull/2580))
- `sv.DetectionDataset.from_yolo`/`as_coco` now size EXIF-oriented images the way `cv2.imread` loads them (swapping width/height for orientations 5 to 8), instead of reading the un-rotated file-header size via Pillow — quarter-turned photos previously scaled boxes/polygons by the swapped dimensions and produced mismatched mask shapes. The OpenCV-free fallback backend's `imread`/`imdecode` now apply EXIF orientation too, matching OpenCV's behavior for every read except `IMREAD_UNCHANGED` — previously the same file loaded with a different shape depending on whether `opencv-python` was installed. ([#2577](https://github.com/roboflow/supervision/pull/2577))
- `sv.DetectionDataset.from_pascal_voc` no longer fails on annotations with decimal coordinates (e.g. `<xmin>48.5</xmin>`), which aborted the whole load with `ValueError: invalid literal for int()` — Datumaro, which CVAT uses for its exports, writes VOC this way. Box coordinates are now read as floats and keep their precision; polygon vertices are rounded after the 1-index offset, as the YOLO and LabelMe loaders already do; non-finite values are still rejected. ([#2568](https://github.com/roboflow/supervision/pull/2568))
- `sv.DetectionDataset.from_pascal_voc` no longer skips `.bmp`, `.tif`, `.tiff`, and `.webp` images without a warning — the loader only listed `.jpg`/`.jpeg`/`.png`, even though `from_yolo`/`from_folder_structure` load those formats and `as_pascal_voc` writes annotations for them, so a dataset exported to Pascal VOC and read back came back smaller than it went out. It now accepts the same extensions as `sv.ClassificationDataset.from_folder_structure`. ([#2569](https://github.com/roboflow/supervision/pull/2569))

**Model connectors**

- `sv.Detections.from_transformers` now loads Transformers v5 `return_binary_maps=True` instance results (a `(num_instances, H, W)` stack), which the v5 path previously compared against each segment's `id` as if it were an id-map, producing a 4-D array that `mask_to_xyxy` rejected with `ValueError: too many values to unpack (expected 3)`. Each segment now indexes the stack at its own `id`, keeping full masks for overlapping instances; segment-id-map results are unchanged. ([#2576](https://github.com/roboflow/supervision/pull/2576))
- `sv.Detections.from_transformers` no longer crashes on a Transformers v4 panoptic result with no segments — `post_process_panoptic`'s empty `segments_info` produced a `(0,)` mask array instead of `(0, H, W)`, and `mask_to_xyxy` raised `ValueError: not enough values to unpack (expected 3, got 1)`. The path now builds a `(0, H, W)` mask stack and an integer `class_id`, matching the v5 paths, and yields empty `Detections`. ([#2571](https://github.com/roboflow/supervision/pull/2571))

**Key points / annotators / video**

- `sv.KeyPoints.from_inference` now places each key point at the slot given by its `class_id` (skeleton index) instead of appending in received order — Inference drops key points below `keypoint_confidence` and multi-skeleton models report different counts per object, so stacking as-received either raised `ValueError: ... inhomogeneous shape` or silently slid later key points into earlier slots, joining the wrong joints. Omitted slots now stay `(0, 0)` at zero confidence, already skipped by the key point annotators and `as_detections`; a result with every key point omitted now loads with zero key points instead of failing validation. ([#2575](https://github.com/roboflow/supervision/pull/2575))
- `sv.KeyPoints.from_ultralytics` no longer crashes on pose models whose key points carry no visibility score (`kpt_shape=[K,2]`, where `Results.keypoints.conf` is `None`) — the connector called `.cpu()` on it unconditionally, raising `AttributeError: 'NoneType' object has no attribute 'cpu'` on every non-empty frame. Such results now load with `keypoint_confidence=None`; models that do report visibility are unaffected. ([#2570](https://github.com/roboflow/supervision/pull/2570))
- `sv.TraceAnnotator.annotate` no longer raises `ValueError: Length of color lookup 3 does not match length of detections 2` when a `custom_color_lookup` is passed alongside pending (`tracker_id == -1`) tracks — the annotator skips those detections but still resolved colors against the full-length lookup, although the other annotators accept the same detections and lookup. The lookup is now filtered alongside the detections, so each confirmed track keeps its own color; frames without pending tracks, and calls without `custom_color_lookup`, are unchanged. ([#2586](https://github.com/roboflow/supervision/pull/2586))
- `sv.get_video_frames_generator` no longer reads `start` frames past `end` when `iterative_seek=True` — it counted `start` down to zero, then measured `end` from that zero instead of from `start` (`start=2, end=5` yielded frames 2 to 6 instead of 2 to 4; `start=4, end=6` yielded frames 4 to 9 instead of 4 to 6). A separate counter now keeps both seek modes aligned; `start=0` and non-iterative calls are unchanged. ([#2583](https://github.com/roboflow/supervision/pull/2583))

## 🏆 Contributors

- **kevin** (@kevin9327) — every fix in this release (#2568–#2587), including the COCO polygon-vertex rounding, UTF-8 encoding for dataset annotation files on Windows, and EXIF-oriented image sizing across the YOLO/COCO loaders and the cv2-free fallback backend

______________________________________________________________________

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

Related Repositories