From 4b60bbc9cc4701060cdb9c6d1d1b6db55e10b804 Mon Sep 17 00:00:00 2001
From: Ruben <74268046+RubenHaisma@users.noreply.github.com>
Date: Thu, 18 Jun 2026 15:29:37 +0200
Subject: [PATCH] fix(dataset): stop Pascal VOC export from mutating source
detections (#2341)
object_to_pascal_voc applied the 1-index offset in place (xyxy += 1).
Because Detections.__iter__ yields each row of xyxy as a view sharing
memory with detections.xyxy, detections_to_pascal_voc wrote the +1 shift
straight back into the caller's array. A single export shifted every box
by +1px; a second export compounded it, producing wrong XML. A single
export-then-reload happened to round-trip because from_pascal_voc
subtracts 1, which is why no test caught it.
Rebind to a new array (xyxy = xyxy + 1) instead of mutating in place.
On-disk output is unchanged; the source detections are left intact.
Add regression tests asserting object_to_pascal_voc does not mutate its
inputs and that two consecutive exports are identical and leave xyxy
unchanged.
---------
Co-authored-by: jirka <6035284+Borda@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
---
docs/changelog.md | 2 +
src/supervision/dataset/formats/pascal_voc.py | 41 ++++++++++++++++--
tests/dataset/formats/test_pascal_voc.py | 42 +++++++++++++++++++
3 files changed, 81 insertions(+), 4 deletions(-)
diff --git a/docs/changelog.md b/docs/changelog.md
index bf204918..db2e8b87 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -17,6 +17,8 @@ date_modified: 2026-06-16
- Fixed [#2333](https://github.com/roboflow/supervision/pull/2333): [`sv.DetectionsSmoother`](https://supervision.roboflow.com/latest/detection/tools/smoother/#supervision.detection.tools.smoother.DetectionsSmoother) no longer raises when smoothing detections without `confidence`. Confidence is now averaged over the frames that carry it; when tracks in the same frame disagree on confidence presence, `confidence` is set to `None` for all smoothed detections.
+- Fixed [#2341](https://github.com/roboflow/supervision/pull/2341): `sv.DetectionDataset.as_pascal_voc` no longer mutates the source `Detections.xyxy` by the 1-index offset on every call. Previously, repeated exports accumulated a `+1` shift in the caller's bounding boxes.
+
- Fixed [#2331](https://github.com/roboflow/supervision/pull/2331): `sv.Precision` and `sv.F1Score` now count predictions on background images (empty target set) as false positives, and count predictions of classes absent from ground truth as false positives under `MICRO` and `MACRO` averaging. Previously both edge cases were silently ignored, inflating scores. `WEIGHTED` averaging is unchanged — absent classes retain weight 0, consistent with scikit-learn. Users relying on previous scores should re-evaluate after upgrading; no API change is required.
### 0.29.0 Jun 15, 2026
diff --git a/src/supervision/dataset/formats/pascal_voc.py b/src/supervision/dataset/formats/pascal_voc.py
index 91b6664c..e6914075 100644
--- a/src/supervision/dataset/formats/pascal_voc.py
+++ b/src/supervision/dataset/formats/pascal_voc.py
@@ -21,13 +21,41 @@ def object_to_pascal_voc(
name: str,
polygon: npt.NDArray[np.number] | None = None,
) -> Element:
+ """Build a Pascal VOC ``