Compare commits

...
507 Commits
Author SHA1 Message Date
Piotr Skalski a61440ee0b Merge pull request #2005 from roboflow/develop
supervision-0.27.0 release
2025-11-16 19:05:40 +01:00
SkalskiP 7f7b25d7e7 bump version from 0.27.0rc6 to 0.27.0 2025-11-16 19:02:42 +01:00
SkalskiP 72506888c0 bump version from 0.27.0rc5 to 0.27.0rc6 2025-11-16 16:31:22 +01:00
Piotr Skalski c5cc24f22d Merge pull request #2017 from roboflow/0.27.0-changelog
supervision 0.27.0 changelog
2025-11-16 16:18:50 +01:00
SkalskiP 47574ed29d supervision 0.27.0 changelog 2025-11-16 16:15:10 +01:00
Piotr Skalski 6119f2017e Merge pull request #2016 from roboflow/feature/make_box_aspect_ratio_part_of_detections_api
add `box_aspect_ratio` property to `sv.Detections`
2025-11-16 13:39:49 +01:00
SkalskiP bbc37b4b6a fix failing tests 2025-11-16 13:34:51 +01:00
SkalskiP e12bfe263d add box_aspect_ratio property to sv.Detections 2025-11-16 13:27:29 +01:00
Piotr Skalski 373fbae1fa Merge pull request #2015 from roboflow/feature/support_for_qwen_3_vl
Qwen3-VL support
2025-11-16 01:02:29 +01:00
SkalskiP 790ccace2c Merge remote-tracking branch 'origin/feature/support_for_qwen_3_vl' into feature/support_for_qwen_3_vl 2025-11-15 22:54:34 +01:00
SkalskiP a99015ac2d more Qwen2.5-VL tests 2025-11-15 22:51:22 +01:00
pre-commit-ci[bot] 52c772d778 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-11-15 21:48:47 +00:00
SkalskiP 5af13c0f8c add Qwen3-VL prompting example 2025-11-15 22:48:20 +01:00
SkalskiP e0edb28ce6 Merge remote-tracking branch 'origin/feature/support_for_qwen_3_vl' into feature/support_for_qwen_3_vl 2025-11-15 22:27:20 +01:00
SkalskiP c35a35cefd plug Qwen3-VL into sv.Detections.from_vlm 2025-11-15 22:27:11 +01:00
pre-commit-ci[bot] 9bd8f71f26 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-11-15 21:19:57 +00:00
SkalskiP e29e3832fb plug Qwen3-VL into sv.Detections.from_vlm 2025-11-15 22:19:28 +01:00
SkalskiP a287e68c92 Merge remote-tracking branch 'origin/feature/support_for_qwen_3_vl' into feature/support_for_qwen_3_vl 2025-11-15 22:06:56 +01:00
SkalskiP 0422a66eaa plug Qwen3-VL into sv.Detections.from_vlm 2025-11-15 22:05:46 +01:00
pre-commit-ci[bot] 31db2ded11 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-11-15 20:40:07 +00:00
SkalskiP 9ab9650763 plug Qwen3-VL into sv.Detections.from_vlm 2025-11-15 21:39:23 +01:00
SkalskiP 353bd5336b Merge remote-tracking branch 'origin/feature/support_for_qwen_3_vl' into feature/support_for_qwen_3_vl 2025-11-15 21:37:05 +01:00
SkalskiP 7301156e46 plug Qwen3-VL into sv.Detections.from_vlm 2025-11-15 21:36:54 +01:00
pre-commit-ci[bot] e68c908077 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-11-15 20:29:38 +00:00
SkalskiP 2edcbdc487 initial commit with from_qwen_3_vl added, from_qwen_2_5_vl improved 2025-11-15 21:27:55 +01:00
Piotr Skalski 25c2f5cf0d Merge pull request #1997 from AnonymDevOSS/feat/threads-queues-video-process
feat: add threaded I/O pipeline for video processing
2025-11-15 02:40:10 +01:00
SkalskiP 316cb6271d make ruff happy 2025-11-15 02:33:11 +01:00
SkalskiP 70e9af9200 make ruff happy 2025-11-15 02:30:55 +01:00
pre-commit-ci[bot] 38f89125b1 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-11-15 01:10:49 +00:00
SkalskiP 80d76e712a style and docstring improvements 2025-11-15 02:10:04 +01:00
SkalskiP a296712000 improved InferenceSlicer docstring 2025-11-15 00:00:52 +01:00
Piotr Skalski 12d7c369db Merge pull request #2014 from roboflow/0.27.0-deprecations
remove 0.27.0 deprecations + InferenceSlicer improvements
2025-11-14 23:51:36 +01:00
pre-commit-ci[bot] c8223cbab2 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-11-14 22:15:39 +00:00
SkalskiP 2c522a9931 improved InferenceSlicer docstring 2025-11-14 23:14:34 +01:00
AnonymDevOSS fc0e133ea9 added example as it was delited by mistake 2025-11-14 21:53:14 +01:00
AnonymDevOSS 15364ac9a3 Rename process_video_threads to process_video and remove legacy implementation and redundant tests 2025-11-14 21:45:06 +01:00
pre-commit-ci[bot] e82b5b9d74 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-11-14 16:58:25 +00:00
SkalskiP c8f5c741fd reimplement crop_image to skip type casting when Pillow Image given
`get_image_resolution_wh` added
2025-11-14 17:55:07 +01:00
SkalskiP 8d464aa1b5 improve InferenceSlicer implementation lowering the count of generated boxes 2025-11-14 16:42:10 +01:00
SkalskiP ce28ab7fc7 fix uti tests for InferenceSlicer._generate_offset 2025-11-14 15:49:22 +01:00
SkalskiP fae1bc2661 bump version from 0.27.0rc4 to 0.27.0rc5 2025-11-14 14:52:57 +01:00
SkalskiP 4663fab4e4 update new docs page marks 2025-11-14 14:52:39 +01:00
SkalskiP db0892f3e9 remove unnecessary package-level import 2025-11-14 14:52:03 +01:00
Piotr Skalski d330bafaa7 Merge pull request #1926 from roboflow/dependabot/pip/develop/mkdocstrings-gte-0.25.2-and-lt-0.31.0
⬆️ Update mkdocstrings requirement from <0.30.0,>=0.25.2 to >=0.25.2,<0.31.0
2025-11-14 14:12:16 +01:00
Piotr Skalski 8b91d733af Merge pull request #1955 from roboflow/dependabot/github_actions/develop/pypa/gh-action-pypi-publish-1.13.0
⬆️ Bump pypa/gh-action-pypi-publish from 1.12.4 to 1.13.0
2025-11-14 14:11:57 +01:00
Piotr Skalski 97bf652d90 Merge pull request #1927 from roboflow/dependabot/pip/develop/build-gte-0.10-and-lt-1.4
⬆️ Update build requirement from <1.3,>=0.10 to >=0.10,<1.4
2025-11-14 14:11:24 +01:00
Piotr Skalski b0bbb74558 Merge pull request #1965 from roboflow/dependabot/github_actions/develop/actions/github-script-8.0.0
⬆️ Bump actions/github-script from 7.0.1 to 8.0.0
2025-11-14 13:50:13 +01:00
Piotr Skalski 1d73468dc5 Merge pull request #2012 from jackiehimel/fix/bugs-1959-2000
fix: correct numpy indexing in denormalize_boxes and add ultralytics validation
2025-11-14 13:25:26 +01:00
SkalskiP dfd2a60855 update denormalize_boxes docstring and examples; update from_google_gemini_2_0, from_google_gemini_2_5 and from_moondream to use latest version of denormalize_boxes 2025-11-14 13:12:39 +01:00
SkalskiP f563c5a634 update denormalize_boxes docstring and examples 2025-11-14 12:53:58 +01:00
SkalskiP 35272b40d3 remove ultralytics tests 2025-11-14 12:38:55 +01:00
Piotr Skalski 9320be5d5b Merge pull request #2013 from dominikWin/docs/how-to-json-path
docs: Update JSONSink example path
2025-11-14 12:28:25 +01:00
Piotr Skalski d109fba9d8 Merge pull request #2001 from AnonymDevOSS/feat/speed-up-box-iou-batch
feat: speed up box iou batch using 2d in-place ops instead of 3d
2025-11-14 12:26:25 +01:00
SkalskiP 42d92ba913 making ruff happy 2025-11-14 12:00:45 +01:00
pre-commit-ci[bot] 1e2dc8f88b fix(pre_commit): 🎨 auto format pre-commit hooks 2025-11-14 10:58:01 +00:00
SkalskiP f716b7fa3b improve docstrings, examples, variable naming conventions and unit tests 2025-11-14 11:57:31 +01:00
Dominik Winecki f6d52b53db docs: Update JSONSink example path 2025-11-13 13:48:16 -05:00
pre-commit-ci[bot] 61cca76acc fix(pre_commit): 🎨 auto format pre-commit hooks 2025-11-13 02:10:25 +00:00
jackiehimel 9ad850af46 fix: correct numpy indexing in denormalize_boxes and add ultralytics validation
- Fix denormalize_boxes numpy indexing bug that caused IndexError with 3+ boxes
- Add validation for missing boxes attribute in from_ultralytics
- Add comprehensive test coverage (11 new tests)

