Compare commits

...
158 Commits
Author SHA1 Message Date
Piotr Skalski 53f4cde18d Merge pull request #764 from roboflow/develop
`supervision-0.18.0` release
2024-01-25 10:33:16 +01:00
Piotr Skalski 181724e2d3 Merge pull request #775 from roboflow/feature/supervision-0-18-0-changelog
`supervision-0.18.0` change log update
2024-01-25 10:31:17 +01:00
pre-commit-ci[bot] 205b2d200a fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-25 09:29:43 +00:00
SkalskiP 3dcb330822 - version bump from 0.18.0rc4 to 0.18.0
- `supervision-0.18.0` release changelog update
- minot docs changes
2024-01-25 10:27:22 +01:00
SkalskiP 6fa4f25250 bump version from 0.18.0rc3 to 0.18.0rc4 2024-01-24 18:10:56 +01:00
Piotr Skalski 35d5738622 Merge pull request #770 from roboflow/yolov8-obb
feat: ✨ yolov8-obb support and OrientedBoxAnnotator added for obb
2024-01-24 17:50:41 +01:00
pre-commit-ci[bot] 9d56d8a9fe fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-24 16:47:55 +00:00
SkalskiP c7d7e8b71e Merge remote-tracking branch 'origin/yolov8-obb' into yolov8-obb
# Conflicts:
#	docs/annotators.md
2024-01-24 17:47:41 +01:00
SkalskiP 6565fae1c2 final docs updates 2024-01-24 17:47:11 +01:00
Onuralp SEZER a5bef3b15e docs: 📝 >>>/... removed from obb docs and code example
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-24 19:31:46 +03:00
SkalskiP 164b9bedce Merge branch 'develop' into yolov8-obb 2024-01-24 16:49:00 +01:00
Piotr Skalski aabe60be6a Merge pull request #774 from roboflow/docs-emojis-fix
fix(docs): 🐞 emoji extension added for show emojis properly
2024-01-24 16:42:19 +01:00
Piotr Skalski 533a8a1aa3 Merge pull request #773 from roboflow/readme-example-docs
docs: 📝 readme >>>/... removed
2024-01-24 16:33:43 +01:00
Onuralp SEZER c7790fdf56 fix(docs): 🐞 emoji extension added for show emojis properly
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-24 17:53:56 +03:00
Onuralp SEZER dd53cd06b9 docs: 📝 readme >>>/... removed
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-24 17:43:10 +03:00
Onuralp SEZER 24593b64d5 Removed '>>>' and '...' from docs - Merge PR #761 from RaghavvGupta/improved-code-usability
Removed '>>>' and '...' from examples and docs and improved some indentation.
2024-01-24 17:21:00 +03:00
Piotr Skalski 80b7eecfaf Merge pull request #696 from roboflow/prediction-smoothing
Add Prediction Smoothing
2024-01-24 15:13:13 +01:00
SkalskiP 6b063736d9 Merge remote-tracking branch 'origin/prediction-smoothing' into prediction-smoothing 2024-01-24 15:11:50 +01:00
SkalskiP 7b0d5e729e ready for merge 2024-01-24 15:09:47 +01:00
pre-commit-ci[bot] 7916d6a6a4 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-24 14:07:46 +00:00
SkalskiP 79560945f5 ready for merge 2024-01-24 15:07:32 +01:00
Onuralp SEZER d4e0d8d46d docs: 📝 supervision/annotators/core.py PercentageBarAnnotator >>>/... removed
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-24 15:41:18 +03:00
Onuralp SEZER 5a89e2170f Merge remote-tracking branch 'mainland/develop' into improved-code-usability 2024-01-24 15:39:07 +03:00
Onuralp SEZER ab5a86596f docs: 📝 supervision downloader classfication draw geometry and utils >>>/... removed
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-24 15:26:43 +03:00
Onuralp SEZER 12b42f80de docs: 📝 supervision/utils/notebook.py >>>/... removed
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-24 15:17:49 +03:00
Onuralp SEZER 8a5cd1bc3e docs: 📝 supervision/utils/video.py >>>/... removed
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-24 15:17:29 +03:00
Onuralp SEZER e29e960c5f docs: 📝 supervision/metrics/detection.py >>>/... removed
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-24 15:14:18 +03:00
pre-commit-ci[bot] f6e5b6dc54 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-24 12:06:21 +00:00
SkalskiP e2751368d5 improvements 2024-01-24 13:06:07 +01:00
Onuralp SEZER 955a6d67fb docs: 📝 supervision/tracker/byte_tracker/core.py >>>/... removed
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-24 13:13:56 +03:00
Onuralp SEZER 833edc95ad docs: 📝 supervision/detection/utils.py >>>/... removed
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-24 13:10:48 +03:00
Onuralp SEZER 4df21ded6a docs: 📝 supervision/detection/annotate.py >>>/... removed
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-24 13:03:34 +03:00
Onuralp SEZER f03be21a9f docs: 📝 supervision/dataset/core.py >>>/... removed
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-24 13:01:55 +03:00
Onuralp SEZER 2d1aa3b996 docs: 📝 supervision/detection/tools/inference_slicer.py >>>/... removed
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-24 12:52:34 +03:00
Onuralp SEZER 7af914bf56 docs: 📝 supervision/annotators/core.py docs/annotators.md >>>/... removed
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-24 12:50:23 +03:00
Onuralp SEZER 8872151d0c docs: 📝 core/detection/core.py >>>/... removed
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-24 12:28:45 +03:00
raghav d961aa9eb4 docs: example image = ... reverted and added annotators.md 2024-01-24 12:05:57 +03:00
raghav 845c9cc2c2 docs: '>>>' and '...' are removed and indentation improvements 2024-01-24 12:05:48 +03:00
Piotr Skalski 4667e02d79 Merge pull request #772 from roboflow/mkdocs-matplotlib-updates
build(deps): 📦 mkdocs-material and matplotlib update for security
2024-01-24 09:07:43 +01:00
Onuralp SEZER 047672d458 build(deps): 📦 mkdocs-material and matplotlib update for security
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-24 10:51:41 +03:00
Piotr Skalski 2622144660 Merge pull request #771 from roboflow/removal-pillow-reqs
build(deps): 📦 package pillow removed
2024-01-23 20:15:31 +01:00
Onuralp SEZER fb0500c394 build(deps): 📦 package pillow removed
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-23 21:19:01 +03:00
Onuralp SEZER 10f3b73e79 docs: 📝 OrientedBox Annotator docs added
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-23 21:07:09 +03:00
Onuralp SEZER 564f979977 Merge branch 'develop' into yolov8-obb 2024-01-23 20:52:07 +03:00
Onuralp SEZER 64302f450c feat: ✨ yolov8-obb support and OrientedBoxAnnotator added for obb
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-23 20:18:47 +03:00
SkalskiP 239ea649c0 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	supervision/annotators/core.py
2024-01-23 18:10:18 +01:00
James Gallagher 626636cfa7 update tracking logic 2024-01-23 17:10:02 +00:00
SkalskiP 6b14cc9572 bump version from 0.18.0rc2 to 0.18.0rc3 2024-01-23 18:09:24 +01:00
SkalskiP 294a476977 bump version from 0.18.0rc2 to 0.18.0rc3 2024-01-23 18:07:25 +01:00
Piotr Skalski 4fedb87f97 Merge pull request #769 from roboflow/feature/color_and_color_palette_updates
`feature/color_and_color_palette_updates`
2024-01-23 17:21:15 +01:00
pre-commit-ci[bot] a1b2297e9f fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-23 16:14:04 +00:00
SkalskiP c064c1fc98 final tests 2024-01-23 17:13:51 +01:00
pre-commit-ci[bot] 0f3359c15f fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-23 15:57:57 +00:00
SkalskiP a9987dbc83 test @classproperty decorator 2024-01-23 16:57:10 +01:00
SkalskiP b14a035ede final changes 2024-01-23 16:45:15 +01:00
SkalskiP 295e8b725b added from_matplotlib method 2024-01-23 16:34:22 +01:00
pre-commit-ci[bot] fa8a9cb5ac fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-23 15:25:02 +00:00
SkalskiP 66245dd0df added documentation and examples 2024-01-23 16:24:15 +01:00
SkalskiP 2d0a3f9d79 working on optimal syntax 2024-01-23 15:42:27 +01:00
pre-commit-ci[bot] 7b2c09c0ab fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-23 14:19:08 +00:00
SkalskiP d314bf0364 initial tests 2024-01-23 14:58:06 +01:00
James Gallagher 21791c8522 remove idle trackers 2024-01-23 13:38:15 +00:00
James Gallagher 24ca25ef94 fix formatting 2024-01-23 13:28:00 +00:00
James 09d771c139 Merge branch 'develop' into prediction-smoothing 2024-01-23 12:59:28 +00:00
James Gallagher 6efd51dfb1 respond to feedback 2024-01-23 12:57:36 +00:00
James Gallagher c299a929c5 respond to feedback 2024-01-23 12:57:31 +00:00
Onuralp SEZER f5f068bffd Merge pull request #720 from xaristeidou/develop
Add `sv.PercentageBarAnnotator()` for confidence level bar annotation in specified position
2024-01-23 14:39:33 +03:00
SkalskiP fc22a9ccd9 update visualization path 2024-01-23 12:32:18 +01:00
SkalskiP 45fe3c1c0f small bug fix 2024-01-23 12:13:08 +01:00
pre-commit-ci[bot] 027b60837a fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-23 10:56:11 +00:00
SkalskiP fa569cb235 ready for tests 2024-01-23 11:54:40 +01:00
Onuralp SEZER 2bc58e7938 feat(core.py): 🚀 Allow custom NumPy array for custom_values in PercentageBarAnnotator
fix(core.py): 🐞 Treat CENTER_OF_MASS the same as CENTER in PercentageBarAnnotator
refactor(core.py): Move border mapping logic to separate method in PercentageBarAnnotator

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-23 04:34:59 +03:00
Onuralp SEZER 6f87069080 docs: 📝 PercentageBar Annotator image added into core.py
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-22 22:13:35 +03:00
Onuralp SEZER c0c541d121 docs: 📝 PercentageBar Annotator docs and usage added.
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-22 22:13:20 +03:00
pre-commit-ci[bot] 08173899a9 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-22 18:51:36 +00:00
Onuralp SEZER 6e9bfc9a05 chore: 🧹 folder attiributes ignored
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-22 21:51:03 +03:00
Onuralp SEZER 65eda0b80c build(docs): 👷 mkdocs-material with imaging dep added for development
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-22 21:46:38 +03:00
Christoforos Aristeidou 740076d961 pre-commit spacing 2024-01-22 21:42:22 +03:00
Christoforos Aristeidou f3cb8e9d27 reformed fill of confidence rectangle, changed border attributes 2024-01-22 21:42:22 +03:00
Christoforos Aristeidou 846b337155 changed if else statements to dictionary mapping 2024-01-22 21:42:21 +03:00
Christoforos Aristeidou ce19f917e9 changed border thickness percentage, extract confidence level 2024-01-22 21:42:21 +03:00
Christoforos Aristeidou c70ee87897 deleted unused coordinates and changed type of anchor coords 2024-01-22 21:42:21 +03:00
Christoforos Aristeidou 786942ba7e changed default value for height 2024-01-22 21:42:21 +03:00
Christoforos Aristeidou 5ed3430b16 corrected border color, added rectangle for percentage 2024-01-22 21:42:21 +03:00
Christoforos Aristeidou 39ef293926 added if else statements for different anchors 2024-01-22 21:42:21 +03:00
Christoforos Aristeidou a59d84549f added anchor xy coordinates 2024-01-22 21:42:20 +03:00
Christoforos Aristeidou f94c0a4930 arguments description update 2024-01-22 21:42:20 +03:00
Christoforos Aristeidou ae7360b5e0 added new attributes of class 2024-01-22 21:42:20 +03:00
Christoforos Aristeidou e809e424e6 added PercentageBarAnnotator() to __init__ of package 2024-01-22 21:42:20 +03:00
Christoforos Aristeidou b1dbeaf9c4 changed class name and description 2024-01-22 21:42:20 +03:00
Christoforos Aristeidou fbcc211079 added boundingboxannotator class to re-write class 2024-01-22 21:42:19 +03:00
Onuralp SEZER 6b7ccfccd9 Merge pull request #763 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2024-01-22 21:16:52 +03:00
Onuralp SEZER 5677b4b5e3 Merge pull request #762 from roboflow/dependabot/pip/ruff-0.1.14
⬆️ Bump ruff from 0.1.13 to 0.1.14
2024-01-22 21:16:37 +03:00
pre-commit-ci[bot] c323ecba8a chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.1.13 → v0.1.14](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.13...v0.1.14)
2024-01-22 17:34:15 +00:00
dependabot[bot] 995f170e30 ⬆️ Bump ruff from 0.1.13 to 0.1.14
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.13 to 0.1.14.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.13...v0.1.14)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-22 00:57:36 +00:00
Onuralp SEZER decbbb62ad Merge pull request #759 from roboflow/dependabot/pip/jupyterlab-4.0.11
⬆️ Bump jupyterlab from 4.0.10 to 4.0.11
2024-01-20 22:36:01 +03:00
Onuralp SEZER 7d3c2114dd Merge pull request #760 from roboflow/dependabot/pip/notebook-7.0.7
⬆️ Bump notebook from 7.0.6 to 7.0.7
2024-01-20 22:35:45 +03:00
dependabot[bot] b008aadd41 ⬆️ Bump notebook from 7.0.6 to 7.0.7
Bumps [notebook](https://github.com/jupyter/notebook) from 7.0.6 to 7.0.7.
- [Release notes](https://github.com/jupyter/notebook/releases)
- [Changelog](https://github.com/jupyter/notebook/blob/@jupyter-notebook/tree@7.0.7/CHANGELOG.md)
- [Commits](https://github.com/jupyter/notebook/compare/@jupyter-notebook/tree@7.0.6...@jupyter-notebook/tree@7.0.7)

---
updated-dependencies:
- dependency-name: notebook
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-19 20:45:54 +00:00
dependabot[bot] cca120915e ⬆️ Bump jupyterlab from 4.0.10 to 4.0.11
Bumps [jupyterlab](https://github.com/jupyterlab/jupyterlab) from 4.0.10 to 4.0.11.
- [Release notes](https://github.com/jupyterlab/jupyterlab/releases)
- [Changelog](https://github.com/jupyterlab/jupyterlab/blob/@jupyterlab/lsp@4.0.11/CHANGELOG.md)
- [Commits](https://github.com/jupyterlab/jupyterlab/compare/@jupyterlab/lsp@4.0.10...@jupyterlab/lsp@4.0.11)

---
updated-dependencies:
- dependency-name: jupyterlab
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-19 20:39:08 +00:00
Piotr Skalski 4e4fad9d04 Merge pull request #756 from roboflow/q1-2024/color-properties
feat(color.py): ✨ add color constants/properties
2024-01-19 09:04:50 +01:00
pre-commit-ci[bot] 38bdeff171 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-18 22:43:26 +00:00
Onuralp SEZER 6fee626828 refactor(Color): 🎨 Deprecate color methods in favor of color properties
Deprecated the `white`, `black`, `red`, `green`, and `blue` methods in
the `Color` class.
Users should now use the `WHITE`, `BLACK`, `RED`, `GREEN`, and `BLUE`
properties instead. This change improves the API and makes it more consistent.

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-19 01:41:31 +03:00
Onuralp SEZER bb4d741aff feat(color.py): ✨ add color constants
This commit adds the WHITE, BLACK, RED, GREEN, and BLUE color constants
to the Color class in color.py. These constants represent
the corresponding colors in the RGB color space.

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-19 00:42:59 +03:00
Piotr Skalski d503e3aa50 Merge pull request #750 from roboflow/opencv-version
fix: ✨ bump opencv version and remove upper limit
2024-01-18 21:42:04 +01:00
Onuralp SEZER 3b13fc41a7 fix: ✨ bump opencv version and remove upper limit
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-18 23:38:06 +03:00
Piotr Skalski 9e6abc0fff Merge pull request #747 from roboflow/scipy-security
fix(security): 🔐 bump scipy version for memory leak vulnerability
2024-01-18 21:26:21 +01:00
Onuralp SEZER 3e48708910 fix(security): 🔐 bump scipy version for memory leak vulnerability
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-18 22:56:59 +03:00
Onuralp SEZER 215741fd83 Merge pull request #742 from roboflow/dependabot/pip/tox-4.12.1
⬆️ Bump tox from 4.12.0 to 4.12.1
2024-01-18 22:34:41 +03:00
Onuralp SEZER c2a5583aa2 Merge pull request #745 from roboflow/dependabot/pip/jupyter-lsp-2.2.2
⬆️ Bump jupyter-lsp from 2.2.1 to 2.2.2
2024-01-18 22:31:46 +03:00
Piotr Skalski c04edbcf85 Merge pull request #735 from revtheundead/add-center-point-crossed-condition-to-line-counter
Added an option to count detection upon the center point of the bounding box crossing the line counter
2024-01-18 17:52:13 +01:00
pre-commit-ci[bot] f768347d26 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-18 16:36:22 +00:00
dependabot[bot] ffc8c16993 ⬆️ Bump jupyter-lsp from 2.2.1 to 2.2.2
Bumps [jupyter-lsp](https://github.com/jupyter-lsp/jupyterlab-lsp) from 2.2.1 to 2.2.2.
- [Release notes](https://github.com/jupyter-lsp/jupyterlab-lsp/releases)
- [Changelog](https://github.com/jupyter-lsp/jupyterlab-lsp/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jupyter-lsp/jupyterlab-lsp/commits)

---
updated-dependencies:
- dependency-name: jupyter-lsp
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-18 16:36:12 +00:00
pre-commit-ci[bot] 6128576ea1 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-18 16:06:10 +00:00
Piotr Skalski cda5016a5a Merge branch 'develop' into add-center-point-crossed-condition-to-line-counter 2024-01-18 17:06:01 +01:00
SkalskiP f8ee3a4290 ready for end-to-end tests. 2024-01-18 16:43:17 +01:00
SkalskiP 5f04836917 calculate_region_of_interest_limits and is_point_in_limits methods added. 2024-01-18 13:36:47 +01:00
SkalskiP 616fc96ca8 Adding ability to calculate vector magnitude. 2024-01-18 12:46:09 +01:00
rev dca7fb43e5 Revert "Fixed cross_product() funtion test"
This reverts commit 353865e017.
2024-01-18 13:50:03 +03:00
rev ec7baf3360 Revert "Implemented cross_product() with numpy"
This reverts commit 9fb97e3c79.
2024-01-18 13:49:54 +03:00
rev 9fb97e3c79 Implemented cross_product() with numpy 2024-01-18 13:45:30 +03:00
Piotr Skalski 536e0d86a8 Merge pull request #733 from roboflow/fix/speed-estimation-issue-when-detections-empty
`fix/speed-estimation-issue-when-detections-empty`
2024-01-18 11:30:04 +01:00
SkalskiP 3a608eeb93 Merge remote-tracking branch 'origin/add-center-point-crossed-condition-to-line-counter' into add-center-point-crossed-condition-to-line-counter 2024-01-18 11:29:18 +01:00
SkalskiP 008b0bd984 Formatting updates. 2024-01-18 11:29:01 +01:00
rev 353865e017 Fixed cross_product() funtion test 2024-01-18 13:26:01 +03:00
SkalskiP da4ee62298 Fixing cross_product tests. 2024-01-18 11:18:18 +01:00
rev ae758a655d Fixed the is_in_line_range() function logic and fixed a minor bug with finding anchors 2024-01-18 12:35:00 +03:00
pre-commit-ci[bot] e38256f316 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-18 00:46:10 +00:00
dependabot[bot] 713bacae08 ⬆️ Bump tox from 4.12.0 to 4.12.1
Bumps [tox](https://github.com/tox-dev/tox) from 4.12.0 to 4.12.1.
- [Release notes](https://github.com/tox-dev/tox/releases)
- [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/tox/compare/4.12.0...4.12.1)

---
updated-dependencies:
- dependency-name: tox
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-18 00:46:02 +00:00
rev 2367fcac82 Resolved merge conflict 2024-01-18 01:41:25 +03:00
rev 1f640980ad Increased line counter flexibility and replaced vector cross product function 2024-01-18 01:21:45 +03:00
Piotr Skalski 64661a3e4a Update built with supervision section in README.md 2024-01-17 15:54:12 +01:00
Piotr Skalski 1c414bd70f Update speed estimation example tutorial README.md 2024-01-17 15:52:41 +01:00
Piotr Skalski e32b05a636 Update speed estimation example tutorial README.md 2024-01-17 15:38:19 +01:00
Piotr Skalski 688cf14f2a Merge pull request #739 from roboflow/feature/add-comments-system-powered-by-github-discussions
`feature/add-comments-system-powered-by-github-discussions`
2024-01-17 12:16:21 +01:00
pre-commit-ci[bot] e3529436e1 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-17 11:14:03 +00:00
SkalskiP 99d1b6d130 ready for review 2024-01-17 12:11:31 +01:00
pre-commit-ci[bot] 47eed7257c fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-16 23:09:46 +00:00
rev 602676bd9c Change cross_product() return type from int to float 2024-01-17 02:06:36 +03:00
rev 0e1f422a75 Added an option to count detection upon the center point of the bounding box crossing the line counter.
Added an option to LineCounter class specifying the condition which determines whether a detection has crossed the line counter or not.

Additionally made it so that the line counters check whether if the corners (or optionally the center point) of a detection's bounding box are in the line counter's coordinate ranges. This way, line counters count only the detections that have precisely crossed the bounds that are drawn without failing and counting targets that have crossed invisible extensions of the lines.
2024-01-17 01:24:49 +03:00
pre-commit-ci[bot] 4d8f15f295 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-16 16:55:57 +00:00
SkalskiP c78354203d ready for review 2024-01-16 17:51:56 +01:00
Brad Dwyer 310a09859d Remove unused tracker_length 2023-12-29 11:42:25 -06:00
pre-commit-ci[bot] bf427d5061 fix(pre_commit): 🎨 auto format pre-commit hooks 2023-12-28 18:21:48 +00:00
Brad Dwyer 8ab7cb659c Merge branch 'prediction-smoothing' of https://github.com/roboflow/supervision into prediction-smoothing 2023-12-28 12:21:38 -06:00
Brad Dwyer a58c8ef0fe Add set_length and use deque 2023-12-28 12:21:07 -06:00
pre-commit-ci[bot] 3e0a665ef2 fix(pre_commit): 🎨 auto format pre-commit hooks 2023-12-28 18:04:43 +00:00
Brad Dwyer 3a0682db08 Remove NoneDict and add type hints 2023-12-28 12:04:33 -06:00
Brad Dwyer a36b426dff Merge branch 'develop' into prediction-smoothing 2023-12-28 11:50:15 -06:00
Brad Dwyer 3f77433f2e Merge branch 'prediction-smoothing' of https://github.com/roboflow/supervision into prediction-smoothing 2023-12-28 11:50:12 -06:00
Brad Dwyer 5d1e76fbd0 Update copy based on feedback from James & Piotr 2023-12-28 11:50:06 -06:00
pre-commit-ci[bot] 6600d7533a fix(pre_commit): 🎨 auto format pre-commit hooks 2023-12-28 17:43:55 +00:00
Brad Dwyer 1d4d7dd4ea Move into tools directory 2023-12-28 11:42:55 -06:00
Brad Dwyer d7420c545e Move video to correct spot in the docs 2023-12-27 15:17:28 -06:00
Brad Dwyer 729eb16ea6 Merge branch 'prediction-smoothing' of https://github.com/roboflow/supervision into prediction-smoothing 2023-12-27 15:15:31 -06:00
Brad Dwyer ca6ba30375 Fix linter errors 2023-12-27 15:15:18 -06:00
pre-commit-ci[bot] 7f4b27a556 fix(pre_commit): 🎨 auto format pre-commit hooks 2023-12-27 21:09:26 +00:00
Brad Dwyer ffdab06376 Update Docs 2023-12-27 15:02:02 -06:00
Brad Dwyer b912db7a06 Add Detections Smoother 2023-12-27 14:13:40 -06:00
68 changed files with 2898 additions and 1584 deletions
+1 -2
View File
@@ -38,14 +38,13 @@ jobs:
numpy==1.21.2 \
opencv-python==4.5.5.64 \
packaging==23.2 \
Pillow==10.1.0 \
pluggy==1.3.0 \
pyparsing==3.1.1 \
pytest==7.2.0 \
python-dateutil==2.8.2 \
PyYAML==5.3 \
requests==2.26.0 \
scipy==1.9.0 \
scipy==1.10.0 \
setuptools-scm==8.0.4 \
six==1.16.0 \
tomli==2.0.1 \
+21
View File
@@ -135,3 +135,24 @@ dmypy.json
# Notebooks
notebooks/
# OSX folder attributes
.DS_Store
.AppleDouble
.LSOverride
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
# Windows
Thumbs.db
Desktop.ini
# Linux
*~
.directory
.Trash-*
+2 -1
View File
@@ -13,6 +13,7 @@ repos:
- id: trailing-whitespace
exclude: test/.*\.py
- id: check-yaml
exclude: mkdocs.yml
- id: check-docstring-first
- id: check-executables-have-shebangs
- id: check-toml
@@ -46,7 +47,7 @@ repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.13
rev: v0.1.14
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
+1 -1
View File
@@ -52,7 +52,7 @@ git push -u origin main
### Pre-commit tool
This project utilizes the [pre-commit](https://pre-commit.com/) tool to maintain code quality and consistency. Before submitting a pull request or making any commits, it is important to run the pre-commit tool to ensure that your changes meet the project's guidelines.
This project uses the [pre-commit](https://pre-commit.com/) tool to maintain code quality and consistency. Before submitting a pull request or making any commits, it is important to run the pre-commit tool to ensure that your changes meet the project's guidelines.
Furthermore, we have integrated a pre-commit GitHub Action into our workflow. This means that with every pull request opened, the pre-commit checks will be automatically enforced, streamlining the code review process and ensuring that all contributions adhere to our quality standards.
+90 -90
View File
@@ -46,17 +46,17 @@ Read more about desktop, headless, and local installation in our [guide](https:/
Supervision was designed to be model agnostic. Just plug in any classification, detection, or segmentation model. For your convenience, we have created [connectors](https://supervision.roboflow.com/detection/core/#detections) for the most popular libraries like Ultralytics, Transformers, or MMDetection.
```python
>>> import cv2
>>> import supervision as sv
>>> from ultralytics import YOLO
import cv2
import supervision as sv
from ultralytics import YOLO
>>> image = cv2.imread(...)
>>> model = YOLO('yolov8s.pt')
>>> result = model(image)[0]
>>> detections = sv.Detections.from_ultralytics(result)
image = cv2.imread(...)
model = YOLO('yolov8s.pt')
result = model(image)[0]
detections = sv.Detections.from_ultralytics(result)
>>> len(detections)
5
len(detections)
# 5
```
<details>
@@ -67,17 +67,17 @@ Supervision was designed to be model agnostic. Just plug in any classification,
Running with [Inference](https://github.com/roboflow/inference) requires a [Roboflow API KEY](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key).
```python
>>> import cv2
>>> import supervision as sv
>>> from inference.models.utils import get_roboflow_model
import cv2
import supervision as sv
from inference.models.utils import get_roboflow_model
>>> image = cv2.imread(...)
>>> model = get_roboflow_model(model_id="yolov8s-640", api_key=<ROBOFLOW API KEY>)
>>> result = model.infer(image)[0]
>>> detections = sv.Detections.from_inference(result)
image = cv2.imread(...)
model = get_roboflow_model(model_id="yolov8s-640", api_key=<ROBOFLOW API KEY>)
result = model.infer(image)[0]
detections = sv.Detections.from_inference(result)
>>> len(detections)
>>> 5
len(detections)
# 5
```
@@ -88,17 +88,17 @@ Supervision was designed to be model agnostic. Just plug in any classification,
Supervision offers a wide range of highly customizable [annotators](https://supervision.roboflow.com/annotators/), allowing you to compose the perfect visualization for your use case.
```python
>>> import cv2
>>> import supervision as sv
import cv2
import supervision as sv
>>> image = cv2.imread(...)
>>> detections = sv.Detections(...)
image = cv2.imread(...)
detections = sv.Detections(...)
>>> bounding_box_annotator = sv.BoundingBoxAnnotator()
>>> annotated_frame = bounding_box_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
bounding_box_annotator = sv.BoundingBoxAnnotator()
annotated_frame = bounding_box_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
https://github.com/roboflow/supervision/assets/26109316/691e219c-0565-4403-9218-ab5644f39bce
@@ -108,19 +108,19 @@ https://github.com/roboflow/supervision/assets/26109316/691e219c-0565-4403-9218-
Supervision provides a set of [utils](https://supervision.roboflow.com/datasets/) that allow you to load, split, merge, and save datasets in one of the supported formats.
```python
>>> import supervision as sv
import supervision as sv
>>> dataset = sv.DetectionDataset.from_yolo(
... images_directory_path=...,
... annotations_directory_path=...,
... data_yaml_path=...
... )
dataset = sv.DetectionDataset.from_yolo(
images_directory_path=...,
annotations_directory_path=...,
data_yaml_path=...
)
>>> dataset.classes
dataset.classes
['dog', 'person']
>>> len(dataset)
1000
len(dataset)
# 1000
```
<details close>
@@ -129,86 +129,86 @@ Supervision provides a set of [utils](https://supervision.roboflow.com/datasets/
- load
```python
>>> dataset = sv.DetectionDataset.from_yolo(
... images_directory_path=...,
... annotations_directory_path=...,
... data_yaml_path=...
... )
dataset = sv.DetectionDataset.from_yolo(
images_directory_path=...,
annotations_directory_path=...,
data_yaml_path=...
)
>>> dataset = sv.DetectionDataset.from_pascal_voc(
... images_directory_path=...,
... annotations_directory_path=...
... )
dataset = sv.DetectionDataset.from_pascal_voc(
images_directory_path=...,
annotations_directory_path=...
)
>>> dataset = sv.DetectionDataset.from_coco(
... images_directory_path=...,
... annotations_path=...
... )
dataset = sv.DetectionDataset.from_coco(
images_directory_path=...,
annotations_path=...
)
```
- split
```python
>>> train_dataset, test_dataset = dataset.split(split_ratio=0.7)
>>> test_dataset, valid_dataset = test_dataset.split(split_ratio=0.5)
train_dataset, test_dataset = dataset.split(split_ratio=0.7)
test_dataset, valid_dataset = test_dataset.split(split_ratio=0.5)
>>> len(train_dataset), len(test_dataset), len(valid_dataset)
(700, 150, 150)
len(train_dataset), len(test_dataset), len(valid_dataset)
# (700, 150, 150)
```
- merge
```python
>>> ds_1 = sv.DetectionDataset(...)
>>> len(ds_1)
100
>>> ds_1.classes
['dog', 'person']
ds_1 = sv.DetectionDataset(...)
len(ds_1)
# 100
ds_1.classes
# ['dog', 'person']
>>> ds_2 = sv.DetectionDataset(...)
>>> len(ds_2)
200
>>> ds_2.classes
['cat']
ds_2 = sv.DetectionDataset(...)
len(ds_2)
# 200
ds_2.classes
# ['cat']
>>> ds_merged = sv.DetectionDataset.merge([ds_1, ds_2])
>>> len(ds_merged)
300
>>> ds_merged.classes
['cat', 'dog', 'person']
ds_merged = sv.DetectionDataset.merge([ds_1, ds_2])
len(ds_merged)
# 300
ds_merged.classes
# ['cat', 'dog', 'person']
```
- save
```python
>>> dataset.as_yolo(
... images_directory_path=...,
... annotations_directory_path=...,
... data_yaml_path=...
... )
dataset.as_yolo(
images_directory_path=...,
annotations_directory_path=...,
data_yaml_path=...
)
>>> dataset.as_pascal_voc(
... images_directory_path=...,
... annotations_directory_path=...
... )
dataset.as_pascal_voc(
images_directory_path=...,
annotations_directory_path=...
)
>>> dataset.as_coco(
... images_directory_path=...,
... annotations_path=...
... )
dataset.as_coco(
images_directory_path=...,
annotations_path=...
)
```
- convert
```python
>>> sv.DetectionDataset.from_yolo(
... images_directory_path=...,
... annotations_directory_path=...,
... data_yaml_path=...
... ).as_pascal_voc(
... images_directory_path=...,
... annotations_directory_path=...
... )
sv.DetectionDataset.from_yolo(
images_directory_path=...,
annotations_directory_path=...,
data_yaml_path=...
).as_pascal_voc(
images_directory_path=...,
annotations_directory_path=...
)
```
</details>
@@ -237,7 +237,7 @@ https://user-images.githubusercontent.com/26109316/207858600-ee862b22-0353-440b-
https://github.com/roboflow/supervision/assets/26109316/c9436828-9fbf-4c25-ae8c-60e9c81b3900
https://github.com/roboflow/supervision/assets/26109316/f84db7b5-79e2-4142-a1da-64daa43ce667
https://github.com/roboflow/supervision/assets/26109316/3ac6982f-4943-4108-9b7f-51787ef1a69f
## 📚 documentation
+183 -149
View File
@@ -1,16 +1,21 @@
---
comments: true
status: new
---
=== "BoundingBox"
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> bounding_box_annotator = sv.BoundingBoxAnnotator()
>>> annotated_frame = bounding_box_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
bounding_box_annotator = sv.BoundingBoxAnnotator()
annotated_frame = bounding_box_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
<div class="result" markdown>
@@ -22,16 +27,16 @@
=== "RoundBox"
```python
>>> import supervision as sv
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>
@@ -43,16 +48,16 @@
=== "BoxCorner"
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> corner_annotator = sv.BoxCornerAnnotator()
>>> annotated_frame = corner_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
corner_annotator = sv.BoxCornerAnnotator()
annotated_frame = corner_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
<div class="result" markdown>
@@ -64,16 +69,16 @@
=== "Color"
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> color_annotator = sv.ColorAnnotator()
>>> annotated_frame = color_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
color_annotator = sv.ColorAnnotator()
annotated_frame = color_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
<div class="result" markdown>
@@ -85,16 +90,16 @@
=== "Circle"
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> circle_annotator = sv.CircleAnnotator()
>>> annotated_frame = circle_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
circle_annotator = sv.CircleAnnotator()
annotated_frame = circle_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
<div class="result" markdown>
@@ -106,37 +111,37 @@
=== "Dot"
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> dot_annotator = sv.DotAnnotator()
>>> annotated_frame = dot_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
dot_annotator = sv.DotAnnotator()
annotated_frame = dot_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
<div class="result" markdown>
![circle-annotator-example](https://media.roboflow.com/supervision-annotator-examples/dot-annotator-example-purple.png){ align=center width="800" }
![dot-annotator-example](https://media.roboflow.com/supervision-annotator-examples/dot-annotator-example-purple.png){ align=center width="800" }
</div>
=== "Triangle"
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> triangle_annotator = sv.TriangleAnnotator()
>>> annotated_frame = triangle_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
triangle_annotator = sv.TriangleAnnotator()
annotated_frame = triangle_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
<div class="result" markdown>
@@ -148,16 +153,16 @@
=== "Ellipse"
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> ellipse_annotator = sv.EllipseAnnotator()
>>> annotated_frame = ellipse_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
ellipse_annotator = sv.EllipseAnnotator()
annotated_frame = ellipse_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
<div class="result" markdown>
@@ -169,37 +174,58 @@
=== "Halo"
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> halo_annotator = sv.HaloAnnotator()
>>> annotated_frame = halo_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
halo_annotator = sv.HaloAnnotator()
annotated_frame = halo_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
<div class="result" markdown>
![ellipse-annotator-example](https://media.roboflow.com/supervision-annotator-examples/halo-annotator-example-purple.png){ align=center width="800" }
![halo-annotator-example](https://media.roboflow.com/supervision-annotator-examples/halo-annotator-example-purple.png){ align=center width="800" }
</div>
=== "PercentageBar"
```python
import supervision as sv
image = ...
detections = sv.Detections(...)
percentage_bar_annotator = sv.PercentageBarAnnotator()
annotated_frame = percentage_bar_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
<div class="result" markdown>
![percentage-bar-annotator-example](https://media.roboflow.com/supervision-annotator-examples/percentage-bar-annotator-example-purple.png){ align=center width="800" }
</div>
=== "Mask"
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> mask_annotator = sv.MaskAnnotator()
>>> annotated_frame = mask_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
mask_annotator = sv.MaskAnnotator()
annotated_frame = mask_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
<div class="result" markdown>
@@ -211,16 +237,16 @@
=== "Polygon"
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> polygon_annotator = sv.PolygonAnnotator()
>>> annotated_frame = polygon_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
polygon_annotator = sv.PolygonAnnotator()
annotated_frame = polygon_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
<div class="result" markdown>
@@ -232,16 +258,16 @@
=== "Label"
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> label_annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER)
>>> annotated_frame = label_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
label_annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER)
annotated_frame = label_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
<div class="result" markdown>
@@ -253,16 +279,16 @@
=== "Blur"
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> blur_annotator = sv.BlurAnnotator()
>>> annotated_frame = blur_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
blur_annotator = sv.BlurAnnotator()
annotated_frame = blur_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
<div class="result" markdown>
@@ -274,16 +300,16 @@
=== "Pixelate"
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> pixelate_annotator = sv.PixelateAnnotator()
>>> annotated_frame = pixelate_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
pixelate_annotator = sv.PixelateAnnotator()
annotated_frame = pixelate_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
<div class="result" markdown>
@@ -295,26 +321,26 @@
=== "Trace"
```python
>>> import supervision as sv
>>> from ultralytics import YOLO
import supervision as sv
from ultralytics import YOLO
>>> model = YOLO('yolov8x.pt')
model = YOLO('yolov8x.pt')
>>> trace_annotator = sv.TraceAnnotator()
trace_annotator = sv.TraceAnnotator()
>>> video_info = sv.VideoInfo.from_video_path(video_path='...')
>>> frames_generator = get_video_frames_generator(source_path='...')
>>> tracker = sv.ByteTrack()
video_info = sv.VideoInfo.from_video_path(video_path='...')
frames_generator = get_video_frames_generator(source_path='...')
tracker = sv.ByteTrack()
>>> with sv.VideoSink(target_path='...', video_info=video_info) as sink:
... for frame in frames_generator:
... result = model(frame)[0]
... detections = sv.Detections.from_ultralytics(result)
... detections = tracker.update_with_detections(detections)
... annotated_frame = trace_annotator.annotate(
... scene=frame.copy(),
... detections=detections)
... sink.write_frame(frame=annotated_frame)
with sv.VideoSink(target_path='...', video_info=video_info) as sink:
for frame in frames_generator:
result = model(frame)[0]
detections = sv.Detections.from_ultralytics(result)
detections = tracker.update_with_detections(detections)
annotated_frame = trace_annotator.annotate(
scene=frame.copy(),
detections=detections)
sink.write_frame(frame=annotated_frame)
```
<div class="result" markdown>
@@ -326,29 +352,29 @@
=== "HeatMap"
```python
>>> import supervision as sv
>>> from ultralytics import YOLO
import supervision as sv
from ultralytics import YOLO
>>> model = YOLO('yolov8x.pt')
model = YOLO('yolov8x.pt')
>>> heat_map_annotator = sv.HeatMapAnnotator()
heat_map_annotator = sv.HeatMapAnnotator()
>>> video_info = sv.VideoInfo.from_video_path(video_path='...')
>>> frames_generator = get_video_frames_generator(source_path='...')
video_info = sv.VideoInfo.from_video_path(video_path='...')
frames_generator = get_video_frames_generator(source_path='...')
>>> with sv.VideoSink(target_path='...', video_info=video_info) as sink:
... for frame in frames_generator:
... result = model(frame)[0]
... detections = sv.Detections.from_ultralytics(result)
... annotated_frame = heat_map_annotator.annotate(
... scene=frame.copy(),
... detections=detections)
... sink.write_frame(frame=annotated_frame)
with sv.VideoSink(target_path='...', video_info=video_info) as sink:
for frame in frames_generator:
result = model(frame)[0]
detections = sv.Detections.from_ultralytics(result)
annotated_frame = heat_map_annotator.annotate(
scene=frame.copy(),
detections=detections)
sink.write_frame(frame=annotated_frame)
```
<div class="result" markdown>
![trace-annotator-example](https://media.roboflow.com/supervision-annotator-examples/heat-map-annotator-example-purple.png){ align=center width="800" }
![heat-map-annotator-example](https://media.roboflow.com/supervision-annotator-examples/heat-map-annotator-example-purple.png){ align=center width="800" }
</div>
@@ -356,10 +382,18 @@
:::supervision.annotators.core.BoundingBoxAnnotator
## RoundBoxAnnotator
:::supervision.annotators.core.RoundBoxAnnotator
## BoxCornerAnnotator
:::supervision.annotators.core.BoxCornerAnnotator
## OrientedBoxAnnotator
:::supervision.annotators.core.OrientedBoxAnnotator
## ColorAnnotator
:::supervision.annotators.core.ColorAnnotator
@@ -384,6 +418,10 @@
:::supervision.annotators.core.HaloAnnotator
## PercentageBarAnnotator
:::supervision.annotators.core.PercentageBarAnnotator
## HeatMapAnnotator
:::supervision.annotators.core.HeatMapAnnotator
@@ -412,10 +450,6 @@
:::supervision.annotators.core.TraceAnnotator
## RoundBoxAnnotator
:::supervision.annotators.core.RoundBoxAnnotator
## ColorLookup
:::supervision.annotators.utils.ColorLookup
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
Supervision offers an assets download utility that allows you to download video files
that you can use in your demos.
+72
View File
@@ -1,3 +1,75 @@
### 0.18.0 <small>January 25, 2024</small>
- Added [#633](https://github.com/roboflow/supervision/pull/720): [`sv.PercentageBarAnnotator`](https://supervision.roboflow.com/annotators/#percentagebarannotator) allowing to annotate images and videos with percentage values representing confidence or other custom property.
```python
>>> import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
>>> percentage_bar_annotator = sv.PercentageBarAnnotator()
>>> annotated_frame = percentage_bar_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
```
- Added [#702](https://github.com/roboflow/supervision/pull/702): [`sv.RoundBoxAnnotator`](https://supervision.roboflow.com/annotators/#roundboxannotator) allowing to annotate images and videos with rounded corners bounding boxes.
- Added [#770](https://github.com/roboflow/supervision/pull/770): [`sv.OrientedBoxAnnotator`](https://supervision.roboflow.com/annotators/#orientedboxannotator) allowing to annotate images and videos with OBB (Oriented Bounding Boxes).
```python
import cv2
import supervision as sv
from ultralytics import YOLO
image = cv2.imread(<SOURCE_IMAGE_PATH>)
model = YOLO("yolov8n-obb.pt")
result = model(image)[0]
detections = sv.Detections.from_ultralytics(result)
oriented_box_annotator = sv.OrientedBoxAnnotator()
annotated_frame = oriented_box_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
- Added [#696](https://github.com/roboflow/supervision/pull/696): [`sv.DetectionsSmoother`](https://supervision.roboflow.com/detection/tools/smoother/#detection-smoother) allowing for smoothing detections over multiple frames in video tracking.
- Added [#769](https://github.com/roboflow/supervision/pull/769): [`sv.ColorPalette.from_matplotlib`](https://supervision.roboflow.com/draw/color/#supervision.draw.color.ColorPalette.from_matplotlib) allowing users to create a `sv.ColorPalette` instance from a Matplotlib color palette.
```python
>>> import supervision as sv
>>> sv.ColorPalette.from_matplotlib('viridis', 5)
ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
```
- Changed [#770](https://github.com/roboflow/supervision/pull/770): [`sv.Detections.from_ultralytics`](https://supervision.roboflow.com/detection/core/#supervision.detection.core.Detections.from_ultralytics) adding support for OBB (Oriented Bounding Boxes).
- Changed [#735](https://github.com/roboflow/supervision/pull/735): [`sv.LineZone`](https://supervision.roboflow.com/detection/tools/line_zone/#linezone) to now accept a list of specific box anchors that must cross the line for a detection to be counted. This update marks a significant improvement from the previous requirement, where all four box corners were necessary. Users can now specify a single anchor, such as `sv.Position.BOTTOM_CENTER`, or any other combination of anchors defined as `List[sv.Position]`.
- Changed [#756](https://github.com/roboflow/supervision/pull/756): [`sv.Color`](https://supervision.roboflow.com/draw/color/#color)'s and [`sv.ColorPalette`](https://supervision.roboflow.com/draw/color/#colorpalette)'s method of accessing predefined colors, transitioning from a function-based approach (`sv.Color.red()`) to a more intuitive and conventional property-based method (`sv.Color.RED`).
!!! warning
`sv.ColorPalette.default()` is deprecated and will be removed in `supervision-0.21.0`. Use `sv.ColorPalette.DEFAULT` instead.
- Changed [#769](https://github.com/roboflow/supervision/pull/769): [`sv.ColorPalette.DEFAULT`](https://supervision.roboflow.com/draw/color/#colorpalette) value, giving users a more extensive set of annotation colors.
- Changed [#677](https://github.com/roboflow/supervision/pull/677): `sv.Detections.from_roboflow` to [`sv.Detections.from_inference`](https://supervision.roboflow.com/detection/core/#supervision.detection.core.Detections.from_inference) streamlining its functionality to be compatible with both the both [inference](https://github.com/roboflow/inference) pip package and the Robloflow [hosted API](https://docs.roboflow.com/deploy/hosted-api).
!!! warning
`Detections.from_roboflow()` is deprecated and will be removed in `supervision-0.21.0`. Use `Detections.from_inference` instead.
- Fixed [#735](https://github.com/roboflow/supervision/pull/735): [`sv.LineZone`](https://supervision.roboflow.com/detection/tools/line_zone/#linezone) functionality to accurately update the counter when an object crosses a line from any direction, including from the side. This enhancement enables more precise tracking and analytics, such as calculating individual in/out counts for each lane on the road.
### 0.17.0 <small>December 06, 2023</small>
- Added [#633](https://github.com/roboflow/supervision/pull/633): [`sv.PixelateAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.PixelateAnnotator) allowing to pixelate objects on images and videos.
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
## Classifications
:::supervision.classification.core.Classifications
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
!!! warning
Dataset API is still fluid and may change. If you use Dataset API in your project until further notice, freeze the
+5
View File
@@ -1,3 +1,8 @@
---
comments: true
status: new
---
## Detections
:::supervision.detection.core.Detections
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
## InferenceSlicer
:::supervision.detection.tools.inference_slicer.InferenceSlicer
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
## LineZone
:::supervision.detection.line_counter.LineZone
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
## PolygonZone
:::supervision.detection.tools.polygon_zone.PolygonZone
+8
View File
@@ -0,0 +1,8 @@
---
comments: true
status: new
---
## Detection Smoother
:::supervision.detection.tools.smoother.DetectionsSmoother
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
## box_iou_batch
:::supervision.detection.utils.box_iou_batch
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
## Color
:::supervision.draw.color.Color
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
## draw_line
:::supervision.draw.utils.draw_line
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
## Position
:::supervision.geometry.core.Position
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
Supervision offers a streamlined solution to effortlessly annotate predictions from a
range of object detection and segmentation models. This guide demonstrates how to
execute inference using the YOLOv8 model with either the
-1
View File
@@ -1 +0,0 @@
🚧 Page under construction.
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
The advanced filtering capabilities of the `Detections` class offer users a versatile and efficient way to narrow down
and refine object detections. This section outlines various filtering methods, including filtering by specific class
or a set of classes, confidence, object area, bounding box area, relative area, box dimensions, and designated zones.
-1
View File
@@ -1 +0,0 @@
🚧 Page under construction.
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
Leverage Supervision's advanced capabilities for enhancing your video analysis by
seamlessly [tracking](https://supervision.roboflow.com/trackers/) objects recognized by
a multitude of object detection and segmentation models. This comprehensive guide will
+46 -1
View File
@@ -1,9 +1,13 @@
---
comments: true
---
<div align="center">
<p>
<a align="center" href="" target="_blank">
<img
width="850"
src="https://media.roboflow.com/open-source/supervision/roboflow-supervision-banner.png?ik-sdk-version=javascript-1.4.3&updatedAt=1674062891088"
src="https://media.roboflow.com/open-source/supervision/rf-supervision-banner.png?updatedAt=1678995927529"
>
</a>
</p>
@@ -13,6 +17,47 @@
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!
<video controls>
<source
src="https://media.roboflow.com/traffic_analysis_result.mp4"
type="video/mp4"
>
</video>
## 🚀 Quickstart
<div class="grid cards" markdown>
- __Detect and Annotate__
---
Annotate predictions from a range of object detection and segmentation models
[:octicons-arrow-right-24: Tutorial](how_to/detect_and_annotate)
- __Track Objects__
---
Discover how to enhance video analysis by implementing seamless object tracking
[:octicons-arrow-right-24: Tutorial](how_to/track_objects)
- > __Count Objects Crossing Line__
---
Explore methods to accurately count and analyze objects crossing a predefined line
- > __Filter Objects in Zone__
---
Master the techniques to selectively filter and focus on objects within a specific zone
</div>
## 💻 Install
You can install `supervision` with pip in a
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
!!! warning
Evaluation API is still fluid and may change. If you use Evaluation API in your project until further notice, freeze the
+51
View File
@@ -0,0 +1,51 @@
{% if page.meta.comments %}
<h2 id="__comments">{{ lang.t("meta.comments") }}</h2>
<!-- Insert Giscus code snippet from https://giscus.app/ here -->
<script src="https://giscus.app/client.js"
data-repo="roboflow/supervision"
data-repo-id="R_kgDOIhIiww"
data-category="Docs"
data-category-id="DIC_kwDOIhIiw84Ccet_"
data-mapping="pathname"
data-strict="0"
data-reactions-enabled="1"
data-emit-metadata="1"
data-input-position="top"
data-theme="preferred_color_scheme"
data-lang="en"
data-loading="lazy"
crossorigin="anonymous"
async>
</script>
<!-- Synchronize Giscus theme with palette -->
<script>
var giscus = document.querySelector("script[src*=giscus]")
/* Set palette on initial load */
var palette = __md_get("__palette")
if (palette && typeof palette.color === "object") {
var theme = palette.color.scheme === "slate" ? "dark" : "light"
giscus.setAttribute("data-theme", theme)
}
/* Register event handlers after documented loaded */
document.addEventListener("DOMContentLoaded", function() {
var ref = document.querySelector("[data-md-component=palette]")
ref.addEventListener("change", function() {
var palette = __md_get("__palette")
if (palette && typeof palette.color === "object") {
var theme = palette.color.scheme === "slate" ? "dark" : "light"
/* Instruct Giscus to change theme */
var frame = document.querySelector(".giscus-frame")
frame.contentWindow.postMessage(
{ giscus: { setConfig: { theme } } },
"https://giscus.app"
)
}
})
})
</script>
{% endif %}
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
## ByteTrack
:::supervision.tracker.byte_tracker.core.ByteTrack
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
## list_files_with_extensions
:::supervision.utils.file.list_files_with_extensions
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
## ImageSink
:::supervision.utils.image.ImageSink
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
## plot_image
:::supervision.utils.notebook.plot_image
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
## VideoInfo
:::supervision.utils.video.VideoInfo
+8
View File
@@ -3,3 +3,11 @@ data/*
# But re-include all .json files
!data/*.json
*.pt
*.pth
*.mp4
*.mov
*.png
*.jpg
*.jpeg
+7
View File
@@ -1,2 +1,9 @@
data/
venv*/
*.pt
*.pth
*.mp4
*.mov
*.png
*.jpg
*.jpeg
+10 -4
View File
@@ -1,18 +1,24 @@
# speed estimation
[![Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/roboflow-ai/notebooks/blob/main/notebooks/how-to-estimate-vehicle-speed-with-computer-vision.ipynb)
[![YouTube](https://badges.aleen42.com/src/youtube.svg)](https://youtu.be/uWP6UjDeZvY)
## 👋 hello
This example performs speed estimation analysis using various object-detection models
and ByteTrack - a simple yet effective online multi-object tracking method. It uses the
supervision package for multiple tasks such as tracking, annotations, etc.
https://github.com/roboflow/supervision/assets/26109316/0542fd3c-bb5f-475e-b96c-793560abeb18
https://github.com/roboflow/supervision/assets/26109316/d50118c1-2ae4-458d-915a-5d860fd36f71
> [!IMPORTANT]
> Adjust the [`SOURCE`](https://github.com/roboflow/supervision/blob/e32b05a636dab2ea1f39299e529c4b22b8baa8da/examples/speed_estimation/ultralytics_example.py#L10)
> and [`TARGET`](https://github.com/roboflow/supervision/blob/e32b05a636dab2ea1f39299e529c4b22b8baa8da/examples/speed_estimation/ultralytics_example.py#L15)
> configuration if you plan to run a speed estimation script on your video file. Those must be adjusted separately for each camera view. You can learn more
> from our YouTube [tutorial](https://youtu.be/uWP6UjDeZvY).
## 💻 install
> [!NOTE]
> YOLO-NAS is compatible with Python versions up to and including Python 3.10.
- clone repository and navigate to example directory
```bash
+4 -4
View File
@@ -1,6 +1,6 @@
supervision==0.18.0rc1
tqdm
tqdm==4.66.1
requests
ultralytics
super-gradients
inference
ultralytics==8.0.237
super-gradients==3.5.0
inference==0.9.8
@@ -29,6 +29,9 @@ class ViewTransformer:
self.m = cv2.getPerspectiveTransform(source, target)
def transform_points(self, points: np.ndarray) -> np.ndarray:
if points.size == 0:
return points
reshaped_points = points.reshape(-1, 1, 2).astype(np.float32)
transformed_points = cv2.perspectiveTransform(reshaped_points, self.m)
return transformed_points.reshape(-1, 2)
+7
View File
@@ -1,2 +1,9 @@
data/
venv/
*.pt
*.pth
*.mp4
*.mov
*.png
*.jpg
*.jpeg
+5 -2
View File
@@ -28,10 +28,8 @@ nav:
- Home: index.md
- How to:
- Detect and Annotate: how_to/detect_and_annotate.md
- Process Video: how_to/process_video.md
- Track Objects: how_to/track_objects.md
- Filter Detections: how_to/filter_detections.md
- Evaluate Model: how_to/evaluate_model.md
- API:
- Classifications:
- Core: classification/core.md
@@ -42,6 +40,7 @@ nav:
- Line Zone: detection/tools/line_zone.md
- Polygon Zone: detection/tools/polygon_zone.md
- Inference Slicer: detection/tools/inference_slicer.md
- Detection Smoother: detection/tools/smoother.md
- Annotators: annotators.md
- Trackers: trackers.md
- Datasets: datasets.md
@@ -82,6 +81,7 @@ theme:
code: Roboto Mono
features:
- content.code.copy
- content.code.annotate
plugins:
- mkdocstrings
@@ -97,6 +97,9 @@ markdown_extensions:
alternate_style: true
- toc:
permalink: true
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
extra_javascript:
- "https://widget.kapa.ai/kapa-widget.bundle.js"
Generated
+654 -507
View File
File diff suppressed because it is too large Load Diff
+10 -7
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "supervision"
version = "0.18.0rc2"
version = "0.18.0"
description = "A set of easy-to-use utils that will come in handy in any Computer Vision project"
authors = ["Piotr Skalski <piotr.skalski92@gmail.com>"]
maintainers = ["Piotr Skalski <piotr.skalski92@gmail.com>"]
@@ -33,17 +33,20 @@ classifiers=[
[tool.poetry.dependencies]
python = "^3.8"
numpy = ">=1.21.2"
scipy = ">=1.9.0"
matplotlib = ">=3.5.0"
scipy = [
{ version = "1.10.0", python = "<3.9" },
{ version = "^1.10.0", python = ">=3.9" }
]
matplotlib = ">=3.6.0"
pyyaml = ">=5.3"
pillow = ">=9.4"
opencv-python = { version = ">=4.5.5.64,<=4.8.1.78", optional = true }
opencv-python-headless = ">=4.5.5.64,<=4.8.1.78"
opencv-python = { version = ">=4.5.5.64", optional = true }
opencv-python-headless = ">=4.5.5.64"
requests = { version = ">=2.26.0,<=2.31.0", optional = true }
tqdm = { version = ">=4.62.3,<=4.66.1", optional = true }
defusedxml = "^0.7.1"
[tool.poetry.extras]
desktop = ["opencv-python"]
assets = ["requests","tqdm"]
@@ -62,7 +65,7 @@ tox = "^4.11.4"
[tool.poetry.group.docs.dependencies]
mkdocs-material = "^9.1.4"
mkdocs-material = {extras = ["imaging"], version = "^9.5.5"}
mkdocstrings = {extras = ["python"], version = ">=0.20,<0.25"}
+3
View File
@@ -18,6 +18,8 @@ from supervision.annotators.core import (
HeatMapAnnotator,
LabelAnnotator,
MaskAnnotator,
OrientedBoxAnnotator,
PercentageBarAnnotator,
PixelateAnnotator,
PolygonAnnotator,
RoundBoxAnnotator,
@@ -36,6 +38,7 @@ from supervision.detection.core import Detections
from supervision.detection.line_counter import LineZone, LineZoneAnnotator
from supervision.detection.tools.inference_slicer import InferenceSlicer
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,
+415 -162
View File
@@ -21,7 +21,7 @@ class BoundingBoxAnnotator(BaseAnnotator):
def __init__(
self,
color: Union[Color, ColorPalette] = ColorPalette.default(),
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
thickness: int = 2,
color_lookup: ColorLookup = ColorLookup.CLASS,
):
@@ -57,16 +57,16 @@ class BoundingBoxAnnotator(BaseAnnotator):
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> bounding_box_annotator = sv.BoundingBoxAnnotator()
>>> annotated_frame = bounding_box_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
bounding_box_annotator = sv.BoundingBoxAnnotator()
annotated_frame = bounding_box_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
![bounding-box-annotator-example](https://media.roboflow.com/
@@ -92,18 +92,98 @@ class BoundingBoxAnnotator(BaseAnnotator):
return scene
class OrientedBoxAnnotator(BaseAnnotator):
"""
A class for drawing oriented bounding boxes on an image using provided detections.
"""
def __init__(
self,
color: Union[Color, ColorPalette] = ColorPalette.default(),
thickness: int = 2,
color_lookup: ColorLookup = ColorLookup.CLASS,
):
"""
Args:
color (Union[Color, ColorPalette]): The color or color palette to use for
annotating detections.
thickness (int): Thickness of the bounding box lines.
color_lookup (str): Strategy for mapping colors to annotations.
Options are `INDEX`, `CLASS`, `TRACK`.
"""
self.color: Union[Color, ColorPalette] = color
self.thickness: int = thickness
self.color_lookup: ColorLookup = color_lookup
def annotate(
self,
scene: np.ndarray,
detections: Detections,
custom_color_lookup: Optional[np.ndarray] = None,
) -> np.ndarray:
"""
Annotates the given scene with oriented bounding boxes based on the provided detections.
Args:
scene (np.ndarray): The image where bounding boxes will be drawn.
detections (Detections): Object detections to annotate.
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
Allows to override the default color mapping strategy.
Returns:
The annotated image.
Example:
```python
import cv2
import supervision as sv
from ultralytics import YOLO
image = cv2.imread(<SOURCE_IMAGE_PATH>)
model = YOLO("yolov8n-obb.pt")
result = model(image)[0]
detections = sv.Detections.from_ultralytics(result)
oriented_box_annotator = sv.OrientedBoxAnnotator()
annotated_frame = oriented_box_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
""" # noqa E501 // docs
if detections.data is None or "xyxyxyxy" not in detections.data:
return scene
for detection_idx in range(len(detections)):
bbox = np.int0(detections.data.get("xyxyxyxy")[detection_idx])
color = resolve_color(
color=self.color,
detections=detections,
detection_idx=detection_idx,
color_lookup=self.color_lookup
if custom_color_lookup is None
else custom_color_lookup,
)
cv2.drawContours(scene, [bbox], 0, color.as_bgr(), self.thickness)
return scene
class MaskAnnotator(BaseAnnotator):
"""
A class for drawing masks on an image using provided detections.
!!! warning
This annotator utilizes the `sv.Detections.mask`.
This annotator uses `sv.Detections.mask`.
"""
def __init__(
self,
color: Union[Color, ColorPalette] = ColorPalette.default(),
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
opacity: float = 0.5,
color_lookup: ColorLookup = ColorLookup.CLASS,
):
@@ -139,16 +219,16 @@ class MaskAnnotator(BaseAnnotator):
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> mask_annotator = sv.MaskAnnotator()
>>> annotated_frame = mask_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
mask_annotator = sv.MaskAnnotator()
annotated_frame = mask_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
![mask-annotator-example](https://media.roboflow.com/
@@ -181,12 +261,12 @@ class PolygonAnnotator(BaseAnnotator):
!!! warning
This annotator utilizes the `sv.Detections.mask`.
This annotator uses `sv.Detections.mask`.
"""
def __init__(
self,
color: Union[Color, ColorPalette] = ColorPalette.default(),
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
thickness: int = 2,
color_lookup: ColorLookup = ColorLookup.CLASS,
):
@@ -222,16 +302,16 @@ class PolygonAnnotator(BaseAnnotator):
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> polygon_annotator = sv.PolygonAnnotator()
>>> annotated_frame = polygon_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
polygon_annotator = sv.PolygonAnnotator()
annotated_frame = polygon_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
![polygon-annotator-example](https://media.roboflow.com/
@@ -268,7 +348,7 @@ class ColorAnnotator(BaseAnnotator):
def __init__(
self,
color: Union[Color, ColorPalette] = ColorPalette.default(),
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
opacity: float = 0.5,
color_lookup: ColorLookup = ColorLookup.CLASS,
):
@@ -304,16 +384,16 @@ class ColorAnnotator(BaseAnnotator):
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> color_annotator = sv.ColorAnnotator()
>>> annotated_frame = color_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
color_annotator = sv.ColorAnnotator()
annotated_frame = color_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
![box-mask-annotator-example](https://media.roboflow.com/
@@ -349,12 +429,12 @@ class HaloAnnotator(BaseAnnotator):
!!! warning
This annotator utilizes the `sv.Detections.mask`.
This annotator uses `sv.Detections.mask`.
"""
def __init__(
self,
color: Union[Color, ColorPalette] = ColorPalette.default(),
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
opacity: float = 0.8,
kernel_size: int = 40,
color_lookup: ColorLookup = ColorLookup.CLASS,
@@ -394,16 +474,16 @@ class HaloAnnotator(BaseAnnotator):
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> halo_annotator = sv.HaloAnnotator()
>>> annotated_frame = halo_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
halo_annotator = sv.HaloAnnotator()
annotated_frame = halo_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
![halo-annotator-example](https://media.roboflow.com/
@@ -446,7 +526,7 @@ class EllipseAnnotator(BaseAnnotator):
def __init__(
self,
color: Union[Color, ColorPalette] = ColorPalette.default(),
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
thickness: int = 2,
start_angle: int = -45,
end_angle: int = 235,
@@ -488,16 +568,16 @@ class EllipseAnnotator(BaseAnnotator):
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> ellipse_annotator = sv.EllipseAnnotator()
>>> annotated_frame = ellipse_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
ellipse_annotator = sv.EllipseAnnotator()
annotated_frame = ellipse_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
![ellipse-annotator-example](https://media.roboflow.com/
@@ -536,7 +616,7 @@ class BoxCornerAnnotator(BaseAnnotator):
def __init__(
self,
color: Union[Color, ColorPalette] = ColorPalette.default(),
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
thickness: int = 4,
corner_length: int = 15,
color_lookup: ColorLookup = ColorLookup.CLASS,
@@ -575,16 +655,16 @@ class BoxCornerAnnotator(BaseAnnotator):
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> corner_annotator = sv.BoxCornerAnnotator()
>>> annotated_frame = corner_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
corner_annotator = sv.BoxCornerAnnotator()
annotated_frame = corner_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
![box-corner-annotator-example](https://media.roboflow.com/
@@ -622,7 +702,7 @@ class CircleAnnotator(BaseAnnotator):
def __init__(
self,
color: Union[Color, ColorPalette] = ColorPalette.default(),
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
thickness: int = 2,
color_lookup: ColorLookup = ColorLookup.CLASS,
):
@@ -659,16 +739,16 @@ class CircleAnnotator(BaseAnnotator):
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> circle_annotator = sv.CircleAnnotator()
>>> annotated_frame = circle_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
circle_annotator = sv.CircleAnnotator()
annotated_frame = circle_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
@@ -706,7 +786,7 @@ class DotAnnotator(BaseAnnotator):
def __init__(
self,
color: Union[Color, ColorPalette] = ColorPalette.default(),
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
radius: int = 4,
position: Position = Position.CENTER,
color_lookup: ColorLookup = ColorLookup.CLASS,
@@ -745,16 +825,16 @@ class DotAnnotator(BaseAnnotator):
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> dot_annotator = sv.DotAnnotator()
>>> annotated_frame = dot_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
dot_annotator = sv.DotAnnotator()
annotated_frame = dot_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
![dot-annotator-example](https://media.roboflow.com/
@@ -782,8 +862,8 @@ class LabelAnnotator:
def __init__(
self,
color: Union[Color, ColorPalette] = ColorPalette.default(),
text_color: Color = Color.black(),
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
text_color: Color = Color.WHITE,
text_scale: float = 0.5,
text_thickness: int = 1,
text_padding: int = 10,
@@ -872,16 +952,16 @@ class LabelAnnotator:
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> label_annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER)
>>> annotated_frame = label_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
label_annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER)
annotated_frame = label_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
![label-annotator-example](https://media.roboflow.com/
@@ -986,16 +1066,16 @@ class BlurAnnotator(BaseAnnotator):
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> blur_annotator = sv.BlurAnnotator()
>>> annotated_frame = circle_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
blur_annotator = sv.BlurAnnotator()
annotated_frame = circle_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
![blur-annotator-example](https://media.roboflow.com/
@@ -1020,14 +1100,14 @@ class TraceAnnotator:
!!! warning
This annotator utilizes the `sv.Detections.tracker_id`. Read
This annotator uses the `sv.Detections.tracker_id`. Read
[here](https://supervision.roboflow.com/trackers/) to learn how to plug
tracking into your inference pipeline.
"""
def __init__(
self,
color: Union[Color, ColorPalette] = ColorPalette.default(),
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
position: Position = Position.CENTER,
trace_length: int = 30,
thickness: int = 2,
@@ -1071,26 +1151,25 @@ class TraceAnnotator:
Example:
```python
>>> import supervision as sv
>>> from ultralytics import YOLO
import supervision as sv
from ultralytics import YOLO
>>> model = YOLO('yolov8x.pt')
model = YOLO('yolov8x.pt')
trace_annotator = sv.TraceAnnotator()
>>> trace_annotator = sv.TraceAnnotator()
video_info = sv.VideoInfo.from_video_path(video_path='...')
frames_generator = sv.get_video_frames_generator(source_path='...')
tracker = sv.ByteTrack()
>>> video_info = sv.VideoInfo.from_video_path(video_path='...')
>>> frames_generator = sv.get_video_frames_generator(source_path='...')
>>> tracker = sv.ByteTrack()
>>> with sv.VideoSink(target_path='...', video_info=video_info) as sink:
... for frame in frames_generator:
... result = model(frame)[0]
... detections = sv.Detections.from_ultralytics(result)
... detections = tracker.update_with_detections(detections)
... annotated_frame = trace_annotator.annotate(
... scene=frame.copy(),
... detections=detections)
... sink.write_frame(frame=annotated_frame)
with sv.VideoSink(target_path='...', video_info=video_info) as sink:
for frame in frames_generator:
result = model(frame)[0]
detections = sv.Detections.from_ultralytics(result)
detections = tracker.update_with_detections(detections)
annotated_frame = trace_annotator.annotate(
scene=frame.copy(),
detections=detections)
sink.write_frame(frame=annotated_frame)
```
![trace-annotator-example](https://media.roboflow.com/
@@ -1167,24 +1246,24 @@ class HeatMapAnnotator:
Example:
```python
>>> import supervision as sv
>>> from ultralytics import YOLO
import supervision as sv
from ultralytics import YOLO
>>> model = YOLO('yolov8x.pt')
model = YOLO('yolov8x.pt')
>>> heat_map_annotator = sv.HeatMapAnnotator()
heat_map_annotator = sv.HeatMapAnnotator()
>>> video_info = sv.VideoInfo.from_video_path(video_path='...')
>>> frames_generator = get_video_frames_generator(source_path='...')
video_info = sv.VideoInfo.from_video_path(video_path='...')
frames_generator = get_video_frames_generator(source_path='...')
>>> with sv.VideoSink(target_path='...', video_info=video_info) as sink:
... for frame in frames_generator:
... result = model(frame)[0]
... detections = sv.Detections.from_ultralytics(result)
... annotated_frame = heat_map_annotator.annotate(
... scene=frame.copy(),
... detections=detections)
... sink.write_frame(frame=annotated_frame)
with sv.VideoSink(target_path='...', video_info=video_info) as sink:
for frame in frames_generator:
result = model(frame)[0]
detections = sv.Detections.from_ultralytics(result)
annotated_frame = heat_map_annotator.annotate(
scene=frame.copy(),
detections=detections)
sink.write_frame(frame=annotated_frame)
```
![heatmap-annotator-example](https://media.roboflow.com/
@@ -1244,16 +1323,16 @@ class PixelateAnnotator(BaseAnnotator):
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> pixelate_annotator = sv.PixelateAnnotator()
>>> annotated_frame = pixelate_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
pixelate_annotator = sv.PixelateAnnotator()
annotated_frame = pixelate_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
![pixelate-annotator-example](https://media.roboflow.com/
@@ -1288,7 +1367,7 @@ class TriangleAnnotator(BaseAnnotator):
def __init__(
self,
color: Union[Color, ColorPalette] = ColorPalette.default(),
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
base: int = 10,
height: int = 10,
position: Position = Position.TOP_CENTER,
@@ -1330,16 +1409,16 @@ class TriangleAnnotator(BaseAnnotator):
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
>>> triangle_annotator = sv.TriangleAnnotator()
>>> annotated_frame = triangle_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
triangle_annotator = sv.TriangleAnnotator()
annotated_frame = triangle_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
![triangle-annotator-example](https://media.roboflow.com/
@@ -1378,7 +1457,7 @@ class RoundBoxAnnotator(BaseAnnotator):
def __init__(
self,
color: Union[Color, ColorPalette] = ColorPalette.default(),
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
thickness: int = 2,
color_lookup: ColorLookup = ColorLookup.CLASS,
roundness: float = 0.6,
@@ -1423,20 +1502,20 @@ class RoundBoxAnnotator(BaseAnnotator):
Example:
```python
>>> import supervision as sv
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
)
```
![round-box-annotator-example](https://media.roboflow.com/
supervision-annotator-examples/round-box-annotator-example.png)
supervision-annotator-examples/round-box-annotator-example-purple.png)
"""
for detection_idx in range(len(detections)):
@@ -1496,3 +1575,177 @@ class RoundBoxAnnotator(BaseAnnotator):
)
return scene
class PercentageBarAnnotator(BaseAnnotator):
"""
A class for drawing percentage bars on an image using provided detections.
"""
def __init__(
self,
height: int = 16,
width: int = 80,
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
border_color: Color = Color.BLACK,
position: Position = Position.TOP_CENTER,
color_lookup: ColorLookup = ColorLookup.CLASS,
border_thickness: int = None,
):
"""
Args:
height (int): The height in pixels of the percentage bar.
width (int): The width in pixels of the percentage bar.
color (Union[Color, ColorPalette]): The color or color palette to use for
annotating detections.
border_color (Color): The color of the border lines.
position (Position): The anchor position of drawing the percentage bar.
color_lookup (str): Strategy for mapping colors to annotations.
Options are `INDEX`, `CLASS`, `TRACK`.
border_thickness (int): The thickness of the border lines.
"""
self.height: int = height
self.width: int = width
self.color: Union[Color, ColorPalette] = color
self.border_color: Color = border_color
self.position: Position = position
self.color_lookup: ColorLookup = color_lookup
if border_thickness is None:
self.border_thickness = int(0.15 * self.height)
def annotate(
self,
scene: np.ndarray,
detections: Detections,
custom_color_lookup: Optional[np.ndarray] = None,
custom_values: Optional[np.ndarray] = None,
) -> np.ndarray:
"""
Annotates the given scene with percentage bars based on the provided
detections. The percentage bars visually represent the confidence or custom
values associated with each detection.
Args:
scene (np.ndarray): The image where percentage bars will be drawn.
detections (Detections): Object detections to annotate.
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
Allows to override the default color mapping strategy.
custom_values (Optional[np.ndarray]): Custom values array to use instead
of the default detection confidences. This array should have the
same length as the number of detections and contain a value between
0 and 1 (inclusive) for each detection, representing the percentage
to be displayed.
Returns:
The annotated image.
Example:
```python
import supervision as sv
image = ...
detections = sv.Detections(...)
percentage_bar_annotator = sv.BoundingBoxAnnotator()
annotated_frame = percentage_bar_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
![percentage-bar-example](https://media.roboflow.com/
supervision-annotator-examples/percentage-bar-annotator-example-purple.png)
"""
self.validate_custom_values(
custom_values=custom_values, detections_count=len(detections)
)
anchors = detections.get_anchors_coordinates(anchor=self.position)
for detection_idx in range(len(detections)):
anchor = anchors[detection_idx]
border_coordinates = self.calculate_border_coordinates(
anchor_xy=(int(anchor[0]), int(anchor[1])),
border_wh=(self.width, self.height),
position=self.position,
)
border_width = border_coordinates[1][0] - border_coordinates[0][0]
value = (
custom_values[detection_idx]
if custom_values is not None
else detections.confidence[detection_idx]
)
color = resolve_color(
color=self.color,
detections=detections,
detection_idx=detection_idx,
color_lookup=self.color_lookup
if custom_color_lookup is None
else custom_color_lookup,
)
cv2.rectangle(
img=scene,
pt1=border_coordinates[0],
pt2=(
border_coordinates[0][0] + int(border_width * value),
border_coordinates[1][1],
),
color=color.as_bgr(),
thickness=-1,
)
cv2.rectangle(
img=scene,
pt1=border_coordinates[0],
pt2=border_coordinates[1],
color=self.border_color.as_bgr(),
thickness=self.border_thickness,
)
return scene
@staticmethod
def calculate_border_coordinates(
anchor_xy: Tuple[int, int], border_wh: Tuple[int, int], position: Position
) -> Tuple[Tuple[int, int], Tuple[int, int]]:
cx, cy = anchor_xy
width, height = border_wh
if position == Position.TOP_LEFT:
return (cx - width, cy - height), (cx, cy)
elif position == Position.TOP_CENTER:
return (cx - width // 2, cy), (cx + width // 2, cy - height)
elif position == Position.TOP_RIGHT:
return (cx, cy), (cx + width, cy - height)
elif position == Position.CENTER_LEFT:
return (cx - width, cy - height // 2), (cx, cy + height // 2)
elif position == Position.CENTER or position == Position.CENTER_OF_MASS:
return (
(cx - width // 2, cy - height // 2),
(cx + width // 2, cy + height // 2),
)
elif position == Position.CENTER_RIGHT:
return (cx, cy - height // 2), (cx + width, cy + height // 2)
elif position == Position.BOTTOM_LEFT:
return (cx - width, cy), (cx, cy + height)
elif position == Position.BOTTOM_CENTER:
return (cx - width // 2, cy), (cx + width // 2, cy + height)
elif position == Position.BOTTOM_RIGHT:
return (cx, cy), (cx + width, cy + height)
@staticmethod
def validate_custom_values(
custom_values: Optional[Union[np.ndarray, List[float]]], detections_count: int
) -> None:
if custom_values is not None:
if not isinstance(custom_values, (np.ndarray, list)):
raise TypeError(
"custom_values must be either a numpy array or a list of floats."
)
if len(custom_values) != detections_count:
raise ValueError(
"The length of custom_values must match the number of detections."
)
if not all(0 <= value <= 1 for value in custom_values):
raise ValueError("All values in custom_values must be between 0 and 1.")
+2 -2
View File
@@ -54,9 +54,9 @@ def download_assets(asset_name: Union[VideoAssets, str]) -> str:
Example:
```python
>>> from supervision.assets import download_assets, VideoAssets
from supervision.assets import download_assets, VideoAssets
>>> download_assets(VideoAssets.VEHICLES)
download_assets(VideoAssets.VEHICLES)
"vehicles.mp4"
```
"""
+33 -33
View File
@@ -59,18 +59,18 @@ class Classifications:
Example:
```python
>>> from PIL import Image
>>> import clip
>>> import supervision as sv
from PIL import Image
import clip
import supervision as sv
>>> model, preprocess = clip.load('ViT-B/32')
model, preprocess = clip.load('ViT-B/32')
>>> image = cv2.imread(SOURCE_IMAGE_PATH)
>>> image = preprocess(image).unsqueeze(0)
image = cv2.imread(SOURCE_IMAGE_PATH)
image = preprocess(image).unsqueeze(0)
>>> text = clip.tokenize(["a diagram", "a dog", "a cat"])
>>> output, _ = model(image, text)
>>> classifications = sv.Classifications.from_clip(output)
text = clip.tokenize(["a diagram", "a dog", "a cat"])
output, _ = model(image, text)
classifications = sv.Classifications.from_clip(output)
```
"""
@@ -97,15 +97,15 @@ class Classifications:
Example:
```python
>>> import cv2
>>> from ultralytics import YOLO
>>> import supervision as sv
import cv2
from ultralytics import YOLO
import supervision as sv
>>> image = cv2.imread(SOURCE_IMAGE_PATH)
>>> model = YOLO('yolov8n-cls.pt')
image = cv2.imread(SOURCE_IMAGE_PATH)
model = YOLO('yolov8n-cls.pt')
>>> output = model(image)[0]
>>> classifications = sv.Classifications.from_ultralytics(output)
output = model(image)[0]
classifications = sv.Classifications.from_ultralytics(output)
```
"""
confidence = ultralytics_results.probs.data.cpu().numpy()
@@ -125,25 +125,25 @@ class Classifications:
Example:
```python
>>> from PIL import Image
>>> import timm
>>> from timm.data import resolve_data_config, create_transform
>>> import supervision as sv
from PIL import Image
import timm
from timm.data import resolve_data_config, create_transform
import supervision as sv
>>> model = timm.create_model(
... model_name='hf-hub:nateraw/resnet50-oxford-iiit-pet',
... pretrained=True
... ).eval()
model = timm.create_model(
model_name='hf-hub:nateraw/resnet50-oxford-iiit-pet',
pretrained=True
).eval()
>>> config = resolve_data_config({}, model=model)
>>> transform = create_transform(**config)
config = resolve_data_config({}, model=model)
transform = create_transform(**config)
>>> image = Image.open(SOURCE_IMAGE_PATH).convert('RGB')
>>> x = transform(image).unsqueeze(0)
image = Image.open(SOURCE_IMAGE_PATH).convert('RGB')
x = transform(image).unsqueeze(0)
>>> output = model(x)
output = model(x)
>>> classifications = sv.Classifications.from_timm(output)
classifications = sv.Classifications.from_timm(output)
```
"""
confidence = timm_results.cpu().detach().numpy()[0]
@@ -168,11 +168,11 @@ class Classifications:
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> classifications = sv.Classifications(...)
classifications = sv.Classifications(...)
>>> classifications.get_top_k(1)
classifications.get_top_k(1)
(array([1]), array([0.9]))
```
+78 -81
View File
@@ -118,13 +118,13 @@ class DetectionDataset(BaseDataset):
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> ds = sv.DetectionDataset(...)
>>> train_ds, test_ds = ds.split(split_ratio=0.7,
... random_state=42, shuffle=True)
>>> len(train_ds), len(test_ds)
(700, 300)
ds = sv.DetectionDataset(...)
train_ds, test_ds = ds.split(split_ratio=0.7,
random_state=42, shuffle=True)
len(train_ds), len(test_ds)
# (700, 300)
```
"""
@@ -231,24 +231,24 @@ class DetectionDataset(BaseDataset):
Example:
```python
>>> import roboflow
>>> from roboflow import Roboflow
>>> import supervision as sv
import roboflow
from roboflow import Roboflow
import supervision as sv
>>> roboflow.login()
roboflow.login()
>>> rf = Roboflow()
rf = Roboflow()
>>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
>>> dataset = project.version(PROJECT_VERSION).download("voc")
project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
dataset = project.version(PROJECT_VERSION).download("voc")
>>> ds = sv.DetectionDataset.from_pascal_voc(
... images_directory_path=f"{dataset.location}/train/images",
... annotations_directory_path=f"{dataset.location}/train/labels"
... )
ds = sv.DetectionDataset.from_pascal_voc(
images_directory_path=f"{dataset.location}/train/images",
annotations_directory_path=f"{dataset.location}/train/labels"
)
>>> ds.classes
['dog', 'person']
ds.classes
# ['dog', 'person']
```
"""
@@ -288,25 +288,24 @@ class DetectionDataset(BaseDataset):
Example:
```python
>>> import roboflow
>>> from roboflow import Roboflow
>>> import supervision as sv
import roboflow
from roboflow import Roboflow
import supervision as sv
>>> roboflow.login()
roboflow.login()
rf = Roboflow()
>>> rf = Roboflow()
project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
dataset = project.version(PROJECT_VERSION).download("yolov5")
>>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
>>> dataset = project.version(PROJECT_VERSION).download("yolov5")
ds = sv.DetectionDataset.from_yolo(
images_directory_path=f"{dataset.location}/train/images",
annotations_directory_path=f"{dataset.location}/train/labels",
data_yaml_path=f"{dataset.location}/data.yaml"
)
>>> ds = sv.DetectionDataset.from_yolo(
... images_directory_path=f"{dataset.location}/train/images",
... annotations_directory_path=f"{dataset.location}/train/labels",
... data_yaml_path=f"{dataset.location}/data.yaml"
... )
>>> ds.classes
['dog', 'person']
ds.classes
# ['dog', 'person']
```
"""
classes, images, annotations = load_yolo_annotations(
@@ -394,24 +393,23 @@ class DetectionDataset(BaseDataset):
Example:
```python
>>> import roboflow
>>> from roboflow import Roboflow
>>> import supervision as sv
import roboflow
from roboflow import Roboflow
import supervision as sv
>>> roboflow.login()
roboflow.login()
rf = Roboflow()
>>> rf = Roboflow()
project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
dataset = project.version(PROJECT_VERSION).download("coco")
>>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
>>> dataset = project.version(PROJECT_VERSION).download("coco")
ds = sv.DetectionDataset.from_coco(
images_directory_path=f"{dataset.location}/train",
annotations_path=f"{dataset.location}/train/_annotations.coco.json",
)
>>> ds = sv.DetectionDataset.from_coco(
... images_directory_path=f"{dataset.location}/train",
... annotations_path=f"{dataset.location}/train/_annotations.coco.json",
... )
>>> ds.classes
['dog', 'person']
ds.classes
# ['dog', 'person']
```
"""
classes, images, annotations = load_coco_annotations(
@@ -486,25 +484,25 @@ class DetectionDataset(BaseDataset):
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> ds_1 = sv.DetectionDataset(...)
>>> len(ds_1)
100
>>> ds_1.classes
['dog', 'person']
ds_1 = sv.DetectionDataset(...)
len(ds_1)
# 100
ds_1.classes
# ['dog', 'person']
>>> ds_2 = sv.DetectionDataset(...)
>>> len(ds_2)
200
>>> ds_2.classes
['cat']
ds_2 = sv.DetectionDataset(...)
len(ds_2)
# 200
ds_2.classes
# ['cat']
>>> ds_merged = sv.DetectionDataset.merge([ds_1, ds_2])
>>> len(ds_merged)
300
>>> ds_merged.classes
['cat', 'dog', 'person']
ds_merged = sv.DetectionDataset.merge([ds_1, ds_2])
len(ds_merged)
# 300
ds_merged.classes
# ['cat', 'dog', 'person']
```
"""
merged_images, merged_annotations = {}, {}
@@ -571,13 +569,13 @@ class ClassificationDataset(BaseDataset):
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> cd = sv.ClassificationDataset(...)
>>> train_cd,test_cd = cd.split(split_ratio=0.7,
... random_state=42,shuffle=True)
>>> len(train_cd), len(test_cd)
(700, 300)
cd = sv.ClassificationDataset(...)
train_cd,test_cd = cd.split(split_ratio=0.7,
random_state=42,shuffle=True)
len(train_cd), len(test_cd)
# (700, 300)
```
"""
image_names = list(self.images.keys())
@@ -639,20 +637,19 @@ class ClassificationDataset(BaseDataset):
Example:
```python
>>> import roboflow
>>> from roboflow import Roboflow
>>> import supervision as sv
import roboflow
from roboflow import Roboflow
import supervision as sv
>>> roboflow.login()
roboflow.login()
rf = Roboflow()
>>> rf = Roboflow()
project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
dataset = project.version(PROJECT_VERSION).download("folder")
>>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
>>> dataset = project.version(PROJECT_VERSION).download("folder")
>>> cd = sv.ClassificationDataset.from_folder_structure(
... root_directory_path=f"{dataset.location}/train"
... )
cd = sv.ClassificationDataset.from_folder_structure(
root_directory_path=f"{dataset.location}/train"
)
```
"""
classes = os.listdir(root_directory_path)
+16 -17
View File
@@ -26,9 +26,9 @@ class BoxAnnotator:
def __init__(
self,
color: Union[Color, ColorPalette] = ColorPalette.default(),
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
thickness: int = 2,
text_color: Color = Color.black(),
text_color: Color = Color.BLACK,
text_scale: float = 0.5,
text_thickness: int = 1,
text_padding: int = 10,
@@ -63,23 +63,22 @@ class BoxAnnotator:
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> classes = ['person', ...]
>>> image = ...
>>> detections = sv.Detections(...)
classes = ['person', ...]
image = ...
detections = sv.Detections(...)
>>> box_annotator = sv.BoxAnnotator()
>>> labels = [
... f"{classes[class_id]} {confidence:0.2f}"
... for _, _, confidence, class_id, _
... in detections
... ]
>>> annotated_frame = box_annotator.annotate(
... scene=image.copy(),
... detections=detections,
... labels=labels
... )
box_annotator = sv.BoxAnnotator()
labels = [
f"{classes[class_id]} {confidence:0.2f}"
for _, _, confidence, class_id, _ in detections
]
annotated_frame = box_annotator.annotate(
scene=image.copy(),
detections=detections,
labels=labels
)
```
"""
font = cv2.FONT_HERSHEY_SIMPLEX
+175 -154
View File
@@ -126,14 +126,14 @@ class Detections:
Example:
```python
>>> import cv2
>>> import torch
>>> import supervision as sv
import cv2
import torch
import supervision as sv
>>> image = cv2.imread(SOURCE_IMAGE_PATH)
>>> model = torch.hub.load('ultralytics/yolov5', 'yolov5s')
>>> result = model(image)
>>> detections = sv.Detections.from_yolov5(result)
image = cv2.imread(<SOURCE_IMAGE_PATH>)
model = torch.hub.load('ultralytics/yolov5', 'yolov5s')
result = model(image)
detections = sv.Detections.from_yolov5(result)
```
"""
yolov5_detections_predictions = yolov5_results.pred[0].cpu().cpu().numpy()
@@ -150,6 +150,13 @@ class Detections:
Creates a Detections instance from a
[YOLOv8](https://github.com/ultralytics/ultralytics) inference result.
!!! Note
`from_ultralytics` is compatible with
[detection](https://docs.ultralytics.com/tasks/detect/),
[segmentation](https://docs.ultralytics.com/tasks/segment/), and
[OBB](https://docs.ultralytics.com/tasks/obb/) models.
Args:
ultralytics_results (ultralytics.yolo.engine.results.Results):
The output Results instance from YOLOv8
@@ -159,16 +166,28 @@ class Detections:
Example:
```python
>>> import cv2
>>> import supervision as sv
>>> from ultralytics import YOLO
import cv2
import supervision as sv
from ultralytics import YOLO
>>> image = cv2.imread(...)
>>> model = YOLO('yolov8s.pt')
>>> result = model(image)[0]
>>> detections = sv.Detections.from_ultralytics(result)
image = cv2.imread(<SOURCE_IMAGE_PATH>)
model = YOLO('yolov8s.pt')
result = model(image)[0]
detections = sv.Detections.from_ultralytics(result)
```
"""
""" # noqa: E501 // docs
if ultralytics_results.obb is not None:
return cls(
xyxy=ultralytics_results.obb.xyxy.cpu().numpy(),
data={"xyxyxyxy": ultralytics_results.obb.xyxyxyxy.cpu().numpy()},
confidence=ultralytics_results.obb.conf.cpu().numpy(),
class_id=ultralytics_results.obb.cls.cpu().numpy().astype(int),
tracker_id=ultralytics_results.obb.id.int().cpu().numpy()
if ultralytics_results.obb.id is not None
else None,
)
return cls(
xyxy=ultralytics_results.boxes.xyxy.cpu().numpy(),
@@ -198,14 +217,15 @@ class Detections:
Example:
```python
>>> import cv2
>>> from super_gradients.training import models
>>> import supervision as sv
import cv2
from super_gradients.training import models
import supervision as sv
>>> image = cv2.imread(SOURCE_IMAGE_PATH)
>>> model = models.get('yolo_nas_l', pretrained_weights="coco")
>>> result = list(model.predict(image, conf=0.35))[0]
>>> detections = sv.Detections.from_yolo_nas(result)
image = cv2.imread(<SOURCE_IMAGE_PATH>)
model = models.get('yolo_nas_l', pretrained_weights="coco")
result = list(model.predict(image, conf=0.35))[0]
detections = sv.Detections.from_yolo_nas(result)
```
"""
if np.asarray(yolo_nas_results.prediction.bboxes_xyxy).shape[0] == 0:
@@ -235,20 +255,16 @@ class Detections:
Example:
```python
>>> import tensorflow as tf
>>> import tensorflow_hub as hub
>>> import numpy as np
>>> import cv2
import tensorflow as tf
import tensorflow_hub as hub
import numpy as np
import cv2
>>> module_handle = "https://tfhub.dev/tensorflow/centernet/hourglass_512x512_kpts/1"
>>> model = hub.load(module_handle)
>>> img = np.array(cv2.imread(SOURCE_IMAGE_PATH))
>>> result = model(img)
>>> detections = sv.Detections.from_tensorflow(result)
module_handle = "https://tfhub.dev/tensorflow/centernet/hourglass_512x512_kpts/1"
model = hub.load(module_handle)
img = np.array(cv2.imread(SOURCE_IMAGE_PATH))
result = model(img)
detections = sv.Detections.from_tensorflow(result)
```
""" # noqa: E501 // docs
@@ -278,15 +294,15 @@ class Detections:
Example:
```python
>>> import supervision as sv
>>> from deepsparse import Pipeline
import supervision as sv
from deepsparse import Pipeline
>>> yolo_pipeline = Pipeline.create(
... task="yolo",
... model_path = "zoo:cv/detection/yolov5-l/pytorch/ultralytics/coco/pruned80_quant-none"
... )
>>> result = yolo_pipeline(<SOURCE IMAGE PATH>)
>>> detections = sv.Detections.from_deepsparse(result)
yolo_pipeline = Pipeline.create(
task="yolo",
model_path = "zoo:cv/detection/yolov5-l/pytorch/ultralytics/coco/pruned80_quant-none"
)
result = yolo_pipeline(<SOURCE IMAGE PATH>)
detections = sv.Detections.from_deepsparse(result)
```
""" # noqa: E501 // docs
@@ -302,9 +318,9 @@ class Detections:
@classmethod
def from_mmdetection(cls, mmdet_results) -> Detections:
"""
Creates a Detections instance from
a [mmdetection](https://github.com/open-mmlab/mmdetection) inference result.
Also supported for [mmyolo](https://github.com/open-mmlab/mmyolo)
Creates a Detections instance from a
[mmdetection](https://github.com/open-mmlab/mmdetection) and
[mmyolo](https://github.com/open-mmlab/mmyolo) inference result.
Args:
mmdet_results (mmdet.structures.DetDataSample):
@@ -315,16 +331,17 @@ class Detections:
Example:
```python
>>> import cv2
>>> import supervision as sv
>>> from mmdet.apis import DetInferencer
import cv2
import supervision as sv
from mmdet.apis import init_detector, inference_detector
>>> inferencer = DetInferencer(model_name, checkpoint, device)
>>> mmdet_result = inferencer(SOURCE_IMAGE_PATH, out_dir='./output',
... return_datasamples=True)["predictions"][0]
>>> detections = sv.Detections.from_mmdetection(mmdet_result)
image = cv2.imread(<SOURCE_IMAGE_PATH>)
model = init_detector(<CONFIG_PATH>, <WEIGHTS_PATH>, device=<DEVICE>)
result = inference_detector(model, image)
detections = sv.Detections.from_mmdetection(result)
```
"""
""" # noqa: E501 // docs
return cls(
xyxy=mmdet_results.pred_instances.bboxes.cpu().numpy(),
@@ -364,18 +381,20 @@ class Detections:
Example:
```python
>>> import cv2
>>> from detectron2.engine import DefaultPredictor
>>> from detectron2.config import get_cfg
>>> import supervision as sv
import cv2
import supervision as sv
from detectron2.engine import DefaultPredictor
from detectron2.config import get_cfg
>>> image = cv2.imread(SOURCE_IMAGE_PATH)
>>> cfg = get_cfg()
>>> cfg.merge_from_file("path/to/config.yaml")
>>> cfg.MODEL.WEIGHTS = "path/to/model_weights.pth"
>>> predictor = DefaultPredictor(cfg)
>>> result = predictor(image)
>>> detections = sv.Detections.from_detectron2(result)
image = cv2.imread(<SOURCE_IMAGE_PATH>)
cfg = get_cfg()
cfg.merge_from_file(<CONFIG_PATH>)
cfg.MODEL.WEIGHTS = <WEIGHTS_PATH>
predictor = DefaultPredictor(cfg)
result = predictor(image)
detections = sv.Detections.from_detectron2(result)
```
"""
@@ -412,14 +431,15 @@ class Detections:
Example:
```python
>>> import cv2
>>> import supervision as sv
>>> from inference.models.utils import get_roboflow_model
import cv2
import supervision as sv
from inference.models.utils import get_roboflow_model
>>> image = cv2.imread(...)
>>> model = get_roboflow_model(model_id="yolov8s-640")
>>> result = model.infer(image)[0]
>>> detections = sv.Detections.from_inference(result)
image = cv2.imread(<SOURCE_IMAGE_PATH>)
model = get_roboflow_model(model_id="yolov8s-640")
result = model.infer(image)[0]
detections = sv.Detections.from_inference(result)
```
"""
with suppress(AttributeError):
@@ -461,14 +481,15 @@ class Detections:
Example:
```python
>>> import cv2
>>> import supervision as sv
>>> from inference.models.utils import get_roboflow_model
import cv2
import supervision as sv
from inference.models.utils import get_roboflow_model
>>> image = cv2.imread(...)
>>> model = get_roboflow_model(model_id="yolov8s-640")
>>> result = model.infer(image)[0]
>>> detections = sv.Detections.from_roboflow(result)
image = cv2.imread(<SOURCE_IMAGE_PATH>)
model = get_roboflow_model(model_id="yolov8s-640")
result = model.infer(image)[0]
detections = sv.Detections.from_roboflow(result)
```
"""
return cls.from_inference(roboflow_result)
@@ -488,17 +509,17 @@ class Detections:
Example:
```python
>>> import supervision as sv
>>> from segment_anything import (
... sam_model_registry,
... SamAutomaticMaskGenerator
... )
import supervision as sv
from segment_anything import (
sam_model_registry,
SamAutomaticMaskGenerator
)
>>> sam_model_reg = sam_model_registry[MODEL_TYPE]
>>> sam = sam_model_reg(checkpoint=CHECKPOINT_PATH).to(device=DEVICE)
>>> mask_generator = SamAutomaticMaskGenerator(sam)
>>> sam_result = mask_generator.generate(IMAGE)
>>> detections = sv.Detections.from_sam(sam_result=sam_result)
sam_model_reg = sam_model_registry[MODEL_TYPE]
sam = sam_model_reg(checkpoint=CHECKPOINT_PATH).to(device=DEVICE)
mask_generator = SamAutomaticMaskGenerator(sam)
sam_result = mask_generator.generate(IMAGE)
detections = sv.Detections.from_sam(sam_result=sam_result)
```
"""
@@ -535,25 +556,25 @@ class Detections:
Example:
```python
>>> import requests
>>> import supervision as sv
import requests
import supervision as sv
>>> image = open(input, "rb").read()
image = open(input, "rb").read()
>>> endpoint = "https://.cognitiveservices.azure.com/"
>>> subscription_key = "..."
endpoint = "https://.cognitiveservices.azure.com/"
subscription_key = ""
>>> headers = {
... "Content-Type": "application/octet-stream",
... "Ocp-Apim-Subscription-Key": subscription_key
... }
headers = {
"Content-Type": "application/octet-stream",
"Ocp-Apim-Subscription-Key": subscription_key
}
>>> response = requests.post(endpoint,
... headers=self.headers,
... data=image
... ).json()
response = requests.post(endpoint,
headers=self.headers,
data=image
).json()
>>> detections = sv.Detections.from_azure_analyze_image(response)
detections = sv.Detections.from_azure_analyze_image(response)
```
"""
if "error" in azure_result:
@@ -617,21 +638,21 @@ class Detections:
Example:
```python
>>> import supervision as sv
>>> import paddle
>>> from ppdet.engine import Trainer
>>> from ppdet.core.workspace import load_config
import supervision as sv
import paddle
from ppdet.engine import Trainer
from ppdet.core.workspace import load_config
>>> weights = (...)
>>> config = (...)
weights = ()
config = ()
>>> cfg = load_config(config)
>>> trainer = Trainer(cfg, mode='test')
>>> trainer.load_weights(weights)
cfg = load_config(config)
trainer = Trainer(cfg, mode='test')
trainer.load_weights(weights)
>>> paddledet_result = trainer.predict([images])[0]
paddledet_result = trainer.predict([images])[0]
>>> detections = sv.Detections.from_paddledet(paddledet_result)
detections = sv.Detections.from_paddledet(paddledet_result)
```
"""
@@ -655,9 +676,9 @@ class Detections:
Example:
```python
>>> from supervision import Detections
from supervision import Detections
>>> empty_detections = Detections.empty()
empty_detections = Detections.empty()
```
"""
return cls(
@@ -689,29 +710,29 @@ class Detections:
import numpy as np
import supervision as sv
>>> detections_1 = sv.Detections(
... xyxy=np.array([[15, 15, 100, 100], [200, 200, 300, 300]]),
... class_id=np.array([1, 2]),
... data={'feature_vector': np.array([0.1, 0.2)])}
... )
detections_1 = sv.Detections(
xyxy=np.array([[15, 15, 100, 100], [200, 200, 300, 300]]),
class_id=np.array([1, 2]),
data={'feature_vector': np.array([0.1, 0.2)])}
)
>>> detections_2 = sv.Detections(
... xyxy=np.array([[30, 30, 120, 120]]),
... class_id=np.array([1]),
... data={'feature_vector': [np.array([0.3])]}
... )
detections_2 = sv.Detections(
xyxy=np.array([[30, 30, 120, 120]]),
class_id=np.array([1]),
data={'feature_vector': [np.array([0.3])]}
)
>>> merged_detections = Detections.merge([detections_1, detections_2])
merged_detections = Detections.merge([detections_1, detections_2])
>>> merged_detections.xyxy
merged_detections.xyxy
array([[ 15, 15, 100, 100],
[200, 200, 300, 300],
[ 30, 30, 120, 120]])
>>> merged_detections.class_id
merged_detections.class_id
array([1, 2, 1])
>>> merged_detections.data['feature_vector']
merged_detections.data['feature_vector']
array([0.1, 0.2, 0.3])
```
"""
@@ -844,17 +865,17 @@ class Detections:
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> detections = sv.Detections(...)
detections = sv.Detections()
>>> first_detection = detections[0]
>>> first_10_detections = detections[0:10]
>>> some_detections = detections[[0, 2, 4]]
>>> class_0_detections = detections[detections.class_id == 0]
>>> high_confidence_detections = detections[detections.confidence > 0.5]
first_detection = detections[0]
first_10_detections = detections[0:10]
some_detections = detections[[0, 2, 4]]
class_0_detections = detections[detections.class_id == 0]
high_confidence_detections = detections[detections.confidence > 0.5]
>>> feature_vector = detections['feature_vector']
feature_vector = detections['feature_vector']
```
"""
if isinstance(index, str):
@@ -880,22 +901,21 @@ class Detections:
Example:
```python
>>> import cv2
>>> from ultralytics import YOLO
>>> import supervision as sv
import cv2
import supervision as sv
from ultralytics import YOLO
>>> model = YOLO('yolov8s.pt')
image = cv2.imread(<SOURCE_IMAGE_PATH>)
model = YOLO('yolov8s.pt')
>>> image = cv2.imread(SOURCE_IMAGE_PATH)
result = model(image)[0]
detections = sv.Detections.from_ultralytics(result)
>>> result = model(image)[0]
>>> detections = sv.Detections.from_ultralytics(result)
>>> detections['names'] = [
... model.model.names[class_id]
... for class_id
... in detections.class_id
... ]
detections['names'] = [
model.model.names[class_id]
for class_id
in detections.class_id
]
```
"""
if not isinstance(value, (np.ndarray, list)):
@@ -915,7 +935,7 @@ class Detections:
Returns:
np.ndarray: An array of floats containing the area of each detection
in the format of `(area_1, area_2, ..., area_n)`,
in the format of `(area_1, area_2, , area_n)`,
where n is the number of detections.
"""
if self.mask is not None:
@@ -930,7 +950,7 @@ class Detections:
Returns:
np.ndarray: An array of floats containing the area of each bounding
box in the format of `(area_1, area_2, ..., area_n)`,
box in the format of `(area_1, area_2, , area_n)`,
where n is the number of detections.
"""
return (self.xyxy[:, 3] - self.xyxy[:, 1]) * (self.xyxy[:, 2] - self.xyxy[:, 0])
@@ -943,7 +963,8 @@ class Detections:
Args:
threshold (float, optional): The intersection-over-union threshold
to use for non-maximum suppression. Defaults to 0.5.
to use for non-maximum suppression. I'm the lower the value the more
restrictive the NMS becomes. Defaults to 0.5.
class_agnostic (bool, optional): Whether to perform class-agnostic
non-maximum suppression. If True, the class_id of each detection
will be ignored. Defaults to False.
+86 -16
View File
@@ -1,11 +1,11 @@
from typing import Dict, Optional, Tuple
from typing import Dict, Iterable, Optional, Tuple
import cv2
import numpy as np
from supervision.detection.core import Detections
from supervision.draw.color import Color
from supervision.geometry.core import Point, Rect, Vector
from supervision.geometry.core import Point, Position, Rect, Vector
class LineZone:
@@ -15,7 +15,7 @@ class LineZone:
!!! warning
LineZone utilizes the `tracker_id`. Read
LineZone uses the `tracker_id`. Read
[here](https://supervision.roboflow.com/trackers/) to learn how to plug
tracking into your inference pipeline.
@@ -26,16 +26,71 @@ class LineZone:
to outside.
"""
def __init__(self, start: Point, end: Point):
def __init__(
self,
start: Point,
end: Point,
triggering_anchors: Iterable[Position] = (
Position.TOP_LEFT,
Position.TOP_RIGHT,
Position.BOTTOM_LEFT,
Position.BOTTOM_RIGHT,
),
):
"""
Args:
start (Point): The starting point of the line.
end (Point): The ending point of the line.
triggering_anchors (List[sv.Position]): A list of positions
specifying which anchors of the detections bounding box
to consider when deciding on whether the detection
has passed the line counter or not. By default, this
contains the four corners of the detection's bounding box
"""
self.vector = Vector(start=start, end=end)
self.limits = self.calculate_region_of_interest_limits(vector=self.vector)
self.tracker_state: Dict[str, bool] = {}
self.in_count: int = 0
self.out_count: int = 0
self.triggering_anchors = triggering_anchors
@staticmethod
def calculate_region_of_interest_limits(vector: Vector) -> Tuple[Vector, Vector]:
magnitude = vector.magnitude
if magnitude == 0:
raise ValueError("The magnitude of the vector cannot be zero.")
delta_x = vector.end.x - vector.start.x
delta_y = vector.end.y - vector.start.y
unit_vector_x = delta_x / magnitude
unit_vector_y = delta_y / magnitude
perpendicular_vector_x = -unit_vector_y
perpendicular_vector_y = unit_vector_x
start_region_limit = Vector(
start=vector.start,
end=Point(
x=vector.start.x + perpendicular_vector_x,
y=vector.start.y + perpendicular_vector_y,
),
)
end_region_limit = Vector(
start=vector.end,
end=Point(
x=vector.end.x - perpendicular_vector_x,
y=vector.end.y - perpendicular_vector_y,
),
)
return start_region_limit, end_region_limit
@staticmethod
def is_point_in_limits(point: Point, limits: Tuple[Vector, Vector]) -> bool:
cross_product_1 = limits[0].cross_product(point)
cross_product_2 = limits[1].cross_product(point)
return (cross_product_1 > 0) == (cross_product_2 > 0)
def trigger(self, detections: Detections) -> Tuple[np.ndarray, np.ndarray]:
"""
@@ -54,20 +109,35 @@ class LineZone:
crossed_in = np.full(len(detections), False)
crossed_out = np.full(len(detections), False)
for i, (xyxy, tracker_id) in enumerate(
zip(detections.xyxy, detections.tracker_id)
):
if len(detections) == 0:
return crossed_in, crossed_out
all_anchors = np.array(
[
detections.get_anchors_coordinates(anchor)
for anchor in self.triggering_anchors
]
)
for i, tracker_id in enumerate(detections.tracker_id):
if tracker_id is None:
continue
x1, y1, x2, y2 = xyxy
anchors = [
Point(x=x1, y=y1),
Point(x=x1, y=y2),
Point(x=x2, y=y1),
Point(x=x2, y=y2),
box_anchors = [Point(x=x, y=y) for x, y in all_anchors[:, i, :]]
in_limits = all(
[
self.is_point_in_limits(point=anchor, limits=self.limits)
for anchor in box_anchors
]
)
if not in_limits:
continue
triggers = [
self.vector.cross_product(point=anchor) > 0 for anchor in box_anchors
]
triggers = [self.vector.is_in(point=anchor) for anchor in anchors]
if len(set(triggers)) == 2:
continue
@@ -96,9 +166,9 @@ class LineZoneAnnotator:
def __init__(
self,
thickness: float = 2,
color: Color = Color.white(),
color: Color = Color.WHITE,
text_thickness: float = 2,
text_color: Color = Color.black(),
text_color: Color = Color.BLACK,
text_scale: float = 0.5,
text_offset: float = 1.5,
text_padding: int = 10,
+10 -10
View File
@@ -77,20 +77,20 @@ class InferenceSlicer:
Example:
```python
>>> import cv2
>>> import supervision as sv
>>> from ultralytics import YOLO
import cv2
import supervision as sv
from ultralytics import YOLO
>>> image = cv2.imread(SOURCE_IMAGE_PATH)
>>> model = 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)
def callback(image_slice: np.ndarray) -> sv.Detections:
result = model(image_slice)[0]
return sv.Detections.from_ultralytics(result)
>>> slicer = sv.InferenceSlicer(callback = callback)
slicer = sv.InferenceSlicer(callback = callback)
>>> detections = slicer(image)
detections = slicer(image)
```
"""
detections_list = []
+1 -1
View File
@@ -90,7 +90,7 @@ class PolygonZoneAnnotator:
zone: PolygonZone,
color: Color,
thickness: int = 2,
text_color: Color = Color.black(),
text_color: Color = Color.BLACK,
text_scale: float = 0.5,
text_thickness: int = 1,
text_padding: int = 10,
+118
View File
@@ -0,0 +1,118 @@
from collections import defaultdict, deque
from copy import deepcopy
from typing import Optional
import numpy as np
from supervision.detection.core import Detections
class DetectionsSmoother:
"""
A utility class for smoothing detections over multiple frames in video tracking.
It maintains a history of detections for each track and provides smoothed
predictions based on these histories.
<video controls>
<source
src="https://media.roboflow.com/supervision-detection-smoothing.mp4"
type="video/mp4">
</video>
!!! warning
- `DetectionsSmoother` requires the `tracker_id` for each detection. Refer to
[Roboflow Trackers](https://supervision.roboflow.com/trackers/) for
information on integrating tracking into your inference pipeline.
- This class is not compatible with segmentation models.
Example:
```python
import supervision as sv
from ultralytics import YOLO
video_info = sv.VideoInfo.from_video_path(video_path=<SOURCE_FILE_PATH>)
frame_generator = sv.get_video_frames_generator(source_path=<SOURCE_FILE_PATH>)
model = YOLO(<MODEL_PATH>)
tracker = sv.ByteTrack(frame_rate=video_info.fps)
smoother = sv.DetectionsSmoother()
annotator = sv.BoundingBoxAnnotator()
with sv.VideoSink(<TARGET_FILE_PATH>, video_info=video_info) as sink:
for frame in frame_generator:
result = model(frame)[0]
detections = sv.Detections.from_ultralytics(result)
detections = tracker.update_with_detections(detections)
detections = smoother.update_with_detections(detections)
annotated_frame = bounding_box_annotator.annotate(frame.copy(), detections)
sink.write_frame(annotated_frame)
```
""" # noqa: E501 // docs
def __init__(self, length: int = 5) -> None:
"""
Args:
length (int): The maximum number of frames to consider for smoothing
detections. Defaults to 5.
"""
self.tracks = defaultdict(lambda: deque(maxlen=length))
def update_with_detections(self, detections: Detections) -> Detections:
"""
Updates the smoother with a new set of detections from a frame.
Args:
detections (Detections): The detections to add to the smoother.
"""
if detections.tracker_id is None:
print(
"Smoothing skipped. DetectionsSmoother requires tracker_id. Refer to "
"https://supervision.roboflow.com/trackers for more information."
)
return detections
for detection_idx in range(len(detections)):
tracker_id = detections.tracker_id[detection_idx]
if tracker_id is None:
continue
self.tracks[tracker_id].append(detections[detection_idx])
for track_id in self.tracks.keys():
if track_id not in detections.tracker_id:
self.tracks[track_id].append(None)
for track_id in list(self.tracks.keys()):
if all([d is None for d in self.tracks[track_id]]):
del self.tracks[track_id]
return self.get_smoothed_detections()
def get_track(self, track_id: int) -> Optional[Detections]:
track = self.tracks.get(track_id, None)
if track is None:
return None
track = [d for d in track if d is not None]
if len(track) == 0:
return None
ret = deepcopy(track[0])
ret.xyxy = np.mean([d.xyxy for d in track], axis=0)
ret.confidence = np.mean([d.confidence for d in track], axis=0)
return ret
def get_smoothed_detections(self) -> Detections:
tracked_detections = []
for track_id in self.tracks:
track = self.get_track(track_id)
if track is not None:
tracked_detections.append(track)
return Detections.merge(tracked_detections)
+20 -18
View File
@@ -415,16 +415,17 @@ def move_boxes(xyxy: np.ndarray, offset: np.ndarray) -> np.ndarray:
Example:
```python
>>> import numpy as np
>>> import supervision as sv
import numpy as np
import supervision as sv
>>> boxes = np.array([[10, 10, 20, 20], [30, 30, 40, 40]])
>>> offset = np.array([5, 5])
>>> sv.move_boxes(boxes, offset)
... array([
... [15, 15, 25, 25],
... [35, 35, 45, 45]
... ])
boxes = np.array([[10, 10, 20, 20], [30, 30, 40, 40]])
offset = np.array([5, 5])
moved_box = sv.move_boxes(boxes, offset)
print(moved_box)
# np.array([
# [15, 15, 25, 25],
# [35, 35, 45, 45]
# ])
```
"""
return xyxy + np.hstack([offset, offset])
@@ -446,16 +447,17 @@ def scale_boxes(xyxy: np.ndarray, factor: float) -> np.ndarray:
Example:
```python
>>> import numpy as np
>>> import supervision as sv
import numpy as np
import supervision as sv
>>> boxes = np.array([[10, 10, 20, 20], [30, 30, 40, 40]])
>>> factor = 1.5
>>> sv.scale_boxes(boxes, factor)
... array([
... [ 7.5, 7.5, 22.5, 22.5],
... [27.5, 27.5, 42.5, 42.5]
... ])
boxes = np.array([[10, 10, 20, 20], [30, 30, 40, 40]])
factor = 1.5
scaled_bb = sv.scale_boxes(boxes, factor)
print(scaled_bb)
# np.array([
# [ 7.5, 7.5, 22.5, 22.5],
# [27.5, 27.5, 42.5, 42.5]
# ])
```
"""
centers = (xyxy[:, :2] + xyxy[:, 2:]) / 2
+236 -42
View File
@@ -3,26 +3,56 @@ from __future__ import annotations
from dataclasses import dataclass
from typing import List, Tuple
import matplotlib.pyplot as plt
from supervision.utils.internal import classproperty, deprecated
DEFAULT_COLOR_PALETTE = [
"#a351fb",
"#e6194b",
"#3cb44b",
"#ffe119",
"#0082c8",
"#f58231",
"#911eb4",
"#46f0f0",
"#f032e6",
"#d2f53c",
"#fabebe",
"#008080",
"#e6beff",
"#aa6e28",
"#fffac8",
"#800000",
"#aaffc3",
"A351FB",
"FF4040",
"FFA1A0",
"FF7633",
"FFB633",
"D1D435",
"4CFB12",
"94CF1A",
"40DE8A",
"1B9640",
"00D6C1",
"2E9CAA",
"00C4FF",
"364797",
"6675FF",
"0019EF",
"863AFF",
"530087",
"CD3AFF",
"FF97CA",
"FF39C9",
]
LEGACY_COLOR_PALETTE = [
"#A351FB",
"#E6194B",
"#3CB44B",
"#FFE119",
"#0082C8",
"#F58231",
"#911EB4",
"#46F0F0",
"#F032E6",
"#D2F53C",
"#FABEBE",
"#008080",
"#E6BEFF",
"#AA6E28",
"#FFFAC8",
"#800000",
"#AAFFC3",
]
ROBOFLOW_COLOR_PALETTE = ["C28DFC", "A351FB", "8315F9", "6706CE", "5905B3", "4D049A"]
def _validate_color_hex(color_hex: str):
color_hex = color_hex.lstrip("#")
@@ -37,10 +67,31 @@ class Color:
"""
Represents a color in RGB format.
This class provides methods to work with colors, including creating colors from hex
codes, converting colors to hex strings, RGB tuples, and BGR tuples.
Attributes:
r (int): Red channel.
g (int): Green channel.
b (int): Blue channel.
r (int): Red channel value (0-255).
g (int): Green channel value (0-255).
b (int): Blue channel value (0-255).
Example:
```python
import supervision as sv
sv.Color.WHITE
# Color(r=255, g=255, b=255)
```
| Constant | Hex Code | RGB |
|------------|------------|------------------|
| `WHITE` | `#FFFFFF` | `(255, 255, 255)`|
| `BLACK` | `#000000` | `(0, 0, 0)` |
| `RED` | `#FF0000` | `(255, 0, 0)` |
| `GREEN` | `#00FF00` | `(0, 255, 0)` |
| `BLUE` | `#0000FF` | `(0, 0, 255)` |
| `YELLOW` | `#FFFF00` | `(255, 255, 0)` |
| `ROBOFLOW` | `#A351FB` | `(163, 81, 251)` |
"""
r: int
@@ -59,9 +110,11 @@ class Color:
Color: Instance representing the color.
Example:
```
>>> Color.from_hex('#ff00ff')
Color(r=255, g=0, b=255)
```python
import supervision as sv
sv.Color.from_hex('#ff00ff')
# Color(r=255, g=0, b=255)
```
"""
_validate_color_hex(color_hex)
@@ -79,9 +132,11 @@ class Color:
str: The hexadecimal color string.
Example:
```
>>> Color(r=255, g=0, b=255).as_hex()
'#ff00ff'
```python
import supervision as sv
sv.Color(r=255, g=255, b=0).as_hex()
# '#ffff00'
```
"""
return f"#{self.r:02x}{self.g:02x}{self.b:02x}"
@@ -94,9 +149,11 @@ class Color:
Tuple[int, int, int]: RGB tuple.
Example:
```
>>> color.as_rgb()
(255, 0, 255)
```python
import supervision as sv
sv.Color(r=255, g=255, b=0).as_rgb()
# (255, 255, 0)
```
"""
return self.r, self.g, self.b
@@ -109,30 +166,80 @@ class Color:
Tuple[int, int, int]: BGR tuple.
Example:
```
>>> color.as_bgr()
(255, 0, 255)
```python
import supervision as sv
sv.Color(r=255, g=255, b=0).as_bgr()
# (0, 255, 255)
```
"""
return self.b, self.g, self.r
@classproperty
def WHITE(cls):
return Color.from_hex("#FFFFFF")
@classproperty
def BLACK(cls):
return Color.from_hex("#000000")
@classproperty
def RED(cls):
return Color.from_hex("#FF0000")
@classproperty
def GREEN(cls):
return Color.from_hex("#00FF00")
@classproperty
def BLUE(cls):
return Color.from_hex("#0000FF")
@classproperty
def YELLOW(cls):
return Color.from_hex("#FFFF00")
@classproperty
def ROBOFLOW(cls):
return Color.from_hex("#A351FB")
@classmethod
@deprecated(
"`Color.white()` is deprecated and will be removed in "
"`supervision-0.20.0`. Use `Color.WHITE` instead."
)
def white(cls) -> Color:
return Color.from_hex(color_hex="#ffffff")
@classmethod
@deprecated(
"`Color.black()` is deprecated and will be removed in "
"`supervision-0.20.0`. Use `Color.BLACK` instead."
)
def black(cls) -> Color:
return Color.from_hex(color_hex="#000000")
@classmethod
@deprecated(
"`Color.red()` is deprecated and will be removed in "
"`supervision-0.20.0`. Use `Color.RED` instead."
)
def red(cls) -> Color:
return Color.from_hex(color_hex="#ff0000")
@classmethod
@deprecated(
"`Color.green()` is deprecated and will be removed in "
"`supervision-0.20.0`. Use `Color.GREEN` instead."
)
def green(cls) -> Color:
return Color.from_hex(color_hex="#00ff00")
@classmethod
@deprecated(
"`Color.blue()` is deprecated and will be removed in "
"`supervision-0.20.0`. Use `Color.BLUE` instead."
)
def blue(cls) -> Color:
return Color.from_hex(color_hex="#0000ff")
@@ -141,7 +248,57 @@ class Color:
class ColorPalette:
colors: List[Color]
@classproperty
def DEFAULT(cls) -> ColorPalette:
"""
Returns a default color palette.
Returns:
ColorPalette: A ColorPalette instance with default colors.
Example:
```python
import supervision as sv
sv.ColorPalette.DEFAULT
# ColorPalette(colors=[Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), ...])
```
![default-color-palette](https://media.roboflow.com/
supervision-annotator-examples/default-color-palette.png)
""" # noqa: E501 // docs
return ColorPalette.from_hex(color_hex_list=DEFAULT_COLOR_PALETTE)
@classproperty
def ROBOFLOW(cls) -> ColorPalette:
"""
Returns a Roboflow color palette.
Returns:
ColorPalette: A ColorPalette instance with Roboflow colors.
Example:
```python
import supervision as sv
sv.ColorPalette.ROBOFLOW
# ColorPalette(colors=[Color(r=194, g=141, b=252), Color(r=163, g=81, b=251), ...])
```
![roboflow-color-palette](https://media.roboflow.com/
supervision-annotator-examples/roboflow-color-palette.png)
""" # noqa: E501 // docs
return ColorPalette.from_hex(color_hex_list=ROBOFLOW_COLOR_PALETTE)
@classproperty
def LEGACY(cls) -> ColorPalette:
return ColorPalette.from_hex(color_hex_list=LEGACY_COLOR_PALETTE)
@classmethod
@deprecated(
"`ColorPalette.default()` is deprecated and will be removed in "
"`supervision-0.20.0`. Use `Color.DEFAULT` instead."
)
def default(cls) -> ColorPalette:
"""
Returns a default color palette.
@@ -150,11 +307,13 @@ class ColorPalette:
ColorPalette: A ColorPalette instance with default colors.
Example:
```python
import supervision as sv
sv.ColorPalette.default()
# ColorPalette(colors=[Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), ...])
```
>>> ColorPalette.default()
ColorPalette(colors=[Color(r=255, g=0, b=0), Color(r=0, g=255, b=0), ...])
```
"""
""" # noqa: E501 // docs
return ColorPalette.from_hex(color_hex_list=DEFAULT_COLOR_PALETTE)
@classmethod
@@ -169,14 +328,46 @@ class ColorPalette:
ColorPalette: A ColorPalette instance.
Example:
```
>>> ColorPalette.from_hex(['#ff0000', '#00ff00', '#0000ff'])
ColorPalette(colors=[Color(r=255, g=0, b=0), Color(r=0, g=255, b=0), ...])
```python
import supervision as sv
sv.ColorPalette.from_hex(['#ff0000', '#00ff00', '#0000ff'])
# ColorPalette(colors=[Color(r=255, g=0, b=0), Color(r=0, g=255, b=0), ...])
```
"""
colors = [Color.from_hex(color_hex) for color_hex in color_hex_list]
return cls(colors)
@classmethod
def from_matplotlib(cls, palette_name: str, color_count: int) -> ColorPalette:
"""
Create a ColorPalette instance from a Matplotlib color palette.
Args:
palette_name (str): Name of the Matplotlib palette.
color_count (int): Number of colors to sample from the palette.
Returns:
ColorPalette: A ColorPalette instance.
Example:
```python
import supervision as sv
sv.ColorPalette.from_matplotlib('viridis', 5)
# ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
```
![visualized_color_palette](https://media.roboflow.com/
supervision-annotator-examples/visualized_color_palette.png)
""" # noqa: E501 // docs
mpl_palette = plt.get_cmap(palette_name, color_count)
colors = [
Color(int(r * 255), int(g * 255), int(b * 255))
for r, g, b, _ in mpl_palette.colors
]
return cls(colors)
def by_idx(self, idx: int) -> Color:
"""
Return the color at a given index in the palette.
@@ -188,9 +379,12 @@ class ColorPalette:
Color: Color at the given index.
Example:
```
>>> color_palette.by_idx(1)
Color(r=0, g=255, b=0)
```python
import supervision as sv
color_palette = sv.ColorPalette.from_hex(['#ff0000', '#00ff00', '#0000ff'])
color_palette.by_idx(1)
# Color(r=0, g=255, b=0)
```
"""
if idx < 0:
+6 -4
View File
@@ -105,7 +105,7 @@ def draw_text(
scene: np.ndarray,
text: str,
text_anchor: Point,
text_color: Color = Color.black(),
text_color: Color = Color.BLACK,
text_scale: float = 0.5,
text_thickness: int = 1,
text_padding: int = 10,
@@ -135,9 +135,11 @@ def draw_text(
Examples:
```python
>>> scene = np.zeros((100, 100, 3), dtype=np.uint8)
>>> text_anchor = Point(x=50, y=50)
>>> scene = draw_text(scene=scene, text="Hello, world!",text_anchor=text_anchor)
import numpy as np
scene = np.zeros((100, 100, 3), dtype=np.uint8)
text_anchor = Point(x=50, y=50)
scene = draw_text(scene=scene, text="Hello, world!",text_anchor=text_anchor)
```
"""
text_width, text_height = cv2.getTextSize(
+33 -7
View File
@@ -2,6 +2,7 @@ from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
from math import sqrt
from typing import Tuple
@@ -43,13 +44,38 @@ class Vector:
start: Point
end: Point
def is_in(self, point: Point) -> bool:
v1 = Vector(self.start, self.end)
v2 = Vector(self.start, point)
cross_product = (v1.end.x - v1.start.x) * (v2.end.y - v2.start.y) - (
v1.end.y - v1.start.y
) * (v2.end.x - v2.start.x)
return cross_product < 0
@property
def magnitude(self) -> float:
"""
Calculate the magnitude (length) of the vector.
Returns:
float: The magnitude of the vector.
"""
dx = self.end.x - self.start.x
dy = self.end.y - self.start.y
return sqrt(dx**2 + dy**2)
def cross_product(self, point: Point) -> float:
"""
Calculate the 2D cross product (also known as the vector product or outer
product) of the vector and a point, treated as vectors in 2D space.
Args:
point (Point): The point to be evaluated, treated as the endpoint of a
vector originating from the 'start' of the main vector.
Returns:
float: The scalar value of the cross product. It is positive if 'point'
lies to the left of the vector (when moving from 'start' to 'end'),
negative if it lies to the right, and 0 if it is collinear with the
vector.
"""
dx_vector = self.end.x - self.start.x
dy_vector = self.end.y - self.start.y
dx_point = point.x - self.start.x
dy_point = point.y - self.start.y
return (dx_vector * dy_point) - (dy_vector * dx_point)
@dataclass
+4 -3
View File
@@ -22,10 +22,11 @@ def get_polygon_center(polygon: np.ndarray) -> Point:
Examples:
```python
>>> from supervision.geometry.utils import get_polygon_center
from supervision.geometry.utils import get_polygon_center
import numpy as np
>>> vertices = np.array([[0, 0], [0, 1], [1, 1], [1, 0]])
>>> get_center(vertices)
vertices = np.array([[0, 0], [0, 1], [1, 1], [1, 0]])
get_center(vertices)
Point(x=0.5, y=0.5)
```
"""
+135 -133
View File
@@ -116,31 +116,31 @@ class ConfusionMatrix:
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> targets = [
... sv.Detections(...),
... sv.Detections(...)
... ]
targets = [
sv.Detections(...),
sv.Detections(...)
]
>>> predictions = [
... sv.Detections(...),
... sv.Detections(...)
... ]
predictions = [
sv.Detections(...),
sv.Detections(...)
]
>>> confusion_matrix = sv.ConfusionMatrix.from_detections(
... predictions=predictions,
... targets=target,
... classes=['person', ...]
... )
confusion_matrix = sv.ConfusionMatrix.from_detections(
predictions=predictions,
targets=target,
classes=['person', ...]
)
>>> confusion_matrix.matrix
array([
[0., 0., 0., 0.],
[0., 1., 0., 1.],
[0., 1., 1., 0.],
[1., 1., 0., 0.]
])
print(confusion_matrix.matrix)
# np.array([
# [0., 0., 0., 0.],
# [0., 1., 0., 1.],
# [0., 1., 1., 0.],
# [1., 1., 0., 0.]
# ])
```
"""
@@ -191,46 +191,47 @@ class ConfusionMatrix:
Example:
```python
>>> import supervision as sv
import supervision as sv
import numpy as np
>>> targets = (
... [
... array(
... [
... [0.0, 0.0, 3.0, 3.0, 1],
... [2.0, 2.0, 5.0, 5.0, 1],
... [6.0, 1.0, 8.0, 3.0, 2],
... ]
... ),
... array([1.0, 1.0, 2.0, 2.0, 2]),
... ]
... )
targets = (
[
np.array(
[
[0.0, 0.0, 3.0, 3.0, 1],
[2.0, 2.0, 5.0, 5.0, 1],
[6.0, 1.0, 8.0, 3.0, 2],
]
),
np.array([1.0, 1.0, 2.0, 2.0, 2]),
]
)
>>> predictions = [
... array(
... [
... [0.0, 0.0, 3.0, 3.0, 1, 0.9],
... [0.1, 0.1, 3.0, 3.0, 0, 0.9],
... [6.0, 1.0, 8.0, 3.0, 1, 0.8],
... [1.0, 6.0, 2.0, 7.0, 1, 0.8],
... ]
... ),
... array([[1.0, 1.0, 2.0, 2.0, 2, 0.8]])
... ]
predictions = [
np.array(
[
[0.0, 0.0, 3.0, 3.0, 1, 0.9],
[0.1, 0.1, 3.0, 3.0, 0, 0.9],
[6.0, 1.0, 8.0, 3.0, 1, 0.8],
[1.0, 6.0, 2.0, 7.0, 1, 0.8],
]
),
np.array([[1.0, 1.0, 2.0, 2.0, 2, 0.8]])
]
>>> confusion_matrix = sv.ConfusionMatrix.from_tensors(
... predictions=predictions,
... targets=targets,
... classes=['person', ...]
... )
confusion_matrix = sv.ConfusionMatrix.from_tensors(
predictions=predictions,
targets=targets,
classes=['person', ...]
)
>>> confusion_matrix.matrix
array([
[0., 0., 0., 0.],
[0., 1., 0., 1.],
[0., 1., 1., 0.],
[1., 1., 0., 0.]
])
print(confusion_matrix.matrix)
# np.array([
# [0., 0., 0., 0.],
# [0., 1., 0., 1.],
# [0., 1., 1., 0.],
# [1., 1., 0., 0.]
# ])
```
"""
validate_input_tensors(predictions, targets)
@@ -365,28 +366,28 @@ class ConfusionMatrix:
Example:
```python
>>> import supervision as sv
>>> from ultralytics import YOLO
import supervision as sv
from ultralytics import YOLO
>>> dataset = sv.DetectionDataset.from_yolo(...)
dataset = sv.DetectionDataset.from_yolo(...)
>>> model = YOLO(...)
>>> def callback(image: np.ndarray) -> sv.Detections:
... result = model(image)[0]
... return sv.Detections.from_ultralytics(result)
model = YOLO(...)
def callback(image: np.ndarray) -> sv.Detections:
result = model(image)[0]
return sv.Detections.from_ultralytics(result)
>>> confusion_matrix = sv.ConfusionMatrix.benchmark(
... dataset = dataset,
... callback = callback
... )
confusion_matrix = sv.ConfusionMatrix.benchmark(
dataset = dataset,
callback = callback
)
>>> confusion_matrix.matrix
array([
[0., 0., 0., 0.],
[0., 1., 0., 1.],
[0., 1., 1., 0.],
[1., 1., 0., 0.]
])
print(confusion_matrix.matrix)
# np.array([
# [0., 0., 0., 0.],
# [0., 1., 0., 1.],
# [0., 1., 1., 0.],
# [1., 1., 0., 0.]
# ])
```
"""
predictions, targets = [], []
@@ -532,25 +533,25 @@ class MeanAveragePrecision:
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> targets = [
... sv.Detections(...),
... sv.Detections(...)
... ]
targets = [
sv.Detections(...),
sv.Detections(...)
]
>>> predictions = [
... sv.Detections(...),
... sv.Detections(...)
... ]
predictions = [
sv.Detections(...),
sv.Detections(...)
]
>>> mean_average_precision = sv.MeanAveragePrecision.from_detections(
... predictions=predictions,
... targets=target,
... )
mean_average_precision = sv.MeanAveragePrecision.from_detections(
predictions=predictions,
targets=target,
)
>>> mean_average_precison.map50_95
0.2899
print(mean_average_precison.map50_95)
# 0.2899
```
"""
prediction_tensors = []
@@ -583,23 +584,23 @@ class MeanAveragePrecision:
Example:
```python
>>> import supervision as sv
>>> from ultralytics import YOLO
import supervision as sv
from ultralytics import YOLO
>>> dataset = sv.DetectionDataset.from_yolo(...)
dataset = sv.DetectionDataset.from_yolo(...)
>>> model = YOLO(...)
>>> def callback(image: np.ndarray) -> sv.Detections:
... result = model(image)[0]
... return sv.Detections.from_ultralytics(result)
model = YOLO(...)
def callback(image: np.ndarray) -> sv.Detections:
result = model(image)[0]
return sv.Detections.from_ultralytics(result)
>>> mean_average_precision = sv.MeanAveragePrecision.benchmark(
... dataset = dataset,
... callback = callback
... )
mean_average_precision = sv.MeanAveragePrecision.benchmark(
dataset = dataset,
callback = callback
)
>>> mean_average_precision.map50_95
0.433
print(mean_average_precision.map50_95)
# 0.433
```
"""
predictions, targets = [], []
@@ -637,40 +638,41 @@ class MeanAveragePrecision:
Example:
```python
>>> import supervision as sv
import supervision as sv
import numpy as np
>>> targets = (
... [
... array(
... [
... [0.0, 0.0, 3.0, 3.0, 1],
... [2.0, 2.0, 5.0, 5.0, 1],
... [6.0, 1.0, 8.0, 3.0, 2],
... ]
... ),
... array([1.0, 1.0, 2.0, 2.0, 2]),
... ]
... )
targets = (
[
np.array(
[
[0.0, 0.0, 3.0, 3.0, 1],
[2.0, 2.0, 5.0, 5.0, 1],
[6.0, 1.0, 8.0, 3.0, 2],
]
),
np.array([[1.0, 1.0, 2.0, 2.0, 2]]),
]
)
>>> predictions = [
... array(
... [
... [0.0, 0.0, 3.0, 3.0, 1, 0.9],
... [0.1, 0.1, 3.0, 3.0, 0, 0.9],
... [6.0, 1.0, 8.0, 3.0, 1, 0.8],
... [1.0, 6.0, 2.0, 7.0, 1, 0.8],
... ]
... ),
... array([[1.0, 1.0, 2.0, 2.0, 2, 0.8]])
... ]
predictions = [
np.array(
[
[0.0, 0.0, 3.0, 3.0, 1, 0.9],
[0.1, 0.1, 3.0, 3.0, 0, 0.9],
[6.0, 1.0, 8.0, 3.0, 1, 0.8],
[1.0, 6.0, 2.0, 7.0, 1, 0.8],
]
),
np.array([[1.0, 1.0, 2.0, 2.0, 2, 0.8]])
]
>>> mean_average_precison = sv.MeanAveragePrecision.from_tensors(
... predictions=predictions,
... targets=targets,
... )
mean_average_precison = sv.MeanAveragePrecision.from_tensors(
predictions=predictions,
targets=targets,
)
>>> mean_average_precison.map50_95
0.2899
print(mean_average_precison.map50_95)
# 0.6649
```
"""
validate_input_tensors(predictions, targets)
+34 -28
View File
@@ -167,13 +167,17 @@ class ByteTrack:
"""
Initialize the ByteTrack object.
<video controls>
<source src="https://media.roboflow.com/supervision/video-examples/how-to/track-objects/annotate-video-with-traces.mp4" type="video/mp4">
</video>
Parameters:
track_thresh (float, optional): Detection confidence threshold
for track activation.
track_buffer (int, optional): Number of frames to buffer when a track is lost.
match_thresh (float, optional): Threshold for matching tracks with detections.
frame_rate (int, optional): The frame rate of the video.
"""
""" # noqa: E501 // docs
def __init__(
self,
@@ -196,39 +200,41 @@ class ByteTrack:
def update_with_detections(self, detections: Detections) -> Detections:
"""
Updates the tracker with the provided detections and
returns the updated detection results.
Updates the tracker with the provided detections and returns the updated
detection results.
Args:
detections (Detections): The detections to pass through the tracker.
Parameters:
detections: The new detections to update with.
Returns:
Detection: The updated detection results that now include tracking IDs.
Example:
```python
>>> import supervision as sv
>>> from ultralytics import YOLO
import supervision as sv
from ultralytics import YOLO
>>> model = YOLO(...)
>>> byte_tracker = sv.ByteTrack()
>>> annotator = sv.BoxAnnotator()
model = YOLO(<MODEL_PATH>)
tracker = sv.ByteTrack()
>>> def callback(frame: np.ndarray, index: int) -> np.ndarray:
... results = model(frame)[0]
... detections = sv.Detections.from_ultralytics(results)
... detections = byte_tracker.update_with_detections(detections)
... labels = [
... f"#{tracker_id} {model.model.names[class_id]} {confidence:0.2f}"
... for _, _, confidence, class_id, tracker_id
... in detections
... ]
... return annotator.annotate(scene=frame.copy(),
... detections=detections, labels=labels)
bounding_box_annotator = sv.BoundingBoxAnnotator()
label_annotator = sv.LabelAnnotator()
>>> sv.process_video(
... source_path='...',
... target_path='...',
... callback=callback
... )
def callback(frame: np.ndarray, index: int) -> np.ndarray:
results = model(frame)[0]
detections = sv.Detections.from_ultralytics(results)
detections = byte_tracker.update_with_detections(detections)
labels = [f"#{tracker_id}" for tracker_id in detections.tracker_id]
annotated_frame = bounding_box_annotator.annotate(
scene=frame.copy(), detections=detections)
annotated_frame = label_annotator.annotate(
scene=annotated_frame, detections=detections, labels=labels)
return annotated_frame
sv.process_video(
source_path=<SOURCE_VIDEO_PATH>,
target_path=<TARGET_VIDEO_PATH>,
callback=callback
)
```
"""
+6 -6
View File
@@ -34,14 +34,14 @@ def list_files_with_extensions(
Examples:
```python
>>> import supervision as sv
import supervision as sv
>>> # List all files in the directory
>>> files = sv.list_files_with_extensions(directory='my_directory')
# List all files in the directory
files = sv.list_files_with_extensions(directory='my_directory')
>>> # List only files with '.txt' and '.md' extensions
>>> files = sv.list_files_with_extensions(
... directory='my_directory', extensions=['txt', 'md'])
# List only files with '.txt' and '.md' extensions
files = sv.list_files_with_extensions(
directory='my_directory', extensions=['txt', 'md'])
```
"""
+12 -12
View File
@@ -20,13 +20,13 @@ def crop_image(image: np.ndarray, xyxy: np.ndarray) -> np.ndarray:
Examples:
```python
>>> import supervision as sv
import supervision as sv
>>> detection = sv.Detections(...)
>>> with sv.ImageSink(target_dir_path='target/directory/path') as sink:
... for xyxy in detection.xyxy:
... cropped_image = sv.crop_image(image=image, xyxy=xyxy)
... sink.save_image(image=image)
detection = sv.Detections(...)
with sv.ImageSink(target_dir_path='target/directory/path') as sink:
for xyxy in detection.xyxy:
cropped_image = sv.crop_image(image=image, xyxy=xyxy)
sink.save_image(image=image)
```
"""
@@ -55,13 +55,13 @@ class ImageSink:
Examples:
```python
>>> import supervision as sv
import supervision as sv
>>> with sv.ImageSink(target_dir_path='target/directory/path',
... overwrite=True) as sink:
... for image in sv.get_video_frames_generator(
... source_path='source_video.mp4', stride=2):
... sink.save_image(image=image)
with sv.ImageSink(target_dir_path='target/directory/path',
overwrite=True) as sink:
for image in sv.get_video_frames_generator(
source_path='source_video.mp4', stride=2):
sink.save_image(image=image)
```
"""
+26
View File
@@ -16,3 +16,29 @@ def deprecated(reason: str):
return wrapper
return decorator
class classproperty(property):
"""
A decorator that combines @classmethod and @property.
It allows a method to be accessed as a property of the class,
rather than an instance, similar to a classmethod.
Usage:
@classproperty
def my_method(cls):
...
"""
def __get__(self, owner_self: object, owner_cls: type) -> object:
"""
Override the __get__ method to return the result of the function call.
Args:
owner_self: The instance through which the attribute was accessed, or None.
owner_cls: The class through which the attribute was accessed.
Returns:
The result of calling the function stored in 'fget' with 'owner_cls'.
"""
return self.fget(owner_cls)
+12 -12
View File
@@ -18,13 +18,13 @@ def plot_image(
Examples:
```python
>>> import cv2
>>> import supervision as sv
import cv2
import supervision as sv
>>> image = cv2.imread("path/to/image.jpg")
image = cv2.imread("path/to/image.jpg")
%matplotlib inline
>>> sv.plot_image(image=image, size=(16, 16))
sv.plot_image(image=image, size=(16, 16))
```
"""
plt.figure(figsize=size)
@@ -63,18 +63,18 @@ def plot_images_grid(
Examples:
```python
>>> import cv2
>>> import supervision as sv
import cv2
import supervision as sv
>>> image1 = cv2.imread("path/to/image1.jpg")
>>> image2 = cv2.imread("path/to/image2.jpg")
>>> image3 = cv2.imread("path/to/image3.jpg")
image1 = cv2.imread("path/to/image1.jpg")
image2 = cv2.imread("path/to/image2.jpg")
image3 = cv2.imread("path/to/image3.jpg")
>>> images = [image1, image2, image3]
>>> titles = ["Image 1", "Image 2", "Image 3"]
images = [image1, image2, image3]
titles = ["Image 1", "Image 2", "Image 3"]
%matplotlib inline
>>> plot_images_grid(images, grid_size=(2, 2), titles=titles, size=(16, 16))
plot_images_grid(images, grid_size=(2, 2), titles=titles, size=(16, 16))
```
"""
nrows, ncols = grid_size
+30 -30
View File
@@ -24,15 +24,15 @@ class VideoInfo:
Examples:
```python
>>> import supervision as sv
import supervision as sv
>>> video_info = sv.VideoInfo.from_video_path(video_path='video.mp4')
video_info = sv.VideoInfo.from_video_path(video_path='video.mp4')
>>> video_info
VideoInfo(width=3840, height=2160, fps=25, total_frames=538)
video_info
# VideoInfo(width=3840, height=2160, fps=25, total_frames=538)
>>> video_info.resolution_wh
(3840, 2160)
video_info.resolution_wh
# (3840, 2160)
```
"""
@@ -71,14 +71,14 @@ class VideoSink:
Example:
```python
>>> import supervision as sv
import supervision as sv
>>> video_info = sv.VideoInfo.from_video_path('source.mp4')
>>> frames_generator = sv.get_video_frames_generator('source.mp4')
video_info = sv.VideoInfo.from_video_path('source.mp4')
frames_generator = sv.get_video_frames_generator('source.mp4')
>>> with sv.VideoSink(target_path='target.mp4', video_info=video_info) as sink:
... for frame in frames_generator:
... sink.write_frame(frame=frame)
with sv.VideoSink(target_path='target.mp4', video_info=video_info) as sink:
for frame in frames_generator:
sink.write_frame(frame=frame)
```
"""
@@ -143,10 +143,10 @@ def get_video_frames_generator(
Examples:
```python
>>> import supervision as sv
import supervision as sv
>>> for frame in sv.get_video_frames_generator(source_path='source_video.mp4'):
... ...
for frame in sv.get_video_frames_generator(source_path='source_video.mp4'):
...
```
"""
video, start, end = _validate_and_setup_video(source_path, start, end)
@@ -183,16 +183,16 @@ def process_video(
Examples:
```python
>>> import supervision as sv
import supervision as sv
>>> def callback(scene: np.ndarray, index: int) -> np.ndarray:
... ...
def callback(scene: np.ndarray, index: int) -> np.ndarray:
...
>>> process_video(
... source_path='...',
... target_path='...',
... callback=callback
... )
process_video(
source_path='...',
target_path='...',
callback=callback
)
```
"""
source_video_info = VideoInfo.from_video_path(video_path=source_path)
@@ -217,15 +217,15 @@ class FPSMonitor:
Examples:
```python
>>> import supervision as sv
import supervision as sv
>>> frames_generator = sv.get_video_frames_generator('source.mp4')
>>> fps_monitor = sv.FPSMonitor()
frames_generator = sv.get_video_frames_generator('source.mp4')
fps_monitor = sv.FPSMonitor()
>>> for frame in frames_generator:
... # your processing code here
... fps_monitor.tick()
... fps = fps_monitor()
for frame in frames_generator:
# your processing code here
fps_monitor.tick()
fps = fps_monitor()
```
"""
self.all_timestamps = deque(maxlen=sample_size)
+72
View File
@@ -0,0 +1,72 @@
from contextlib import ExitStack as DoesNotRaise
from typing import Optional, Tuple
import pytest
from supervision import LineZone
from supervision.geometry.core import Point, Vector
@pytest.mark.parametrize(
"vector, expected_result, exception",
[
(
Vector(start=Point(x=0.0, y=0.0), end=Point(x=0.0, y=0.0)),
None,
pytest.raises(ValueError),
),
(
Vector(start=Point(x=1.0, y=1.0), end=Point(x=1.0, y=1.0)),
None,
pytest.raises(ValueError),
),
(
Vector(start=Point(x=0.0, y=0.0), end=Point(x=0.0, y=4.0)),
(
Vector(start=Point(x=0.0, y=0.0), end=Point(x=-1.0, y=0.0)),
Vector(start=Point(x=0.0, y=4.0), end=Point(x=1.0, y=4.0)),
),
DoesNotRaise(),
),
(
Vector(Point(0.0, 0.0), Point(4.0, 0.0)),
(
Vector(start=Point(x=0.0, y=0.0), end=Point(x=0.0, y=1.0)),
Vector(start=Point(x=4.0, y=0.0), end=Point(x=4.0, y=-1.0)),
),
DoesNotRaise(),
),
(
Vector(Point(0.0, 0.0), Point(3.0, 4.0)),
(
Vector(start=Point(x=0, y=0), end=Point(x=-0.8, y=0.6)),
Vector(start=Point(x=3, y=4), end=Point(x=3.8, y=3.4)),
),
DoesNotRaise(),
),
(
Vector(Point(0.0, 0.0), Point(4.0, 3.0)),
(
Vector(start=Point(x=0, y=0), end=Point(x=-0.6, y=0.8)),
Vector(start=Point(x=4, y=3), end=Point(x=4.6, y=2.2)),
),
DoesNotRaise(),
),
(
Vector(Point(0.0, 0.0), Point(3.0, -4.0)),
(
Vector(start=Point(x=0, y=0), end=Point(x=0.8, y=0.6)),
Vector(start=Point(x=3, y=-4), end=Point(x=2.2, y=-4.6)),
),
DoesNotRaise(),
),
],
)
def test_calculate_region_of_interest_limits(
vector: Vector,
expected_result: Optional[Tuple[Vector, Vector]],
exception: Exception,
) -> None:
with exception:
result = LineZone.calculate_region_of_interest_limits(vector=vector)
assert result == expected_result
+12 -12
View File
@@ -9,13 +9,13 @@ from supervision.draw.color import Color
@pytest.mark.parametrize(
"color_hex, expected_result, exception",
[
("fff", Color.white(), DoesNotRaise()),
("#fff", Color.white(), DoesNotRaise()),
("ffffff", Color.white(), DoesNotRaise()),
("#ffffff", Color.white(), DoesNotRaise()),
("f00", Color.red(), DoesNotRaise()),
("0f0", Color.green(), DoesNotRaise()),
("00f", Color.blue(), DoesNotRaise()),
("fff", Color.WHITE, DoesNotRaise()),
("#fff", Color.WHITE, DoesNotRaise()),
("ffffff", Color.WHITE, DoesNotRaise()),
("#ffffff", Color.WHITE, DoesNotRaise()),
("f00", Color.RED, DoesNotRaise()),
("0f0", Color.GREEN, DoesNotRaise()),
("00f", Color.BLUE, DoesNotRaise()),
("#808000", Color(r=128, g=128, b=0), DoesNotRaise()),
("", None, pytest.raises(ValueError)),
("00", None, pytest.raises(ValueError)),
@@ -35,11 +35,11 @@ def test_color_from_hex(
@pytest.mark.parametrize(
"color, expected_result, exception",
[
(Color.white(), "#ffffff", DoesNotRaise()),
(Color.black(), "#000000", DoesNotRaise()),
(Color.red(), "#ff0000", DoesNotRaise()),
(Color.green(), "#00ff00", DoesNotRaise()),
(Color.blue(), "#0000ff", DoesNotRaise()),
(Color.WHITE, "#ffffff", DoesNotRaise()),
(Color.BLACK, "#000000", DoesNotRaise()),
(Color.RED, "#ff0000", DoesNotRaise()),
(Color.GREEN, "#00ff00", DoesNotRaise()),
(Color.BLUE, "#0000ff", DoesNotRaise()),
(Color(r=128, g=128, b=0), "#808000", DoesNotRaise()),
],
)
+59
View File
@@ -0,0 +1,59 @@
import pytest
from supervision.geometry.core import Point, Vector
@pytest.mark.parametrize(
"vector, point, expected_result",
[
(Vector(start=Point(x=0, y=0), end=Point(x=5, y=5)), Point(x=-1, y=1), 10.0),
(Vector(start=Point(x=0, y=0), end=Point(x=5, y=5)), Point(x=6, y=6), 0.0),
(Vector(start=Point(x=0, y=0), end=Point(x=5, y=5)), Point(x=3, y=6), 15.0),
(Vector(start=Point(x=5, y=5), end=Point(x=0, y=0)), Point(x=-1, y=1), -10.0),
(Vector(start=Point(x=5, y=5), end=Point(x=0, y=0)), Point(x=6, y=6), 0.0),
(Vector(start=Point(x=5, y=5), end=Point(x=0, y=0)), Point(x=3, y=6), -15.0),
(Vector(start=Point(x=0, y=0), end=Point(x=1, y=0)), Point(x=0, y=0), 0.0),
(Vector(start=Point(x=0, y=0), end=Point(x=1, y=0)), Point(x=0, y=-1), -1.0),
(Vector(start=Point(x=0, y=0), end=Point(x=1, y=0)), Point(x=0, y=1), 1.0),
(Vector(start=Point(x=1, y=0), end=Point(x=0, y=0)), Point(x=0, y=0), 0.0),
(Vector(start=Point(x=1, y=0), end=Point(x=0, y=0)), Point(x=0, y=-1), 1.0),
(Vector(start=Point(x=1, y=0), end=Point(x=0, y=0)), Point(x=0, y=1), -1.0),
(Vector(start=Point(x=1, y=1), end=Point(x=1, y=3)), Point(x=0, y=0), 2.0),
(Vector(start=Point(x=1, y=1), end=Point(x=1, y=3)), Point(x=1, y=4), 0.0),
(Vector(start=Point(x=1, y=1), end=Point(x=1, y=3)), Point(x=2, y=4), -2.0),
(Vector(start=Point(x=1, y=3), end=Point(x=1, y=1)), Point(x=0, y=0), -2.0),
(Vector(start=Point(x=1, y=3), end=Point(x=1, y=1)), Point(x=1, y=4), 0.0),
(Vector(start=Point(x=1, y=3), end=Point(x=1, y=1)), Point(x=2, y=4), 2.0),
],
)
def test_vector_cross_product(
vector: Vector, point: Point, expected_result: float
) -> None:
result = vector.cross_product(point=point)
assert result == expected_result
@pytest.mark.parametrize(
"vector, expected_result",
[
(Vector(start=Point(x=0, y=0), end=Point(x=0, y=0)), 0.0),
(Vector(start=Point(x=1, y=0), end=Point(x=0, y=0)), 1.0),
(Vector(start=Point(x=0, y=1), end=Point(x=0, y=0)), 1.0),
(Vector(start=Point(x=0, y=0), end=Point(x=1, y=0)), 1.0),
(Vector(start=Point(x=0, y=0), end=Point(x=0, y=1)), 1.0),
(Vector(start=Point(x=-1, y=0), end=Point(x=0, y=0)), 1.0),
(Vector(start=Point(x=0, y=-1), end=Point(x=0, y=0)), 1.0),
(Vector(start=Point(x=0, y=0), end=Point(x=-1, y=0)), 1.0),
(Vector(start=Point(x=0, y=0), end=Point(x=0, y=-1)), 1.0),
(Vector(start=Point(x=0, y=0), end=Point(x=3, y=4)), 5.0),
(Vector(start=Point(x=0, y=0), end=Point(x=-3, y=4)), 5.0),
(Vector(start=Point(x=0, y=0), end=Point(x=3, y=-4)), 5.0),
(Vector(start=Point(x=0, y=0), end=Point(x=-3, y=-4)), 5.0),
(Vector(start=Point(x=0, y=0), end=Point(x=4, y=3)), 5.0),
(Vector(start=Point(x=3, y=4), end=Point(x=0, y=0)), 5.0),
(Vector(start=Point(x=4, y=3), end=Point(x=0, y=0)), 5.0),
],
)
def test_vector_magnitude(vector: Vector, expected_result: float) -> None:
result = vector.magnitude
assert result == expected_result
-31
View File
@@ -1,31 +0,0 @@
import pytest
from supervision.geometry.core import Point, Vector
@pytest.mark.parametrize(
"vector, point, expected_result",
[
(Vector(start=Point(x=0, y=0), end=Point(x=5, y=5)), Point(x=-1, y=1), False),
(Vector(start=Point(x=0, y=0), end=Point(x=5, y=5)), Point(x=6, y=6), False),
(Vector(start=Point(x=0, y=0), end=Point(x=5, y=5)), Point(x=3, y=6), False),
(Vector(start=Point(x=5, y=5), end=Point(x=0, y=0)), Point(x=-1, y=1), True),
(Vector(start=Point(x=5, y=5), end=Point(x=0, y=0)), Point(x=6, y=6), False),
(Vector(start=Point(x=5, y=5), end=Point(x=0, y=0)), Point(x=3, y=6), True),
(Vector(start=Point(x=0, y=0), end=Point(x=1, y=0)), Point(x=0, y=0), False),
(Vector(start=Point(x=0, y=0), end=Point(x=1, y=0)), Point(x=0, y=-1), True),
(Vector(start=Point(x=0, y=0), end=Point(x=1, y=0)), Point(x=0, y=1), False),
(Vector(start=Point(x=1, y=0), end=Point(x=0, y=0)), Point(x=0, y=0), False),
(Vector(start=Point(x=1, y=0), end=Point(x=0, y=0)), Point(x=0, y=-1), False),
(Vector(start=Point(x=1, y=0), end=Point(x=0, y=0)), Point(x=0, y=1), True),
(Vector(start=Point(x=1, y=1), end=Point(x=1, y=3)), Point(x=0, y=0), False),
(Vector(start=Point(x=1, y=1), end=Point(x=1, y=3)), Point(x=1, y=4), False),
(Vector(start=Point(x=1, y=1), end=Point(x=1, y=3)), Point(x=2, y=4), True),
(Vector(start=Point(x=1, y=3), end=Point(x=1, y=1)), Point(x=0, y=0), True),
(Vector(start=Point(x=1, y=3), end=Point(x=1, y=1)), Point(x=1, y=4), False),
(Vector(start=Point(x=1, y=3), end=Point(x=1, y=1)), Point(x=2, y=4), False),
],
)
def test_vector_is_in(vector: Vector, point: Point, expected_result: bool) -> None:
result = vector.is_in(point=point)
assert result == expected_result