Compare commits

...
156 Commits
Author SHA1 Message Date
Piotr Skalski f06dcf73a5 Merge pull request #1277 from roboflow/feat/patch-rich-label-annotator
Patch RichLabelAnnotator, fix in-place functionality of other annotators
2024-07-03 13:39:10 +02:00
SkalskiP 0bb25fdd91 bump version from 0.21.0 to 0.22.0rc1 2024-07-03 13:15:49 +02:00
Onuralp SEZER 9501a0c72e Merge pull request #1323 from roboflow/dependabot/pip/mkdocs-jupyter-0.24.8
⬆️ Bump mkdocs-jupyter from 0.24.7 to 0.24.8
2024-07-03 13:05:22 +03:00
Onuralp SEZER d9b9143f07 Merge pull request #1322 from roboflow/dependabot/pip/mkdocs-material-9.5.28
⬆️ Bump mkdocs-material from 9.5.27 to 9.5.28
2024-07-03 13:04:46 +03:00
dependabot[bot] b7e6bcc15f ⬆️ Bump mkdocs-jupyter from 0.24.7 to 0.24.8
Bumps [mkdocs-jupyter](https://github.com/danielfrg/mkdocs-jupyter) from 0.24.7 to 0.24.8.
- [Changelog](https://github.com/danielfrg/mkdocs-jupyter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/danielfrg/mkdocs-jupyter/compare/0.24.7...0.24.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-03 00:44:18 +00:00
dependabot[bot] 08fbcbd8ea ⬆️ Bump mkdocs-material from 9.5.27 to 9.5.28
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.27 to 9.5.28.
- [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.27...9.5.28)

---
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-07-03 00:43:11 +00:00
Onuralp SEZER e5114a233c Merge pull request #1321 from roboflow/dependabot/pip/pillow-10.4.0
⬆️ Bump pillow from 10.3.0 to 10.4.0
2024-07-02 04:06:01 +03:00
dependabot[bot] dea7f83e50 ⬆️ Bump pillow from 10.3.0 to 10.4.0
Bumps [pillow](https://github.com/python-pillow/Pillow) from 10.3.0 to 10.4.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/10.3.0...10.4.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-02 00:58:12 +00:00
Onuralp SEZER 034d3eaaac Merge pull request #1319 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2024-07-01 20:58:13 +03:00
pre-commit-ci[bot] 7397d8a240 chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.10 → v0.5.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.10...v0.5.0)
2024-07-01 17:57:15 +00:00
Linas Kondrackis 09a06e10a1 Merge pull request #1312 from Bhavay-2001/overlay_image
Updated overlay_image function in CropAnnotator
2024-07-01 14:32:31 +03:00
Linas Kondrackis 8d26a9291b Merge pull request #1317 from roboflow/fix/poetry/dep
fix: 🐞 skip broken docutils package, revert numpy version lock
2024-07-01 11:12:07 +03:00
Onuralp SEZER 28d219c86d fix: 🐞 skip broken docutils and remove numpy version lock
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-06-29 00:42:11 +03:00
Onuralp SEZER 89ef841955 Merge pull request #1314 from roboflow/dependabot/pip/ruff-0.5.0
⬆️ Bump ruff from 0.4.10 to 0.5.0
2024-06-28 23:21:12 +03:00
dependabot[bot] 2f39a2c3c6 ⬆️ Bump ruff from 0.4.10 to 0.5.0
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.4.10 to 0.5.0.
- [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.4.10...0.5.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-28 00:45:48 +00:00
Onuralp SEZER d8f990231c Merge pull request #1300 from kirilman/develop
detectron2 mask convert to detection
2024-06-27 22:21:51 +03:00
pre-commit-ci[bot] 552c8dbede fix(pre_commit): 🎨 auto format pre-commit hooks 2024-06-27 12:21:29 +00:00
Bhavay Malhotra e421af494f Changed the parameters of overlay_image function 2024-06-27 17:43:10 +05:30
Onuralp SEZER 5b3fc12aea Merge pull request #1311 from roboflow/dependabot/pip/twine-5.1.1
⬆️ Bump twine from 5.1.0 to 5.1.1
2024-06-27 09:22:41 +03:00
dependabot[bot] 854fb18b34 ⬆️ Bump twine from 5.1.0 to 5.1.1
Bumps [twine](https://github.com/pypa/twine) from 5.1.0 to 5.1.1.
- [Release notes](https://github.com/pypa/twine/releases)
- [Changelog](https://github.com/pypa/twine/blob/main/docs/changelog.rst)
- [Commits](https://github.com/pypa/twine/compare/5.1.0...v5.1.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-27 01:03:10 +00:00
Onuralp SEZER f66444bc91 fix: 🐞 add if condition for check existince of 'pred_masks' for detectron2
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-06-25 16:25:28 +03:00
Onuralp SEZER 6de21048d2 Merge pull request #1307 from roboflow/docs/suppress-mkdocs-relative-link-messages
Suppress mkdocs relative link INFO messages
2024-06-25 10:40:15 +03:00
Onuralp SEZER db9d6c623b Merge pull request #1305 from roboflow/dependabot/pip/mypy-1.10.1
⬆️ Bump mypy from 1.10.0 to 1.10.1
2024-06-25 10:15:08 +03:00
Onuralp SEZER fb7ecd73d9 Merge pull request #1304 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2024-06-25 10:14:55 +03:00
Onuralp SEZER 2bc19d69af Merge pull request #1306 from roboflow/dependabot/pip/mike-2.1.2
⬆️ Bump mike from 2.1.1 to 2.1.2
2024-06-25 10:14:39 +03:00
LinasKo a377b54a60 Merge branch 'develop' into feat/supervision-cleanup 2024-06-25 09:57:13 +03:00
dependabot[bot] db1998c155 ⬆️ Bump mike from 2.1.1 to 2.1.2
Bumps [mike](https://github.com/jimporter/mike) from 2.1.1 to 2.1.2.
- [Release notes](https://github.com/jimporter/mike/releases)
- [Changelog](https://github.com/jimporter/mike/blob/master/CHANGES.md)
- [Commits](https://github.com/jimporter/mike/compare/v2.1.1...v2.1.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-25 01:04:21 +00:00
dependabot[bot] ef6369b93d ⬆️ Bump mypy from 1.10.0 to 1.10.1
Bumps [mypy](https://github.com/python/mypy) from 1.10.0 to 1.10.1.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/v1.10.0...v1.10.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-25 01:02:06 +00:00
pre-commit-ci[bot] a21a17ba29 chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.9 → v0.4.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.9...v0.4.10)
2024-06-24 17:47:27 +00:00
Onuralp SEZER c973647cb8 Merge pull request #975 from roboflow/cookbooks/json-csv-sinks
docs(notebooks): 📝 json and csv sink cookbooks are added
2024-06-24 15:53:08 +03:00
LinasKo 71bdb01547 Sinks cookbooks: add 'person' label, min_consectutive_frames 2024-06-24 15:35:21 +03:00
LinasKo 92a5a931a8 Merge branch 'develop' into cookbooks/json-csv-sinks 2024-06-24 15:08:16 +03:00
LinasKo 0ecd88d4d1 Sink cookbooks: Fix cell output error 2024-06-24 14:36:03 +03:00
LinasKo 4d12853b95 Sink cookbooks fix: _to_detections unpacks into List[Detections]
* 1 sv.Detections per frame
2024-06-24 14:31:59 +03:00
LinasKo 9333460ee9 Tidy up Sink cookbook text 2024-06-24 13:31:35 +03:00
Piotr Skalski d5cebd4fd2 Merge pull request #1296 from LinasKo/feat/florence-2-support
Add Florence 2 support
2024-06-21 11:40:00 -07:00
LinasKo 61579af25a Make ruff happy 2024-06-21 17:49:43 +03:00
pre-commit-ci[bot] bcd3e1be17 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-06-21 13:59:41 +00:00
LinasKo 9ef697cd10 Florence 2 units tests 2024-06-21 16:59:01 +03:00
LinasKo 19bf417810 Special case for REGION_TO_DESCRIPTION when nothing is detected 2024-06-21 15:38:21 +03:00
pre-commit-ci[bot] 3df33f0f9d fix(pre_commit): 🎨 auto format pre-commit hooks 2024-06-21 11:24:24 +00:00
kirilman b8b31f6f00 detectron2 mask convert to detection 2024-06-21 16:15:34 +05:00
LinasKo fc11f4d7af Masks are now bool 2024-06-21 12:56:14 +03:00
LinasKo ec517124f8 florence_2: Clean up task selector 2024-06-21 12:34:09 +03:00
Onuralp SEZER f00dcede1a Merge pull request #1299 from roboflow/dependabot/pip/ruff-0.4.10
⬆️ Bump ruff from 0.4.9 to 0.4.10
2024-06-21 09:23:46 +03:00
dependabot[bot] 6fb90f6b75 ⬆️ Bump ruff from 0.4.9 to 0.4.10
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.4.9 to 0.4.10.
- [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.4.9...v0.4.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-21 01:01:46 +00:00
LinasKo 23e63501eb fix, from_florence_2: extra dimension for xyxy, REGION_TO_CATEGORY 2024-06-20 21:35:49 +03:00
Onuralp SEZER 0df56b1d66 docs(notebook): code clean up and table of content fixes made for mkdocs version
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-06-20 21:26:56 +03:00
LinasKo ec75a86a83 Fix, from_florence_2: polygon_to_mask expects np.int32 2024-06-20 21:26:54 +03:00
LinasKo 4599548eeb Fix, from_florence_2: polygons must be shaped (-1, 2) 2024-06-20 21:22:15 +03:00
LinasKo bb513f880a Add segmentation and region methods 2024-06-20 21:08:42 +03:00
Onuralp SEZER fbce6e45d4 Merge pull request #1298 from roboflow/fix/readme-broken-links
Readme: fix broken links
2024-06-20 16:23:45 +03:00
LinasKo a00e2d22ba Readme: fix broken links 2024-06-20 16:12:35 +03:00
LinasKo 04c5f18da0 Bugfix: florence 2, handle case with no detections 2024-06-20 15:49:13 +03:00
Onuralp SEZER 8b933f640e Merge pull request #1297 from roboflow/readme/hackfest-banner
Readme: change hackfest banner link to project page
2024-06-20 15:23:12 +03:00
LinasKo f01ca33511 Readme: change hackfest banner link to project page 2024-06-20 15:14:33 +03:00
LinasKo 2f88bb2b8e Add Florence 2 support 2024-06-20 14:50:10 +03:00
Onuralp SEZERandpre-commit-ci[bot] b2b251a424 feat: 🚀 Triangle and Dot annotators outline thickness level and triangle and circle shapes added (#1294)
* feat: 🚀 Triangle_annotator and Dot_annotator outline thickness value added
* feat: 🚀 Triangle_annotator and Dot_annotator outline triangle and circle added
* docs: 📝 outline_thickness py docs added
* refactor: remove else statement for thickness in circle not needed 
* fix(pre_commit): 🎨 auto format pre-commit hooks

---------

Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-06-20 12:49:36 +03:00
Onuralp SEZER eec3615250 refactor: remove else statement for thickness in circle not needed
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-06-20 12:45:55 +03:00
pre-commit-ci[bot] 272a9cb35e fix(pre_commit): 🎨 auto format pre-commit hooks 2024-06-19 18:43:35 +00:00
Dripto Saha 0d0328ef22 Updated the Triangle_annotator and the Dot_annotator in the annotator-core.py file 2024-06-20 00:12:42 +05:30
Dripto Saha d821fca2af Updated the Triangle_annotator and the Dot_annotator in the annotator-core.py 2024-06-19 23:59:27 +05:30
pre-commit-ci[bot] ff9a63a68b fix(pre_commit): 🎨 auto format pre-commit hooks 2024-06-19 18:15:45 +00:00
Dripto Saha a6222a065c Updated the Triangle_annotator and the Dot_annotator in the annotator-core.py 2024-06-19 23:44:30 +05:30
Onuralp SEZER 1489f82e0f docs: 📝 outline_thickness py docs added
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-06-19 20:43:55 +03:00
pre-commit-ci[bot] 12722df65b fix(pre_commit): 🎨 auto format pre-commit hooks 2024-06-19 17:38:49 +00:00
Dripto Saha 900952acae Modified annotators-core.py 2024-06-19 23:08:36 +05:30
pre-commit-ci[bot] 95308126c7 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-06-19 17:28:09 +00:00
Dripto Saha e7de6859e9 Updated annotation-core.py 2024-06-19 22:57:52 +05:30
pre-commit-ci[bot] feeea38e6e fix(pre_commit): 🎨 auto format pre-commit hooks 2024-06-19 14:11:46 +00:00
Dripto Saha 6ea07a1c2a Updated dot-annotator-core.py 2024-06-19 19:11:52 +05:30
Linas Kondrackis fbb770a486 Merge pull request #1227 from Bhavay-2001/OBB
Adding support for Yolo-OBB (Oriented Bounding Boxes) format
2024-06-19 12:10:23 +03:00
Linas Kondrackis 09f663139c Merge pull request #1290 from roboflow/expose_file_script
file utilities ret-type updated and extended function to work with Pathlib
2024-06-19 12:02:11 +03:00
hardikdava 9efd04925c Merge remote-tracking branch 'origin/expose_file_script' into expose_file_script 2024-06-19 09:46:46 +02:00
hardikdava 14ce0799ce revert global import 2024-06-19 09:46:38 +02:00
LinasKo a81f24e08d Explain OBB format in docstring 2024-06-19 01:06:39 +03:00
LinasKo 257e73fb37 Change uncommon np.int0 type to its alias np.intp 2024-06-19 00:36:27 +03:00
LinasKo dcfb122816 Fix xyxyxyxy shape
* Ultralytics return (-1, 4, 2), and that's waht our annotators expect
2024-06-19 00:33:00 +03:00
LinasKo 79ea168fd4 Merge branch 'develop' into OBB 2024-06-18 23:59:36 +03:00
LinasKo d35830ddeb Include tests 2024-06-18 22:19:36 +03:00
LinasKo c5ff58910d Reduce number of copy operations 2024-06-18 21:32:40 +03:00
Linas Kondrackis 15d9b75225 Merge pull request #1287 from roboflow/dependabot/pip/mkdocs-material-9.5.27
⬆️ Bump mkdocs-material from 9.5.26 to 9.5.27
2024-06-18 16:12:04 +03:00
Linas Kondrackis 49318ffe31 Merge pull request #1289 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2024-06-18 16:05:56 +03:00
Linas Kondrackis b52a0297f1 Merge pull request #1291 from roboflow/dependabot/pip/urllib3-2.2.2
⬆️ Bump urllib3 from 2.2.1 to 2.2.2
2024-06-18 15:12:13 +03:00
dependabot[bot] eceebb33e3 ⬆️ Bump urllib3 from 2.2.1 to 2.2.2
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.2.1 to 2.2.2.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.2.1...2.2.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-17 23:02:23 +00:00
pre-commit-ci[bot] fa7821b1c4 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-06-17 18:54:25 +00:00
hardikdava a56a16937f missing rettype added 2024-06-17 20:54:08 +02:00
hardikdava 0c95a6126d converting paths to string 2024-06-17 20:50:40 +02:00
hardikdava c14642accf docstrings updated 2024-06-17 20:47:36 +02:00
hardikdava 8c0e2fb362 exposing some file utilities 2024-06-17 20:44:48 +02:00
pre-commit-ci[bot] e5fb43aea6 chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/PyCQA/bandit: 1.7.8 → 1.7.9](https://github.com/PyCQA/bandit/compare/1.7.8...1.7.9)
- [github.com/astral-sh/ruff-pre-commit: v0.4.8 → v0.4.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.8...v0.4.9)
2024-06-17 17:46:44 +00:00
Onuralp SEZER 32b70c5d37 Merge pull request #1286 from roboflow/dependabot/pip/ruff-0.4.9
⬆️ Bump ruff from 0.4.8 to 0.4.9
2024-06-17 18:07:47 +03:00
dependabot[bot] b9e98a5e20 ⬆️ Bump ruff from 0.4.8 to 0.4.9
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.4.8 to 0.4.9.
- [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.4.8...v0.4.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-17 15:05:59 +00:00
Onuralp SEZER 246b8a720c Merge pull request #1288 from roboflow/feat/disalllow-numpy-v2
Numpy upper limit: `<2.0.0`
2024-06-17 18:03:07 +03:00
LinasKo a43970fb65 Pin numpy to <2.0.0 2024-06-17 16:46:47 +03:00
Dripto Saha ff5709def2 Updated annotators-core.py 2024-06-17 16:46:41 +05:30
Dripto Saha 2805f41675 Updated annotators-core.py 2024-06-17 16:08:12 +05:30
dependabot[bot] 4eaa897f2c ⬆️ Bump mkdocs-material from 9.5.26 to 9.5.27
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.26 to 9.5.27.
- [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.26...9.5.27)

---
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-06-17 00:44:28 +00:00
Piotr Skalski f96e7766f3 Merge pull request #1232 from David-rn/feat/keypoints-from-mediapipe
Feat/keypoints from mediapipe
2024-06-13 14:09:56 -04:00
pre-commit-ci[bot] 07cb0d8ccf fix(pre_commit): 🎨 auto format pre-commit hooks 2024-06-13 17:50:45 +00:00
SkalskiP 3725beb2db docs improvements 2024-06-13 13:50:27 -04:00
Bhavay Malhotra d5f8b94d65 Merge branch 'OBB' of https://github.com/Bhavay-2001/roboflow-supervision into OBB 2024-06-13 20:22:36 +05:30
Bhavay Malhotra 6f7f728044 Changed the position of a line 2024-06-13 20:22:26 +05:30
LinasKo aad3c4fce4 Defensive default font loading 2024-06-12 15:48:53 +03:00
LinasKo 52724ca673 RichLabelAnnotator: Minor docstring styling 2024-06-12 15:38:52 +03:00
LinasKo e3fc238c70 Revert RichLabelAnnotator font size 2024-06-12 15:33:04 +03:00
LinasKo cfe29cd05f Bugfix: MaskAnnotator and HaloAnnotator now work in-place 2024-06-12 15:09:50 +03:00
LinasKo 0f76f731ee bugfix: ColorAnnotator is now in-place 2024-06-12 14:58:48 +03:00
LinasKo c239291541 Bugfix: PIL exposes font size param, but complains if set 2024-06-12 11:08:30 +03:00
LinasKo a446ffd021 Bugfix: decorators for annotators now work in-place 2024-06-12 10:59:25 +03:00
LinasKo c2084c4d0f Add missing base classes for annotators 2024-06-11 17:34:51 +03:00
LinasKo e3cb417129 RichLabelAnnotator fix: convert back to numpy 2024-06-11 16:15:23 +03:00
LinasKo 27de665940 Docs: Remove INFO about absolute links when building 2024-06-11 15:19:52 +03:00
LinasKo 93b21a8d2d minor improvement to code clarity 2024-06-11 14:02:52 +03:00
LinasKo a4bdfc2062 minor tidy-up for mediapipe 2024-06-11 13:51:23 +03:00
LinasKo fd1a07cb6b Adjust long lines 2024-06-11 13:14:38 +03:00
LinasKo edfb814fc4 Merge branch 'develop' into feat/keypoints-from-mediapipe 2024-06-11 13:06:40 +03:00
Onuralp SEZER 3d5437b960 Merge pull request #1273 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2024-06-10 20:51:46 +03:00
pre-commit-ci[bot] 68c2504d61 chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.7 → v0.4.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.7...v0.4.8)
2024-06-10 17:50:26 +00:00
Onuralp SEZER 6984d72f4a Merge pull request #1272 from roboflow/dependabot/pip/notebook-7.2.1
⬆️ Bump notebook from 7.2.0 to 7.2.1
2024-06-10 09:09:23 +03:00
dependabot[bot] 6f5b477ac0 ⬆️ Bump notebook from 7.2.0 to 7.2.1
Bumps [notebook](https://github.com/jupyter/notebook) from 7.2.0 to 7.2.1.
- [Release notes](https://github.com/jupyter/notebook/releases)
- [Changelog](https://github.com/jupyter/notebook/blob/@jupyter-notebook/tree@7.2.1/CHANGELOG.md)
- [Commits](https://github.com/jupyter/notebook/compare/@jupyter-notebook/tree@7.2.0...@jupyter-notebook/tree@7.2.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-10 01:16:42 +00:00
Onuralp SEZER b19511968d Merge pull request #1271 from mqasim41/develop
Fix Grammatical Mistake In CONTRIBUTING.md
2024-06-08 19:45:24 +03:00
mqasim41 165c77d830 Fix Grammatical Mistake In CONTRIBUTING.md 2024-06-07 18:03:12 +05:00
Onuralp SEZER 558581943b Merge pull request #1268 from roboflow/dependabot/pip/mkdocs-material-9.5.26
⬆️ Bump mkdocs-material from 9.5.25 to 9.5.26
2024-06-07 11:43:01 +03:00
Onuralp SEZER 8acbc422c9 Merge pull request #1267 from roboflow/dependabot/pip/tornado-6.4.1
⬆️ Bump tornado from 6.4 to 6.4.1
2024-06-07 11:37:20 +03:00
dependabot[bot] 9995771604 ⬆️ Bump mkdocs-material from 9.5.25 to 9.5.26
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.25 to 9.5.26.
- [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.25...9.5.26)

---
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-06-07 01:07:22 +00:00
dependabot[bot] bcc1bc1ff2 ⬆️ Bump tornado from 6.4 to 6.4.1
Bumps [tornado](https://github.com/tornadoweb/tornado) from 6.4 to 6.4.1.
- [Changelog](https://github.com/tornadoweb/tornado/blob/master/docs/releases.rst)
- [Commits](https://github.com/tornadoweb/tornado/compare/v6.4.0...v6.4.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-06 23:39:07 +00:00
pre-commit-ci[bot] cce69d893e fix(pre_commit): 🎨 auto format pre-commit hooks 2024-06-06 16:57:06 +00:00
Bhavay Malhotra 3e2fe835db Merge branch 'OBB' of https://github.com/Bhavay-2001/roboflow-supervision into OBB 2024-06-06 22:24:29 +05:30
Bhavay Malhotra 47f1c44e20 Updated criteria for xyxyxyxy 2024-06-06 22:22:54 +05:30
pre-commit-ci[bot] 420543534d fix(pre_commit): 🎨 auto format pre-commit hooks 2024-06-06 16:45:31 +00:00
Bhavay Malhotra 1ea9345845 Merge branch 'OBB' of https://github.com/Bhavay-2001/roboflow-supervision into OBB 2024-06-06 22:14:11 +05:30
Bhavay Malhotra 6ca09ccbfc Added default value for is_obb parameter 2024-06-06 22:14:02 +05:30
Onuralp SEZER 7f78f93407 Merge pull request #1263 from roboflow/dependabot/pip/ruff-0.4.8
⬆️ Bump ruff from 0.4.7 to 0.4.8
2024-06-06 11:03:11 +03:00
Onuralp SEZER 05ceca70ef Merge pull request #1262 from roboflow/dependabot/pip/tox-4.15.1
⬆️ Bump tox from 4.15.0 to 4.15.1
2024-06-06 11:02:56 +03:00
Piotr Skalski e8f8c38f39 Merge pull request #1264 from roboflow/main
Merge pull request #1258 from roboflow/develop
2024-06-06 08:13:34 +02:00
dependabot[bot] 0739e24b25 ⬆️ Bump ruff from 0.4.7 to 0.4.8
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.4.7 to 0.4.8.
- [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.4.7...v0.4.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-06 01:33:50 +00:00
dependabot[bot] 560760993a ⬆️ Bump tox from 4.15.0 to 4.15.1
Bumps [tox](https://github.com/tox-dev/tox) from 4.15.0 to 4.15.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.15.0...4.15.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-06-06 01:31:57 +00:00
pre-commit-ci[bot] 859f0258bc fix(pre_commit): 🎨 auto format pre-commit hooks 2024-06-05 17:34:09 +00:00
Bhavay Malhotra 2f644c52e3 Merge branch 'OBB' of https://github.com/Bhavay-2001/roboflow-supervision into OBB 2024-06-05 23:03:33 +05:30
Bhavay Malhotra a82c37c7fa Updated yolo.py for OBB support 2024-06-05 23:03:24 +05:30
pre-commit-ci[bot] fc2710c9c9 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-06-05 17:31:11 +00:00
Bhavay Malhotra 29161a485e Merge branch 'OBB' of https://github.com/Bhavay-2001/roboflow-supervision into OBB 2024-06-05 23:00:45 +05:30
Bhavay Malhotra 523d218dd4 Made few changes to add OBB support 2024-06-05 22:59:04 +05:30
pre-commit-ci[bot] 46bd267079 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-06-05 12:17:11 +00:00
Bhavay Malhotra d025791805 Merge branch 'OBB' of https://github.com/Bhavay-2001/roboflow-supervision into OBB 2024-06-05 17:46:12 +05:30
Bhavay Malhotra 7e4fc8a485 Made few changes to add OBB support 2024-06-05 17:43:58 +05:30
pre-commit-ci[bot] 26d70219bf fix(pre_commit): 🎨 auto format pre-commit hooks 2024-06-02 15:34:54 +00:00
David Redo daf13442f3 Updated from_mediapipe method to support two results types 2024-06-02 17:34:36 +02:00
Onuralp SEZER a6b985ff4b refactor: 📦 supervision updated to 0.21.0
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-05-28 22:53:07 +03:00
David Redo 4fea0031ba Updated docstring comment due to line too large 2024-05-26 21:45:59 +02:00
David Redo 490a341d5d add method in KeyPoints class 2024-05-26 20:14:57 +02:00
David Redo 2c3fa27929 add GHUM Skeleton enum 2024-05-26 20:14:09 +02:00
pre-commit-ci[bot] 51c7ebdf60 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-05-24 14:56:03 +00:00
Bhavay Malhotra 2653f5a090 Adding support for Yolo-OBB format 2024-05-24 20:23:17 +05:30
Onuralp SEZER d309d74f37 docs(notebooks): 📝 json and csv sink cookbooks are added
Signed-off-by: Onuralp SEZER <thunderbirdtr@gmail.com>
2024-03-05 05:27:58 +03:00
24 changed files with 3369 additions and 1078 deletions
+2 -2
View File
@@ -28,7 +28,7 @@ repos:
- repo: https://github.com/PyCQA/bandit
rev: '1.7.8'
rev: '1.7.9'
hooks:
- id: bandit
args: ["-c", "pyproject.toml"]
@@ -45,7 +45,7 @@ repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.7
rev: v0.5.0
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
+1 -1
View File
@@ -64,7 +64,7 @@ To run the pre-commit tool, follow these steps:
3. Run the command `pre-commit run --all-files`. This will execute the pre-commit hooks configured for this project against the modified files. If any issues are found, the pre-commit tool will provide feedback on how to resolve them. Make the necessary changes and re-run the pre-commit command until all issues are resolved.
4. You can also install pre-commit as a git hook by execute `pre-commit install`. Every time you made `git commit` pre-commit run automatically for you.
4. You can also install pre-commit as a git hook by executing `pre-commit install`. Every time you do a `git commit` pre-commit run automatically for you.
### Docstrings
+3 -3
View File
@@ -28,7 +28,7 @@
**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! 🤝
[![supervision-hackfest](https://github.com/roboflow/supervision/assets/26109316/c05cc954-b9a6-4ed5-9a52-d0b4b619ff65)](https://github.com/orgs/roboflow/projects/10)
[![supervision-hackfest](https://github.com/roboflow/supervision/assets/26109316/c05cc954-b9a6-4ed5-9a52-d0b4b619ff65)](https://github.com/orgs/roboflow/projects)
## 💻 install
@@ -86,7 +86,7 @@ len(detections)
### annotators
Supervision offers a wide range of highly customizable [annotators](https://supervision.roboflow.com/latest/annotators/), allowing you to compose the perfect visualization for your use case.
Supervision offers a wide range of highly customizable [annotators](https://supervision.roboflow.com/latest/detection/annotators/), allowing you to compose the perfect visualization for your use case.
```python
import cv2
@@ -106,7 +106,7 @@ https://github.com/roboflow/supervision/assets/26109316/691e219c-0565-4403-9218-
### datasets
Supervision provides a set of [utils](https://supervision.roboflow.com/latest/datasets/) that allow you to load, split, merge, and save datasets in one of the supported formats.
Supervision provides a set of [utils](https://supervision.roboflow.com/latest/datasets/core/) that allow you to load, split, merge, and save datasets in one of the supported formats.
```python
import supervision as sv
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3
View File
@@ -19,6 +19,9 @@
<p class="card repo-card" data-url="/develop/notebooks/object-tracking" data-name="Object Tracking" data-labels="TRACKING, ANNOTATOR" data-version="v0.18.0" data-author="nickherrig"></p>
<p class="card repo-card" data-url="/develop/notebooks/occupancy_analytics" data-name="Analyzing Zone Occupancy" data-labels="ANNOTATOR,DETECTION,ZONES" data-version="v0.19.0" data-author="stellasphere"></p>
<p class="card repo-card" data-url="/develop/notebooks/evaluating-alignment-of-text-to-image-diffusion-models" data-name="Evaluating Alignment of Text-to-image Diffusion Models" data-labels="ANNOTATORS,YOLO WORLD" data-version="v0.19.0rc5" data-author="iamhatesz"></p>
<p class="card repo-card" data-url="/develop/notebooks/serialise-detections-to-csv" data-name="Serialise Detections to a CSV File" data-labels="DETECTIONS,CSV SINK,INFERENCE" data-version="v0.21.0" data-author="onuralpszr"></p>
<p class="card repo-card" data-url="/develop/notebooks/serialise-detections-to-json" data-name="Serialise Detections to a JSON File" data-labels="DETECTIONS,JSON SINK,INFERENCE" data-version="v0.21.0" data-author="onuralpszr"></p>
</div>
</div>
</section>
+9
View File
@@ -178,3 +178,12 @@ extra_javascript:
- "javascripts/init_kapa_widget.js"
- "javascripts/cookbooks-card.js"
- "https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.8/purify.min.js"
# Messages shown during document build
# Reference: https://www.mkdocs.org/user-guide/configuration/#validation
# Values: [warn, info, ignore]
validation:
nav:
absolute_links: ignore
links:
absolute_links: ignore
Generated
+944 -889
View File
File diff suppressed because it is too large Load Diff
+6 -2
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "supervision"
version = "0.21.0"
version = "0.22.0rc1"
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>"]
@@ -51,7 +51,7 @@ desktop = ["opencv-python"]
assets = ["requests","tqdm"]
[tool.poetry.group.dev.dependencies]
twine = ">=4.0.2,<6.0.0"
twine = "^5.1.1"
pytest = ">=7.2.2,<9.0.0"
wheel = ">=0.40,<0.44"
build = ">=0.10,<1.3"
@@ -63,6 +63,10 @@ notebook = ">=6.5.3,<8.0.0"
ipywidgets = "^8.1.1"
jupytext = "^1.16.1"
nbconvert = "^7.14.2"
docutils = [
{ version = "^0.20.1", python = "<3.9" },
{ version = "^0.21.1", python = ">=3.9" }
]
[tool.poetry.group.docs.dependencies]
+84 -60
View File
@@ -3,7 +3,7 @@ from typing import List, Optional, Tuple, Union
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
from PIL import ImageDraw, ImageFont
from supervision.annotators.base import BaseAnnotator, ImageType
from supervision.annotators.utils import (
@@ -18,7 +18,10 @@ from supervision.detection.utils import clip_boxes, mask_to_polygons
from supervision.draw.color import Color, ColorPalette
from supervision.draw.utils import draw_polygon
from supervision.geometry.core import Position
from supervision.utils.conversion import convert_for_annotation_method
from supervision.utils.conversion import (
ensure_cv2_image_for_annotation,
ensure_pil_image_for_annotation,
)
from supervision.utils.image import crop_image, overlay_image, scale_image
@@ -45,7 +48,7 @@ class BoundingBoxAnnotator(BaseAnnotator):
self.thickness: int = thickness
self.color_lookup: ColorLookup = color_lookup
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(
self,
scene: ImageType,
@@ -126,7 +129,7 @@ class OrientedBoxAnnotator(BaseAnnotator):
self.thickness: int = thickness
self.color_lookup: ColorLookup = color_lookup
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(
self,
scene: ImageType,
@@ -172,7 +175,7 @@ class OrientedBoxAnnotator(BaseAnnotator):
return scene
for detection_idx in range(len(detections)):
bbox = np.int0(detections.data.get(ORIENTED_BOX_COORDINATES)[detection_idx])
bbox = np.intp(detections.data.get(ORIENTED_BOX_COORDINATES)[detection_idx])
color = resolve_color(
color=self.color,
detections=detections,
@@ -214,7 +217,7 @@ class MaskAnnotator(BaseAnnotator):
self.opacity = opacity
self.color_lookup: ColorLookup = color_lookup
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(
self,
scene: ImageType,
@@ -270,8 +273,10 @@ class MaskAnnotator(BaseAnnotator):
mask = detections.mask[detection_idx]
colored_mask[mask] = color.as_bgr()
scene = cv2.addWeighted(colored_mask, self.opacity, scene, 1 - self.opacity, 0)
return scene.astype(np.uint8)
cv2.addWeighted(
colored_mask, self.opacity, scene, 1 - self.opacity, 0, dst=scene
)
return scene
class PolygonAnnotator(BaseAnnotator):
@@ -301,7 +306,7 @@ class PolygonAnnotator(BaseAnnotator):
self.thickness: int = thickness
self.color_lookup: ColorLookup = color_lookup
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(
self,
scene: ImageType,
@@ -387,7 +392,7 @@ class ColorAnnotator(BaseAnnotator):
self.color_lookup: ColorLookup = color_lookup
self.opacity = opacity
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(
self,
scene: ImageType,
@@ -426,7 +431,7 @@ class ColorAnnotator(BaseAnnotator):
![box-mask-annotator-example](https://media.roboflow.com/
supervision-annotator-examples/box-mask-annotator-example-purple.png)
"""
mask_image = scene.copy()
scene_with_boxes = scene.copy()
for detection_idx in range(len(detections)):
x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
color = resolve_color(
@@ -438,14 +443,15 @@ class ColorAnnotator(BaseAnnotator):
else custom_color_lookup,
)
cv2.rectangle(
img=scene,
img=scene_with_boxes,
pt1=(x1, y1),
pt2=(x2, y2),
color=color.as_bgr(),
thickness=-1,
)
scene = cv2.addWeighted(
scene, self.opacity, mask_image, 1 - self.opacity, gamma=0
cv2.addWeighted(
scene_with_boxes, self.opacity, scene, 1 - self.opacity, gamma=0, dst=scene
)
return scene
@@ -481,7 +487,7 @@ class HaloAnnotator(BaseAnnotator):
self.color_lookup: ColorLookup = color_lookup
self.kernel_size: int = kernel_size
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(
self,
scene: ImageType,
@@ -546,7 +552,8 @@ class HaloAnnotator(BaseAnnotator):
gray = cv2.cvtColor(colored_mask, cv2.COLOR_BGR2GRAY)
alpha = self.opacity * gray / gray.max()
alpha_mask = alpha[:, :, np.newaxis]
scene = np.uint8(scene * (1 - alpha_mask) + colored_mask * self.opacity)
blended_scene = np.uint8(scene * (1 - alpha_mask) + colored_mask * self.opacity)
np.copyto(scene, blended_scene)
return scene
@@ -579,7 +586,7 @@ class EllipseAnnotator(BaseAnnotator):
self.end_angle: int = end_angle
self.color_lookup: ColorLookup = color_lookup
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(
self,
scene: ImageType,
@@ -670,7 +677,7 @@ class BoxCornerAnnotator(BaseAnnotator):
self.corner_length: int = corner_length
self.color_lookup: ColorLookup = color_lookup
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(
self,
scene: ImageType,
@@ -758,7 +765,7 @@ class CircleAnnotator(BaseAnnotator):
self.thickness: int = thickness
self.color_lookup: ColorLookup = color_lookup
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(
self,
scene: ImageType,
@@ -833,6 +840,7 @@ class DotAnnotator(BaseAnnotator):
radius: int = 4,
position: Position = Position.CENTER,
color_lookup: ColorLookup = ColorLookup.CLASS,
outline_thickness: int = 0,
):
"""
Args:
@@ -842,13 +850,15 @@ class DotAnnotator(BaseAnnotator):
position (Position): The anchor position for placing the dot.
color_lookup (ColorLookup): Strategy for mapping colors to annotations.
Options are `INDEX`, `CLASS`, `TRACK`.
outline_thickness (int): Thickness of the outline of the dot.
"""
self.color: Union[Color, ColorPalette] = color
self.radius: int = radius
self.position: Position = position
self.color_lookup: ColorLookup = color_lookup
self.outline_thickness = outline_thickness
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(
self,
scene: ImageType,
@@ -898,11 +908,16 @@ class DotAnnotator(BaseAnnotator):
else custom_color_lookup,
)
center = (int(xy[detection_idx, 0]), int(xy[detection_idx, 1]))
cv2.circle(scene, center, self.radius, color.as_bgr(), -1)
if self.outline_thickness:
cv2.circle(
scene, center, self.radius, (0, 0, 0), self.outline_thickness
)
return scene
class LabelAnnotator:
class LabelAnnotator(BaseAnnotator):
"""
A class for annotating labels on an image using provided detections.
"""
@@ -942,12 +957,12 @@ class LabelAnnotator:
self.text_anchor: Position = text_position
self.color_lookup: ColorLookup = color_lookup
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(
self,
scene: ImageType,
detections: Detections,
labels: List[str] = None,
labels: Optional[List[str]] = None,
custom_color_lookup: Optional[np.ndarray] = None,
) -> ImageType:
"""
@@ -958,7 +973,7 @@ class LabelAnnotator:
`ImageType` is a flexible type, accepting either `numpy.ndarray`
or `PIL.Image.Image`.
detections (Detections): Object detections to annotate.
labels (List[str]): Optional. Custom labels for each detection.
labels (Optional[List[str]]): Custom labels for each detection.
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
Allows to override the default color mapping strategy.
@@ -968,7 +983,7 @@ class LabelAnnotator:
Example:
```python
import supervision as sv
import supervision as sv
image = ...
detections = sv.Detections(...)
@@ -996,12 +1011,9 @@ class LabelAnnotator:
).astype(int)
if labels is not None and len(labels) != len(detections):
raise ValueError(
f"The number of labels provided ({len(labels)}) does not match the "
f"number of detections ({len(detections)}). Each detection should have "
f"a corresponding label. This discrepancy can occur if the labels and "
f"detections are not aligned or if an incorrect number of labels has "
f"been provided. Please ensure that the labels array has the same "
f"length as the Detections object."
f"The number of labels ({len(labels)}) does not match the "
f"number of detections ({len(detections)}). Each detection "
f"should have exactly 1 label."
)
for detection_idx, center_coordinates in enumerate(anchors_coordinates):
@@ -1103,7 +1115,7 @@ class LabelAnnotator:
return scene
class RichLabelAnnotator:
class RichLabelAnnotator(BaseAnnotator):
"""
A class for annotating labels on an image using provided detections,
with support for Unicode characters by using a custom font.
@@ -1113,7 +1125,7 @@ class RichLabelAnnotator:
self,
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
text_color: Color = Color.WHITE,
font_path: str = None,
font_path: Optional[str] = None,
font_size: int = 10,
text_padding: int = 10,
text_position: Position = Position.TOP_LEFT,
@@ -1125,8 +1137,8 @@ class RichLabelAnnotator:
color (Union[Color, ColorPalette]): The color or color palette to use for
annotating the text background.
text_color (Color): The color to use for the text.
font_path (str): Path to the font file (e.g., ".ttf" or ".otf") to use for
rendering text. If `None`, the default PIL font will be used.
font_path (Optional[str]): Path to the font file (e.g., ".ttf" or ".otf")
to use for rendering text. If `None`, the default PIL font will be used.
font_size (int): Font size for the text.
text_padding (int): Padding around the text within its background box.
text_position (Position): Position of the text relative to the detection.
@@ -1147,15 +1159,16 @@ class RichLabelAnnotator:
self.font = ImageFont.truetype(font_path, font_size)
except OSError:
print(f"Font path '{font_path}' not found. Using PIL's default font.")
self.font = ImageFont.load_default(size=font_size)
self.font = self._load_default_font(font_size)
else:
self.font = ImageFont.load_default(size=font_size)
self.font = self._load_default_font(font_size)
@ensure_pil_image_for_annotation
def annotate(
self,
scene: ImageType,
detections: Detections,
labels: List[str] = None,
labels: Optional[List[str]] = None,
custom_color_lookup: Optional[np.ndarray] = None,
) -> ImageType:
"""
@@ -1167,7 +1180,7 @@ class RichLabelAnnotator:
`ImageType` is a flexible type, accepting either `numpy.ndarray`
or `PIL.Image.Image`.
detections (Detections): Object detections to annotate.
labels (List[str]): Optional. Custom labels for each detection.
labels (Optional[List[str]]): Custom labels for each detection.
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
Allows to override the default color mapping strategy.
@@ -1197,8 +1210,6 @@ class RichLabelAnnotator:
```
"""
if isinstance(scene, np.ndarray):
scene = Image.fromarray(cv2.cvtColor(scene, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(scene)
anchors_coordinates = detections.get_anchors_coordinates(
anchor=self.text_anchor
@@ -1207,10 +1218,7 @@ class RichLabelAnnotator:
raise ValueError(
f"The number of labels provided ({len(labels)}) does not match the "
f"number of detections ({len(detections)}). Each detection should have "
f"a corresponding label. This discrepancy can occur if the labels and "
f"detections are not aligned or if an incorrect number of labels has "
f"been provided. Please ensure that the labels array has the same "
f"length as the Detections object."
f"a corresponding label."
)
for detection_idx, center_coordinates in enumerate(anchors_coordinates):
color = resolve_color(
@@ -1258,9 +1266,21 @@ class RichLabelAnnotator:
font=self.font,
fill=self.text_color.as_rgb(),
)
return scene
@staticmethod
def _load_default_font(size):
"""
PIL either loads a font that accepts a size (e.g. on my machine)
or raises an error saying `load_default` does not accept arguments
(e.g. in Colab).
"""
try:
font = ImageFont.load_default(size)
except TypeError:
font = ImageFont.load_default()
return font
class BlurAnnotator(BaseAnnotator):
"""
@@ -1274,7 +1294,7 @@ class BlurAnnotator(BaseAnnotator):
"""
self.kernel_size: int = kernel_size
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(
self,
scene: ImageType,
@@ -1323,7 +1343,7 @@ class BlurAnnotator(BaseAnnotator):
return scene
class TraceAnnotator:
class TraceAnnotator(BaseAnnotator):
"""
A class for drawing trace paths on an image based on detection coordinates.
@@ -1359,7 +1379,7 @@ class TraceAnnotator:
self.thickness = thickness
self.color_lookup: ColorLookup = color_lookup
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(
self,
scene: ImageType,
@@ -1432,7 +1452,7 @@ class TraceAnnotator:
return scene
class HeatMapAnnotator:
class HeatMapAnnotator(BaseAnnotator):
"""
A class for drawing heatmaps on an image based on provided detections.
Heat accumulates over time and is drawn as a semi-transparent overlay
@@ -1466,7 +1486,7 @@ class HeatMapAnnotator:
self.top_hue = top_hue
self.low_hue = low_hue
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(self, scene: ImageType, detections: Detections) -> ImageType:
"""
Annotates the scene with a heatmap based on the provided detections.
@@ -1542,7 +1562,7 @@ class PixelateAnnotator(BaseAnnotator):
"""
self.pixel_size: int = pixel_size
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(
self,
scene: ImageType,
@@ -1613,6 +1633,7 @@ class TriangleAnnotator(BaseAnnotator):
height: int = 10,
position: Position = Position.TOP_CENTER,
color_lookup: ColorLookup = ColorLookup.CLASS,
outline_thickness: int = 0,
):
"""
Args:
@@ -1623,14 +1644,16 @@ class TriangleAnnotator(BaseAnnotator):
position (Position): The anchor position for placing the triangle.
color_lookup (ColorLookup): Strategy for mapping colors to annotations.
Options are `INDEX`, `CLASS`, `TRACK`.
outline_thickness (int): Thickness of the outline of the triangle.
"""
self.color: Union[Color, ColorPalette] = color
self.base: int = base
self.height: int = height
self.position: Position = position
self.color_lookup: ColorLookup = color_lookup
self.outline_thickness: int = outline_thickness
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(
self,
scene: ImageType,
@@ -1690,7 +1713,10 @@ class TriangleAnnotator(BaseAnnotator):
)
cv2.fillPoly(scene, [vertices], color.as_bgr())
if self.outline_thickness:
cv2.polylines(
scene, [vertices], True, (0, 0, 0), thickness=self.outline_thickness
)
return scene
@@ -1726,7 +1752,7 @@ class RoundBoxAnnotator(BaseAnnotator):
raise ValueError("roundness attribute must be float between (0, 1.0]")
self.roundness: float = roundness
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(
self,
scene: ImageType,
@@ -1863,7 +1889,7 @@ class PercentageBarAnnotator(BaseAnnotator):
if border_thickness is None:
self.border_thickness = int(0.15 * self.height)
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(
self,
scene: ImageType,
@@ -2036,7 +2062,7 @@ class CropAnnotator(BaseAnnotator):
self.border_thickness: int = border_thickness
self.border_color_lookup: ColorLookup = border_color_lookup
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(
self,
scene: ImageType,
@@ -2088,9 +2114,7 @@ class CropAnnotator(BaseAnnotator):
(x1, y1), (x2, y2) = self.calculate_crop_coordinates(
anchor=anchor, crop_wh=crop_wh, position=self.position
)
scene = overlay_image(
scene=scene, inserted_image=resized_crop, anchor=(x1, y1)
)
scene = overlay_image(image=scene, overlay=resized_crop, anchor=(x1, y1))
color = resolve_color(
color=self.border_color,
detections=detections,
+5
View File
@@ -266,6 +266,7 @@ class DetectionDataset(BaseDataset):
annotations_directory_path: str,
data_yaml_path: str,
force_masks: bool = False,
is_obb: bool = False,
) -> DetectionDataset:
"""
Creates a Dataset instance from YOLO formatted data.
@@ -280,6 +281,9 @@ class DetectionDataset(BaseDataset):
force_masks (bool, optional): If True, forces
masks to be loaded for all annotations,
regardless of whether they are present.
is_obb (bool, optional): If True, loads the annotations in OBB format.
OBB annotations are defined as `[class_id, x, y, x, y, x, y, x, y]`,
where pairs of [x, y] are box corners.
Returns:
DetectionDataset: A DetectionDataset instance
@@ -312,6 +316,7 @@ class DetectionDataset(BaseDataset):
annotations_directory_path=annotations_directory_path,
data_yaml_path=data_yaml_path,
force_masks=force_masks,
is_obb=is_obb,
)
return DetectionDataset(classes=classes, images=images, annotations=annotations)
+25 -6
View File
@@ -5,6 +5,7 @@ from typing import Dict, List, Optional, Tuple
import cv2
import numpy as np
from supervision.config import ORIENTED_BOX_COORDINATES
from supervision.dataset.utils import approximate_mask_with_polygons
from supervision.detection.core import Detections
from supervision.detection.utils import polygon_to_mask, polygon_to_xyxy
@@ -70,12 +71,15 @@ def _image_name_to_annotation_name(image_name: str) -> str:
def yolo_annotations_to_detections(
lines: List[str], resolution_wh: Tuple[int, int], with_masks: bool
lines: List[str],
resolution_wh: Tuple[int, int],
with_masks: bool,
is_obb: bool = False,
) -> Detections:
if len(lines) == 0:
return Detections.empty()
class_id, relative_xyxy, relative_polygon = [], [], []
class_id, relative_xyxy, relative_polygon, relative_xyxyxyxy = [], [], [], []
w, h = resolution_wh
for line in lines:
values = line.split()
@@ -88,21 +92,30 @@ def yolo_annotations_to_detections(
elif len(values) > 5:
polygon = _parse_polygon(values=values[1:])
relative_xyxy.append(polygon_to_xyxy(polygon=polygon))
if is_obb:
relative_xyxyxyxy.append(np.array(values[1:]))
if with_masks:
relative_polygon.append(polygon)
class_id = np.array(class_id, dtype=int)
relative_xyxy = np.array(relative_xyxy, dtype=np.float32)
xyxy = relative_xyxy * np.array([w, h, w, h], dtype=np.float32)
data = {}
if is_obb:
relative_xyxyxyxy = np.array(relative_xyxyxyxy, dtype=np.float32)
xyxyxyxy = relative_xyxyxyxy.reshape(-1, 4, 2)
xyxyxyxy *= np.array([w, h], dtype=np.float32)
data[ORIENTED_BOX_COORDINATES] = xyxyxyxy
if not with_masks:
return Detections(class_id=class_id, xyxy=xyxy)
return Detections(class_id=class_id, xyxy=xyxy, data=data)
polygons = [
(polygon * np.array(resolution_wh)).astype(int) for polygon in relative_polygon
]
mask = _polygons_to_masks(polygons=polygons, resolution_wh=resolution_wh)
return Detections(class_id=class_id, xyxy=xyxy, mask=mask)
return Detections(class_id=class_id, xyxy=xyxy, data=data, mask=mask)
def load_yolo_annotations(
@@ -110,6 +123,7 @@ def load_yolo_annotations(
annotations_directory_path: str,
data_yaml_path: str,
force_masks: bool = False,
is_obb: bool = False,
) -> Tuple[List[str], Dict[str, np.ndarray], Dict[str, Detections]]:
"""
Loads YOLO annotations and returns class names, images,
@@ -123,6 +137,9 @@ def load_yolo_annotations(
YAML file containing class information.
force_masks (bool, optional): If True, forces masks to be loaded
for all annotations, regardless of whether they are present.
is_obb (bool, optional): If True, loads the annotations in OBB format.
OBB annotations are defined as `[class_id, x, y, x, y, x, y, x, y]`,
where pairs of [x, y] are box corners.
Returns:
Tuple[List[str], Dict[str, np.ndarray], Dict[str, Detections]]:
@@ -156,9 +173,11 @@ def load_yolo_annotations(
with_masks = _with_mask(lines=lines)
with_masks = force_masks if force_masks else with_masks
annotation = yolo_annotations_to_detections(
lines=lines, resolution_wh=resolution_wh, with_masks=with_masks
lines=lines,
resolution_wh=resolution_wh,
with_masks=with_masks,
is_obb=is_obb,
)
images[image_path] = image
annotations[image_path] = annotation
return classes, images, annotations
+2 -2
View File
@@ -5,7 +5,7 @@ import cv2
from supervision.annotators.base import ImageType
from supervision.detection.core import Detections
from supervision.draw.color import Color, ColorPalette
from supervision.utils.conversion import convert_for_annotation_method
from supervision.utils.conversion import ensure_cv2_image_for_annotation
from supervision.utils.internal import deprecated
@@ -46,7 +46,7 @@ class BoxAnnotator:
"`BoxAnnotator` is deprecated and will be removed in "
"`supervision-0.22.0`. Use `BoundingBoxAnnotator` and `LabelAnnotator` instead"
)
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(
self,
scene: ImageType,
+28 -3
View File
@@ -7,7 +7,12 @@ from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
import numpy as np
from supervision.config import CLASS_NAME_DATA_FIELD, ORIENTED_BOX_COORDINATES
from supervision.detection.lmm import LMM, from_paligemma, validate_lmm_and_kwargs
from supervision.detection.lmm import (
LMM,
from_florence_2,
from_paligemma,
validate_lmm_parameters,
)
from supervision.detection.overlap_filter import (
box_non_max_merge,
box_non_max_suppression,
@@ -540,6 +545,9 @@ class Detections:
return cls(
xyxy=detectron2_results["instances"].pred_boxes.tensor.cpu().numpy(),
confidence=detectron2_results["instances"].scores.cpu().numpy(),
mask=detectron2_results["instances"].pred_masks.cpu().numpy()
if hasattr(detectron2_results["instances"], "pred_masks")
else None,
class_id=detectron2_results["instances"]
.pred_classes.cpu()
.numpy()
@@ -811,7 +819,9 @@ class Detections:
)
@classmethod
def from_lmm(cls, lmm: Union[LMM, str], result: str, **kwargs) -> Detections:
def from_lmm(
cls, lmm: Union[LMM, str], result: Union[str, dict], **kwargs
) -> Detections:
"""
Creates a Detections object from the given result string based on the specified
Large Multimodal Model (LMM).
@@ -847,13 +857,28 @@ class Detections:
# array([0])
```
"""
lmm = validate_lmm_and_kwargs(lmm, kwargs)
lmm = validate_lmm_parameters(lmm, result, kwargs)
if lmm == LMM.PALIGEMMA:
assert isinstance(result, str)
xyxy, class_id, class_name = from_paligemma(result, **kwargs)
data = {CLASS_NAME_DATA_FIELD: class_name}
return cls(xyxy=xyxy, class_id=class_id, data=data)
if lmm == LMM.FLORENCE_2:
assert isinstance(result, dict)
xyxy, labels, mask, xyxyxyxy = from_florence_2(result, **kwargs)
if len(xyxy) == 0:
return cls.empty()
data = {}
if labels is not None:
data[CLASS_NAME_DATA_FIELD] = labels
if xyxyxyxy is not None:
data[ORIENTED_BOX_COORDINATES] = xyxyxyxy
return cls(xyxy=xyxy, mask=mask, data=data)
raise ValueError(f"Unsupported LMM: {lmm}")
@classmethod
+126 -3
View File
@@ -4,17 +4,43 @@ from typing import Any, Dict, List, Optional, Tuple, Union
import numpy as np
from supervision.detection.utils import polygon_to_mask, polygon_to_xyxy
class LMM(Enum):
PALIGEMMA = "paligemma"
FLORENCE_2 = "florence_2"
REQUIRED_ARGUMENTS: Dict[LMM, List[str]] = {LMM.PALIGEMMA: ["resolution_wh"]}
RESULT_TYPES: Dict[LMM, type] = {LMM.PALIGEMMA: str, LMM.FLORENCE_2: dict}
ALLOWED_ARGUMENTS: Dict[LMM, List[str]] = {LMM.PALIGEMMA: ["resolution_wh", "classes"]}
REQUIRED_ARGUMENTS: Dict[LMM, List[str]] = {
LMM.PALIGEMMA: ["resolution_wh"],
LMM.FLORENCE_2: ["resolution_wh"],
}
ALLOWED_ARGUMENTS: Dict[LMM, List[str]] = {
LMM.PALIGEMMA: ["resolution_wh", "classes"],
LMM.FLORENCE_2: ["resolution_wh"],
}
SUPPORTED_TASKS_FLORENCE_2 = [
"<OD>",
"<CAPTION_TO_PHRASE_GROUNDING>",
"<DENSE_REGION_CAPTION>",
"<REGION_PROPOSAL>",
"<OCR_WITH_REGION>",
"<REFERRING_EXPRESSION_SEGMENTATION>",
"<REGION_TO_SEGMENTATION>",
"<OPEN_VOCABULARY_DETECTION>",
"<REGION_TO_CATEGORY>",
"<REGION_TO_DESCRIPTION>",
]
def validate_lmm_and_kwargs(lmm: Union[LMM, str], kwargs: Dict[str, Any]) -> LMM:
def validate_lmm_parameters(
lmm: Union[LMM, str], result: Any, kwargs: Dict[str, Any]
) -> LMM:
if isinstance(lmm, str):
try:
lmm = LMM(lmm.lower())
@@ -23,6 +49,11 @@ def validate_lmm_and_kwargs(lmm: Union[LMM, str], kwargs: Dict[str, Any]) -> LMM
f"Invalid lmm value: {lmm}. Must be one of {[e.value for e in LMM]}"
)
if not isinstance(result, RESULT_TYPES[lmm]):
raise ValueError(
f"Invalid LMM result type: {type(result)}. Must be {RESULT_TYPES[lmm]}"
)
required_args = REQUIRED_ARGUMENTS.get(lmm, [])
for arg in required_args:
if arg not in kwargs:
@@ -57,3 +88,95 @@ def from_paligemma(
class_id = np.array([classes.index(name) for name in class_name])
return xyxy, class_id, class_name
def from_florence_2(
result: dict, resolution_wh: Tuple[int, int]
) -> Tuple[
np.ndarray, Optional[np.ndarray], Optional[np.ndarray], Optional[np.ndarray]
]:
"""
Parse results from the Florence 2 multi-model model.
https://huggingface.co/microsoft/Florence-2-large
Parameters:
result: dict containing the model output
Returns:
xyxy (np.ndarray): An array of shape `(n, 4)` containing
the bounding boxes coordinates in format `[x1, y1, x2, y2]`
labels: (Optional[np.ndarray]): An array of shape `(n,)` containing
the class labels for each bounding box
masks: (Optional[np.ndarray]): An array of shape `(n, h, w)` containing
the segmentation masks for each bounding box
obb_boxes: (Optional[np.ndarray]): An array of shape `(n, 4, 2)` containing
oriented bounding boxes.
"""
assert len(result) == 1, f"Expected result with a single element. Got: {result}"
task = list(result.keys())[0]
if task not in SUPPORTED_TASKS_FLORENCE_2:
raise ValueError(
f"{task} not supported. Supported tasks are: {SUPPORTED_TASKS_FLORENCE_2}"
)
result = result[task]
if task in ["<OD>", "<CAPTION_TO_PHRASE_GROUNDING>", "<DENSE_REGION_CAPTION>"]:
xyxy = np.array(result["bboxes"], dtype=np.float32)
labels = np.array(result["labels"])
return xyxy, labels, None, None
if task == "<REGION_PROPOSAL>":
xyxy = np.array(result["bboxes"], dtype=np.float32)
# provides labels, but they are ["", "", "", ...]
return xyxy, None, None, None
if task == "<OCR_WITH_REGION>":
xyxyxyxy = np.array(result["quad_boxes"], dtype=np.float32)
xyxyxyxy = xyxyxyxy.reshape(-1, 4, 2)
xyxy = np.array([polygon_to_xyxy(polygon) for polygon in xyxyxyxy])
labels = np.array(result["labels"])
return xyxy, labels, None, xyxyxyxy
if task in ["<REFERRING_EXPRESSION_SEGMENTATION>", "<REGION_TO_SEGMENTATION>"]:
xyxy_list = []
masks_list = []
for polygons_of_same_class in result["polygons"]:
for polygon in polygons_of_same_class:
polygon = np.reshape(polygon, (-1, 2)).astype(np.int32)
mask = polygon_to_mask(polygon, resolution_wh).astype(bool)
masks_list.append(mask)
xyxy = polygon_to_xyxy(polygon)
xyxy_list.append(xyxy)
# per-class labels also provided, but they are ["", "", "", ...]
# when we figure out how to set class names, we can do
# zip(result["labels"], result["polygons"])
xyxy = np.array(xyxy_list, dtype=np.float32)
masks = np.array(masks_list)
return xyxy, None, masks, None
if task == "<OPEN_VOCABULARY_DETECTION>":
xyxy = np.array(result["bboxes"], dtype=np.float32)
labels = np.array(result["bboxes_labels"])
# Also has "polygons" and "polygons_labels", but they don't seem to be used
return xyxy, labels, None, None
if task in ["<REGION_TO_CATEGORY>", "<REGION_TO_DESCRIPTION>"]:
assert isinstance(
result, str
), f"Expected string as <REGION_TO_CATEGORY> result, got {type(result)}"
if result == "No object detected.":
return np.empty((0, 4), dtype=np.float32), np.array([]), None, None
pattern = re.compile(r"<loc_(\d+)><loc_(\d+)><loc_(\d+)><loc_(\d+)>")
match = pattern.search(result)
assert (
match is not None
), f"Expected string to end in location tags, but got {result}"
xyxy = np.array([match.groups()], dtype=np.float32)
result_string = result[: match.start()]
labels = np.array([result_string])
return xyxy, labels, None, None
assert False, f"Unimplemented task: {task}"
+3 -3
View File
@@ -11,7 +11,7 @@ from supervision.draw.color import Color
from supervision.draw.utils import draw_rounded_rectangle
from supervision.keypoint.core import KeyPoints
from supervision.keypoint.skeletons import SKELETONS_BY_VERTEX_COUNT
from supervision.utils.conversion import convert_for_annotation_method
from supervision.utils.conversion import ensure_cv2_image_for_annotation
class BaseKeyPointAnnotator(ABC):
@@ -41,7 +41,7 @@ class VertexAnnotator(BaseKeyPointAnnotator):
self.color = color
self.radius = radius
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(self, scene: ImageType, key_points: KeyPoints) -> ImageType:
"""
Annotates the given scene with skeleton vertices based on the provided key
@@ -117,7 +117,7 @@ class EdgeAnnotator(BaseKeyPointAnnotator):
self.thickness = thickness
self.edges = edges
@convert_for_annotation_method
@ensure_cv2_image_for_annotation
def annotate(self, scene: ImageType, key_points: KeyPoints) -> ImageType:
"""
Annotates the given scene by drawing lines between specified key points to form
+190 -46
View File
@@ -18,12 +18,13 @@ class KeyPoints:
The `sv.KeyPoints` class in the Supervision library standardizes results from
various keypoint detection and pose estimation models into a consistent format. This
class simplifies data manipulation and filtering, providing a uniform API for
integration with Supervision annotators.
integration with Supervision [keypoints annotators](/keypoint/annotators).
=== "Ultralytics"
Use [`sv.KeyPoints.from_ultralytics`](/keypoint/core/#supervision.keypoint.core.KeyPoints.from_ultralytics)
method, which accepts model results.
method, which accepts [YOLOv8](https://github.com/ultralytics/ultralytics)
pose result.
```python
import cv2
@@ -32,10 +33,60 @@ class KeyPoints:
image = cv2.imread(<SOURCE_IMAGE_PATH>)
model = YOLO('yolov8s-pose.pt')
result = model(image)[0]
key_points = sv.KeyPoints.from_ultralytics(result)
```
=== "Inference"
Use [`sv.KeyPoints.from_inference`](/keypoint/core/#supervision.keypoint.core.KeyPoints.from_inference)
method, which accepts [Inference](https://inference.roboflow.com/) pose result.
```python
import cv2
import supervision as sv
from inference import get_model
image = cv2.imread(<SOURCE_IMAGE_PATH>)
model = get_model(model_id=<POSE_MODEL_ID>, api_key=<ROBOFLOW_API_KEY>)
result = model.infer(image)[0]
key_points = sv.KeyPoints.from_inference(result)
```
=== "MediaPipe"
Use [`sv.KeyPoints.from_mediapipe`](/keypoint/core/#supervision.keypoint.core.KeyPoints.from_mediapipe)
method, which accepts [MediaPipe](https://github.com/google-ai-edge/mediapipe)
pose result.
```python
import cv2
import mediapipe as mp
import supervision as sv
image = cv2.imread(<SOURCE_IMAGE_PATH>)
image_height, image_width, _ = image.shape
mediapipe_image = mp.Image(
image_format=mp.ImageFormat.SRGB,
data=cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
options = mp.tasks.vision.PoseLandmarkerOptions(
base_options=mp.tasks.BaseOptions(
model_asset_path="pose_landmarker_heavy.task"
),
running_mode=mp.tasks.vision.RunningMode.IMAGE,
num_poses=2)
PoseLandmarker = mp.tasks.vision.PoseLandmarker
with PoseLandmarker.create_from_options(options) as landmarker:
pose_landmarker_result = landmarker.detect(mediapipe_image)
key_points = sv.KeyPoints.from_mediapipe(
pose_landmarker_result, (image_width, image_height))
```
Attributes:
xy (np.ndarray): An array of shape `(n, 2)` containing
the bounding boxes coordinates in format `[x1, y1]`
@@ -63,7 +114,7 @@ class KeyPoints:
def __len__(self) -> int:
"""
Returns the number of keypoints in the keypoints object.
Returns the number of keypoints in the `sv.KeyPoints` object.
"""
return len(self.xy)
@@ -106,18 +157,17 @@ class KeyPoints:
"""
Create a `sv.KeyPoints` object from the [Roboflow](https://roboflow.com/)
API inference result or the [Inference](https://inference.roboflow.com/)
package results. When a keypoint detection model is used, this method
extracts the keypoint coordinates, class IDs, confidences, and class names.
package results.
Args:
inference_result (dict, any): The result from the
Roboflow API or Inference package containing predictions with keypoints.
Returns:
(KeyPoints): A KeyPoints object containing the keypoint coordinates,
class IDs, and confidences of each keypoint.
A `sv.KeyPoints` object containing the keypoint coordinates, class IDs,
and class names, and confidences of each keypoint.
Example:
Examples:
```python
import cv2
import supervision as sv
@@ -151,8 +201,6 @@ class KeyPoints:
"You can retrieve it like so: inference_result = model.infer(image)[0]"
)
# Unpack the result if received from inference.get_model,
# rather than inference_sdk.InferenceHTTPClient
with suppress(AttributeError):
inference_result = inference_result.dict(exclude_none=True, by_alias=True)
@@ -185,20 +233,105 @@ class KeyPoints:
data=data,
)
@classmethod
def from_mediapipe(
cls, mediapipe_results, resolution_wh: Tuple[int, int]
) -> KeyPoints:
"""
Creates a `sv.KeyPoints` instance from a
[MediaPipe](https://github.com/google-ai-edge/mediapipe)
pose landmark detection inference result.
Args:
mediapipe_results (Union[PoseLandmarkerResult, SolutionOutputs]):
The output results from Mediapipe. It supports both: the inference
result `PoseLandmarker` and the legacy one from `Pose`.
resolution_wh (Tuple[int, int]): A tuple of the form `(width, height)`
representing the resolution of the frame.
Returns:
A `sv.KeyPoints` object containing the keypoint coordinates and
confidences of each keypoint.
!!! tip
Before you start, download model bundles from the
[MediaPipe website](https://ai.google.dev/edge/mediapipe/solutions/vision/pose_landmarker/index#models).
Examples:
```python
import cv2
import mediapipe as mp
import supervision as sv
image = cv2.imread(<SOURCE_IMAGE_PATH>)
image_height, image_width, _ = image.shape
mediapipe_image = mp.Image(
image_format=mp.ImageFormat.SRGB,
data=cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
options = mp.tasks.vision.PoseLandmarkerOptions(
base_options=mp.tasks.BaseOptions(
model_asset_path="pose_landmarker_heavy.task"
),
running_mode=mp.tasks.vision.RunningMode.IMAGE,
num_poses=2)
PoseLandmarker = mp.tasks.vision.PoseLandmarker
with PoseLandmarker.create_from_options(options) as landmarker:
pose_landmarker_result = landmarker.detect(mediapipe_image)
key_points = sv.KeyPoints.from_mediapipe(
pose_landmarker_result, (image_width, image_height))
```
""" # noqa: E501 // docs
results = mediapipe_results.pose_landmarks
if not isinstance(mediapipe_results.pose_landmarks, list):
if mediapipe_results.pose_landmarks is None:
results = []
else:
results = [
[landmark for landmark in mediapipe_results.pose_landmarks.landmark]
]
if len(results) == 0:
return cls.empty()
xy = []
confidence = []
for pose in results:
prediction_xy = []
prediction_confidence = []
for landmark in pose:
keypoint_xy = [
landmark.x * resolution_wh[0],
landmark.y * resolution_wh[1],
]
prediction_xy.append(keypoint_xy)
prediction_confidence.append(landmark.visibility)
xy.append(prediction_xy)
confidence.append(prediction_confidence)
return cls(
xy=np.array(xy, dtype=np.float32),
confidence=np.array(confidence, dtype=np.float32),
)
@classmethod
def from_ultralytics(cls, ultralytics_results) -> KeyPoints:
"""
Creates a KeyPoints instance from a
[YOLOv8](https://github.com/ultralytics/ultralytics) inference result.
Creates a `sv.KeyPoints` instance from a
[YOLOv8](https://github.com/ultralytics/ultralytics) pose inference result.
Args:
ultralytics_results (ultralytics.engine.results.Keypoints):
The output Results instance from YOLOv8
Returns:
KeyPoints: A new KeyPoints object.
A `sv.KeyPoints` object containing the keypoint coordinates, class IDs,
and class names, and confidences of each keypoint.
Example:
Examples:
```python
import cv2
import supervision as sv
@@ -206,8 +339,9 @@ class KeyPoints:
image = cv2.imread(<SOURCE_IMAGE_PATH>)
model = YOLO('yolov8s-pose.pt')
result = model(image)[0]
keypoints = sv.KeyPoints.from_ultralytics(result)
key_points = sv.KeyPoints.from_ultralytics(result)
```
"""
if ultralytics_results.keypoints.xy.numel() == 0:
@@ -224,16 +358,18 @@ class KeyPoints:
@classmethod
def from_yolo_nas(cls, yolo_nas_results) -> KeyPoints:
"""
Create a KeyPoints instance from a YOLO NAS results.
Create a `sv.KeyPoints` instance from a [YOLO-NAS](https://github.com/Deci-AI/super-gradients/blob/master/YOLONAS-POSE.md)
pose inference results.
Args:
yolo_nas_results (ImagePoseEstimationPrediction):
The output object from YOLO NAS.
yolo_nas_results (ImagePoseEstimationPrediction): The output object from
YOLO NAS.
Returns:
KeyPoints: A new KeyPoints object.
A `sv.KeyPoints` object containing the keypoint coordinates, class IDs,
and class names, and confidences of each keypoint.
Example:
Examples:
```python
import cv2
import torch
@@ -243,13 +379,13 @@ class KeyPoints:
image = cv2.imread(<SOURCE_IMAGE_PATH>)
device = "cuda" if torch.cuda.is_available() else "cpu"
yolo_nas = super_gradients.training.models.get(
model = super_gradients.training.models.get(
"yolo_nas_pose_s", pretrained_weights="coco_pose").to(device)
results = yolo_nas.predict(image, conf=0.1)
keypoints = sv.KeyPoints.from_yolo_nas(results)
results = model.predict(image, conf=0.1)
key_points = sv.KeyPoints.from_yolo_nas(results)
```
"""
""" # noqa: E501 // docs
if len(yolo_nas_results.prediction.poses) == 0:
return cls.empty()
@@ -282,34 +418,41 @@ class KeyPoints:
self, index: Union[int, slice, List[int], np.ndarray, str]
) -> Union[KeyPoints, List, np.ndarray, None]:
"""
Get a subset of the KeyPoints object or access an item from its data field.
Get a subset of the `sv.KeyPoints` object or access an item from its data field.
When provided with an integer, slice, list of integers, or a numpy array, this
method returns a new KeyPoints object that represents a subset of the original
keypoints. When provided with a string, it accesses the corresponding item in
the data dictionary.
method returns a new `sv.KeyPoints` object that represents a subset of the
original `sv.KeyPoints`. When provided with a string, it accesses the
corresponding item in the data dictionary.
Args:
index (Union[int, slice, List[int], np.ndarray, str]): The index, indices,
or key to access a subset of the KeyPoints or an item from the data.
or key to access a subset of the `sv.KeyPoints` or an item from the
data.
Returns:
Union[KeyPoints, Any]: A subset of the KeyPoints object or an item from
the data field.
A subset of the `sv.KeyPoints` object or an item from the data field.
Example:
Examples:
```python
import supervision as sv
keypoints = sv.KeyPoints()
key_points = sv.KeyPoints()
first_detection = keypoints[0]
first_10_keypoints = keypoints[0:10]
some_keypoints = keypoints[[0, 2, 4]]
class_0_keypoints = keypoints[keypoints.class_id == 0]
high_confidence_keypoints = keypoints[keypoints.confidence > 0.5]
# access the first keypoint using an integer index
key_points[0]
feature_vector = keypoints['feature_vector']
# access the first 10 keypoints using index slice
key_points[0:10]
# access selected keypoints using a list of indices
key_points[[0, 2, 4]]
# access keypoints with selected class_id
key_points[key_points.class_id == 0]
# access keypoints with confidence greater than 0.5
key_points[key_points.confidence > 0.5]
```
"""
if isinstance(index, str):
@@ -325,13 +468,13 @@ class KeyPoints:
def __setitem__(self, key: str, value: Union[np.ndarray, List]):
"""
Set a value in the data dictionary of the KeyPoints object.
Set a value in the data dictionary of the `sv.KeyPoints` object.
Args:
key (str): The key in the data dictionary to set.
value (Union[np.ndarray, List]): The value to set for the key.
Example:
Examples:
```python
import cv2
import supervision as sv
@@ -343,7 +486,7 @@ class KeyPoints:
result = model(image)[0]
keypoints = sv.KeyPoints.from_ultralytics(result)
keypoints['names'] = [
keypoints['class_name'] = [
model.model.names[class_id]
for class_id
in keypoints.class_id
@@ -364,12 +507,13 @@ class KeyPoints:
Create an empty Keypoints object with no keypoints.
Returns:
(KeyPoints): An empty Keypoints object.
An empty `sv.KeyPoints` object.
Example:
Examples:
```python
from supervision import Keypoints
empty_keypoints = Keypoints.empty()
import supervision as sv
key_points = sv.KeyPoints.empty()
```
"""
return cls(xy=np.empty((0, 0, 2), dtype=np.float32))
+38
View File
@@ -25,6 +25,44 @@ class Skeleton(Enum):
(17, 15),
]
GHUM = [
(1, 2),
(1, 5),
(2, 3),
(3, 4),
(4, 8),
(5, 6),
(6, 7),
(7, 9),
(10, 11),
(12, 13),
(12, 14),
(12, 24),
(13, 15),
(13, 25),
(14, 16),
(15, 17),
(16, 18),
(15, 19),
(16, 22),
(17, 19),
(17, 21),
(17, 23),
(18, 20),
(19, 21),
(24, 25),
(24, 26),
(25, 27),
(26, 28),
(27, 29),
(28, 30),
(28, 32),
(29, 31),
(29, 33),
(30, 32),
(31, 33),
]
SKELETONS_BY_EDGE_COUNT: Dict[int, Edges] = {}
SKELETONS_BY_VERTEX_COUNT: Dict[int, Edges] = {}
+36 -7
View File
@@ -8,11 +8,13 @@ from PIL import Image
from supervision.annotators.base import ImageType
def convert_for_annotation_method(annotate_func):
def ensure_cv2_image_for_annotation(annotate_func):
"""
Decorates `BaseAnnotator.annotate` implementations, converts scene to
an image type used internally by the annotators, converts back when annotation
is complete.
Assumes the annotators modify the scene in-place.
"""
@wraps(annotate_func)
@@ -21,19 +23,22 @@ def convert_for_annotation_method(annotate_func):
return annotate_func(self, scene, *args, **kwargs)
if isinstance(scene, Image.Image):
scene = pillow_to_cv2(scene)
annotated = annotate_func(self, scene, *args, **kwargs)
return cv2_to_pillow(image=annotated)
scene_np = pillow_to_cv2(scene)
annotated_np = annotate_func(self, scene_np, *args, **kwargs)
scene.paste(cv2_to_pillow(annotated_np))
return scene
raise ValueError(f"Unsupported image type: {type(scene)}")
return wrapper
def convert_for_image_processing(image_processing_fun):
def ensure_cv2_image_for_processing(image_processing_fun):
"""
Decorates image processing functions that accept np.ndarray, converting `image` to
np.ndarray, converts back when processing is complete.
Assumes the annotators do NOT modify the scene in-place.
"""
@wraps(image_processing_fun)
@@ -44,13 +49,37 @@ def convert_for_image_processing(image_processing_fun):
if isinstance(image, Image.Image):
scene = pillow_to_cv2(image)
annotated = image_processing_fun(scene, *args, **kwargs)
return cv2_to_pillow(image=annotated)
return cv2_to_pillow(annotated)
raise ValueError(f"Unsupported image type: {type(image)}")
return wrapper
def ensure_pil_image_for_annotation(annotate_func):
"""
Decorates image processing functions that accept np.ndarray, converting `image` to
PIL image, converts back when processing is complete.
Assumes the annotators modify the scene in-place.
"""
@wraps(annotate_func)
def wrapper(self, scene: ImageType, *args, **kwargs):
if isinstance(scene, np.ndarray):
scene_pil = cv2_to_pillow(scene)
annotated_pil = annotate_func(self, scene_pil, *args, **kwargs)
np.copyto(scene, pillow_to_cv2(annotated_pil))
return scene
if isinstance(scene, Image.Image):
return annotate_func(self, scene, *args, **kwargs)
raise ValueError(f"Unsupported image type: {type(scene)}")
return wrapper
def images_to_cv2(images: List[ImageType]) -> List[np.ndarray]:
"""
Converts images provided either as Pillow images or OpenCV
@@ -67,7 +96,7 @@ def images_to_cv2(images: List[ImageType]) -> List[np.ndarray]:
result = []
for image in images:
if issubclass(type(image), Image.Image):
image = pillow_to_cv2(image=image)
image = pillow_to_cv2(image)
result.append(image)
return result
+19 -19
View File
@@ -1,6 +1,6 @@
import json
from pathlib import Path
from typing import List, Optional, Union
from typing import Dict, List, Optional, Union
import numpy as np
import yaml
@@ -57,20 +57,20 @@ def list_files_with_extensions(
return files_with_extensions
def read_txt_file(file_path: str, skip_empty: bool = False) -> List[str]:
def read_txt_file(file_path: Union[str, Path], skip_empty: bool = False) -> List[str]:
"""
Read a text file and return a list of strings without newline characters.
Optionally skip empty lines.
Args:
file_path (str): The path to the text file.
file_path (Union[str, Path]): The file path as a string or Path object.
skip_empty (bool): If True, skip lines that are empty or contain only
whitespace. Default is False.
Returns:
List[str]: A list of strings representing the lines in the text file.
"""
with open(file_path, "r") as file:
with open(str(file_path), "r") as file:
if skip_empty:
lines = [line.rstrip("\n") for line in file if line.strip()]
else:
@@ -79,71 +79,71 @@ def read_txt_file(file_path: str, skip_empty: bool = False) -> List[str]:
return lines
def save_text_file(lines: List[str], file_path: str):
def save_text_file(lines: List[str], file_path: Union[str, Path]) -> None:
"""
Write a list of strings to a text file, each string on a new line.
Args:
lines (List[str]): The list of strings to be written to the file.
file_path (str): The path to the text file.
file_path (Union[str, Path]): The file path as a string or Path object.
"""
with open(file_path, "w") as file:
with open(str(file_path), "w") as file:
for line in lines:
file.write(line + "\n")
def read_json_file(file_path: str) -> dict:
def read_json_file(file_path: Union[str, Path]) -> Dict:
"""
Read a json file and return a dict.
Args:
file_path (str): The path to the json file.
file_path (Union[str, Path]): The file path as a string or Path object.
Returns:
dict: A dict of annotations information
"""
with open(file_path, "r") as file:
with open(str(file_path), "r") as file:
data = json.load(file)
return data
def save_json_file(data: dict, file_path: str, indent: int = 3) -> None:
def save_json_file(data: dict, file_path: Union[str, Path], indent: int = 3) -> None:
"""
Write a dict to a json file.
Args:
indent:
data (dict): dict with unique keys and value as pair.
file_path (str): The path to the json file.
file_path (Union[str, Path]): The file path as a string or Path object.
"""
with open(file_path, "w") as fp:
with open(str(file_path), "w") as fp:
json.dump(data, fp, cls=NumpyJsonEncoder, indent=indent)
def read_yaml_file(file_path: str) -> dict:
def read_yaml_file(file_path: Union[str, Path]) -> Dict:
"""
Read a yaml file and return a dict.
Args:
file_path (str): The path to the yaml file.
file_path (Union[str, Path]): The file path as a string or Path object.
Returns:
dict: A dict of content information
"""
with open(file_path, "r") as file:
with open(str(file_path), "r") as file:
data = yaml.safe_load(file)
return data
def save_yaml_file(data: dict, file_path: str) -> None:
def save_yaml_file(data: dict, file_path: Union[str, Path]) -> None:
"""
Save a dict to a json file.
Args:
indent:
data (dict): dict with unique keys and value as pair.
file_path (str): The path to the json file.
file_path (Union[str, Path]): The file path as a string or Path object.
"""
with open(file_path, "w") as outfile:
with open(str(file_path), "w") as outfile:
yaml.dump(data, outfile, sort_keys=False, default_flow_style=None)
+5 -5
View File
@@ -14,8 +14,8 @@ from supervision.draw.color import Color, unify_to_bgr
from supervision.draw.utils import calculate_optimal_text_scale, draw_text
from supervision.geometry.core import Point
from supervision.utils.conversion import (
convert_for_image_processing,
cv2_to_pillow,
ensure_cv2_image_for_processing,
images_to_cv2,
)
from supervision.utils.iterables import create_batches, fill
@@ -25,7 +25,7 @@ RelativePosition = Literal["top", "bottom"]
MAX_COLUMNS_FOR_SINGLE_ROW_GRID = 3
@convert_for_image_processing
@ensure_cv2_image_for_processing
def crop_image(
image: ImageType,
xyxy: Union[npt.NDArray[int], List[int], Tuple[int, int, int, int]],
@@ -86,7 +86,7 @@ def crop_image(
return image[y_min:y_max, x_min:x_max]
@convert_for_image_processing
@ensure_cv2_image_for_processing
def scale_image(image: ImageType, scale_factor: float) -> ImageType:
"""
Scales the given image based on the given scale factor.
@@ -143,7 +143,7 @@ def scale_image(image: ImageType, scale_factor: float) -> ImageType:
return cv2.resize(image, (width_new, height_new), interpolation=cv2.INTER_LINEAR)
@convert_for_image_processing
@ensure_cv2_image_for_processing
def resize_image(
image: ImageType,
resolution_wh: Tuple[int, int],
@@ -216,7 +216,7 @@ def resize_image(
return cv2.resize(image, (width_new, height_new), interpolation=cv2.INTER_LINEAR)
@convert_for_image_processing
@ensure_cv2_image_for_processing
def letterbox_image(
image: ImageType,
resolution_wh: Tuple[int, int],
+291
View File
@@ -0,0 +1,291 @@
from contextlib import ExitStack as DoesNotRaise
from typing import Optional, Tuple
import numpy as np
import pytest
from supervision.detection.lmm import from_florence_2
@pytest.mark.parametrize(
"florence_result, resolution_wh, expected_results, exception",
[
( # Object detection: empty
{"<OD>": {"bboxes": [], "labels": []}},
(10, 10),
(np.array([], dtype=np.float32), np.array([]), None, None),
DoesNotRaise(),
),
( # Object detection: two detections
{
"<OD>": {
"bboxes": [[4, 4, 6, 6], [5, 5, 7, 7]],
"labels": ["car", "door"],
}
},
(10, 10),
(
np.array([[4, 4, 6, 6], [5, 5, 7, 7]], dtype=np.float32),
np.array(["car", "door"]),
None,
None,
),
DoesNotRaise(),
),
( # Caption: unsupported
{"<CAPTION>": "A green car parked in front of a yellow building."},
(10, 10),
None,
pytest.raises(ValueError),
),
( # Detailed Caption: unsupported
{
"<DETAILED_CAPTION>": "The image shows a blue Volkswagen Beetle parked "
"in front of a yellow building with two brown doors, surrounded by "
"trees and a clear blue sky."
},
(10, 10),
None,
pytest.raises(ValueError),
),
( # More Detailed Caption: unsupported
{
"<MORE_DETAILED_CAPTION>": "The image shows a vintage Volkswagen "
"Beetle car parked on a "
"cobblestone street in front of a yellow building with two wooden "
"doors. The car is painted in a bright turquoise color and has a "
"white stripe running along the side. It has two doors on either side "
"of the car, one on top of the other, and a small window on the "
"front. The building appears to be old and dilapidated, with peeling "
"paint and crumbling walls. The sky is blue and there are trees in "
"the background."
},
(10, 10),
None,
pytest.raises(ValueError),
),
( # Caption to Phrase Grounding: empty
{"<CAPTION_TO_PHRASE_GROUNDING>": {"bboxes": [], "labels": []}},
(10, 10),
(np.array([], dtype=np.float32), np.array([]), None, None),
DoesNotRaise(),
),
( # Caption to Phrase Grounding: two detections
{
"<CAPTION_TO_PHRASE_GROUNDING>": {
"bboxes": [[4, 4, 6, 6], [5, 5, 7, 7]],
"labels": ["a green car", "a yellow building"],
}
},
(10, 10),
(
np.array([[4, 4, 6, 6], [5, 5, 7, 7]], dtype=np.float32),
np.array(["a green car", "a yellow building"]),
None,
None,
),
DoesNotRaise(),
),
( # Dense Region caption: empty
{"<DENSE_REGION_CAPTION>": {"bboxes": [], "labels": []}},
(10, 10),
(np.array([], dtype=np.float32), np.array([]), None, None),
DoesNotRaise(),
),
( # Caption to Phrase Grounding: two detections
{
"<DENSE_REGION_CAPTION>": {
"bboxes": [[4, 4, 6, 6], [5, 5, 7, 7]],
"labels": ["a green car", "a yellow building"],
}
},
(10, 10),
(
np.array([[4, 4, 6, 6], [5, 5, 7, 7]], dtype=np.float32),
np.array(["a green car", "a yellow building"]),
None,
None,
),
DoesNotRaise(),
),
( # Region proposal
{
"<REGION_PROPOSAL>": {
"bboxes": [[4, 4, 6, 6], [5, 5, 7, 7]],
"labels": ["", ""],
}
},
(10, 10),
(
np.array([[4, 4, 6, 6], [5, 5, 7, 7]], dtype=np.float32),
None,
None,
None,
),
DoesNotRaise(),
),
( # Referring Expression Segmentation
{
"<REFERRING_EXPRESSION_SEGMENTATION>": {
"polygons": [[[1, 1, 2, 1, 2, 2, 1, 2]]],
"labels": [""],
}
},
(10, 10),
(
np.array([[1.0, 1.0, 2.0, 2.0]], dtype=np.float32),
None,
np.array(
[
[
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
]
],
dtype=bool,
),
None,
),
DoesNotRaise(),
),
( # Referring Expression Segmentation
{
"<REFERRING_EXPRESSION_SEGMENTATION>": {
"polygons": [[[1, 1, 2, 1, 2, 2, 1, 2]]],
"labels": [""],
}
},
(10, 10),
(
np.array([[1.0, 1.0, 2.0, 2.0]], dtype=np.float32),
None,
np.array(
[
[
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
]
],
dtype=bool,
),
None,
),
DoesNotRaise(),
),
( # OCR: unsupported
{"<OCR>": "A"},
(10, 10),
None,
pytest.raises(ValueError),
),
( # OCR with Region: obb boxes
{
"<OCR_WITH_REGION>": {
"quad_boxes": [[2, 2, 6, 4, 5, 6, 1, 5], [4, 4, 5, 5, 4, 6, 3, 5]],
"labels": ["some text", "other text"],
}
},
(10, 10),
(
np.array([[1, 2, 6, 6], [3, 4, 5, 6]], dtype=np.float32),
np.array(["some text", "other text"]),
None,
np.array(
[[[2, 2], [6, 4], [5, 6], [1, 5]], [[4, 4], [5, 5], [4, 6], [3, 5]]]
),
),
DoesNotRaise(),
),
( # Open Vocabulary Detection
{
"<OPEN_VOCABULARY_DETECTION>": {
"bboxes": [[4, 4, 6, 6], [5, 5, 7, 7]],
"bboxes_labels": ["cat", "cat"],
"polygon": [],
"polygons_labels": [],
}
},
(10, 10),
(
np.array([[4, 4, 6, 6], [5, 5, 7, 7]], dtype=np.float32),
np.array(["cat", "cat"]),
None,
None,
),
DoesNotRaise(),
),
( # Region to Category: empty
{"<REGION_TO_CATEGORY>": "No object detected."},
(10, 10),
(np.empty((0, 4), dtype=np.float32), np.array([]), None, None),
DoesNotRaise(),
),
( # Region to Category: detected
{"<REGION_TO_CATEGORY>": "some object<loc_3><loc_4><loc_5><loc_6>"},
(10, 10),
(
np.array([[3, 4, 5, 6]], dtype=np.float32),
np.array(["some object"]),
None,
None,
),
DoesNotRaise(),
),
( # Region to Description: empty
{"<REGION_TO_DESCRIPTION>": "No object detected."},
(10, 10),
(np.empty((0, 4), dtype=np.float32), np.array([]), None, None),
DoesNotRaise(),
),
( # Region to Description: detected
{"<REGION_TO_DESCRIPTION>": "some description<loc_3><loc_4><loc_5><loc_6>"},
(10, 10),
(
np.array([[3, 4, 5, 6]], dtype=np.float32),
np.array(["some description"]),
None,
None,
),
DoesNotRaise(),
),
],
)
def test_florence_2(
florence_result: dict,
resolution_wh: Tuple[int, int],
expected_results: Tuple[
np.ndarray, Optional[np.ndarray], Optional[np.ndarray], Optional[np.ndarray]
],
exception: Exception,
) -> None:
with exception:
result = from_florence_2(florence_result, resolution_wh)
np.testing.assert_array_equal(result[0], expected_results[0])
if expected_results[1] is None:
assert result[1] is None
else:
np.testing.assert_array_equal(result[1], expected_results[1])
if expected_results[2] is None:
assert result[2] is None
else:
np.testing.assert_array_equal(result[2], expected_results[2])
if expected_results[3] is None:
assert result[3] is None
else:
np.testing.assert_array_equal(result[3], expected_results[3])
+1 -1
View File
@@ -11,7 +11,7 @@ ALL_IMAGES_LIST = [os.path.join(ASSETS_DIR, f"{i}.jpg") for i in range(1, 6)]
@fixture(scope="function")
def empty_opencv_image() -> np.ndarray:
def empty_cv2_image() -> np.ndarray:
return np.zeros((128, 128, 3), dtype=np.uint8)
+24 -26
View File
@@ -2,28 +2,28 @@ import numpy as np
from PIL import Image, ImageChops
from supervision.utils.conversion import (
convert_for_image_processing,
cv2_to_pillow,
ensure_cv2_image_for_processing,
images_to_cv2,
pillow_to_cv2,
)
def test_convert_for_image_processing_when_pillow_image_submitted(
empty_opencv_image: np.ndarray, empty_pillow_image: Image.Image
def test_ensure_cv2_image_for_processing_when_pillow_image_submitted(
empty_cv2_image: np.ndarray, empty_pillow_image: Image.Image
) -> None:
# given
param_a_value = 3
param_b_value = "some"
@convert_for_image_processing
@ensure_cv2_image_for_processing
def my_custom_processing_function(
image: np.ndarray,
param_a: int,
param_b: str,
) -> np.ndarray:
assert np.allclose(
image, empty_opencv_image
image, empty_cv2_image
), "Expected conversion to OpenCV image to happen"
assert (
param_a == param_a_value
@@ -48,21 +48,21 @@ def test_convert_for_image_processing_when_pillow_image_submitted(
)
def test_convert_for_image_processing_when_opencv_image_submitted(
empty_opencv_image: np.ndarray,
def test_ensure_cv2_image_for_processing_when_cv2_image_submitted(
empty_cv2_image: np.ndarray,
) -> None:
# given
param_a_value = 3
param_b_value = "some"
@convert_for_image_processing
@ensure_cv2_image_for_processing
def my_custom_processing_function(
image: np.ndarray,
param_a: int,
param_b: str,
) -> np.ndarray:
assert np.allclose(
image, empty_opencv_image
image, empty_cv2_image
), "Expected conversion to OpenCV image to happen"
assert (
param_a == param_a_value
@@ -74,22 +74,20 @@ def test_convert_for_image_processing_when_opencv_image_submitted(
# when
result = my_custom_processing_function(
empty_opencv_image,
empty_cv2_image,
param_a_value,
param_b=param_b_value,
)
# then
assert (
result is empty_opencv_image
), "Expected to return OpenCV image without changes"
assert result is empty_cv2_image, "Expected to return OpenCV image without changes"
def test_cv2_to_pillow(
empty_opencv_image: np.ndarray, empty_pillow_image: Image.Image
empty_cv2_image: np.ndarray, empty_pillow_image: Image.Image
) -> None:
# when
result = cv2_to_pillow(image=empty_opencv_image)
result = cv2_to_pillow(image=empty_cv2_image)
# then
difference = ImageChops.difference(result, empty_pillow_image)
@@ -99,14 +97,14 @@ def test_cv2_to_pillow(
def test_pillow_to_cv2(
empty_opencv_image: np.ndarray, empty_pillow_image: Image.Image
empty_cv2_image: np.ndarray, empty_pillow_image: Image.Image
) -> None:
# when
result = pillow_to_cv2(image=empty_pillow_image)
# then
assert np.allclose(
result, empty_opencv_image
result, empty_cv2_image
), "Conversion to OpenCV image expected not to change the content of image"
@@ -119,10 +117,10 @@ def test_images_to_cv2_when_empty_input_provided() -> None:
def test_images_to_cv2_when_only_cv2_images_provided(
empty_opencv_image: np.ndarray,
empty_cv2_image: np.ndarray,
) -> None:
# given
images = [empty_opencv_image] * 5
images = [empty_cv2_image] * 5
# when
result = images_to_cv2(images=images)
@@ -131,13 +129,13 @@ def test_images_to_cv2_when_only_cv2_images_provided(
assert len(result) == 5, "Expected the same number of output element as input ones"
for result_element in result:
assert (
result_element is empty_opencv_image
result_element is empty_cv2_image
), "Expected CV images not to be touched by conversion"
def test_images_to_cv2_when_only_pillow_images_provided(
empty_pillow_image: Image.Image,
empty_opencv_image: np.ndarray,
empty_cv2_image: np.ndarray,
) -> None:
# given
images = [empty_pillow_image] * 5
@@ -149,16 +147,16 @@ def test_images_to_cv2_when_only_pillow_images_provided(
assert len(result) == 5, "Expected the same number of output element as input ones"
for result_element in result:
assert np.allclose(
result_element, empty_opencv_image
result_element, empty_cv2_image
), "Output images expected to be equal to empty OpenCV image"
def test_images_to_cv2_when_mixed_input_provided(
empty_pillow_image: Image.Image,
empty_opencv_image: np.ndarray,
empty_cv2_image: np.ndarray,
) -> None:
# given
images = [empty_pillow_image, empty_opencv_image]
images = [empty_pillow_image, empty_cv2_image]
# when
result = images_to_cv2(images=images)
@@ -166,8 +164,8 @@ def test_images_to_cv2_when_mixed_input_provided(
# then
assert len(result) == 2, "Expected the same number of output element as input ones"
assert np.allclose(
result[0], empty_opencv_image
result[0], empty_cv2_image
), "PIL image should be converted to OpenCV one, equal to example empty image"
assert (
result[1] is empty_opencv_image
result[1] is empty_cv2_image
), "Expected CV images not to be touched by conversion"