Compare commits

...
147 Commits
Author SHA1 Message Date
SkalskiP 239ea649c0 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	supervision/annotators/core.py
2024-01-23 18:10:18 +01: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
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
SkalskiP 2ba6a22d69 bump version from 0.18.0rc1 to 0.18.0rc2 2024-01-17 09:12:53 +01:00
Piotr Skalski 3de2efea12 Merge pull request #737 from roboflow/fix/line-zone-trigger-returns-value-error
`fix/line-zone-trigger-returns-value-error`
2024-01-17 09:12:10 +01:00
pre-commit-ci[bot] f7145aa6aa fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-17 08:06:37 +00:00
SkalskiP b12d73a883 ready for review 2024-01-17 09:01:17 +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
Onuralp SEZER 3b531249bc Merge pull request #731 from roboflow/dependabot/pip/mkdocs-material-9.5.4
⬆️ Bump mkdocs-material from 9.5.3 to 9.5.4
2024-01-16 20:01:05 +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
Piotr Skalski 624a2e4c10 Update tutorials section in README.md 2024-01-16 14:59:40 +01:00
dependabot[bot] 4c063acd2b ⬆️ Bump mkdocs-material from 9.5.3 to 9.5.4
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.3 to 9.5.4.
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.3...9.5.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-16 00:36:23 +00:00
Onuralp SEZER 0b805b7a04 Merge pull request #730 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2024-01-15 23:23:42 +03:00
pre-commit-ci[bot] a8081bec05 chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.1.11 → v0.1.13](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.11...v0.1.13)
2024-01-15 17:30:17 +00:00
Onuralp SEZER 68df3604ea Merge pull request #727 from roboflow/dependabot/pip/tox-4.12.0
⬆️ Bump tox from 4.11.4 to 4.12.0
2024-01-15 04:23:11 +03:00
Onuralp SEZER 3224032542 Merge pull request #726 from roboflow/dependabot/pip/ruff-0.1.13
⬆️ Bump ruff from 0.1.12 to 0.1.13
2024-01-15 04:22:58 +03:00
dependabot[bot] 1005de429c ⬆️ Bump tox from 4.11.4 to 4.12.0
Bumps [tox](https://github.com/tox-dev/tox) from 4.11.4 to 4.12.0.
- [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.11.4...4.12.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-15 00:48:05 +00:00
dependabot[bot] 1f1edfc8f8 ⬆️ Bump ruff from 0.1.12 to 0.1.13
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.12 to 0.1.13.
- [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.12...v0.1.13)

---
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-15 00:41:57 +00:00
Onuralp SEZER 338d62b2cb Merge pull request #721 from roboflow/dependabot/pip/ruff-0.1.12
⬆️ Bump ruff from 0.1.11 to 0.1.12
2024-01-13 03:14:32 +03:00
dependabot[bot] 17f7949b96 ⬆️ Bump ruff from 0.1.11 to 0.1.12
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.11 to 0.1.12.
- [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.11...v0.1.12)

---
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-12 00:42:35 +00:00
Onuralp SEZER 5ceb61a185 Merge pull request #719 from roboflow/dependabot/pip/jinja2-3.1.3
⬆️ Bump jinja2 from 3.1.2 to 3.1.3
2024-01-12 00:31:21 +03:00
dependabot[bot] 27d6832507 ⬆️ Bump jinja2 from 3.1.2 to 3.1.3
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.2 to 3.1.3.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/3.1.2...3.1.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-11 21:12:58 +00:00
James 8acc0a9ed8 Merge pull request #684 from roboflow/py38req
ci: 👷 close dependabot warning and PR requests for min version(py38)
2024-01-11 13:18:52 +00:00
Onuralp SEZER 97d9472f42 Add sv.RoundBoxAnnotator #702 from xaristeidou/develop
Add `sv.RoundBoxAnnotator` which annotates bounding box with round edges
2024-01-10 14:43:48 +03:00
Onuralp SEZER f3fa12d169 feat(rename): ✨ RoundBoundingBoxAnnotator to RoundBoxAnnotator
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-10 14:41:12 +03:00
Piotr Skalski 9b5bba1422 Merge pull request #714 from roboflow/example/speed_estimation
example/speed_estimation 🚗 💨💨💨
2024-01-10 12:10:52 +01:00
Onuralp SEZER a337ee591a feat: ✨ supervison downloader added
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-09 05:15:28 +03:00
Onuralp SEZER a1da604798 Merge pull request #716 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2024-01-08 21:27:35 +03:00
pre-commit-ci[bot] 1d4c3f3a68 chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.1.9 → v0.1.11](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.9...v0.1.11)
2024-01-08 17:42:51 +00:00
Piotr Skalski a72ad42628 Update README.md 2024-01-08 14:02:27 +01:00
pre-commit-ci[bot] be5748c086 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-08 12:57:53 +00:00
SkalskiP 9e05a545c7 Merge remote-tracking branch 'origin/example/speed_estimation' into example/speed_estimation
# Conflicts:
#	examples/speed_estimation/README.md
2024-01-08 13:57:37 +01:00
SkalskiP f0142344ae update note in speed estimation emo README.md 2024-01-08 13:57:17 +01:00
pre-commit-ci[bot] d596d10b3f fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-08 12:52:34 +00:00
SkalskiP 86134993c8 Merge remote-tracking branch 'origin/example/speed_estimation' into example/speed_estimation 2024-01-08 13:52:19 +01:00
SkalskiP 8bab49157d working version of inference_example.py 2024-01-08 13:52:09 +01:00
pre-commit-ci[bot] 4ae9131f0a fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-08 12:03:31 +00:00
SkalskiP 69894c82af initial version of inference_example.py script 2024-01-08 13:01:08 +01:00
SkalskiP e009402934 yolo_nas_example.py script is ready 2024-01-08 12:26:43 +01:00
SkalskiP 351ef464d9 initial version of yolo_nas_example.py script 2024-01-08 12:04:29 +01:00
SkalskiP 949e326559 updated README.md and ultralytics_example.py 2024-01-08 08:55:28 +01:00
SkalskiP 0a7e116d7f added script.sh and updated README.md 2024-01-08 08:34:55 +01:00
SkalskiP 5ce65a2fd5 current version 2024-01-08 08:11:40 +01:00
Onuralp SEZER 73ae555e10 docs: 📝 RoundBoundingBox documentations roundness fix
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-06 03:33:10 +03:00
Onuralp SEZER 98c0b8c2a5 Merge branch 'develop' into develop 2024-01-05 23:01:13 +03:00
Onuralp SEZER 7b7a734f2c docs: 📝 RoundBoundingBox documentations and small change on code section
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-01-05 22:59:29 +03:00
SkalskiP 3e6c1799ef current version 2024-01-05 16:51:27 +01:00
SkalskiP d83547ec80 initial working version 2024-01-04 23:38:47 +01:00
SkalskiP 7e8d44ac35 dropping idea of using lines to calculate speed 2024-01-04 22:35:59 +01:00
SkalskiP 2e9b2a1bcb detection, tracking and annotation added 2024-01-04 20:54:37 +01:00
SkalskiP 5df9730b3f 🚗💨💨💨 speed estimation example initial commit 2024-01-04 20:08:01 +01:00
Christoforos Aristeidou 4d955c8531 Merge branch 'round-edge-annotator' into develop 2023-12-29 11:05:43 +02:00
Christoforos Aristeidou bbc9374f06 🎨 pre-commit class sort 2023-12-29 11:03:40 +02:00
Christoforos Aristeidou 921256548e 🎨 pre-commit space correction 2023-12-29 11:03:16 +02:00
Christoforos Aristeidou b68c019fe1 shorted description of annotate method to fit 88 chars 2023-12-29 10:55:37 +02:00
Christoforos Aristeidou 055d06b56f changed default value of roundness 2023-12-29 10:52:57 +02:00
Christoforos Aristeidou 5274147856 description for annotate method of RoundBoundingBoxAnnotator 2023-12-29 10:47:05 +02:00
Christoforos Aristeidou 2b8c31a481 class description correction 2023-12-29 10:41:03 +02:00
Christoforos Aristeidou 0bd9862f8a Class description for RoundBoundingBoxAnnotator 2023-12-29 10:40:14 +02:00
Christoforos Aristeidou 587ed25f49 added RoundBoundingBoxAnnotator to __init__ import 2023-12-29 10:36:24 +02:00
Christoforos Aristeidou df6b628a30 added RoundBoundingBoxAnnotator class annotator 2023-12-29 10:20:33 +02:00
Onuralp SEZER f0e7c0c847 ci: 👷 close dependabot warning and PR requests for min version(py38)
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2023-12-19 16:53:59 +03:00
55 changed files with 2320 additions and 678 deletions
-28
View File
@@ -1,28 +0,0 @@
attrs==23.1.0
certifi==2023.7.22
charset-normalizer==2.0.12
cycler==0.12.1
exceptiongroup==1.1.3
fonttools==4.43.1
idna==3.4
iniconfig==2.0.0
kiwisolver==1.4.5
matplotlib==3.5.0
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
setuptools-scm==8.0.4
six==1.16.0
tomli==2.0.1
tqdm==4.62.3
typing_extensions==4.8.0
urllib3==1.26.18
defusedxml == 0.7.1
+29 -3
View File
@@ -20,13 +20,39 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
check-latest: true
cache: 'pip'
cache-dependency-path: '**/requirements-dev-min.txt'
- name: 📦 Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r .github/requirements-dev-min.txt
pip install \
attrs==23.1.0 \
certifi==2023.7.22 \
charset-normalizer==2.0.12 \
cycler==0.12.1 \
exceptiongroup==1.1.3 \
fonttools==4.43.1 \
idna==3.4 \
iniconfig==2.0.0 \
kiwisolver==1.4.5 \
matplotlib==3.5.0 \
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.10.0 \
setuptools-scm==8.0.4 \
six==1.16.0 \
tomli==2.0.1 \
tqdm==4.62.3 \
typing_extensions==4.8.0 \
urllib3==1.26.18 \
defusedxml==0.7.1
- name: 🧪 Test
run: "python -m pytest ./test"
+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-*
+1 -1
View File
@@ -46,7 +46,7 @@ repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.9
rev: v0.1.14
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
+9 -9
View File
@@ -216,18 +216,18 @@ Supervision provides a set of [utils](https://supervision.roboflow.com/datasets/
## 🎬 tutorials
<p align="left">
<a href="https://youtu.be/4Q3ut7vqD5o" title="Traffic Analysis with YOLOv8 and ByteTrack - Vehicle Detection and Tracking"><img src="https://github.com/roboflow/supervision/assets/26109316/54afdf1c-218c-4451-8f12-627fb85f1682" alt="Traffic Analysis with YOLOv8 and ByteTrack - Vehicle Detection and Tracking" width="300px" align="left" /></a>
<a href="https://youtu.be/4Q3ut7vqD5o" title="Traffic Analysis with YOLOv8 and ByteTrack - Vehicle Detection and Tracking"><strong>Traffic Analysis with YOLOv8 and ByteTrack - Vehicle Detection and Tracking</strong></a>
<div><strong>Created: 6 Sep 2023</strong> | <strong>Updated: 6 Sep 2023</strong></div>
<br/> In this video, we explore real-time traffic analysis using YOLOv8 and ByteTrack to detect and track vehicles on aerial images. Harnessing the power of Python and Supervision, we delve deep into assigning cars to specific entry zones and understanding their direction of movement. By visualizing their paths, we gain insights into traffic flow across bustling roundabouts... </p>
<a href="https://youtu.be/uWP6UjDeZvY" title="Speed Estimation & Vehicle Tracking | Computer Vision | Open Source"><img src="https://github.com/SkalskiP/SkalskiP/assets/26109316/61a444c8-b135-48ce-b979-2a5ab47c5a91" alt="Speed Estimation & Vehicle Tracking | Computer Vision | Open Source" width="300px" align="left" /></a>
<a href="https://youtu.be/uWP6UjDeZvY" title="Speed Estimation & Vehicle Tracking | Computer Vision | Open Source"><strong>Speed Estimation & Vehicle Tracking | Computer Vision | Open Source</strong></a>
<div><strong>Created: 11 Jan 2024</strong> | <strong>Updated: 11 Jan 2024</strong></div>
<br/> Learn how to track and estimate the speed of vehicles using YOLO, ByteTrack, and Roboflow Inference. This comprehensive tutorial covers object detection, multi-object tracking, filtering detections, perspective transformation, speed estimation, visualization improvements, and more.</p>
<br/>
<p align="left">
<a href="https://youtu.be/D-D6ZmadzPE" title="SAM - Segment Anything Model by Meta AI: Complete Guide"><img src="https://github.com/SkalskiP/SkalskiP/assets/26109316/6913ff11-53c6-4341-8d90-eaff3023c3fd" alt="SAM - Segment Anything Model by Meta AI: Complete Guide" width="300px" align="left" /></a>
<a href="https://youtu.be/D-D6ZmadzPE" title="SAM - Segment Anything Model by Meta AI: Complete Guide"><strong>SAM - Segment Anything Model by Meta AI: Complete Guide</strong></a>
<div><strong>Created: 11 Apr 2023</strong> | <strong>Updated: 11 Apr 2023</strong></div>
<br/> Discover the incredible potential of Meta AI's Segment Anything Model (SAM)! We dive into SAM, an efficient and promptable model for image segmentation, which has revolutionized computer vision tasks. With over 1 billion masks on 11M licensed and privacy-respecting images, SAM's zero-shot performance is often competitive with or even superior to prior fully supervised results... </p>
<a href="https://youtu.be/4Q3ut7vqD5o" title="Traffic Analysis with YOLOv8 and ByteTrack - Vehicle Detection and Tracking"><img src="https://github.com/roboflow/supervision/assets/26109316/54afdf1c-218c-4451-8f12-627fb85f1682" alt="Traffic Analysis with YOLOv8 and ByteTrack - Vehicle Detection and Tracking" width="300px" align="left" /></a>
<a href="https://youtu.be/4Q3ut7vqD5o" title="Traffic Analysis with YOLOv8 and ByteTrack - Vehicle Detection and Tracking"><strong>Traffic Analysis with YOLOv8 and ByteTrack - Vehicle Detection and Tracking</strong></a>
<div><strong>Created: 6 Sep 2023</strong> | <strong>Updated: 6 Sep 2023</strong></div>
<br/> In this video, we explore real-time traffic analysis using YOLOv8 and ByteTrack to detect and track vehicles on aerial images. Harnessing the power of Python and Supervision, we delve deep into assigning cars to specific entry zones and understanding their direction of movement. By visualizing their paths, we gain insights into traffic flow across bustling roundabouts... </p>
## 💜 built with supervision
@@ -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
+57 -3
View File
@@ -1,3 +1,7 @@
---
comments: true
---
=== "BoundingBox"
```python
@@ -19,6 +23,27 @@
</div>
=== "RoundBox"
```python
>>> import supervision as sv
>>> image = ...
>>> detections = sv.Detections(...)
>>> round_box_annotator = sv.RoundBoxAnnotator()
>>> annotated_frame = round_box_annotator.annotate(
... scene=image.copy(),
... detections=detections
... )
```
<div class="result" markdown>
![round-box-annotator-example](https://media.roboflow.com/supervision-annotator-examples/round-box-annotator-example-purple.png){ align=center width="800" }
</div>
=== "BoxCorner"
```python
@@ -99,7 +124,7 @@
<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>
@@ -162,7 +187,28 @@
<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>
@@ -327,7 +373,7 @@
<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>
@@ -335,6 +381,10 @@
:::supervision.annotators.core.BoundingBoxAnnotator
## RoundBoxAnnotator
:::supervision.annotators.core.RoundBoxAnnotator
## BoxCornerAnnotator
:::supervision.annotators.core.BoxCornerAnnotator
@@ -363,6 +413,10 @@
:::supervision.annotators.core.HaloAnnotator
## PercentageBarAnnotator
:::supervision.annotators.core.PercentageBarAnnotator
## HeatMapAnnotator
:::supervision.annotators.core.HeatMapAnnotator
+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.
+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
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
## 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
+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
+4
View File
@@ -1 +1,5 @@
---
comments: true
---
🚧 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.
+4
View File
@@ -1 +1,5 @@
---
comments: true
---
🚧 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
+4
View File
@@ -1,3 +1,7 @@
---
comments: true
---
<div align="center">
<p>
<a align="center" href="" target="_blank">
+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
+1
View File
@@ -7,6 +7,7 @@ interfaces with diverse applications.
- [tracking](./tracking) by [@SkalskiP](https://github.com/SkalskiP)
- [count people in zone](./count_people_in_zone) by [@SkalskiP](https://github.com/SkalskiP)
- [traffic analysis](./traffic_analysis) by [@SkalskiP](https://github.com/SkalskiP)
- [speed estimation](./speed_estimation) by [@SkalskiP](https://github.com/SkalskiP)
- [heatmap and track](./heatmap_and_track/) by [@HinePo](https://github.com/HinePo)
## contributing
+8
View File
@@ -3,3 +3,11 @@ data/*
# But re-include all .json files
!data/*.json
*.pt
*.pth
*.mp4
*.mov
*.png
*.jpg
*.jpeg
+9
View File
@@ -0,0 +1,9 @@
data/
venv*/
*.pt
*.pth
*.mp4
*.mov
*.png
*.jpg
*.jpeg
+117
View File
@@ -0,0 +1,117 @@
# 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/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
- clone repository and navigate to example directory
```bash
git clone https://github.com/roboflow/supervision.git
cd supervision/examples/speed_estimation
```
- setup python environment and activate it [optional]
```bash
python3.10 -m venv venv
source venv/bin/activate
```
- install required dependencies
```bash
pip install -r requirements.txt
```
- download `vehicles.mp4` file
```bash
python3.10 video_downloader.py
```
## 🛠️ script arguments
- `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided
directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment
variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key)
to acquire your `API KEY`.
- `--model_id` (optional): Designates the Roboflow model ID to be used. The default
value is `"yolov8x-1280"`.
- `--source_weights_path`: Required. Specifies the path to the YOLO model's weights
file, which is essential for the object detection process. This file contains the
data that the model uses to identify objects in the video.
- `--source_video_path`: Required. The path to the source video file that will be
analyzed. This is the input video on which traffic flow analysis will be performed.
- `--target_video_path`: The path to save the output video with
annotations. If not specified, the processed video will be displayed in real-time
without being saved.
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO
model to filter detections. Default is `0.3`. This determines how confident the
model should be to recognize an object in the video.
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
for the model. Default is 0.7. This value is used to manage object detection
accuracy, particularly in distinguishing between different objects.
## ⚙️ run
- yolo-nas
```bash
python yolo_nas_example.py \
--source_video_path data/vehicles.mp4 \
--target_video_path data/vehicles-result.mp4 \
--confidence_threshold 0.3 \
--iou_threshold 0.5
```
- inference
```bash
python inference_example.py \
--roboflow_api_key <ROBOFLOW API KEY> \
--source_video_path data/vehicles.mp4 \
--target_video_path data/vehicles-result.mp4 \
--confidence_threshold 0.3 \
--iou_threshold 0.5
```
- ultralytics
```bash
python ultralytics_example.py \
--source_video_path data/vehicles.mp4 \
--target_video_path data/vehicles-result.mp4 \
--confidence_threshold 0.3 \
--iou_threshold 0.5
```
## © license
This demo integrates two main components, each with its own licensing:
- ultralytics: The object detection model used in this demo, YOLOv8, is distributed
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
You can find more details about this license here.
- supervision: The analytics code that powers the zone-based analysis in this demo is
based on the Supervision library, which is licensed under the
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
makes the Supervision part of the code fully open source and freely usable in your
projects.
@@ -0,0 +1,167 @@
import argparse
import os
from collections import defaultdict, deque
import cv2
import numpy as np
from inference.models.utils import get_roboflow_model
import supervision as sv
SOURCE = np.array([[1252, 787], [2298, 803], [5039, 2159], [-550, 2159]])
TARGET_WIDTH = 25
TARGET_HEIGHT = 250
TARGET = np.array(
[
[0, 0],
[TARGET_WIDTH - 1, 0],
[TARGET_WIDTH - 1, TARGET_HEIGHT - 1],
[0, TARGET_HEIGHT - 1],
]
)
class ViewTransformer:
def __init__(self, source: np.ndarray, target: np.ndarray) -> None:
source = source.astype(np.float32)
target = target.astype(np.float32)
self.m = cv2.getPerspectiveTransform(source, target)
def transform_points(self, points: np.ndarray) -> np.ndarray:
reshaped_points = points.reshape(-1, 1, 2).astype(np.float32)
transformed_points = cv2.perspectiveTransform(reshaped_points, self.m)
return transformed_points.reshape(-1, 2)
def parse_arguments() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Vehicle Speed Estimation using Inference and Supervision"
)
parser.add_argument(
"--model_id",
default="yolov8x-640",
help="Roboflow model ID",
type=str,
)
parser.add_argument(
"--roboflow_api_key",
default=None,
help="Roboflow API KEY",
type=str,
)
parser.add_argument(
"--source_video_path",
required=True,
help="Path to the source video file",
type=str,
)
parser.add_argument(
"--target_video_path",
required=True,
help="Path to the target video file (output)",
type=str,
)
parser.add_argument(
"--confidence_threshold",
default=0.3,
help="Confidence threshold for the model",
type=float,
)
parser.add_argument(
"--iou_threshold", default=0.7, help="IOU threshold for the model", type=float
)
return parser.parse_args()
if __name__ == "__main__":
args = parse_arguments()
api_key = args.roboflow_api_key
api_key = os.environ.get("ROBOFLOW_API_KEY", api_key)
if api_key is None:
raise ValueError(
"Roboflow API key is missing. Please provide it as an argument or set the "
"ROBOFLOW_API_KEY environment variable."
)
args.roboflow_api_key = api_key
video_info = sv.VideoInfo.from_video_path(video_path=args.source_video_path)
model = get_roboflow_model(model_id=args.model_id, api_key=args.roboflow_api_key)
byte_track = sv.ByteTrack(
frame_rate=video_info.fps, track_thresh=args.confidence_threshold
)
thickness = sv.calculate_dynamic_line_thickness(
resolution_wh=video_info.resolution_wh
)
text_scale = sv.calculate_dynamic_text_scale(resolution_wh=video_info.resolution_wh)
bounding_box_annotator = sv.BoundingBoxAnnotator(thickness=thickness)
label_annotator = sv.LabelAnnotator(
text_scale=text_scale,
text_thickness=thickness,
text_position=sv.Position.BOTTOM_CENTER,
)
trace_annotator = sv.TraceAnnotator(
thickness=thickness,
trace_length=video_info.fps * 2,
position=sv.Position.BOTTOM_CENTER,
)
frame_generator = sv.get_video_frames_generator(source_path=args.source_video_path)
polygon_zone = sv.PolygonZone(
polygon=SOURCE, frame_resolution_wh=video_info.resolution_wh
)
view_transformer = ViewTransformer(source=SOURCE, target=TARGET)
coordinates = defaultdict(lambda: deque(maxlen=video_info.fps))
with sv.VideoSink(args.target_video_path, video_info) as sink:
for frame in frame_generator:
results = model.infer(frame)[0]
detections = sv.Detections.from_inference(results)
detections = detections[detections.confidence > args.confidence_threshold]
detections = detections[polygon_zone.trigger(detections)]
detections = detections.with_nms(threshold=args.iou_threshold)
detections = byte_track.update_with_detections(detections=detections)
points = detections.get_anchors_coordinates(
anchor=sv.Position.BOTTOM_CENTER
)
points = view_transformer.transform_points(points=points).astype(int)
for tracker_id, [_, y] in zip(detections.tracker_id, points):
coordinates[tracker_id].append(y)
labels = []
for tracker_id in detections.tracker_id:
if len(coordinates[tracker_id]) < video_info.fps / 2:
labels.append(f"#{tracker_id}")
else:
coordinate_start = coordinates[tracker_id][-1]
coordinate_end = coordinates[tracker_id][0]
distance = abs(coordinate_start - coordinate_end)
time = len(coordinates[tracker_id]) / video_info.fps
speed = distance / time * 3.6
labels.append(f"#{tracker_id} {int(speed)} km/h")
annotated_frame = frame.copy()
annotated_frame = trace_annotator.annotate(
scene=annotated_frame, detections=detections
)
annotated_frame = bounding_box_annotator.annotate(
scene=annotated_frame, detections=detections
)
annotated_frame = label_annotator.annotate(
scene=annotated_frame, detections=detections, labels=labels
)
sink.write_frame(annotated_frame)
cv2.imshow("frame", annotated_frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cv2.destroyAllWindows()
@@ -0,0 +1,6 @@
supervision==0.18.0rc1
tqdm==4.66.1
requests
ultralytics==8.0.237
super-gradients==3.5.0
inference==0.9.8
@@ -0,0 +1,148 @@
import argparse
from collections import defaultdict, deque
import cv2
import numpy as np
from ultralytics import YOLO
import supervision as sv
SOURCE = np.array([[1252, 787], [2298, 803], [5039, 2159], [-550, 2159]])
TARGET_WIDTH = 25
TARGET_HEIGHT = 250
TARGET = np.array(
[
[0, 0],
[TARGET_WIDTH - 1, 0],
[TARGET_WIDTH - 1, TARGET_HEIGHT - 1],
[0, TARGET_HEIGHT - 1],
]
)
class ViewTransformer:
def __init__(self, source: np.ndarray, target: np.ndarray) -> None:
source = source.astype(np.float32)
target = target.astype(np.float32)
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)
def parse_arguments() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Vehicle Speed Estimation using Ultralytics and Supervision"
)
parser.add_argument(
"--source_video_path",
required=True,
help="Path to the source video file",
type=str,
)
parser.add_argument(
"--target_video_path",
required=True,
help="Path to the target video file (output)",
type=str,
)
parser.add_argument(
"--confidence_threshold",
default=0.3,
help="Confidence threshold for the model",
type=float,
)
parser.add_argument(
"--iou_threshold", default=0.7, help="IOU threshold for the model", type=float
)
return parser.parse_args()
if __name__ == "__main__":
args = parse_arguments()
video_info = sv.VideoInfo.from_video_path(video_path=args.source_video_path)
model = YOLO("yolov8x.pt")
byte_track = sv.ByteTrack(
frame_rate=video_info.fps, track_thresh=args.confidence_threshold
)
thickness = sv.calculate_dynamic_line_thickness(
resolution_wh=video_info.resolution_wh
)
text_scale = sv.calculate_dynamic_text_scale(resolution_wh=video_info.resolution_wh)
bounding_box_annotator = sv.BoundingBoxAnnotator(thickness=thickness)
label_annotator = sv.LabelAnnotator(
text_scale=text_scale,
text_thickness=thickness,
text_position=sv.Position.BOTTOM_CENTER,
)
trace_annotator = sv.TraceAnnotator(
thickness=thickness,
trace_length=video_info.fps * 2,
position=sv.Position.BOTTOM_CENTER,
)
frame_generator = sv.get_video_frames_generator(source_path=args.source_video_path)
polygon_zone = sv.PolygonZone(
polygon=SOURCE, frame_resolution_wh=video_info.resolution_wh
)
view_transformer = ViewTransformer(source=SOURCE, target=TARGET)
coordinates = defaultdict(lambda: deque(maxlen=video_info.fps))
with sv.VideoSink(args.target_video_path, video_info) as sink:
for frame in frame_generator:
result = model(frame)[0]
detections = sv.Detections.from_ultralytics(result)
detections = detections[detections.confidence > args.confidence_threshold]
detections = detections[polygon_zone.trigger(detections)]
detections = detections.with_nms(threshold=args.iou_threshold)
detections = byte_track.update_with_detections(detections=detections)
points = detections.get_anchors_coordinates(
anchor=sv.Position.BOTTOM_CENTER
)
points = view_transformer.transform_points(points=points).astype(int)
for tracker_id, [_, y] in zip(detections.tracker_id, points):
coordinates[tracker_id].append(y)
labels = []
for tracker_id in detections.tracker_id:
if len(coordinates[tracker_id]) < video_info.fps / 2:
labels.append(f"#{tracker_id}")
else:
coordinate_start = coordinates[tracker_id][-1]
coordinate_end = coordinates[tracker_id][0]
distance = abs(coordinate_start - coordinate_end)
time = len(coordinates[tracker_id]) / video_info.fps
speed = distance / time * 3.6
labels.append(f"#{tracker_id} {int(speed)} km/h")
annotated_frame = frame.copy()
annotated_frame = trace_annotator.annotate(
scene=annotated_frame, detections=detections
)
annotated_frame = bounding_box_annotator.annotate(
scene=annotated_frame, detections=detections
)
annotated_frame = label_annotator.annotate(
scene=annotated_frame, detections=detections, labels=labels
)
sink.write_frame(annotated_frame)
cv2.imshow("frame", annotated_frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cv2.destroyAllWindows()
@@ -0,0 +1,8 @@
import os
from supervision.assets import VideoAssets, download_assets
if not os.path.exists("data"):
os.makedirs("data")
os.chdir("data")
download_assets(VideoAssets.VEHICLES)
@@ -0,0 +1,145 @@
import argparse
from collections import defaultdict, deque
import cv2
import numpy as np
from super_gradients.common.object_names import Models
from super_gradients.training import models
import supervision as sv
SOURCE = np.array([[1252, 787], [2298, 803], [5039, 2159], [-550, 2159]])
TARGET_WIDTH = 25
TARGET_HEIGHT = 250
TARGET = np.array(
[
[0, 0],
[TARGET_WIDTH - 1, 0],
[TARGET_WIDTH - 1, TARGET_HEIGHT - 1],
[0, TARGET_HEIGHT - 1],
]
)
class ViewTransformer:
def __init__(self, source: np.ndarray, target: np.ndarray) -> None:
source = source.astype(np.float32)
target = target.astype(np.float32)
self.m = cv2.getPerspectiveTransform(source, target)
def transform_points(self, points: np.ndarray) -> np.ndarray:
reshaped_points = points.reshape(-1, 1, 2).astype(np.float32)
transformed_points = cv2.perspectiveTransform(reshaped_points, self.m)
return transformed_points.reshape(-1, 2)
def parse_arguments() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Vehicle Speed Estimation using YOLO-NAS and Supervision"
)
parser.add_argument(
"--source_video_path",
required=True,
help="Path to the source video file",
type=str,
)
parser.add_argument(
"--target_video_path",
required=True,
help="Path to the target video file (output)",
type=str,
)
parser.add_argument(
"--confidence_threshold",
default=0.3,
help="Confidence threshold for the model",
type=float,
)
parser.add_argument(
"--iou_threshold", default=0.7, help="IOU threshold for the model", type=float
)
return parser.parse_args()
if __name__ == "__main__":
args = parse_arguments()
video_info = sv.VideoInfo.from_video_path(video_path=args.source_video_path)
model = models.get(Models.YOLO_NAS_L, pretrained_weights="coco")
byte_track = sv.ByteTrack(
frame_rate=video_info.fps, track_thresh=args.confidence_threshold
)
thickness = sv.calculate_dynamic_line_thickness(
resolution_wh=video_info.resolution_wh
)
text_scale = sv.calculate_dynamic_text_scale(resolution_wh=video_info.resolution_wh)
bounding_box_annotator = sv.BoundingBoxAnnotator(thickness=thickness)
label_annotator = sv.LabelAnnotator(
text_scale=text_scale,
text_thickness=thickness,
text_position=sv.Position.BOTTOM_CENTER,
)
trace_annotator = sv.TraceAnnotator(
thickness=thickness,
trace_length=video_info.fps * 2,
position=sv.Position.BOTTOM_CENTER,
)
frame_generator = sv.get_video_frames_generator(source_path=args.source_video_path)
polygon_zone = sv.PolygonZone(
polygon=SOURCE, frame_resolution_wh=video_info.resolution_wh
)
view_transformer = ViewTransformer(source=SOURCE, target=TARGET)
coordinates = defaultdict(lambda: deque(maxlen=video_info.fps))
with sv.VideoSink(args.target_video_path, video_info) as sink:
for frame in frame_generator:
result = model.predict(frame)[0]
detections = sv.Detections.from_yolo_nas(result)
detections = detections[polygon_zone.trigger(detections)]
detections = detections.with_nms(threshold=args.iou_threshold)
detections = byte_track.update_with_detections(detections=detections)
points = detections.get_anchors_coordinates(
anchor=sv.Position.BOTTOM_CENTER
)
points = view_transformer.transform_points(points=points).astype(int)
for tracker_id, [_, y] in zip(detections.tracker_id, points):
coordinates[tracker_id].append(y)
labels = []
for tracker_id in detections.tracker_id:
if len(coordinates[tracker_id]) < video_info.fps / 2:
labels.append(f"#{tracker_id}")
else:
coordinate_start = coordinates[tracker_id][-1]
coordinate_end = coordinates[tracker_id][0]
distance = abs(coordinate_start - coordinate_end)
time = len(coordinates[tracker_id]) / video_info.fps
speed = distance / time * 3.6
labels.append(f"#{tracker_id} {int(speed)} km/h")
annotated_frame = frame.copy()
annotated_frame = trace_annotator.annotate(
scene=annotated_frame, detections=detections
)
annotated_frame = bounding_box_annotator.annotate(
scene=annotated_frame, detections=detections
)
annotated_frame = label_annotator.annotate(
scene=annotated_frame, detections=detections, labels=labels
)
sink.write_frame(annotated_frame)
cv2.imshow("frame", annotated_frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cv2.destroyAllWindows()
+8
View File
@@ -1 +1,9 @@
data/
venv/
*.pt
*.pth
*.mp4
*.mov
*.png
*.jpg
*.jpeg
Generated
+590 -506
View File
File diff suppressed because it is too large Load Diff
+9 -6
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "supervision"
version = "0.18.0rc1"
version = "0.18.0rc3"
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,12 +33,15 @@ classifiers=[
[tool.poetry.dependencies]
python = "^3.8"
numpy = ">=1.21.2"
scipy = ">=1.9.0"
scipy = [
{ version = "1.10.0", python = "<3.9" },
{ version = "^1.10.0", python = ">=3.9" }
]
matplotlib = ">=3.5.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"
@@ -54,7 +57,7 @@ pytest = "^7.2.2"
wheel = ">=0.40,<0.43"
notebook = ">=6.5.3,<8.0.0"
build = ">=0.10,<1.1"
ruff = ">=0.0.280,<0.1.12"
ruff = ">=0.1.0"
isort = "^5.12.0"
mypy = "^1.4.1"
pre-commit = "^3.3.3"
@@ -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.4"}
mkdocstrings = {extras = ["python"], version = ">=0.20,<0.25"}
+2
View File
@@ -18,8 +18,10 @@ from supervision.annotators.core import (
HeatMapAnnotator,
LabelAnnotator,
MaskAnnotator,
PercentageBarAnnotator,
PixelateAnnotator,
PolygonAnnotator,
RoundBoxAnnotator,
TraceAnnotator,
TriangleAnnotator,
)
+315 -13
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,
):
@@ -103,7 +103,7 @@ class MaskAnnotator(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,
):
@@ -186,7 +186,7 @@ class PolygonAnnotator(BaseAnnotator):
def __init__(
self,
color: Union[Color, ColorPalette] = ColorPalette.default(),
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
thickness: int = 2,
color_lookup: ColorLookup = ColorLookup.CLASS,
):
@@ -268,7 +268,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,
):
@@ -354,7 +354,7 @@ class HaloAnnotator(BaseAnnotator):
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,
@@ -446,7 +446,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,
@@ -536,7 +536,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,
@@ -622,7 +622,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,
):
@@ -706,7 +706,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,
@@ -782,8 +782,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,
@@ -1027,7 +1027,7 @@ class TraceAnnotator:
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,
@@ -1288,7 +1288,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,
@@ -1368,3 +1368,305 @@ class TriangleAnnotator(BaseAnnotator):
cv2.fillPoly(scene, [vertices], color.as_bgr())
return scene
class RoundBoxAnnotator(BaseAnnotator):
"""
A class for drawing bounding boxes with round edges on an image
using provided detections.
"""
def __init__(
self,
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
thickness: int = 2,
color_lookup: ColorLookup = ColorLookup.CLASS,
roundness: float = 0.6,
):
"""
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`.
roundness (float): Percent of roundness for edges of bounding box.
Value must be float 0 < roundness <= 1.0
By default roundness percent is calculated based on smaller side
length (width or height).
"""
self.color: Union[Color, ColorPalette] = color
self.thickness: int = thickness
self.color_lookup: ColorLookup = color_lookup
if not 0 < roundness <= 1.0:
raise ValueError("roundness attribute must be float between (0, 1.0]")
self.roundness: float = roundness
def annotate(
self,
scene: np.ndarray,
detections: Detections,
custom_color_lookup: Optional[np.ndarray] = None,
) -> np.ndarray:
"""
Annotates the given scene with bounding boxes with rounded edges
based on the provided detections.
Args:
scene (np.ndarray): The image where rounded 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 supervision as sv
>>> image = ...
>>> detections = sv.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-purple.png)
"""
for detection_idx in range(len(detections)):
x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
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,
)
radius = (
int((x2 - x1) // 2 * self.roundness)
if abs(x1 - x2) < abs(y1 - y2)
else int((y2 - y1) // 2 * self.roundness)
)
circle_coordinates = [
((x1 + radius), (y1 + radius)),
((x2 - radius), (y1 + radius)),
((x2 - radius), (y2 - radius)),
((x1 + radius), (y2 - radius)),
]
line_coordinates = [
((x1 + radius, y1), (x2 - radius, y1)),
((x2, y1 + radius), (x2, y2 - radius)),
((x1 + radius, y2), (x2 - radius, y2)),
((x1, y1 + radius), (x1, y2 - radius)),
]
start_angles = (180, 270, 0, 90)
end_angles = (270, 360, 90, 180)
for center_coordinates, line, start_angle, end_angle in zip(
circle_coordinates, line_coordinates, start_angles, end_angles
):
cv2.ellipse(
img=scene,
center=center_coordinates,
axes=(radius, radius),
angle=0,
startAngle=start_angle,
endAngle=end_angle,
color=color.as_bgr(),
thickness=self.thickness,
)
cv2.line(
img=scene,
pt1=line[0],
pt2=line[1],
color=color.as_bgr(),
thickness=self.thickness,
)
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
@@ -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,
+85 -13
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:
@@ -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,18 +109,35 @@ class LineZone:
crossed_in = np.full(len(detections), False)
crossed_out = np.full(len(detections), False)
for i, (xyxy, _, confidence, class_id, tracker_id) in enumerate(detections):
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
@@ -94,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,
+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,
+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:
+1 -1
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,
+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
+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)
+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