Compare commits

...
143 Commits
Author SHA1 Message Date
SkalskiP 19396771f1 bump package version from 0.21.0rc4 to 0.21.0rc5 2024-05-13 15:58:22 +02:00
Onuralp SEZER f5324897a3 Merge pull request #1189 from roboflow/dependabot/pip/mkdocs-material-9.5.22
⬆️ Bump mkdocs-material from 9.5.21 to 9.5.22
2024-05-13 16:56:57 +03:00
Piotr Skalski cd8a2be04b Merge pull request #1178 from roboflow/feat/inference-slicer-segmentation
Feat/inference slicer segmentation
2024-05-13 15:55:02 +02:00
Linas Kondrackis 8901192b92 ruff 2024-05-13 16:53:06 +03:00
Linas Kondrackis a9044e4fed Merge branch 'develop' into feat/inference-slicer-segmentation 2024-05-13 16:52:50 +03:00
Linas Kondrackis 85e4c0ec65 Slicer: highlight & mask docstring 2024-05-13 16:51:59 +03:00
Linas Kondrackis 93398108ee Slier docs: remove transfomer example - has issues. 2024-05-13 16:26:35 +03:00
Piotr Skalski f41adca384 Merge pull request #1173 from roboflow/vertex_label_annotator
`VertexLabelAnnotator`
2024-05-13 15:23:51 +02:00
SkalskiP d64bb3879c Merge remote-tracking branch 'origin/vertex_label_annotator' into vertex_label_annotator 2024-05-13 15:21:35 +02:00
SkalskiP 452e1f1ea4 more VertexLabelAnnotator docs 2024-05-13 15:21:25 +02:00
Linas Kondrackis 45e5f49c5a Infrence slicer docs: fix callbacks in transfomers 2024-05-13 16:20:05 +03:00
LinasKo 4c36b39f94 Untested: Small object segmentationation 2024-05-13 15:41:43 +03:00
pre-commit-ci[bot] e13c7dc22c fix(pre_commit): 🎨 auto format pre-commit hooks 2024-05-13 12:31:37 +00:00
SkalskiP 713719a582 Merge remote-tracking branch 'origin/vertex_label_annotator' into vertex_label_annotator 2024-05-13 14:31:12 +02:00
SkalskiP 051fe5178b VertexLabelAnnotator plugged into docs 2024-05-13 14:30:55 +02:00
pre-commit-ci[bot] 1ebccebf4c fix(pre_commit): 🎨 auto format pre-commit hooks 2024-05-13 12:01:26 +00:00
SkalskiP f56d7a1733 changes after code review 2024-05-13 14:01:07 +02:00
LinasKo 82ab6c57cb infrenceSlicer: resolution_wh 2024-05-13 14:14:42 +03:00
Linas Kondrackis 816aecb51b PR comments: tuples, docs, optional 2024-05-13 11:35:23 +03:00
dependabot[bot] 3c8995758d ⬆️ Bump mkdocs-material from 9.5.21 to 9.5.22
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.21 to 9.5.22.
- [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.21...9.5.22)

