Repository navigation
releasing 0.30.5 [rebase & merge] - #2617
Merged
Merged
Conversation
…es (#2588) The OpenCV-free backend returned Pillow's pixel layout for IMREAD_UNCHANGED: grayscale with alpha as 2 channels, RGB with a transparent color without alpha, and 1-bit images as booleans. OpenCV returns BGRA and 8-bit arrays for these, so IconAnnotator and draw_image failed on or ignored the transparency of such icons without OpenCV. --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
plt.subplots returns a lone Axes instead of an array for a 1x1 grid, so iterating over `axes.flat` failed with AttributeError. Request the axes with squeeze=False so they are always an array. --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Icons are read with IMREAD_UNCHANGED to keep alpha, which returns a grayscale PNG without alpha as a 2-D array. The overlay only handles BGR and BGRA, so annotate failed with IndexError. Expand grayscale icons to BGR when loading them. --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Jirka Borovec <6035284+Borda@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
…nCV (#2592) Given no parameters, cv2.imwrite and cv2.imencode write JPEG at quality 95 and WebP losslessly. The OpenCV-free fallback used Pillow's defaults, JPEG at quality 75 and lossy WebP, so ImageSink and in-memory dataset exports stored frames differently depending on whether OpenCV was installed. --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Jirka Borovec <6035284+Borda@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
…2596) * fix: handle source_image metadata in InferenceSlicer (closes #2594) * Implement test for source image metadata in slicer * Refactor detection merging and source image handling * Update changelog with fixes and deprecations * Refactor test_inference_slicer to use random image data * fix: resolve indentation and test kwargs in inference slicer * fix(inference_slicer): handle numpy arrays and unmergeable metadata across slices * test: add regression tests for metadata drop and source image restore * fix(detections): total metadata equality + NaN eq * refactor(detections): move lenient-merge policy into internal.py * fix(inference-slicer): warn once when metadata keys are dropped * fix(inference-slicer): restore source_image for PIL/raster input * perf(inference-slicer): assign source_image after overlap filter * test(inference-slicer): cover empty/single-slice/thread+NMM metadata paths * docs(inference-slicer): document drop rule, aliasing, and eq change * test(inference-slicer): cosmetic cleanup in metadata test suite --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: jirka <6035284+borda@users.noreply.github.com> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Bumps the uv group with 4 updates in the / directory: [cairosvg](https://github.com/Kozea/CairoSVG), [gitpython](https://github.com/gitpython-developers/GitPython), [pygments](https://github.com/pygments/pygments) and [soupsieve](https://github.com/facelessuser/soupsieve). Updates `cairosvg` from 2.8.2 to 2.9.0 - [Release notes](https://github.com/Kozea/CairoSVG/releases) - [Changelog](https://github.com/Kozea/CairoSVG/blob/main/NEWS.rst) - [Commits](Kozea/CairoSVG@2.8.2...2.9.0) Updates `gitpython` from 3.1.54 to 3.1.59 - [Release notes](https://github.com/gitpython-developers/GitPython/releases) - [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES) - [Commits](gitpython-developers/GitPython@3.1.54...3.1.59) Updates `pygments` from 2.19.2 to 2.20.0 - [Release notes](https://github.com/pygments/pygments/releases) - [Changelog](https://github.com/pygments/pygments/blob/master/CHANGES) - [Commits](pygments/pygments@2.19.2...2.20.0) Updates `soupsieve` from 2.8.4 to 2.9 - [Release notes](https://github.com/facelessuser/soupsieve/releases) - [Commits](facelessuser/soupsieve@2.8.4...2.9) --- updated-dependencies: - dependency-name: cairosvg dependency-version: 2.9.0 dependency-type: indirect dependency-group: uv - dependency-name: gitpython dependency-version: 3.1.59 dependency-type: indirect dependency-group: uv - dependency-name: pygments dependency-version: 2.20.0 dependency-type: indirect dependency-group: uv - dependency-name: soupsieve dependency-version: '2.9' dependency-type: indirect dependency-group: uv ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…ssing (#2600) `LineZone.trigger` confirmed a crossing whenever the oldest entry of its `minimum_crossing_threshold + 1` frame history differed from every later entry. That guard never checked that the tracker had actually been settled on the side it supposedly came from, so a single sub-threshold excursion was read as a crossing back into the side the tracker never left. With `minimum_crossing_threshold=2` the side sequence `A,A,A,B,A,A,A` left `[B,A,A]` in the history: the oldest `B` occurred once, the guard passed, and a crossing into `A` was counted. Counts drifted by one per flicker, in the wrong direction; three separated flickers gave `in_count=3` instead of `0`. A history only `threshold + 1` deep structurally cannot tell "settled on B" from "flickered to B for one frame". Track the last side each tracker was confirmed on and measure crossings against that instead of against the oldest history entry. A side is only promoted to confirmed once it has been held for the full threshold, so a flicker that reverts early never becomes the reference and produces no count. The confirmed side is dropped alongside the history on eviction so a reused tracker ID starts fresh. Sustained crossings, `minimum_crossing_threshold=1` (the default), and per-tracker isolation are unchanged. --------- Co-authored-by: jirka <6035284+borda@users.noreply.github.com> Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
…ssing (#2600) `LineZone.trigger` confirmed a crossing whenever the oldest entry of its `minimum_crossing_threshold + 1` frame history differed from every later entry. That guard never checked that the tracker had actually been settled on the side it supposedly came from, so a single sub-threshold excursion was read as a crossing back into the side the tracker never left. With `minimum_crossing_threshold=2` the side sequence `A,A,A,B,A,A,A` left `[B,A,A]` in the history: the oldest `B` occurred once, the guard passed, and a crossing into `A` was counted. Counts drifted by one per flicker, in the wrong direction; three separated flickers gave `in_count=3` instead of `0`. A history only `threshold + 1` deep structurally cannot tell "settled on B" from "flickered to B for one frame". Track the last side each tracker was confirmed on and measure crossings against that instead of against the oldest history entry. A side is only promoted to confirmed once it has been held for the full threshold, so a flicker that reverts early never becomes the reference and produces no count. The confirmed side is dropped alongside the history on eviction so a reused tracker ID starts fresh. Sustained crossings, `minimum_crossing_threshold=1` (the default), and per-tracker isolation are unchanged. --------- Co-authored-by: jirka <6035284+borda@users.noreply.github.com> Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
OpenCV's FFmpeg backend applies a video's display rotation to decoded frames and to the reported width and height. The PyAV fallback ignored it, so portrait phone videos were read sideways without OpenCV. --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: jirka <6035284+borda@users.noreply.github.com> Co-authored-by: Codex <codex@openai.com>
With IMREAD_UNCHANGED, OpenCV converts CMYK JPEGs to BGR and CMYK TIFFs to opaque BGRA. The Pillow fallback returned the raw ink channels in BGRA order, so IconAnnotator and draw_image used black ink as alpha. --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: jirka <6035284+borda@users.noreply.github.com>
draw_image reads image paths with IMREAD_UNCHANGED, which keeps a 16-bit PNG at 16 bits with OpenCV installed. The blend into the 8-bit scene then saturated every nonzero channel to 255. Scale 16-bit images to 8 bits on load, as the IconAnnotator icon loader already does. --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Jirka Borovec <6035284+Borda@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Codex <codex@openai.com>
…2604) MeanAverageRecall matched every prediction first and then kept the top K by confidence. The matcher pairs by highest IoU, so a prediction ranked below K could take a target from one ranked within it. Match each detection limit's own top K predictions instead. --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: jirka <6035284+borda@users.noreply.github.com> Co-authored-by: Codex <codex@openai.com>
…xes (#2605) as_detections built each skeleton's box from every key point that was not [0, 0] or non-finite, ignoring `visible`, while with_nms derives the same box from visible key points only. Apply the visible mask in as_detections too. --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: jirka <6035284+borda@users.noreply.github.com>
Bumps the uv group with 1 update in the / directory: [anyio](https://github.com/agronholm/anyio). Updates `anyio` from 4.9.0 to 4.14.2 - [Release notes](https://github.com/agronholm/anyio/releases) - [Commits](agronholm/anyio@4.9.0...4.14.2) --- updated-dependencies: - dependency-name: anyio dependency-version: 4.14.2 dependency-type: indirect dependency-group: uv ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the github-actions group with 1 update: [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv). Updates `astral-sh/setup-uv` from 10.0.1 to 10.1.0 - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](astral-sh/setup-uv@20cfd1b...bec219d) --- updated-dependencies: - dependency-name: astral-sh/setup-uv dependency-version: 10.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## release/latest #2617 +/- ##
================================================
Coverage ? 88%
================================================
Files ? 85
Lines ? 12391
Branches ? 0
================================================
Hits ? 10956
Misses ? 1435
Partials ? 0 🚀 New features to boost your workflow:
|
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Regenerate uv.lock so its package version and dependency constraints match pyproject.toml for frozen CI installs.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (1)
What changed in this PR
Patch release 0.30.5 fixes tracking, metrics, metadata, image/video fallback, drawing, and utility correctness issues.
Changes:
- Corrects crossing counts, mAR@K scoring, keypoint boxes, and single-image plotting.
- Improves OpenCV-free media handling and drawing.
- Adds metadata reconciliation, regression tests, release documentation, and workflow updates.
uv.lockrequires regeneration to matchpyproject.toml.
| File | Summary |
|---|---|
uv.lock |
Dependency lock metadata is out of sync and requires regeneration. |
tests/utils/test_video.py |
Adds rotated-video regression coverage. |
tests/utils/test_sinks.py |
Tests lossless WebP output. |
tests/utils/test_notebook.py |
Tests single-cell image grids. |
tests/metrics/test_mean_average_recall.py |
Tests per-image top-K mAR scoring. |
tests/key_points/test_core.py |
Tests invisible-keypoint box handling. |
tests/draw/test_utils.py |
Tests 16-bit image drawing. |
tests/detection/utils/test_internal.py |
Tests metadata comparison and merging. |
tests/detection/tools/test_inference_slicer.py |
Tests conflicting metadata reconciliation. |
tests/detection/test_line_counter.py |
Tests flicker-crossing behavior. |
tests/cv2/test_video.py |
Tests video rotation parity. |
tests/cv2/test_image.py |
Tests fallback image I/O parity. |
tests/annotators/test_core.py |
Tests icon rendering fixes. |
src/supervision/utils/notebook.py |
Supports 1×1 image grids. |
src/supervision/metrics/mean_average_recall.py |
Applies matching within each top-K limit. |
src/supervision/key_points/core.py |
Excludes invisible keypoints from boxes. |
src/supervision/draw/utils.py |
Scales 16-bit images before drawing. |
src/supervision/detection/utils/internal.py |
Improves metadata equality and merging. |
src/supervision/detection/tools/inference_slicer.py |
Handles conflicting slice metadata. |
src/supervision/detection/line_zone.py |
Prevents flicker crossings. |
src/supervision/config.py |
Adds the source-image metadata constant. |
src/supervision/annotators/core.py |
Handles grayscale and 16-bit icons. |
src/supervision/_cv2/_video.py |
Applies display rotation in fallback video reads. |
src/supervision/_cv2/_image.py |
Improves fallback image handling and encoding. |
pyproject.toml |
Bumps the package version to 0.30.5. |
docs/how_to/opencv_migration.md |
Documents encoder parity. |
docs/changelog.md |
Adds release notes for 0.30.5. |
.github/workflows/publish-testpypi.yml |
Updates release-branch checks. |
.github/workflows/publish-release.yml |
Updates release-branch checks. |
.github/workflows/publish-pre-release.yml |
Updates release-branch checks. |
.github/workflows/publish-docs.yml |
Updates documentation workflow tooling. |
.github/workflows/ci-tests.yml |
Updates branches and setup tooling. |
.github/workflows/ci-github-tests.yml |
Updates branches and setup tooling. |
.github/workflows/ci-check-links.yml |
Updates release-branch checks. |
.github/workflows/ci-build-docs.yml |
Updates branches and setup tooling. |
.github/workflows/build-package.yml |
Updates package build tooling. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

0.30.5: Tracking, metrics, and image-drawing correctness fixes
📋 Summary
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.triggermiscounted crossings by one per flicker whenever a tracker briefly touched the far side of the line, andMeanAverageRecallscored 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 inInferenceSliceron conflicting slice metadata (plus two relatedDetections.__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-bitdraw_image, grayscaleIconAnnotatoricons), and fix two isolated bugs inKeyPoints.as_detectionsandplot_images_grid. Every fix ships a regression test.✨ Spotlights / highlights
sv.LineZone.triggerno longer counts flicker as a crossingA crossing was confirmed whenever the oldest entry of a
minimum_crossing_threshold + 1frame history differed from every later entry, which never verified the tracker had actually settled on the side it supposedly came from. Withminimum_crossing_threshold=2the side sequenceA,A,A,B,A,A,Acounted a crossing intoA— the side the object never left — so counts drifted by one per flicker, in the wrong direction; three separated flickers gavein_count=3instead of0.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.MeanAverageRecallnow scores mAR@K from each image's own top KThe 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.71scored mAR@10.5alone but0.0once a second, unrelated prediction at IoU1.0and confidence0.1was added. Each detection limit now matches only its own top K predictions.sv.InferenceSlicerno longer crashes when slices disagree onmetadataRF-DETR and
inference-package connectors attach asource_imagearray 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_imageis reattached afterward as the full input image.Fixing this also closed two
Detections.__eq__bugs:NaNin a float-array metadata value now compares equal to itself, and a list-valued value against an ndarray-valued one now returnsFalseinstead of raisingValueError.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_detectionsdrops invisible key points from the derived boxas_detectionsstretched each box over every key point that wasn't[0, 0]or non-finite, ignoringvisible— unlikewith_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_inmarkers in the codebase target0.31.0and are untouched by this patch release.sv.config.SOURCE_IMAGE_METADATA_FIELDis a new public constant (added alongside theInferenceSlicerfix) naming theDetections.metadatakey RF-DETR/inference-package connectors use for the source image — additive, no existing code needs to change.📝 Notable changes
🔧 Fixed
Tracking / metrics
sv.LineZone.triggerno longer counts a spurious crossing in the opposite direction when a tracker flickers to the far side of the line for fewer thanminimum_crossing_thresholdframes. 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)sv.metrics.MeanAverageRecallnow 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)Model connectors
sv.InferenceSlicerno longer raises when slices disagree onmetadata(e.g. asource_imageNumPy 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 aSupervisionWarningswarning naming the dropped keys, emitted once per slicer instance.source_imageis a special case: removed from each slice before the lenient merge, then reattached afterward as the full input image. Fixing this also closed two relatedDetections.__eq__bugs: metadata holdingNaNin 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 returnsFalseinstead of raisingValueError. (#2596)OpenCV-free fallback backend
sv.VideoInfo.from_video_path,sv.get_video_frames_generatorandsv.process_videonow 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)sv.IconAnnotatorandsv.draw_imagenow 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)sv.ImageSinkand 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)sv.IconAnnotatorandsv.draw_imagenow 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 forIMREAD_UNCHANGEDrather than OpenCV's, causing shape-mismatchValueErrors or black-drawn transparent pixels. Other images, and every read with OpenCV installed, are unchanged. (#2588)Annotators / drawing
sv.draw_imagenow 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, sinceIMREAD_UNCHANGEDkeeps a 16-bit PNG at 16 bits. Eight-bit images, and images passed as arrays, are drawn as before. (#2603)sv.IconAnnotatornow draws grayscale PNG icons instead of failing withIndexError— the overlay only handled BGR and BGRA arrays, andcv2.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, ascv2.imreaddoes by default. Eight-bit color icons are drawn as before. (#2591)Key points / utils
sv.KeyPoints.as_detectionsnow leaves key points marked not visible out of the box it derives for each skeleton, assv.KeyPoints.with_nmsalready does. A skeleton with no visible key point is now dropped, as one with only missing key points already was. Key points withoutvisibleconvert as before. (#2605)sv.plot_images_gridnow plots a single image in agrid_size=(1, 1)grid, instead of failing withAttributeError—plt.subplotsreturns a loneAxesrather than an array for a 1x1 grid. Grids with more than one cell plot as before. (#2590)🏆 Contributors
draw_imageand grayscaleIconAnnotatordrawing fixes, plus theMeanAverageRecall/plot_images_grid/KeyPoints.as_detectionsfixesLineZoneflicker-crossing fixInferenceSlicerconflicting-metadata fix and the twoDetections.__eq__bugs it uncoveredFull changelog: 0.30.4...0.30.5