Fixes #1959
Fixes #2000
2025-11-12 20:58:25 -05:00
AnonymDevOSS eee6a85eaa Merge branch 'feat/speed-up-box-iou-batch' of github.com:AnonymDevOSS/supervision into feat/speed-up-box-iou-batch 2025-11-06 23:01:49 +01:00
AnonymDevOSS bb87ab0a6e fixed autoformat errors 2025-11-06 23:00:33 +01:00
pre-commit-ci[bot] da7c5cd65a fix(pre_commit): 🎨 auto format pre-commit hooks 2025-11-06 21:57:29 +00:00
AnonymDevOSS ceefe0bab2 feat/ speed up box iou - replaced original function; added tests 2025-11-06 22:52:25 +01:00
Piotr Skalski 78439e03cc Merge pull request #1999 from roboflow/dependabot/github_actions/develop/astral-sh/setup-uv-7.1.2
⬆️ Bump astral-sh/setup-uv from 6.4.3 to 7.1.2
2025-11-06 00:09:30 +01:00
Piotr Skalski b66416e074 Merge pull request #1993 from ernestlwt/fix/overlapmetric-to-upper
fix: Overlapmetric should check string using uppercase conversion
2025-11-06 00:07:20 +01:00
Piotr Skalski 6c7c49e051 Merge pull request #1974 from kshitijaucharmal/fix/add-pre-commit-in-pyproject
fix: added precommit under dev group
2025-11-05 23:58:32 +01:00
SkalskiP b1b69ad972 bump version from 0.27.0rc3 to 0.27.0rc4 2025-11-05 23:53:36 +01:00
Piotr Skalski 9bf9685165 Merge pull request #2007 from cdeil/patch-1
Fix Albumentations docs typo
2025-11-05 22:33:48 +01:00
SkalskiP 52be9dc818 fix filter_segments_by_distance docstring 2025-11-05 21:19:33 +01:00
Piotr Skalski c7e95483bf Merge pull request #2008 from roboflow/feature/filter_segments_by_distance
initial version of `filter_segments_by_distance`
2025-11-05 20:41:43 +01:00
SkalskiP 65f6e418a1 making flake8 happy 2025-11-05 20:34:22 +01:00
pre-commit-ci[bot] 1fef946a19 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-11-05 19:29:22 +00:00
SkalskiP 4c3d51f186 making flake8 happy 2025-11-05 20:28:55 +01:00
pre-commit-ci[bot] 9340cb962b fix(pre_commit): 🎨 auto format pre-commit hooks 2025-11-05 19:23:02 +00:00
SkalskiP 6578b4ea52 initial version of filter_segments_by_distance 2025-11-05 20:18:10 +01:00
Christoph Deil e37b32f5ca Fix Albumentations docs typo 2025-11-05 16:31:24 +01:00
SkalskiP 0bd60874ca bump version from 0.27.0rc2 to 0.27.0rc3 2025-11-05 13:39:13 +01:00
Piotr Skalski b02ec93b30 Merge pull request #2006 from roboflow/feature/xyxy_to_mask
Feature/xyxy to mask
2025-11-05 13:23:13 +01:00
pre-commit-ci[bot] 89ee57f519 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-11-05 12:14:45 +00:00
SkalskiP 1c8398942a docs and examples 2025-11-05 13:12:49 +01:00
pre-commit-ci[bot] 3e7e4caf3f fix(pre_commit): 🎨 auto format pre-commit hooks 2025-11-05 12:03:55 +00:00
SkalskiP a3ea8cdb1f bump version from 0.27.0rc1 to 0.27.0rc2 2025-11-05 13:01:30 +01:00
SkalskiP 32dca290ef xyxy_to_mask implementation + tests 2025-11-05 13:00:11 +01:00
AnonymDevOSS 03f623920a feat: tests for add threaded I/O pipeline for video processing 2025-10-28 14:05:52 +01:00
AnonymDevOSS 98476dfacf feat: add threaded I/O pipeline for video processing
Implements pipeline with bounded queues to overlap decode,
compute and encode. Reduces I/O stalls.
2025-10-28 14:05:37 +01:00
AnonymDevOSS 35ff87b08d feat: speed up box iou batch using 2d in-place ops instead of 3d
without creating intermediary (N, M, 2) arrays.
2025-10-28 13:51:28 +01:00
dependabot[bot] 03eed3fd64 ⬆️ Bump astral-sh/setup-uv from 6.4.3 to 7.1.2
Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 6.4.3 to 7.1.2.
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/e92bafb6253dcd438e0484186d7669ea7a8ca1cc...85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: 7.1.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 00:05:24 +00:00
Ernest Lim 272d9ed5bf fix: Overlapmetric should check string using uppercase conversion 2025-10-16 16:12:28 +08:00
Kshitij Aucharmal 371f8ecc5b fix: added precommit under dev group 2025-10-03 19:46:46 +05:30
Piotr Skalski 171687f3ab Merge pull request #1968 from kshitijaucharmal/develop
Fixed: random print("hm") in merge_metadata
2025-09-25 17:22:01 +02:00
Kshitij Aucharmal 421ad21c98 Fixed: random print("hm") in merge_metadata 2025-09-24 23:25:12 +05:30
dependabot[bot] 50358e96b6 ⬆️ Bump actions/github-script from 7.0.1 to 8.0.0
Bumps [actions/github-script](https://github.com/actions/github-script) from 7.0.1 to 8.0.0.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/60a0d83039c74a4aee543508d2ffcb1c3799cdea...ed597411d8f924073f98dfc5c65a23a2325f34cd)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: 8.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-22 00:11:46 +00:00
dependabot[bot] 86e10db19e ⬆️ Bump pypa/gh-action-pypi-publish from 1.12.4 to 1.13.0
Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.12.4 to 1.13.0.
- [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases)
- [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/76f52bc884231f62b9a034ebfe128415bbaabdfc...ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e)

---
updated-dependencies:
- dependency-name: pypa/gh-action-pypi-publish
  dependency-version: 1.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 00:07:58 +00:00
Piotr Skalski deb1c9c4f4 Merge pull request #1945 from roboflow/docs/refactor_0.27.0
docs/refactor 0.27.0 missing commits
2025-08-07 16:49:03 +02:00
pre-commit-ci[bot] 6679b1f5a9 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-08-07 14:45:36 +00:00
Piotr Skalski c7de70ed20 Merge pull request #1943 from roboflow/docs/refactor_0.27.0
docs/refactor 0.27.0
2025-08-07 13:46:36 +02:00
SkalskiP 2cdb70eb91 Merge remote-tracking branch 'origin/docs/refactor_0.27.0' into docs/refactor_0.27.0
# Conflicts:
#	supervision/utils/image.py
2025-08-07 13:32:56 +02:00
SkalskiP 2ef029ef4b visualize examples 2025-08-07 13:32:44 +02:00
pre-commit-ci[bot] dce96d462e fix(pre_commit): 🎨 auto format pre-commit hooks 2025-08-07 10:36:03 +00:00
SkalskiP 3fcbef9482 Merge remote-tracking branch 'origin/docs/refactor_0.27.0' into docs/refactor_0.27.0
# Conflicts:
#	supervision/utils/image.py
2025-08-07 12:35:35 +02:00
SkalskiP c8b966be1a align new utils with naming conventions 2025-08-07 12:35:09 +02:00
pre-commit-ci[bot] 72621dce32 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-08-07 10:30:22 +00:00
SkalskiP 098d5e3eb1 Merge remote-tracking branch 'origin/docs/refactor_0.27.0' into docs/refactor_0.27.0
# Conflicts:
#	supervision/utils/image.py
2025-08-07 12:29:54 +02:00
SkalskiP 8c49465d49 align new utils with naming conventions 2025-08-07 12:29:05 +02:00
pre-commit-ci[bot] 4ad36db3bb fix(pre_commit): 🎨 auto format pre-commit hooks 2025-08-07 09:53:22 +00:00
SkalskiP a933b93234 update image utils docs; deprecate overlay_image image 2025-08-07 11:51:59 +02:00
SkalskiP cbb58a8c6f tint_image and grayscale_image functions added 2025-08-07 10:01:50 +02:00
SkalskiP 8fc22f0281 move ImageType to a more suitable location 2025-08-07 07:54:50 +02:00
SkalskiP 4b7b347aea drop create tiles from public API 2025-08-07 07:45:55 +02:00
SkalskiP 5c6745291b after few tests drop support for pycon in docs examples 2025-08-07 07:41:52 +02:00
SkalskiP 83d490dadc initial support for pycon docs examples 2025-08-06 23:19:21 +02:00
SkalskiP 005cdbd37a bump version from 0.27.0rc0 to 0.27.0rc1 2025-08-06 12:27:16 +02:00
Piotr Skalski ade2668ae3 Merge pull request #1932 from roboflow/fix/numpy.int64-object-has-no-attribute-splitlines
fix: AttributeError: 'numpy.int64' object has no attribute 'splitlines'
2025-08-06 12:25:46 +02:00
pre-commit-ci[bot] 46e5965680 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-08-06 10:17:35 +00:00
SkalskiP aab3160cd7 make ruff happy 2025-08-06 12:17:04 +02:00
pre-commit-ci[bot] 13ab215fdf fix(pre_commit): 🎨 auto format pre-commit hooks 2025-08-06 10:05:47 +00:00
SkalskiP a08cda30fb ready for review 2025-08-06 12:02:51 +02:00
dependabot[bot] 652b0a190f ⬆️ Update build requirement from <1.3,>=0.10 to >=0.10,<1.4
Updates the requirements on [build](https://github.com/pypa/build) to permit the latest version.
- [Release notes](https://github.com/pypa/build/releases)
- [Changelog](https://github.com/pypa/build/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pypa/build/compare/0.10.0...1.3.0)

---
updated-dependencies:
- dependency-name: build
  dependency-version: 1.3.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-04 00:52:10 +00:00
dependabot[bot] 6f87e2639e ⬆️ Update mkdocstrings requirement
Updates the requirements on [mkdocstrings](https://github.com/mkdocstrings/mkdocstrings) to permit the latest version.
- [Release notes](https://github.com/mkdocstrings/mkdocstrings/releases)
- [Changelog](https://github.com/mkdocstrings/mkdocstrings/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mkdocstrings/mkdocstrings/compare/0.25.2...0.30.0)

---
updated-dependencies:
- dependency-name: mkdocstrings
  dependency-version: 0.30.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-04 00:50:49 +00:00
SkalskiP e1393691a4 bump version from 0.26.1 to 0.27.0rc0 2025-07-31 17:49:06 +02:00
Piotr Skalski 90f8582e4f Merge pull request #1919 from Ashp116/feature/spline-tracer
FEATURE: Spline Annotations
2025-07-31 16:53:33 +02:00
Ashp116 0c9fea4d9f UPDATE: Change Smooth to bool 2025-07-30 18:24:14 -04:00
Ashp116 cdca2e6b51 UPDATE: Removed SplineAnnotator and added smooth flag for TraceAnnotator 2025-07-30 13:48:38 -04:00
pre-commit-ci[bot] 8407d5a692 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-30 07:15:02 +00:00
Ashp116 eaf6cdd477 Precommit 2025-07-30 03:14:00 -04:00
pre-commit-ci[bot] f447b6fa95 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-30 07:10:19 +00:00
Ashp116 8c6ac2dd29 UPDATE: Made smoothing and spline_order as hyperparameters 2025-07-30 02:53:58 -04:00
Ashp116 22b6d3d464 ADD: Added a spline annotator 2025-07-30 02:04:24 -04:00
Piotr Skalski dda3900943 Merge pull request #1917 from roboflow/feat/label-annotator-offset
add: support for an additional parameter `text_anchor_offset` in label annotators
2025-07-29 14:50:34 +02:00
Piotr Skalski ac0c367e81 Merge pull request #1914 from roboflow/dependabot/github_actions/develop/astral-sh/setup-uv-6.4.3
⬆️ Bump astral-sh/setup-uv from 6.4.1 to 6.4.3
2025-07-29 14:48:27 +02:00
SkalskiP 6f2490cc23 fix formatting 2025-07-29 14:43:38 +02:00
Piotr Skalski 4a9d06eb50 Merge pull request #1918 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2025-07-29 14:40:38 +02:00
pre-commit-ci[bot] 21e311a319 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-29 12:40:11 +00:00
SkalskiP 71a14812fe LabelAnnotator and RichLabelAnnotator docs improvement 2025-07-29 14:39:41 +02:00
SkalskiP 35fdcb0a1a Merge branch 'develop' into feat/label-annotator-offset 2025-07-29 12:46:06 +02:00
Piotr Skalski 6d26a9766c Merge pull request #1915 from roboflow/feature/advanced_keypoints_indexing_and_slicing
advanced `sv.KeyPoints` filtering and slicing
2025-07-29 12:45:29 +02:00
SkalskiP 1cfaef9405 Merge branch 'develop' into feature/advanced_keypoints_indexing_and_slicing 2025-07-29 12:42:34 +02:00
pre-commit-ci[bot] 063e2092c8 chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.4 → v0.12.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.4...v0.12.5)
2025-07-28 17:28:44 +00:00
soumik12345 cb215d26f3 add: support for an additional parameter text_anchor_offset in _BaseLabelAnnotator 2025-07-28 19:03:17 +05:30
Soumik Rakshit 8bbe80e683 Merge pull request #1916 from AlexBodner/fix/docs-vitpose
Corrected vitpose docstring
2025-07-28 18:38:22 +05:30
Alex Bodner 9a0483446a fixed docs 2025-07-28 09:53:44 -03:00
pre-commit-ci[bot] f4561f9ba7 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-28 06:29:08 +00:00
SkalskiP beaf89cb22 initial version of advanced key points filtering and slicing + some naming refactor 2025-07-28 08:24:43 +02:00
dependabot[bot] 96b58c2860 ⬆️ Bump astral-sh/setup-uv from 6.4.1 to 6.4.3
Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 6.4.1 to 6.4.3.
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/7edac99f961f18b581bbd960d59d049f04c0002f...e92bafb6253dcd438e0484186d7669ea7a8ca1cc)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: 6.4.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-28 00:07:27 +00:00
Piotr Skalski 8873da4a30 Merge pull request #1902 from roboflow/dependabot/github_actions/develop/astral-sh/setup-uv-6.4.1
⬆️ Bump astral-sh/setup-uv from 6.3.1 to 6.4.1
2025-07-26 17:57:44 +02:00
Piotr Skalski 1362850986 Merge pull request #1904 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2025-07-26 17:57:15 +02:00
Piotr Skalski 98a4391677 Merge pull request #1913 from roboflow/fix/pending_track_id
fix for #1911
2025-07-26 17:51:14 +02:00
SkalskiP 036a956473 fix for #1911 2025-07-26 17:46:04 +02:00
Piotr Skalski b381fa198f Merge pull request #1884 from roboflow/feat/from_deepseek_vl_2_support
feat: 🚀 from_vlm deepseek vl 2 support
2025-07-25 16:57:44 +02:00
pre-commit-ci[bot] 8ca2e9fe8d fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-25 14:44:15 +00:00
soumik12345 bc1d73a307 chore: address feedback 2025-07-25 20:13:28 +05:30
Piotr Skalski 9679cd5c4e Merge pull request #1912 from roboflow/feature/edit_distance_and_fuzzy_match
`edit_distance` and `fuzzy_match_index` that will allow to match faulty class name generated by VLMs with exact class names
2025-07-25 14:52:24 +02:00
pre-commit-ci[bot] 00817df9e2 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-25 11:41:30 +00:00
SkalskiP ec189bf388 new, more memory efficient edit_distance implementation 2025-07-25 13:41:03 +02:00
SkalskiP 58bdce5707 tests fixed 2025-07-24 15:13:46 +02:00
pre-commit-ci[bot] 8262b6e64b fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-24 13:06:48 +00:00
SkalskiP 72fe0bf47f initial version of edit_distance and fuzzy_match_index that will allow to match faulty class name generated by VLMs with exact class names 2025-07-24 15:05:19 +02:00
soumik12345 137078602b add: tests 2025-07-24 15:17:05 +05:30
soumik12345 8fa21dd165 update: from_deepseek_vl_2 2025-07-24 15:04:59 +05:30
soumik12345 da70389546 add: prompt engineering tip for deepseek-vl2 2025-07-24 14:23:24 +05:30
soumik12345 cc1525707a update: docstring for Detections.from_vlm 2025-07-24 13:11:06 +05:30
soumik12345 b3aee43c3b update: from_deepseek_vl_2 logic 2025-07-24 12:55:40 +05:30
Soumik Rakshit 932c1aa91e Merge pull request #1910 from rafaelpadilla/fix/import_box_iou_batch
exposing `box_iou_batch`  in `supervision/detection/utils__init__.py`
2025-07-23 22:00:55 +05:30
rcvsq 76ee05420d exposing box_iou_batch in supervision/detection/utils__init__.py 2025-07-23 14:58:09 +00:00
soumik12345 a470bc2976 chore: make pre-commit happy 2025-07-23 18:36:31 +05:30
Soumik Rakshit cf725dd4db Merge branch 'develop' into feat/from_deepseek_vl_2_support 2025-07-23 18:31:07 +05:30
Piotr Skalski 7ecfc9f475 Merge pull request #1908 from roboflow/develop
`supervision-0.26.1` release
2025-07-23 08:46:45 +02:00
Piotr Skalski 317ac0810e Merge pull request #1907 from roboflow/changelog/0.26.1
update: changelog for 0.26.1
2025-07-23 08:35:47 +02:00
SkalskiP cb6018ef04 final changes before supervision-0.26.1 2025-07-23 08:32:07 +02:00
pre-commit-ci[bot] 580fcdde4c fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-22 10:51:53 +00:00
soumik12345 ad4f44644c update: changelog for 0.26.1 2025-07-22 16:20:38 +05:30
Piotr Skalski 0c8a9d34a6 Merge pull request #1906 from roboflow/fix/border_thickness
fix: border thickness parameter usage for `PercentageBarAnnotator`
2025-07-22 12:27:26 +02:00
soumik12345 d067f32678 fix: border thickness parameter usage for PercentageBarAnnotator 2025-07-22 15:48:54 +05:30
Piotr Skalski d251677774 Merge pull request #1894 from roboflow/fix/mAP-area
fix: correct area handling in MeanAveragePrecision for size-specific mAP
2025-07-22 08:07:20 +02:00
pre-commit-ci[bot] b1780fbae5 chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.3 → v0.12.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.3...v0.12.4)
2025-07-21 17:29:41 +00:00
SkalskiP e8cb962fcc bump version from 0.26.1 to 0.26.1rc0 2025-07-21 10:14:58 +02:00
dependabot[bot] 1e84351d60 ⬆️ Bump astral-sh/setup-uv from 6.3.1 to 6.4.1
Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 6.3.1 to 6.4.1.
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/bd01e18f51369d5a26f1651c3cb451d3417e3bba...7edac99f961f18b581bbd960d59d049f04c0002f)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: 6.4.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-21 00:32:44 +00:00
Piotr Skalski baa0402d5a Merge pull request #1901 from roboflow/fix/inference-overlap-metric-fix
fix:  🐞 update inference_slicer.py for improved detection handling
2025-07-21 00:07:28 +02:00
pre-commit-ci[bot] c54d16c57e fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-20 22:02:42 +00:00
SkalskiP 1adb75520f bump version from 0.26.0 to 0.26.1; improve docstrings; unify naming; improve parsing 2025-07-21 00:02:14 +02:00
pre-commit-ci[bot] db51a9ff98 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-20 17:12:24 +00:00
Onuralp SEZER f044398c17 fix: 🐞 update inference_slicer.py for improved detection handling 2025-07-20 20:11:37 +03:00
pre-commit-ci[bot] 704652a0d2 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-18 15:19:38 +00:00
Balthasar 6e3001194d improve tests 2025-07-18 17:19:10 +02:00
Balthasar d7774381cc Merge branch 'develop' into fix/mAP-area 2025-07-18 12:11:05 +02:00
Soumik Rakshit 1c8ec0a287 Merge pull request #1898 from roboflow/fix/negative-mAP-values
fix: handle -1 sentinel values in mAP calculation to prevent negative results and mimic pycocotools behaviour
2025-07-18 15:32:21 +05:30
Balthasar 118e3a496c fix: line length 2025-07-18 10:09:53 +02:00
pre-commit-ci[bot] a139fb0b49 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-18 08:00:37 +00:00
Balthasar afdbb0a75c fix: keep it DRY 2025-07-18 09:41:06 +02:00
Balthasar 001a4254f8 fix: improve code, more fixes and add tests 2025-07-18 09:41:06 +02:00
Balthasar 2e72e70893 basic fix 2025-07-18 09:41:06 +02:00
pre-commit-ci[bot] 2907c40ecc fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-17 08:00:04 +00:00
Balthasar 2c67cee720 fix: tests and adjust comment 2025-07-17 09:57:55 +02:00
Balthasar 0ddbcb2010 fix: start id with 1 because 0 means no match 2025-07-17 09:49:21 +02:00
pre-commit-ci[bot] 40d4c6f554 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-16 18:07:52 +00:00
Balthasar bf88636487 Merge branch 'fix/mAP-area' of https://github.com/roboflow/supervision into fix/mAP-area 2025-07-16 20:07:30 +02:00
Balthasar a444b3bde8 fix: cleanup comment 2025-07-16 20:06:27 +02:00
pre-commit-ci[bot] 4009f9d886 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-16 18:00:47 +00:00
Balthasar 3f926c618a fix: cleanup comments 2025-07-16 19:51:59 +02:00
Balthasar 71d6670e7d fix(metrics): update area calculation in MeanAveragePrecision to use area property as fallback if it's not COCO 2025-07-16 19:50:37 +02:00
Piotr Skalski 08226e9199 Merge pull request #1893 from roboflow/main
Merge pull request #1742 from roboflow/develop
2025-07-16 02:50:11 +02:00
Piotr Skalski d8de58d7f3 Merge pull request #1742 from roboflow/develop
`supervision-0.26.0` release
2025-07-16 02:39:07 +02:00
SkalskiP a1e402987b bump version from 0.26.0rc8 to 0.26.0 2025-07-16 02:33:29 +02:00
Piotr Skalski 78e5cd56f3 Merge pull request #1891 from roboflow/docs/vlm-improvements
docs: 📝 enhance prompt engineering tips for Qwen2.5-VL and Moondream models
2025-07-16 02:32:39 +02:00
Onuralp SEZER 462146b34e docs: 📝 add prompt engineering tips for Qwen2.5-VL and Moondream models for from_lmm 2025-07-16 02:30:12 +02:00
Onuralp SEZER 12e38da1a0 docs: 📝 enhance prompt engineering tips for Qwen2.5-VL and Moondream models 2025-07-16 01:50:34 +02:00
Piotr Skalski 56a0ac5cbf Merge pull request #1888 from roboflow/docs/changelog-0.26.0
update(docs): changelog + deprecation for supervision 0.26.0
2025-07-16 01:07:51 +02:00
pre-commit-ci[bot] d1aa1726f5 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-15 23:05:19 +00:00
SkalskiP 0f56a405c6 updates before supervision-0.26.0 release 2025-07-16 01:04:51 +02:00
SkalskiP 4948612cd4 Merge branch 'develop' into docs/changelog-0.26.0 2025-07-16 00:06:50 +02:00
pre-commit-ci[bot] 6046e88eb1 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-15 22:01:32 +00:00
soumik12345 77c45355ff chore: address feedback 2025-07-16 03:31:03 +05:30
soumik12345 3ff1414fc2 update: changelog 2025-07-16 02:20:01 +05:30
SkalskiP 4e832b2cf4 bump version from 0.26.0rc7 to 0.26.0rc8 2025-07-15 22:12:22 +02:00
Piotr Skalski 5c1a5eab3a Merge pull request #1890 from roboflow/refactor/split_detection_utils_to_prevent_circular_imports_and_increase_utils_visibility
refactor: split utils across multiple files to increase utils visibility and prevent circular imports
2025-07-15 22:05:50 +02:00
Onuralp SEZER 5d6f5bfde3 fix(deprecated): change MeanAveragePrecision to frozen dataclass and enhance deprecated decorator for classes 2025-07-15 22:41:25 +03:00
SkalskiP 2b47d8e4cc fix double decorator bug 2025-07-15 21:28:58 +02:00
SkalskiP bbadfb3cbd Revert "fix double decorator bug"
This reverts commit 880a9e8eb5.
2025-07-15 21:27:48 +02:00
SkalskiP 880a9e8eb5 fix double decorator bug 2025-07-15 21:21:05 +02:00
pre-commit-ci[bot] 5397031cee fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-15 19:14:46 +00:00
SkalskiP c4ede20a2d add deprecation message to old mAP 2025-07-15 21:13:46 +02:00
SkalskiP a135bd6268 add deprecation message to old mAP 2025-07-15 21:12:50 +02:00
SkalskiP 0a3333474a Merge remote-tracking branch 'origin/refactor/split_detection_utils_to_prevent_circular_imports_and_increase_utils_visibility' into refactor/split_detection_utils_to_prevent_circular_imports_and_increase_utils_visibility
# Conflicts:
#	supervision/detection/utils/iou_and_nms.py
2025-07-15 20:55:52 +02:00
SkalskiP d854e23e9a fix latex problems 2025-07-15 20:55:12 +02:00
pre-commit-ci[bot] 1bc672afd3 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-15 18:47:25 +00:00
soumik12345 14d3b79387 fix: formula in box_iou docstring 2025-07-16 00:17:00 +05:30
pre-commit-ci[bot] facc64bb3a fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-15 18:38:12 +00:00
SkalskiP f9cdb93b87 Merge remote-tracking branch 'origin/refactor/split_detection_utils_to_prevent_circular_imports_and_increase_utils_visibility' into refactor/split_detection_utils_to_prevent_circular_imports_and_increase_utils_visibility 2025-07-15 20:37:47 +02:00
SkalskiP 2dca99d0dc added mask_non_max_merge to quick imports 2025-07-15 20:35:44 +02:00
Onuralp SEZER 334d8e6506 fix: 🐞 correct possessive form in docstring for LMM and VLM enums 2025-07-15 21:34:24 +03:00
Onuralp SEZER 8710bde5b2 docs: 📝 add docstring to validate_vlm_parameters 2025-07-15 21:31:29 +03:00
pre-commit-ci[bot] 3ba5c12211 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-15 18:26:17 +00:00
Onuralp SEZER b8d646ac98 fix: 🐞 type annotations for internal.py vars 2025-07-15 21:23:41 +03:00
SkalskiP ab9d128828 mark from_lmm as deprecated in docs 2025-07-15 20:21:27 +02:00
SkalskiP 873f249bf0 Merge remote-tracking branch 'origin/refactor/split_detection_utils_to_prevent_circular_imports_and_increase_utils_visibility' into refactor/split_detection_utils_to_prevent_circular_imports_and_increase_utils_visibility 2025-07-15 20:10:53 +02:00
SkalskiP 5b42be9b4a make sure only recently updated pages have new status 2025-07-15 20:10:35 +02:00
pre-commit-ci[bot] b507ed90dd fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-15 17:56:39 +00:00
SkalskiP a25039ff54 Merge remote-tracking branch 'origin/refactor/split_detection_utils_to_prevent_circular_imports_and_increase_utils_visibility' into refactor/split_detection_utils_to_prevent_circular_imports_and_increase_utils_visibility 2025-07-15 19:56:13 +02:00
SkalskiP 6b905f2d18 works with python 3.9; docs update 2025-07-15 19:56:04 +02:00
Onuralp SEZER d52fcf17a8 fix: 🐛 add future annotations import for compatibility and fix tests 2025-07-15 20:41:38 +03:00
pre-commit-ci[bot] 592a649b50 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-15 17:26:01 +00:00
SkalskiP fa5fcbb264 utils split across multiple files; internal and external imports working; utils tests split across multiple files; tests working locally 2025-07-15 19:23:55 +02:00
Piotr Skalski 9dc2785762 Merge pull request #1886 from roboflow/feat/upgrade-python-3-9
feat: 🚀 dropping python 3.8 support and upgrade all codes to python3.9 syntax style
2025-07-15 15:59:20 +02:00
Onuralp SEZER 212e69b5a4 refactor: 🧪 type hints to use the new union syntax for optional types
- Updated type hints across multiple files to replace `Optional[X]` with `X | None` for better readability and consistency.
- Added `from __future__ import annotations` to files to enable postponed evaluation of type annotations.
- Adjusted function signatures and variable types in various modules including `polygon_zone.py`, `smoother.py`, `transformers.py`, `vlm.py`, and others.
- Ensured compatibility with Python 3.10+ type hinting features.

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2025-07-15 16:36:01 +03:00
Onuralp SEZER 7fde67ef75 Merge branch 'develop' into feat/upgrade-python-3-9 2025-07-15 16:26:55 +03:00
Piotr Skalski 8a7e2fedbc Merge pull request #1820 from hidara2000/develop
feat(annotators): enhance label annotators with frame boundary adjust…
2025-07-15 14:57:01 +02:00
Onuralp SEZER 3b2ca1b542 Merge branch 'develop' into feat/upgrade-python-3-9 2025-07-15 15:32:12 +03:00
Onuralp SEZER 8a000b93b0 Merge pull request #1889 from roboflow/docs/mathjax
docs(supervision): configure mkdocs to add mathjax support
2025-07-15 15:31:26 +03:00
Onuralp SEZER b6cd9dd3f3 chore: 🧹 clean up unused imports and optimize code structure and convert more List to list 2025-07-15 15:30:08 +03:00
pre-commit-ci[bot] a5155e23f2 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-15 12:27:38 +00:00
Onuralp SEZER 3dd1ce4e1c Merge branch 'develop' into feat/upgrade-python-3-9 2025-07-15 15:27:05 +03:00
Piotr Skalski 71e7c3e17c Merge pull request #1774 from SunHao-AI/develop
Support for IOS Matching Metric. Introduced the `mask_non_max_merge` function for handling non-maximum merging of masks
2025-07-15 14:11:40 +02:00
pre-commit-ci[bot] cf179b9dfe fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-15 11:48:01 +00:00
Soumik Rakshit 9f3ffdd26d fix: typo in utils.py 2025-07-15 17:17:39 +05:30
soumik12345 7c1168fbc6 chore: make pre-commit happy 2025-07-15 16:46:10 +05:30
soumik12345 af31b96199 update: docstring for box_iou to add formula 2025-07-15 16:41:54 +05:30
pre-commit-ci[bot] 9400270e34 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-15 11:05:31 +00:00
soumik12345 bcbbc57854 chore: configure mkdocs to add mathjax support 2025-07-15 16:34:22 +05:30
Onuralp SEZER b792db72fd chore: 🧹 update requirements.txt to remove version constraints for dependencies 2025-07-15 12:29:47 +02:00
soumik12345 84c9a34f92 update: docstring 2025-07-15 15:58:38 +05:30
soumik12345 c11a7de949 chore: refactor docs for 2025-07-15 15:58:26 +05:30
pre-commit-ci[bot] f68a6bc362 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-15 10:16:29 +00:00
soumik12345 7ea8c2e43c chore: remove excessive comments in group_overlapping_masks 2025-07-15 15:46:10 +05:30
Onuralp SEZER 2adb64b949 chore: 🧹 update requirements files to remove version constraints 2025-07-15 12:15:55 +02:00
pre-commit-ci[bot] 9a38ebd15e fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-15 10:14:09 +00:00
soumik12345 4977fd64b1 add: test_mask_non_max_merge 2025-07-15 15:43:44 +05:30
pre-commit-ci[bot] ae77077853 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-15 10:05:00 +00:00
soumik12345 007d8f5dad chore: make enum comparisons 2025-07-15 15:34:24 +05:30
soumik12345 b3024e780d chore: make pre-commit happy 2025-07-15 15:01:57 +05:30
soumik12345 ebf4b930b7 add: test for mask_non_max_merge 2025-07-15 15:01:36 +05:30
soumik12345 fd25f4bf81 add: docs for mask_non_max_merge 2025-07-15 15:01:25 +05:30
soumik12345 3fed5f9a25 update: docs 2025-07-15 14:18:00 +05:30
soumik12345 7849831e12 chore: make pre-commit happy 2025-07-15 14:18:00 +05:30
pre-commit-ci[bot] 42e9c5ffcb fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-15 07:37:28 +00:00
soumik12345 fb16bd9ee1 chore: change match_metric to overlap_metric 2025-07-15 13:07:01 +05:30
soumik12345 2c986abf5e add: remove_noisy_segments in the changelog 2025-07-15 12:20:23 +05:30
Onuralp SEZER 667e6491ac chore: 🧹 update uv lock file 2025-07-15 02:13:44 +02:00
Onuralp SEZER 1d3e887fca chore: 🧹 update requirements and change default weights file in ultralytics_example.py for count people example 2025-07-15 00:58:35 +03:00
Onuralp SEZER 63cc934cd4 chore: 🧹 remove unused pyupgrade configuration from pyproject.toml 2025-07-15 00:17:52 +03:00
Onuralp SEZER edfe0a944a refactor: 🛠️ update type hints to use built-in types in VLM and validation functions 2025-07-15 00:16:40 +03:00
Onuralp SEZER 25edd9be21 Merge branch 'develop' into feat/upgrade-python-3-9 2025-07-15 00:07:17 +03:00
Piotr Skalski daaadaa1f9 Merge pull request #1877 from roboflow/feat/gemini-2_5_segmentation
feat: 🚀 add support for Google Gemini 2.5 bounding box and mask parsing for "from_vlm"
2025-07-14 23:04:33 +02:00
Onuralp SEZER 9443a433fc fix: 🐞quick typo fix. 2025-07-14 22:03:00 +03:00
soumik12345 b2776cadf7 chore: make pre-commit happy 2025-07-15 00:32:03 +05:30
soumik12345 1337b7ffd5 update: changelog + deprecation for supervision 0.26.0 2025-07-15 00:28:23 +05:30
Onuralp SEZER efe59cb91c chore: 🧹 use same var name compare to other functions 2025-07-14 20:57:07 +03:00
Onuralp SEZER 13fa672d9e chore: 🧹formatting fix 2025-07-14 20:50:54 +03:00
Onuralp SEZER 85ec990aa4 docs: 📝 improve documentation for VLM parameters
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2025-07-14 20:48:35 +03:00
pre-commit-ci[bot] 6929f1b798 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-14 17:32:55 +00:00
SkalskiP 63519dd89c Merge remote-tracking branch 'origin/feat/gemini-2_5_segmentation' into feat/gemini-2_5_segmentation 2025-07-14 19:32:33 +02:00
SkalskiP 2b052430bd updated from_lmm docs 2025-07-14 19:32:14 +02:00
Onuralp SEZER 7b8560016e Merge pull request #1887 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2025-07-14 20:27:14 +03:00
pre-commit-ci[bot] 886bc16fd4 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-14 17:25:43 +00:00
pre-commit-ci[bot] 4ea79f3634 chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.12.2 → v0.12.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.2...v0.12.3)
2025-07-14 17:24:59 +00:00
Onuralp SEZER 5d883447a3 docs: 📝 update Python version requirement to 3.9 in docs and clean up code 2025-07-14 20:21:35 +03:00
Onuralp SEZER 00fab37e58 feat: 🚀 dropping python 3.8 support and upgrade all codes to python3.9 syntax style as min
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2025-07-14 20:12:11 +03:00
Onuralp SEZER 328c34ab8b feat: ✨ add MOONDREAM enum and allowed arguments in VLM 2025-07-14 19:59:44 +03:00
Onuralp SEZER d29fb8ad05 test: 🧪 add test case for Google Gemini 2.5 with sample JSON output for 2 masks 2025-07-14 18:48:17 +03:00
Onuralp SEZER c2abb24c26 test: 🧪 add test case for gemini 2.5 segmentation 2025-07-14 18:38:32 +03:00
Onuralp SEZER 8c4a1e5a5e fix: 🐞 correct formatting and remove trailing whitespace in core.py 2025-07-14 17:59:48 +03:00
pre-commit-ci[bot] ff3c85d380 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-14 14:57:26 +00:00
Onuralp SEZER 2c63511af6 docs: ✏️ add usage tips and examples for Google Gemini 2.5 in core.py 2025-07-14 17:56:58 +03:00
soumik12345 9517987b7b chore: make pre-commit happy 2025-07-14 20:19:32 +05:30
Onuralp SEZER 129eb89c20 fix: 🐞 add missing newline at end of extra.css file 2025-07-14 17:29:25 +03:00
Onuralp SEZER e8ca201198 Merge branch 'develop' into feat/gemini-2_5_segmentation 2025-07-14 17:29:00 +03:00
Onuralp SEZER b87a504d22 fix: 🐞 remove GOOGLE_GEMINI_2_5 from gemini_2_0 check and only use new gemini_2_5 func 2025-07-14 17:26:59 +03:00
Onuralp SEZER 076f9e293f fix: 🐞 fix mask filtering in from_google_gemini_2_5 to detect properly 2025-07-14 17:18:03 +03:00
Onuralp SEZER 6c4a2a8d44 test: 🧪 add gemini_2_5 test and fix failed cases in gemini_2_5 function for better to handle different cases
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2025-07-14 16:50:41 +03:00
SkalskiP 1a83005a44 add docs table style 2025-07-14 15:12:27 +02:00
pre-commit-ci[bot] 74d5f0f9a6 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-14 13:07:22 +00:00
Soumik Rakshit 50c0d54dd5 Merge branch 'develop' into develop 2025-07-14 18:36:25 +05:30
Onuralp SEZER 377f7db0ad feat: ✨ add confidence field to the result of from_google_gemini_2_5 function 2025-07-14 15:22:10 +03:00
Onuralp SEZER 3b112ffb46 refactor: ✨ update from_google_gemini_2_5 function to include optional classes parameter and adjust return values 2025-07-14 15:11:12 +03:00
Piotr Skalski a192eed093 Merge pull request #1874 from Ashp116/single-iou
Feature: box_iou
2025-07-14 13:37:35 +02:00
soumik12345 18cd73f55a chore: import box_iou to supervision/__init__.py 2025-07-14 17:03:23 +05:30
Onuralp SEZER 4ed667da88 fix: 🐞 rename from_google_gemini to from_google_gemini_2_0 keep names consistent 2025-07-14 14:24:37 +03:00
Onuralp SEZER 5d866fd8a1 refactor: ✨ improve validate_resolution function to have type,value check similar to other validate functions 2025-07-14 14:14:03 +03:00
Onuralp SEZER ddf9a060ba Merge branch 'develop' into feat/gemini-2_5_segmentation 2025-07-14 14:10:12 +03:00
pre-commit-ci[bot] 8e437f89fa fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-14 10:49:36 +00:00
soumik12345 9ce4632f47 chore: add usage examples in docstrings 2025-07-14 16:18:04 +05:30
Piotr Skalski baa78fc4da Merge pull request #1882 from roboflow/feat/vlm-tests
Remove test file for from_florence_2 function and merge into main test_vlm file
2025-07-14 12:22:05 +02:00
Onuralp SEZER 2bf448f285 refactor: 🧹 remove test file for from_florence_2 function and merge into main test_vlm file
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2025-07-14 13:09:58 +03:00
Piotr Skalski 462468f3d8 Merge pull request #1878 from roboflow/feat/moondream-vlm-object-detection
feat: 🚀  Support for Moondream VLM for Object Detections
2025-07-14 11:46:57 +02:00
Onuralp SEZER 6bae8bc6e6 formatting: 🧹 code readability by formatting return statements and function signatures in Google Gemini 2.5 integration 2025-07-14 11:02:19 +03:00
Onuralp SEZER 3152f5f0ea docs: 📝 add docs to confidence scores parameter to from_google_gemini_2_5 function 2025-07-14 10:59:49 +03:00
Onuralp SEZER c7ebfeb4bc feat: 🚀 enhance Google Gemini 2.5 integration to include confidence scores in detection results 2025-07-14 10:58:59 +03:00
Ashp116 d1c27f2d2b FIX: Fixed docsstrings 2025-07-12 18:13:37 -04:00
Ashp116 92599ad497 UPDATE: Updated documentation 2025-07-12 15:59:59 -04:00
Ashp116 a118599233 Precommit 2025-07-12 14:57:18 -04:00
pre-commit-ci[bot] 2f44aab2b5 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-12 18:56:14 +00:00
Ashp116 0488abf083 Merge branch 'single-iou' of https://github.com/Ashp116/supervision into single-iou 2025-07-12 14:55:50 -04:00
Ashp116 d2ffd04aed Precommit 2025-07-12 14:54:26 -04:00
pre-commit-ci[bot] ee7782b551 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-12 18:52:14 +00:00
Ashp116 2378d8bdca UPDATE: Code review fix and docs updated 2025-07-12 14:50:43 -04:00
Onuralp SEZER 3218da8cbd test: 🧪 re-add parameterized tests for from_moondream function 2025-07-11 21:39:45 +03:00
Onuralp SEZER eb330ac1c1 Merge branch 'develop' into feat/moondream-vlm-object-detection 2025-07-11 21:13:34 +03:00
Onuralp SEZER 641b343c7f docs: 📝 update resolution parameter to fixed values in Detections class methods 2025-07-11 20:17:28 +03:00
Onuralp SEZER 0601a4157d refactor: simplify return statement in from_moondream function 2025-07-11 20:16:07 +03:00
SkalskiP 62e80b82ac update docstring examples 2025-07-11 19:05:18 +02:00
Piotr Skalski a352521c15 Merge pull request #1881 from roboflow/feat/pyproject-deps-groups
feat: 🚀 exclude all dev extras from pypi package and only available to dev installations #pep-0735
2025-07-11 18:49:33 +02:00
Piotr Skalski 8697fc0233 Merge pull request #1883 from roboflow/fix/gemini-vlm-support
chore: address feedback for google gemini support
2025-07-11 18:46:25 +02:00
soumik12345 fe0cefd1dc chore: add comments to testcase parameters 2025-07-11 21:44:14 +05:30
soumik12345 4d364885e9 update: docstring 2025-07-11 21:40:04 +05:30
Onuralp SEZER 535cb2a91b fix(vlm): 📝 correct formatting in docstring for from_deepseek_vl_2 function 2025-07-11 18:49:50 +03:00
pre-commit-ci[bot] 8e845caeeb fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-11 15:44:15 +00:00
Onuralp SEZER 9e2a535044 Merge branch 'develop' into feat/from_deepseek_vl_2_support 2025-07-11 18:43:02 +03:00
soumik12345 857aad66fc update: docstring 2025-07-11 21:10:19 +05:30
soumik12345 b860edd8f5 update: test_from_google_gemini 2025-07-11 21:04:50 +05:30
soumik12345 ca86f6e64d chore: address feedback 2025-07-11 21:00:42 +05:30
Onuralp SEZER d002b8afbf fix: 🐞 remove unused class_id references in Detections class from_vlm method 2025-07-11 18:14:35 +03:00
Onuralp SEZER c613005f69 fix: 🐞 update Detections class to remove class_id return from from_moondream function 2025-07-11 18:10:50 +03:00
Onuralp SEZER 99415f1da1 test: 🧪 add parameterized tests for from_moondream function in test_vlm.py 2025-07-11 18:08:43 +03:00
Onuralp SEZER 656b9c6c89 fix: 🐞 update from_moondream function to remove class_id return and adjust return type 2025-07-11 18:08:33 +03:00
Onuralp SEZER d34f050642 feat: 🚀 implement resolution validation function and refactor related code 2025-07-11 17:41:52 +03:00
Onuralp SEZER 89f4242e6f docs: 📝 add usage examples for from_lmm with Moondream results in Detections class
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2025-07-11 17:30:29 +03:00
Onuralp SEZER f7d4da9b1d ci: 👷 update CI installation commands to use --group instead of --extra because of pep rule "pep-0735" 2025-07-11 17:18:12 +03:00
Onuralp SEZER 1034313ac2 feat: 🚀 exclude all dev extras from pypi package and only available to dev installations #pep-0735 2025-07-11 17:13:11 +03:00
pre-commit-ci[bot] a6d9550a9a fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-11 13:51:24 +00:00
Onuralp SEZERandSoumik Rakshit fd1bb5b4b2 Update supervision/detection/vlm.py
Co-authored-by: Soumik Rakshit <19soumik.rakshit96@gmail.com>
2025-07-11 16:49:58 +03:00
Piotr Skalski 8c581e4855 Merge pull request #1880 from roboflow/docs/cookbook-updates
chore: 🧹 removal of version constraint, using latest bbox annotator and removal of asset extra from installations
2025-07-11 14:10:32 +02:00
Onuralp SEZER bf2a67b85b chore: 🧹 update cookbook.html notebook versions to v0.26.0 2025-07-11 02:18:43 +02:00
hidara2000 d3a2012750 Merge branch 'roboflow:develop' into develop 2025-07-11 10:16:58 +10:00
hidara2000 0b00edc526 Remove redundant duplicated calls to spread_out_boxes 2025-07-11 10:14:07 +10:00
Onuralp SEZER fa98266914 chore: 🧹 removal of version constraint, using latest bbox annotator and removal of asset extra from installations
- Removed specific version constraint for `supervision` package in installation command.
- Updated references from `sv.BoundingBoxAnnotator` to `sv.BoxAnnotator` for consistency with the latest library changes.
- Removed asset extension of package installation because it is has been included into main package

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2025-07-11 02:10:08 +02:00
hidara2000 d7e0dedf74 pull from main 2025-07-11 09:57:06 +10:00
Onuralp SEZER f90a94be2e chore: 🧹 drop asset extra from installation and clean up import in video download duplicate 2025-07-11 01:56:42 +02:00
Onuralp SEZER 5206b6cbbb docs: 📝 update sv and ultralytics packages and remove old BoundingBox annotator from cookbook for count objects notebook
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2025-07-11 01:11:46 +02:00
Onuralp SEZER 58aacc788d fix: 🐞 update Moondream result format fix and doc example code block fixed
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2025-07-11 00:03:04 +03:00
Onuralp SEZER 0343db7d95 fix: 🐞 update Moondream result format to remove label requirement in Detections class 2025-07-10 23:57:27 +03:00
Onuralp SEZER 7076b4da0c docs: 📝 add examples for using from_vlm with Moondream results in Detections class
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2025-07-10 23:54:38 +03:00
Onuralp SEZER e17e7d712f fix: 🐞 remove class_name from from_moondream return in Detections class 2025-07-10 23:30:33 +03:00
Onuralp SEZER 399d2efa59 fix: 🐞 update from_moondream function to remove label requirement and improve box denormalization 2025-07-10 23:07:56 +03:00
Onuralp SEZER 300426ed41 fix: 📝 remove deprecated Google Gemini flash preview support from Detections class 2025-07-10 21:03:42 +03:00
Piotr Skalski 20066174e5 Merge pull request #1879 from roboflow/fix-utils-page 2025-07-10 20:01:34 +02:00
James 413db5a1e5 fix broken page 2025-07-10 18:39:38 +01:00
Onuralp SEZER 9855927597 feat: 🚀 add support for Moondream in Detections class
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2025-07-10 20:32:50 +03:00
Onuralp SEZER 72bff07498 fix: 📝 update docstring for from_moondream function to comply with style guide 2025-07-10 20:29:41 +03:00
Onuralp SEZER 409ff28200 feat: 🚀 add initial support for Moondream in VLM enum and implement from_moondream function for sv.Detections
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2025-07-10 20:21:41 +03:00
Onuralp SEZER 5d2616093d feat: 🚀 add support for Google Gemini 2.5 detection processing 2025-07-10 19:45:02 +03:00
Onuralp SEZER 74ba99479c chore: 🧹 clean up imports and improve formatting in Google Gemini functions 2025-07-10 19:44:48 +03:00
Onuralp SEZER 0e8ec18c20 fix: 🐞 remove not exist Google Gemini 2.0 and 2.5 flash preview mappings 2025-07-10 19:39:00 +03:00
Onuralp SEZER 7ca52da776 feat: 🚀 add support for Google Gemini 2.5 bounding box and mask parsing 2025-07-10 19:38:35 +03:00
Piotr Skalski 2dfadfe1b1 Merge pull request #1876 from roboflow/fix/google-gemini-support
update: google gemini support
2025-07-10 18:21:56 +02:00
SkalskiP 8b39478913 fix malfunctioned docstring 2025-07-10 18:18:38 +02:00
Soumik Rakshit 2c0eaa1059 update: docstrings 2025-07-10 14:02:01 +00:00
pre-commit-ci[bot] 4804d51de4 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-10 13:49:03 +00:00
soumik12345 097af2a38a add: qwen example 2025-07-10 19:18:24 +05:30
pre-commit-ci[bot] 9cc6d2d325 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-10 13:13:43 +00:00
soumik12345 b4f30b5ad6 update: docstrings 2025-07-10 18:41:59 +05:30
soumik12345 abb881c1a6 update: docstrings 2025-07-10 18:39:15 +05:30
soumik12345 6448fc7ea1 chore: address feedback on google gemini support 2025-07-10 18:32:18 +05:30
Piotr Skalski 98ba121b5c Merge pull request #1875 from roboflow/fix-wrapped-text
Fix wrapped `supervision` text in navigation bar
2025-07-10 12:43:44 +02:00
SkalskiP f50216eebf remove duplicated features from mkdocs.yaml 2025-07-10 12:10:10 +02:00
James 21a3584513 fix 2025-07-10 11:03:48 +01:00
pre-commit-ci[bot] 091559433a fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-10 09:55:06 +00:00
James b15a7ea005 fix wrapped text 2025-07-10 10:52:48 +01:00
Piotr Skalski 451b4b25d1 Merge pull request #1858 from roboflow/new-theme
New documentation theme
2025-07-10 11:39:23 +02:00
SkalskiP 4879ed38c8 include maestro link 2025-07-10 11:23:56 +02:00
Onuralp SEZER 096b9a5b99 Merge pull request #1792 from roboflow/feature/gemini-object-detection
🚀 from_vlm now has Google gemini 2D spatial understanding support for Detection class 🎯
2025-07-10 11:05:10 +03:00
pre-commit-ci[bot] 5467fbf7c2 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-10 07:08:54 +00:00
Ashp116 6e87237070 ADD: Added box_iou 2025-07-10 03:05:30 -04:00
James 796cc26fb9 fix version card alignment 2025-07-09 17:25:17 +01:00
Onuralp SEZER 174253715e fix: 🐞 update expected output in test_from_google_gemini for bbox coordinates 2025-07-09 19:23:18 +03:00
James 8373a38b35 fix conflict 2025-07-09 17:12:13 +01:00
Onuralp SEZER e608d8204f fix: 🐞 Google Gemini bbox order for normaliztion convert and add new enums for feature models to come 2025-07-09 19:04:19 +03:00
Onuralp SEZER ddfbaa3127 Merge pull request #1831 from roboflow/dependabot/github_actions/develop/astral-sh/setup-uv-6.0.0
⬆️ Bump astral-sh/setup-uv from 5.4.2 to 6.3.1
2025-07-09 16:18:47 +03:00
Soumik Rakshit b5bf082a95 Merge pull request #1872 from roboflow/deprecate/DetectionDataset.images
deprecate(supervision): DetectionDataset.images
2025-07-09 18:46:47 +05:30
pre-commit-ci[bot] d48614d72b fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-09 13:13:54 +00:00
soumik12345 b736499fc9 deprecate: DetectionDataset.images 2025-07-09 18:35:04 +05:30
pre-commit-ci[bot] 031a0296f9 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-09 12:55:37 +00:00
Onuralp SEZER 84d7fb29e0 Merge branch 'develop' into feature/gemini-object-detection 2025-07-09 15:54:58 +03:00
Onuralp SEZER 0af46f6416 chore(setup-uv): 📦 update astral-sh/setup-uv to v6.3.1 across workflow files 2025-07-09 15:43:39 +03:00
Onuralp SEZER a64874f27d Merge pull request #1871 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2025-07-09 15:40:22 +03:00
pre-commit-ci[bot] e99fc921df fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-08 11:25:59 +00:00
James 0a7bf58905 respond to feedback 2025-07-08 12:25:35 +01:00
Piotr Skalski 334d0619e6 Merge pull request #1869 from roboflow/fix/docs/track_objects
fix(docs): Track Objects on Video example not retrieving class name correctly
2025-07-08 13:08:50 +02:00
Soumik Rakshit 1190ed2931 update: image.py 2025-07-08 16:35:59 +05:30
Soumik Rakshit ccf84d5653 update: image.py 2025-07-08 16:21:08 +05:30
Soumik Rakshit 8a905ce79a Merge branch 'develop' into fix/docs/track_objects 2025-07-08 16:12:17 +05:30
pre-commit-ci[bot] 724d851dcd chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/PyCQA/bandit: 1.8.5 → 1.8.6](https://github.com/PyCQA/bandit/compare/1.8.5...1.8.6)
- [github.com/astral-sh/ruff-pre-commit: v0.12.1 → v0.12.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.1...v0.12.2)
2025-07-07 17:34:37 +00:00
Piotr Skalski 95d7204ae8 Merge pull request #1834 from rafaelpadilla/fix/mAP
Fix/mAP
2025-07-07 15:43:44 +02:00
pre-commit-ci[bot] 5aa6b6f959 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-07 13:30:30 +00:00
SkalskiP fcb47f7226 mkdocs update + pre-commit fix 2025-07-07 15:30:05 +02:00
Hao Sun f47f032b23 Merge branch 'roboflow:develop' into develop 2025-07-07 15:27:29 +08:00
pre-commit-ci[bot] 0b120f1748 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-07 07:21:58 +00:00
AI-Hao 13e857cf00 refactor(detection): 重构合并检测对象的逻辑
- 移除 merge_inner_detections_objects 函数中的 threshold 和 match_metric 参数
- 新增 merge_inner_detections_objects_without_iou 函数,用于合并检测对象
- 优化非最大值抑制(NMM)的逻辑,提高效率
- 调整 mask_iou_batch 函数的实现,简化代码
2025-07-07 15:21:28 +08:00
rafaelpadilla b867968a10 breaking long lines, preventing E501. 2025-07-06 03:33:30 +00:00
rafaelpadilla a95a818441 Including function get_coco_class_index_mapping in supervision.__init__.py.
Unifying default value for parameter `use_iscrowd`.
Adding detailed docstrings to function `get_coco_class_index_mapping`.
2025-07-06 03:02:07 +00:00
rafaelpadilla 03430699f9 Expanded all test cases in test_coco_annotations_to_detections including use_iscrowd=True and use_iscrowd=False. 2025-07-06 02:07:52 +00:00
rafaelpadilla c7b4993721 Moving _jaccard and box_iou_batch_with_jaccard to supervision/detection/utils.py. 2025-07-06 01:31:30 +00:00
Rafael PadillaandPiotr Skalski d543be924f Update supervision/dataset/formats/coco.py
Co-authored-by: Piotr Skalski <piotr.skalski92@gmail.com>
2025-07-05 21:41:02 -03:00
soumik12345 2ef9d69e01 chore: make pre-commit happy 2025-07-04 17:07:53 +05:30
soumik12345 3786017424 fix: Track Objects on Video example not retrieving class name correctly 2025-07-04 17:01:05 +05:30
Onuralp SEZER a6f738d2d1 Merge pull request #1867 from JESUSROYETH/fix/typo-xyxy_to_xcycarh
fix: typo in __all__ (xyxy_to_xcycarh)
2025-07-03 23:11:12 +03:00
JESUSROYETH 1facc6b28b fix: correct export name xyxy_to_xyah -> xyxy_to_xcycarh in __all__ 2025-07-03 15:43:10 -04:00
pre-commit-ci[bot] 4414e15a00 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-07-01 23:19:27 +00:00
rafaelpadilla 49a8fd96d6 renaming iou_with_jaccard(...) to box_iou_batch_with_jaccard(...) and moving it to supervision.__init__.py 2025-07-01 23:18:47 +00:00
rafaelpadilla 0a55b3b3a2 making mean_average_precision.py have a lazy import of box_iou_batch_with_jaccard(...) 2025-07-01 23:17:48 +00:00
rafaelpadilla 5f9c55d9f3 keeping argument use_iscrowd in function load_coco_annotations(...) ; moving box_iou_batch_with_jaccard(...) to supervision/__init__.py 2025-07-01 23:16:27 +00:00
Onuralp SEZER 93c1b5f520 Merge pull request #1862 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2025-06-30 20:21:50 +03:00
pre-commit-ci[bot] 097614f2be chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/PyCQA/bandit: 1.8.4 → 1.8.5](https://github.com/PyCQA/bandit/compare/1.8.4...1.8.5)
- [github.com/astral-sh/ruff-pre-commit: v0.11.13 → v0.12.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.13...v0.12.1)
2025-06-30 17:21:08 +00:00
AI-Hao eb170bc393 ci:取消注释 GitHub App token 步骤
取消注释了 publish-docs.yml 文件中的 Create GitHub App token (mkdocs) 步骤。这个步骤现在将作为工作流程的一部分正常运行,不再被跳过。
2025-06-25 11:02:17 +08:00
AI-Hao d002858169 ci: 注释掉创建 GitHub App token 的步骤
暂时注释掉创建 GitHub App token 的步骤,可能是因为该步骤不再需要或出现了问题。这个修改不影响其他功能,只是减少了工作流中的一个步骤。
2025-06-25 10:56:50 +08:00
pre-commit-ci[bot] 646ac83bef fix(pre_commit): 🎨 auto format pre-commit hooks 2025-06-25 01:05:00 +00:00
Hao Sun 9a9324af9a Merge branch 'roboflow:develop' into develop 2025-06-25 09:04:37 +08:00
pre-commit-ci[bot] c2d0ed2365 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-06-18 21:23:04 +00:00
James 89ca7226b9 Merge branch 'develop' into new-theme 2025-06-18 17:20:33 -04:00
James 5313f479a4 fix kapa widget 2025-06-17 17:12:24 -04:00
James 568a94c0e1 fix kapa 2025-06-17 17:10:07 -04:00
James 463a54fb58 work on mobile styles 2025-06-17 16:08:45 -04:00
James d5f06eaf6f work on search styles 2025-06-17 14:55:56 -04:00
James 4255e63269 work on new theme 2025-06-17 09:34:23 -04:00
Onuralp SEZER 57975735b3 Merge pull request #1854 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2025-06-17 11:34:04 +03:00
pre-commit-ci[bot] 84d190a40c chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/PyCQA/bandit: 1.8.3 → 1.8.4](https://github.com/PyCQA/bandit/compare/1.8.3...1.8.4)
- [github.com/astral-sh/ruff-pre-commit: v0.11.11 → v0.11.13](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.11...v0.11.13)
2025-06-16 17:18:16 +00:00
rafaelpadilla fd3135145a renaming and reordering inputs of function to keep consistency with other functions 2025-06-01 17:21:45 +00:00
rafaelpadilla cff974555e 1) Simplifying the logic of use_iscrowd and compatibility with other datasets.
2) Making `get_coco_class_index_mapping` return the inversed mapping, to simplify its usage.
2025-06-01 15:52:01 +00:00
Onuralp SEZER 14b6d104df Merge pull request #1852 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2025-05-31 03:51:40 +03:00
pre-commit-ci[bot] d21ebc77cc chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.10 → v0.11.11](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.10...v0.11.11)
2025-05-26 17:24:02 +00:00
Onuralp SEZER 95f2b54cc8 Merge pull request #1848 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2025-05-20 23:14:03 +03:00
pre-commit-ci[bot] 9f67fb9f5e chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.9 → v0.11.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.9...v0.11.10)
2025-05-19 17:24:35 +00:00
Onuralp SEZER 3f99fde52d Merge pull request #1841 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2025-05-12 20:26:35 +03:00
pre-commit-ci[bot] bfe11b3f8e chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.8 → v0.11.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.8...v0.11.9)
2025-05-12 17:23:07 +00:00
pre-commit-ci[bot] 52980a6aa7 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-05-08 01:52:48 +00:00
rafaelpadilla 8f72e49080 Moving iou_with_jaccard and jaccard from mean_average_precision.py to detection/utils.py 2025-05-08 01:46:46 +00:00
rafaelpadilla 19c4e44e2c removing unnecessary flag 2025-05-07 22:10:15 +00:00
rafaelpadilla 790baa615f Removing spaces 2025-05-07 22:05:04 +00:00
rafaelpadilla bba43ecdf4 Updating docstring to reflect newly added arguments 2025-05-07 20:31:05 +00:00
rafaelpadilla 688d987765 replacing __str__ implementation with to_pycocotools_output 2025-05-07 20:28:39 +00:00
Onuralp SEZER 8b92485b6a Merge pull request #1837 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2025-05-06 12:55:01 +03:00
pre-commit-ci[bot] 2a3c9c9164 chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.7 → v0.11.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.7...v0.11.8)
2025-05-05 17:27:00 +00:00
Onuralp SEZER 5e3573cf84 Merge pull request #1835 from roboflow/dependabot/github_actions/develop/actions/create-github-app-token-2.0.6
⬆️ Bump actions/create-github-app-token from 2.0.2 to 2.0.6
2025-05-05 03:37:37 +03:00
dependabot[bot] 4031a7f3df ⬆️ Bump actions/create-github-app-token from 2.0.2 to 2.0.6
Bumps [actions/create-github-app-token](https://github.com/actions/create-github-app-token) from 2.0.2 to 2.0.6.
- [Release notes](https://github.com/actions/create-github-app-token/releases)
- [Commits](https://github.com/actions/create-github-app-token/compare/3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5...df432ceedc7162793a195dd1713ff69aefc7379e)

---
updated-dependencies:
- dependency-name: actions/create-github-app-token
  dependency-version: 2.0.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-05 00:07:03 +00:00
rafaelpadilla 15cd445108 Removing unnecessary comments and including references. 2025-04-28 23:28:26 +00:00
Onuralp SEZER ed3186c506 Merge pull request #1833 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2025-04-29 02:11:04 +03:00
rafaelpadilla 157ed3af06 fix data default value 2025-04-28 23:08:37 +00:00
rafaelpadilla 36a21717eb pre-commit (lint) fixes 2025-04-28 22:11:32 +00:00
rafaelpadilla 232916a8eb implementing mean average precision using COCO approach 2025-04-28 20:16:07 +00:00
rafaelpadilla 317cbd3fc4 preparing coco dataset with tags area and iscrowd 2025-04-28 20:12:06 +00:00
pre-commit-ci[bot] 1bfc67bed7 chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.11.6 → v0.11.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.6...v0.11.7)
2025-04-28 17:22:17 +00:00
Onuralp SEZER 1c795e4a9f ci(env): 🐞 astral-sh/setup-uv new parameter activate-environment: true added
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2025-04-28 16:14:16 +03:00
dependabot[bot] 087dc939b2 ⬆️ Bump astral-sh/setup-uv from 5.4.2 to 6.0.0
Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 5.4.2 to 6.0.0.
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86...c7f87aa956e4c323abf06d5dec078e358f6b4d04)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-28 01:00:17 +00:00
hidara2000 0c2aae6bd2 refactor(snap_boxes): enhance docstring and improve bounding box shifting logic 2025-04-25 09:48:47 +10:00
pre-commit-ci[bot] a6aa9b7d3b fix(pre_commit): 🎨 auto format pre-commit hooks 2025-04-24 23:33:17 +00:00
hidara2000 29695055d2 refactor(annotators): streamline label handling and improve box snapping logic 2025-04-25 09:32:54 +10:00
Piotr Skalski 1876019faf Merge branch 'develop' into develop 2025-04-23 09:13:08 +02:00
pre-commit-ci[bot] 95a2ecc49b fix(pre_commit): 🎨 auto format pre-commit hooks 2025-04-17 01:02:48 +00:00
hidara2000 5c35af99d0 fix long comment lines 2025-04-17 11:02:25 +10:00
pre-commit-ci[bot] c68218a07e fix(pre_commit): 🎨 auto format pre-commit hooks 2025-04-17 00:48:27 +00:00
hidara2000 0539ec9d1f feat(annotators): add max_line_length parameter and text wrapping functionality 2025-04-17 10:48:02 +10:00
hidara2000 c4dce2752d fix(annotators): add missing comma in annotate method signature 2025-04-15 22:09:51 +10:00
hidara2000andOnuralp SEZER 474c8bc478 Update supervision/annotators/core.py
Co-authored-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2025-04-15 21:58:31 +10:00
hidara2000andOnuralp SEZER a5c833b80f Update supervision/annotators/core.py
Co-authored-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2025-04-15 21:58:20 +10:00
hidara2000andOnuralp SEZER a1e43f8e93 Update supervision/annotators/core.py
Co-authored-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2025-04-15 21:58:06 +10:00
pre-commit-ci[bot] ca5596ceb2 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-04-15 08:30:30 +00:00
hidara2000 f2e900171e feat(annotators): enhance label annotators with frame boundary adjustments and new base class
- ensures labels are within frame
- May have a few overlaps at edges,in very busy frames, when smart_pos is enabled. but running smart_pos  first yields better results
2025-04-15 18:09:08 +10:00
SkalskiP f737782ae8 initial commit 2025-04-07 14:56:58 +02:00
Onuralp SEZER 1f692a043f Merge branch 'develop' into feature/gemini-object-detection 2025-03-22 21:33:56 +03:00
Onuralp SEZER 7f1e0ea36b Merge branch 'develop' into feature/gemini-object-detection 2025-02-24 06:37:26 +03:00
Onuralp SEZER 0c4a156662 feat: ✨ implement normalized_xyxy_to_absolute_xyxy function and integrate into Google Gemini processing 2025-02-19 18:14:17 +03:00
Onuralp SEZER 766467ab78 docs(refactor): ♻️ rename variables for clarity in detection module 2025-02-19 08:44:51 +03:00
Onuralp SEZER 0cec129cf0 style: 💄 improve code formatting and readability in various files 2025-02-19 08:29:41 +03:00
Onuralp SEZER 65b48f6b4a feat: ✨ add support for Google Gemini model in detection module
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2025-02-19 08:29:05 +03:00
SunHao-AI 39766f4c0a Merge branch 'roboflow:develop' into develop 2025-01-16 11:09:47 +08:00
pre-commit-ci[bot] 4679334d86 fix(pre_commit): 🎨 auto format pre-commit hooks 2025-01-09 12:22:20 +00:00
AI-Hao 8d29796810 Merge remote-tracking branch 'origin/develop' into develop 2025-01-09 20:03:57 +08:00
AI-Hao 9cd6549cdc feat(detection): Support for IOS Matching Metric
- Added the `match_metric` parameter to the `with_nms` and `with_nmm` methods in the `Detections` class.
- Modified the `box_non_max_suppression` and `box_non_max_merge` functions to support IOS calculation.
- Introduced the `mask_non_max_merge` function for handling non-maximum merging of masks.
- Updated the `box_iou_batch` and `mask_iou_batch` functions to support both IOU and IOS calculations.
- Enhanced the `InferenceSlicer` class to accommodate the IOS matching metric.

Notes
- IOS (Intersection over Smaller): A variation of IOU that compares intersection over the smaller area, useful in scenarios with varying object sizes.
- Refinements: Improved terminology for clarity and consistency, ensuring technical accuracy.
2025-01-09 20:03:31 +08:00
AI-Hao e4b5a8e0f9 ### feat(detection): Support for IOS Matching Metric**
- Added the `match_metric` parameter to the `with_nms` and `with_nmm` methods in the `Detections` class.
- Modified the `box_non_max_suppression` and `box_non_max_merge` functions to support IOS calculation.
- Introduced the `mask_non_max_merge` function for handling non-maximum merging of masks.
- Updated the `box_iou_batch` and `mask_iou_batch` functions to support both IOU and IOS calculations.
- Enhanced the `InferenceSlicer` class to accommodate the IOS matching metric.

### Notes
- IOS (Intersection over Smaller): A variation of IOU that compares intersection over the smaller area, useful in scenarios with varying object sizes.
- Refinements: Improved terminology for clarity and consistency, ensuring technical accuracy.
2025-01-09 20:00:32 +08:00
172 changed files with 16574 additions and 10789 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
# These owners will be the default owners for everything in
# the repo. They will be requested for review when someone
# opens a pull request.
* @SkalskiP @onuralpszr
* @SkalskiP @soumik12345
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Clear cache
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
console.log("Starting cache cleanup...")
+5 -3
View File
@@ -34,13 +34,15 @@ jobs:
fetch-depth: 0
- name: 🐍 Install uv and set Python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2
with:
python-version: ${{ matrix.python-version }}
activate-environment: true
- name: 🔑 Create GitHub App token (mkdocs)
id: mkdocs_token
uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
with:
app-id: ${{ secrets.MKDOCS_APP_ID }}
private-key: ${{ secrets.MKDOCS_PEM }}
@@ -49,7 +51,7 @@ jobs:
- name: 🏗️ Install dependencies
run: |
uv pip install -r pyproject.toml --extra docs
uv pip install -r pyproject.toml --group docs
# Install mkdocs-material-insiders using the GitHub App token
uv pip install "git+https://roboflow:${{ steps.mkdocs_token.outputs.token }}@github.com/roboflow/mkdocs-material-insiders.git@9.5.49-insiders-4.53.14#egg=mkdocs-material[imaging]"
+5 -3
View File
@@ -29,17 +29,19 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: 🐍 Install uv and set Python version ${{ matrix.python-version }}
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2
with:
python-version: ${{ matrix.python-version }}
activate-environment: true
- name: 🏗️ Build source and wheel distributions
run: |
uv pip install -r pyproject.toml --extra build
uv pip install -r pyproject.toml --group build
uv build
uv run twine check --strict dist/*
- name: 🚀 Publish to PyPi
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
attestations: true
+5 -3
View File
@@ -27,17 +27,19 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: 🐍 Install uv and set Python version ${{ matrix.python-version }}
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2
with:
python-version: ${{ matrix.python-version }}
activate-environment: true
- name: 🏗️ Build source and wheel distributions
run: |
uv pip install -r pyproject.toml --extra build
uv pip install -r pyproject.toml --group build
uv build
uv run twine check --strict dist/*
- name: 🚀 Publish to PyPi
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
attestations: true
+5 -3
View File
@@ -24,18 +24,20 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: 🐍 Install uv and set Python version ${{ matrix.python-version }}
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2
with:
python-version: ${{ matrix.python-version }}
activate-environment: true
- name: 🏗️ Build source and wheel distributions
run: |
uv pip install -r pyproject.toml --extra build
uv pip install -r pyproject.toml --group build
uv build
uv run twine check --strict dist/*
- name: 🚀 Publish to Test-PyPi
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
repository-url: https://test.pypi.org/legacy/
attestations: true
+3 -3
View File
@@ -24,14 +24,14 @@ jobs:
fetch-depth: 0
- name: 🐍 Install uv and set Python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2
with:
python-version: ${{ matrix.python-version }}
activate-environment: true
- name: 🏗️ Install dependencies
run: |
uv pip install -r pyproject.toml --extra docs
uv pip install -r pyproject.toml --group docs
- name: 🧪 Test Docs Build
run: uv run mkdocs build --verbose
+5 -3
View File
@@ -12,19 +12,21 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
runs-on: ${{ matrix.os }}
steps:
- name: 📥 Checkout the repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: 🐍 Install uv and set Python version ${{ matrix.python-version }}
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2
with:
python-version: ${{ matrix.python-version }}
activate-environment: true
- name: 🚀 Install Packages
run: uv pip install -r pyproject.toml --extra dev --extra docs --extra metrics
run: uv pip install -r pyproject.toml --group dev --group docs --extra metrics
- name: 🧪 Run the Import test
run: uv run python -c "import supervision; from supervision import assets; from supervision import metrics; print(supervision.__version__)"
+10 -2
View File
@@ -25,14 +25,14 @@ repos:
- id: mixed-line-ending
- repo: https://github.com/PyCQA/bandit
rev: '1.8.3'
rev: '1.8.6'
hooks:
- id: bandit
args: ["-c", "pyproject.toml"]
additional_dependencies: ["bandit[toml]"]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.6
rev: v0.12.5
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
@@ -53,3 +53,11 @@ repos:
- id: codespell
additional_dependencies:
- tomli
- repo: https://github.com/asottile/pyupgrade
rev: v3.20.0
hooks:
- id: pyupgrade
args: ["--py310-plus"]
additional_dependencies:
- tomli
+1 -1
View File
@@ -37,7 +37,7 @@
## 💻 install
Pip install the supervision package in a
[**Python>=3.8**](https://www.python.org/) environment.
[**Python>=3.9**](https://www.python.org/) environment.
```bash
pip install supervision
+261 -6
View File
@@ -1,4 +1,259 @@
# CHANGELOG
# Changelog
### 0.27.0 <small>Nov 16, 2025</small>
- Added [#2008](https://github.com/roboflow/supervision/pull/2008): [`sv.filter_segments_by_distance`](https://supervision.roboflow.com/0.27.0/detection/utils/masks/#supervision.detection.utils.masks.filter_segments_by_distance) to keep the largest connected component and nearby components within an absolute or relative distance threshold. Useful for cleaning segmentation predictions from models such as SAM, SAM2, YOLO segmentation, and RF-DETR segmentation.
- Added [#2006](https://github.com/roboflow/supervision/pull/2006): [`sv.xyxy_to_mask`](https://supervision.roboflow.com/0.27.0/detection/utils/converters/#supervision.detection.utils.converters.xyxy_to_mask) to convert bounding boxes into 2D boolean masks, where each mask corresponds to a single box.
- Added [#1943](https://github.com/roboflow/supervision/pull/1943): [`sv.tint_image`](https://supervision.roboflow.com/0.27.0/utils/image/#supervision.utils.image.tint_image) to apply a solid color overlay to an image at a given opacity. Works with both NumPy and PIL inputs.
- Added [#1943](https://github.com/roboflow/supervision/pull/1943): [`sv.grayscale_image`](https://supervision.roboflow.com/0.27.0/utils/image/#supervision.utils.image.tint_image) to convert an image to 3 channel grayscale for compatibility with color based drawing utilities.
- Added [#2014](https://github.com/roboflow/supervision/pull/2014): [`sv.get_image_resolution_wh`](https://supervision.roboflow.com/0.27.0/utils/image/#supervision.utils.image.get_image_resolution_wh) as a unified way to read image width and height from NumPy and PIL inputs.
- Added [#1912](https://github.com/roboflow/supervision/pull/1912): [`sv.edit_distance`](https://supervision.roboflow.com/0.27.0/detection/utils/vlms/#supervision.detection.utils.vlms.edit_distance) for Levenshtein distance between two strings. Supports insert, delete, and substitute operations.
- Added [#1912](https://github.com/roboflow/supervision/pull/1912): [`sv.fuzzy_match_index`](https://supervision.roboflow.com/0.27.0/detection/utils/vlms/#supervision.detection.utils.vlms.fuzzy_match_index) to find the first close match in a list using edit distance.
- Changed [#2015](https://github.com/roboflow/supervision/pull/2015): [`sv.Detections.from_vlm`](https://supervision.roboflow.com/0.27.0/detection/core/#supervision.detection.core.Detections.from_vlm) and legacy `from_lmm` now support Qwen3 VL via `vlm=sv.VLM.QWEN_3_VL`.
- Changed [#1884](https://github.com/roboflow/supervision/pull/1884): [`sv.Detections.from_vlm`](https://supervision.roboflow.com/0.27.0/detection/core/#supervision.detection.core.Detections.from_vlm) and legacy `from_lmm` now support DeepSeek VL 2 via `vlm=sv.VLM.DEEPSEEK_VL_2`.
- Changed [#2015](https://github.com/roboflow/supervision/pull/2015): [`sv.Detections.from_vlm`](https://supervision.roboflow.com/0.27.0/detection/core/#supervision.detection.core.Detections.from_vlm) now parses Qwen 2.5 VL outputs more robustly and handles incomplete or truncated JSON responses.
- Changed [#2014](https://github.com/roboflow/supervision/pull/2014): [`sv.InferenceSlicer`](https://supervision.roboflow.com/0.27.0/detection/tools/inference_slicer/#supervision.detection.tools.inference_slicer.InferenceSlicer) now uses a new offset generation logic that removes redundant tiles and aligns borders cleanly. This reduces the number of processed tiles and shortens inference time without hurting detection quality.
- Changed [#2016](https://github.com/roboflow/supervision/pull/2016): [`sv.Detections`](https://supervision.roboflow.com/0.27.0/detection/core/#supervision.detection.core.Detections) now includes a `box_aspect_ratio` property for vectorized aspect ratio computation, useful for filtering detections based on box shape.
- Changed [#2001](https://github.com/roboflow/supervision/pull/2001): Significantly improved the performance of [`sv.box_iou_batch`](https://supervision.roboflow.com/0.27.0/detection/utils/iou_and_nms/#supervision.detection.utils.iou_and_nms.box_iou_batch). On internal benchmarks, processing runs approximately 2x to 5x faster.
- Changed [#1997](https://github.com/roboflow/supervision/pull/1997): [`sv.process_video`](https://supervision.roboflow.com/0.27.0/utils/video/#supervision.utils.video.process_video) now uses a threaded reader, processor, and writer pipeline. This removes I/O stalls and improves throughput while keeping the callback single threaded and safe for stateful models.
- Changed: [`sv.denormalize_boxes`](https://supervision.roboflow.com/0.27.0/detection/utils/boxes/#supervision.detection.utils.boxes.denormalize_boxes) now supports batch conversion of bounding boxes. The function accepts arrays of shape `(N, 4)` and returns a batch of absolute pixel coordinates.
- Changed [#1917](https://github.com/roboflow/supervision/pull/1917): [`sv.LabelAnnotator`](https://supervision.roboflow.com/develop/0.27.0/annotators/#supervision.annotators.core.LabelAnnotator) and [`sv.RichLabelAnnotator`](https://supervision.roboflow.com/develop/0.27.0/annotators/#supervision.annotators.core.LabelAnnotator) now accept `text_offset=(x, y)` to shift the label relative to `text_position`. Works with smart label position and line wrapping.
!!! failure "Removed"
Removed the deprecated `overlap_ratio_wh` argument from `sv.InferenceSlicer`. Use the pixel based `overlap_wh` argument to control slice overlap.
!!! info "Tip"
Convert your old ratio based overlap to pixel based overlap by multiplying each ratio by the slice dimensions.
```python
# before
slice_wh = (640, 640)
overlap_ratio_wh = (0.25, 0.25)
slicer = sv.InferenceSlicer(
callback=callback,
slice_wh=slice_wh,
overlap_ratio_wh=overlap_ratio_wh,
overlap_filter=sv.OverlapFilter.NON_MAX_SUPPRESSION,
)
# after
overlap_wh = (
int(overlap_ratio_wh[0] * slice_wh[0]),
int(overlap_ratio_wh[1] * slice_wh[1]),
)
slicer = sv.InferenceSlicer(
callback=callback,
slice_wh=slice_wh,
overlap_wh=overlap_wh,
overlap_filter=sv.OverlapFilter.NON_MAX_SUPPRESSION,
)
```
### 0.26.1 <small>Jul 22, 2025</small>
- Fixed [1894](https://github.com/roboflow/supervision/pull/1894): Error in [`sv.MeanAveragePrecision`](https://supervision.roboflow.com/0.26.1/metrics/mean_average_precision/#supervision.metrics.mean_average_precision.MeanAveragePrecision) where the area used for size-specific evaluation (small / medium / large) was always zero unless explicitly provided in `sv.Detections.data`.
- Fixed [1895](https://github.com/roboflow/supervision/pull/1895): `ID=0` bug in [`sv.MeanAveragePrecision`](https://supervision.roboflow.com/0.26.1/metrics/mean_average_precision/#supervision.metrics.mean_average_precision.MeanAveragePrecision) where objects were getting `0.0` mAP despite perfect IoU matches due to a bug in annotation ID assignment.
- Fixed [1898](https://github.com/roboflow/supervision/pull/1898): Issue where [`sv.MeanAveragePrecision`](https://supervision.roboflow.com/0.26.1/metrics/mean_average_precision/#supervision.metrics.mean_average_precision.MeanAveragePrecision) could return negative values when certain object size categories have no data.
- Fixed [1901](https://github.com/roboflow/supervision/pull/1901): `match_metric` support for [`sv.Detections.with_nms`](https://supervision.roboflow.com/0.26.1/metrics/mean_average_precision/#supervision.detection.core.Detections.with_nms).
- Fixed [1906](https://github.com/roboflow/supervision/pull/1906): `border_thickness` parameter usage for [`sv.PercentageBarAnnotator`](https://supervision.roboflow.com/0.26.1/metrics/mean_average_precision/#supervision.annotators.core.PercentageBarAnnotator).
### 0.26.0 <small>Jul 16, 2025</small>
!!! failure "Removed"
`supervision-0.26.0` drops `python3.8` support and upgrade all codes to `python3.9` syntax style.
!!! info "Tip"
Supervision’s documentation theme now has a fresh look that is consistent with the documentations of all Roboflow open-source projects. ([#1858](https://github.com/roboflow/supervision/pull/1858))
- Added [#1774](https://github.com/roboflow/supervision/pull/1774): Support for the IOS (Intersection over Smallest) overlap metric that measures how much of the smaller object is covered by the larger one in [`sv.Detections.with_nms`](https://supervision.roboflow.com/0.26.0/detection/core/#supervision.detection.core.Detections.with_nms), [`sv.Detections.with_nmm`](https://supervision.roboflow.com/0.26.0/detection/core/#supervision.detection.core.Detections.with_nmm), [`sv.box_iou_batch`](https://supervision.roboflow.com/0.26.0/detection/utils/iou_and_nms/#supervision.detection.utils.iou_and_nms.box_iou_batch), and [`sv.mask_iou_batch`](https://supervision.roboflow.com/0.26.0/detection/utils/iou_and_nms/#supervision.detection.utils.iou_and_nms.mask_iou_batch).
```python
import numpy as np
import supervision as sv
boxes_true = np.array([
[100, 100, 200, 200],
[300, 300, 400, 400]
])
boxes_detection = np.array([
[150, 150, 250, 250],
[320, 320, 420, 420]
])
sv.box_iou_batch(
boxes_true=boxes_true,
boxes_detection=boxes_detection,
overlap_metric=sv.OverlapMetric.IOU
)
# array([[0.14285714, 0. ],
# [0. , 0.47058824]])
sv.box_iou_batch(
boxes_true=boxes_true,
boxes_detection=boxes_detection,
overlap_metric=sv.OverlapMetric.IOS
)
# array([[0.25, 0. ],
# [0. , 0.64]])
```
- Added [#1874](https://github.com/roboflow/supervision/pull/1874): [`sv.box_iou`](https://supervision.roboflow.com/0.26.0/detection/utils/iou_and_nms/#supervision.detection.utils.iou_and_nms.box_iou) that efficiently computes the Intersection over Union (IoU) between two individual bounding boxes.
- Added [#1816](https://github.com/roboflow/supervision/pull/1816): Support for frame limitations and progress bar in [`sv.process_video`](https://supervision.roboflow.com/0.26.0/utils/video/#supervision.utils.video.process_video).
- Added [#1788](https://github.com/roboflow/supervision/pull/1788): Support for creating [`sv.KeyPoints`](https://supervision.roboflow.com/0.26.0/keypoint/core/#supervision.keypoint.core.KeyPoints) objects from [ViTPose](https://huggingface.co/docs/transformers/en/model_doc/vitpose) and [ViTPose++](https://huggingface.co/docs/transformers/en/model_doc/vitpose#vitpose-models) inference results via [`sv.KeyPoints.from_transformers`](https://supervision.roboflow.com/0.26.0/keypoint/core/#supervision.keypoint.core.KeyPoints.from_transformers).
- Added [#1823](https://github.com/roboflow/supervision/pull/1823): [`sv.xyxy_to_xcycarh`](https://supervision.roboflow.com/0.26.0/detection/utils/converters/#supervision.detection.utils.converters.xyxy_to_xcycarh) function to convert bounding box coordinates from `(x_min, y_min, x_max, y_max)` into measurement space to format `(center x, center y, aspect ratio, height)`, where the aspect ratio is `width / height`.
- Added [#1788](https://github.com/roboflow/supervision/pull/1788): [`sv.xyxy_to_xywh`](https://supervision.roboflow.com/0.26.0/detection/utils/converters/#supervision.detection.utils.converters.xyxy_to_xywh) function to convert bounding box coordinates from `(x_min, y_min, x_max, y_max)` format to `(x, y, width, height)` format.
- Changed [#1820](https://github.com/roboflow/supervision/pull/1820): [`sv.LabelAnnotator`](https://supervision.roboflow.com/0.26.0/detection/annotators/#supervision.annotators.core.LabelAnnotator) now supports the `smart_position` parameter to automatically keep labels within frame boundaries, and the `max_line_length` parameter to control text wrapping for long or multi-line labels.
- Changed [#1825](https://github.com/roboflow/supervision/pull/1825): [`sv.LabelAnnotator`](https://supervision.roboflow.com/0.26.0/detection/annotators/#supervision.annotators.core.LabelAnnotator) now supports non-string labels.
- Changed [#1792](https://github.com/roboflow/supervision/pull/1792): [`sv.Detections.from_vlm`](https://supervision.roboflow.com/0.26.0/detection/core/#supervision.detection.core.Detections.from_vlm) now supports parsing bounding boxes and segmentation masks from responses generated by [Google Gemini models](https://ai.google.dev/gemini-api/docs/vision).
```python
import supervision as sv
gemini_response_text = """```json
[
{"box_2d": [543, 40, 728, 200], "label": "cat", "id": 1},
{"box_2d": [653, 352, 820, 522], "label": "dog", "id": 2}
]
```"""
detections = sv.Detections.from_vlm(
sv.VLM.GOOGLE_GEMINI_2_5,
gemini_response_text,
resolution_wh=(1000, 1000),
classes=['cat', 'dog'],
)
detections.xyxy
# array([[543., 40., 728., 200.], [653., 352., 820., 522.]])
detections.data
# {'class_name': array(['cat', 'dog'], dtype='<U26')}
detections.class_id
# array([0, 1])
```
- Changed [#1878](https://github.com/roboflow/supervision/pull/1878): [`sv.Detections.from_vlm`](https://supervision.roboflow.com/0.26.0/detection/core/#supervision.detection.core.Detections.from_vlm) now supports parsing bounding boxes from responses generated by [Moondream](https://github.com/vikhyat/moondream).
```python
import supervision as sv
moondream_result = {
'objects': [
{
'x_min': 0.5704046934843063,
'y_min': 0.20069346576929092,
'x_max': 0.7049859315156937,
'y_max': 0.3012596592307091
},
{
'x_min': 0.6210969910025597,
'y_min': 0.3300672620534897,
'x_max': 0.8417936339974403,
'y_max': 0.4961046129465103
}
]
}
detections = sv.Detections.from_vlm(
sv.VLM.MOONDREAM,
moondream_result,
resolution_wh=(1000, 1000),
)
detections.xyxy
# array([[1752.28, 818.82, 2165.72, 1229.14],
# [1908.01, 1346.67, 2585.99, 2024.11]])
```
- Changed [#1709](https://github.com/roboflow/supervision/pull/1790): [`sv.Detections.from_vlm`](https://supervision.roboflow.com/0.26.0/detection/core/#supervision.detection.core.Detections.from_vlm) now supports parsing bounding boxes from responses generated by [Qwen-2.5 VL](https://github.com/QwenLM/Qwen2.5-VL).
```python
import supervision as sv
qwen_2_5_vl_result = """```json
[
{"bbox_2d": [139, 768, 315, 954], "label": "cat"},
{"bbox_2d": [366, 679, 536, 849], "label": "dog"}
]
```"""
detections = sv.Detections.from_vlm(
sv.VLM.QWEN_2_5_VL,
qwen_2_5_vl_result,
input_wh=(1000, 1000),
resolution_wh=(1000, 1000),
classes=['cat', 'dog'],
)
detections.xyxy
# array([[139., 768., 315., 954.], [366., 679., 536., 849.]])
detections.class_id
# array([0, 1])
detections.data
# {'class_name': array(['cat', 'dog'], dtype='<U10')}
detections.class_id
# array([0, 1])
```
- Changed [#1786](https://github.com/roboflow/supervision/pull/1786): Significantly improved the speed of HSV color mapping in [`sv.HeatMapAnnotator`](https://supervision.roboflow.com/0.26.0/detection/annotators/#supervision.annotators.core.HeatMapAnnotator), achieving approximately 28x faster performance on 1920x1080 frames.
- Fixed [#1834](https://github.com/roboflow/supervision/pull/1834): Supervision’s [`sv.MeanAveragePrecision`](https://supervision.roboflow.com/0.26.0/metrics/mean_average_precision/#supervision.metrics.mean_average_precision.MeanAveragePrecision) is now fully aligned with [pycocotools](https://github.com/ppwwyyxx/cocoapi), the official COCO evaluation tool, ensuring accurate and standardized metrics. This update enabled us to launch a new version of the [Computer Vision Model Leaderboard](https://leaderboard.roboflow.com/).
```python
import supervision as sv
from supervision.metrics import MeanAveragePrecision
predictions = sv.Detections(...)
targets = sv.Detections(...)
map_metric = MeanAveragePrecision()
map_metric.update(predictions, targets).compute()
# Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.464
# Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.637
# Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.203
# Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.284
# Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.497
# Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.629
```
- Fixed [#1767](https://github.com/roboflow/supervision/pull/1767): Fixed losing `sv.Detections.data` when detections filtering.
### 0.25.0 <small>Nov 12, 2024</small>
@@ -382,9 +637,9 @@ detections = sv.Detections.from_sam(sam_result=sam_result)
- Changed [#1434](https://github.com/roboflow/supervision/pull/1434): [`InferenceSlicer`](https://supervision.roboflow.com/0.23.0/detection/tools/inference_slicer/) now features an `overlap_wh` parameter, making it easier to compute slice sizes when handling overlapping slices.
- Fix [#1448](https://github.com/roboflow/supervision/pull/1448): Various annotator type issues have been resolved, supporting expanded error handling.
- Fixed [#1448](https://github.com/roboflow/supervision/pull/1448): Various annotator type issues have been resolved, supporting expanded error handling.
- Fix [#1348](https://github.com/roboflow/supervision/pull/1348): Introduced a new method for [seeking to a specific video frame](https://supervision.roboflow.com/0.23.0/utils/video/#supervision.utils.video.get_video_frames_generator), addressing cases where traditional seek methods were failing. It can be enabled with `iterative_seek=True`.
- Fixed [#1348](https://github.com/roboflow/supervision/pull/1348): Introduced a new method for [seeking to a specific video frame](https://supervision.roboflow.com/0.23.0/utils/video/#supervision.utils.video.get_video_frames_generator), addressing cases where traditional seek methods were failing. It can be enabled with `iterative_seek=True`.
```python
import supervision as sv
@@ -397,7 +652,7 @@ for frame in sv.get_video_frames_generator(
...
```
- Fix [#1424](https://github.com/roboflow/supervision/pull/1424): `plot_image` function now clearly indicates that the size is in inches.
- Fixed [#1424](https://github.com/roboflow/supervision/pull/1424): `plot_image` function now clearly indicates that the size is in inches.
!!! failure "Removed"
@@ -1110,7 +1365,7 @@ array([
### 0.11.1 <small>June 29, 2023</small>
- Fix [#165](https://github.com/roboflow/supervision/pull/165): [`as_folder_structure`](/0.11.1/dataset/core/#supervision.dataset.core.ClassificationDataset.as_folder_structure) fails to save [`sv.ClassificationDataset`](/0.11.1/dataset/core/#classificationdataset) when it is result of inference.
- Fixed [#165](https://github.com/roboflow/supervision/pull/165): [`as_folder_structure`](/0.11.1/dataset/core/#supervision.dataset.core.ClassificationDataset.as_folder_structure) fails to save [`sv.ClassificationDataset`](/0.11.1/dataset/core/#classificationdataset) when it is result of inference.
### 0.11.0 <small>June 28, 2023</small>
@@ -1156,7 +1411,7 @@ array([
- Added [#162](https://github.com/roboflow/supervision/pull/162): additional `start` and `end` arguments to [`sv.get_video_frames_generator`](/0.11.0/utils/video/#get_video_frames_generator) allowing to generate frames only for a selected part of the video.
- Fix [#157](https://github.com/roboflow/supervision/pull/157): incorrect loading of YOLO dataset class names from `data.yaml`.
- Fixed [#157](https://github.com/roboflow/supervision/pull/157): incorrect loading of YOLO dataset class names from `data.yaml`.
### 0.10.0 <small>June 14, 2023</small>
+15 -17
View File
@@ -7,21 +7,19 @@ status: deprecated
These features are phased out due to better alternatives or potential issues in future versions. Deprecated functionalities are supported for **five subsequent releases**, providing time for users to transition to updated methods.
- Constructing [`DetectionDataset`](https://supervision.roboflow.com/latest/datasets/core/#supervision.dataset.core.DetectionDataset) and [`ClassificationDataset`](https://supervision.roboflow.com/latest/datasets/core/#supervision.dataset.core.ClassificationDataset) with parameter `images` as `Dict[str, np.ndarray]` will be removed in `supervision-0.26.0`. Please pass a list of paths `List[str]` instead.
- The `DetectionDataset.images` property will be removed in `supervision-0.26.0`. Please loop over images with `for path, image, annotation in dataset:`, as that does not require loading all images into memory.
- `BoundingBoxAnnotator` has been renamed to `BoxAnnotator` after the old implementation of [`BoxAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator) has been removed. `BoundingBoxAnnotator` will be removed in `supervision-0.26.0`.
- `overlap_filter_strategy` in [`InferenceSlicer.__init__`](https://supervision.roboflow.com/latest/detection/tools/inference_slicer/) is deprecated and will be removed in `supervision-0.27.0`. Use `overlap_strategy` instead.
- `overlap_ratio_wh` in [`InferenceSlicer.__init__`](https://supervision.roboflow.com/latest/detection/tools/inference_slicer/) is deprecated and will be removed in `supervision-0.27.0`. Use `overlap_wh` instead.
- `sv.LMM` enum is deprecated and will be removed in `supervision-0.31.0`. Use `sv.VLM` instead.
- [`sv.Detections.from_lmm`](https://supervision.roboflow.com/0.26.0/detection/core/#supervision.detection.core.Detections.from_lmm) property is deprecated and will be removed in `supervision-0.31.0`. Use [`sv.Detections.from_vlm`](https://supervision.roboflow.com/0.26.0/detection/core/#supervision.detection.core.Detections.from_vlm) instead.
# Removed
### 0.25.0
### 0.26.0
- The `sv.DetectionDataset.images` property has been removed in `supervision-0.26.0`. Please loop over images with `for path, image, annotation in dataset:`, as that does not require loading all images into memory. Also, constructing `sv.DetectionDataset` with parameter `images` as `Dict[str, np.ndarray]` is deprecated and has been removed in `supervision-0.26.0`. Please pass a list of paths `List[str]` instead.
- The name `sv.BoundingBoxAnnotator` is deprecated and has been removed in `supervision-0.26.0`. It has been renamed to [`sv.BoxAnnotator`](https://supervision.roboflow.com/0.22.0/detection/annotators/#supervision.annotators.core.BoxAnnotator).
No removals in this version!
### 0.24.0
@@ -35,12 +33,12 @@ No removals in this version!
### 0.22.0
- `Detections.from_roboflow` is removed as of `supervision-0.22.0`. Use [`Detections.from_inference`](detection/core.md/#supervision.detection.core.Detections.from_inference) instead.
- The method `Color.white()` was removed as of `supervision-0.22.0`. Use the constant `Color.WHITE` instead.
- The method `Color.black()` was removed as of `supervision-0.22.0`. Use the constant `Color.BLACK` instead.
- The method `Color.red()` was removed as of `supervision-0.22.0`. Use the constant `Color.RED` instead.
- The method `Color.green()` was removed as of `supervision-0.22.0`. Use the constant `Color.GREEN` instead.
- The method `Color.blue()` was removed as of `supervision-0.22.0`. Use the constant `Color.BLUE` instead.
- The method `ColorPalette.default()` was removed as of `supervision-0.22.0`. Use the constant [`ColorPalette.DEFAULT`](/utils/draw/#supervision.draw.color.ColorPalette.DEFAULT) instead.
- `BoxAnnotator` was removed as of `supervision-0.22.0`, however `BoundingBoxAnnotator` was immediately renamed to `BoxAnnotator`. Use [`BoxAnnotator`](detection/annotators.md/#supervision.annotators.core.BoxAnnotator) and [`LabelAnnotator`](detection/annotators.md/#supervision.annotators.core.LabelAnnotator) instead of the old `BoxAnnotator`.
- The method `FPSMonitor.__call__` was removed as of `supervision-0.22.0`. Use the attribute [`FPSMonitor.fps`](utils/video.md/#supervision.utils.video.FPSMonitor.fps) instead.
- `sv.Detections.from_roboflow` is removed as of `supervision-0.22.0`. Use [`Detections.from_inference`](detection/core.md/#supervision.detection.core.Detections.from_inference) instead.
- The method `sv.Color.white()` was removed as of `supervision-0.22.0`. Use the constant `sv.Color.WHITE` instead.
- The method `sv.Color.black()` was removed as of `supervision-0.22.0`. Use the constant `sv.Color.BLACK` instead.
- The method `sv.Color.red()` was removed as of `supervision-0.22.0`. Use the constant `sv.Color.RED` instead.
- The method `sv.Color.green()` was removed as of `supervision-0.22.0`. Use the constant `sv.Color.GREEN` instead.
- The method `sv.Color.blue()` was removed as of `supervision-0.22.0`. Use the constant `sv.Color.BLUE` instead.
- The method `sv.ColorPalette.default()` was removed as of `supervision-0.22.0`. Use the constant [`ColorPalette.DEFAULT`](/utils/draw/#supervision.draw.color.ColorPalette.DEFAULT) instead.
- `sv.BoxAnnotator` was removed as of `supervision-0.22.0`, however `sv.BoundingBoxAnnotator` was immediately renamed to `sv.BoxAnnotator`. Use [`BoxAnnotator`](detection/annotators.md/#supervision.annotators.core.BoxAnnotator) and [`LabelAnnotator`](detection/annotators.md/#supervision.annotators.core.LabelAnnotator) instead of the old `sv.BoxAnnotator`.
- The method `sv.FPSMonitor.__call__` was removed as of `supervision-0.22.0`. Use the attribute [`sv.FPSMonitor.fps`](utils/video.md/#supervision.utils.video.FPSMonitor.fps) instead.
+14 -17
View File
@@ -1,6 +1,5 @@
---
comments: true
status: new
---
# Annotators
@@ -28,28 +27,26 @@ Annotators accept detections and apply box or mask visualizations to the detecti
</div>
<!--
=== "RoundBox"
=== "RoundBox"
```python
import supervision as sv
```python
import supervision as sv
image = ...
detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
round_box_annotator = sv.RoundBoxAnnotator()
annotated_frame = round_box_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
round_box_annotator = sv.RoundBoxAnnotator()
annotated_frame = round_box_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
<div class="result" markdown>
<div class="result" markdown>
![round-box-annotator-example](https://media.roboflow.com/supervision-annotator-examples/round-box-annotator-example-purple.png){ align=center width="800" }
![round-box-annotator-example](https://media.roboflow.com/supervision-annotator-examples/round-box-annotator-example-purple.png){ align=center width="800" }
</div>
-->
</div>
=== "BoxCorner"
-1
View File
@@ -1,6 +1,5 @@
---
comments: true
status: new
---
# Detections
-29
View File
@@ -1,29 +0,0 @@
---
comments: true
---
# Double Detection Filter
<div class="md-typeset">
<h2><a href="#supervision.detection.overlap_filter.OverlapFilter">OverlapFilter</a></h2>
</div>
:::supervision.detection.overlap_filter.OverlapFilter
<div class="md-typeset">
<h2><a href="#supervision.detection.overlap_filter.box_non_max_suppression">box_non_max_suppression</a></h2>
</div>
:::supervision.detection.overlap_filter.box_non_max_suppression
<div class="md-typeset">
<h2><a href="#supervision.detection.overlap_filter.mask_non_max_suppression">mask_non_max_suppression</a></h2>
</div>
:::supervision.detection.overlap_filter.mask_non_max_suppression
<div class="md-typeset">
<h2><a href="#supervision.detection.overlap_filter.box_non_max_merge">box_non_max_merge</a></h2>
</div>
:::supervision.detection.overlap_filter.box_non_max_merge
-1
View File
@@ -1,6 +1,5 @@
---
comments: true
status: new
---
<div class="md-typeset">
-120
View File
@@ -1,120 +0,0 @@
---
comments: true
status: new
---
# Detection Utils
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.box_iou_batch">box_iou_batch</a></h2>
</div>
:::supervision.detection.utils.box_iou_batch
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.mask_iou_batch">mask_iou_batch</a></h2>
</div>
:::supervision.detection.utils.mask_iou_batch
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.oriented_box_iou_batch">oriented_box_iou_batch</a></h2>
</div>
:::supervision.detection.utils.oriented_box_iou_batch
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.polygon_to_mask">polygon_to_mask</a></h2>
</div>
:::supervision.detection.utils.polygon_to_mask
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.mask_to_xyxy">mask_to_xyxy</a></h2>
</div>
:::supervision.detection.utils.mask_to_xyxy
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.mask_to_polygons">mask_to_polygons</a></h2>
</div>
:::supervision.detection.utils.mask_to_polygons
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.polygon_to_xyxy">polygon_to_xyxy</a></h2>
</div>
:::supervision.detection.utils.polygon_to_xyxy
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.filter_polygons_by_area">filter_polygons_by_area</a></h2>
</div>
:::supervision.detection.utils.filter_polygons_by_area
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.move_boxes">move_boxes</a></h2>
</div>
:::supervision.detection.utils.move_boxes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.move_masks">move_masks</a></h2>
</div>
:::supervision.detection.utils.move_masks
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.scale_boxes">scale_boxes</a></h2>
</div>
:::supervision.detection.utils.scale_boxes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.clip_boxes">clip_boxes</a></h2>
</div>
:::supervision.detection.utils.clip_boxes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.pad_boxes">pad_boxes</a></h2>
</div>
:::supervision.detection.utils.pad_boxes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.xywh_to_xyxy">xywh_to_xyxy</a></h2>
</div>
:::supervision.detection.utils.xywh_to_xyxy
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.xyxy_to_xywh">xyxy_to_xywh</a></h2>
</div>
:::supervision.detection.utils.xyxy_to_xywh
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.xyxy_to_xcycarh">xyxy_to_xcycarh</a></h2>
</div>
:::supervision.detection.utils.xyxy_to_xcycarh
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.xcycwh_to_xyxy">xcycwh_to_xyxy</a></h2>
</div>
:::supervision.detection.utils.xcycwh_to_xyxy
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.contains_holes">contains_holes</a></h2>
</div>
:::supervision.detection.utils.contains_holes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.contains_multiple_segments">contains_multiple_segments</a></h2>
</div>
:::supervision.detection.utils.contains_multiple_segments
+35
View File
@@ -0,0 +1,35 @@
---
comments: true
---
# Boxes Utils
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.boxes.move_boxes">move_boxes</a></h2>
</div>
:::supervision.detection.utils.boxes.move_boxes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.boxes.scale_boxes">scale_boxes</a></h2>
</div>
:::supervision.detection.utils.boxes.scale_boxes
<div class="md-typeset">
<h2><a href="#supervision.detection.boxes.utils.clip_boxes">clip_boxes</a></h2>
</div>
:::supervision.detection.utils.boxes.clip_boxes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.boxes.pad_boxes">pad_boxes</a></h2>
</div>
:::supervision.detection.utils.boxes.pad_boxes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.boxes.denormalize_boxes">denormalize_boxes</a></h2>
</div>
:::supervision.detection.utils.boxes.denormalize_boxes
+66
View File
@@ -0,0 +1,66 @@
---
comments: true
status: new
---
# Converters Utils
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.converters.xyxy_to_xywh">xyxy_to_xywh</a></h2>
</div>
:::supervision.detection.utils.converters.xyxy_to_xywh
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.converters.xywh_to_xyxy">xywh_to_xyxy</a></h2>
</div>
:::supervision.detection.utils.converters.xywh_to_xyxy
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.converters.xyxy_to_xcycarh">xyxy_to_xcycarh</a></h2>
</div>
:::supervision.detection.utils.converters.xyxy_to_xcycarh
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.converters.xcycwh_to_xyxy">xcycwh_to_xyxy</a></h2>
</div>
:::supervision.detection.utils.converters.xcycwh_to_xyxy
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.converters.xyxy_to_polygons">xyxy_to_polygons</a></h2>
</div>
:::supervision.detection.utils.converters.xyxy_to_polygons
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.converters.mask_to_xyxy">mask_to_xyxy</a></h2>
</div>
:::supervision.detection.utils.converters.mask_to_xyxy
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.converters.mask_to_polygons">mask_to_polygons</a></h2>
</div>
:::supervision.detection.utils.converters.mask_to_polygons
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.converters.polygon_to_mask">polygon_to_mask</a></h2>
</div>
:::supervision.detection.utils.converters.polygon_to_mask
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.converters.polygon_to_xyxy">polygon_to_xyxy</a></h2>
</div>
:::supervision.detection.utils.converters.polygon_to_xyxy
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.converters.xyxy_to_mask">xyxy_to_mask</a></h2>
</div>
:::supervision.detection.utils.converters.xyxy_to_mask
+71
View File
@@ -0,0 +1,71 @@
---
comments: true
---
# IoU and NMS Utils
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.OverlapFilter">OverlapFilter</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.OverlapFilter
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.OverlapMetric">OverlapMetric</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.OverlapMetric
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.utils.box_iou">box_iou</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.box_iou
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.box_iou_batch">box_iou_batch</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.box_iou_batch
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.box_iou_batch_with_jaccard">box_iou_batch_with_jaccard</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.box_iou_batch_with_jaccard
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.mask_iou_batch">mask_iou_batch</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.mask_iou_batch
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.oriented_box_iou_batch">oriented_box_iou_batch</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.oriented_box_iou_batch
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.box_non_max_suppression">box_non_max_suppression</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.box_non_max_suppression
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.mask_non_max_suppression">mask_non_max_suppression</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.mask_non_max_suppression
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.box_non_max_merge">box_non_max_merge</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.box_non_max_merge
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.mask_non_max_merge">mask_non_max_merge</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.mask_non_max_merge
+30
View File
@@ -0,0 +1,30 @@
---
comments: true
status: new
---
# Masks Utils
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.masks.move_masks">move_masks</a></h2>
</div>
:::supervision.detection.utils.masks.move_masks
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.masks.contains_holes">contains_holes</a></h2>
</div>
:::supervision.detection.utils.masks.contains_holes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.masks.contains_multiple_segments">contains_multiple_segments</a></h2>
</div>
:::supervision.detection.utils.masks.contains_multiple_segments
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.masks.filter_segments_by_distance">filter_segments_by_distance</a></h2>
</div>
:::supervision.detection.utils.masks.filter_segments_by_distance
+17
View File
@@ -0,0 +1,17 @@
---
comments: true
---
# Polygons Utils
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.polygons.filter_polygons_by_area">filter_polygons_by_area</a></h2>
</div>
:::supervision.detection.utils.polygons.filter_polygons_by_area
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.polygons.approximate_polygon">approximate_polygon</a></h2>
</div>
:::supervision.detection.utils.polygons.approximate_polygon
+18
View File
@@ -0,0 +1,18 @@
---
comments: true
status: new
---
# VLMs Utils
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.vlms.edit_distance">edit_distance</a></h2>
</div>
:::supervision.detection.utils.vlms.edit_distance
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.vlms.fuzzy_match_index">fuzzy_match_index</a></h2>
</div>
:::supervision.detection.utils.vlms.fuzzy_match_index
-1
View File
@@ -1,6 +1,5 @@
---
comments: true
status: new
---
![Corgi Example](https://media.roboflow.com/supervision/image-examples/how-to/benchmark-models/corgi-sorted-2.png)
+2 -5
View File
@@ -331,12 +331,9 @@ for i in range(16):
annotated_image = label_annotator.annotate(annotated_image, annotations, labels)
annotated_images.append(annotated_image)
grid = sv.create_tiles(
sv.plot_images_grid(
annotated_images,
grid_size=(4, 4),
single_tile_size=(400, 400),
tile_padding_color=sv.Color.WHITE,
tile_margin_color=sv.Color.WHITE
)
```
@@ -398,7 +395,7 @@ increase the size and diversity of training datasets, leading to improved model
performance and generalization.
```bash
pip install augmentation
pip install albumentations
```
Albumentations provides a flexible and powerful API for image augmentation. The core of
+3 -3
View File
@@ -234,7 +234,7 @@ with
model = get_model(model_id="yolov8n-640")
frames_generator = sv.get_video_frames_generator(<SOURCE_VIDEO_PATH>)
with sv.JSONSink(<TARGET_CSV_PATH>) as sink:
with sv.JSONSink(<TARGET_JSON_PATH>) as sink:
for frame_index, frame in enumerate(frames_generator):
results = model.infer(image)[0]
@@ -250,7 +250,7 @@ with
model = YOLO("yolov8n.pt")
frames_generator = sv.get_video_frames_generator(<SOURCE_VIDEO_PATH>)
with sv.JSONSink(<TARGET_CSV_PATH>) as sink:
with sv.JSONSink(<TARGET_JSON_PATH>) as sink:
for frame_index, frame in enumerate(frames_generator):
results = model(frame)[0]
@@ -268,7 +268,7 @@ with
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
frames_generator = sv.get_video_frames_generator(<SOURCE_VIDEO_PATH>)
with sv.JSONSink(<TARGET_CSV_PATH>) as sink:
with sv.JSONSink(<TARGET_JSON_PATH>) as sink:
for frame_index, frame in enumerate(frames_generator):
frame = sv.cv2_to_pillow(frame)
+14 -15
View File
@@ -1,6 +1,5 @@
---
comments: true
status: new
---
# Track Objects
@@ -177,9 +176,9 @@ offering a clear visual representation of each object's class and unique identif
detections = tracker.update_with_detections(detections)
labels = [
f"#{tracker_id} {results.names[class_id]}"
for class_id, tracker_id
in zip(detections.class_id, detections.tracker_id)
f"#{tracker_id} {class_name}"
for class_name, tracker_id
in zip(detections.data["class_name"], detections.tracker_id)
]
annotated_frame = box_annotator.annotate(
@@ -212,9 +211,9 @@ offering a clear visual representation of each object's class and unique identif
detections = tracker.update_with_detections(detections)
labels = [
f"#{tracker_id} {results.names[class_id]}"
for class_id, tracker_id
in zip(detections.class_id, detections.tracker_id)
f"#{tracker_id} {class_name}"
for class_name, tracker_id
in zip(detections.data["class_name"], detections.tracker_id)
]
annotated_frame = box_annotator.annotate(
@@ -260,9 +259,9 @@ movement patterns and interactions between objects in the video.
detections = tracker.update_with_detections(detections)
labels = [
f"#{tracker_id} {results.names[class_id]}"
for class_id, tracker_id
in zip(detections.class_id, detections.tracker_id)
f"#{tracker_id} {class_name}"
for class_name, tracker_id
in zip(detections.data["class_name"], detections.tracker_id)
]
annotated_frame = box_annotator.annotate(
@@ -298,9 +297,9 @@ movement patterns and interactions between objects in the video.
detections = tracker.update_with_detections(detections)
labels = [
f"#{tracker_id} {results.names[class_id]}"
for class_id, tracker_id
in zip(detections.class_id, detections.tracker_id)
f"#{tracker_id} {class_name}"
for class_name, tracker_id
in zip(detections.data["class_name"], detections.tracker_id)
]
annotated_frame = box_annotator.annotate(
@@ -345,7 +344,7 @@ Supervision is versatile and compatible with various models. Check this [link](/
We will define a `callback` function, which will process each frame of the video by obtaining model predictions and then annotating the frame based on these predictions.
Let's immediately visualize the results with our [`EdgeAnnotator`](/latest/keypoint/annotators/#supervision.keypoint.annotators.EdgeAnnotator) and [`VertexAnnotator`](https://supervision.roboflow.com/latest/keypoint/annotators/#supervision.keypoint.annotators.VertexAnnotator).
Let's immediately visualize the results with our [`EdgeAnnotator`](/latest/keypoint/annotators/#supervision.key_points.annotators.EdgeAnnotator) and [`VertexAnnotator`](https://supervision.roboflow.com/latest/keypoint/annotators/#supervision.key_points.annotators.VertexAnnotator).
=== "Ultralytics"
@@ -408,7 +407,7 @@ Let's immediately visualize the results with our [`EdgeAnnotator`](/latest/keypo
### Convert to Detections
Keypoint tracking is currently supported via the conversion of `KeyPoints` to `Detections`. This is achieved with the [`KeyPoints.as_detections()`](/latest/keypoint/core/#supervision.keypoint.core.KeyPoints.as_detections) function.
Keypoint tracking is currently supported via the conversion of `KeyPoints` to `Detections`. This is achieved with the [`KeyPoints.as_detections()`](/latest/keypoint/core/#supervision.key_points.core.KeyPoints.as_detections) function.
Let's convert to detections and visualize the results with our [`BoxAnnotator`](/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator).
+8 -2
View File
@@ -10,13 +10,19 @@ hide:
<h1></h1>
</div>
<div align="center" id="logo">
<div align="center" id="logo" style="padding-top: 1rem;">
<a align="center" href="" target="_blank">
<img width="850"
src="https://media.roboflow.com/open-source/supervision/rf-supervision-banner.png?updatedAt=1678995927529">
</a>
</div>
<style>
#hello {
margin: 0;
}
</style>
## 👋 Hello
We write your reusable computer vision tools. Whether you need to load your dataset from your hard drive, draw detections on an image or video, or count how many detections are in a zone. You can count on us!
@@ -31,7 +37,7 @@ We write your reusable computer vision tools. Whether you need to load your data
## 💻 Install
You can install `supervision` in a
[**Python>=3.8**](https://www.python.org/) environment.
[**Python>=3.9**](https://www.python.org/) environment.
!!! example "Installation"
+9 -5
View File
@@ -59,7 +59,7 @@ document.addEventListener("DOMContentLoaded", function () {
<div
class="author-container"
data-login="${authorData.login}-${elementIndex}"
style="margin-left: ${marginLeft}; z-index: ${zIndex};"
style="margin-left: ${marginLeft};"
>
<a
href="https://github.com/${authorData.login}"
@@ -90,14 +90,17 @@ document.addEventListener("DOMContentLoaded", function () {
).join(',&nbsp;');
let authorsHTML = `
<div class="authors">
<div class="authors" style="margin: 0;">
${authorAvatarsHTML}
<div class="author-names">${authorNamesHTML}</div>
</div>
`;
element.innerText = `
<div style="flex-direction: column; height: 100%; display: flex;
<div style="
display: grid !important;
grid-template-rows: auto;
height: 100%;
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji; background: ${theme.background}; font-size: 14px; line-height: 1.5; color: ${theme.color}">
<div style="display: flex; align-items: center;">
<span style="font-weight: 700; font-size: 1rem; color: ${theme.linkColor};">
@@ -105,13 +108,14 @@ document.addEventListener("DOMContentLoaded", function () {
</span>
</div>
${authorsHTML}
<div style="font-size: 12px; color: ${theme.color}; display: flex; flex: 0; justify-content: space-between">
<div style="font-size: 12px; color: ${theme.color}; display: grid; grid-template-columns: auto 3fr; justify-content: space-between; gap: 1rem;">
<div style="display: flex; align-items: center;">
<img src="/assets/supervision-lenny.png" aria-label="stars" width="20" height="20" role="img" />
&nbsp;
<span style="margin-left: 4px">${version}</span>
</div>
<div style="display: flex; align-items: center; flex-wrap: wrap">
<div style="display: flex; align-items: center; flex-wrap: wrap; align-content: right;
gap: 0.1rem;">
${labelHTML}
</div>
</div>
+19
View File
@@ -0,0 +1,19 @@
window.MathJax = {
tex: {
inlineMath: [["\\(", "\\)"]],
displayMath: [["\\[", "\\]"]],
processEscapes: true,
processEnvironments: true
},
options: {
ignoreHtmlClass: ".*|",
processHtmlClass: "arithmatex"
}
};
document$.subscribe(() => {
MathJax.startup.output.clearCache()
MathJax.typesetClear()
MathJax.texReset()
MathJax.typesetPromise()
})
+6 -7
View File
@@ -1,6 +1,5 @@
---
comments: true
status: new
---
# Annotators
@@ -79,19 +78,19 @@ status: new
</div>
<div class="md-typeset">
<h2><a href="#supervision.keypoint.annotators.VertexAnnotator">VertexAnnotator</a></h2>
<h2><a href="#supervision.key_points.annotators.VertexAnnotator">VertexAnnotator</a></h2>
</div>
:::supervision.keypoint.annotators.VertexAnnotator
:::supervision.key_points.annotators.VertexAnnotator
<div class="md-typeset">
<h2><a href="#supervision.keypoint.annotators.EdgeAnnotator">EdgeAnnotator</a></h2>
<h2><a href="#supervision.key_points.annotators.EdgeAnnotator">EdgeAnnotator</a></h2>
</div>
:::supervision.keypoint.annotators.EdgeAnnotator
:::supervision.key_points.annotators.EdgeAnnotator
<div class="md-typeset">
<h2><a href="#supervision.keypoint.annotators.VertexLabelAnnotator">VertexLabelAnnotator</a></h2>
<h2><a href="#supervision.key_points.annotators.VertexLabelAnnotator">VertexLabelAnnotator</a></h2>
</div>
:::supervision.keypoint.annotators.VertexLabelAnnotator
:::supervision.key_points.annotators.VertexLabelAnnotator
+1 -2
View File
@@ -1,8 +1,7 @@
---
comments: true
status: new
---
# Keypoint Detection
:::supervision.keypoint.core.KeyPoints
:::supervision.key_points.core.KeyPoints
-1
View File
@@ -1,6 +1,5 @@
---
comments: true
status: new
---
# Common Values
-1
View File
@@ -1,6 +1,5 @@
---
comments: true
status: new
---
# F1 Score
+6 -1
View File
@@ -1,6 +1,5 @@
---
comments: true
status: new
---
# Mean Average Precision
@@ -16,3 +15,9 @@ status: new
</div>
:::supervision.metrics.mean_average_precision.MeanAveragePrecisionResult
<div class="md-typeset">
<h2><a href="#supervision.dataset.formats.coco.get_coco_class_index_mapping">get_coco_class_index_mapping</a></h2>
</div>
:::supervision.dataset.formats.coco.get_coco_class_index_mapping
-1
View File
@@ -1,6 +1,5 @@
---
comments: true
status: new
---
# Mean Average Recall
-1
View File
@@ -1,6 +1,5 @@
---
comments: true
status: new
---
# Precision
-1
View File
@@ -1,6 +1,5 @@
---
comments: true
status: new
---
# Recall
+2 -3
View File
@@ -93,7 +93,7 @@
},
"outputs": [],
"source": [
"!pip install -q inference-gpu \"supervision[assets]\""
"!pip install -q inference-gpu \"supervision\""
]
},
{
@@ -156,7 +156,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
@@ -207,7 +207,6 @@
],
"source": [
"import supervision as sv\n",
"from supervision.assets import download_assets, VideoAssets\n",
"from inference.models.utils import get_roboflow_model\n",
"\n",
"\n",
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -17,7 +17,7 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": null,
"metadata": {
"vscode": {
"languageId": "shellscript"
@@ -25,7 +25,7 @@
},
"outputs": [],
"source": [
"!pip install -q \"supervision[assets]\""
"pip install -q \"supervision\""
]
},
{
@@ -101,7 +101,7 @@
},
"outputs": [],
"source": [
"!pip install -q torch diffusers accelerate inference-gpu[yolo-world] dill git+https://github.com/openai/CLIP.git supervision==0.19.0rc5"
"!pip install -q torch diffusers accelerate inference-gpu[yolo-world] dill git+https://github.com/openai/CLIP.git supervision"
]
},
{
+1 -1
View File
@@ -92,7 +92,7 @@
},
"outputs": [],
"source": [
"!pip install -q inference-gpu \"supervision[assets]\""
"!pip install -q inference-gpu \"supervision\""
]
},
{
+1 -1
View File
@@ -72,7 +72,7 @@
},
"outputs": [],
"source": [
"!pip install roboflow supervision==0.19.0 -q"
"!pip install roboflow supervision -q"
]
},
{
+5 -5
View File
@@ -300,7 +300,7 @@
},
"outputs": [],
"source": [
"!pip install -q \"ultralytics<=8.3.40\""
"!pip install -q ultralytics"
]
},
{
@@ -495,7 +495,7 @@
"source": [
"from ultralytics import YOLO\n",
"\n",
"model = YOLO(\"yolov8x.pt\")\n",
"model = YOLO(\"yolo11x.pt\")\n",
"result = model(image, verbose=False)[0]\n",
"detections = sv.Detections.from_ultralytics(result)"
]
@@ -594,7 +594,7 @@
},
{
"cell_type": "code",
"execution_count": 33,
"execution_count": null,
"metadata": {
"id": "yM6dmicTRGl6"
},
@@ -602,7 +602,7 @@
"source": [
"from ultralytics import YOLO\n",
"\n",
"model = YOLO(\"yolov8x-seg.pt\")\n",
"model = YOLO(\"yolo11x-seg.pt\")\n",
"result = model(image, verbose=False)[0]\n",
"detections = sv.Detections.from_ultralytics(result)"
]
@@ -926,7 +926,7 @@
},
"outputs": [],
"source": [
"!pip install -q supervision[assets]"
"!pip install -q supervision"
]
},
{
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -32,7 +32,7 @@
},
"outputs": [],
"source": [
"!pip install -q inference requests tqdm supervision==0.21.0"
"!pip install -q inference requests tqdm supervision"
]
},
{
@@ -695,12 +695,12 @@
"source": [
"###\u00a0Annotate Image with Detections\n",
"\n",
"Finally, we can annotate the image with the predictions. Since we are working with an object detection model, we will use the [`sv.BoundingBoxAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.BoundingBoxAnnotator) and [`sv.LabelAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.LabelAnnotator) classes."
"Finally, we can annotate the image with the predictions. Since we are working with an object detection model, we will use the [`sv.BoxAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator) and [`sv.LabelAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.LabelAnnotator) classes."
]
},
{
"cell_type": "code",
"execution_count": 49,
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
@@ -722,7 +722,7 @@
}
],
"source": [
"bounding_box_annotator = sv.BoundingBoxAnnotator()\n",
"bounding_box_annotator = sv.BoxAnnotator()\n",
"label_annotator = sv.LabelAnnotator()\n",
"\n",
"annotated_frame = frame.copy()\n",
@@ -15,7 +15,7 @@
/* Large screens (1024px and up) */
@media (min-width: 1024px) {
.custom-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}
+241 -2
View File
@@ -1,7 +1,10 @@
:root {
:root, body {
/* Default to light theme */
--md-primary-fg-color: #8315F9;
--md-accent-fg-color: #00FFCE;
--md-code-hl-color: #8315F9 !important;
--md-accent-fg-color: #8315F9 !important;
--md-code-hl-color--light: #e8d2ff89 !important;
--md-footer-fg-color--light: rgb(111, 108, 121) !important;
}
body.light {
@@ -9,6 +12,202 @@ body.light {
--md-text-color: #000000;
--md-h2-color: #000000;
}
.md-grid {
max-width: 85%;
margin: auto;
}
.sublist {
display: none;
list-style: none;
padding-left: 0;
background: white;
position: absolute;
border-radius: 8px;
margin-top: 0.25rem;
}
.sublist {
transition: opacity 0.5s ease-in-out;
display: none;
position: absolute; /* Ensure it overlaps and doesn't break flow */
background: white; /* So it's visible */
z-index: 1000;
}
.sublist li {
padding: 0.5rem;
}
#products-list *:hover .products-sublist {
display: block;
}
#resources-list *, #products-list * {
cursor: pointer;
}
.products-sublist, .resources-sublist {
padding: 0.25rem;
}
.products-sublist li:hover, .resources-sublist li:hover, .md-nav__link[href]:hover {
background: rgb(242, 241, 247) !important;
border-radius: 6px;
color: initial !important;
}
.md-search {
flex-grow: 2;
}
.portfolio-section .md-grid {
max-width: 100%;
}
.md-header__inner {
align-items: center;
display: grid;
grid-template-columns: 0.1fr 1.4fr 2fr 2fr;
padding-right: 1rem;
}
.md-search__inner {
max-width: 600px;
width: 100%;
min-width: 100%;
}
.md-search__input {
background: white;
border: 1px solid rgb(229, 231, 235);
border-radius: 8px;
color: rgb(111, 108, 121);
}
.md-search__form *, .md-search__icon, .md-search__input {
color: rgb(111, 108, 121);
}
.md-search__input::placeholder {
color: rgb(156, 163, 175);
}
.md-search__form {
background: none !important;
}
.md-footer, .md-footer-meta {
background-color: transparent;
color: rgb(111, 108, 121);
}
.md-typeset .tabbed-set > input:first-child:checked ~ .tabbed-labels > :first-child, .md-typeset .tabbed-set > input:nth-child(10):checked ~ .tabbed-labels > :nth-child(10), .md-typeset .tabbed-set > input:nth-child(11):checked ~ .tabbed-labels > :nth-child(11), .md-typeset .tabbed-set > input:nth-child(12):checked ~ .tabbed-labels > :nth-child(12), .md-typeset .tabbed-set > input:nth-child(13):checked ~ .tabbed-labels > :nth-child(13), .md-typeset .tabbed-set > input:nth-child(14):checked ~ .tabbed-labels > :nth-child(14), .md-typeset .tabbed-set > input:nth-child(15):checked ~ .tabbed-labels > :nth-child(15), .md-typeset .tabbed-set > input:nth-child(16):checked ~ .tabbed-labels > :nth-child(16), .md-typeset .tabbed-set > input:nth-child(17):checked ~ .tabbed-labels > :nth-child(17), .md-typeset .tabbed-set > input:nth-child(18):checked ~ .tabbed-labels > :nth-child(18), .md-typeset .tabbed-set > input:nth-child(19):checked ~ .tabbed-labels > :nth-child(19), .md-typeset .tabbed-set > input:nth-child(2):checked ~ .tabbed-labels > :nth-child(2), .md-typeset .tabbed-set > input:nth-child(20):checked ~ .tabbed-labels > :nth-child(20), .md-typeset .tabbed-set > input:nth-child(3):checked ~ .tabbed-labels > :nth-child(3), .md-typeset .tabbed-set > input:nth-child(4):checked ~ .tabbed-labels > :nth-child(4), .md-typeset .tabbed-set > input:nth-child(5):checked ~ .tabbed-labels > :nth-child(5), .md-typeset .tabbed-set > input:nth-child(6):checked ~ .tabbed-labels > :nth-child(6), .md-typeset .tabbed-set > input:nth-child(7):checked ~ .tabbed-labels > :nth-child(7), .md-typeset .tabbed-set > input:nth-child(8):checked ~ .tabbed-labels > :nth-child(8), .md-typeset .tabbed-set > input:nth-child(9):checked ~ .tabbed-labels > :nth-child(9) {
color: #8315F9;
border-bottom: 1px solid #8315F9;
}
.md-footer *, html .md-footer-meta.md-typeset a {
color: rgb(111, 108, 121);
}
.repo-card {
height: 100%;
}
.header-btn {
text-align: center;
}
.header-btn, .sublist {
box-shadow: rgb(255, 255, 255) 0px 0px 0px 0px, rgb(217, 215, 226) 0px 0px 0px 1px, rgb(217, 215, 226) 0px 1px 2px 0px;
}
.header-btn:hover {
box-shadow: rgb(255, 255, 255) 0px 0px 0px 0px, rgb(217, 215, 226) 0px 0px 0px 1px, rgb(217, 215, 226) 0px 1.0001px 2.00013px -0.0000327245px, rgba(0, 0, 0, 0) 0px 0.000065449px 0.000130898px -0.000065449px;
}
.md-typeset .headerlink:hover, .md-typeset .headerlink:target {
color: #8315F9;
}
.md-typeset h1, .md-header__title {
color: black;
font-weight: 800;
}
.md-typeset h1 {
font-weight: normal;
margin-bottom: 1rem;
}
body {
background: linear-gradient(to left bottom, rgb(243, 238, 255), rgb(255, 255, 255) 60%) no-repeat;
}
/* .md-nav__link:has([tabindex=""]) {
text-transform: uppercase;
} */
.header-list {
display: flex;
align-items: center;
gap: 1rem;
list-style: none;
font-size: 0.75rem;
justify-content: flex-end;
}
.md-nav__list label, .md-nav--secondary label {
/* text-transform: uppercase; */
color: rgb(29, 29, 31) !important;
font-size: 0.7rem;
margin-bottom: 0;
}
.md-nav--secondary label {
margin-left: 0.5rem;
}
.md-nav__link {
padding: 0.25rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.md-nav__link--active {
background: rgb(243, 238, 255);
border-radius: 6px;
padding-top: 0.25rem;
}
.md-tabs__item--active {
color: var(--md-primary-fg-color);
border-bottom: 2px solid var(--md-primary-fg-color);
}
.md-nav--secondary .md-nav__title {
background: transparent;
box-shadow: none;
}
.md-header, .md-tabs {
color: rgb(111, 108, 121);
background-color: transparent;
}
.md-header--shadow {
background: linear-gradient(to left bottom, rgb(243, 238, 255), rgb(255, 255, 255) 60%);
box-shadow: none;
border-bottom: 1px solid rgb(229, 231, 235);
}
#item-logo {
display: none;
}
.md-main__inner, .md-header__inner, .md-grid {
max-width: 100%;
}
@media (max-width: 1200px) {
.md-header__inner {
display: flex;
}
.header-list {
display: none;
}
#item-logo {
display: block;
}
}
.md-content {
max-width: 40rem;
margin: auto;
}
/* // if no md-sidebar--primary, make .md-content full width */
.md-main__inner:has(.md-sidebar--primary[hidden]) .md-content {
max-width: 100%;
}
.md-sidebar--primary {
flex: 0 20%;
}
.md-tabs {
border-bottom: 1px solid rgb(229, 231, 235);
}
.md-main__inner {
padding-top: 1rem;
margin-top: 0;
}
body.dark {
/* Dark theme */
@@ -29,3 +228,43 @@ body[data-md-url$="/cookbooks/"] .md-content {
margin-left: 0;
width: 100%;
}
.md-main, nav .md-grid, .md-header__inner {
max-width: 1600px;
width: 100%;
margin: auto;
}
.md-search__scrollwrap {
width: 100% !important;
}
.md-nav--secondary .md-nav__title {
position: initial !important;
}
.md-header__title .md-ellipsis {
overflow: initial !important;
text-overflow: initial !important;
}
.md-search {
flex-grow: 0;
}
/* Table style */
th, td {
border: 1px solid var(--md-typeset-table-color);
}
.md-typeset__table {
line-height: 1.5;
}
.md-typeset__table table:not([class]) {
font-size: 0.6rem;
border-collapse: collapse;
}
.md-typeset__table table:not([class]) td,
.md-typeset__table table:not([class]) th {
padding: 10px;
}
+9 -9
View File
@@ -12,15 +12,15 @@
</div>
<div class="custom-grid">
<a href="/develop/notebooks/quickstart"> <p class="card repo-card" data-name="Supervision Quickstart" data-labels="ANNOTATOR,DETECTION,SAM"
data-version="v0.18.0" data-author="SkalskiP,onuralpszr"></p>
data-version="v0.26.0" data-author="SkalskiP,onuralpszr"></p>
</a>
<a href="/develop/notebooks/count-objects-crossing-the-line">
<p class="card repo-card" data-name="Count Objects Crossing the Line"
data-labels="ANNOTATORS,LINE ZONE,TRACKING" data-version="v0.18.0" data-author="SkalskiP"></p>
data-labels="ANNOTATORS,LINE ZONE,TRACKING" data-version="v0.26.0" data-author="SkalskiP"></p>
</a>
<a href="/develop/notebooks/zero-shot-object-detection-with-yolo-world">
<p class="card repo-card" data-name="Zero-Shot Object Detection with YOLO-World"
data-labels="ANNOTATORS,DETECTION,INFERENCE" data-version="v0.19.0" data-author="SkalskiP"></p>
data-labels="ANNOTATORS,DETECTION,INFERENCE" data-version="v0.26.0" data-author="SkalskiP"></p>
</a>
<a href="/develop/notebooks/download-supervision-assets">
<p class="card repo-card" data-name="Downloading Supervision Assets" data-labels="ASSETS" data-version="v0.18.0"
@@ -28,7 +28,7 @@
</a>
<a href="/develop/notebooks/annotate-video-with-detections">
<p class="card repo-card" data-name="Annotate Video with Detections" data-labels="INFERENCE,YOLOV8"
data-version="v0.18.0" data-author="nickherrig"></p>
data-version="v0.26.0" data-author="nickherrig"></p>
</a>
<a href="/develop/notebooks/object-tracking">
<p class="card repo-card" data-name="Object Tracking" data-labels="TRACKING, ANNOTATOR" data-version="v0.18.0"
@@ -36,23 +36,23 @@
</a>
<a href="/develop/notebooks/occupancy_analytics">
<p class="card repo-card" data-name="Analyzing Zone Occupancy" data-labels="ANNOTATOR,DETECTION,ZONES"
data-version="v0.19.0" data-author="stellasphere"></p>
data-version="v0.26.0" data-author="stellasphere"></p>
</a>
<a href="/develop/notebooks/evaluating-alignment-of-text-to-image-diffusion-models">
<p class="card repo-card" data-name="Evaluating Alignment of Text-to-image Diffusion Models"
data-labels="ANNOTATORS,YOLO WORLD" data-version="v0.19.0rc5" data-author="iamhatesz"></p>
data-labels="ANNOTATORS,YOLO WORLD" data-version="v0.26.0" data-author="iamhatesz"></p>
</a>
<a href="/develop/notebooks/serialise-detections-to-csv">
<p class="card repo-card" data-name="Serialise Detections to a CSV File"
data-labels="DETECTIONS,CSV SINK,INFERENCE" data-version="v0.21.0" data-author="onuralpszr"></p>
data-labels="DETECTIONS,CSV SINK,INFERENCE" data-version="v0.26.0" data-author="onuralpszr"></p>
</a>
<a href="/develop/notebooks/serialise-detections-to-json">
<p class="card repo-card" data-name="Serialise Detections to a JSON File"
data-labels="DETECTIONS,JSON SINK,INFERENCE" data-version="v0.21.0" data-author="onuralpszr"></p>
data-labels="DETECTIONS,JSON SINK,INFERENCE" data-version="v0.26.0" data-author="onuralpszr"></p>
</a>
<a href="/develop/notebooks/small-object-detection-with-sahi">
<p class="card repo-card" data-name="Small Object Detection with SAHI"
data-labels="DETECTIONS,SAHI,SMALL,OBJECT,INFERENCE" data-version="v0.23.0" data-author="ediardo"></p>
data-labels="DETECTIONS,SAHI,SMALL,OBJECT,INFERENCE" data-version="v0.26.0" data-author="ediardo"></p>
</a>
</div>
</div>
+181
View File
@@ -0,0 +1,181 @@
{% set class = "md-header" %}
{% if "navigation.tabs.sticky" in features %}
{% set class = class ~ " md-header--shadow md-header--lifted" %}
{% elif "navigation.tabs" not in features %}
{% set class = class ~ " md-header--shadow" %}
{% endif %}
<!-- Header -->
<header class="{{ class }}" data-md-component="header">
<nav
class="md-header__inner md-grid"
aria-label="{{ lang.t('header') }}"
>
<!-- Link to home -->
<a
href="{{ config.extra.homepage | d(nav.homepage.url, true) | url }}"
title="{{ config.site_name | e }}"
class="md-header__button md-logo"
aria-label="{{ config.site_name }}"
data-md-component="logo"
>
{% include "partials/logo.html" %}
</a>
<!-- Button to open drawer -->
<label class="md-header__button md-icon" for="__drawer">
{% set icon = config.theme.icon.menu or "material/menu" %}
{% include ".icons/" ~ icon ~ ".svg" %}
</label>
<!-- Header title -->
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
{{ config.site_name }} Docs
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis" style="display: flex; align-items: center; gap: 0.5rem;">
<a
href="{{ config.extra.homepage | d(nav.homepage.url, true) | url }}"
title="{{ config.site_name | e }}"
class="md-header__button md-logo"
aria-label="{{ config.site_name }}"
data-md-component="logo"
id="item-logo"
>
{% include "partials/logo.html" %}
</a>
{% if page.meta and page.meta.title %}
{{ page.meta.title }}
{% else %}
{{ page.title }}
{% endif %}
</span>
</div>
</div>
</div>
<!-- Button to open search modal -->
{% if "material/search" in config.plugins %}
{% set search = config.plugins["material/search"] | attr("config") %}
<!-- Check if search is actually enabled - see https://t.ly/DT_0V -->
{% if search.enabled %}
<label class="md-header__button md-icon" for="__search">
{% set icon = config.theme.icon.search or "material/magnify" %}
{% include ".icons/" ~ icon ~ ".svg" %}
</label>
<!-- Search interface -->
{% include "partials/search.html" %}
{% endif %}
{% endif %}
<ul class="header-list">
<li style="align-items: center;" id="resources-list">
<label for="dropdown-resources"><span>Resources <img src="https://ka-p.fontawesome.com/releases/v6.6.0/svgs/regular/chevron-down.svg?v=2&token=a463935e93" style="height: 0.5rem;" /></span></label>
<input type="radio" name="dropdown" id="dropdown-resources" style="display: none;" />
<ul class="resources-sublist sublist">
<li><a href="https://blog.roboflow.com">Blog</a></li>
<li><a href="https://discuss.roboflow.com">Community Forum</a></li>
<li><a href="https://roboflow.com/sales">Contact Sales</a></li>
<li><a href="https://universe.roboflow.com">Universe</a></li>
</ul>
</li>
<li style="align-items: center;" id="products-list">
<label for="dropdown-products"><span>Docs <img src="https://ka-p.fontawesome.com/releases/v6.6.0/svgs/regular/chevron-down.svg?v=2&token=a463935e93" style="height: 0.5rem;" /></span></label>
<input type="radio" name="dropdown" id="dropdown-products" style="display: none;" />
<ul class="products-sublist sublist">
<li><a href="https://inference.roboflow.com">Inference</a></li>
<li><a href="https://supervision.roboflow.com">Supervision</a></li>
<li><a href="https://trackers.roboflow.com">Trackers</a></li>
<li><a href="https://maestro.roboflow.com">Maestro</a></li>
<li><a href="https://docs.roboflow.com">Roboflow</a></li>
</ul>
</li>
<script>
document.addEventListener('click', function(event) {
const resourcesList = document.getElementById('resources-list');
const productsList = document.getElementById('products-list');
const dropdownResources = document.getElementById('dropdown-resources');
const dropdownProducts = document.getElementById('dropdown-products');
if (!resourcesList.contains(event.target)) {
dropdownResources.checked = false;
}
if (!productsList.contains(event.target)) {
dropdownProducts.checked = false;
}
});
// on mouse over
document.getElementById('resources-list').addEventListener('mouseover', function() {
document.getElementById('dropdown-resources').checked = true;
});
document.getElementById('products-list').addEventListener('mouseover', function() {
document.getElementById('dropdown-products').checked = true;
});
// on mouse out
document.getElementById('resources-list').addEventListener('mouseout', function() {
// if not hovering over the sublist or the label, uncheck the dropdown
// wait 1 sec
setTimeout(function() {
if (!document.querySelector('.resources-sublist:hover') && !document.querySelector('#resources-list:hover')) {
document.getElementById('dropdown-resources').checked = false;
}
}, 350);
});
// if mouseout of sublist, uncheck immediately
document.querySelector('.resources-sublist').addEventListener('mouseout', function() {
setTimeout(function() {
if (!document.querySelector('.resources-sublist:hover') && !document.querySelector('#resources-list:hover')) {
document.getElementById('dropdown-resources').checked = false;
}
}, 450);
});
document.getElementById('products-list').addEventListener('mouseout', function() {
// if not hovering over the sublist, uncheck the dropdown
// wait 1 sec
setTimeout(function() {
if (!document.querySelector('.products-sublist:hover') && !document.querySelector('#products-list:hover')) {
document.getElementById('dropdown-products').checked = false;
}
}, 500);
});
// if mouseout of sublist, uncheck immediately
document.querySelector('.products-sublist').addEventListener('mouseout', function() {
setTimeout(function() {
if (!document.querySelector('.products-sublist:hover') && !document.querySelector('#products-list:hover')) {
document.getElementById('dropdown-products').checked = false;
}
}, 500);
});
</script>
<style>
#dropdown-resources:checked ~ .resources-sublist {
display: block;
}
#dropdown-products:checked ~ .products-sublist {
display: block;
}
/* Hide dropdown if clicking outside */
body:not(:has(#dropdown-resources:checked)) .resources-sublist,
body:not(:has(#dropdown-products:checked)) .products-sublist {
display: none;
}
</style>
<a href="https://github.com/roboflow/supervision"><li class="header-btn" style="border-radius: 5px; color: white; background: var(--md-typeset-a-color); padding-top: 0.25rem; padding-left: 0.5rem; padding-bottom: 0.25rem; padding-right: 0.5rem; border: 1px solid #8315F9;">Go to GitHub</li></a>
</ul>
</nav>
<!-- Navigation tabs (sticky) -->
{% if "navigation.tabs.sticky" in features %}
{% if "navigation.tabs" in features %}
{% include "partials/tabs.html" %}
{% endif %}
{% endif %}
</header>
-1
View File
@@ -1,6 +1,5 @@
---
comments: true
status: new
---
# ByteTrack
+15 -2
View File
@@ -1,5 +1,6 @@
---
comments: true
status: new
---
# Image Utils
@@ -29,10 +30,22 @@ comments: true
:::supervision.utils.image.letterbox_image
<div class="md-typeset">
<h2><a href="#supervision.utils.image.overlay_image">overlay_image</a></h2>
<h2><a href="#supervision.utils.image.tint_image">tint_image</a></h2>
</div>
:::supervision.utils.image.overlay_image
:::supervision.utils.image.tint_image
<div class="md-typeset">
<h2><a href="#supervision.utils.image.grayscale_image">grayscale_image</a></h2>
</div>
:::supervision.utils.image.grayscale_image
<div class="md-typeset">
<h2><a href="#supervision.utils.image.get_image_resolution_wh">get_image_resolution_wh</a></h2>
</div>
:::supervision.utils.image.get_image_resolution_wh
<div class="md-typeset">
<h2><a href="#supervision.utils.image.ImageSink">ImageSink</a></h2>
@@ -1,7 +1,6 @@
import argparse
import json
import os
from typing import List, Tuple
import cv2
import numpy as np
@@ -14,7 +13,7 @@ import supervision as sv
COLORS = sv.ColorPalette.DEFAULT
def load_zones_config(file_path: str) -> List[np.ndarray]:
def load_zones_config(file_path: str) -> list[np.ndarray]:
"""
Load polygon zone configurations from a JSON file.
@@ -28,14 +27,14 @@ def load_zones_config(file_path: str) -> List[np.ndarray]:
Returns:
List[np.ndarray]: A list of polygons, each represented as a NumPy array.
"""
with open(file_path, "r") as file:
with open(file_path) as file:
data = json.load(file)
return [np.array(polygon, np.int32) for polygon in data["polygons"]]
def initiate_annotators(
polygons: List[np.ndarray], resolution_wh: Tuple[int, int]
) -> Tuple[List[sv.PolygonZone], List[sv.PolygonZoneAnnotator], List[sv.BoxAnnotator]]:
polygons: list[np.ndarray], resolution_wh: tuple[int, int]
) -> tuple[list[sv.PolygonZone], list[sv.PolygonZoneAnnotator], list[sv.BoxAnnotator]]:
line_thickness = sv.calculate_optimal_line_thickness(resolution_wh=resolution_wh)
text_scale = sv.calculate_optimal_text_scale(resolution_wh=resolution_wh)
@@ -93,9 +92,9 @@ def detect(
def annotate(
frame: np.ndarray,
zones: List[sv.PolygonZone],
zone_annotators: List[sv.PolygonZoneAnnotator],
box_annotators: List[sv.BoxAnnotator],
zones: list[sv.PolygonZone],
zone_annotators: list[sv.PolygonZoneAnnotator],
box_annotators: list[sv.BoxAnnotator],
detections: sv.Detections,
) -> np.ndarray:
"""
@@ -1,5 +1,5 @@
gdown
inference==0.9.17
supervision>=0.20.0
inference
supervision
tqdm
ultralytics<=8.3.40
ultralytics
@@ -1,6 +1,5 @@
import argparse
import json
from typing import List, Tuple
import cv2
import numpy as np
@@ -12,7 +11,7 @@ import supervision as sv
COLORS = sv.ColorPalette.DEFAULT
def load_zones_config(file_path: str) -> List[np.ndarray]:
def load_zones_config(file_path: str) -> list[np.ndarray]:
"""
Load polygon zone configurations from a JSON file.
@@ -26,14 +25,14 @@ def load_zones_config(file_path: str) -> List[np.ndarray]:
Returns:
List[np.ndarray]: A list of polygons, each represented as a NumPy array.
"""
with open(file_path, "r") as file:
with open(file_path) as file:
data = json.load(file)
return [np.array(polygon, np.int32) for polygon in data["polygons"]]
def initiate_annotators(
polygons: List[np.ndarray], resolution_wh: Tuple[int, int]
) -> Tuple[List[sv.PolygonZone], List[sv.PolygonZoneAnnotator], List[sv.BoxAnnotator]]:
polygons: list[np.ndarray], resolution_wh: tuple[int, int]
) -> tuple[list[sv.PolygonZone], list[sv.PolygonZoneAnnotator], list[sv.BoxAnnotator]]:
line_thickness = sv.calculate_optimal_line_thickness(resolution_wh=resolution_wh)
text_scale = sv.calculate_optimal_text_scale(resolution_wh=resolution_wh)
@@ -90,9 +89,9 @@ def detect(
def annotate(
frame: np.ndarray,
zones: List[sv.PolygonZone],
zone_annotators: List[sv.PolygonZoneAnnotator],
box_annotators: List[sv.BoxAnnotator],
zones: list[sv.PolygonZone],
zone_annotators: list[sv.PolygonZoneAnnotator],
box_annotators: list[sv.BoxAnnotator],
detections: sv.Detections,
) -> np.ndarray:
"""
@@ -135,7 +134,7 @@ if __name__ == "__main__":
)
parser.add_argument(
"--source_weights_path",
default="yolov8x.pt",
default="yolo11x.pt",
help="Path to the source weights file",
type=str,
)
+2 -2
View File
@@ -1,2 +1,2 @@
supervision[assets]==0.19.0
ultralytics<=8.3.40
supervision
ultralytics
+4 -4
View File
@@ -1,6 +1,6 @@
supervision>=0.20.0
tqdm==4.66.3
supervision
tqdm
requests
ultralytics==8.0.237
ultralytics
super-gradients==3.5.0
inference==0.9.17
inference
@@ -70,7 +70,7 @@ if __name__ == "__main__":
args = parse_arguments()
video_info = sv.VideoInfo.from_video_path(video_path=args.source_video_path)
model = YOLO("yolov8x.pt")
model = YOLO("yolo11x.pt")
byte_track = sv.ByteTrack(
frame_rate=video_info.fps, track_activation_threshold=args.confidence_threshold
@@ -1,5 +1,4 @@
import argparse
from typing import List
import cv2
import numpy as np
@@ -22,7 +21,7 @@ def main(
model_id: str,
confidence: float,
iou: float,
classes: List[int],
classes: list[int],
) -> None:
model = get_model(model_id=model_id)
tracker = sv.ByteTrack(minimum_matching_threshold=0.5)
@@ -1,5 +1,4 @@
import argparse
from typing import List
import cv2
import numpy as np
@@ -22,7 +21,7 @@ def main(
model_id: str,
confidence: float,
iou: float,
classes: List[int],
classes: list[int],
) -> None:
model = get_model(model_id=model_id)
tracker = sv.ByteTrack(minimum_matching_threshold=0.5)
@@ -1,5 +1,4 @@
import argparse
from typing import List
import cv2
import numpy as np
@@ -18,7 +17,7 @@ LABEL_ANNOTATOR = sv.LabelAnnotator(
class CustomSink:
def __init__(self, zone_configuration_path: str, classes: List[int]):
def __init__(self, zone_configuration_path: str, classes: list[int]):
self.classes = classes
self.tracker = sv.ByteTrack(minimum_matching_threshold=0.5)
self.fps_monitor = sv.FPSMonitor()
@@ -83,7 +82,7 @@ def main(
model_id: str,
confidence: float,
iou: float,
classes: List[int],
classes: list[int],
) -> None:
sink = CustomSink(zone_configuration_path=zone_configuration_path, classes=classes)
+6 -5
View File
@@ -1,5 +1,6 @@
opencv-python
supervision>=0.20.0
ultralytics<=8.3.40
inference==0.9.17
pytube
supervision
ultralytics
inference
# https://github.com/pytube/pytube/issues/2044
# pytube
pytubefix
@@ -1,11 +1,12 @@
from __future__ import annotations
import argparse
import os
from typing import Optional
from pytube import YouTube
from pytubefix import YouTube
def main(url: str, output_path: Optional[str], file_name: Optional[str]) -> None:
def main(url: str, output_path: str | None, file_name: str | None) -> None:
yt = YouTube(url)
stream = yt.streams.get_highest_resolution()
+5 -3
View File
@@ -1,7 +1,9 @@
from __future__ import annotations
import argparse
import json
import os
from typing import Any, Optional, Tuple
from typing import Any
import cv2
import numpy as np
@@ -19,10 +21,10 @@ COLORS = sv.ColorPalette.DEFAULT
WINDOW_NAME = "Draw Zones"
POLYGONS = [[]]
current_mouse_position: Optional[Tuple[int, int]] = None
current_mouse_position: tuple[int, int] | None = None
def resolve_source(source_path: str) -> Optional[np.ndarray]:
def resolve_source(source_path: str) -> np.ndarray | None:
if not os.path.exists(source_path):
return None
@@ -1,5 +1,4 @@
import argparse
from typing import List
import cv2
import numpy as np
@@ -23,7 +22,7 @@ def main(
device: str,
confidence: float,
iou: float,
classes: List[int],
classes: list[int],
) -> None:
model = YOLO(weights)
tracker = sv.ByteTrack(minimum_matching_threshold=0.5)
@@ -1,5 +1,4 @@
import argparse
from typing import List
import cv2
import numpy as np
@@ -23,7 +22,7 @@ def main(
device: str,
confidence: float,
iou: float,
classes: List[int],
classes: list[int],
) -> None:
model = YOLO(weights)
tracker = sv.ByteTrack(minimum_matching_threshold=0.5)
@@ -1,5 +1,4 @@
import argparse
from typing import List
import cv2
import numpy as np
@@ -19,7 +18,7 @@ LABEL_ANNOTATOR = sv.LabelAnnotator(
class CustomSink:
def __init__(self, zone_configuration_path: str, classes: List[int]):
def __init__(self, zone_configuration_path: str, classes: list[int]):
self.classes = classes
self.tracker = sv.ByteTrack(minimum_matching_threshold=0.8)
self.fps_monitor = sv.FPSMonitor()
@@ -84,7 +83,7 @@ def main(
device: str,
confidence: float,
iou: float,
classes: List[int],
classes: list[int],
) -> None:
model = YOLO(weights)
+4 -4
View File
@@ -1,11 +1,11 @@
import json
from typing import Generator, List
from collections.abc import Generator
import cv2
import numpy as np
def load_zones_config(file_path: str) -> List[np.ndarray]:
def load_zones_config(file_path: str) -> list[np.ndarray]:
"""
Load polygon zone configurations from a JSON file.
@@ -19,12 +19,12 @@ def load_zones_config(file_path: str) -> List[np.ndarray]:
Returns:
List[np.ndarray]: A list of polygons, each represented as a NumPy array.
"""
with open(file_path, "r") as file:
with open(file_path) as file:
data = json.load(file)
return [np.array(polygon, np.int32) for polygon in data]
def find_in_list(array: np.ndarray, search_list: List[int]) -> np.ndarray:
def find_in_list(array: np.ndarray, search_list: list[int]) -> np.ndarray:
"""Determines if elements of a numpy array are present in a list.
Args:
+2 -3
View File
@@ -1,5 +1,4 @@
from datetime import datetime
from typing import Dict
import numpy as np
@@ -26,7 +25,7 @@ class FPSBasedTimer:
"""
self.fps = fps
self.frame_id = 0
self.tracker_id2frame_id: Dict[int, int] = {}
self.tracker_id2frame_id: dict[int, int] = {}
def tick(self, detections: sv.Detections) -> np.ndarray:
"""Processes the current frame, updating time durations for each tracker.
@@ -63,7 +62,7 @@ class ClockBasedTimer:
def __init__(self) -> None:
"""Initializes the ClockBasedTimer."""
self.tracker_id2start_time: Dict[int, datetime] = {}
self.tracker_id2start_time: dict[int, datetime] = {}
def tick(self, detections: sv.Detections) -> np.ndarray:
"""Processes the current frame, updating time durations for each tracker.
+3 -3
View File
@@ -1,4 +1,4 @@
inference==0.9.17
supervision==0.19.0
inference
supervision
tqdm
ultralytics<=8.3.40
ultralytics
+10 -8
View File
@@ -1,6 +1,8 @@
from __future__ import annotations
import argparse
import os
from typing import Dict, Iterable, List, Optional, Set
from collections.abc import Iterable
import cv2
import numpy as np
@@ -29,14 +31,14 @@ ZONE_OUT_POLYGONS = [
class DetectionsManager:
def __init__(self) -> None:
self.tracker_id_to_zone_id: Dict[int, int] = {}
self.counts: Dict[int, Dict[int, Set[int]]] = {}
self.tracker_id_to_zone_id: dict[int, int] = {}
self.counts: dict[int, dict[int, set[int]]] = {}
def update(
self,
detections_all: sv.Detections,
detections_in_zones: List[sv.Detections],
detections_out_zones: List[sv.Detections],
detections_in_zones: list[sv.Detections],
detections_out_zones: list[sv.Detections],
) -> sv.Detections:
for zone_in_id, detections_in_zone in enumerate(detections_in_zones):
for tracker_id in detections_in_zone.tracker_id:
@@ -59,9 +61,9 @@ class DetectionsManager:
def initiate_polygon_zones(
polygons: List[np.ndarray],
polygons: list[np.ndarray],
triggering_anchors: Iterable[sv.Position] = [sv.Position.CENTER],
) -> List[sv.PolygonZone]:
) -> list[sv.PolygonZone]:
return [
sv.PolygonZone(
polygon=polygon,
@@ -77,7 +79,7 @@ class VideoProcessor:
roboflow_api_key: str,
model_id: str,
source_video_path: str,
target_video_path: Optional[str] = None,
target_video_path: str | None = None,
confidence_threshold: float = 0.3,
iou_threshold: float = 0.7,
) -> None:
+3 -3
View File
@@ -1,5 +1,5 @@
gdown
inference==0.9.17
supervision>=0.20.0
inference
supervision
tqdm
ultralytics<=8.3.40
ultralytics
@@ -1,5 +1,7 @@
from __future__ import annotations
import argparse
from typing import Dict, Iterable, List, Optional, Set
from collections.abc import Iterable
import cv2
import numpy as np
@@ -27,14 +29,14 @@ ZONE_OUT_POLYGONS = [
class DetectionsManager:
def __init__(self) -> None:
self.tracker_id_to_zone_id: Dict[int, int] = {}
self.counts: Dict[int, Dict[int, Set[int]]] = {}
self.tracker_id_to_zone_id: dict[int, int] = {}
self.counts: dict[int, dict[int, set[int]]] = {}
def update(
self,
detections_all: sv.Detections,
detections_in_zones: List[sv.Detections],
detections_out_zones: List[sv.Detections],
detections_in_zones: list[sv.Detections],
detections_out_zones: list[sv.Detections],
) -> sv.Detections:
for zone_in_id, detections_in_zone in enumerate(detections_in_zones):
for tracker_id in detections_in_zone.tracker_id:
@@ -57,9 +59,9 @@ class DetectionsManager:
def initiate_polygon_zones(
polygons: List[np.ndarray],
polygons: list[np.ndarray],
triggering_anchors: Iterable[sv.Position] = [sv.Position.CENTER],
) -> List[sv.PolygonZone]:
) -> list[sv.PolygonZone]:
return [
sv.PolygonZone(
polygon=polygon,
@@ -74,7 +76,7 @@ class VideoProcessor:
self,
source_weights_path: str,
source_video_path: str,
target_video_path: Optional[str] = None,
target_video_path: str | None = None,
confidence_threshold: float = 0.3,
iou_threshold: float = 0.7,
) -> None:
+18 -23
View File
@@ -3,9 +3,8 @@ site_url: https://supervision.roboflow.com/
site_author: Roboflow
site_description: A set of easy-to-use utilities that will come in handy in any computer vision project.
repo_name: roboflow/supervision
repo_url: https://github.com/roboflow/supervision
edit_uri: https://github.com/roboflow/supervision/tree/main/docs
copyright: Roboflow 2024. All rights reserved.
copyright: Roboflow 2025. All rights reserved.
extra:
social:
@@ -13,12 +12,8 @@ extra:
link: https://github.com/roboflow
- icon: fontawesome/brands/python
link: https://pypi.org/project/supervision
- icon: fontawesome/brands/docker
link: https://hub.docker.com/u/roboflow
- icon: fontawesome/brands/youtube
link: https://www.youtube.com/roboflow
- icon: fontawesome/brands/linkedin
link: https://www.linkedin.com/company/roboflow-ai/
- icon: fontawesome/brands/x-twitter
link: https://twitter.com/roboflow
- icon: fontawesome/brands/discord
@@ -31,10 +26,10 @@ extra:
extra_css:
- stylesheets/extra.css
- stylesheets/cookbooks-card.css
- stylesheets/cookbooks_card.css
nav:
- Supervision: index.md
- Home: index.md
- Learn:
- Detect and Annotate: how_to/detect_and_annotate.md
- Save Detections: how_to/save_detections.md
@@ -43,13 +38,16 @@ nav:
- Track Objects on Video: how_to/track_objects.md
- Process Datasets: how_to/process_datasets.md
- Benchmark a Model: how_to/benchmark_a_model.md
- Reference - Code API:
- Reference:
- Detection and Segmentation:
- Core: detection/core.md
- Annotators: detection/annotators.md
- Double Detection Filter: detection/double_detection_filter.md
- Utils: detection/utils.md
- Converters: detection/utils/converters.md
- IoU and NMS: detection/utils/iou_and_nms.md
- Boxes: detection/utils/boxes.md
- Masks: detection/utils/masks.md
- Polygons: detection/utils/polygons.md
- VLMs: detection/utils/vlms.md
- Keypoint Detection:
- Core: keypoint/core.md
- Annotators: keypoint/annotators.md
@@ -83,12 +81,7 @@ nav:
- Geometry: utils/geometry.md
- Assets: assets.md
- Cookbooks: cookbooks.md
- Cheatsheet: https://roboflow.github.io/cheatsheet-supervision/
- Contribute:
- Contributing: contributing.md
- Code of Conduct: code_of_conduct.md
- License: license.md
- Release Notes:
- Changelog:
- Changelog: changelog.md
- Deprecated: deprecated.md
@@ -106,6 +99,7 @@ theme:
- content.tooltips
- content.code.annotate
- navigation.tabs
- navigation.tabs.sticky
palette:
# Palette for light mode
@@ -123,11 +117,8 @@ theme:
name: Switch to light mode
font:
text: Roboto
code: Roboto Mono
features:
- content.code.copy
- content.code.annotate
text: Inter
code: IBM Plex Mono
plugins:
- search
@@ -181,12 +172,16 @@ markdown_extensions:
anchor_linenums: true
line_spans: __span
pygments_lang_class: true
- pymdownx.arithmatex:
generic: true
extra_javascript:
- "javascripts/init_kapa_widget.js"
- "javascripts/cookbooks-card.js"
- "javascripts/segment.js"
- "javascripts/mathjax.js"
- "https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.8/purify.min.js"
- "https://unpkg.com/mathjax@3/es5/tex-mml-chtml.js"
# Messages shown during document build
# Reference: https://www.mkdocs.org/user-guide/configuration/#validation
+11 -8
View File
@@ -2,9 +2,9 @@
name = "supervision"
description = "A set of easy-to-use utils that will come in handy in any Computer Vision project"
license = { text = "MIT" }
version = "0.26.0rc7"
version = "0.27.0"
readme = "README.md"
requires-python = ">=3.8"
requires-python = ">=3.9"
authors = [
{ name = "Piotr Skalski", email = "piotr.skalski92@gmail.com" }
]
@@ -58,6 +58,8 @@ Documentation = "https://supervision.roboflow.com/latest/"
metrics = [
"pandas>=2.0.0",
]
[dependency-groups]
dev = [
"pytest>=7.2.2,<9.0.0",
"tox>=4.11.4",
@@ -65,21 +67,22 @@ dev = [
"ipywidgets>=8.1.1",
"jupytext>=1.16.1",
"nbconvert>=7.14.2",
"docutils!=0.21"
"docutils!=0.21",
"pre-commit>=3.8.0"
]
docs = [
"mkdocs-material[imaging]>=9.5.5",
"mkdocstrings>=0.25.2,<0.30.0",
"mkdocstrings>=0.25.2,<0.31.0",
"mkdocstrings-python>=1.10.9",
"mike>=2.0.0",
"mkdocs-jupyter>=0.24.3",
"mkdocs-git-committers-plugin-2>=2.4.1; python_version >= '3.8' and python_version < '4'",
"mkdocs-git-committers-plugin-2>=2.4.1; python_version >= '3.9' and python_version < '4'",
"mkdocs-git-revision-date-localized-plugin>=1.2.4"
]
build = [
"twine>=5.1.1,<7.0.0",
"wheel>=0.40,<0.46",
"build>=0.10,<1.3"
"build>=0.10,<1.4"
]
[tool.bandit]
@@ -91,7 +94,7 @@ check = true
imports = ["cv2", "supervision"]
[tool.ruff]
target-version = "py38"
target-version = "py39"
# Exclude a variety of commonly ignored directories.
exclude = [
@@ -126,7 +129,7 @@ indent-width = 4
[tool.ruff.lint]
# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default.
select = ["E", "F", "I", "A", "Q", "W", "RUF"]
select = ["E", "F", "I", "A", "Q", "W", "RUF", "UP"]
ignore = []
# Allow autofix for all enabled rules (when `--fix`) is provided.
fixable = [
+53 -23
View File
@@ -38,6 +38,7 @@ from supervision.dataset.core import (
ClassificationDataset,
DetectionDataset,
)
from supervision.dataset.formats.coco import get_coco_class_index_mapping
from supervision.dataset.utils import mask_to_rle, rle_to_mask
from supervision.detection.core import Detections
from supervision.detection.line_zone import (
@@ -45,40 +46,55 @@ from supervision.detection.line_zone import (
LineZoneAnnotator,
LineZoneAnnotatorMulticlass,
)
from supervision.detection.overlap_filter import (
OverlapFilter,
box_non_max_merge,
box_non_max_suppression,
mask_non_max_suppression,
)
from supervision.detection.tools.csv_sink import CSVSink
from supervision.detection.tools.inference_slicer import InferenceSlicer
from supervision.detection.tools.json_sink import JSONSink
from supervision.detection.tools.polygon_zone import PolygonZone, PolygonZoneAnnotator
from supervision.detection.tools.smoother import DetectionsSmoother
from supervision.detection.utils import (
box_iou_batch,
calculate_masks_centroids,
from supervision.detection.utils.boxes import (
clip_boxes,
contains_holes,
contains_multiple_segments,
filter_polygons_by_area,
mask_iou_batch,
denormalize_boxes,
move_boxes,
pad_boxes,
scale_boxes,
)
from supervision.detection.utils.converters import (
mask_to_polygons,
mask_to_xyxy,
move_boxes,
move_masks,
oriented_box_iou_batch,
pad_boxes,
polygon_to_mask,
polygon_to_xyxy,
scale_boxes,
xcycwh_to_xyxy,
xywh_to_xyxy,
xyxy_to_mask,
xyxy_to_polygons,
xyxy_to_xcycarh,
xyxy_to_xywh,
)
from supervision.detection.utils.iou_and_nms import (
OverlapFilter,
OverlapMetric,
box_iou,
box_iou_batch,
box_iou_batch_with_jaccard,
box_non_max_merge,
box_non_max_suppression,
mask_iou_batch,
mask_non_max_merge,
mask_non_max_suppression,
oriented_box_iou_batch,
)
from supervision.detection.utils.masks import (
calculate_masks_centroids,
contains_holes,
contains_multiple_segments,
filter_segments_by_distance,
move_masks,
)
from supervision.detection.utils.polygons import (
approximate_polygon,
filter_polygons_by_area,
)
from supervision.detection.utils.vlms import edit_distance, fuzzy_match_index
from supervision.detection.vlm import LMM, VLM
from supervision.draw.color import Color, ColorPalette
from supervision.draw.utils import (
@@ -94,24 +110,26 @@ from supervision.draw.utils import (
)
from supervision.geometry.core import Point, Position, Rect
from supervision.geometry.utils import get_polygon_center
from supervision.keypoint.annotators import (
from supervision.key_points.annotators import (
EdgeAnnotator,
VertexAnnotator,
VertexLabelAnnotator,
)
from supervision.keypoint.core import KeyPoints
from supervision.key_points.core import KeyPoints
from supervision.metrics.detection import ConfusionMatrix, MeanAveragePrecision
from supervision.tracker.byte_tracker.core import ByteTrack
from supervision.utils.conversion import cv2_to_pillow, pillow_to_cv2
from supervision.utils.file import list_files_with_extensions
from supervision.utils.image import (
ImageSink,
create_tiles,
crop_image,
get_image_resolution_wh,
grayscale_image,
letterbox_image,
overlay_image,
resize_image,
scale_image,
tint_image,
)
from supervision.utils.notebook import plot_image, plot_images_grid
from supervision.utils.video import (
@@ -163,6 +181,7 @@ __all__ = [
"MeanAveragePrecision",
"OrientedBoxAnnotator",
"OverlapFilter",
"OverlapMetric",
"PercentageBarAnnotator",
"PixelateAnnotator",
"Point",
@@ -179,7 +198,10 @@ __all__ = [
"VertexLabelAnnotator",
"VideoInfo",
"VideoSink",
"approximate_polygon",
"box_iou",
"box_iou_batch",
"box_iou_batch_with_jaccard",
"box_non_max_merge",
"box_non_max_suppression",
"calculate_masks_centroids",
@@ -188,7 +210,6 @@ __all__ = [
"clip_boxes",
"contains_holes",
"contains_multiple_segments",
"create_tiles",
"crop_image",
"cv2_to_pillow",
"draw_filled_polygon",
@@ -198,12 +219,19 @@ __all__ = [
"draw_polygon",
"draw_rectangle",
"draw_text",
"edit_distance",
"filter_polygons_by_area",
"filter_segments_by_distance",
"fuzzy_match_index",
"get_coco_class_index_mapping",
"get_image_resolution_wh",
"get_polygon_center",
"get_video_frames_generator",
"grayscale_image",
"letterbox_image",
"list_files_with_extensions",
"mask_iou_batch",
"mask_non_max_merge",
"mask_non_max_suppression",
"mask_to_polygons",
"mask_to_rle",
@@ -223,9 +251,11 @@ __all__ = [
"rle_to_mask",
"scale_boxes",
"scale_image",
"tint_image",
"xcycwh_to_xyxy",
"xywh_to_xyxy",
"xyxy_to_mask",
"xyxy_to_polygons",
"xyxy_to_xyah",
"xyxy_to_xcycarh",
"xyxy_to_xywh",
]
+1 -13
View File
@@ -1,19 +1,7 @@
from abc import ABC, abstractmethod
from typing import TypeVar
import numpy as np
from PIL import Image
from supervision.detection.core import Detections
ImageType = TypeVar("ImageType", np.ndarray, Image.Image)
"""
An image of type `np.ndarray` or `PIL.Image.Image`.
Unlike a `Union`, ensures the type remains consistent. If a function
takes an `ImageType` argument and returns an `ImageType`, when you
pass an `np.ndarray`, you will get an `np.ndarray` back.
"""
from supervision.draw.base import ImageType
class BaseAnnotator(ABC):
File diff suppressed because it is too large Load Diff
+185 -11
View File
@@ -1,8 +1,12 @@
from __future__ import annotations
import textwrap
from enum import Enum
from typing import Optional, Tuple, Union
from typing import Any
import numpy as np
from supervision.config import CLASS_NAME_DATA_FIELD
from supervision.detection.core import Detections
from supervision.draw.color import Color, ColorPalette
from supervision.geometry.core import Position
@@ -33,7 +37,7 @@ class ColorLookup(Enum):
def resolve_color_idx(
detections: Detections,
detection_idx: int,
color_lookup: Union[ColorLookup, np.ndarray] = ColorLookup.CLASS,
color_lookup: ColorLookup | np.ndarray = ColorLookup.CLASS,
) -> int:
if detection_idx >= len(detections):
raise ValueError(
@@ -70,10 +74,10 @@ def resolve_color_idx(
def resolve_text_background_xyxy(
center_coordinates: Tuple[int, int],
text_wh: Tuple[int, int],
center_coordinates: tuple[int, int],
text_wh: tuple[int, int],
position: Position,
) -> Tuple[int, int, int, int]:
) -> tuple[int, int, int, int]:
center_x, center_y = center_coordinates
text_w, text_h = text_wh
@@ -122,32 +126,199 @@ def resolve_text_background_xyxy(
)
def get_color_by_index(color: Union[Color, ColorPalette], idx: int) -> Color:
def get_color_by_index(color: Color | ColorPalette, idx: int) -> Color:
if isinstance(color, ColorPalette):
return color.by_idx(idx)
return color
def resolve_color(
color: Union[Color, ColorPalette],
color: Color | ColorPalette,
detections: Detections,
detection_idx: int,
color_lookup: Union[ColorLookup, np.ndarray] = ColorLookup.CLASS,
color_lookup: ColorLookup | np.ndarray = ColorLookup.CLASS,
) -> Color:
idx = resolve_color_idx(
detections=detections,
detection_idx=detection_idx,
color_lookup=color_lookup,
)
if color_lookup == ColorLookup.TRACK and idx == PENDING_TRACK_ID:
if (
isinstance(color_lookup, ColorLookup)
and color_lookup == ColorLookup.TRACK
and idx == PENDING_TRACK_ID
):
return PENDING_TRACK_COLOR
return get_color_by_index(color=color, idx=idx)
def wrap_text(text: Any, max_line_length=None) -> list[str]:
"""
Wrap `text` to the specified maximum line length, respecting existing
newlines. Falls back to str() if `text` is not already a string.
Args:
text (Any): The text (or object) to wrap.
max_line_length (int | None): Maximum width for each wrapped line.
Returns:
list[str]: Wrapped lines.
"""
if not text:
return [""]
if not isinstance(text, str):
text = str(text)
if max_line_length is None:
return text.splitlines() or [""]
if max_line_length <= 0:
raise ValueError("max_line_length must be a positive integer")
paragraphs = text.split("\n")
all_lines: list[str] = []
for paragraph in paragraphs:
if paragraph == "":
all_lines.append("")
continue
wrapped = textwrap.wrap(
paragraph,
width=max_line_length,
break_long_words=True,
replace_whitespace=False,
drop_whitespace=True,
)
all_lines.extend(wrapped or [""])
return all_lines or [""]
def validate_labels(labels: list[str] | None, detections: Detections):
"""
Validates that the number of provided labels matches the number of detections.
Args:
labels (Optional[List[str]]): A list of labels, one for each detection. Can
be None.
detections (Detections): The detections to be labeled.
Raises:
ValueError: If `labels` is not None and its length does not match the number
of detections.
"""
if labels is not None and len(labels) != len(detections):
raise ValueError(
f"The number of labels ({len(labels)}) does not match the "
f"number of detections ({len(detections)}). Each detection "
f"should have exactly 1 label."
)
def get_labels_text(
detections: Detections, custom_labels: list[str] | None
) -> list[str]:
"""
Retrieves the text labels for the detections.
If `custom_labels` are provided, they are used. Otherwise, the labels are
extracted from the `detections` object, prioritizing the 'class_name' field,
then the `class_id`, and finally using the detection index as a string.
Args:
detections (Detections): The detections to get labels for.
custom_labels (Optional[List[str]]): An optional list of custom labels.
Returns:
List[str]: A list of text labels for each detection.
"""
if custom_labels is not None:
return custom_labels
labels = []
for idx in range(len(detections)):
if CLASS_NAME_DATA_FIELD in detections.data:
labels.append(detections.data[CLASS_NAME_DATA_FIELD][idx])
elif detections.class_id is not None:
labels.append(str(detections.class_id[idx]))
else:
labels.append(str(idx))
return labels
def snap_boxes(xyxy: np.ndarray, resolution_wh: tuple[int, int]) -> np.ndarray:
"""
Shifts `label` bounding boxes into the frame so that they are fully contained
within the given resolution, prioritizing the top/left edge.
Unlike `clip_boxes`, this function does not crop boxes.
It moves them entirely if they exceed the frame boundaries.
Args:
xyxy (np.ndarray): A numpy array of shape `(N, 4)` where each
row corresponds to a bounding box in the format
`(x_min, y_min, x_max, y_max)`.
resolution_wh (Tuple[int, int]): A tuple `(width, height)`
representing the resolution of the frame.
Returns:
np.ndarray: A numpy array of shape `(N, 4)` with boxes shifted into frame.
Examples:
```python
import numpy as np
# Example boxes:
xyxy = np.array([
[-10, 10, 30, 50], # Off left edge
[310, 200, 350, 250], # Off right edge
[100, -20, 150, 30], # Off top edge
[200, 220, 250, 270], # Off bottom edge
[-20, 10, 350, 50], # Wider than frame (370 vs 320)
[10, -20, 30, 260] # Taller than frame (280 vs 240)
])
resolution_wh = (320, 240)
snapped_boxes = snap_boxes(xyxy=xyxy, resolution_wh=resolution_wh)
# Results:
# [[ 0 10 40 50] # Left edge shifted right
# [280 200 320 250] # Right edge shifted left
# [100 0 150 50] # Top edge shifted down
# [200 190 250 240] # Bottom edge shifted up
# [ 0 10 370 50] # Wide box aligned to left edge
# [ 10 0 30 280]] # Tall box aligned to top edge
```
"""
result = np.copy(xyxy)
width, height = resolution_wh
# X-axis (prioritize left edge)
left_overflow = result[:, 0] < 0
result[left_overflow, 0:3:2] -= result[left_overflow, 0:1]
right_overflow = (~left_overflow) & (result[:, 2] > width)
right_shift = width - result[right_overflow, 2]
result[right_overflow, 0:3:2] += right_shift[:, np.newaxis]
# Y-axis (prioritize top edge)
top_overflow = result[:, 1] < 0
result[top_overflow, 1:4:2] -= result[top_overflow, 1:2]
bottom_overflow = (~top_overflow) & (result[:, 3] > height)
bottom_shift = height - result[bottom_overflow, 3]
result[bottom_overflow, 1:4:2] += bottom_shift[:, np.newaxis]
return result
class Trace:
def __init__(
self,
max_size: Optional[int] = None,
max_size: int | None = None,
start_frame_id: int = 0,
anchor: Position = Position.CENTER,
) -> None:
@@ -163,7 +334,10 @@ class Trace:
frame_id = np.full(len(detections), self.current_frame_id, dtype=int)
self.frame_id = np.concatenate([self.frame_id, frame_id])
self.xy = np.concatenate(
[self.xy, detections.get_anchors_coordinates(self.anchor)]
[
self.xy,
detections.get_anchors_coordinates(self.anchor),
]
)
self.tracker_id = np.concatenate([self.tracker_id, detections.tracker_id])
+3 -2
View File
@@ -1,8 +1,9 @@
from __future__ import annotations
import os
from hashlib import new as hash_new
from pathlib import Path
from shutil import copyfileobj
from typing import Union
from requests import get
from tqdm.auto import tqdm
@@ -32,7 +33,7 @@ def is_md5_hash_matching(filename: str, original_md5_hash: str) -> bool:
return computed_md5_hash.hexdigest() == original_md5_hash
def download_assets(asset_name: Union[VideoAssets, str]) -> str:
def download_assets(asset_name: VideoAssets | str) -> str:
"""
Download a specified asset if it doesn't already exist or is corrupted.
+1 -2
View File
@@ -1,5 +1,4 @@
from enum import Enum
from typing import Dict, Tuple
BASE_VIDEO_URL = "https://media.roboflow.com/supervision/video-examples/"
@@ -39,7 +38,7 @@ class VideoAssets(Enum):
return list(map(lambda c: c.value, cls))
VIDEO_ASSETS: Dict[str, Tuple[str, str]] = {
VIDEO_ASSETS: dict[str, tuple[str, str]] = {
VideoAssets.VEHICLES.value: (
f"{BASE_VIDEO_URL}{VideoAssets.VEHICLES.value}",
"8155ff4e4de08cfa25f39de96483f918",
+3 -3
View File
@@ -1,7 +1,7 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Optional, Tuple
from typing import Any
import numpy as np
@@ -28,7 +28,7 @@ def _validate_confidence(confidence: Any, n: int) -> None:
@dataclass
class Classifications:
class_id: np.ndarray
confidence: Optional[np.ndarray] = None
confidence: np.ndarray | None = None
def __post_init__(self) -> None:
"""
@@ -154,7 +154,7 @@ class Classifications:
class_id = np.arange(len(confidence))
return cls(class_id=class_id, confidence=confidence)
def get_top_k(self, k: int) -> Tuple[np.ndarray, np.ndarray]:
def get_top_k(self, k: int) -> tuple[np.ndarray, np.ndarray]:
"""
Retrieve the top k class IDs and confidences,
ordered in descending order by confidence.
+32 -66
View File
@@ -2,10 +2,10 @@ from __future__ import annotations
import os
from abc import ABC, abstractmethod
from collections.abc import Iterator
from dataclasses import dataclass
from itertools import chain
from pathlib import Path
from typing import Dict, Iterator, List, Optional, Tuple, Union
import cv2
import numpy as np
@@ -32,7 +32,7 @@ from supervision.dataset.utils import (
train_test_split,
)
from supervision.detection.core import Detections
from supervision.utils.internal import deprecated, warn_deprecated
from supervision.utils.internal import warn_deprecated
from supervision.utils.iterables import find_duplicates
@@ -45,9 +45,9 @@ class BaseDataset(ABC):
def split(
self,
split_ratio: float = 0.8,
random_state: Optional[int] = None,
random_state: int | None = None,
shuffle: bool = True,
) -> Tuple[BaseDataset, BaseDataset]:
) -> tuple[BaseDataset, BaseDataset]:
pass
@@ -71,9 +71,9 @@ class DetectionDataset(BaseDataset):
def __init__(
self,
classes: List[str],
images: Union[List[str], Dict[str, np.ndarray]],
annotations: Dict[str, Detections],
classes: list[str],
images: list[str] | dict[str, np.ndarray],
annotations: dict[str, Detections],
) -> None:
self.classes = classes
@@ -86,40 +86,7 @@ class DetectionDataset(BaseDataset):
# Eliminate duplicates while preserving order
self.image_paths = list(dict.fromkeys(images))
self._images_in_memory: Dict[str, np.ndarray] = {}
if isinstance(images, dict):
self._images_in_memory = images
warn_deprecated(
"Passing a `Dict[str, np.ndarray]` into `DetectionDataset` is "
"deprecated and will be removed in `supervision-0.26.0`. Use "
"a list of paths `List[str]` instead."
)
# TODO: when supervision-0.26.0 is released, and Dict[str, np.ndarray]
# for images is no longer supported, also simplify the rest of
# the code. E.g. list(images) is no longer needed, and merge can
# be simplified.
@property
@deprecated(
"`DetectionDataset.images` property is deprecated and will be removed in "
"`supervision-0.26.0`. Iterate with `for path, image, annotation in dataset:` "
"instead."
)
def images(self) -> Dict[str, np.ndarray]:
"""
Load all images to memory and return them as a dictionary.
!!! warning
Only use this when you need all images at once.
It is much more memory-efficient to initialize dataset with
image paths and use `for path, image, annotation in dataset:`.
"""
if self._images_in_memory:
return self._images_in_memory
images = {image_path: cv2.imread(image_path) for image_path in self.image_paths}
return images
self._images_in_memory: dict[str, np.ndarray] = {}
def _get_image(self, image_path: str) -> np.ndarray:
"""Assumes that image is in dataset"""
@@ -130,7 +97,7 @@ class DetectionDataset(BaseDataset):
def __len__(self) -> int:
return len(self._images_in_memory) or len(self.image_paths)
def __getitem__(self, i: int) -> Tuple[str, np.ndarray, Detections]:
def __getitem__(self, i: int) -> tuple[str, np.ndarray, Detections]:
"""
Returns:
Tuple[str, np.ndarray, Detections]: The image path, image data,
@@ -141,7 +108,7 @@ class DetectionDataset(BaseDataset):
annotation = self.annotations[image_path]
return image_path, image, annotation
def __iter__(self) -> Iterator[Tuple[str, np.ndarray, Detections]]:
def __iter__(self) -> Iterator[tuple[str, np.ndarray, Detections]]:
"""
Iterate over the images and annotations in the dataset.
@@ -179,9 +146,9 @@ class DetectionDataset(BaseDataset):
def split(
self,
split_ratio: float = 0.8,
random_state: Optional[int] = None,
random_state: int | None = None,
shuffle: bool = True,
) -> Tuple[DetectionDataset, DetectionDataset]:
) -> tuple[DetectionDataset, DetectionDataset]:
"""
Splits the dataset into two parts (training and testing)
using the provided split_ratio.
@@ -215,8 +182,8 @@ class DetectionDataset(BaseDataset):
shuffle=shuffle,
)
train_input: Union[List[str], Dict[str, np.ndarray]]
test_input: Union[List[str], Dict[str, np.ndarray]]
train_input: list[str] | dict[str, np.ndarray]
test_input: list[str] | dict[str, np.ndarray]
if self._images_in_memory:
train_input = {path: self._images_in_memory[path] for path in train_paths}
test_input = {path: self._images_in_memory[path] for path in test_paths}
@@ -239,7 +206,7 @@ class DetectionDataset(BaseDataset):
return train_dataset, test_dataset
@classmethod
def merge(cls, dataset_list: List[DetectionDataset]) -> DetectionDataset:
def merge(cls, dataset_list: list[DetectionDataset]) -> DetectionDataset:
"""
Merge a list of `DetectionDataset` objects into a single
`DetectionDataset` object.
@@ -333,8 +300,8 @@ class DetectionDataset(BaseDataset):
def as_pascal_voc(
self,
images_directory_path: Optional[str] = None,
annotations_directory_path: Optional[str] = None,
images_directory_path: str | None = None,
annotations_directory_path: str | None = None,
min_image_area_percentage: float = 0.0,
max_image_area_percentage: float = 1.0,
approximation_percentage: float = 0.0,
@@ -507,9 +474,9 @@ class DetectionDataset(BaseDataset):
def as_yolo(
self,
images_directory_path: Optional[str] = None,
annotations_directory_path: Optional[str] = None,
data_yaml_path: Optional[str] = None,
images_directory_path: str | None = None,
annotations_directory_path: str | None = None,
data_yaml_path: str | None = None,
min_image_area_percentage: float = 0.0,
max_image_area_percentage: float = 1.0,
approximation_percentage: float = 0.0,
@@ -574,7 +541,6 @@ class DetectionDataset(BaseDataset):
force_masks (bool): If True,
forces masks to be loaded for all annotations,
regardless of whether they are present.
Returns:
DetectionDataset: A DetectionDataset instance containing
the loaded images and annotations.
@@ -609,8 +575,8 @@ class DetectionDataset(BaseDataset):
def as_coco(
self,
images_directory_path: Optional[str] = None,
annotations_path: Optional[str] = None,
images_directory_path: str | None = None,
annotations_path: str | None = None,
min_image_area_percentage: float = 0.0,
max_image_area_percentage: float = 1.0,
approximation_percentage: float = 0.0,
@@ -681,9 +647,9 @@ class ClassificationDataset(BaseDataset):
def __init__(
self,
classes: List[str],
images: Union[List[str], Dict[str, np.ndarray]],
annotations: Dict[str, Classifications],
classes: list[str],
images: list[str] | dict[str, np.ndarray],
annotations: dict[str, Classifications],
) -> None:
self.classes = classes
@@ -696,7 +662,7 @@ class ClassificationDataset(BaseDataset):
# Eliminate duplicates while preserving order
self.image_paths = list(dict.fromkeys(images))
self._images_in_memory: Dict[str, np.ndarray] = {}
self._images_in_memory: dict[str, np.ndarray] = {}
if isinstance(images, dict):
self._images_in_memory = images
warn_deprecated(
@@ -714,7 +680,7 @@ class ClassificationDataset(BaseDataset):
def __len__(self) -> int:
return len(self._images_in_memory) or len(self.image_paths)
def __getitem__(self, i: int) -> Tuple[str, np.ndarray, Classifications]:
def __getitem__(self, i: int) -> tuple[str, np.ndarray, Classifications]:
"""
Returns:
Tuple[str, np.ndarray, Classifications]: The image path, image data,
@@ -725,7 +691,7 @@ class ClassificationDataset(BaseDataset):
annotation = self.annotations[image_path]
return image_path, image, annotation
def __iter__(self) -> Iterator[Tuple[str, np.ndarray, Classifications]]:
def __iter__(self) -> Iterator[tuple[str, np.ndarray, Classifications]]:
"""
Iterate over the images and annotations in the dataset.
@@ -763,9 +729,9 @@ class ClassificationDataset(BaseDataset):
def split(
self,
split_ratio: float = 0.8,
random_state: Optional[int] = None,
random_state: int | None = None,
shuffle: bool = True,
) -> Tuple[ClassificationDataset, ClassificationDataset]:
) -> tuple[ClassificationDataset, ClassificationDataset]:
"""
Splits the dataset into two parts (training and testing)
using the provided split_ratio.
@@ -798,8 +764,8 @@ class ClassificationDataset(BaseDataset):
shuffle=shuffle,
)
train_input: Union[List[str], Dict[str, np.ndarray]]
test_input: Union[List[str], Dict[str, np.ndarray]]
train_input: list[str] | dict[str, np.ndarray]
test_input: list[str] | dict[str, np.ndarray]
if self._images_in_memory:
train_input = {path: self._images_in_memory[path] for path in train_paths}
test_input = {path: self._images_in_memory[path] for path in test_paths}
+75 -20
View File
@@ -1,7 +1,7 @@
import os
from datetime import datetime
from pathlib import Path
from typing import TYPE_CHECKING, Dict, List, Tuple
from typing import TYPE_CHECKING
import numpy as np
import numpy.typing as npt
@@ -13,18 +13,15 @@ from supervision.dataset.utils import (
rle_to_mask,
)
from supervision.detection.core import Detections
from supervision.detection.utils import (
contains_holes,
contains_multiple_segments,
polygon_to_mask,
)
from supervision.detection.utils.converters import polygon_to_mask
from supervision.detection.utils.masks import contains_holes, contains_multiple_segments
from supervision.utils.file import read_json_file, save_json_file
if TYPE_CHECKING:
from supervision.dataset.core import DetectionDataset
def coco_categories_to_classes(coco_categories: List[dict]) -> List[str]:
def coco_categories_to_classes(coco_categories: list[dict]) -> list[str]:
return [
category["name"]
for category in sorted(coco_categories, key=lambda category: category["id"])
@@ -32,8 +29,8 @@ def coco_categories_to_classes(coco_categories: List[dict]) -> List[str]:
def build_coco_class_index_mapping(
coco_categories: List[dict], target_classes: List[str]
) -> Dict[int, int]:
coco_categories: list[dict], target_classes: list[str]
) -> dict[int, int]:
source_class_to_index = {
category["name"]: category["id"] for category in coco_categories
}
@@ -43,7 +40,7 @@ def build_coco_class_index_mapping(
}
def classes_to_coco_categories(classes: List[str]) -> List[dict]:
def classes_to_coco_categories(classes: list[str]) -> list[dict]:
return [
{
"id": class_id,
@@ -55,8 +52,8 @@ def classes_to_coco_categories(classes: List[str]) -> List[dict]:
def group_coco_annotations_by_image_id(
coco_annotations: List[dict],
) -> Dict[int, List[dict]]:
coco_annotations: list[dict],
) -> dict[int, list[dict]]:
annotations = {}
for annotation in coco_annotations:
image_id = annotation["image_id"]
@@ -67,7 +64,7 @@ def group_coco_annotations_by_image_id(
def coco_annotations_to_masks(
image_annotations: List[dict], resolution_wh: Tuple[int, int]
image_annotations: list[dict], resolution_wh: tuple[int, int]
) -> npt.NDArray[np.bool_]:
return np.array(
[
@@ -90,7 +87,10 @@ def coco_annotations_to_masks(
def coco_annotations_to_detections(
image_annotations: List[dict], resolution_wh: Tuple[int, int], with_masks: bool
image_annotations: list[dict],
resolution_wh: tuple[int, int],
with_masks: bool,
use_iscrowd: bool = True,
) -> Detections:
if not image_annotations:
return Detections.empty()
@@ -102,15 +102,26 @@ def coco_annotations_to_detections(
xyxy = np.asarray(xyxy)
xyxy[:, 2:4] += xyxy[:, 0:2]
data = dict()
if use_iscrowd:
iscrowd = [
image_annotation["iscrowd"] for image_annotation in image_annotations
]
area = [image_annotation["area"] for image_annotation in image_annotations]
data = dict(
iscrowd=np.asarray(iscrowd, dtype=int), area=np.asarray(area, dtype=float)
)
if with_masks:
mask = coco_annotations_to_masks(
image_annotations=image_annotations, resolution_wh=resolution_wh
)
return Detections(
class_id=np.asarray(class_ids, dtype=int), xyxy=xyxy, mask=mask
)
else:
mask = None
return Detections(xyxy=xyxy, class_id=np.asarray(class_ids, dtype=int))
return Detections(
class_id=np.asarray(class_ids, dtype=int), xyxy=xyxy, mask=mask, data=data
)
def detections_to_coco_annotations(
@@ -120,7 +131,7 @@ def detections_to_coco_annotations(
min_image_area_percentage: float = 0.0,
max_image_area_percentage: float = 1.0,
approximation_percentage: float = 0.75,
) -> Tuple[List[Dict], int]:
) -> tuple[list[dict], int]:
coco_annotations = []
for xyxy, mask, _, class_id, _, _ in detections:
box_width, box_height = xyxy[2] - xyxy[0], xyxy[3] - xyxy[1]
@@ -159,16 +170,58 @@ def detections_to_coco_annotations(
return coco_annotations, annotation_id
def get_coco_class_index_mapping(annotations_path: str) -> dict[int, int]:
"""
Generates a mapping from sequential class indices to original COCO class ids.
This function is essential when working with models that expect class ids to be
zero-indexed and sequential (0 to 79), as opposed to the original COCO
dataset where category ids are non-contiguous ranging from 1 to 90 but skipping some
ids.
Use Cases:
- Evaluating models trained with COCO-style annotations where class ids
are sequential ranging from 0 to 79.
- Ensuring consistent class indexing across training, inference and evaluation,
when using different tools or datasets with COCO format.
- Reproducing results from models that assume sequential class ids (0 to 79).
How it Works:
- Reads the COCO annotation file in its original format (`annotations_path`).
- Extracts and sorts all class names by their original COCO id (1 to 90).
- Builds a mapping from COCO class ids (not sequential with skipped ids) to
new class ids (sequential ranging from 0 to 79).
- Returns a dictionary mapping: `{new_class_id: original_COCO_class_id}`.
Args:
annotations_path (str): Path to COCO JSON annotations file
(e.g., `instances_val2017.json`).
Returns:
Dict[int, int]: A mapping from new class id (sequential ranging from 0 to 79)
to original COCO class id (1 to 90 with skipped ids).
"""
coco_data = read_json_file(annotations_path)
classes = coco_categories_to_classes(coco_categories=coco_data["categories"])
class_mapping = build_coco_class_index_mapping(
coco_categories=coco_data["categories"], target_classes=classes
)
return {v: k for k, v in class_mapping.items()}
def load_coco_annotations(
images_directory_path: str,
annotations_path: str,
force_masks: bool = False,
) -> Tuple[List[str], List[str], Dict[str, Detections]]:
use_iscrowd: bool = True,
) -> tuple[list[str], list[str], dict[str, Detections]]:
coco_data = read_json_file(file_path=annotations_path)
classes = coco_categories_to_classes(coco_categories=coco_data["categories"])
class_index_mapping = build_coco_class_index_mapping(
coco_categories=coco_data["categories"], target_classes=classes
)
coco_images = coco_data["images"]
coco_annotations_groups = group_coco_annotations_by_image_id(
coco_annotations=coco_data["annotations"]
@@ -190,7 +243,9 @@ def load_coco_annotations(
image_annotations=image_annotations,
resolution_wh=(image_width, image_height),
with_masks=force_masks,
use_iscrowd=use_iscrowd,
)
annotation = map_detections_class_id(
source_to_target_mapping=class_index_mapping,
detections=annotation,
+10 -9
View File
@@ -1,6 +1,7 @@
from __future__ import annotations
import os
from pathlib import Path
from typing import Dict, List, Optional, Tuple
from xml.etree.ElementTree import Element, SubElement
import cv2
@@ -10,12 +11,12 @@ from defusedxml.minidom import parseString
from supervision.dataset.utils import approximate_mask_with_polygons
from supervision.detection.core import Detections
from supervision.detection.utils import polygon_to_mask, polygon_to_xyxy
from supervision.detection.utils.converters import polygon_to_mask, polygon_to_xyxy
from supervision.utils.file import list_files_with_extensions
def object_to_pascal_voc(
xyxy: np.ndarray, name: str, polygon: Optional[np.ndarray] = None
xyxy: np.ndarray, name: str, polygon: np.ndarray | None = None
) -> Element:
root = Element("object")
@@ -51,9 +52,9 @@ def object_to_pascal_voc(
def detections_to_pascal_voc(
detections: Detections,
classes: List[str],
classes: list[str],
filename: str,
image_shape: Tuple[int, int, int],
image_shape: tuple[int, int, int],
min_image_area_percentage: float = 0.0,
max_image_area_percentage: float = 1.0,
approximation_percentage: float = 0.75,
@@ -138,7 +139,7 @@ def load_pascal_voc_annotations(
images_directory_path: str,
annotations_directory_path: str,
force_masks: bool = False,
) -> Tuple[List[str], List[str], Dict[str, Detections]]:
) -> tuple[list[str], list[str], dict[str, Detections]]:
"""
Loads PASCAL VOC XML annotations and returns the image name,
a Detections instance, and a list of class names.
@@ -163,7 +164,7 @@ def load_pascal_voc_annotations(
)
]
classes: List[str] = []
classes: list[str] = []
annotations = {}
for image_path in image_paths:
@@ -187,8 +188,8 @@ def load_pascal_voc_annotations(
def detections_from_xml_obj(
root: Element, classes: List[str], resolution_wh, force_masks: bool = False
) -> Tuple[Detections, List[str]]:
root: Element, classes: list[str], resolution_wh, force_masks: bool = False
) -> tuple[Detections, list[str]]:
"""
Converts an XML object in Pascal VOC format to a Detections object.
Expected XML format:
+18 -16
View File
@@ -1,6 +1,8 @@
from __future__ import annotations
import os
from pathlib import Path
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
from typing import TYPE_CHECKING
import numpy as np
from PIL import Image
@@ -8,7 +10,7 @@ from PIL import Image
from supervision.config import ORIENTED_BOX_COORDINATES
from supervision.dataset.utils import approximate_mask_with_polygons
from supervision.detection.core import Detections
from supervision.detection.utils import polygon_to_mask, polygon_to_xyxy
from supervision.detection.utils.converters import polygon_to_mask, polygon_to_xyxy
from supervision.utils.file import (
list_files_with_extensions,
read_txt_file,
@@ -21,7 +23,7 @@ if TYPE_CHECKING:
from supervision.dataset.core import DetectionDataset
def _parse_box(values: List[str]) -> np.ndarray:
def _parse_box(values: list[str]) -> np.ndarray:
x_center, y_center, width, height = values
return np.array(
[
@@ -40,12 +42,12 @@ def _box_to_polygon(box: np.ndarray) -> np.ndarray:
)
def _parse_polygon(values: List[str]) -> np.ndarray:
def _parse_polygon(values: list[str]) -> np.ndarray:
return np.array(values, dtype=np.float32).reshape(-1, 2)
def _polygons_to_masks(
polygons: List[np.ndarray], resolution_wh: Tuple[int, int]
polygons: list[np.ndarray], resolution_wh: tuple[int, int]
) -> np.ndarray:
return np.array(
[
@@ -56,11 +58,11 @@ def _polygons_to_masks(
)
def _with_mask(lines: List[str]) -> bool:
def _with_mask(lines: list[str]) -> bool:
return any([len(line.split()) > 5 for line in lines])
def _extract_class_names(file_path: str) -> List[str]:
def _extract_class_names(file_path: str) -> list[str]:
data = read_yaml_file(file_path=file_path)
names = data["names"]
if isinstance(names, dict):
@@ -74,8 +76,8 @@ def _image_name_to_annotation_name(image_name: str) -> str:
def yolo_annotations_to_detections(
lines: List[str],
resolution_wh: Tuple[int, int],
lines: list[str],
resolution_wh: tuple[int, int],
with_masks: bool,
is_obb: bool = False,
) -> Detections:
@@ -127,7 +129,7 @@ def load_yolo_annotations(
data_yaml_path: str,
force_masks: bool = False,
is_obb: bool = False,
) -> Tuple[List[str], List[str], Dict[str, Detections]]:
) -> tuple[list[str], list[str], dict[str, Detections]]:
"""
Loads YOLO annotations and returns class names, images,
and their corresponding detections.
@@ -204,8 +206,8 @@ def load_yolo_annotations(
def object_to_yolo(
xyxy: np.ndarray,
class_id: int,
image_shape: Tuple[int, int, int],
polygon: Optional[np.ndarray] = None,
image_shape: tuple[int, int, int],
polygon: np.ndarray | None = None,
) -> str:
h, w, _ = image_shape
if polygon is None:
@@ -225,11 +227,11 @@ def object_to_yolo(
def detections_to_yolo_annotations(
detections: Detections,
image_shape: Tuple[int, int, int],
image_shape: tuple[int, int, int],
min_image_area_percentage: float = 0.0,
max_image_area_percentage: float = 1.0,
approximation_percentage: float = 0.75,
) -> List[str]:
) -> list[str]:
annotation = []
for xyxy, mask, _, class_id, _, _ in detections:
if class_id is None:
@@ -260,7 +262,7 @@ def detections_to_yolo_annotations(
def save_yolo_annotations(
dataset: "DetectionDataset",
dataset: DetectionDataset,
annotations_directory_path: str,
min_image_area_percentage: float = 0.0,
max_image_area_percentage: float = 1.0,
@@ -283,7 +285,7 @@ def save_yolo_annotations(
save_text_file(lines=lines, file_path=yolo_annotations_path)
def save_data_yaml(data_yaml_path: str, classes: List[str]) -> None:
def save_data_yaml(data_yaml_path: str, classes: list[str]) -> None:
data = {"nc": len(classes), "names": classes}
Path(data_yaml_path).parent.mkdir(parents=True, exist_ok=True)
save_yaml_file(data=data, file_path=data_yaml_path)
+16 -16
View File
@@ -1,19 +1,21 @@
from __future__ import annotations
import copy
import os
import random
import shutil
from pathlib import Path
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, TypeVar, Union
from typing import TYPE_CHECKING, TypeVar
import cv2
import numpy as np
import numpy.typing as npt
from supervision.detection.core import Detections
from supervision.detection.utils import (
from supervision.detection.utils.converters import mask_to_polygons
from supervision.detection.utils.polygons import (
approximate_polygon,
filter_polygons_by_area,
mask_to_polygons,
)
if TYPE_CHECKING:
@@ -27,7 +29,7 @@ def approximate_mask_with_polygons(
min_image_area_percentage: float = 0.0,
max_image_area_percentage: float = 1.0,
approximation_percentage: float = 0.75,
) -> List[np.ndarray]:
) -> list[np.ndarray]:
height, width = mask.shape
image_area = height * width
minimum_detection_area = min_image_area_percentage * image_area
@@ -50,7 +52,7 @@ def approximate_mask_with_polygons(
]
def merge_class_lists(class_lists: List[List[str]]) -> List[str]:
def merge_class_lists(class_lists: list[list[str]]) -> list[str]:
unique_classes = set()
for class_list in class_lists:
@@ -61,8 +63,8 @@ def merge_class_lists(class_lists: List[List[str]]) -> List[str]:
def build_class_index_mapping(
source_classes: List[str], target_classes: List[str]
) -> Dict[int, int]:
source_classes: list[str], target_classes: list[str]
) -> dict[int, int]:
"""Returns the index map of source classes -> target classes."""
index_mapping = {}
@@ -79,7 +81,7 @@ def build_class_index_mapping(
def map_detections_class_id(
source_to_target_mapping: Dict[int, int], detections: Detections
source_to_target_mapping: dict[int, int], detections: Detections
) -> Detections:
if detections.class_id is None:
raise ValueError("Detections must have class_id attribute.")
@@ -98,9 +100,7 @@ def map_detections_class_id(
return detections_copy
def save_dataset_images(
dataset: "DetectionDataset", images_directory_path: str
) -> None:
def save_dataset_images(dataset: DetectionDataset, images_directory_path: str) -> None:
Path(images_directory_path).mkdir(parents=True, exist_ok=True)
for image_path in dataset.image_paths:
final_path = os.path.join(images_directory_path, Path(image_path).name)
@@ -112,11 +112,11 @@ def save_dataset_images(
def train_test_split(
data: List[T],
data: list[T],
train_ratio: float = 0.8,
random_state: Optional[int] = None,
random_state: int | None = None,
shuffle: bool = True,
) -> Tuple[List[T], List[T]]:
) -> tuple[list[T], list[T]]:
"""
Splits the data into two parts using the provided train_ratio.
@@ -140,7 +140,7 @@ def train_test_split(
def rle_to_mask(
rle: Union[npt.NDArray[np.int_], List[int]], resolution_wh: Tuple[int, int]
rle: npt.NDArray[np.int_] | list[int], resolution_wh: tuple[int, int]
) -> npt.NDArray[np.bool_]:
"""
Converts run-length encoding (RLE) to a binary mask.
@@ -195,7 +195,7 @@ def rle_to_mask(
return decoded_rle.reshape((height, width), order="F")
def mask_to_rle(mask: npt.NDArray[np.bool_]) -> List[int]:
def mask_to_rle(mask: npt.NDArray[np.bool_]) -> list[int]:
"""
Converts a binary mask into a run-length encoding (RLE).
File diff suppressed because it is too large Load Diff
+19 -16
View File
@@ -1,8 +1,11 @@
from __future__ import annotations
import math
import warnings
from collections import Counter, defaultdict, deque
from collections.abc import Iterable
from functools import lru_cache
from typing import Any, Deque, Dict, Iterable, List, Literal, Optional, Tuple
from typing import Any, Literal
import cv2
import numpy as np
@@ -10,7 +13,7 @@ import numpy.typing as npt
from supervision.config import CLASS_NAME_DATA_FIELD
from supervision.detection.core import Detections
from supervision.detection.utils import cross_product
from supervision.detection.utils.internal import cross_product
from supervision.draw.color import Color
from supervision.draw.utils import draw_rectangle, draw_text
from supervision.geometry.core import Point, Position, Rect, Vector
@@ -99,7 +102,7 @@ class LineZone:
self.vector = Vector(start=start, end=end)
self.limits = self._calculate_region_of_interest_limits(vector=self.vector)
self.crossing_history_length = max(2, minimum_crossing_threshold + 1)
self.crossing_state_history: Dict[int, Deque[bool]] = defaultdict(
self.crossing_state_history: dict[int, deque[bool]] = defaultdict(
lambda: deque(maxlen=self.crossing_history_length)
)
self._in_count_per_class: Counter = Counter()
@@ -107,7 +110,7 @@ class LineZone:
self.triggering_anchors = triggering_anchors
if not list(self.triggering_anchors):
raise ValueError("Triggering anchors cannot be empty.")
self.class_id_to_name: Dict[int, str] = {}
self.class_id_to_name: dict[int, str] = {}
@property
def in_count(self) -> int:
@@ -118,14 +121,14 @@ class LineZone:
return sum(self._out_count_per_class.values())
@property
def in_count_per_class(self) -> Dict[int, int]:
def in_count_per_class(self) -> dict[int, int]:
return dict(self._in_count_per_class)
@property
def out_count_per_class(self) -> Dict[int, int]:
def out_count_per_class(self) -> dict[int, int]:
return dict(self._out_count_per_class)
def trigger(self, detections: Detections) -> Tuple[np.ndarray, np.ndarray]:
def trigger(self, detections: Detections) -> tuple[np.ndarray, np.ndarray]:
"""
Update the `in_count` and `out_count` based on the objects that cross the line.
@@ -160,7 +163,7 @@ class LineZone:
self._compute_anchor_sides(detections)
)
class_ids: List[Optional[int]] = (
class_ids: list[int | None] = (
list(detections.class_id)
if detections.class_id is not None
else [None] * len(detections)
@@ -200,7 +203,7 @@ class LineZone:
return crossed_in, crossed_out
@staticmethod
def _calculate_region_of_interest_limits(vector: Vector) -> Tuple[Vector, Vector]:
def _calculate_region_of_interest_limits(vector: Vector) -> tuple[Vector, Vector]:
magnitude = vector.magnitude
if magnitude == 0:
@@ -233,7 +236,7 @@ class LineZone:
def _compute_anchor_sides(
self, detections: Detections
) -> Tuple[npt.NDArray[np.bool_], npt.NDArray[np.bool_], npt.NDArray[np.bool_]]:
) -> tuple[npt.NDArray[np.bool_], npt.NDArray[np.bool_], npt.NDArray[np.bool_]]:
"""
Find if detections' anchors are within the limit of the line
zone and which anchors are on its left and right side.
@@ -324,8 +327,8 @@ class LineZoneAnnotator:
text_scale: float = 0.5,
text_offset: float = 1.5,
text_padding: int = 10,
custom_in_text: Optional[str] = None,
custom_out_text: Optional[str] = None,
custom_in_text: str | None = None,
custom_out_text: str | None = None,
display_in_count: bool = True,
display_out_count: bool = True,
display_text_box: bool = True,
@@ -471,7 +474,7 @@ class LineZoneAnnotator:
text_height: int,
is_in_count: bool,
label_dimension: int,
) -> Tuple[int, int]:
) -> tuple[int, int]:
"""
Calculate insertion anchor in frame to position the center of the count image.
@@ -663,7 +666,7 @@ class LineZoneAnnotator:
annotation = np.zeros((*annotation_shape, 3), dtype=np.uint8)
annotation_alpha = np.zeros((*annotation_shape, 1), dtype=np.uint8)
text_args: Dict[str, Any] = dict(
text_args: dict[str, Any] = dict(
text=text,
text_anchor=annotation_center,
text_scale=text_scale,
@@ -756,8 +759,8 @@ class LineZoneAnnotatorMulticlass:
def annotate(
self,
frame: np.ndarray,
line_zones: List[LineZone],
line_zone_labels: Optional[List[str]] = None,
line_zones: list[LineZone],
line_zone_labels: list[str] | None = None,
) -> np.ndarray:
"""
Draws a table with the number of objects of each class that crossed each line.
-269
View File
@@ -1,269 +0,0 @@
from __future__ import annotations
from enum import Enum
from typing import List, Union
import numpy as np
import numpy.typing as npt
from supervision.detection.utils import box_iou_batch, mask_iou_batch
def resize_masks(masks: np.ndarray, max_dimension: int = 640) -> np.ndarray:
"""
Resize all masks in the array to have a maximum dimension of max_dimension,
maintaining aspect ratio.
Args:
masks (np.ndarray): 3D array of binary masks with shape (N, H, W).
max_dimension (int): The maximum dimension for the resized masks.
Returns:
np.ndarray: Array of resized masks.
"""
max_height = np.max(masks.shape[1])
max_width = np.max(masks.shape[2])
scale = min(max_dimension / max_height, max_dimension / max_width)
new_height = int(scale * max_height)
new_width = int(scale * max_width)
x = np.linspace(0, max_width - 1, new_width).astype(int)
y = np.linspace(0, max_height - 1, new_height).astype(int)
xv, yv = np.meshgrid(x, y)
resized_masks = masks[:, yv, xv]
resized_masks = resized_masks.reshape(masks.shape[0], new_height, new_width)
return resized_masks
def mask_non_max_suppression(
predictions: np.ndarray,
masks: np.ndarray,
iou_threshold: float = 0.5,
mask_dimension: int = 640,
) -> np.ndarray:
"""
Perform Non-Maximum Suppression (NMS) on segmentation predictions.
Args:
predictions (np.ndarray): A 2D array of object detection predictions in
the format of `(x_min, y_min, x_max, y_max, score)`
or `(x_min, y_min, x_max, y_max, score, class)`. Shape: `(N, 5)` or
`(N, 6)`, where N is the number of predictions.
masks (np.ndarray): A 3D array of binary masks corresponding to the predictions.
Shape: `(N, H, W)`, where N is the number of predictions, and H, W are the
dimensions of each mask.
iou_threshold (float): The intersection-over-union threshold
to use for non-maximum suppression.
mask_dimension (int): The dimension to which the masks should be
resized before computing IOU values. Defaults to 640.
Returns:
np.ndarray: A boolean array indicating which predictions to keep after
non-maximum suppression.
Raises:
AssertionError: If `iou_threshold` is not within the closed
range from `0` to `1`.
"""
assert 0 <= iou_threshold <= 1, (
"Value of `iou_threshold` must be in the closed range from 0 to 1, "
f"{iou_threshold} given."
)
rows, columns = predictions.shape
if columns == 5:
predictions = np.c_[predictions, np.zeros(rows)]
sort_index = predictions[:, 4].argsort()[::-1]
predictions = predictions[sort_index]
masks = masks[sort_index]
masks_resized = resize_masks(masks, mask_dimension)
ious = mask_iou_batch(masks_resized, masks_resized)
categories = predictions[:, 5]
keep = np.ones(rows, dtype=bool)
for i in range(rows):
if keep[i]:
condition = (ious[i] > iou_threshold) & (categories[i] == categories)
keep[i + 1 :] = np.where(condition[i + 1 :], False, keep[i + 1 :])
return keep[sort_index.argsort()]
def box_non_max_suppression(
predictions: np.ndarray, iou_threshold: float = 0.5
) -> np.ndarray:
"""
Perform Non-Maximum Suppression (NMS) on object detection predictions.
Args:
predictions (np.ndarray): An array of object detection predictions in
the format of `(x_min, y_min, x_max, y_max, score)`
or `(x_min, y_min, x_max, y_max, score, class)`.
iou_threshold (float): The intersection-over-union threshold
to use for non-maximum suppression.
Returns:
np.ndarray: A boolean array indicating which predictions to keep after n
on-maximum suppression.
Raises:
AssertionError: If `iou_threshold` is not within the
closed range from `0` to `1`.
"""
assert 0 <= iou_threshold <= 1, (
"Value of `iou_threshold` must be in the closed range from 0 to 1, "
f"{iou_threshold} given."
)
rows, columns = predictions.shape
# add column #5 - category filled with zeros for agnostic nms
if columns == 5:
predictions = np.c_[predictions, np.zeros(rows)]
# sort predictions column #4 - score
sort_index = np.flip(predictions[:, 4].argsort())
predictions = predictions[sort_index]
boxes = predictions[:, :4]
categories = predictions[:, 5]
ious = box_iou_batch(boxes, boxes)
ious = ious - np.eye(rows)
keep = np.ones(rows, dtype=bool)
for index, (iou, category) in enumerate(zip(ious, categories)):
if not keep[index]:
continue
# drop detections with iou > iou_threshold and
# same category as current detections
condition = (iou > iou_threshold) & (categories == category)
keep = keep & ~condition
return keep[sort_index.argsort()]
def group_overlapping_boxes(
predictions: npt.NDArray[np.float64], iou_threshold: float = 0.5
) -> List[List[int]]:
"""
Apply greedy version of non-maximum merging to avoid detecting too many
overlapping bounding boxes for a given object.
Args:
predictions (npt.NDArray[np.float64]): An array of shape `(n, 5)` containing
the bounding boxes coordinates in format `[x1, y1, x2, y2]`
and the confidence scores.
iou_threshold (float): The intersection-over-union threshold
to use for non-maximum suppression. Defaults to 0.5.
Returns:
List[List[int]]: Groups of prediction indices be merged.
Each group may have 1 or more elements.
"""
merge_groups: List[List[int]] = []
scores = predictions[:, 4]
order = scores.argsort()
while len(order) > 0:
idx = int(order[-1])
order = order[:-1]
if len(order) == 0:
merge_groups.append([idx])
break
merge_candidate = np.expand_dims(predictions[idx], axis=0)
ious = box_iou_batch(predictions[order][:, :4], merge_candidate[:, :4])
ious = ious.flatten()
above_threshold = ious >= iou_threshold
merge_group = [idx, *np.flip(order[above_threshold]).tolist()]
merge_groups.append(merge_group)
order = order[~above_threshold]
return merge_groups
def box_non_max_merge(
predictions: npt.NDArray[np.float64],
iou_threshold: float = 0.5,
) -> List[List[int]]:
"""
Apply greedy version of non-maximum merging per category to avoid detecting
too many overlapping bounding boxes for a given object.
Args:
predictions (npt.NDArray[np.float64]): An array of shape `(n, 5)` or `(n, 6)`
containing the bounding boxes coordinates in format `[x1, y1, x2, y2]`,
the confidence scores and class_ids. Omit class_id column to allow
detections of different classes to be merged.
iou_threshold (float): The intersection-over-union threshold
to use for non-maximum suppression. Defaults to 0.5.
Returns:
List[List[int]]: Groups of prediction indices be merged.
Each group may have 1 or more elements.
"""
if predictions.shape[1] == 5:
return group_overlapping_boxes(predictions, iou_threshold)
category_ids = predictions[:, 5]
merge_groups = []
for category_id in np.unique(category_ids):
curr_indices = np.where(category_ids == category_id)[0]
merge_class_groups = group_overlapping_boxes(
predictions[curr_indices], iou_threshold
)
for merge_class_group in merge_class_groups:
merge_groups.append(curr_indices[merge_class_group].tolist())
for merge_group in merge_groups:
if len(merge_group) == 0:
raise ValueError(
f"Empty group detected when non-max-merging detections: {merge_groups}"
)
return merge_groups
class OverlapFilter(Enum):
"""
Enum specifying the strategy for filtering overlapping detections.
Attributes:
NONE: Do not filter detections based on overlap.
NON_MAX_SUPPRESSION: Filter detections using non-max suppression. This means,
detections that overlap by more than a set threshold will be discarded,
except for the one with the highest confidence.
NON_MAX_MERGE: Merge detections with non-max merging. This means,
detections that overlap by more than a set threshold will be merged
into a single detection.
"""
NONE = "none"
NON_MAX_SUPPRESSION = "non_max_suppression"
NON_MAX_MERGE = "non_max_merge"
@classmethod
def list(cls):
return list(map(lambda c: c.value, cls))
@classmethod
def from_value(cls, value: Union[OverlapFilter, str]) -> OverlapFilter:
if isinstance(value, cls):
return value
if isinstance(value, str):
value = value.lower()
try:
return cls(value)
except ValueError:
raise ValueError(f"Invalid value: {value}. Must be one of {cls.list()}")
raise ValueError(
f"Invalid value type: {type(value)}. Must be an instance of "
f"{cls.__name__} or str."
)
+11 -11
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
import csv
import os
from typing import Any, Dict, List, Optional
from typing import Any
from supervision.detection.core import Detections
@@ -61,8 +61,8 @@ class CSVSink:
None
"""
self.file_name = file_name
self.file: Optional[open] = None
self.writer: Optional[csv.writer] = None
self.file: open | None = None
self.writer: csv.writer | None = None
self.header_written = False
self.field_names = []
@@ -72,9 +72,9 @@ class CSVSink:
def __exit__(
self,
exc_type: Optional[type],
exc_val: Optional[Exception],
exc_tb: Optional[Any],
exc_type: type | None,
exc_val: Exception | None,
exc_tb: Any | None,
) -> None:
self.close()
@@ -104,8 +104,8 @@ class CSVSink:
@staticmethod
def parse_detection_data(
detections: Detections, custom_data: Optional[Dict[str, Any]] = None
) -> List[Dict[str, Any]]:
detections: Detections, custom_data: dict[str, Any] | None = None
) -> list[dict[str, Any]]:
parsed_rows = []
for i in range(len(detections.xyxy)):
row = {
@@ -137,7 +137,7 @@ class CSVSink:
return parsed_rows
def append(
self, detections: Detections, custom_data: Optional[Dict[str, Any]] = None
self, detections: Detections, custom_data: dict[str, Any] | None = None
) -> None:
"""
Append detection data to the CSV file.
@@ -173,8 +173,8 @@ class CSVSink:
@staticmethod
def parse_field_names(
detections: Detections, custom_data: Dict[str, Any]
) -> List[str]:
detections: Detections, custom_data: dict[str, Any]
) -> list[str]:
dynamic_header = sorted(
set(custom_data.keys()) | set(getattr(detections, "data", {}).keys())
)
+223 -170
View File
@@ -1,24 +1,25 @@
from __future__ import annotations
import warnings
from collections.abc import Callable
from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import Callable, Optional, Tuple, Union
import numpy as np
from supervision.config import ORIENTED_BOX_COORDINATES
from supervision.detection.core import Detections
from supervision.detection.overlap_filter import OverlapFilter
from supervision.detection.utils import move_boxes, move_masks, move_oriented_boxes
from supervision.utils.image import crop_image
from supervision.utils.internal import (
SupervisionWarnings,
warn_deprecated,
)
from supervision.detection.utils.boxes import move_boxes, move_oriented_boxes
from supervision.detection.utils.iou_and_nms import OverlapFilter, OverlapMetric
from supervision.detection.utils.masks import move_masks
from supervision.draw.base import ImageType
from supervision.utils.image import crop_image, get_image_resolution_wh
from supervision.utils.internal import SupervisionWarnings
def move_detections(
detections: Detections,
offset: np.ndarray,
resolution_wh: Optional[Tuple[int, int]] = None,
resolution_wh: tuple[int, int] | None = None,
) -> Detections:
"""
Args:
@@ -50,107 +51,106 @@ def move_detections(
class InferenceSlicer:
"""
InferenceSlicer performs slicing-based inference for small target detection. This
method, often referred to as
[Slicing Adaptive Inference (SAHI)](https://ieeexplore.ieee.org/document/9897990),
involves dividing a larger image into smaller slices, performing inference on each
slice, and then merging the detections.
Perform tiled inference on large images by slicing them into overlapping patches.
This class divides an input image into overlapping slices of configurable size
and overlap, runs inference on each slice through a user-provided callback, and
merges the resulting detections. The slicing process allows efficient processing
of large images with limited resources while preserving detection accuracy via
configurable overlap and post-processing of overlaps. Uses multi-threading for
parallel slice inference.
Args:
slice_wh (Tuple[int, int]): Dimensions of each slice measured in pixels. The
tuple should be in the format `(width, height)`.
overlap_ratio_wh (Optional[Tuple[float, float]]): [⚠️ Deprecated: please set
to `None` and use `overlap_wh`] A tuple representing the
desired overlap ratio for width and height between consecutive slices.
Each value should be in the range [0, 1), where 0 means no overlap and
a value close to 1 means high overlap.
overlap_wh (Optional[Tuple[int, int]]): A tuple representing the desired
overlap for width and height between consecutive slices measured in pixels.
Each value should be greater than or equal to 0. Takes precedence over
`overlap_ratio_wh`.
overlap_filter (Union[OverlapFilter, str]): Strategy for
filtering or merging overlapping detections in slices.
iou_threshold (float): Intersection over Union (IoU) threshold
used when filtering by overlap.
callback (Callable): A function that performs inference on a given image
slice and returns detections.
thread_workers (int): Number of threads for parallel execution.
callback (Callable[[ImageType], Detections]): Inference function that takes
a sliced image and returns a `Detections` object.
slice_wh (int or tuple[int, int]): Size of each slice `(width, height)`.
If int, both width and height are set to this value.
overlap_wh (int or tuple[int, int]): Overlap size `(width, height)` between
slices. If int, both width and height are set to this value.
overlap_filter (OverlapFilter or str): Strategy to merge overlapping
detections (`NON_MAX_SUPPRESSION`, `NON_MAX_MERGE`, or `NONE`).
iou_threshold (float): IOU threshold used in merging overlap filtering.
overlap_metric (OverlapMetric or str): Metric to compute overlap
(`IOU` or `IOS`).
thread_workers (int): Number of threads for concurrent slice inference.
Note:
The class ensures that slices do not exceed the boundaries of the original
image. As a result, the final slices in the row and column dimensions might be
smaller than the specified slice dimensions if the image's width or height is
not a multiple of the slice's width or height minus the overlap.
Raises:
ValueError: If `slice_wh` or `overlap_wh` are invalid or inconsistent.
Example:
```python
import cv2
import supervision as sv
from rfdetr import RFDETRMedium
model = RFDETRMedium()
def callback(tile):
return model.predict(tile)
slicer = sv.InferenceSlicer(callback, slice_wh=640, overlap_wh=100)
image = cv2.imread("example.png")
detections = slicer(image)
```
```python
import supervision as sv
from PIL import Image
from ultralytics import YOLO
model = YOLO("yolo11m.pt")
def callback(tile):
results = model(tile)[0]
return sv.Detections.from_ultralytics(results)
slicer = sv.InferenceSlicer(callback, slice_wh=640, overlap_wh=100)
image = Image.open("example.png")
detections = slicer(image)
```
"""
def __init__(
self,
callback: Callable[[np.ndarray], Detections],
slice_wh: Tuple[int, int] = (320, 320),
overlap_ratio_wh: Optional[Tuple[float, float]] = (0.2, 0.2),
overlap_wh: Optional[Tuple[int, int]] = None,
overlap_filter: Union[OverlapFilter, str] = OverlapFilter.NON_MAX_SUPPRESSION,
callback: Callable[[ImageType], Detections],
slice_wh: int | tuple[int, int] = 640,
overlap_wh: int | tuple[int, int] = 100,
overlap_filter: OverlapFilter | str = OverlapFilter.NON_MAX_SUPPRESSION,
iou_threshold: float = 0.5,
overlap_metric: OverlapMetric | str = OverlapMetric.IOU,
thread_workers: int = 1,
):
if overlap_ratio_wh is not None:
warn_deprecated(
"`overlap_ratio_wh` in `InferenceSlicer.__init__` is deprecated and "
"will be removed in `supervision-0.27.0`. Please manually set it to "
"`None` and use `overlap_wh` instead."
)
slice_wh_norm = self._normalize_slice_wh(slice_wh)
overlap_wh_norm = self._normalize_overlap_wh(overlap_wh)
self._validate_overlap(overlap_ratio_wh, overlap_wh)
self.overlap_ratio_wh = overlap_ratio_wh
self.overlap_wh = overlap_wh
self._validate_overlap(slice_wh=slice_wh_norm, overlap_wh=overlap_wh_norm)
self.slice_wh = slice_wh
self.slice_wh = slice_wh_norm
self.overlap_wh = overlap_wh_norm
self.iou_threshold = iou_threshold
self.overlap_metric = OverlapMetric.from_value(overlap_metric)
self.overlap_filter = OverlapFilter.from_value(overlap_filter)
self.callback = callback
self.thread_workers = thread_workers
def __call__(self, image: np.ndarray) -> Detections:
def __call__(self, image: ImageType) -> Detections:
"""
Performs slicing-based inference on the provided image using the specified
callback.
Perform tiled inference on the full image and return merged detections.
Args:
image (np.ndarray): The input image on which inference needs to be
performed. The image should be in the format
`(height, width, channels)`.
image (ImageType): The full image to run inference on.
Returns:
Detections: A collection of detections for the entire image after merging
results from all slices and applying NMS.
Example:
```python
import cv2
import supervision as sv
from ultralytics import YOLO
image = cv2.imread(SOURCE_IMAGE_PATH)
model = YOLO(...)
def callback(image_slice: np.ndarray) -> sv.Detections:
result = model(image_slice)[0]
return sv.Detections.from_ultralytics(result)
slicer = sv.InferenceSlicer(
callback=callback,
overlap_filter_strategy=sv.OverlapFilter.NON_MAX_SUPPRESSION,
)
detections = slicer(image)
```
Detections: Merged detections across all slices.
"""
detections_list = []
resolution_wh = (image.shape[1], image.shape[0])
detections_list: list[Detections] = []
resolution_wh = get_image_resolution_wh(image)
offsets = self._generate_offset(
resolution_wh=resolution_wh,
slice_wh=self.slice_wh,
overlap_ratio_wh=self.overlap_ratio_wh,
overlap_wh=self.overlap_wh,
)
@@ -164,125 +164,178 @@ class InferenceSlicer:
merged = Detections.merge(detections_list=detections_list)
if self.overlap_filter == OverlapFilter.NONE:
return merged
elif self.overlap_filter == OverlapFilter.NON_MAX_SUPPRESSION:
return merged.with_nms(threshold=self.iou_threshold)
elif self.overlap_filter == OverlapFilter.NON_MAX_MERGE:
return merged.with_nmm(threshold=self.iou_threshold)
else:
warnings.warn(
f"Invalid overlap filter strategy: {self.overlap_filter}",
category=SupervisionWarnings,
if self.overlap_filter == OverlapFilter.NON_MAX_SUPPRESSION:
return merged.with_nms(
threshold=self.iou_threshold,
overlap_metric=self.overlap_metric,
)
if self.overlap_filter == OverlapFilter.NON_MAX_MERGE:
return merged.with_nmm(
threshold=self.iou_threshold,
overlap_metric=self.overlap_metric,
)
return merged
def _run_callback(self, image, offset) -> Detections:
warnings.warn(
f"Invalid overlap filter strategy: {self.overlap_filter}",
category=SupervisionWarnings,
)
return merged
def _run_callback(self, image: ImageType, offset: np.ndarray) -> Detections:
"""
Run the provided callback on a slice of an image.
Run detection callback on a sliced portion of the image and adjust coordinates.
Args:
image (np.ndarray): The input image on which inference needs to run
offset (np.ndarray): An array of shape `(4,)` containing coordinates
for the slice.
image (ImageType): The full image.
offset (numpy.ndarray): Coordinates `(x_min, y_min, x_max, y_max)` defining
the slice region.
Returns:
Detections: A collection of detections for the slice.
Detections: Detections adjusted to the full image coordinate system.
"""
image_slice = crop_image(image=image, xyxy=offset)
image_slice: ImageType = crop_image(image=image, xyxy=offset)
detections = self.callback(image_slice)
resolution_wh = (image.shape[1], image.shape[0])
detections = move_detections(
detections=detections, offset=offset[:2], resolution_wh=resolution_wh
)
resolution_wh = get_image_resolution_wh(image)
detections = move_detections(
detections=detections,
offset=offset[:2],
resolution_wh=resolution_wh,
)
return detections
@staticmethod
def _normalize_slice_wh(
slice_wh: int | tuple[int, int],
) -> tuple[int, int]:
if isinstance(slice_wh, int):
if slice_wh <= 0:
raise ValueError(
f"`slice_wh` must be a positive integer. Received: {slice_wh}"
)
return slice_wh, slice_wh
if isinstance(slice_wh, tuple) and len(slice_wh) == 2:
width, height = slice_wh
if width <= 0 or height <= 0:
raise ValueError(
f"`slice_wh` values must be positive. Received: {slice_wh}"
)
return width, height
raise ValueError(
"`slice_wh` must be an int or a tuple of two positive integers "
"(slice_w, slice_h). "
f"Received: {slice_wh}"
)
@staticmethod
def _normalize_overlap_wh(
overlap_wh: int | tuple[int, int],
) -> tuple[int, int]:
if isinstance(overlap_wh, int):
if overlap_wh < 0:
raise ValueError(
"`overlap_wh` must be a non negative integer. "
f"Received: {overlap_wh}"
)
return overlap_wh, overlap_wh
if isinstance(overlap_wh, tuple) and len(overlap_wh) == 2:
overlap_w, overlap_h = overlap_wh
if overlap_w < 0 or overlap_h < 0:
raise ValueError(
f"`overlap_wh` values must be non negative. Received: {overlap_wh}"
)
return overlap_w, overlap_h
raise ValueError(
"`overlap_wh` must be an int or a tuple of two non negative integers "
"(overlap_w, overlap_h). "
f"Received: {overlap_wh}"
)
@staticmethod
def _generate_offset(
resolution_wh: Tuple[int, int],
slice_wh: Tuple[int, int],
overlap_ratio_wh: Optional[Tuple[float, float]],
overlap_wh: Optional[Tuple[int, int]],
resolution_wh: tuple[int, int],
slice_wh: tuple[int, int],
overlap_wh: tuple[int, int],
) -> np.ndarray:
"""
Generate offset coordinates for slicing an image based on the given resolution,
slice dimensions, and overlap ratios.
Generate bounding boxes defining the coordinates of image slices with overlap.
Args:
resolution_wh (Tuple[int, int]): A tuple representing the width and height
of the image to be sliced.
slice_wh (Tuple[int, int]): Dimensions of each slice measured in pixels. The
tuple should be in the format `(width, height)`.
overlap_ratio_wh (Optional[Tuple[float, float]]): A tuple representing the
desired overlap ratio for width and height between consecutive slices.
Each value should be in the range [0, 1), where 0 means no overlap and
a value close to 1 means high overlap.
overlap_wh (Optional[Tuple[int, int]]): A tuple representing the desired
overlap for width and height between consecutive slices measured in
pixels. Each value should be greater than or equal to 0.
resolution_wh (tuple[int, int]): Image resolution `(width, height)`.
slice_wh (tuple[int, int]): Size of each slice `(width, height)`.
overlap_wh (tuple[int, int]): Overlap size between slices `(width, height)`.
Returns:
np.ndarray: An array of shape `(n, 4)` containing coordinates for each
slice in the format `[xmin, ymin, xmax, ymax]`.
Note:
The function ensures that slices do not exceed the boundaries of the
original image. As a result, the final slices in the row and column
dimensions might be smaller than the specified slice dimensions if the
image's width or height is not a multiple of the slice's width or
height minus the overlap.
numpy.ndarray: Array of shape `(num_slices, 4)` with each row as
`(x_min, y_min, x_max, y_max)` coordinates for a slice.
"""
slice_width, slice_height = slice_wh
image_width, image_height = resolution_wh
overlap_width = (
overlap_wh[0]
if overlap_wh is not None
else int(overlap_ratio_wh[0] * slice_width)
overlap_width, overlap_height = overlap_wh
stride_x = slice_width - overlap_width
stride_y = slice_height - overlap_height
def _compute_axis_starts(
image_size: int,
slice_size: int,
stride: int,
) -> list[int]:
if image_size <= slice_size:
return [0]
if stride == slice_size:
return np.arange(0, image_size, stride).tolist()
last_start = image_size - slice_size
starts = np.arange(0, last_start, stride).tolist()
if not starts or starts[-1] != last_start:
starts.append(last_start)
return starts
x_starts = _compute_axis_starts(
image_size=image_width,
slice_size=slice_width,
stride=stride_x,
)
overlap_height = (
overlap_wh[1]
if overlap_wh is not None
else int(overlap_ratio_wh[1] * slice_height)
y_starts = _compute_axis_starts(
image_size=image_height,
slice_size=slice_height,
stride=stride_y,
)
width_stride = slice_width - overlap_width
height_stride = slice_height - overlap_height
x_min, y_min = np.meshgrid(x_starts, y_starts)
x_max = np.clip(x_min + slice_width, 0, image_width)
y_max = np.clip(y_min + slice_height, 0, image_height)
ws = np.arange(0, image_width, width_stride)
hs = np.arange(0, image_height, height_stride)
xmin, ymin = np.meshgrid(ws, hs)
xmax = np.clip(xmin + slice_width, 0, image_width)
ymax = np.clip(ymin + slice_height, 0, image_height)
offsets = np.stack([xmin, ymin, xmax, ymax], axis=-1).reshape(-1, 4)
offsets = np.stack(
[x_min, y_min, x_max, y_max],
axis=-1,
).reshape(-1, 4)
return offsets
@staticmethod
def _validate_overlap(
overlap_ratio_wh: Optional[Tuple[float, float]],
overlap_wh: Optional[Tuple[int, int]],
slice_wh: tuple[int, int],
overlap_wh: tuple[int, int],
) -> None:
if overlap_ratio_wh is not None and overlap_wh is not None:
overlap_w, overlap_h = overlap_wh
slice_w, slice_h = slice_wh
if overlap_w < 0 or overlap_h < 0:
raise ValueError(
"Both `overlap_ratio_wh` and `overlap_wh` cannot be provided. "
"Please provide only one of them."
)
if overlap_ratio_wh is None and overlap_wh is None:
raise ValueError(
"Either `overlap_ratio_wh` or `overlap_wh` must be provided. "
"Please provide one of them."
"Overlap values must be greater than or equal to 0. "
f"Received: {overlap_wh}"
)
if overlap_ratio_wh is not None:
if not (0 <= overlap_ratio_wh[0] < 1 and 0 <= overlap_ratio_wh[1] < 1):
raise ValueError(
"Overlap ratios must be in the range [0, 1). "
f"Received: {overlap_ratio_wh}"
)
if overlap_wh is not None:
if not (overlap_wh[0] >= 0 and overlap_wh[1] >= 0):
raise ValueError(
"Overlap values must be greater than or equal to 0. "
f"Received: {overlap_wh}"
)
if overlap_w >= slice_w or overlap_h >= slice_h:
raise ValueError(
"`overlap_wh` must be smaller than `slice_wh` in both dimensions "
f"to keep a positive stride. Received overlap_wh={overlap_wh}, "
f"slice_wh={slice_wh}."
)
+9 -9
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
import json
import os
from typing import Any, Dict, List, Optional
from typing import Any
from supervision.detection.core import Detections
@@ -51,8 +51,8 @@ class JSONSink:
None
"""
self.file_name = file_name
self.file: Optional[open] = None
self.data: List[Dict[str, Any]] = []
self.file: open | None = None
self.data: list[dict[str, Any]] = []
def __enter__(self) -> JSONSink:
self.open()
@@ -60,9 +60,9 @@ class JSONSink:
def __exit__(
self,
exc_type: Optional[type],
exc_val: Optional[Exception],
exc_tb: Optional[Any],
exc_type: type | None,
exc_val: Exception | None,
exc_tb: Any | None,
) -> None:
self.write_and_close()
@@ -92,8 +92,8 @@ class JSONSink:
@staticmethod
def parse_detection_data(
detections: Detections, custom_data: Optional[Dict[str, Any]] = None
) -> List[Dict[str, Any]]:
detections: Detections, custom_data: dict[str, Any] | None = None
) -> list[dict[str, Any]]:
parsed_rows = []
for i in range(len(detections.xyxy)):
row = {
@@ -126,7 +126,7 @@ class JSONSink:
return parsed_rows
def append(
self, detections: Detections, custom_data: Optional[Dict[str, Any]] = None
self, detections: Detections, custom_data: dict[str, Any] | None = None
) -> None:
"""
Append detection data to the JSON file.
+6 -3
View File
@@ -1,12 +1,15 @@
from __future__ import annotations
from collections.abc import Iterable
from dataclasses import replace
from typing import Iterable, Optional
import cv2
import numpy as np
import numpy.typing as npt
from supervision import Detections
from supervision.detection.utils import clip_boxes, polygon_to_mask
from supervision.detection.utils.boxes import clip_boxes
from supervision.detection.utils.converters import polygon_to_mask
from supervision.draw.color import Color
from supervision.draw.utils import draw_filled_polygon, draw_polygon, draw_text
from supervision.geometry.core import Position
@@ -153,7 +156,7 @@ class PolygonZoneAnnotator:
self.display_in_zone_count = display_in_zone_count
self.opacity = opacity
def annotate(self, scene: np.ndarray, label: Optional[str] = None) -> np.ndarray:
def annotate(self, scene: np.ndarray, label: str | None = None) -> np.ndarray:
"""
Annotates the polygon zone within a frame with a count of detected objects.
+3 -2
View File
@@ -1,7 +1,8 @@
from __future__ import annotations
import warnings
from collections import defaultdict, deque
from copy import deepcopy
from typing import Optional
import numpy as np
@@ -95,7 +96,7 @@ class DetectionsSmoother:
return self.get_smoothed_detections()
def get_track(self, track_id: int) -> Optional[Detections]:
def get_track(self, track_id: int) -> Detections | None:
track = self.tracks.get(track_id, None)
if track is None:
return None
+13 -11
View File
@@ -1,15 +1,17 @@
from __future__ import annotations
import io
from typing import Any, Dict, Optional
from typing import Any
import numpy as np
from PIL import Image
from supervision.config import CLASS_NAME_DATA_FIELD
from supervision.detection.utils import mask_to_xyxy
from supervision.detection.utils.converters import mask_to_xyxy
def process_transformers_detection_result(
detection_result: dict, id2label: Optional[Dict[int, str]]
detection_result: dict, id2label: dict[int, str] | None
) -> dict:
"""
Process the result of Transformers object detection functions such as
@@ -38,7 +40,7 @@ def process_transformers_detection_result(
def process_transformers_v4_segmentation_result(
segmentation_result: dict, id2label: Optional[Dict[int, str]]
segmentation_result: dict, id2label: dict[int, str] | None
) -> dict:
"""
Process the result of Transformers segmentation functions such as
@@ -77,7 +79,7 @@ def process_transformers_v4_segmentation_result(
def process_transformers_v5_segmentation_result(
segmentation_result: dict, id2label: Optional[Dict[int, str]]
segmentation_result: dict, id2label: dict[int, str] | None
) -> dict:
"""
Process the result of Transformers segmentation functions such as
@@ -107,7 +109,7 @@ def process_transformers_v5_segmentation_result(
def process_transformers_v5_semantic_or_instance_segmentation_result(
segmentation_result: dict, id2label: Optional[Dict[int, str]]
segmentation_result: dict, id2label: dict[int, str] | None
) -> dict:
"""
Process the result of Transformers segmentation functions such as
@@ -143,7 +145,7 @@ def process_transformers_v5_semantic_or_instance_segmentation_result(
def process_transformers_v4_panoptic_segmentation_result(
segmentation_result: dict, id2label: Optional[Dict[int, str]]
segmentation_result: dict, id2label: dict[int, str] | None
) -> dict:
"""
Process the result of the Transformers function `post_process_panoptic` (v4).
@@ -177,7 +179,7 @@ def process_transformers_v4_panoptic_segmentation_result(
def process_transformers_v5_panoptic_segmentation_result(
segmentation_array: np.ndarray, id2label: Optional[Dict[int, str]]
segmentation_array: np.ndarray, id2label: dict[int, str] | None
) -> dict:
"""
Process the result of the Transformers function
@@ -220,9 +222,9 @@ def png_string_to_segmentation_array(png_string: bytes) -> np.ndarray:
def append_class_names_to_data(
class_ids: np.ndarray,
id2label: Optional[Dict[int, str]],
data: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
id2label: dict[int, str] | None,
data: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""
Helper function to create or append to a data dictionary with class names if
available.
File diff suppressed because it is too large Load Diff
+343
View File
@@ -0,0 +1,343 @@
from __future__ import annotations
import numpy as np
import numpy.typing as npt
from supervision.detection.utils.iou_and_nms import box_iou_batch
def clip_boxes(xyxy: np.ndarray, resolution_wh: tuple[int, int]) -> np.ndarray:
"""
Clips bounding boxes coordinates to fit within the frame resolution.
Args:
xyxy (np.ndarray): A numpy array of shape `(N, 4)` where each
row corresponds to a bounding box in
the format `(x_min, y_min, x_max, y_max)`.
resolution_wh (Tuple[int, int]): A tuple of the form
`(width, height)` representing the resolution of the frame.
Returns:
np.ndarray: A numpy array of shape `(N, 4)` where each row
corresponds to a bounding box with coordinates clipped to fit
within the frame resolution.
Examples:
```python
import numpy as np
import supervision as sv
xyxy = np.array([
[10, 20, 300, 200],
[15, 25, 350, 450],
[-10, -20, 30, 40]
])
sv.clip_boxes(xyxy=xyxy, resolution_wh=(320, 240))
# array([
# [ 10, 20, 300, 200],
# [ 15, 25, 320, 240],
# [ 0, 0, 30, 40]
# ])
```
"""
result = np.copy(xyxy)
width, height = resolution_wh
result[:, [0, 2]] = result[:, [0, 2]].clip(0, width)
result[:, [1, 3]] = result[:, [1, 3]].clip(0, height)
return result
def pad_boxes(xyxy: np.ndarray, px: int, py: int | None = None) -> np.ndarray:
"""
Pads bounding boxes coordinates with a constant padding.
Args:
xyxy (np.ndarray): A numpy array of shape `(N, 4)` where each
row corresponds to a bounding box in the format
`(x_min, y_min, x_max, y_max)`.
px (int): The padding value to be added to both the left and right sides of
each bounding box.
py (Optional[int]): The padding value to be added to both the top and bottom
sides of each bounding box. If not provided, `px` will be used for both
dimensions.
Returns:
np.ndarray: A numpy array of shape `(N, 4)` where each row corresponds to a
bounding box with coordinates padded according to the provided padding
values.
Examples:
```python
import numpy as np
import supervision as sv
xyxy = np.array([
[10, 20, 30, 40],
[15, 25, 35, 45]
])
sv.pad_boxes(xyxy=xyxy, px=5, py=10)
# array([
# [ 5, 10, 35, 50],
# [10, 15, 40, 55]
# ])
```
"""
if py is None:
py = px
result = xyxy.copy()
result[:, [0, 1]] -= [px, py]
result[:, [2, 3]] += [px, py]
return result
def denormalize_boxes(
xyxy: np.ndarray,
resolution_wh: tuple[int, int],
normalization_factor: float = 1.0,
) -> np.ndarray:
"""
Convert normalized bounding box coordinates to absolute pixel coordinates.
Multiplies each bounding box coordinate by image size and divides by
`normalization_factor`, mapping values from normalized `[0, normalization_factor]`
to absolute pixel values for a given resolution.
Args:
xyxy (`numpy.ndarray`): Normalized bounding boxes of shape `(N, 4)`,
where each row is `(x_min, y_min, x_max, y_max)`, values in
`[0, normalization_factor]`.
resolution_wh (`tuple[int, int]`): Target image resolution as `(width, height)`.
normalization_factor (`float`): Maximum value of input coordinate range.
Defaults to `1.0`.
Returns:
(`numpy.ndarray`): Array of shape `(N, 4)` with absolute coordinates in
`(x_min, y_min, x_max, y_max)` format.
Examples:
```python
import numpy as np
import supervision as sv
xyxy = np.array([
[0.1, 0.2, 0.5, 0.6],
[0.3, 0.4, 0.7, 0.8],
[0.2, 0.1, 0.6, 0.5]
])
sv.denormalize_boxes(xyxy, (1280, 720))
# array([
# [128., 144., 640., 432.],
# [384., 288., 896., 576.],
# [256., 72., 768., 360.]
# ])
```
```
import numpy as np
import supervision as sv
xyxy = np.array([
[256., 128., 768., 640.]
])
sv.denormalize_boxes(xyxy, (1280, 720), normalization_factor=1024.0)
# array([
# [320., 90., 960., 450.]
# ])
```
"""
width, height = resolution_wh
result = xyxy.copy()
result[:, [0, 2]] = (result[:, [0, 2]] * width) / normalization_factor
result[:, [1, 3]] = (result[:, [1, 3]] * height) / normalization_factor
return result
def move_boxes(
xyxy: npt.NDArray[np.float64], offset: npt.NDArray[np.int32]
) -> npt.NDArray[np.float64]:
"""
Parameters:
xyxy (npt.NDArray[np.float64]): An array of shape `(n, 4)` containing the
bounding boxes coordinates in format `[x1, y1, x2, y2]`
offset (np.array): An array of shape `(2,)` containing offset values in format
is `[dx, dy]`.
Returns:
npt.NDArray[np.float64]: Repositioned bounding boxes.
Examples:
```python
import numpy as np
import supervision as sv
xyxy = np.array([
[10, 10, 20, 20],
[30, 30, 40, 40]
])
offset = np.array([5, 5])
sv.move_boxes(xyxy=xyxy, offset=offset)
# array([
# [15, 15, 25, 25],
# [35, 35, 45, 45]
# ])
```
"""
return xyxy + np.hstack([offset, offset])
def move_oriented_boxes(
xyxyxyxy: npt.NDArray[np.float64], offset: npt.NDArray[np.int32]
) -> npt.NDArray[np.float64]:
"""
Parameters:
xyxyxyxy (npt.NDArray[np.float64]): An array of shape `(n, 4, 2)` containing the
oriented bounding boxes coordinates in format
`[[x1, y1], [x2, y2], [x3, y3], [x3, y3]]`
offset (np.array): An array of shape `(2,)` containing offset values in format
is `[dx, dy]`.
Returns:
npt.NDArray[np.float64]: Repositioned bounding boxes.
Examples:
```python
import numpy as np
import supervision as sv
xyxyxyxy = np.array([
[
[20, 10],
[10, 20],
[20, 30],
[30, 20]
],
[
[30 ,30],
[20, 40],
[30, 50],
[40, 40]
]
])
offset = np.array([5, 5])
sv.move_oriented_boxes(xyxy=xyxy, offset=offset)
# array([
# [
# [25, 15],
# [15, 25],
# [25, 35],
# [35, 25]
# ],
# [
# [35, 35],
# [25, 45],
# [35, 55],
# [45, 45]
# ]
# ])
```
"""
return xyxyxyxy + offset
def scale_boxes(
xyxy: npt.NDArray[np.float64], factor: float
) -> npt.NDArray[np.float64]:
"""
Scale the dimensions of bounding boxes.
Parameters:
xyxy (npt.NDArray[np.float64]): An array of shape `(n, 4)` containing the
bounding boxes coordinates in format `[x1, y1, x2, y2]`
factor (float): A float value representing the factor by which the box
dimensions are scaled. A factor greater than 1 enlarges the boxes, while a
factor less than 1 shrinks them.
Returns:
npt.NDArray[np.float64]: Scaled bounding boxes.
Examples:
```python
import numpy as np
import supervision as sv
xyxy = np.array([
[10, 10, 20, 20],
[30, 30, 40, 40]
])
sv.scale_boxes(xyxy=xyxy, factor=1.5)
# array([
# [ 7.5, 7.5, 22.5, 22.5],
# [27.5, 27.5, 42.5, 42.5]
# ])
```
"""
centers = (xyxy[:, :2] + xyxy[:, 2:]) / 2
new_sizes = (xyxy[:, 2:] - xyxy[:, :2]) * factor
return np.concatenate((centers - new_sizes / 2, centers + new_sizes / 2), axis=1)
def spread_out_boxes(
xyxy: np.ndarray,
max_iterations: int = 100,
) -> np.ndarray:
"""
Spread out boxes that overlap with each other.
Args:
xyxy: Numpy array of shape (N, 4) where N is the number of boxes.
max_iterations: Maximum number of iterations to run the algorithm for.
"""
if len(xyxy) == 0:
return xyxy
xyxy_padded = pad_boxes(xyxy, px=1)
for _ in range(max_iterations):
# NxN
iou = box_iou_batch(xyxy_padded, xyxy_padded)
np.fill_diagonal(iou, 0)
if np.all(iou == 0):
break
overlap_mask = iou > 0
# Nx2
centers = (xyxy_padded[:, :2] + xyxy_padded[:, 2:]) / 2
# NxNx2
delta_centers = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
delta_centers *= overlap_mask[:, :, np.newaxis]
# Nx2
delta_sum = np.sum(delta_centers, axis=1)
delta_magnitude = np.linalg.norm(delta_sum, axis=1, keepdims=True)
direction_vectors = np.divide(
delta_sum,
delta_magnitude,
out=np.zeros_like(delta_sum),
where=delta_magnitude != 0,
)
force_vectors = np.sum(iou, axis=1)
force_vectors = force_vectors[:, np.newaxis] * direction_vectors
force_vectors *= 10
force_vectors[(force_vectors > 0) & (force_vectors < 2)] = 2
force_vectors[(force_vectors < 0) & (force_vectors > -2)] = -2
force_vectors = force_vectors.astype(int)
xyxy_padded[:, [0, 1]] += force_vectors
xyxy_padded[:, [2, 3]] += force_vectors
return pad_boxes(xyxy_padded, px=-1)

Some files were not shown because too many files have changed in this diff Show More