---
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-05-13 01:21:27 +00:00
Onuralp SEZER acfabc247f Merge pull request #1185 from roboflow/dependabot/pip/ruff-0.4.4
⬆️ Bump ruff from 0.4.3 to 0.4.4
2024-05-11 23:54:29 +03:00
Onuralp SEZER 61a63c83d9 Merge pull request #1188 from roboflow/docs/missing-return-in-slicer-callback
Minor docs update: missing return in inference slicer callback
2024-05-11 21:56:13 +03:00
Linas Kondrackis 528c960587 Minor docs update: missing return in inference slicer callback 2024-05-11 13:38:59 +03:00
Linas Kondrackis 6c6171b5d5 Simpify passing of shape 2024-05-10 18:25:02 +03:00
Linas Kondrackis b8efa25e93 Merge branch 'develop' into feat/inference-slicer-segmentation 2024-05-10 17:18:07 +03:00
SkalskiP 17c8e41fb3 bump version from 0.21.0rc3 to 0.21.0rc4 2024-05-10 15:50:03 +02:00
Piotr Skalski 0c3594432c Merge pull request #1177 from roboflow/fix/detections-merge-flexibility
More flexibility in Detections.merge
2024-05-10 15:40:06 +02:00
SkalskiP a8c44cfaff ready for merge 2024-05-10 15:36:44 +02:00
pre-commit-ci[bot] 3d0c3d9182 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-05-10 13:08:35 +00:00
SkalskiP 2364abf481 small error message update + few more test cases for merge_data 2024-05-10 15:08:18 +02:00
dependabot[bot] 3b61ee674e ⬆️ Bump ruff from 0.4.3 to 0.4.4
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.4.3 to 0.4.4.
- [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.3...v0.4.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-10 00:28:34 +00:00
Linas Kondrackis 01f7eb5be6 Retain type info by not excluding empty detections 2024-05-09 18:19:51 +03:00
pre-commit-ci[bot] 4cd1fbcef9 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-05-09 14:15:31 +00:00
Linas Kondrackis b6a55694f2 Move type resolution logic to loop where it's used 2024-05-09 17:12:53 +03:00
Linas Kondrackis 8c58ebe30d Roll back flex-merge on data, merge when key missing 2024-05-09 17:06:51 +03:00
Piotr Skalski 77034700dd Merge pull request #767 from DrawingProcess/mmdetectionmask
Mmdetection mask
2024-05-09 13:19:00 +02:00
Linas Kondrackis 1ebbe3a8d1 Removed comments, deindented, removed unused var 2024-05-09 09:17:37 +03:00
Onuralp SEZER 617bd30c5f Merge pull request #1175 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2024-05-08 22:50:20 +03:00
Onuralp SEZER 2e8720f156 Merge pull request #1176 from roboflow/dependabot/pip/jinja2-3.1.4
⬆️ Bump jinja2 from 3.1.3 to 3.1.4
2024-05-08 22:49:59 +03:00
Linas Kondrackis 45563dfddf InferenceSlicer: Now works with segmentation 2024-05-08 14:07:37 +03:00
Linas Kondrackis 7bb94ce966 Detections.merge merges None and []
* Detections.merge is much friendlier now. If there's a None
  or an empty array, it will merge it happily rather than
  complaining that everything needs to be either None or [].
* Data merge follows suit.
2024-05-07 17:30:12 +03:00
Linas Kondrackis fb49be8197 Tests for the new merge function 2024-05-07 16:09:05 +03:00
Piotr Skalski d7b6af84e6 Update README.md
fix errors in example commands
2024-05-07 12:25:41 +02:00
Piotr Skalski d3b845fea5 Merge pull request #1172 from ManzarIMalik/develop
Update README.md
2024-05-07 12:24:35 +02:00
Piotr Skalski 330eae4411 Merge pull request #1166 from tc360950/fix-inreasing-memory-consumption-in-bytetrack
Fix ever increasing memory consumption in ByteTrack
2024-05-07 12:20:18 +02:00
dependabot[bot] 4d25437459 ⬆️ Bump jinja2 from 3.1.3 to 3.1.4
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.3 to 3.1.4.
- [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.3...3.1.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 20:43:41 +00:00
pre-commit-ci[bot] 478fbd5751 chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.2 → v0.4.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.2...v0.4.3)
2024-05-06 17:48:31 +00:00
pre-commit-ci[bot] 7823e0de89 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-05-06 16:16:39 +00:00
SkalskiP ab0882dd55 initial version of VertexLabelAnnotator 2024-05-06 18:15:05 +02:00
SkalskiP ec6864ff04 roll back new draw_rounded_rectangle implementation 2024-05-06 18:00:13 +02:00
SkalskiP 81786ff9d3 Merge remote-tracking branch 'origin/vertex_label_annotator' into vertex_label_annotator 2024-05-06 17:40:43 +02:00
SkalskiP 95c243c058 better draw_rounded_rectangle implementation 2024-05-06 17:40:33 +02:00
pre-commit-ci[bot] 5c539c3bda fix(pre_commit): 🎨 auto format pre-commit hooks 2024-05-06 13:49:24 +00:00
SkalskiP 5d38120743 added from_xyxy and as_xyxy_int_tuple methods to Rect. added draw_rounded_rectangle function. 2024-05-06 15:39:59 +02:00
Manzar Iqbal Malik aaf5df2f6a Update README.md
Aligned draw zones file name for both examples, fixed file name for ultralytics example
2024-05-06 12:15:38 +01:00
Onuralp SEZER 447ef41fc4 Merge pull request #1171 from roboflow/dependabot/pip/ruff-0.4.3
⬆️ Bump ruff from 0.4.2 to 0.4.3
2024-05-06 12:41:10 +03:00
dependabot[bot] edd944fff8 ⬆️ Bump ruff from 0.4.2 to 0.4.3
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.4.2 to 0.4.3.
- [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.2...v0.4.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 01:18:43 +00:00
Onuralp SEZER a75efbbf73 Merge pull request #1170 from roboflow/dependabot/pip/mkdocstrings-0.25.1
⬆️ Bump mkdocstrings from 0.25.0 to 0.25.1
2024-05-06 04:16:35 +03:00
Onuralp SEZER e9c8a80823 Merge pull request #1167 from roboflow/dependabot/pip/mike-2.1.1
⬆️ Bump mike from 2.1.0 to 2.1.1
2024-05-06 04:16:01 +03:00
Onuralp SEZER 0b92417948 Merge pull request #1168 from roboflow/dependabot/pip/mkdocs-material-9.5.21
⬆️ Bump mkdocs-material from 9.5.20 to 9.5.21
2024-05-06 04:15:20 +03:00
dependabot[bot] aebbd01830 ⬆️ Bump mkdocstrings from 0.25.0 to 0.25.1
Bumps [mkdocstrings](https://github.com/mkdocstrings/mkdocstrings) from 0.25.0 to 0.25.1.
- [Release notes](https://github.com/mkdocstrings/mkdocstrings/releases)
- [Changelog](https://github.com/mkdocstrings/mkdocstrings/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mkdocstrings/mkdocstrings/compare/0.25.0...0.25.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 01:15:08 +00:00
Onuralp SEZER c3ebccabe8 Merge pull request #1169 from roboflow/dependabot/pip/jupytext-1.16.2
⬆️ Bump jupytext from 1.16.1 to 1.16.2
2024-05-06 04:14:43 +03:00
dependabot[bot] ff52352b00 ⬆️ Bump jupytext from 1.16.1 to 1.16.2
Bumps [jupytext](https://github.com/mwouts/jupytext) from 1.16.1 to 1.16.2.
- [Release notes](https://github.com/mwouts/jupytext/releases)
- [Changelog](https://github.com/mwouts/jupytext/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mwouts/jupytext/compare/v1.16.1...v1.16.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 01:12:45 +00:00
dependabot[bot] f7d21acd3a ⬆️ Bump mkdocs-material from 9.5.20 to 9.5.21
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.20 to 9.5.21.
- [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.20...9.5.21)

---
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-05-06 01:09:12 +00:00
dependabot[bot] 92d56eda15 ⬆️ Bump mike from 2.1.0 to 2.1.1
Bumps [mike](https://github.com/jimporter/mike) from 2.1.0 to 2.1.1.
- [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.0...v2.1.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 01:06:46 +00:00
tc360950 d37a5f90a4 Remove old tracklets from ByteTrack.removed_tracks collection 2024-05-05 17:16:05 +02:00
Onuralp SEZER 34a5b34c88 Merge pull request #1162 from roboflow/dependabot/pip/examples/speed_estimation/tqdm-4.66.3
Bump tqdm from 4.66.1 to 4.66.3 in /examples/speed_estimation
2024-05-04 01:26:51 +03:00
dependabot[bot] f72d06b05e Bump tqdm from 4.66.1 to 4.66.3 in /examples/speed_estimation
Bumps [tqdm](https://github.com/tqdm/tqdm) from 4.66.1 to 4.66.3.
- [Release notes](https://github.com/tqdm/tqdm/releases)
- [Commits](https://github.com/tqdm/tqdm/compare/v4.66.1...v4.66.3)

---
updated-dependencies:
- dependency-name: tqdm
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-03 21:51:32 +00:00
Onuralp SEZER 044a8fac06 Merge pull request #1160 from roboflow/dependabot/pip/mike-2.1.0
⬆️ Bump mike from 2.0.0 to 2.1.0
2024-05-03 04:04:46 +03:00
Onuralp SEZER a88089ced7 Merge pull request #1159 from roboflow/dependabot/pip/tqdm-4.66.4
⬆️ Bump tqdm from 4.66.2 to 4.66.4
2024-05-03 04:03:58 +03:00
dependabot[bot] 29b0c514cf ⬆️ Bump mike from 2.0.0 to 2.1.0
Bumps [mike](https://github.com/jimporter/mike) from 2.0.0 to 2.1.0.
- [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.0.0...v2.1.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-03 00:36:24 +00:00
dependabot[bot] 7d5a0bce45 ⬆️ Bump tqdm from 4.66.2 to 4.66.4
Bumps [tqdm](https://github.com/tqdm/tqdm) from 4.66.2 to 4.66.4.
- [Release notes](https://github.com/tqdm/tqdm/releases)
- [Commits](https://github.com/tqdm/tqdm/compare/v4.66.2...v4.66.4)

---
updated-dependencies:
- dependency-name: tqdm
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-03 00:33:20 +00:00
Piotr Skalski 177ab6ad4d Merge pull request #1138 from roboflow/feat/keypoints-from-yolo-nas
Implemented `from_yolo_nas` for keypoints
2024-05-02 16:11:09 +02:00
pre-commit-ci[bot] 7e79fa3d07 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-05-02 13:34:14 +00:00
Linas Kondrackis 347b75935b Ruff 2024-05-02 16:31:48 +03:00
Linas Kondrackis 079008cd17 from_yolo_nas - None class_id & names if not found 2024-05-02 16:30:01 +03:00
pre-commit-ci[bot] aab3ed2ba9 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-05-01 12:23:54 +00:00
Linas Kondrackis ba30244add Remove hardcoding of class_names, class_id.
* Still hardcoded ID to -1 and name to "" if not provided - this lets us
  stick to (n,) shape.
* Found no way to add class_names to pose tracker, so used the response
  of YOLO NAS detection instead, to check what the response looks like.
2024-05-01 15:20:50 +03:00
Onuralp SEZER 55e8f16029 Merge pull request #1158 from roboflow/dependabot/pip/mkdocs-git-revision-date-localized-plugin-1.2.5
⬆️ Bump mkdocs-git-revision-date-localized-plugin from 1.2.4 to 1.2.5
2024-05-01 03:46:57 +03:00
dependabot[bot] 33e69b2da3 ⬆️ Bump mkdocs-git-revision-date-localized-plugin
Bumps [mkdocs-git-revision-date-localized-plugin](https://github.com/timvink/mkdocs-git-revision-date-localized-plugin) from 1.2.4 to 1.2.5.
- [Release notes](https://github.com/timvink/mkdocs-git-revision-date-localized-plugin/releases)
- [Commits](https://github.com/timvink/mkdocs-git-revision-date-localized-plugin/compare/v1.2.4...v1.2.5)

---
updated-dependencies:
- dependency-name: mkdocs-git-revision-date-localized-plugin
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-01 00:35:55 +00:00
Piotr Skalski 9fe3170ead Merge pull request #1157 from roboflow/fix/pin_inference_version_in_exampels_to_9.17.0
pin inference version to 0.9.17
2024-04-30 16:09:07 +02:00
SkalskiP ffda94b1e5 pin inference version to 0.9.17 2024-04-30 16:06:02 +02:00
SkalskiP 7fd5d80a3b bump version from 0.21.0rc2 to 0.21.0rc3 2024-04-30 12:30:44 +02:00
Onuralp SEZER b67d9b475f Merge pull request #1155 from roboflow/dependabot/pip/nbconvert-7.16.4
⬆️ Bump nbconvert from 7.16.3 to 7.16.4
2024-04-30 13:24:17 +03:00
Onuralp SEZER d859842fa3 Merge pull request #1156 from roboflow/dependabot/pip/mkdocs-material-9.5.20
⬆️ Bump mkdocs-material from 9.5.19 to 9.5.20
2024-04-30 13:24:03 +03:00
Onuralp SEZER 131c6ffefb Merge pull request #1153 from roboflow/pre-commit-ci-update-config
chore(pre_commit): ⬆ pre_commit autoupdate
2024-04-30 13:23:49 +03:00
dependabot[bot] 71fd3178cc ⬆️ Bump mkdocs-material from 9.5.19 to 9.5.20
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.19 to 9.5.20.
- [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.19...9.5.20)

---
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-04-30 00:35:39 +00:00
dependabot[bot] 71919f1d6f ⬆️ Bump nbconvert from 7.16.3 to 7.16.4
Bumps [nbconvert](https://github.com/jupyter/nbconvert) from 7.16.3 to 7.16.4.
- [Release notes](https://github.com/jupyter/nbconvert/releases)
- [Changelog](https://github.com/jupyter/nbconvert/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jupyter/nbconvert/compare/v7.16.3...v7.16.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-30 00:33:04 +00:00
SkalskiP 4732b6f1d0 bump version from 0.21.0rc1 to 0.21.0rc2 2024-04-29 22:58:13 +02:00
Piotr Skalski c024b62020 Merge pull request #1154 from roboflow/fix/pin_inference_version_in_examples_to_fix_issues_related_to_braking_changes
pin inference version in examples
2024-04-29 21:50:37 +02:00
SkalskiP d4b1375db7 pin inference version 2024-04-29 21:46:07 +02:00
Piotr Skalski 71200a5942 Merge pull request #1147 from roboflow/feat/keypoints-from-inference
Add `from_inference` to KeyPoints
2024-04-29 20:40:17 +02:00
SkalskiP 66f6a86bbf add Roboflow API KEY to example 2024-04-29 20:37:56 +02:00
SkalskiP a70588284a add Roboflow API example 2024-04-29 20:36:05 +02:00
pre-commit-ci[bot] 67160c96b0 chore(pre_commit): ⬆ pre_commit autoupdate
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.1 → v0.4.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.1...v0.4.2)
2024-04-29 17:52:51 +00:00
LinasKo 7331577e69 keypoints, from_inference: removed class_name option 2024-04-29 13:37:37 +03:00
Onuralp SEZER 5f00d8b595 Merge pull request #1151 from roboflow/dependabot/pip/tox-4.15.0
⬆️ Bump tox from 4.14.2 to 4.15.0
2024-04-29 12:03:30 +03:00
Onuralp SEZER 88209f233d Merge pull request #1150 from roboflow/dependabot/pip/mkdocstrings-0.25.0
⬆️ Bump mkdocstrings from 0.24.3 to 0.25.0
2024-04-29 12:03:09 +03:00
Onuralp SEZER d51067164a Merge pull request #1149 from roboflow/dependabot/pip/pytest-8.2.0
⬆️ Bump pytest from 8.1.1 to 8.2.0
2024-04-29 12:02:46 +03:00
pre-commit-ci[bot] 0dd8d7dca8 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-04-29 06:53:36 +00:00
Linas Kondrackis 7c39d9dc8d Merge branch 'develop' into feat/keypoints-from-inference 2024-04-29 09:53:22 +03:00
Linas Kondrackis d2d69f986e from_infernece: Example suggests users find their own model
* Example incorrectly suggested yolov8s-640
* Replaced with "<POSE_MODEL_ID>" until we have a COCO human pose model
  on prod. I don't think "horse-pose/3" is helpful - the use case is
  uncommon and the training quality is poor.
2024-04-29 09:46:12 +03:00
dependabot[bot] df0b2df2f6 ⬆️ Bump tox from 4.14.2 to 4.15.0
Bumps [tox](https://github.com/tox-dev/tox) from 4.14.2 to 4.15.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.14.2...4.15.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-04-29 01:25:37 +00:00
dependabot[bot] 31844c165b ⬆️ Bump mkdocstrings from 0.24.3 to 0.25.0
Bumps [mkdocstrings](https://github.com/mkdocstrings/mkdocstrings) from 0.24.3 to 0.25.0.
- [Release notes](https://github.com/mkdocstrings/mkdocstrings/releases)
- [Changelog](https://github.com/mkdocstrings/mkdocstrings/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mkdocstrings/mkdocstrings/compare/0.24.3...0.25.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-29 01:24:07 +00:00
dependabot[bot] c5a2f87bb9 ⬆️ Bump pytest from 8.1.1 to 8.2.0
Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.1.1 to 8.2.0.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.1.1...8.2.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-29 01:20:56 +00:00
Piotr Skalski 6ef9ba7390 Merge pull request #1148 from roboflow/fix/code_examples_after_supervision_0.20.0_release
fix/code examples after supervision 0.20.0 release
2024-04-26 17:59:25 +02:00
SkalskiP a532670611 bump version from 0.20.0 to 0.21.0rc1 2024-04-26 17:55:34 +02:00
SkalskiP ba2d30d110 calculate_dynamic_line_thickness -> calculate_optimal_line_thickness
`calculate_dynamic_text_scale` -> `calculate_optimal_text_scale`
2024-04-26 17:36:50 +02:00
Linas Kondrackis 619da9575a pre-commit tidy-up 2024-04-26 17:55:17 +03:00
LinasKo 25bdb560eb from_inference, support direct inference results 2024-04-26 17:53:19 +03:00
Onuralp SEZER 9b447296bf Merge pull request #1142 from roboflow/dependabot/pip/ruff-0.4.2
⬆️ Bump ruff from 0.4.1 to 0.4.2
2024-04-26 03:42:20 +03:00
Onuralp SEZER 4a16d66bfb Merge pull request #1141 from roboflow/dependabot/pip/mkdocs-material-9.5.19
⬆️ Bump mkdocs-material from 9.5.18 to 9.5.19
2024-04-26 03:41:54 +03:00
dependabot[bot] 8436f52f8a ⬆️ Bump ruff from 0.4.1 to 0.4.2
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.4.1 to 0.4.2.
- [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.1...v0.4.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-26 00:33:10 +00:00
dependabot[bot] 059583e2ba ⬆️ Bump mkdocs-material from 9.5.18 to 9.5.19
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.18 to 9.5.19.
- [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.18...9.5.19)

---
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-04-26 00:31:25 +00:00
pre-commit-ci[bot] 4bd593fe3c fix(pre_commit): 🎨 auto format pre-commit hooks 2024-04-25 12:02:23 +00:00
Linas Kondrackis 399342513a Keypoints: rm NAS skeleton, improve docs examples 2024-04-25 15:00:01 +03:00
Linas Kondrackis c8c2ff5c45 Minor styling 2024-04-25 12:53:09 +03:00
Linas Kondrackis 18b3c17ed4 Styling 2024-04-25 12:42:33 +03:00
Linas Kondrackis cab06f2c2b Implementation of from_yolo_nas for keypoints
* Missing batch image support
2024-04-25 12:29:08 +03:00
Onuralp SEZER 2b53216a0b Merge pull request #1137 from roboflow/dependabot/pip/mypy-1.10.0
⬆️ Bump mypy from 1.9.0 to 1.10.0
2024-04-25 03:51:18 +03:00
dependabot[bot] e253f86e8f ⬆️ Bump mypy from 1.9.0 to 1.10.0
Bumps [mypy](https://github.com/python/mypy) from 1.9.0 to 1.10.0.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/1.9.0...v1.10.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-25 00:46:24 +00:00
Piotr Skalski df4c546c10 Merge pull request #1076 from rolson24/add-minimum_consecutive_frames-to-bytetrack-better
add minimum_consecutive_frames to bytetrack
2024-04-25 00:25:18 +02:00
Piotr Skalski 25fc1ab6fa Merge pull request #1115 from jeslinpjames/update-examples
Update examples to remove `frame_resloution_wh` in [PolygonZone]
2024-04-24 22:43:47 +02:00
SkalskiP a45341ce63 final cleanup; setting supervision>=0.20.0 in requirements.txt and refactoring stream processing scripts 2024-04-24 22:41:59 +02:00
Piotr Skalski e93ef041d4 Merge pull request #1133 from roboflow/main
Merge pull request #1127 from roboflow/develop
2024-04-24 22:22:42 +02:00
pre-commit-ci[bot] d531c59f85 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-04-13 05:39:05 +00:00
Jeslin P James 15b1473c2f Updated examples to remove frame_resolution_wh from PolygonZone 2024-04-13 10:28:11 +05:30
Jeslin P James 26c2a7ef9c updated examples 2024-04-13 10:18:30 +05:30
pre-commit-ci[bot] 361e444f53 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-04-11 13:27:43 +00:00
Raif Olson 07756328db Merge branch 'add-minimum_consecutive_frames-to-bytetrack-better' of https://github.com/rolson24/supervision into add-minimum_consecutive_frames-to-bytetrack-better 2024-04-11 09:27:28 -04:00
Raif Olson f98ded49a2 fix 2024-04-11 09:27:24 -04:00
pre-commit-ci[bot] bcb31f8809 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-04-11 13:21:51 +00:00
Raif Olson b1d5d4faf0 fix to exactly match existing release version when minimum_consecutive_frames = 1 2024-04-11 09:21:32 -04:00
Raif Olson 7924a13dc5 only assign external track_id once 2024-04-11 08:28:08 -04:00
pre-commit-ci[bot] 755f292ed1 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-04-11 12:21:46 +00:00
Raif Olson 6166d92458 fix reassigning track_id after a track is re-activated. 2024-04-11 08:21:26 -04:00
pre-commit-ci[bot] 2c9a1f69c5 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-04-10 17:57:46 -04:00
pre-commit-ci[bot] a2ab113f21 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-04-10 17:57:46 -04:00
Raif Olson eaff97f902 add external and internal track_ids to keep valid track ids sequential 2024-04-10 17:57:45 -04:00
Raif Olson ad0e6c3b51 Add minimum consecutive frames to ByteTrack 2024-03-29 16:59:29 -04:00
pre-commit-ci[bot] 27b61ab4a3 fix(pre_commit): 🎨 auto format pre-commit hooks 2024-01-22 23:31:31 +00:00
Seongjun Choi bddb72fb28 Modify: from_mmdetection add mask. See #703 2024-01-23 15:26:02 +09:00
Seongjun Choi 823dc56057 Modify: from_mmdetection add mask. See #703 2024-01-23 15:14:56 +09:00
36 changed files with 1116 additions and 308 deletions
+1 -1
View File
@@ -45,7 +45,7 @@ repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.1
rev: v0.4.3
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
+18
View File
@@ -65,8 +65,26 @@ status: new
:::supervision.detection.utils.move_boxes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.move_masks">move_masks</a></h2>
</div>
:::supervision.detection.utils.move_masks
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.scale_boxes">scale_boxes</a></h2>
</div>
:::supervision.detection.utils.scale_boxes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.clip_boxes">clip_boxes</a></h2>
</div>
:::supervision.detection.utils.clip_boxes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.pad_boxes">pad_boxes</a></h2>
</div>
:::supervision.detection.utils.pad_boxes
+68 -8
View File
@@ -6,7 +6,7 @@ status: new
# Detect Small Objects
This guide shows how to detect small objects
with the [Inference](https://github.com/roboflow/inference),
with the [Inference](https://github.com/roboflow/inference),
[Ultralytics](https://github.com/ultralytics/ultralytics) or
[Transformers](https://github.com/huggingface/transformers) packages using
[`InferenceSlicer`](/latest/detection/tools/inference_slicer/#supervision.detection.tools.inference_slicer.InferenceSlicer).
@@ -68,10 +68,10 @@ size relative to the image resolution.
import torch
import supervision as sv
from PIL import Image
from transformers import DetrImageProcessor, DetrForObjectDetection
from transformers import DetrImageProcessor, DetrForSegmentation
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
model = DetrForSegmentation.from_pretrained("facebook/detr-resnet-50")
image = Image.open(<SOURCE_IMAGE_PATH>)
inputs = processor(images=image, return_tensors="pt")
@@ -79,8 +79,8 @@ size relative to the image resolution.
with torch.no_grad():
outputs = model(**inputs)
width, height = image.size
target_size = torch.tensor([[height, width]])
width, height = image_slice.size
target_size = torch.tensor([[width, height]])
results = processor.post_process_object_detection(
outputs=outputs, target_sizes=target_size)[0]
detections = sv.Detections.from_transformers(results)
@@ -175,7 +175,7 @@ objects within each, and aggregating the results.
def callback(image_slice: np.ndarray) -> sv.Detections:
results = model.infer(image_slice)[0]
detections = sv.Detections.from_inference(results)
return sv.Detections.from_inference(results)
slicer = sv.InferenceSlicer(callback = callback)
detections = slicer(image)
@@ -239,8 +239,8 @@ objects within each, and aggregating the results.
with torch.no_grad():
outputs = model(**inputs)
width, height = image.size
target_size = torch.tensor([[height, width]])
width, height = image_slice.size
target_size = torch.tensor([[width, height]])
results = processor.post_process_object_detection(
outputs=outputs, target_sizes=target_size)[0]
return sv.Detections.from_transformers(results)
@@ -264,3 +264,63 @@ objects within each, and aggregating the results.
```
![detection-with-inference-slicer](https://media.roboflow.com/supervision_detect_small_objects_example_3.png)
## Small Object Segmentation
[`InferenceSlicer`](/latest/detection/tools/inference_slicer/#supervision.detection.tools.inference_slicer.InferenceSlicer) can perform segmentation tasks too.
=== "Inference"
```{ .py hl_lines="6 16 19-20" }
import cv2
import numpy as np
import supervision as sv
from inference import get_model
model = get_model(model_id="yolov8x-seg-640")
image = cv2.imread(<SOURCE_IMAGE_PATH>)
def callback(image_slice: np.ndarray) -> sv.Detections:
results = model.infer(image_slice)[0]
detections = sv.Detections.from_inference(results)
slicer = sv.InferenceSlicer(callback = callback)
detections = slicer(image)
mask_annotator = sv.MaskAnnotator()
label_annotator = sv.LabelAnnotator()
annotated_image = mask_annotator.annotate(
scene=image, detections=detections)
annotated_image = label_annotator.annotate(
scene=annotated_image, detections=detections)
```
=== "Ultralytics"
```{ .py hl_lines="6 16 19-20" }
import cv2
import numpy as np
import supervision as sv
from ultralytics import YOLO
model = YOLO("yolov8x-seg.pt")
image = cv2.imread(<SOURCE_IMAGE_PATH>)
def callback(image_slice: np.ndarray) -> sv.Detections:
result = model(image_slice)[0]
return sv.Detections.from_ultralytics(result)
slicer = sv.InferenceSlicer(callback = callback)
detections = slicer(image)
mask_annotator = sv.MaskAnnotator()
label_annotator = sv.LabelAnnotator()
annotated_image = mask_annotator.annotate(
scene=image, detections=detections)
annotated_image = label_annotator.annotate(
scene=annotated_image, detections=detections)
```
![detection-with-inference-slicer](https://media.roboflow.com/supervision-docs/inference-slicer-segmentation-example.png)
+39 -2
View File
@@ -13,7 +13,10 @@ status: new
image = ...
key_points = sv.KeyPoints(...)
vertex_annotator = sv.VertexAnnotator(color=sv.Color.GREEN, radius=10)
vertex_annotator = sv.VertexAnnotator(
color=sv.Color.GREEN,
radius=10
)
annotated_frame = vertex_annotator.annotate(
scene=image.copy(),
key_points=key_points
@@ -34,7 +37,10 @@ status: new
image = ...
key_points = sv.KeyPoints(...)
edge_annotator = sv.EdgeAnnotator(color=sv.Color.GREEN, thickness=5)
edge_annotator = sv.EdgeAnnotator(
color=sv.Color.GREEN,
thickness=5
)
annotated_frame = edge_annotator.annotate(
scene=image.copy(),
key_points=key_points
@@ -47,6 +53,31 @@ status: new
</div>
=== "VertexLabelAnnotator"
```python
import supervision as sv
image = ...
key_points = sv.KeyPoints(...)
vertex_label_annotator = sv.VertexLabelAnnotator(
color=sv.Color.GREEN,
text_color=sv.Color.BLACK,
border_radius=5
)
annotated_frame = vertex_label_annotator.annotate(
scene=image.copy(),
key_points=key_points
)
```
<div class="result" markdown>
![vertex-label-annotator-example](https://media.roboflow.com/supervision-annotator-examples/vertex-label-annotator-example.png){ align=center width="800" }
</div>
<div class="md-typeset">
<h2><a href="#supervision.keypoint.annotators.VertexAnnotator">VertexAnnotator</a></h2>
</div>
@@ -58,3 +89,9 @@ status: new
</div>
:::supervision.keypoint.annotators.EdgeAnnotator
<div class="md-typeset">
<h2><a href="#supervision.keypoint.annotators.VertexLabelAnnotator">VertexLabelAnnotator</a></h2>
</div>
:::supervision.keypoint.annotators.VertexLabelAnnotator
@@ -38,15 +38,15 @@ def initiate_annotators(
) -> Tuple[
List[sv.PolygonZone], List[sv.PolygonZoneAnnotator], List[sv.BoundingBoxAnnotator]
]:
line_thickness = sv.calculate_dynamic_line_thickness(resolution_wh=resolution_wh)
text_scale = sv.calculate_dynamic_text_scale(resolution_wh=resolution_wh)
line_thickness = sv.calculate_optimal_line_thickness(resolution_wh=resolution_wh)
text_scale = sv.calculate_optimal_text_scale(resolution_wh=resolution_wh)
zones = []
zone_annotators = []
box_annotators = []
for index, polygon in enumerate(polygons):
zone = sv.PolygonZone(polygon=polygon, frame_resolution_wh=resolution_wh)
zone = sv.PolygonZone(polygon=polygon)
zone_annotator = sv.PolygonZoneAnnotator(
zone=zone,
color=COLORS.by_idx(index),
@@ -1,5 +1,5 @@
gdown
inference
supervision==0.19.0
inference==0.9.17
supervision>=0.20.0
tqdm
ultralytics
@@ -36,15 +36,15 @@ def initiate_annotators(
) -> Tuple[
List[sv.PolygonZone], List[sv.PolygonZoneAnnotator], List[sv.BoundingBoxAnnotator]
]:
line_thickness = sv.calculate_dynamic_line_thickness(resolution_wh=resolution_wh)
text_scale = sv.calculate_dynamic_text_scale(resolution_wh=resolution_wh)
line_thickness = sv.calculate_optimal_line_thickness(resolution_wh=resolution_wh)
text_scale = sv.calculate_optimal_text_scale(resolution_wh=resolution_wh)
zones = []
zone_annotators = []
box_annotators = []
for index, polygon in enumerate(polygons):
zone = sv.PolygonZone(polygon=polygon, frame_resolution_wh=resolution_wh)
zone = sv.PolygonZone(polygon=polygon)
zone_annotator = sv.PolygonZoneAnnotator(
zone=zone,
color=COLORS.by_idx(index),
@@ -98,10 +98,10 @@ if __name__ == "__main__":
frame_rate=video_info.fps, track_thresh=args.confidence_threshold
)
thickness = sv.calculate_dynamic_line_thickness(
thickness = sv.calculate_optimal_line_thickness(
resolution_wh=video_info.resolution_wh
)
text_scale = sv.calculate_dynamic_text_scale(resolution_wh=video_info.resolution_wh)
text_scale = sv.calculate_optimal_text_scale(resolution_wh=video_info.resolution_wh)
bounding_box_annotator = sv.BoundingBoxAnnotator(thickness=thickness)
label_annotator = sv.LabelAnnotator(
text_scale=text_scale,
@@ -116,9 +116,7 @@ if __name__ == "__main__":
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
)
polygon_zone = sv.PolygonZone(polygon=SOURCE)
view_transformer = ViewTransformer(source=SOURCE, target=TARGET)
coordinates = defaultdict(lambda: deque(maxlen=video_info.fps))
+3 -3
View File
@@ -1,6 +1,6 @@
supervision==0.19.0
tqdm==4.66.1
supervision>=0.20.0
tqdm==4.66.3
requests
ultralytics==8.0.237
super-gradients==3.5.0
inference==0.9.8
inference==0.9.17
@@ -76,10 +76,10 @@ if __name__ == "__main__":
frame_rate=video_info.fps, track_thresh=args.confidence_threshold
)
thickness = sv.calculate_dynamic_line_thickness(
thickness = sv.calculate_optimal_line_thickness(
resolution_wh=video_info.resolution_wh
)
text_scale = sv.calculate_dynamic_text_scale(resolution_wh=video_info.resolution_wh)
text_scale = sv.calculate_optimal_text_scale(resolution_wh=video_info.resolution_wh)
bounding_box_annotator = sv.BoundingBoxAnnotator(thickness=thickness)
label_annotator = sv.LabelAnnotator(
text_scale=text_scale,
@@ -94,9 +94,7 @@ if __name__ == "__main__":
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
)
polygon_zone = sv.PolygonZone(polygon=SOURCE)
view_transformer = ViewTransformer(source=SOURCE, target=TARGET)
coordinates = defaultdict(lambda: deque(maxlen=video_info.fps))
@@ -77,10 +77,10 @@ if __name__ == "__main__":
frame_rate=video_info.fps, track_thresh=args.confidence_threshold
)
thickness = sv.calculate_dynamic_line_thickness(
thickness = sv.calculate_optimal_line_thickness(
resolution_wh=video_info.resolution_wh
)
text_scale = sv.calculate_dynamic_text_scale(resolution_wh=video_info.resolution_wh)
text_scale = sv.calculate_optimal_text_scale(resolution_wh=video_info.resolution_wh)
bounding_box_annotator = sv.BoundingBoxAnnotator(thickness=thickness)
label_annotator = sv.LabelAnnotator(
text_scale=text_scale,
@@ -95,9 +95,7 @@ if __name__ == "__main__":
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
)
polygon_zone = sv.PolygonZone(polygon=SOURCE)
view_transformer = ViewTransformer(source=SOURCE, target=TARGET)
coordinates = defaultdict(lambda: deque(maxlen=video_info.fps))
+7 -7
View File
@@ -103,7 +103,7 @@ python scripts/draw_zones.py \
```bash
python scripts/draw_zones.py \
--source_path "data/traffic/video.mp4" \
--zone_configuration_path "data/traffic/custom_config.json"
--zone_configuration_path "data/traffic/config.json"
```
https://github.com/roboflow/supervision/assets/26109316/9d514c9e-2a61-418b-ae49-6ac1ad6ae5ac
@@ -157,7 +157,7 @@ Script to run object detection on a video stream using the Roboflow Inference mo
- `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`.
```bash
python inference_file_example.py \
python inference_stream_example.py \
--zone_configuration_path "data/checkout/config.json" \
--rtsp_url "rtsp://localhost:8554/live0.stream" \
--model_id "yolov8x-640" \
@@ -167,7 +167,7 @@ python inference_file_example.py \
```
```bash
python inference_file_example.py \
python inference_stream_example.py \
--zone_configuration_path "data/traffic/config.json" \
--rtsp_url "rtsp://localhost:8554/live0.stream" \
--model_id "yolov8x-640" \
@@ -192,7 +192,7 @@ Script to run object detection on a video file using the Ultralytics YOLOv8 mode
- `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`.
```bash
python inference_file_example.py \
python ultralytics_file_example.py \
--zone_configuration_path "data/checkout/config.json" \
--source_video_path "data/checkout/video.mp4" \
--weights "yolov8x.pt" \
@@ -203,7 +203,7 @@ python inference_file_example.py \
```
```bash
python inference_file_example.py \
python ultralytics_file_example.py \
--zone_configuration_path "data/traffic/config.json" \
--source_video_path "data/traffic/video.mp4" \
--weights "yolov8x.pt" \
@@ -226,7 +226,7 @@ Script to run object detection on a video stream using the Ultralytics YOLOv8 mo
- `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`.
```bash
python inference_file_example.py \
python ultralytics_stream_example.py \
--zone_configuration_path "data/checkout/config.json" \
--rtsp_url "rtsp://localhost:8554/live0.stream" \
--weights "yolov8x.pt" \
@@ -237,7 +237,7 @@ python inference_file_example.py \
```
```bash
python inference_file_example.py \
python ultralytics_stream_example.py \
--zone_configuration_path "data/traffic/config.json" \
--rtsp_url "rtsp://localhost:8554/live0.stream" \
--weights "yolov8x.pt" \
@@ -29,14 +29,10 @@ def main(
video_info = sv.VideoInfo.from_video_path(video_path=source_video_path)
frames_generator = sv.get_video_frames_generator(source_video_path)
frame = next(frames_generator)
resolution_wh = frame.shape[1], frame.shape[0]
polygons = load_zones_config(file_path=zone_configuration_path)
zones = [
sv.PolygonZone(
polygon=polygon,
frame_resolution_wh=resolution_wh,
triggering_anchors=(sv.Position.CENTER,),
)
for polygon in polygons
@@ -29,14 +29,10 @@ def main(
frames_generator = get_stream_frames_generator(rtsp_url=rtsp_url)
fps_monitor = sv.FPSMonitor()
frame = next(frames_generator)
resolution_wh = frame.shape[1], frame.shape[0]
polygons = load_zones_config(file_path=zone_configuration_path)
zones = [
sv.PolygonZone(
polygon=polygon,
frame_resolution_wh=resolution_wh,
triggering_anchors=(sv.Position.CENTER,),
)
for polygon in polygons
@@ -24,20 +24,15 @@ class CustomSink:
self.fps_monitor = sv.FPSMonitor()
self.polygons = load_zones_config(file_path=zone_configuration_path)
self.timers = [ClockBasedTimer() for _ in self.polygons]
self.zones = None
self.zones = [
sv.PolygonZone(
polygon=polygon,
triggering_anchors=(sv.Position.CENTER,),
)
for polygon in self.polygons
]
def on_prediction(self, result: dict, frame: VideoFrame) -> None:
if self.zones is None:
resolution_wh = frame.image.shape[1], frame.image.shape[0]
self.zones = [
sv.PolygonZone(
polygon=polygon,
frame_resolution_wh=resolution_wh,
triggering_anchors=(sv.Position.CENTER,),
)
for polygon in self.polygons
]
self.fps_monitor.tick()
fps = self.fps_monitor.fps
+2 -2
View File
@@ -1,5 +1,5 @@
opencv-python
supervision
supervision>=0.20.0
ultralytics
inference
inference==0.9.17
pytube
@@ -30,14 +30,10 @@ def main(
video_info = sv.VideoInfo.from_video_path(video_path=source_video_path)
frames_generator = sv.get_video_frames_generator(source_video_path)
frame = next(frames_generator)
resolution_wh = frame.shape[1], frame.shape[0]
polygons = load_zones_config(file_path=zone_configuration_path)
zones = [
sv.PolygonZone(
polygon=polygon,
frame_resolution_wh=resolution_wh,
triggering_anchors=(sv.Position.CENTER,),
)
for polygon in polygons
@@ -30,14 +30,10 @@ def main(
frames_generator = get_stream_frames_generator(rtsp_url=rtsp_url)
fps_monitor = sv.FPSMonitor()
frame = next(frames_generator)
resolution_wh = frame.shape[1], frame.shape[0]
polygons = load_zones_config(file_path=zone_configuration_path)
zones = [
sv.PolygonZone(
polygon=polygon,
frame_resolution_wh=resolution_wh,
triggering_anchors=(sv.Position.CENTER,),
)
for polygon in polygons
@@ -25,20 +25,15 @@ class CustomSink:
self.fps_monitor = sv.FPSMonitor()
self.polygons = load_zones_config(file_path=zone_configuration_path)
self.timers = [ClockBasedTimer() for _ in self.polygons]
self.zones = None
self.zones = [
sv.PolygonZone(
polygon=polygon,
triggering_anchors=(sv.Position.CENTER,),
)
for polygon in self.polygons
]
def on_prediction(self, detections: sv.Detections, frame: VideoFrame) -> None:
if self.zones is None:
resolution_wh = frame.image.shape[1], frame.image.shape[0]
self.zones = [
sv.PolygonZone(
polygon=polygon,
frame_resolution_wh=resolution_wh,
triggering_anchors=(sv.Position.CENTER,),
)
for polygon in self.polygons
]
self.fps_monitor.tick()
fps = self.fps_monitor.fps
+1 -1
View File
@@ -1,4 +1,4 @@
inference
inference==0.9.17
supervision==0.19.0
tqdm
ultralytics
@@ -1,6 +1,6 @@
import argparse
import os
from typing import Dict, Iterable, List, Set, Tuple
from typing import Dict, Iterable, List, Set
import cv2
import numpy as np
@@ -60,13 +60,11 @@ class DetectionsManager:
def initiate_polygon_zones(
polygons: List[np.ndarray],
frame_resolution_wh: Tuple[int, int],
triggering_anchors: Iterable[sv.Position] = [sv.Position.CENTER],
) -> List[sv.PolygonZone]:
return [
sv.PolygonZone(
polygon=polygon,
frame_resolution_wh=frame_resolution_wh,
triggering_anchors=triggering_anchors,
)
for polygon in polygons
@@ -92,12 +90,8 @@ class VideoProcessor:
self.tracker = sv.ByteTrack()
self.video_info = sv.VideoInfo.from_video_path(source_video_path)
self.zones_in = initiate_polygon_zones(
ZONE_IN_POLYGONS, self.video_info.resolution_wh, [sv.Position.CENTER]
)
self.zones_out = initiate_polygon_zones(
ZONE_OUT_POLYGONS, self.video_info.resolution_wh, [sv.Position.CENTER]
)
self.zones_in = initiate_polygon_zones(ZONE_IN_POLYGONS, [sv.Position.CENTER])
self.zones_out = initiate_polygon_zones(ZONE_OUT_POLYGONS, [sv.Position.CENTER])
self.bounding_box_annotator = sv.BoundingBoxAnnotator(color=COLORS)
self.label_annotator = sv.LabelAnnotator(
+2 -2
View File
@@ -1,5 +1,5 @@
gdown
inference
supervision>=0.19.0
inference==0.9.17
supervision>=0.20.0
tqdm
ultralytics
@@ -1,5 +1,5 @@
import argparse
from typing import Dict, Iterable, List, Set, Tuple
from typing import Dict, Iterable, List, Set
import cv2
import numpy as np
@@ -58,13 +58,11 @@ class DetectionsManager:
def initiate_polygon_zones(
polygons: List[np.ndarray],
frame_resolution_wh: Tuple[int, int],
triggering_anchors: Iterable[sv.Position] = [sv.Position.CENTER],
) -> List[sv.PolygonZone]:
return [
sv.PolygonZone(
polygon=polygon,
frame_resolution_wh=frame_resolution_wh,
triggering_anchors=triggering_anchors,
)
for polygon in polygons
@@ -89,12 +87,8 @@ class VideoProcessor:
self.tracker = sv.ByteTrack()
self.video_info = sv.VideoInfo.from_video_path(source_video_path)
self.zones_in = initiate_polygon_zones(
ZONE_IN_POLYGONS, self.video_info.resolution_wh, [sv.Position.CENTER]
)
self.zones_out = initiate_polygon_zones(
ZONE_OUT_POLYGONS, self.video_info.resolution_wh, [sv.Position.CENTER]
)
self.zones_in = initiate_polygon_zones(ZONE_IN_POLYGONS, [sv.Position.CENTER])
self.zones_out = initiate_polygon_zones(ZONE_OUT_POLYGONS, [sv.Position.CENTER])
self.bounding_box_annotator = sv.BoundingBoxAnnotator(color=COLORS)
self.label_annotator = sv.LabelAnnotator(
Generated
+117 -110
View File
@@ -1340,13 +1340,13 @@ trio = ["async_generator", "trio"]
[[package]]
name = "jinja2"
version = "3.1.3"
version = "3.1.4"
description = "A very fast and expressive template engine."
optional = false
python-versions = ">=3.7"
files = [
{file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"},
{file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"},
{file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"},
{file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"},
]
[package.dependencies]
@@ -1648,13 +1648,13 @@ files = [
[[package]]
name = "jupytext"
version = "1.16.1"
version = "1.16.2"
description = "Jupyter notebooks as Markdown documents, Julia, Python or R scripts"
optional = false
python-versions = ">=3.8"
files = [
{file = "jupytext-1.16.1-py3-none-any.whl", hash = "sha256:796ec4f68ada663569e5d38d4ef03738a01284bfe21c943c485bc36433898bd0"},
{file = "jupytext-1.16.1.tar.gz", hash = "sha256:68c7b68685e870e80e60fda8286fbd6269e9c74dc1df4316df6fe46eabc94c99"},
{file = "jupytext-1.16.2-py3-none-any.whl", hash = "sha256:197a43fef31dca612b68b311e01b8abd54441c7e637810b16b6cb8f2ab66065e"},
{file = "jupytext-1.16.2.tar.gz", hash = "sha256:8627dd9becbbebd79cc4a4ed4727d89d78e606b4b464eab72357b3b029023a14"},
]
[package.dependencies]
@@ -1663,16 +1663,16 @@ mdit-py-plugins = "*"
nbformat = "*"
packaging = "*"
pyyaml = "*"
toml = "*"
tomli = {version = "*", markers = "python_version < \"3.11\""}
[package.extras]
dev = ["jupytext[test-cov,test-external]"]
dev = ["autopep8", "black", "flake8", "gitpython", "ipykernel", "isort", "jupyter-fs (<0.4.0)", "jupyter-server (!=2.11)", "nbconvert", "pre-commit", "pytest", "pytest-cov (>=2.6.1)", "pytest-randomly", "pytest-xdist", "sphinx-gallery (<0.8)"]
docs = ["myst-parser", "sphinx", "sphinx-copybutton", "sphinx-rtd-theme"]
test = ["pytest", "pytest-randomly", "pytest-xdist"]
test-cov = ["jupytext[test-integration]", "pytest-cov (>=2.6.1)"]
test-external = ["autopep8", "black", "flake8", "gitpython", "isort", "jupyter-fs (<0.4.0)", "jupytext[test-integration]", "pre-commit", "sphinx-gallery (<0.8)"]
test-functional = ["jupytext[test]"]
test-integration = ["ipykernel", "jupyter-server (!=2.11)", "jupytext[test-functional]", "nbconvert"]
test-cov = ["ipykernel", "jupyter-server (!=2.11)", "nbconvert", "pytest", "pytest-cov (>=2.6.1)", "pytest-randomly", "pytest-xdist"]
test-external = ["autopep8", "black", "flake8", "gitpython", "ipykernel", "isort", "jupyter-fs (<0.4.0)", "jupyter-server (!=2.11)", "nbconvert", "pre-commit", "pytest", "pytest-randomly", "pytest-xdist", "sphinx-gallery (<0.8)"]
test-functional = ["pytest", "pytest-randomly", "pytest-xdist"]
test-integration = ["ipykernel", "jupyter-server (!=2.11)", "nbconvert", "pytest", "pytest-randomly", "pytest-xdist"]
test-ui = ["calysto-bash"]
[[package]]
@@ -2048,13 +2048,13 @@ files = [
[[package]]
name = "mike"
version = "2.0.0"
version = "2.1.1"
description = "Manage multiple versions of your MkDocs-powered documentation"
optional = false
python-versions = "*"
files = [
{file = "mike-2.0.0-py3-none-any.whl", hash = "sha256:87f496a65900f93ba92d72940242b65c86f3f2f82871bc60ebdcffc91fad1d9e"},
{file = "mike-2.0.0.tar.gz", hash = "sha256:566f1cab1a58cc50b106fb79ea2f1f56e7bfc8b25a051e95e6eaee9fba0922de"},
{file = "mike-2.1.1-py3-none-any.whl", hash = "sha256:0b1d01a397a423284593eeb1b5f3194e37169488f929b860c9bfe95c0d5efb79"},
{file = "mike-2.1.1.tar.gz", hash = "sha256:f39ed39f3737da83ad0adc33e9f885092ed27f8c9e7ff0523add0480352a2c22"},
]
[package.dependencies]
@@ -2064,6 +2064,7 @@ jinja2 = ">=2.7"
mkdocs = ">=1.0"
pyparsing = ">=3.0"
pyyaml = ">=5.1"
pyyaml-env-tag = "*"
verspec = "*"
[package.extras]
@@ -2083,34 +2084,34 @@ files = [
[[package]]
name = "mkdocs"
version = "1.5.3"
version = "1.6.0"
description = "Project documentation with Markdown."
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "mkdocs-1.5.3-py3-none-any.whl", hash = "sha256:3b3a78e736b31158d64dbb2f8ba29bd46a379d0c6e324c2246c3bc3d2189cfc1"},
{file = "mkdocs-1.5.3.tar.gz", hash = "sha256:eb7c99214dcb945313ba30426c2451b735992c73c2e10838f76d09e39ff4d0e2"},
{file = "mkdocs-1.6.0-py3-none-any.whl", hash = "sha256:1eb5cb7676b7d89323e62b56235010216319217d4af5ddc543a91beb8d125ea7"},
{file = "mkdocs-1.6.0.tar.gz", hash = "sha256:a73f735824ef83a4f3bcb7a231dcab23f5a838f88b7efc54a0eef5fbdbc3c512"},
]
[package.dependencies]
click = ">=7.0"
colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""}
ghp-import = ">=1.0"
importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""}
importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
jinja2 = ">=2.11.1"
markdown = ">=3.2.1"
markdown = ">=3.3.6"
markupsafe = ">=2.0.1"
mergedeep = ">=1.3.4"
mkdocs-get-deps = ">=0.2.0"
packaging = ">=20.5"
pathspec = ">=0.11.1"
platformdirs = ">=2.2.0"
pyyaml = ">=5.1"
pyyaml-env-tag = ">=0.1"
watchdog = ">=2.0"
[package.extras]
i18n = ["babel (>=2.9.0)"]
min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.3)", "jinja2 (==2.11.1)", "markdown (==3.2.1)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "packaging (==20.5)", "pathspec (==0.11.1)", "platformdirs (==2.2.0)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "typing-extensions (==3.10)", "watchdog (==2.0)"]
min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"]
[[package]]
name = "mkdocs-autorefs"
@@ -2127,6 +2128,23 @@ files = [
Markdown = ">=3.3"
mkdocs = ">=1.1"
[[package]]
name = "mkdocs-get-deps"
version = "0.2.0"
description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file"
optional = false
python-versions = ">=3.8"
files = [
{file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"},
{file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"},
]
[package.dependencies]
importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""}
mergedeep = ">=1.3.4"
platformdirs = ">=2.2.0"
pyyaml = ">=5.1"
[[package]]
name = "mkdocs-git-committers-plugin-2"
version = "2.3.0"
@@ -2145,13 +2163,13 @@ requests = "*"
[[package]]
name = "mkdocs-git-revision-date-localized-plugin"
version = "1.2.4"
version = "1.2.5"
description = "Mkdocs plugin that enables displaying the localized date of the last git modification of a markdown file."
optional = false
python-versions = ">=3.8"
files = [
{file = "mkdocs-git-revision-date-localized-plugin-1.2.4.tar.gz", hash = "sha256:08fd0c6f33c8da9e00daf40f7865943113b3879a1c621b2bbf0fa794ffe997d3"},
{file = "mkdocs_git_revision_date_localized_plugin-1.2.4-py3-none-any.whl", hash = "sha256:1f94eb510862ef94e982a2910404fa17a1657ecf29f45a07b0f438c00767fc85"},
{file = "mkdocs_git_revision_date_localized_plugin-1.2.5-py3-none-any.whl", hash = "sha256:d796a18b07cfcdb154c133e3ec099d2bb5f38389e4fd54d3eb516a8a736815b8"},
{file = "mkdocs_git_revision_date_localized_plugin-1.2.5.tar.gz", hash = "sha256:0c439816d9d0dba48e027d9d074b2b9f1d7cd179f74ba46b51e4da7bb3dc4b9b"},
]
[package.dependencies]
@@ -2180,13 +2198,13 @@ pygments = ">2.12.0"
[[package]]
name = "mkdocs-material"
version = "9.5.18"
version = "9.5.22"
description = "Documentation that simply works"
optional = false
python-versions = ">=3.8"
files = [
{file = "mkdocs_material-9.5.18-py3-none-any.whl", hash = "sha256:1e0e27fc9fe239f9064318acf548771a4629d5fd5dfd45444fd80a953fe21eb4"},
{file = "mkdocs_material-9.5.18.tar.gz", hash = "sha256:a43f470947053fa2405c33995f282d24992c752a50114f23f30da9d8d0c57e62"},
{file = "mkdocs_material-9.5.22-py3-none-any.whl", hash = "sha256:8c7a377d323567934e6cd46915e64dc209efceaec0dec1cf2202184f5649862c"},
{file = "mkdocs_material-9.5.22.tar.gz", hash = "sha256:22a853a456ae8c581c4628159574d6fc7c71b2c7569dc9c3a82cc70432219599"},
]
[package.dependencies]
@@ -2195,7 +2213,7 @@ cairosvg = {version = ">=2.6,<3.0", optional = true, markers = "extra == \"imagi
colorama = ">=0.4,<1.0"
jinja2 = ">=3.0,<4.0"
markdown = ">=3.2,<4.0"
mkdocs = ">=1.5.3,<1.6.0"
mkdocs = ">=1.6,<2.0"
mkdocs-material-extensions = ">=1.3,<2.0"
paginate = ">=0.5,<1.0"
pillow = {version = ">=10.2,<11.0", optional = true, markers = "extra == \"imaging\""}
@@ -2222,13 +2240,13 @@ files = [
[[package]]
name = "mkdocstrings"
version = "0.24.3"
version = "0.25.1"
description = "Automatic documentation from sources, for MkDocs."
optional = false
python-versions = ">=3.8"
files = [
{file = "mkdocstrings-0.24.3-py3-none-any.whl", hash = "sha256:5c9cf2a32958cd161d5428699b79c8b0988856b0d4a8c5baf8395fc1bf4087c3"},
{file = "mkdocstrings-0.24.3.tar.gz", hash = "sha256:f327b234eb8d2551a306735436e157d0a22d45f79963c60a8b585d5f7a94c1d2"},
{file = "mkdocstrings-0.25.1-py3-none-any.whl", hash = "sha256:da01fcc2670ad61888e8fe5b60afe9fee5781017d67431996832d63e887c2e51"},
{file = "mkdocstrings-0.25.1.tar.gz", hash = "sha256:c3a2515f31577f311a9ee58d089e4c51fc6046dbd9e9b4c3de4c3194667fe9bf"},
]
[package.dependencies]
@@ -2277,38 +2295,38 @@ files = [
[[package]]
name = "mypy"
version = "1.9.0"
version = "1.10.0"
description = "Optional static typing for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"},
{file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"},
{file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"},
{file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"},
{file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"},
{file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"},
{file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"},
{file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"},
{file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"},
{file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"},
{file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"},
{file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"},
{file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"},
{file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"},
{file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"},
{file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"},
{file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"},
{file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"},
{file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"},
{file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"},
{file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"},
{file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"},
{file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"},
{file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"},
{file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"},
{file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"},
{file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"},
{file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"},
{file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"},
{file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"},
{file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"},
{file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"},
{file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"},
{file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"},
{file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"},
{file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"},
{file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"},
{file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"},
{file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"},
{file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"},
{file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"},
{file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"},
{file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"},
{file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"},
{file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"},
{file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"},
{file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"},
{file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"},
{file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"},
{file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"},
{file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"},
{file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"},
{file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"},
{file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"},
]
[package.dependencies]
@@ -2357,13 +2375,13 @@ test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=
[[package]]
name = "nbconvert"
version = "7.16.3"
version = "7.16.4"
description = "Converting Jupyter Notebooks (.ipynb files) to other formats. Output formats include asciidoc, html, latex, markdown, pdf, py, rst, script. nbconvert can be used both as a Python library (`import nbconvert`) or as a command line tool (invoked as `jupyter nbconvert ...`)."
optional = false
python-versions = ">=3.8"
files = [
{file = "nbconvert-7.16.3-py3-none-any.whl", hash = "sha256:ddeff14beeeedf3dd0bc506623e41e4507e551736de59df69a91f86700292b3b"},
{file = "nbconvert-7.16.3.tar.gz", hash = "sha256:a6733b78ce3d47c3f85e504998495b07e6ea9cf9bf6ec1c98dda63ec6ad19142"},
{file = "nbconvert-7.16.4-py3-none-any.whl", hash = "sha256:05873c620fe520b6322bf8a5ad562692343fe3452abda5765c7a34b7d1aa3eb3"},
{file = "nbconvert-7.16.4.tar.gz", hash = "sha256:86ca91ba266b0a448dc96fa6c5b9d98affabde2867b363258703536807f9f7f4"},
]
[package.dependencies]
@@ -2385,9 +2403,9 @@ tinycss2 = "*"
traitlets = ">=5.1"
[package.extras]
all = ["nbconvert[docs,qtpdf,serve,test,webpdf]"]
all = ["flaky", "ipykernel", "ipython", "ipywidgets (>=7.5)", "myst-parser", "nbsphinx (>=0.2.12)", "playwright", "pydata-sphinx-theme", "pyqtwebengine (>=5.15)", "pytest (>=7)", "sphinx (==5.0.2)", "sphinxcontrib-spelling", "tornado (>=6.1)"]
docs = ["ipykernel", "ipython", "myst-parser", "nbsphinx (>=0.2.12)", "pydata-sphinx-theme", "sphinx (==5.0.2)", "sphinxcontrib-spelling"]
qtpdf = ["nbconvert[qtpng]"]
qtpdf = ["pyqtwebengine (>=5.15)"]
qtpng = ["pyqtwebengine (>=5.15)"]
serve = ["tornado (>=6.1)"]
test = ["flaky", "ipykernel", "ipywidgets (>=7.5)", "pytest (>=7)"]
@@ -2815,13 +2833,13 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-
[[package]]
name = "pluggy"
version = "1.4.0"
version = "1.5.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.8"
files = [
{file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"},
{file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"},
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
]
[package.extras]
@@ -3020,13 +3038,13 @@ tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
[[package]]
name = "pytest"
version = "8.1.1"
version = "8.2.0"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"},
{file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"},
{file = "pytest-8.2.0-py3-none-any.whl", hash = "sha256:1733f0620f6cda4095bbf0d9ff8022486e91892245bb9e7d5542c018f612f233"},
{file = "pytest-8.2.0.tar.gz", hash = "sha256:d507d4482197eac0ba2bae2e9babf0672eb333017bcedaa5fb1a3d42c1174b3f"},
]
[package.dependencies]
@@ -3034,11 +3052,11 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""}
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=1.4,<2.0"
pluggy = ">=1.5,<2.0"
tomli = {version = ">=1", markers = "python_version < \"3.11\""}
[package.extras]
testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
[[package]]
name = "python-dateutil"
@@ -3643,28 +3661,28 @@ files = [
[[package]]
name = "ruff"
version = "0.4.1"
version = "0.4.4"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
{file = "ruff-0.4.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2d9ef6231e3fbdc0b8c72404a1a0c46fd0dcea84efca83beb4681c318ea6a953"},
{file = "ruff-0.4.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9485f54a7189e6f7433e0058cf8581bee45c31a25cd69009d2a040d1bd4bfaef"},
{file = "ruff-0.4.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2921ac03ce1383e360e8a95442ffb0d757a6a7ddd9a5be68561a671e0e5807e"},
{file = "ruff-0.4.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eec8d185fe193ad053eda3a6be23069e0c8ba8c5d20bc5ace6e3b9e37d246d3f"},
{file = "ruff-0.4.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:baa27d9d72a94574d250f42b7640b3bd2edc4c58ac8ac2778a8c82374bb27984"},
{file = "ruff-0.4.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f1ee41580bff1a651339eb3337c20c12f4037f6110a36ae4a2d864c52e5ef954"},
{file = "ruff-0.4.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0926cefb57fc5fced629603fbd1a23d458b25418681d96823992ba975f050c2b"},
{file = "ruff-0.4.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c6e37f2e3cd74496a74af9a4fa67b547ab3ca137688c484749189bf3a686ceb"},
{file = "ruff-0.4.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efd703a5975ac1998c2cc5e9494e13b28f31e66c616b0a76e206de2562e0843c"},
{file = "ruff-0.4.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b92f03b4aa9fa23e1799b40f15f8b95cdc418782a567d6c43def65e1bbb7f1cf"},
{file = "ruff-0.4.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1c859f294f8633889e7d77de228b203eb0e9a03071b72b5989d89a0cf98ee262"},
{file = "ruff-0.4.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b34510141e393519a47f2d7b8216fec747ea1f2c81e85f076e9f2910588d4b64"},
{file = "ruff-0.4.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6e68d248ed688b9d69fd4d18737edcbb79c98b251bba5a2b031ce2470224bdf9"},
{file = "ruff-0.4.1-py3-none-win32.whl", hash = "sha256:b90506f3d6d1f41f43f9b7b5ff845aeefabed6d2494307bc7b178360a8805252"},
{file = "ruff-0.4.1-py3-none-win_amd64.whl", hash = "sha256:c7d391e5936af5c9e252743d767c564670dc3889aff460d35c518ee76e4b26d7"},
{file = "ruff-0.4.1-py3-none-win_arm64.whl", hash = "sha256:a1eaf03d87e6a7cd5e661d36d8c6e874693cb9bc3049d110bc9a97b350680c43"},
{file = "ruff-0.4.1.tar.gz", hash = "sha256:d592116cdbb65f8b1b7e2a2b48297eb865f6bdc20641879aa9d7b9c11d86db79"},
{file = "ruff-0.4.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:29d44ef5bb6a08e235c8249294fa8d431adc1426bfda99ed493119e6f9ea1bf6"},
{file = "ruff-0.4.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c4efe62b5bbb24178c950732ddd40712b878a9b96b1d02b0ff0b08a090cbd891"},
{file = "ruff-0.4.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c8e2f1e8fc12d07ab521a9005d68a969e167b589cbcaee354cb61e9d9de9c15"},
{file = "ruff-0.4.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60ed88b636a463214905c002fa3eaab19795679ed55529f91e488db3fe8976ab"},
{file = "ruff-0.4.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b90fc5e170fc71c712cc4d9ab0e24ea505c6a9e4ebf346787a67e691dfb72e85"},
{file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8e7e6ebc10ef16dcdc77fd5557ee60647512b400e4a60bdc4849468f076f6eef"},
{file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9ddb2c494fb79fc208cd15ffe08f32b7682519e067413dbaf5f4b01a6087bcd"},
{file = "ruff-0.4.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c51c928a14f9f0a871082603e25a1588059b7e08a920f2f9fa7157b5bf08cfe9"},
{file = "ruff-0.4.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5eb0a4bfd6400b7d07c09a7725e1a98c3b838be557fee229ac0f84d9aa49c36"},
{file = "ruff-0.4.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b1867ee9bf3acc21778dcb293db504692eda5f7a11a6e6cc40890182a9f9e595"},
{file = "ruff-0.4.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1aecced1269481ef2894cc495647392a34b0bf3e28ff53ed95a385b13aa45768"},
{file = "ruff-0.4.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9da73eb616b3241a307b837f32756dc20a0b07e2bcb694fec73699c93d04a69e"},
{file = "ruff-0.4.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:958b4ea5589706a81065e2a776237de2ecc3e763342e5cc8e02a4a4d8a5e6f95"},
{file = "ruff-0.4.4-py3-none-win32.whl", hash = "sha256:cb53473849f011bca6e754f2cdf47cafc9c4f4ff4570003a0dad0b9b6890e876"},
{file = "ruff-0.4.4-py3-none-win_amd64.whl", hash = "sha256:424e5b72597482543b684c11def82669cc6b395aa8cc69acc1858b5ef3e5daae"},
{file = "ruff-0.4.4-py3-none-win_arm64.whl", hash = "sha256:39df0537b47d3b597293edbb95baf54ff5b49589eb7ff41926d8243caa995ea6"},
{file = "ruff-0.4.4.tar.gz", hash = "sha256:f87ea42d5cdebdc6a69761a9d0bc83ae9b3b30d0ad78952005ba6568d6c022af"},
]
[[package]]
@@ -3896,17 +3914,6 @@ webencodings = ">=0.4"
doc = ["sphinx", "sphinx_rtd_theme"]
test = ["flake8", "isort", "pytest"]
[[package]]
name = "toml"
version = "0.10.2"
description = "Python Library for Tom's Obvious, Minimal Language"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
]
[[package]]
name = "tomli"
version = "2.0.1"
@@ -3940,13 +3947,13 @@ files = [
[[package]]
name = "tox"
version = "4.14.2"
version = "4.15.0"
description = "tox is a generic virtualenv management and test command line tool"
optional = false
python-versions = ">=3.8"
files = [
{file = "tox-4.14.2-py3-none-any.whl", hash = "sha256:2900c4eb7b716af4a928a7fdc2ed248ad6575294ed7cfae2ea41203937422847"},
{file = "tox-4.14.2.tar.gz", hash = "sha256:0defb44f6dafd911b61788325741cc6b2e12ea71f987ac025ad4d649f1f1a104"},
{file = "tox-4.15.0-py3-none-any.whl", hash = "sha256:300055f335d855b2ab1b12c5802de7f62a36d4fd53f30bd2835f6a201dda46ea"},
{file = "tox-4.15.0.tar.gz", hash = "sha256:7a0beeef166fbe566f54f795b4906c31b428eddafc0102ac00d20998dd1933f6"},
]
[package.dependencies]
@@ -3967,13 +3974,13 @@ testing = ["build[virtualenv] (>=1.0.3)", "covdefaults (>=2.3)", "detect-test-po
[[package]]
name = "tqdm"
version = "4.66.2"
version = "4.66.4"
description = "Fast, Extensible Progress Meter"
optional = true
python-versions = ">=3.7"
files = [
{file = "tqdm-4.66.2-py3-none-any.whl", hash = "sha256:1ee4f8a893eb9bef51c6e35730cebf234d5d0b6bd112b0271e10ed7c24a02bd9"},
{file = "tqdm-4.66.2.tar.gz", hash = "sha256:6cd52cdf0fef0e0f543299cfc96fec90d7b8a7e88745f411ec33eb44d5ed3531"},
{file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"},
{file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"},
]
[package.dependencies]
@@ -4250,4 +4257,4 @@ desktop = ["opencv-python"]
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
content-hash = "56ddae6824a9f28c9954badd4c642c57f687b099c6169f97fa0372e294500c17"
content-hash = "29af5aa06f97e77a2dba94c5a6d77d7d1903448724df07416026a378d3c6a64d"
+3 -3
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "supervision"
version = "0.20.0"
version = "0.21.0rc5"
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>"]
@@ -43,7 +43,7 @@ defusedxml = "^0.7.1"
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.2", optional = true }
tqdm = { version = ">=4.62.3,<=4.66.4", optional = true }
pillow = ">=9.4"
[tool.poetry.extras]
@@ -67,7 +67,7 @@ nbconvert = "^7.14.2"
[tool.poetry.group.docs.dependencies]
mkdocs-material = {extras = ["imaging"], version = "^9.5.5"}
mkdocstrings = {extras = ["python"], version = ">=0.20,<0.25"}
mkdocstrings = {extras = ["python"], version = ">=0.20,<0.26"}
mike = "^2.0.0"
# For Documentation Development use Python 3.10 or above
# Use Latest mkdocs-jupyter min 0.24.6 for Jupyter Notebook Theme support
+8 -1
View File
@@ -46,12 +46,15 @@ from supervision.detection.utils import (
box_iou_batch,
box_non_max_suppression,
calculate_masks_centroids,
clip_boxes,
filter_polygons_by_area,
mask_iou_batch,
mask_non_max_suppression,
mask_to_polygons,
mask_to_xyxy,
move_boxes,
move_masks,
pad_boxes,
polygon_to_mask,
polygon_to_xyxy,
scale_boxes,
@@ -69,7 +72,11 @@ from supervision.draw.utils import (
)
from supervision.geometry.core import Point, Position, Rect
from supervision.geometry.utils import get_polygon_center
from supervision.keypoint.annotators import EdgeAnnotator, VertexAnnotator
from supervision.keypoint.annotators import (
EdgeAnnotator,
VertexAnnotator,
VertexLabelAnnotator,
)
from supervision.keypoint.core import KeyPoints
from supervision.metrics.detection import ConfusionMatrix, MeanAveragePrecision
from supervision.tracker.byte_tracker.core import ByteTrack
+13 -10
View File
@@ -418,6 +418,9 @@ class Detections:
xyxy=mmdet_results.pred_instances.bboxes.cpu().numpy(),
confidence=mmdet_results.pred_instances.scores.cpu().numpy(),
class_id=mmdet_results.pred_instances.labels.cpu().numpy().astype(int),
mask=mmdet_results.pred_instances.masks.cpu().numpy()
if "masks" in mmdet_results.pred_instances
else None,
)
@classmethod
@@ -831,9 +834,10 @@ class Detections:
This method takes a list of Detections objects and combines their
respective fields (`xyxy`, `mask`, `confidence`, `class_id`, and `tracker_id`)
into a single Detections object. If all elements in a field are not
`None`, the corresponding field will be stacked.
Otherwise, the field will be set to `None`.
into a single Detections object.
For example, if merging Detections with 3 and 4 detected objects, this method
will return a Detections with 7 objects (7 entries in `xyxy`, `mask`, etc).
Args:
detections_list (List[Detections]): A list of Detections objects to merge.
@@ -891,13 +895,12 @@ class Detections:
def stack_or_none(name: str):
if all(d.__getattribute__(name) is None for d in detections_list):
return None
if any(d.__getattribute__(name) is None for d in detections_list):
raise ValueError(f"All or none of the '{name}' fields must be None")
return (
np.vstack([d.__getattribute__(name) for d in detections_list])
if name == "mask"
else np.hstack([d.__getattribute__(name) for d in detections_list])
)
stack_list = [
d.__getattribute__(name)
for d in detections_list
if d.__getattribute__(name) is not None
]
return np.vstack(stack_list) if name == "mask" else np.hstack(stack_list)
mask = stack_or_none("mask")
confidence = stack_or_none("confidence")
@@ -4,20 +4,36 @@ from typing import Callable, Optional, Tuple
import numpy as np
from supervision.detection.core import Detections
from supervision.detection.utils import move_boxes
from supervision.detection.utils import move_boxes, move_masks
from supervision.utils.image import crop_image
def move_detections(detections: Detections, offset: np.array) -> Detections:
def move_detections(
detections: Detections,
offset: np.ndarray,
resolution_wh: Optional[Tuple[int, int]] = None,
) -> Detections:
"""
Args:
detections (sv.Detections): Detections object to be moved.
offset (np.array): An array of shape `(2,)` containing offset values in format
offset (np.ndarray): An array of shape `(2,)` containing offset values in format
is `[dx, dy]`.
resolution_wh (Tuple[int, int]): The width and height of the desired mask
resolution. Required for segmentation detections.
Returns:
(sv.Detections) repositioned Detections object.
"""
detections.xyxy = move_boxes(xyxy=detections.xyxy, offset=offset)
if detections.mask is not None:
if resolution_wh is None:
raise ValueError(
"Resolution width and height are required for moving segmentation "
"detections. This should be the same as (width, height) of image shape."
)
detections.mask = move_masks(
masks=detections.mask, offset=offset, resolution_wh=resolution_wh
)
return detections
@@ -126,7 +142,10 @@ class InferenceSlicer:
"""
image_slice = crop_image(image=image, xyxy=offset)
detections = self.callback(image_slice)
detections = move_detections(detections=detections, offset=offset[:2])
resolution_wh = (image.shape[1], image.shape[0])
detections = move_detections(
detections=detections, offset=offset[:2], resolution_wh=resolution_wh
)
return detections
+82 -8
View File
@@ -297,6 +297,35 @@ def clip_boxes(xyxy: np.ndarray, resolution_wh: Tuple[int, int]) -> np.ndarray:
return result
def pad_boxes(xyxy: np.ndarray, px: int, py: Optional[int] = None) -> np.ndarray:
"""
Pads bounding boxes coordinates with a constant padding.
Args:
xyxy (np.ndarray): A numpy array of shape `(N, 4)` where each
row corresponds to a bounding box in the format
`(x_min, y_min, x_max, y_max)`.
px (int): The padding value to be added to both the left and right sides of
each bounding box.
py (Optional[int]): The padding value to be added to both the top and bottom
sides of each bounding box. If not provided, `px` will be used for both
dimensions.
Returns:
np.ndarray: A numpy array of shape `(N, 4)` where each row corresponds to a
bounding box with coordinates padded according to the provided padding
values.
"""
if py is None:
py = px
result = xyxy.copy()
result[:, [0, 1]] -= [px, py]
result[:, [2, 3]] += [px, py]
return result
def xywh_to_xyxy(boxes_xywh: np.ndarray) -> np.ndarray:
xyxy = boxes_xywh.copy()
xyxy[:, 2] = boxes_xywh[:, 0] + boxes_xywh[:, 2]
@@ -500,7 +529,7 @@ def process_roboflow_result(
np.ndarray,
Optional[np.ndarray],
Optional[np.ndarray],
Dict[str, List[np.ndarray]],
Dict[str, Union[List[np.ndarray], np.ndarray]],
]:
if not roboflow_result["predictions"]:
return (
@@ -592,6 +621,40 @@ def move_boxes(xyxy: np.ndarray, offset: np.ndarray) -> np.ndarray:
return xyxy + np.hstack([offset, offset])
def move_masks(
masks: np.ndarray,
offset: np.ndarray,
resolution_wh: Tuple[int, int] = None,
) -> np.ndarray:
"""
Offset the masks in an array by the specified (x, y) amount.
Args:
masks (np.ndarray): A 3D array of binary masks corresponding to the predictions.
Shape: `(N, H, W)`, where N is the number of predictions, and H, W are the
dimensions of each mask.
offset (np.ndarray): An array of shape `(2,)` containing non-negative int values
`[dx, dy]`.
resolution_wh (Tuple[int, int]): The width and height of the desired mask
resolution.
Returns:
(np.ndarray) repositioned masks, optionally padded to the specified shape.
"""
if offset[0] < 0 or offset[1] < 0:
raise ValueError(f"Offset values must be non-negative integers. Got: {offset}")
mask_array = np.full((masks.shape[0], resolution_wh[1], resolution_wh[0]), False)
mask_array[
:,
offset[1] : masks.shape[1] + offset[1],
offset[0] : masks.shape[2] + offset[0],
] = masks
return mask_array
def scale_boxes(xyxy: np.ndarray, factor: float) -> np.ndarray:
"""
Scale the dimensions of bounding boxes.
@@ -678,7 +741,9 @@ def merge_data(
Merges the data payloads of a list of Detections instances.
Args:
data_list: The data payloads of the instances.
data_list: The data payloads of the Detections instances. Each data payload
is a dictionary with the same keys, and the values are either lists or
np.ndarray.
Returns:
A single data payload containing the merged data, preserving the original data
@@ -691,10 +756,6 @@ def merge_data(
if not data_list:
return {}
all_keys_sets = [set(data.keys()) for data in data_list]
if not all(keys_set == all_keys_sets[0] for keys_set in all_keys_sets):
raise ValueError("All data dictionaries must have the same keys to merge.")
for data in data_list:
lengths = [len(value) for value in data.values()]
if len(set(lengths)) > 1:
@@ -702,10 +763,23 @@ def merge_data(
"All data values within a single object must have equal length."
)
merged_data = {key: [] for key in all_keys_sets[0]}
keys_by_data = [set(data.keys()) for data in data_list]
keys_by_data = [keys for keys in keys_by_data if len(keys) > 0]
if not keys_by_data:
return {}
common_keys = set.intersection(*keys_by_data)
all_keys = set.union(*keys_by_data)
if common_keys != all_keys:
raise ValueError(
f"All sv.Detections.data dictionaries must have the same keys. Common "
f"keys: {common_keys}, but some dictionaries have additional keys: "
f"{all_keys.difference(common_keys)}."
)
merged_data = {key: [] for key in all_keys}
for data in data_list:
for key in merged_data:
for key in data:
merged_data[key].append(data[key])
for key in merged_data:
+52
View File
@@ -81,6 +81,58 @@ def draw_filled_rectangle(scene: np.ndarray, rect: Rect, color: Color) -> np.nda
return scene
def draw_rounded_rectangle(
scene: np.ndarray,
rect: Rect,
color: Color,
border_radius: int,
) -> np.ndarray:
"""
Draws a rounded rectangle on an image.
Parameters:
scene (np.ndarray): The image on which the rounded rectangle will be drawn.
rect (Rect): The rectangle to be drawn.
color (Color): The color of the rounded rectangle.
border_radius (int): The radius of the corner rounding.
Returns:
np.ndarray: The image with the rounded rectangle drawn on it.
"""
x1, y1, x2, y2 = rect.as_xyxy_int_tuple()
width, height = x2 - x1, y2 - y1
border_radius = min(border_radius, min(width, height) // 2)
rectangle_coordinates = [
((x1 + border_radius, y1), (x2 - border_radius, y2)),
((x1, y1 + border_radius), (x2, y2 - border_radius)),
]
circle_centers = [
(x1 + border_radius, y1 + border_radius),
(x2 - border_radius, y1 + border_radius),
(x1 + border_radius, y2 - border_radius),
(x2 - border_radius, y2 - border_radius),
]
for coordinates in rectangle_coordinates:
cv2.rectangle(
img=scene,
pt1=coordinates[0],
pt2=coordinates[1],
color=color.as_bgr(),
thickness=-1,
)
for center in circle_centers:
cv2.circle(
img=scene,
center=center,
radius=border_radius,
color=color.as_bgr(),
thickness=-1,
)
return scene
def draw_polygon(
scene: np.ndarray, polygon: np.ndarray, color: Color, thickness: int = 2
) -> np.ndarray:
+13
View File
@@ -98,6 +98,11 @@ class Rect:
width: float
height: float
@classmethod
def from_xyxy(cls, xyxy: Tuple[float, float, float, float]) -> Rect:
x1, y1, x2, y2 = xyxy
return cls(x=x1, y=y1, width=x2 - x1, height=y2 - y1)
@property
def top_left(self) -> Point:
return Point(x=self.x, y=self.y)
@@ -113,3 +118,11 @@ class Rect:
width=self.width + 2 * padding,
height=self.height + 2 * padding,
)
def as_xyxy_int_tuple(self) -> Tuple[int, int, int, int]:
return (
int(self.x),
int(self.y),
int(self.x + self.width),
int(self.y + self.height),
)
+247 -6
View File
@@ -1,12 +1,14 @@
from abc import ABC, abstractmethod
from logging import warn
from typing import List, Optional, Tuple
from typing import List, Optional, Tuple, Union
import cv2
import numpy as np
from supervision import Rect, pad_boxes
from supervision.annotators.base import ImageType
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
@@ -46,8 +48,8 @@ class VertexAnnotator(BaseKeyPointAnnotator):
points. It draws circles at each key point location.
Args:
scene (ImageType): The image where bounding boxes will be drawn. `ImageType`
is a flexible type, accepting either `numpy.ndarray` or
scene (ImageType): The image where skeleton vertices will be drawn.
`ImageType` is a flexible type, accepting either `numpy.ndarray` or
`PIL.Image.Image`.
key_points (KeyPoints): A collection of key points where each key point
consists of x and y coordinates.
@@ -63,7 +65,10 @@ class VertexAnnotator(BaseKeyPointAnnotator):
image = ...
key_points = sv.KeyPoints(...)
vertex_annotator = sv.VertexAnnotator(color=sv.Color.GREEN, radius=10)
vertex_annotator = sv.VertexAnnotator(
color=sv.Color.GREEN,
radius=10
)
annotated_frame = vertex_annotator.annotate(
scene=image.copy(),
key_points=key_points
@@ -119,7 +124,7 @@ class EdgeAnnotator(BaseKeyPointAnnotator):
edges.
Args:
scene (ImageType): The image where bounding boxes will be drawn. `ImageType`
scene (ImageType): The image where skeleton edges will be drawn. `ImageType`
is a flexible type, accepting either `numpy.ndarray` or
`PIL.Image.Image`.
key_points (KeyPoints): A collection of key points where each key point
@@ -137,7 +142,10 @@ class EdgeAnnotator(BaseKeyPointAnnotator):
image = ...
key_points = sv.KeyPoints(...)
edge_annotator = sv.EdgeAnnotator(color=sv.Color.GREEN, thickness=5)
edge_annotator = sv.EdgeAnnotator(
color=sv.Color.GREEN,
thickness=5
)
annotated_frame = edge_annotator.annotate(
scene=image.copy(),
key_points=key_points
@@ -175,3 +183,236 @@ class EdgeAnnotator(BaseKeyPointAnnotator):
)
return scene
class VertexLabelAnnotator:
"""
A class that draws labels of skeleton vertices on images. It uses specified key
points to determine the locations where the vertices should be drawn.
"""
def __init__(
self,
color: Union[Color, List[Color]] = Color.ROBOFLOW,
text_color: Color = Color.WHITE,
text_scale: float = 0.5,
text_thickness: int = 1,
text_padding: int = 10,
border_radius: int = 0,
):
"""
Args:
color (Union[Color, List[Color]], optional): The color to use for each
keypoint label. If a list is provided, the colors will be used in order
for each keypoint.
text_color (Color, optional): The color to use for the labels.
text_scale (float, optional): The scale of the text.
text_thickness (int, optional): The thickness of the text.
text_padding (int, optional): The padding around the text.
border_radius (int, optional): The radius of the rounded corners of the
boxes. Set to a high value to produce circles.
"""
self.border_radius: int = border_radius
self.color: Union[Color, List[Color]] = color
self.text_color: Color = text_color
self.text_scale: float = text_scale
self.text_thickness: int = text_thickness
self.text_padding: int = text_padding
def annotate(
self, scene: ImageType, key_points: KeyPoints, labels: List[str] = None
) -> ImageType:
"""
A class that draws labels of skeleton vertices on images. It uses specified key
points to determine the locations where the vertices should be drawn.
Args:
scene (ImageType): The image where vertex labels will be drawn. `ImageType`
is a flexible type, accepting either `numpy.ndarray` or
`PIL.Image.Image`.
key_points (KeyPoints): A collection of key points where each key point
consists of x and y coordinates.
labels (List[str], optional): A list of labels to be displayed on the
annotated image. If not provided, keypoint indices will be used.
Returns:
The annotated image, matching the type of `scene` (`numpy.ndarray`
or `PIL.Image.Image`)
Example:
```python
import supervision as sv
image = ...
key_points = sv.KeyPoints(...)
vertex_label_annotator = sv.VertexLabelAnnotator(
color=sv.Color.GREEN,
text_color=sv.Color.BLACK,
border_radius=5
)
annotated_frame = vertex_label_annotator.annotate(
scene=image.copy(),
key_points=key_points
)
```
![vertex-label-annotator-example](https://media.roboflow.com/
supervision-annotator-examples/vertex-label-annotator-example.png)
!!! tip
`VertexLabelAnnotator` allows to customize the color of each keypoint label
values.
Example:
```python
import supervision as sv
image = ...
key_points = sv.KeyPoints(...)
LABELS = [
"nose", "left eye", "right eye", "left ear",
"right ear", "left shoulder", "right shoulder", "left elbow",
"right elbow", "left wrist", "right wrist", "left hip",
"right hip", "left knee", "right knee", "left ankle",
"right ankle"
]
COLORS = [
"#FF6347", "#FF6347", "#FF6347", "#FF6347",
"#FF6347", "#FF1493", "#00FF00", "#FF1493",
"#00FF00", "#FF1493", "#00FF00", "#FFD700",
"#00BFFF", "#FFD700", "#00BFFF", "#FFD700",
"#00BFFF"
]
COLORS = [sv.Color.from_hex(color_hex=c) for c in COLORS]
vertex_label_annotator = sv.VertexLabelAnnotator(
color=COLORS,
text_color=sv.Color.BLACK,
border_radius=5
)
annotated_frame = vertex_label_annotator.annotate(
scene=image.copy(),
key_points=key_points,
labels=labels
)
```
![vertex-label-annotator-custom-example](https://media.roboflow.com/
supervision-annotator-examples/vertex-label-annotator-custom-example.png)
"""
font = cv2.FONT_HERSHEY_SIMPLEX
skeletons_count, points_count, _ = key_points.xy.shape
if skeletons_count == 0:
return scene
anchors = key_points.xy.reshape(points_count * skeletons_count, 2).astype(int)
mask = np.all(anchors != 0, axis=1)
if not np.any(mask):
return scene
colors = self.preprocess_and_validate_colors(
colors=self.color,
points_count=points_count,
skeletons_count=skeletons_count,
)
labels = self.preprocess_and_validate_labels(
labels=labels, points_count=points_count, skeletons_count=skeletons_count
)
anchors = anchors[mask]
colors = colors[mask]
labels = labels[mask]
xyxy = np.array(
[
self.get_text_bounding_box(
text=label,
font=font,
text_scale=self.text_scale,
text_thickness=self.text_thickness,
center_coordinates=tuple(anchor),
)
for anchor, label in zip(anchors, labels)
]
)
xyxy_padded = pad_boxes(xyxy=xyxy, px=self.text_padding)
for text, color, box, box_padded in zip(labels, colors, xyxy, xyxy_padded):
draw_rounded_rectangle(
scene=scene,
rect=Rect.from_xyxy(box_padded),
color=color,
border_radius=self.border_radius,
)
cv2.putText(
img=scene,
text=text,
org=(box[0], box[1] + self.text_padding),
fontFace=font,
fontScale=self.text_scale,
color=self.text_color.as_rgb(),
thickness=self.text_thickness,
lineType=cv2.LINE_AA,
)
return scene
@staticmethod
def get_text_bounding_box(
text: str,
font: int,
text_scale: float,
text_thickness: int,
center_coordinates: Tuple[int, int],
) -> Tuple[int, int, int, int]:
text_w, text_h = cv2.getTextSize(
text=text,
fontFace=font,
fontScale=text_scale,
thickness=text_thickness,
)[0]
center_x, center_y = center_coordinates
return (
center_x - text_w // 2,
center_y - text_h // 2,
center_x + text_w // 2,
center_y + text_h // 2,
)
@staticmethod
def preprocess_and_validate_labels(
labels: Optional[List[str]], points_count: int, skeletons_count: int
) -> np.array:
if labels and len(labels) != points_count:
raise ValueError(
f"Number of labels ({len(labels)}) must match number of key points "
f"({points_count})."
)
if labels is None:
labels = [str(i) for i in range(points_count)]
return np.array(labels * skeletons_count)
@staticmethod
def preprocess_and_validate_colors(
colors: Optional[Union[Color, List[Color]]],
points_count: int,
skeletons_count: int,
) -> np.array:
if isinstance(colors, list) and len(colors) != points_count:
raise ValueError(
f"Number of colors ({len(colors)}) must match number of key points "
f"({points_count})."
)
return (
np.array(colors * skeletons_count)
if isinstance(colors, list)
else np.array([colors] * points_count * skeletons_count)
)
+145 -3
View File
@@ -1,5 +1,6 @@
from __future__ import annotations
from contextlib import suppress
from dataclasses import dataclass, field
from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
@@ -100,10 +101,94 @@ class KeyPoints:
]
)
@classmethod
def from_inference(cls, inference_result: Union[dict, Any]) -> 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.
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.
Example:
```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)
```
```python
import cv2
import supervision as sv
from inference_sdk import InferenceHTTPClient
image = cv2.imread(<SOURCE_IMAGE_PATH>)
client = InferenceHTTPClient(
api_url="https://detect.roboflow.com",
api_key=<ROBOFLOW_API_KEY>
)
result = client.infer(image, model_id=<POSE_MODEL_ID>)
key_points = sv.KeyPoints.from_inference(result)
```
"""
if isinstance(inference_result, list):
raise ValueError(
"from_inference() operates on a single result at a time."
"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)
if not inference_result.get("predictions"):
return cls.empty()
xy = []
confidence = []
class_id = []
class_names = []
for prediction in inference_result["predictions"]:
prediction_xy = []
prediction_confidence = []
for keypoint in prediction["keypoints"]:
prediction_xy.append([keypoint["x"], keypoint["y"]])
prediction_confidence.append(keypoint["confidence"])
xy.append(prediction_xy)
confidence.append(prediction_confidence)
class_id.append(prediction["class_id"])
class_names.append(prediction["class"])
data = {CLASS_NAME_DATA_FIELD: np.array(class_names)}
return cls(
xy=np.array(xy, dtype=np.float32),
confidence=np.array(confidence, dtype=np.float32),
class_id=np.array(class_id, dtype=int),
data=data,
)
@classmethod
def from_ultralytics(cls, ultralytics_results) -> KeyPoints:
"""
Creates a Keypoints instance from a
Creates a KeyPoints instance from a
[YOLOv8](https://github.com/ultralytics/ultralytics) inference result.
Args:
@@ -111,7 +196,7 @@ class KeyPoints:
The output Results instance from YOLOv8
Returns:
KeyPoints: A new Keypoints object.
KeyPoints: A new KeyPoints object.
Example:
```python
@@ -136,9 +221,66 @@ class KeyPoints:
data = {CLASS_NAME_DATA_FIELD: class_names}
return cls(xy, class_id, confidence, data)
@classmethod
def from_yolo_nas(cls, yolo_nas_results) -> KeyPoints:
"""
Create a KeyPoints instance from a YOLO NAS results.
Args:
yolo_nas_results (ImagePoseEstimationPrediction):
The output object from YOLO NAS.
Returns:
KeyPoints: A new KeyPoints object.
Example:
```python
import cv2
import torch
import supervision as sv
import super_gradients
image = cv2.imread(<SOURCE_IMAGE_PATH>)
device = "cuda" if torch.cuda.is_available() else "cpu"
yolo_nas = 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)
```
"""
if len(yolo_nas_results.prediction.poses) == 0:
return cls.empty()
xy = yolo_nas_results.prediction.poses[:, :, :2]
confidence = yolo_nas_results.prediction.poses[:, :, 2]
# yolo_nas_results treats params differently.
# prediction.labels may not exist, whereas class_names might be None
if hasattr(yolo_nas_results.prediction, "labels"):
class_id = yolo_nas_results.prediction.labels # np.array[int]
else:
class_id = None
data = {}
if class_id is not None and yolo_nas_results.class_names is not None:
class_names = []
for c_id in class_id:
name = yolo_nas_results.class_names[c_id] # tuple[str]
class_names.append(name)
data[CLASS_NAME_DATA_FIELD] = class_names
return cls(
xy=xy,
confidence=confidence,
class_id=class_id,
data=data,
)
def __getitem__(
self, index: Union[int, slice, List[int], np.ndarray, str]
) -> Union["KeyPoints", List, np.ndarray, None]:
) -> Union[KeyPoints, List, np.ndarray, None]:
"""
Get a subset of the KeyPoints object or access an item from its data field.
+54 -21
View File
@@ -12,8 +12,9 @@ from supervision.utils.internal import deprecated_parameter
class STrack(BaseTrack):
shared_kalman = KalmanFilter()
_external_count = 0
def __init__(self, tlwh, score, class_ids):
def __init__(self, tlwh, score, class_ids, minimum_consecutive_frames):
# wait activate
self._tlwh = np.asarray(tlwh, dtype=np.float32)
self.kalman_filter = None
@@ -24,6 +25,10 @@ class STrack(BaseTrack):
self.class_ids = class_ids
self.tracklet_len = 0
self.external_track_id = -1
self.minimum_consecutive_frames = minimum_consecutive_frames
def predict(self):
mean_state = self.mean.copy()
if self.state != TrackState.Tracked:
@@ -53,7 +58,7 @@ class STrack(BaseTrack):
def activate(self, kalman_filter, frame_id):
"""Start a new tracklet"""
self.kalman_filter = kalman_filter
self.track_id = self.next_id()
self.internal_track_id = self.next_id()
self.mean, self.covariance = self.kalman_filter.initiate(
self.tlwh_to_xyah(self._tlwh)
)
@@ -62,6 +67,10 @@ class STrack(BaseTrack):
self.state = TrackState.Tracked
if frame_id == 1:
self.is_activated = True
if self.minimum_consecutive_frames == 1:
self.external_track_id = self.next_external_id()
self.frame_id = frame_id
self.start_frame = frame_id
@@ -71,10 +80,10 @@ class STrack(BaseTrack):
)
self.tracklet_len = 0
self.state = TrackState.Tracked
self.is_activated = True
self.frame_id = frame_id
if new_id:
self.track_id = self.next_id()
self.internal_track_id = self.next_id()
self.score = new_track.score
def update(self, new_track, frame_id):
@@ -93,7 +102,10 @@ class STrack(BaseTrack):
self.mean, self.covariance, self.tlwh_to_xyah(new_tlwh)
)
self.state = TrackState.Tracked
self.is_activated = True
if self.tracklet_len == self.minimum_consecutive_frames:
self.is_activated = True
if self.external_track_id == -1:
self.external_track_id = self.next_external_id()
self.score = new_track.score
@@ -131,6 +143,15 @@ class STrack(BaseTrack):
def to_xyah(self):
return self.tlwh_to_xyah(self.tlwh)
@staticmethod
def next_external_id():
STrack._external_count += 1
return STrack._external_count
@staticmethod
def reset_external_counter():
STrack._external_count = 0
@staticmethod
def tlbr_to_tlwh(tlbr):
ret = np.asarray(tlbr).copy()
@@ -144,7 +165,9 @@ class STrack(BaseTrack):
return ret
def __repr__(self):
return "OT_{}_({}-{})".format(self.track_id, self.start_frame, self.end_frame)
return "OT_{}_({}-{})".format(
self.internal_track_id, self.start_frame, self.end_frame
)
def detections2boxes(detections: Detections) -> np.ndarray:
@@ -186,6 +209,10 @@ class ByteTrack:
Increasing minimum_matching_threshold improves accuracy but risks fragmentation.
Decreasing it improves completeness but risks false positives and drift.
frame_rate (int, optional): The frame rate of the video.
minimum_consecutive_frames (int, optional): Number of consecutive frames that an object must
be tracked before it is considered a 'valid' track.
Increasing minimum_consecutive_frames prevents the creation of accidental tracks from
false detection or double detection, but risks missing shorter tracks.
""" # noqa: E501 // docs
@deprecated_parameter(
@@ -218,6 +245,7 @@ class ByteTrack:
lost_track_buffer: int = 30,
minimum_matching_threshold: float = 0.8,
frame_rate: int = 30,
minimum_consecutive_frames: int = 1,
):
self.track_activation_threshold = track_activation_threshold
self.minimum_matching_threshold = minimum_matching_threshold
@@ -225,6 +253,7 @@ class ByteTrack:
self.frame_id = 0
self.det_thresh = self.track_activation_threshold + 0.1
self.max_time_lost = int(frame_rate / 30.0 * lost_track_buffer)
self.minimum_consecutive_frames = minimum_consecutive_frames
self.kalman_filter = KalmanFilter()
self.tracked_tracks: List[STrack] = []
@@ -285,11 +314,14 @@ class ByteTrack:
matches, _, _ = matching.linear_assignment(iou_costs, 0.5)
detections.tracker_id = np.full(len(detections), -1, dtype=int)
for i_detection, i_track in matches:
detections.tracker_id[i_detection] = int(tracks[i_track].track_id)
detections.tracker_id[i_detection] = int(
tracks[i_track].external_track_id
)
return detections[detections.tracker_id != -1]
else:
detections = Detections.empty()
detections.tracker_id = np.array([], dtype=int)
return detections
@@ -308,6 +340,7 @@ class ByteTrack:
self.lost_tracks: List[STrack] = []
self.removed_tracks: List[STrack] = []
BaseTrack.reset_counter()
STrack.reset_external_counter()
def update_with_tensors(self, tensors: np.ndarray) -> List[STrack]:
"""
@@ -345,7 +378,7 @@ class ByteTrack:
if len(dets) > 0:
"""Detections"""
detections = [
STrack(STrack.tlbr_to_tlwh(tlbr), s, c)
STrack(STrack.tlbr_to_tlwh(tlbr), s, c, self.minimum_consecutive_frames)
for (tlbr, s, c) in zip(dets, scores_keep, class_ids_keep)
]
else:
@@ -387,7 +420,7 @@ class ByteTrack:
if len(dets_second) > 0:
"""Detections"""
detections_second = [
STrack(STrack.tlbr_to_tlwh(tlbr), s, c)
STrack(STrack.tlbr_to_tlwh(tlbr), s, c, self.minimum_consecutive_frames)
for (tlbr, s, c) in zip(dets_second, scores_second, class_ids_second)
]
else:
@@ -454,7 +487,7 @@ class ByteTrack:
self.lost_tracks = sub_tracks(self.lost_tracks, self.tracked_tracks)
self.lost_tracks.extend(lost_stracks)
self.lost_tracks = sub_tracks(self.lost_tracks, self.removed_tracks)
self.removed_tracks.extend(removed_stracks)
self.removed_tracks = removed_stracks
self.tracked_tracks, self.lost_tracks = remove_duplicate_tracks(
self.tracked_tracks, self.lost_tracks
)
@@ -468,22 +501,22 @@ def joint_tracks(
) -> List[STrack]:
"""
Joins two lists of tracks, ensuring that the resulting list does not
contain tracks with duplicate track_id values.
contain tracks with duplicate internal_track_id values.
Parameters:
track_list_a: First list of tracks (with track_id attribute).
track_list_b: Second list of tracks (with track_id attribute).
track_list_a: First list of tracks (with internal_track_id attribute).
track_list_b: Second list of tracks (with internal_track_id attribute).
Returns:
Combined list of tracks from track_list_a and track_list_b
without duplicate track_id values.
without duplicate internal_track_id values.
"""
seen_track_ids = set()
result = []
for track in track_list_a + track_list_b:
if track.track_id not in seen_track_ids:
seen_track_ids.add(track.track_id)
if track.internal_track_id not in seen_track_ids:
seen_track_ids.add(track.internal_track_id)
result.append(track)
return result
@@ -492,17 +525,17 @@ def joint_tracks(
def sub_tracks(track_list_a: List, track_list_b: List) -> List[int]:
"""
Returns a list of tracks from track_list_a after removing any tracks
that share the same track_id with tracks in track_list_b.
that share the same internal_track_id with tracks in track_list_b.
Parameters:
track_list_a: List of tracks (with track_id attribute).
track_list_b: List of tracks (with track_id attribute) to
track_list_a: List of tracks (with internal_track_id attribute).
track_list_b: List of tracks (with internal_track_id attribute) to
be subtracted from track_list_a.
Returns:
List of remaining tracks from track_list_a after subtraction.
"""
tracks = {track.track_id: track for track in track_list_a}
track_ids_b = {track.track_id for track in track_list_b}
tracks = {track.internal_track_id: track for track in track_list_a}
track_ids_b = {track.internal_track_id for track in track_list_b}
for track_id in track_ids_b:
tracks.pop(track_id, None)
+118 -34
View File
@@ -30,6 +30,84 @@ DETECTIONS = Detections(
)
# Merge test
TEST_MASK = np.zeros((1000, 1000), dtype=bool)
TEST_MASK[300:351, 200:251] = True
TEST_DET_1 = Detections(
xyxy=np.array([[10, 10, 20, 20], [30, 30, 40, 40], [50, 50, 60, 60]]),
mask=np.array([TEST_MASK, TEST_MASK, TEST_MASK]),
confidence=np.array([0.1, 0.2, 0.3]),
class_id=np.array([1, 2, 3]),
tracker_id=np.array([1, 2, 3]),
data={
"some_key": [1, 2, 3],
"other_key": [["1", "2"], ["3", "4"], ["5", "6"]],
},
)
TEST_DET_2 = Detections(
xyxy=np.array([[70, 70, 80, 80], [90, 90, 100, 100]]),
mask=np.array([TEST_MASK, TEST_MASK]),
confidence=np.array([0.4, 0.5]),
class_id=np.array([4, 5]),
tracker_id=np.array([4, 5]),
data={
"some_key": [4, 5],
"other_key": [["7", "8"], ["9", "10"]],
},
)
TEST_DET_1_2 = Detections(
xyxy=np.array(
[
[10, 10, 20, 20],
[30, 30, 40, 40],
[50, 50, 60, 60],
[70, 70, 80, 80],
[90, 90, 100, 100],
]
),
mask=np.array([TEST_MASK, TEST_MASK, TEST_MASK, TEST_MASK, TEST_MASK]),
confidence=np.array([0.1, 0.2, 0.3, 0.4, 0.5]),
class_id=np.array([1, 2, 3, 4, 5]),
tracker_id=np.array([1, 2, 3, 4, 5]),
data={
"some_key": [1, 2, 3, 4, 5],
"other_key": [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9", "10"]],
},
)
TEST_DET_ZERO_LENGTH = Detections(
xyxy=np.empty((0, 4), dtype=np.float32),
mask=np.empty((0, *TEST_MASK.shape), dtype=bool),
confidence=np.empty((0,)),
class_id=np.empty((0,)),
tracker_id=np.empty((0,)),
data={
"some_key": [],
"other_key": [],
},
)
TEST_DET_NONE = Detections(
xyxy=np.empty((0, 4), dtype=np.float32),
)
TEST_DET_DIFFERENT_FIELDS = Detections(
xyxy=np.array([[88, 88, 99, 99]]),
mask=np.array([np.logical_not(TEST_MASK)]),
confidence=None,
class_id=None,
tracker_id=np.array([9]),
data={"some_key": [9], "other_key": [["11", "12"]]},
)
TEST_DET_DIFFERENT_DATA = Detections(
xyxy=np.array([[88, 88, 99, 99]]),
mask=np.array([np.logical_not(TEST_MASK)]),
confidence=np.array([0.9]),
class_id=np.array([9]),
tracker_id=np.array([9]),
data={
"never_seen_key": [9],
},
)
@pytest.mark.parametrize(
"detections, index, expected_result, exception",
[
@@ -148,52 +226,58 @@ def test_getitem(
DoesNotRaise(),
), # single empty detections
(
[mock_detections(xyxy=[[10, 10, 20, 20]])],
mock_detections(xyxy=[[10, 10, 20, 20]]),
[Detections.empty(), Detections.empty()],
Detections.empty(),
DoesNotRaise(),
), # single detection with xyxy field
), # two empty detections
(
[TEST_DET_1],
TEST_DET_1,
DoesNotRaise(),
), # single detection with fields
(
[TEST_DET_NONE],
TEST_DET_NONE,
DoesNotRaise(),
), # Single weakly-defined detection
(
[TEST_DET_1, TEST_DET_2],
TEST_DET_1_2,
DoesNotRaise(),
), # Fields with same keys
# Fields and empty
(
[TEST_DET_1, Detections.empty()],
TEST_DET_1,
DoesNotRaise(),
), # single detection with fields
(
[
mock_detections(xyxy=[[10, 10, 20, 20]]),
mock_detections(xyxy=np.empty((0, 4), dtype=np.float32)),
TEST_DET_1,
TEST_DET_ZERO_LENGTH,
],
mock_detections(xyxy=[[10, 10, 20, 20]]),
TEST_DET_1,
DoesNotRaise(),
), # single detection with xyxy field + empty detection
), # Single detection and empty-array fields
(
[
mock_detections(xyxy=[[10, 10, 20, 20]]),
mock_detections(xyxy=[[20, 20, 30, 30]]),
TEST_DET_1,
TEST_DET_NONE,
],
mock_detections(xyxy=[[10, 10, 20, 20], [20, 20, 30, 30]]),
TEST_DET_1,
DoesNotRaise(),
), # two detections with xyxy field
), # Single detection and None fields (+ missing Dict keys)
# Errors: Non-zero-length differently defined keys & data
(
[
mock_detections(xyxy=[[10, 10, 20, 20]], class_id=[0]),
mock_detections(xyxy=[[20, 20, 30, 30]]),
],
mock_detections(xyxy=[[10, 10, 20, 20], [20, 20, 30, 30]]),
[TEST_DET_1, TEST_DET_DIFFERENT_FIELDS],
None,
pytest.raises(ValueError),
), # detection with xyxy, class_id fields + detection with xyxy field
), # Non-empty detections with different fields
(
[
mock_detections(xyxy=[[10, 10, 20, 20]], class_id=[0]),
mock_detections(xyxy=[[20, 20, 30, 30]], class_id=[1]),
],
mock_detections(xyxy=[[10, 10, 20, 20], [20, 20, 30, 30]], class_id=[0, 1]),
DoesNotRaise(),
), # two detections with xyxy, class_id fields
(
[
mock_detections(xyxy=[[10, 10, 20, 20]], data={"test": [1]}),
mock_detections(xyxy=[[20, 20, 30, 30]], data={"test": [2]}),
],
mock_detections(
xyxy=[[10, 10, 20, 20], [20, 20, 30, 30]], data={"test": [1, 2]}
),
DoesNotRaise(),
), # two detections with xyxy, data fields
[TEST_DET_1, TEST_DET_DIFFERENT_DATA],
None,
pytest.raises(ValueError),
), # Non-empty detections with different data keys
],
)
def test_merge(
+63 -1
View File
@@ -911,6 +911,14 @@ def test_calculate_masks_centroids(
{"test_1": []},
DoesNotRaise(),
), # single data dict with a single field name and empty list values
(
[
{"test_1": []},
{"test_1": []},
],
{"test_1": []},
DoesNotRaise(),
), # two data dicts with the same field name and empty list values
(
[
{"test_1": np.array([])},
@@ -918,6 +926,14 @@ def test_calculate_masks_centroids(
{"test_1": np.array([])},
DoesNotRaise(),
), # single data dict with a single field name and empty np.array values
(
[
{"test_1": np.array([])},
{"test_1": np.array([])},
],
{"test_1": np.array([])},
DoesNotRaise(),
), # two data dicts with the same field name and empty np.array values
(
[
{"test_1": [1, 2, 3]},
@@ -932,7 +948,7 @@ def test_calculate_masks_centroids(
],
{"test_1": [3, 2, 1]},
DoesNotRaise(),
), # two data dicts with the same field name and empty and list values
), # two data dicts with the same field name; one of with empty list as value
(
[
{"test_1": [1, 2, 3]},
@@ -1012,6 +1028,49 @@ def test_calculate_masks_centroids(
None,
pytest.raises(ValueError),
), # two data dicts with the same field name and different length arrays values
(
[{}, {"test_1": [1, 2, 3]}],
{"test_1": [1, 2, 3]},
DoesNotRaise(),
), # two data dicts; one empty and one non-empty dict
(
[{"test_1": [], "test_2": []}, {"test_1": [1, 2, 3], "test_2": [1, 2, 3]}],
{"test_1": [1, 2, 3], "test_2": [1, 2, 3]},
DoesNotRaise(),
), # two data dicts; one empty and one non-empty dict; same keys
(
[{"test_1": []}, {"test_1": [1, 2, 3], "test_2": [4, 5, 6]}],
None,
pytest.raises(ValueError),
), # two data dicts; one empty and one non-empty dict; different keys
(
[
{
"test_1": [1, 2, 3],
"test_2": [4, 5, 6],
"test_3": [7, 8, 9],
},
{"test_1": [1, 2, 3], "test_2": [4, 5, 6]},
],
None,
pytest.raises(ValueError),
), # two data dicts; one with three keys, one with two keys
(
[
{"test_1": [1, 2, 3]},
{"test_1": [1, 2, 3], "test_2": [1, 2, 3]},
],
None,
pytest.raises(ValueError),
), # some keys missing in one dict
(
[
{"test_1": [1, 2, 3], "test_2": ["a", "b"]},
{"test_1": [4, 5], "test_2": ["c", "d", "e"]},
],
None,
pytest.raises(ValueError),
), # different value lengths for the same key
],
)
def test_merge_data(
@@ -1021,6 +1080,9 @@ def test_merge_data(
):
with exception:
result = merge_data(data_list=data_list)
if expected_result is None:
assert False, f"Expected an error, but got result {result}"
for key in result:
if isinstance(result[key], np.ndarray):
assert np.array_equal(