Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0aff293841 | ||
|
|
b13e3e0b8e | ||
|
|
18085340e2 | ||
|
|
7d65a9445a | ||
|
|
a68e5b4a6d | ||
|
|
d3383fc02f | ||
|
|
6613f57a81 | ||
|
|
00c84d4530 | ||
|
|
cefd9e100c | ||
|
|
07763bb2d0 | ||
|
|
af03010a07 | ||
|
|
f12182b016 | ||
|
|
25deda23f2 | ||
|
|
a790040c84 | ||
|
|
d0ad5fe1d4 | ||
|
|
0c0685987a | ||
|
|
5f85339851 | ||
|
|
13cb923541 | ||
|
|
5e1d9bbd40 | ||
|
|
cb8f4af8bf | ||
|
|
165ed424bf | ||
|
|
96225cda0c | ||
|
|
c039a90053 | ||
|
|
d9ccbda0a2 | ||
|
|
49d4a4a9fd | ||
|
|
97c01aa1a7 | ||
|
|
3f9f87f645 | ||
|
|
2ba70f644c | ||
|
|
fa65d5f34e | ||
|
|
a9f0154ca3 | ||
|
|
b6f67e607e | ||
|
|
a5bb5a6aa8 | ||
|
|
8b443d1b45 | ||
|
|
61adff2c9a | ||
|
|
d4cee86e2b | ||
|
|
cb46751c9a | ||
|
|
5e7463dc73 | ||
|
|
9b50c92ec0 | ||
|
|
7ebbcd6c8e | ||
|
|
069f6f22ca | ||
|
|
5bfd8429a8 | ||
|
|
e8ab777dfd | ||
|
|
d63837181f | ||
|
|
c523fc163a | ||
|
|
2e6e61b778 | ||
|
|
4ee56be8a0 | ||
|
|
e5dfc4c221 | ||
|
|
02c1cb001c | ||
|
|
86a309d255 | ||
|
|
fefd3386c1 | ||
|
|
54a4a3606f | ||
|
|
d8396c10ea | ||
|
|
5255a25f03 | ||
|
|
afcb77965c | ||
|
|
451e0200cb | ||
|
|
b0667ce6cb | ||
|
|
b2e085dd21 | ||
|
|
a3a8fcb433 | ||
|
|
7d46c64628 | ||
|
|
419c9e375b | ||
|
|
59bfc68b6f | ||
|
|
8383b445f1 | ||
|
|
8599345a3c | ||
|
|
69fadf026b | ||
|
|
4b2a9bf607 | ||
|
|
a1d4cb8b33 | ||
|
|
e9d0635a74 | ||
|
|
dd07502543 | ||
|
|
56d3e8ef0b | ||
|
|
c328cf0469 | ||
|
|
e34d274d8f | ||
|
|
88f0c68929 | ||
|
|
34d73e8847 | ||
|
|
56a7f8a55f | ||
|
|
afacc19236 | ||
|
|
59ba52fd80 | ||
|
|
5ffb3424f3 | ||
|
|
ac7d36b215 | ||
|
|
cf3a954416 | ||
|
|
6615c553e5 | ||
|
|
0f36679ac7 | ||
|
|
97a986cd11 | ||
|
|
a21d54fab3 | ||
|
|
b80a88ee25 | ||
|
|
c1c528d378 | ||
|
|
1c1a6236ea | ||
|
|
df3f8464b1 | ||
|
|
8625c7217c | ||
|
|
382b4e0801 | ||
|
|
6d2d0c01a3 | ||
|
|
15b099cb71 | ||
|
|
6ba9e70b69 | ||
|
|
14771fa326 | ||
|
|
95398c2a8e | ||
|
|
75bb07078c | ||
|
|
ba7077918d | ||
|
|
6b16970d50 | ||
|
|
20b412b7e1 | ||
|
|
3f965c9121 | ||
|
|
08418246c2 | ||
|
|
e1607b4590 | ||
|
|
be2d75c312 | ||
|
|
a697e71ccd | ||
|
|
e3f0ca6937 | ||
|
|
dafa2a42de | ||
|
|
c96022b97a | ||
|
|
899be6a5a4 | ||
|
|
94b0e9cd22 | ||
|
|
8ac3b5f8d2 | ||
|
|
cebf726997 | ||
|
|
8ddfcdcd9a | ||
|
|
5997c39ac7 | ||
|
|
39b5563465 | ||
|
|
1b07974e93 | ||
|
|
ba81a09b2c | ||
|
|
2e898f137e | ||
|
|
c7e8534650 | ||
|
|
fd2b3c4e8e | ||
|
|
632d078f4b | ||
|
|
cbd77b10f9 | ||
|
|
48500cd350 | ||
|
|
ac53125bb6 | ||
|
|
0f2a18efd1 | ||
|
|
c8f7622e91 | ||
|
|
c2dd5031ba | ||
|
|
2f9fee6451 | ||
|
|
b6dc988e37 | ||
|
|
2ea2231324 | ||
|
|
78d6890508 | ||
|
|
d8584333ef | ||
|
|
69213b19d7 | ||
|
|
0d312f3cd7 | ||
|
|
11197f8a4e | ||
|
|
01e7330e6e | ||
|
|
9d71636051 | ||
|
|
569157f0f0 | ||
|
|
8529a3ec18 | ||
|
|
115d4492da | ||
|
|
6cf9ece3a8 | ||
|
|
84b54d31bf | ||
|
|
57c7109498 | ||
|
|
008dbcb0b4 | ||
|
|
122ca82cad | ||
|
|
9ca7d3eb75 | ||
|
|
5b2bdf58e7 | ||
|
|
b7070ae574 | ||
|
|
85f09e3323 | ||
|
|
03f7001a2b | ||
|
|
36a825b7a0 | ||
|
|
0950f90ddf | ||
|
|
cb22b94fe1 | ||
|
|
019c41bf1a | ||
|
|
607d94dad9 | ||
|
|
aacdbe76f0 | ||
|
|
f340c49a70 | ||
|
|
5bd30f4c72 | ||
|
|
d0f31448b9 | ||
|
|
7b4cf2f0f6 | ||
|
|
fb7dd6aefd | ||
|
|
ce70266d23 | ||
|
|
f1a2b99931 | ||
|
|
e3adcbeec1 | ||
|
|
48af41377f | ||
|
|
0b7b8bb439 | ||
|
|
95d28fabb5 | ||
|
|
efd06ac77d | ||
|
|
1ac497a3e2 | ||
|
|
3dd3781b1b | ||
|
|
a015fc176e | ||
|
|
775c1b65f8 | ||
|
|
8514f086b7 | ||
|
|
4685a0a2ff | ||
|
|
a26dd9416e | ||
|
|
a6466d13a9 | ||
|
|
82b18ae3f8 | ||
|
|
08a204d872 | ||
|
|
4e5331b92d | ||
|
|
985624e35e | ||
|
|
6a091b58a8 | ||
|
|
4ef2d04dec | ||
|
|
f81ccbcc5f | ||
|
|
d900e68787 | ||
|
|
ab38cb1531 | ||
|
|
7f51984063 | ||
|
|
1ad26b5527 | ||
|
|
d2ccc4f270 | ||
|
|
15b4914448 | ||
|
|
3ddeed1945 | ||
|
|
1a20bb5745 | ||
|
|
c4365871b5 | ||
|
|
1fba192319 | ||
|
|
6ffd159b77 | ||
|
|
16d3214db9 | ||
|
|
381bc8aed6 | ||
|
|
3150f5a672 | ||
|
|
eb35fdd1b7 | ||
|
|
9e95e1f0b3 | ||
|
|
4bfbe3c8f7 | ||
|
|
a6d9961b2b | ||
|
|
a670faf646 | ||
|
|
824a9140b6 | ||
|
|
36ec1b6481 | ||
|
|
79a13d0432 | ||
|
|
cbbfb453f2 | ||
|
|
bbe0bfc760 | ||
|
|
98e7310038 | ||
|
|
71836ed1ee | ||
|
|
b681274954 | ||
|
|
619fa832c5 | ||
|
|
f2e65cf6fc | ||
|
|
8f6887d994 | ||
|
|
c46a2e9c08 | ||
|
|
7e44838e51 | ||
|
|
c98dbbf2f6 | ||
|
|
239f7333f1 | ||
|
|
35c9ebdd60 | ||
|
|
879cf6d9dc | ||
|
|
fd70f3ce5d | ||
|
|
cf63c838b2 | ||
|
|
9ee42e5257 | ||
|
|
9253da570f | ||
|
|
beee1c715d | ||
|
|
d9adc45ca9 | ||
|
|
24df5aa78e | ||
|
|
4a58c0b726 | ||
|
|
9fc789e6cf | ||
|
|
1e87a99259 | ||
|
|
ebe2785287 | ||
|
|
aaf3621841 | ||
|
|
6a9c013949 | ||
|
|
d423ff3b6c | ||
|
|
7fae4dbbb8 | ||
|
|
87a4927d03 | ||
|
|
6bebd4e70d | ||
|
|
2e76c66675 | ||
|
|
d6ee6440ad | ||
|
|
abb8daa352 | ||
|
|
ac6f3d6e7c | ||
|
|
0ccb0b85ad | ||
|
|
e3c9ea5ee6 | ||
|
|
26135bd84d | ||
|
|
cdd88dafeb | ||
|
|
177bcddcd9 | ||
|
|
e9bab7913f | ||
|
|
b1034fe139 | ||
|
|
e899b428ca | ||
|
|
aba5c0a07b | ||
|
|
788bc317fa | ||
|
|
732aa09059 | ||
|
|
671b48df4b | ||
|
|
71a18a12d7 | ||
|
|
3576f4f1a1 | ||
|
|
df9616948b | ||
|
|
edf35f4c6e | ||
|
|
ab5c8cfa68 | ||
|
|
847ebeb7b2 | ||
|
|
7a04a11524 | ||
|
|
fc3bced56d | ||
|
|
775efd0d41 | ||
|
|
81d0c3a1a3 | ||
|
|
ee58e28d25 | ||
|
|
889044cf06 | ||
|
|
5f8127a1a5 | ||
|
|
18c0ec4196 | ||
|
|
46426f816a | ||
|
|
2280de707b | ||
|
|
9da2d9f761 | ||
|
|
da8cc0cbaa | ||
|
|
833a9f117d | ||
|
|
d00f5e4797 | ||
|
|
b6ddd21cdb | ||
|
|
92f8fefef1 | ||
|
|
5771a29d33 | ||
|
|
ff3df644c9 | ||
|
|
fc5126f352 | ||
|
|
34af22581e | ||
|
|
3d2530f3a6 | ||
|
|
9817598c2c | ||
|
|
bef5fb02d0 | ||
|
|
9ba0511610 | ||
|
|
6993ca3d71 | ||
|
|
163aa785f9 | ||
|
|
d009f75c28 | ||
|
|
91260bdfc9 | ||
|
|
4ec353f5c2 | ||
|
|
173719020e | ||
|
|
fd00bd4ba2 | ||
|
|
12568336ba | ||
|
|
c692d264ae | ||
|
|
840fe2c5d5 | ||
|
|
49a30f9830 | ||
|
|
c7501868d0 | ||
|
|
5f684e717c | ||
|
|
95a19d1c0e | ||
|
|
b81eb85616 | ||
|
|
bf9d56215a | ||
|
|
31fff6c821 | ||
|
|
9a3c3fee2f | ||
|
|
443edc9760 | ||
|
|
beec044421 | ||
|
|
1aab21c333 | ||
|
|
05aed165dc | ||
|
|
921b450040 | ||
|
|
a8d8ed5cb4 | ||
|
|
c44767cc12 | ||
|
|
947f8d35c4 | ||
|
|
eaaa1a83c7 | ||
|
|
0d36d01afc | ||
|
|
fc571cc191 | ||
|
|
c6718d374c | ||
|
|
5e339f99fc | ||
|
|
4b75daaad6 | ||
|
|
e801879e70 | ||
|
|
33b7861c70 | ||
|
|
5d93ae61ee | ||
|
|
7f9153e826 | ||
|
|
a1cc319293 | ||
|
|
6147b6ee03 | ||
|
|
c285ff68b4 | ||
|
|
5ae3327737 | ||
|
|
e0294cd296 | ||
|
|
226090aeaf | ||
|
|
0603527bb2 | ||
|
|
c781a8efa1 | ||
|
|
82aa4bb857 | ||
|
|
6c98e211e5 | ||
|
|
1e482e172f | ||
|
|
499cf7e294 | ||
|
|
9aaa18865b | ||
|
|
9210ccf8b7 | ||
|
|
563a0299a8 | ||
|
|
30db3c3f06 | ||
|
|
5cc21bc768 | ||
|
|
9deea66679 | ||
|
|
cfc086f3ea | ||
|
|
28730308a5 | ||
|
|
89edbcf8ff | ||
|
|
598e387f7c | ||
|
|
2d494893fc | ||
|
|
a44c76563e | ||
|
|
c41b68e9cb | ||
|
|
9b21231204 | ||
|
|
83fa7d0f2c | ||
|
|
fff13d5ef2 | ||
|
|
5347b78469 | ||
|
|
3024ddca83 |
+31
-10
@@ -1,10 +1,8 @@
|
||||
name: Docs WorkFlow
|
||||
name: Docs WorkFlow - Develop Tag 📚
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
- develop
|
||||
|
||||
permissions:
|
||||
@@ -16,11 +14,34 @@ jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
- name: 🔄 Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
python-version: 3.x
|
||||
- run: pip install mkdocs-material
|
||||
- run: pip install "mkdocstrings[python]"
|
||||
- run: pip install "mkdocs-material[imaging]"
|
||||
- run: mkdocs gh-deploy --force
|
||||
fetch-depth: 0
|
||||
- name: 🐍 Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: 📦 Install mkdocs-material
|
||||
run: pip install "mkdocs-material[all]"
|
||||
- name: 📦 Install mkdocstrings[python]
|
||||
run: pip install "mkdocstrings[python]"
|
||||
- name: 📦 Install mkdocs-material[imaging]
|
||||
run: pip install "mkdocs-material[imaging]"
|
||||
- name: 📦 Install mike
|
||||
run: pip install "mike"
|
||||
- name: 📦 Install mkdocs-git-revision-date-localized-plugin
|
||||
run: pip install "mkdocs-git-revision-date-localized-plugin"
|
||||
- name: 📦 Install JupyterLab
|
||||
run: pip install jupyterlab
|
||||
- name: 📦 Install mkdocs-jupyter
|
||||
run: pip install mkdocs-jupyter
|
||||
- name: 📦 Install mkdocs-git-committers-plugin-2
|
||||
run: pip install mkdocs-git-committers-plugin-2
|
||||
- name: ⚙️ Configure git for github-actions
|
||||
run: |
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
- name: 🚀 Deploy MkDoc-Material with mike
|
||||
run: |
|
||||
MKDOCS_GIT_COMMITTERS_APIKEY=${{ secrets.GITHUB_TOKEN }} mike deploy --push develop
|
||||
|
||||
@@ -3,8 +3,6 @@ on:
|
||||
push:
|
||||
tags:
|
||||
- '[0-9]+.[0-9]+[0-9]+.[0-9]'
|
||||
- '[0-9]+.[0-9]+[0-9]+.[0-9]'
|
||||
- '[0-9]+.[0-9]+[0-9]+.[0-9]'
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -37,6 +37,7 @@ jobs:
|
||||
matplotlib==3.5.0 \
|
||||
numpy==1.21.2 \
|
||||
opencv-python==4.5.5.64 \
|
||||
Pillow==10.1.0 \
|
||||
packaging==23.2 \
|
||||
pluggy==1.3.0 \
|
||||
pyparsing==3.1.1 \
|
||||
|
||||
@@ -133,9 +133,6 @@ dmypy.json
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# Notebooks
|
||||
notebooks/
|
||||
|
||||
# OSX folder attributes
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
|
||||
@@ -14,7 +14,6 @@ repos:
|
||||
exclude: test/.*\.py
|
||||
- id: check-yaml
|
||||
exclude: mkdocs.yml
|
||||
- id: check-docstring-first
|
||||
- id: check-executables-have-shebangs
|
||||
- id: check-toml
|
||||
- id: check-case-conflict
|
||||
@@ -30,7 +29,7 @@ repos:
|
||||
|
||||
|
||||
- repo: https://github.com/PyCQA/bandit
|
||||
rev: '1.7.6'
|
||||
rev: '1.7.7'
|
||||
hooks:
|
||||
- id: bandit
|
||||
args: ["-c", "pyproject.toml"]
|
||||
@@ -47,7 +46,7 @@ repos:
|
||||
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.1.14
|
||||
rev: v0.2.2
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix, --exit-non-zero-on-fix]
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ url: 'https://roboflow.github.io/supervision/'
|
||||
abstract: >-
|
||||
supervision features a range of utilities for use in
|
||||
computer vision projects, from detections processing and
|
||||
filtering to confusion matrix calcuation.
|
||||
filtering to confusion matrix calculation.
|
||||
keywords:
|
||||
- computer vision
|
||||
- image processing
|
||||
|
||||
@@ -112,6 +112,20 @@ To run the documentation, install the project requirements with `poetry install
|
||||
|
||||
You can learn more about mkdocs on the [mkdocs website](https://www.mkdocs.org/).
|
||||
|
||||
## 🧑🍳 cookbooks
|
||||
|
||||
We are always looking for new examples and cookbooks to add to the `supervision`
|
||||
documentation. If you have a use case that you think would be helpful to others, please
|
||||
submit a PR with your example. Here are some guidelines for submitting a new example:
|
||||
|
||||
- Create a new notebook in the [`docs/nodebooks`](https://github.com/roboflow/supervision/tree/develop/docs/notebooks) folder.
|
||||
- Add a link to the new notebook in [`docs/theme/cookbooks.html`](https://github.com/roboflow/supervision/blob/develop/docs/theme/cookbooks.html). Make sure to add the path to the new notebook, as well as a title, labels, author and supervision version.
|
||||
- Use the [Count Objects Crossing the Line](https://supervision.roboflow.com/develop/notebooks/count-objects-crossing-the-line/) example as a template for your new example.
|
||||
- Freeze the version of `supervision` you are using.
|
||||
- Place an appropriate Open in Colab button at the top of the notebook. You can find an example of such a button in the aforementioned `Count Objects Crossing the Line` cookbook.
|
||||
- Notebook should be self-contained. If you rely on external data ( videos, images, etc.) or libraries, include download and installation commands in the notebook.
|
||||
- Annotate the code with appropriate comments, including links to the documentation describing each of the tools you have used.
|
||||
|
||||
## 🧪 tests
|
||||
|
||||
[`pytests`](https://docs.pytest.org/en/7.1.x/) is used to run our tests.
|
||||
|
||||
@@ -21,13 +21,15 @@
|
||||
[](https://colab.research.google.com/github/roboflow/supervision/blob/main/demo.ipynb)
|
||||
[](https://huggingface.co/spaces/Roboflow/Annotators)
|
||||
[](https://discord.gg/GbfgXGJ8Bk)
|
||||
|
||||
[](https://squidfunk.github.io/mkdocs-material/)
|
||||
</div>
|
||||
|
||||
## 👋 hello
|
||||
|
||||
**We write your reusable computer vision tools.** Whether you need to load your dataset from your hard drive, draw detections on an image or video, or count how many detections are in a zone. You can count on us! 🤝
|
||||
|
||||
[](https://github.com/orgs/roboflow/projects/10)
|
||||
|
||||
## 💻 install
|
||||
|
||||
Pip install the supervision package in a
|
||||
@@ -43,7 +45,7 @@ Read more about desktop, headless, and local installation in our [guide](https:/
|
||||
|
||||
### models
|
||||
|
||||
Supervision was designed to be model agnostic. Just plug in any classification, detection, or segmentation model. For your convenience, we have created [connectors](https://supervision.roboflow.com/detection/core/#detections) for the most popular libraries like Ultralytics, Transformers, or MMDetection.
|
||||
Supervision was designed to be model agnostic. Just plug in any classification, detection, or segmentation model. For your convenience, we have created [connectors](https://supervision.roboflow.com/latest/detection/core/#detections) for the most popular libraries like Ultralytics, Transformers, or MMDetection.
|
||||
|
||||
```python
|
||||
import cv2
|
||||
@@ -85,7 +87,7 @@ len(detections)
|
||||
|
||||
### annotators
|
||||
|
||||
Supervision offers a wide range of highly customizable [annotators](https://supervision.roboflow.com/annotators/), allowing you to compose the perfect visualization for your use case.
|
||||
Supervision offers a wide range of highly customizable [annotators](https://supervision.roboflow.com/latest/annotators/), allowing you to compose the perfect visualization for your use case.
|
||||
|
||||
```python
|
||||
import cv2
|
||||
@@ -105,7 +107,7 @@ https://github.com/roboflow/supervision/assets/26109316/691e219c-0565-4403-9218-
|
||||
|
||||
### datasets
|
||||
|
||||
Supervision provides a set of [utils](https://supervision.roboflow.com/datasets/) that allow you to load, split, merge, and save datasets in one of the supported formats.
|
||||
Supervision provides a set of [utils](https://supervision.roboflow.com/latest/datasets/) that allow you to load, split, merge, and save datasets in one of the supported formats.
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Supervision Cookbooks
|
||||
|
||||
## About
|
||||
|
||||
The Roboflow Cookbook is an open-source collection of examples and guides for building with the [Roboflow Ecosystem](https://roboflow.com/).
|
||||
|
||||
Cookbooks are meant to be common and small scoped computer vision tasks that can allow a user to quickly hit the ground running. Learning is the main goal and purpose.
|
||||
|
||||
Some examples require a Roboflow API key. You can [create a free account here](https://app.roboflow.com/login).
|
||||
|
||||
## Other Resources
|
||||
|
||||
Other than Cookbooks, Roboflow offers other learning resources including:
|
||||
|
||||
- A set of open source tools found on [Github](https://github.com/roboflow).
|
||||
- A [blog](https://blog.roboflow.com/) detailing interesting tutorials, computer vision news, and product updates.
|
||||
- A [Youtube channel](https://www.youtube.com/roboflow) with end to end commputer vision projects.
|
||||
- A [forum](https://discuss.roboflow.com/) for sharing feedback, discussions, and other questions.
|
||||
- The worlds largest [open source collection](https://universe.roboflow.com/) of images, datasets, and fine-tuned models.
|
||||
Vendored
+17
-5
@@ -12,11 +12,23 @@
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"[](https://badge.fury.io/py/supervision)\n",
|
||||
"[](https://pypistats.org/packages/supervision)\n",
|
||||
"[](https://github.com/roboflow/supervision/blob/main/LICENSE.md)\n",
|
||||
"[](https://badge.fury.io/py/supervision)\n",
|
||||
"[](https://github.com/roboflow/supervision)\n",
|
||||
"<p align=\"center\">\n",
|
||||
" <a href=\"https://badge.fury.io/py/supervision\"><img src=\"https://badge.fury.io/py/supervision.svg\" alt=\"version\"></a>\n",
|
||||
" <a href=\"https://pypistats.org/packages/supervision\"><img src=\"https://img.shields.io/pypi/dm/supervision\" alt=\"downloads\"></a>\n",
|
||||
" <a href=\"https://github.com/roboflow/supervision/blob/main/LICENSE.md\"><img src=\"https://img.shields.io/pypi/l/supervision\" alt=\"license\"></a>\n",
|
||||
" <a href=\"https://badge.fury.io/py/supervision\"><img src=\"https://img.shields.io/pypi/pyversions/supervision\" alt=\"python-version\"></a>\n",
|
||||
" <a href=\"https://github.com/roboflow/supervision\"><img src=\"https://badges.aleen42.com/src/github.svg\" alt=\"GitHub\"></a>\n",
|
||||
"</p>\n",
|
||||
"\n",
|
||||
"<p align=\"center\">\n",
|
||||
" <a href=\"https://colab.research.google.com/github/roboflow/supervision/blob/main/demo.ipynb\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Colab\"></a>\n",
|
||||
" <a href=\"https://kaggle.com/kernels/welcome?src=https://github.com/roboflow/supervision/blob/main/demo.ipynb\"><img src=\"https://kaggle.com/static/images/open-in-kaggle.svg\" alt=\"Kaggle\"></a>\n",
|
||||
" <a href=\"https://studiolab.sagemaker.aws/import/github/roboflow/supervision/blob/main/demo.ipynb\"><img src=\"https://raw.githubusercontent.com/roboflow-ai/notebooks/main/assets/badges/sage-maker.svg\" alt=\"SageMaker\"></a>\n",
|
||||
" <a href=\"https://nbviewer.jupyter.org/github/roboflow/supervision/blob/main/demo.ipynb\"><img src=\"https://img.shields.io/badge/Open_in_Nbviewer-F37626.svg?logo=Jupyter&logoColor=white\" alt=\"nbviewer\">\n",
|
||||
" <a href=\"https://mybinder.org/v2/gh/roboflow/supervision/develop?labpath=demo.ipynb\"><img src=\"https://mybinder.org/badge_logo.svg\" alt=\"Binder\"></a>\n",
|
||||
" <a href=\"https://github.com/roboflow/supervision/raw/main/demo.ipynb\" download><img src=\"https://img.shields.io/badge/Download-Notebook-A351FB.svg\" alt=\"Download\"></a>\n",
|
||||
"</p>\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"We write your reusable computer vision tools. Whether you need to load your dataset from your hard drive, draw detections on an image or video, or count how many detections are in a zone. You can count on us! 🤝\n",
|
||||
"\n",
|
||||
|
||||
+59
-19
@@ -3,6 +3,8 @@ comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Annotators
|
||||
|
||||
=== "BoundingBox"
|
||||
|
||||
```python
|
||||
@@ -378,78 +380,116 @@ status: new
|
||||
|
||||
</div>
|
||||
|
||||
## BoundingBoxAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.BoundingBoxAnnotator">BoundingBoxAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.BoundingBoxAnnotator
|
||||
|
||||
## RoundBoxAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.RoundBoxAnnotator">RoundBoxAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.RoundBoxAnnotator
|
||||
|
||||
## BoxCornerAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.BoxCornerAnnotator">BoxCornerAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.BoxCornerAnnotator
|
||||
|
||||
## OrientedBoxAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.OrientedBoxAnnotator">OrientedBoxAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.OrientedBoxAnnotator
|
||||
|
||||
## ColorAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.ColorAnnotator">ColorAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.ColorAnnotator
|
||||
|
||||
## CircleAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.CircleAnnotator">CircleAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.CircleAnnotator
|
||||
|
||||
## DotAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.DotAnnotator">DotAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.DotAnnotator
|
||||
|
||||
## TriangleAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.TriangleAnnotator">TriangleAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.TriangleAnnotator
|
||||
|
||||
## EllipseAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.EllipseAnnotator">EllipseAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.EllipseAnnotator
|
||||
|
||||
## HaloAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.HaloAnnotator">HaloAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.HaloAnnotator
|
||||
|
||||
## PercentageBarAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.PercentageBarAnnotator">PercentageBarAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.PercentageBarAnnotator
|
||||
|
||||
## HeatMapAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.HeatMapAnnotator">HeatMapAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.HeatMapAnnotator
|
||||
|
||||
## MaskAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.MaskAnnotator">MaskAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.MaskAnnotator
|
||||
|
||||
## PolygonAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.PolygonAnnotator">PolygonAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.PolygonAnnotator
|
||||
|
||||
## LabelAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.LabelAnnotator">LabelAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.LabelAnnotator
|
||||
|
||||
## BlurAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.BlurAnnotator">BlurAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.BlurAnnotator
|
||||
|
||||
## PixelateAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.PixelateAnnotator">PixelateAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.PixelateAnnotator
|
||||
|
||||
## TraceAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.TraceAnnotator">TraceAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.TraceAnnotator
|
||||
|
||||
## ColorLookup
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.ColorLookup">ColorLookup</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.utils.ColorLookup
|
||||
|
||||
+10
-4
@@ -2,10 +2,12 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
# Assets
|
||||
|
||||
Supervision offers an assets download utility that allows you to download video files
|
||||
that you can use in your demos.
|
||||
|
||||
## install extra
|
||||
## Install extra
|
||||
|
||||
To install the Supervision assets utility, you can use `pip`. This utility is available
|
||||
as an extra within the Supervision package.
|
||||
@@ -13,13 +15,17 @@ as an extra within the Supervision package.
|
||||
!!! example "pip install"
|
||||
|
||||
```bash
|
||||
pip install supervision[assets]
|
||||
pip install "supervision[assets]"
|
||||
```
|
||||
|
||||
## download_assets
|
||||
<div class="md-typeset">
|
||||
<h2>download_assets</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.assets.downloader.download_assets
|
||||
|
||||
## VideoAssets
|
||||
<div class="md-typeset">
|
||||
<h2>VideoAssets</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.assets.list.VideoAssets
|
||||
|
||||
+88
-88
@@ -1,6 +1,6 @@
|
||||
### 0.18.0 <small>January 25, 2024</small>
|
||||
|
||||
- Added [#633](https://github.com/roboflow/supervision/pull/720): [`sv.PercentageBarAnnotator`](https://supervision.roboflow.com/annotators/#percentagebarannotator) allowing to annotate images and videos with percentage values representing confidence or other custom property.
|
||||
- Added [#633](https://github.com/roboflow/supervision/pull/720): [`sv.PercentageBarAnnotator`](/0.18.0/annotators/#percentagebarannotator) allowing to annotate images and videos with percentage values representing confidence or other custom property.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -15,9 +15,9 @@
|
||||
... )
|
||||
```
|
||||
|
||||
- Added [#702](https://github.com/roboflow/supervision/pull/702): [`sv.RoundBoxAnnotator`](https://supervision.roboflow.com/annotators/#roundboxannotator) allowing to annotate images and videos with rounded corners bounding boxes.
|
||||
- Added [#702](https://github.com/roboflow/supervision/pull/702): [`sv.RoundBoxAnnotator`](/0.18.0/annotators/#roundboxannotator) allowing to annotate images and videos with rounded corners bounding boxes.
|
||||
|
||||
- Added [#770](https://github.com/roboflow/supervision/pull/770): [`sv.OrientedBoxAnnotator`](https://supervision.roboflow.com/annotators/#orientedboxannotator) allowing to annotate images and videos with OBB (Oriented Bounding Boxes).
|
||||
- Added [#770](https://github.com/roboflow/supervision/pull/770): [`sv.OrientedBoxAnnotator`](/0.18.0/annotators/#orientedboxannotator) allowing to annotate images and videos with OBB (Oriented Bounding Boxes).
|
||||
|
||||
```python
|
||||
import cv2
|
||||
@@ -37,9 +37,9 @@ annotated_frame = oriented_box_annotator.annotate(
|
||||
)
|
||||
```
|
||||
|
||||
- Added [#696](https://github.com/roboflow/supervision/pull/696): [`sv.DetectionsSmoother`](https://supervision.roboflow.com/detection/tools/smoother/#detection-smoother) allowing for smoothing detections over multiple frames in video tracking.
|
||||
- Added [#696](https://github.com/roboflow/supervision/pull/696): [`sv.DetectionsSmoother`](/0.18.0/detection/tools/smoother/#detection-smoother) allowing for smoothing detections over multiple frames in video tracking.
|
||||
|
||||
- Added [#769](https://github.com/roboflow/supervision/pull/769): [`sv.ColorPalette.from_matplotlib`](https://supervision.roboflow.com/draw/color/#supervision.draw.color.ColorPalette.from_matplotlib) allowing users to create a `sv.ColorPalette` instance from a Matplotlib color palette.
|
||||
- Added [#769](https://github.com/roboflow/supervision/pull/769): [`sv.ColorPalette.from_matplotlib`](/0.18.0/draw/color/#supervision.draw.color.ColorPalette.from_matplotlib) allowing users to create a `sv.ColorPalette` instance from a Matplotlib color palette.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -48,35 +48,35 @@ annotated_frame = oriented_box_annotator.annotate(
|
||||
ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
|
||||
```
|
||||
|
||||
- Changed [#770](https://github.com/roboflow/supervision/pull/770): [`sv.Detections.from_ultralytics`](https://supervision.roboflow.com/detection/core/#supervision.detection.core.Detections.from_ultralytics) adding support for OBB (Oriented Bounding Boxes).
|
||||
- Changed [#770](https://github.com/roboflow/supervision/pull/770): [`sv.Detections.from_ultralytics`](/0.18.0/detection/core/#supervision.detection.core.Detections.from_ultralytics) adding support for OBB (Oriented Bounding Boxes).
|
||||
|
||||
- Changed [#735](https://github.com/roboflow/supervision/pull/735): [`sv.LineZone`](https://supervision.roboflow.com/detection/tools/line_zone/#linezone) to now accept a list of specific box anchors that must cross the line for a detection to be counted. This update marks a significant improvement from the previous requirement, where all four box corners were necessary. Users can now specify a single anchor, such as `sv.Position.BOTTOM_CENTER`, or any other combination of anchors defined as `List[sv.Position]`.
|
||||
- Changed [#735](https://github.com/roboflow/supervision/pull/735): [`sv.LineZone`](/0.18.0/detection/tools/line_zone/#linezone) to now accept a list of specific box anchors that must cross the line for a detection to be counted. This update marks a significant improvement from the previous requirement, where all four box corners were necessary. Users can now specify a single anchor, such as `sv.Position.BOTTOM_CENTER`, or any other combination of anchors defined as `List[sv.Position]`.
|
||||
|
||||
- Changed [#756](https://github.com/roboflow/supervision/pull/756): [`sv.Color`](https://supervision.roboflow.com/draw/color/#color)'s and [`sv.ColorPalette`](https://supervision.roboflow.com/draw/color/#colorpalette)'s method of accessing predefined colors, transitioning from a function-based approach (`sv.Color.red()`) to a more intuitive and conventional property-based method (`sv.Color.RED`).
|
||||
- Changed [#756](https://github.com/roboflow/supervision/pull/756): [`sv.Color`](/0.18.0/draw/color/#color)'s and [`sv.ColorPalette`](/0.18.0/draw/color/#colorpalette)'s method of accessing predefined colors, transitioning from a function-based approach (`sv.Color.red()`) to a more intuitive and conventional property-based method (`sv.Color.RED`).
|
||||
|
||||
!!! warning
|
||||
!!! failure "Deprecated"
|
||||
|
||||
`sv.ColorPalette.default()` is deprecated and will be removed in `supervision-0.21.0`. Use `sv.ColorPalette.DEFAULT` instead.
|
||||
`sv.ColorPalette.default()` is deprecated and will be removed in `supervision-0.22.0`. Use `sv.ColorPalette.DEFAULT` instead.
|
||||
|
||||
|
||||
- Changed [#769](https://github.com/roboflow/supervision/pull/769): [`sv.ColorPalette.DEFAULT`](https://supervision.roboflow.com/draw/color/#colorpalette) value, giving users a more extensive set of annotation colors.
|
||||
- Changed [#769](https://github.com/roboflow/supervision/pull/769): [`sv.ColorPalette.DEFAULT`](/0.18.0/draw/color/#colorpalette) value, giving users a more extensive set of annotation colors.
|
||||
|
||||
- Changed [#677](https://github.com/roboflow/supervision/pull/677): `sv.Detections.from_roboflow` to [`sv.Detections.from_inference`](https://supervision.roboflow.com/detection/core/#supervision.detection.core.Detections.from_inference) streamlining its functionality to be compatible with both the both [inference](https://github.com/roboflow/inference) pip package and the Robloflow [hosted API](https://docs.roboflow.com/deploy/hosted-api).
|
||||
- Changed [#677](https://github.com/roboflow/supervision/pull/677): `sv.Detections.from_roboflow` to [`sv.Detections.from_inference`](/0.18.0/detection/core/#supervision.detection.core.Detections.from_inference) streamlining its functionality to be compatible with both the both [inference](https://github.com/roboflow/inference) pip package and the Robloflow [hosted API](https://docs.roboflow.com/deploy/hosted-api).
|
||||
|
||||
!!! warning
|
||||
!!! failure "Deprecated"
|
||||
|
||||
`Detections.from_roboflow()` is deprecated and will be removed in `supervision-0.21.0`. Use `Detections.from_inference` instead.
|
||||
`Detections.from_roboflow()` is deprecated and will be removed in `supervision-0.22.0`. Use `Detections.from_inference` instead.
|
||||
|
||||
|
||||
- Fixed [#735](https://github.com/roboflow/supervision/pull/735): [`sv.LineZone`](https://supervision.roboflow.com/detection/tools/line_zone/#linezone) functionality to accurately update the counter when an object crosses a line from any direction, including from the side. This enhancement enables more precise tracking and analytics, such as calculating individual in/out counts for each lane on the road.
|
||||
- Fixed [#735](https://github.com/roboflow/supervision/pull/735): [`sv.LineZone`](/0.18.0/detection/tools/line_zone/#linezone) functionality to accurately update the counter when an object crosses a line from any direction, including from the side. This enhancement enables more precise tracking and analytics, such as calculating individual in/out counts for each lane on the road.
|
||||
|
||||
### 0.17.0 <small>December 06, 2023</small>
|
||||
|
||||
- Added [#633](https://github.com/roboflow/supervision/pull/633): [`sv.PixelateAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.PixelateAnnotator) allowing to pixelate objects on images and videos.
|
||||
- Added [#633](https://github.com/roboflow/supervision/pull/633): [`sv.PixelateAnnotator`](/0.17.0/annotators/#supervision.annotators.core.PixelateAnnotator) allowing to pixelate objects on images and videos.
|
||||
|
||||
- Added [#652](https://github.com/roboflow/supervision/pull/652): [`sv.TriangleAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.TriangleAnnotator) allowing to annotate images and videos with triangle markers.
|
||||
- Added [#652](https://github.com/roboflow/supervision/pull/652): [`sv.TriangleAnnotator`](/0.17.0/annotators/#supervision.annotators.core.TriangleAnnotator) allowing to annotate images and videos with triangle markers.
|
||||
|
||||
- Added [#602](https://github.com/roboflow/supervision/pull/602): [`sv.PolygonAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.PolygonAnnotator) allowing to annotate images and videos with segmentation mask outline.
|
||||
- Added [#602](https://github.com/roboflow/supervision/pull/602): [`sv.PolygonAnnotator`](/0.17.0/annotators/#supervision.annotators.core.PolygonAnnotator) allowing to annotate images and videos with segmentation mask outline.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -91,7 +91,7 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
|
||||
... )
|
||||
```
|
||||
|
||||
- Added [#476](https://github.com/roboflow/supervision/pull/476): [`sv.assets`](https://supervision.roboflow.com/assets/) allowing download of video files that you can use in your demos.
|
||||
- Added [#476](https://github.com/roboflow/supervision/pull/476): [`sv.assets`](/0.18.0/assets/) allowing download of video files that you can use in your demos.
|
||||
|
||||
```python
|
||||
>>> from supervision.assets import download_assets, VideoAssets
|
||||
@@ -99,35 +99,35 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
|
||||
"vehicles.mp4"
|
||||
```
|
||||
|
||||
- Added [#605](https://github.com/roboflow/supervision/pull/605): [`Position.CENTER_OF_MASS`](https://supervision.roboflow.com/geometry/core/#position) allowing to place labels in center of mass of segmentation masks.
|
||||
- Added [#605](https://github.com/roboflow/supervision/pull/605): [`Position.CENTER_OF_MASS`](/0.17.0/geometry/core/#position) allowing to place labels in center of mass of segmentation masks.
|
||||
|
||||
- Added [#651](https://github.com/roboflow/supervision/pull/651): [`sv.scale_boxes`](https://supervision.roboflow.com/detection/utils/#supervision.detection.utils.scale_boxes) allowing to scale [`sv.Detections.xyxy`](https://supervision.roboflow.com/detection/core/#supervision.detection.core.Detections) values.
|
||||
- Added [#651](https://github.com/roboflow/supervision/pull/651): [`sv.scale_boxes`](/0.17.0/detection/utils/#supervision.detection.utils.scale_boxes) allowing to scale [`sv.Detections.xyxy`](/0.17.0/detection/core/#supervision.detection.core.Detections) values.
|
||||
|
||||
- Added [#637](https://github.com/roboflow/supervision/pull/637): [`sv.calculate_dynamic_text_scale`](https://supervision.roboflow.com/draw/utils/#supervision.draw.utils.calculate_dynamic_text_scale) and [`sv.calculate_dynamic_line_thickness`](https://supervision.roboflow.com/draw/utils/#supervision.draw.utils.calculate_dynamic_line_thickness) allowing text scale and line thickness to match image resolution.
|
||||
- Added [#637](https://github.com/roboflow/supervision/pull/637): [`sv.calculate_dynamic_text_scale`](/0.17.0/draw/utils/#supervision.draw.utils.calculate_dynamic_text_scale) and [`sv.calculate_dynamic_line_thickness`](/0.17.0/draw/utils/#supervision.draw.utils.calculate_dynamic_line_thickness) allowing text scale and line thickness to match image resolution.
|
||||
|
||||
- Added [#620](https://github.com/roboflow/supervision/pull/620): [`sv.Color.as_hex`](https://supervision.roboflow.com/draw/color/#supervision.draw.color.Color.as_hex) allowing to extract color value in HEX format.
|
||||
- Added [#620](https://github.com/roboflow/supervision/pull/620): [`sv.Color.as_hex`](/0.17.0/draw/color/#supervision.draw.color.Color.as_hex) allowing to extract color value in HEX format.
|
||||
|
||||
- Added [#572](https://github.com/roboflow/supervision/pull/572): [`sv.Classifications.from_timm`](https://supervision.roboflow.com/classification/core/#supervision.classification.core.Classifications.from_timm) allowing to load classification result from [timm](https://huggingface.co/docs/hub/timm) models.
|
||||
- Added [#572](https://github.com/roboflow/supervision/pull/572): [`sv.Classifications.from_timm`](/0.17.0/classification/core/#supervision.classification.core.Classifications.from_timm) allowing to load classification result from [timm](https://huggingface.co/docs/hub/timm) models.
|
||||
|
||||
- Added [#478](https://github.com/roboflow/supervision/pull/478): [`sv.Classifications.from_clip`](https://supervision.roboflow.com/classification/core/#supervision.classification.core.Classifications.from_clip) allowing to load classification result from [clip](https://github.com/openai/clip) model.
|
||||
- Added [#478](https://github.com/roboflow/supervision/pull/478): [`sv.Classifications.from_clip`](/0.17.0/classification/core/#supervision.classification.core.Classifications.from_clip) allowing to load classification result from [clip](https://github.com/openai/clip) model.
|
||||
|
||||
- Added [#571](https://github.com/roboflow/supervision/pull/571): [`sv.Detections.from_azure_analyze_image`](https://supervision.roboflow.com/detection/core/#supervision.detection.core.Detections.from_azure_analyze_image) allowing to load detection results from [Azure Image Analysis](https://learn.microsoft.com/en-us/azure/ai-services/computer-vision/concept-object-detection-40).
|
||||
- Added [#571](https://github.com/roboflow/supervision/pull/571): [`sv.Detections.from_azure_analyze_image`](/0.17.0/detection/core/#supervision.detection.core.Detections.from_azure_analyze_image) allowing to load detection results from [Azure Image Analysis](https://learn.microsoft.com/en-us/azure/ai-services/computer-vision/concept-object-detection-40).
|
||||
|
||||
- Changed [#646](https://github.com/roboflow/supervision/pull/646): `sv.BoxMaskAnnotator` renaming it to [`sv.ColorAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.ColorAnnotator).
|
||||
- Changed [#646](https://github.com/roboflow/supervision/pull/646): `sv.BoxMaskAnnotator` renaming it to [`sv.ColorAnnotator`](/0.17.0/annotators/#supervision.annotators.core.ColorAnnotator).
|
||||
|
||||
- Changed [#606](https://github.com/roboflow/supervision/pull/606): [`sv.MaskAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.MaskAnnotator) to make it **5x faster**.
|
||||
- Changed [#606](https://github.com/roboflow/supervision/pull/606): [`sv.MaskAnnotator`](/0.17.0/annotators/#supervision.annotators.core.MaskAnnotator) to make it **5x faster**.
|
||||
|
||||
- Fixed [#584](https://github.com/roboflow/supervision/pull/584): [`sv.DetectionDataset.from_yolo`](https://supervision.roboflow.com/datasets/#supervision.dataset.core.DetectionDataset.from_yolo) to ignore empty lines in annotation files.
|
||||
- Fixed [#584](https://github.com/roboflow/supervision/pull/584): [`sv.DetectionDataset.from_yolo`](/0.17.0/datasets/#supervision.dataset.core.DetectionDataset.from_yolo) to ignore empty lines in annotation files.
|
||||
|
||||
- Fixed [#555](https://github.com/roboflow/supervision/pull/555): [`sv.BlurAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.BlurAnnotator) to trim negative coordinates before bluring detections.
|
||||
- Fixed [#555](https://github.com/roboflow/supervision/pull/555): [`sv.BlurAnnotator`](/0.17.0/annotators/#supervision.annotators.core.BlurAnnotator) to trim negative coordinates before bluring detections.
|
||||
|
||||
- Fixed [#511](https://github.com/roboflow/supervision/pull/511): [`sv.TraceAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.TraceAnnotator) to respect trace position.
|
||||
- Fixed [#511](https://github.com/roboflow/supervision/pull/511): [`sv.TraceAnnotator`](/0.17.0/annotators/#supervision.annotators.core.TraceAnnotator) to respect trace position.
|
||||
|
||||
### 0.16.0 <small>October 19, 2023</small>
|
||||
|
||||
- Added [#422](https://github.com/roboflow/supervision/pull/422): [`sv.BoxMaskAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.BoxMaskAnnotator) allowing to annotate images and videos with mox masks.
|
||||
- Added [#422](https://github.com/roboflow/supervision/pull/422): [`sv.BoxMaskAnnotator`](/0.16.0/annotators/#supervision.annotators.core.BoxMaskAnnotator) allowing to annotate images and videos with mox masks.
|
||||
|
||||
- Added [#433](https://github.com/roboflow/supervision/pull/433): [`sv.HaloAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.HaloAnnotator) allowing to annotate images and videos with halo effect.
|
||||
- Added [#433](https://github.com/roboflow/supervision/pull/433): [`sv.HaloAnnotator`](/0.16.0/annotators/#supervision.annotators.core.HaloAnnotator) allowing to annotate images and videos with halo effect.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -142,46 +142,46 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
|
||||
... )
|
||||
```
|
||||
|
||||
- Added [#466](https://github.com/roboflow/supervision/pull/466): [`sv.HeatMapAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.HeatMapAnnotator) allowing to annotate videos with heat maps.
|
||||
- Added [#466](https://github.com/roboflow/supervision/pull/466): [`sv.HeatMapAnnotator`](/0.16.0/annotators/#supervision.annotators.core.HeatMapAnnotator) allowing to annotate videos with heat maps.
|
||||
|
||||
- Added [#492](https://github.com/roboflow/supervision/pull/492): [`sv.DotAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.DotAnnotator) allowing to annotate images and videos with dots.
|
||||
- Added [#492](https://github.com/roboflow/supervision/pull/492): [`sv.DotAnnotator`](/0.16.0/annotators/#supervision.annotators.core.DotAnnotator) allowing to annotate images and videos with dots.
|
||||
|
||||
- Added [#449](https://github.com/roboflow/supervision/pull/449): [`sv.draw_image`](https://supervision.roboflow.com/draw/utils/#supervision.draw.utils.draw_image) allowing to draw an image onto a given scene with specified opacity and dimensions.
|
||||
- Added [#449](https://github.com/roboflow/supervision/pull/449): [`sv.draw_image`](/0.16.0/draw/utils/#supervision.draw.utils.draw_image) allowing to draw an image onto a given scene with specified opacity and dimensions.
|
||||
|
||||
- Added [#280](https://github.com/roboflow/supervision/pull/280): [`sv.FPSMonitor`](https://supervision.roboflow.com/utils/video/#supervision.utils.video.FPSMonitor) for monitoring frames per second (FPS) to benchmark latency.
|
||||
- Added [#280](https://github.com/roboflow/supervision/pull/280): [`sv.FPSMonitor`](/0.16.0/utils/video/#supervision.utils.video.FPSMonitor) for monitoring frames per second (FPS) to benchmark latency.
|
||||
|
||||
- Added [#454](https://github.com/roboflow/supervision/pull/454): 🤗 Hugging Face Annotators [space](https://huggingface.co/spaces/Roboflow/Annotators).
|
||||
|
||||
- Changed [#482](https://github.com/roboflow/supervision/pull/482): [`sv.LineZone.tigger`](https://supervision.roboflow.com/detection/tools/line_zone/#supervision.detection.line_counter.LineZone.trigger) now return `Tuple[np.ndarray, np.ndarray]`. The first array indicates which detections have crossed the line from outside to inside. The second array indicates which detections have crossed the line from inside to outside.
|
||||
- Changed [#482](https://github.com/roboflow/supervision/pull/482): [`sv.LineZone.trigger`](/0.16.0/detection/tools/line_zone/#supervision.detection.line_counter.LineZone.trigger) now return `Tuple[np.ndarray, np.ndarray]`. The first array indicates which detections have crossed the line from outside to inside. The second array indicates which detections have crossed the line from inside to outside.
|
||||
|
||||
- Changed [#465](https://github.com/roboflow/supervision/pull/465): Annotator argument name from `color_map: str` to `color_lookup: ColorLookup` enum to increase type safety.
|
||||
|
||||
- Changed [#426](https://github.com/roboflow/supervision/pull/426): [`sv.MaskAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.MaskAnnotator) allowing 2x faster annotation.
|
||||
- Changed [#426](https://github.com/roboflow/supervision/pull/426): [`sv.MaskAnnotator`](/0.16.0/annotators/#supervision.annotators.core.MaskAnnotator) allowing 2x faster annotation.
|
||||
|
||||
- Fixed [#477](https://github.com/roboflow/supervision/pull/477): Poetry env definition allowing proper local installation.
|
||||
|
||||
- Fixed [#430](https://github.com/roboflow/supervision/pull/430): [`sv.ByteTrack`](https://supervision.roboflow.com/trackers/#supervision.tracker.byte_tracker.core.ByteTrack) to return `np.array([], dtype=int)` when `svDetections` is empty.
|
||||
- Fixed [#430](https://github.com/roboflow/supervision/pull/430): [`sv.ByteTrack`](/0.16.0/trackers/#supervision.tracker.byte_tracker.core.ByteTrack) to return `np.array([], dtype=int)` when `svDetections` is empty.
|
||||
|
||||
!!! warning
|
||||
!!! failure "Deprecated"
|
||||
|
||||
`sv.Detections.from_yolov8` and `sv.Classifications.from_yolov8` as those are now replaced by [`sv.Detections.from_ultralytics`](https://supervision.roboflow.com/detection/core/#supervision.detection.core.Detections.from_ultralytics) and [`sv.Classifications.from_ultralytics`](https://supervision.roboflow.com/classification/core/#supervision.classification.core.Classifications.from_ultralytics).
|
||||
`sv.Detections.from_yolov8` and `sv.Classifications.from_yolov8` as those are now replaced by [`sv.Detections.from_ultralytics`](/0.16.0/detection/core/#supervision.detection.core.Detections.from_ultralytics) and [`sv.Classifications.from_ultralytics`](/0.16.0/classification/core/#supervision.classification.core.Classifications.from_ultralytics).
|
||||
|
||||
|
||||
### 0.15.0 <small>October 5, 2023</small>
|
||||
|
||||
- Added [#170](https://github.com/roboflow/supervision/pull/170): [`sv.BoundingBoxAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.BoundingBoxAnnotator) allowing to annotate images and videos with bounding boxes.
|
||||
- Added [#170](https://github.com/roboflow/supervision/pull/170): [`sv.BoundingBoxAnnotator`](/0.15.0/annotators/#supervision.annotators.core.BoundingBoxAnnotator) allowing to annotate images and videos with bounding boxes.
|
||||
|
||||
- Added [#170](https://github.com/roboflow/supervision/pull/170): [`sv.BoxCornerAnnotator `](https://supervision.roboflow.com/annotators/#supervision.annotators.core.BoxCornerAnnotator) allowing to annotate images and videos with just bounding box corners.
|
||||
- Added [#170](https://github.com/roboflow/supervision/pull/170): [`sv.BoxCornerAnnotator `](/0.15.0/annotators/#supervision.annotators.core.BoxCornerAnnotator) allowing to annotate images and videos with just bounding box corners.
|
||||
|
||||
- Added [#170](https://github.com/roboflow/supervision/pull/170): [`sv.MaskAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.MaskAnnotator) allowing to annotate images and videos with segmentation masks.
|
||||
- Added [#170](https://github.com/roboflow/supervision/pull/170): [`sv.MaskAnnotator`](/0.15.0/annotators/#supervision.annotators.core.MaskAnnotator) allowing to annotate images and videos with segmentation masks.
|
||||
|
||||
- Added [#170](https://github.com/roboflow/supervision/pull/170): [`sv.EllipseAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.EllipseAnnotator) allowing to annotate images and videos with ellipses (sports game style).
|
||||
- Added [#170](https://github.com/roboflow/supervision/pull/170): [`sv.EllipseAnnotator`](/0.15.0/annotators/#supervision.annotators.core.EllipseAnnotator) allowing to annotate images and videos with ellipses (sports game style).
|
||||
|
||||
- Added [#386](https://github.com/roboflow/supervision/pull/386): [`sv.CircleAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.CircleAnnotator) allowing to annotate images and videos with circles.
|
||||
- Added [#386](https://github.com/roboflow/supervision/pull/386): [`sv.CircleAnnotator`](/0.15.0/annotators/#supervision.annotators.core.CircleAnnotator) allowing to annotate images and videos with circles.
|
||||
|
||||
- Added [#354](https://github.com/roboflow/supervision/pull/354): [`sv.TraceAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.TraceAnnotator) allowing to draw path of moving objects on videos.
|
||||
- Added [#354](https://github.com/roboflow/supervision/pull/354): [`sv.TraceAnnotator`](/0.15.0/annotators/#supervision.annotators.core.TraceAnnotator) allowing to draw path of moving objects on videos.
|
||||
|
||||
- Added [#405](https://github.com/roboflow/supervision/pull/405): [`sv.BlurAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.BlurAnnotator) allowing to blur objects on images and videos.
|
||||
- Added [#405](https://github.com/roboflow/supervision/pull/405): [`sv.BlurAnnotator`](/0.15.0/annotators/#supervision.annotators.core.BlurAnnotator) allowing to blur objects on images and videos.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -198,17 +198,17 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
|
||||
|
||||
- Added [#354](https://github.com/roboflow/supervision/pull/354): Supervision usage [example](https://github.com/roboflow/supervision/tree/develop/examples/traffic_analysis). You can now learn how to perform traffic flow analysis with Supervision.
|
||||
|
||||
- Changed [#399](https://github.com/roboflow/supervision/pull/399): [`sv.Detections.from_roboflow`](https://supervision.roboflow.com/detection/core/#supervision.detection.core.Detections.from_roboflow) now does not require `class_list` to be specified. The `class_id` value can be extracted directly from the [inference](https://github.com/roboflow/inference) response.
|
||||
- Changed [#399](https://github.com/roboflow/supervision/pull/399): [`sv.Detections.from_roboflow`](/0.15.0/detection/core/#supervision.detection.core.Detections.from_roboflow) now does not require `class_list` to be specified. The `class_id` value can be extracted directly from the [inference](https://github.com/roboflow/inference) response.
|
||||
|
||||
- Changed [#381](https://github.com/roboflow/supervision/pull/381): [`sv.VideoSink`](https://supervision.roboflow.com/utils/video/#videosink) now allows to customize the output codec.
|
||||
- Changed [#381](https://github.com/roboflow/supervision/pull/381): [`sv.VideoSink`](/0.15.0/utils/video/#videosink) now allows to customize the output codec.
|
||||
|
||||
- Changed [#361](https://github.com/roboflow/supervision/pull/361): [`sv.InferenceSlicer`](https://supervision.roboflow.com/detection/tools/inference_slicer/#supervision.detection.tools.inference_slicer.InferenceSlicer) can now operate in multithreading mode.
|
||||
- Changed [#361](https://github.com/roboflow/supervision/pull/361): [`sv.InferenceSlicer`](/0.15.0/detection/tools/inference_slicer/#supervision.detection.tools.inference_slicer.InferenceSlicer) can now operate in multithreading mode.
|
||||
|
||||
- Fixed [#348](https://github.com/roboflow/supervision/pull/348): [`sv.Detections.from_deepsparse`](https://supervision.roboflow.com/detection/core/#supervision.detection.core.Detections.from_deepsparse) to allow processing empty [deepsparse](https://github.com/neuralmagic/deepsparse) result object.
|
||||
- Fixed [#348](https://github.com/roboflow/supervision/pull/348): [`sv.Detections.from_deepsparse`](/0.15.0/detection/core/#supervision.detection.core.Detections.from_deepsparse) to allow processing empty [deepsparse](https://github.com/neuralmagic/deepsparse) result object.
|
||||
|
||||
### 0.14.0 <small>August 31, 2023</small>
|
||||
|
||||
- Added [#282](https://github.com/roboflow/supervision/pull/282): support for SAHI inference technique with [`sv.InferenceSlicer`](https://supervision.roboflow.com/detection/tools/inference_slicer).
|
||||
- Added [#282](https://github.com/roboflow/supervision/pull/282): support for SAHI inference technique with [`sv.InferenceSlicer`](/0.14.0/detection/tools/inference_slicer).
|
||||
|
||||
```python
|
||||
>>> import cv2
|
||||
@@ -227,23 +227,23 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
|
||||
>>> detections = slicer(image)
|
||||
```
|
||||
|
||||
- Added [#297](https://github.com/roboflow/supervision/pull/297): [`Detections.from_deepsparse`](https://roboflow.github.io/supervision/detection/core/#supervision.detection.core.Detections.from_deepsparse) to enable seamless integration with [DeepSparse](https://github.com/neuralmagic/deepsparse) framework.
|
||||
- Added [#297](https://github.com/roboflow/supervision/pull/297): [`Detections.from_deepsparse`](/0.14.0/detection/core/#supervision.detection.core.Detections.from_deepsparse) to enable seamless integration with [DeepSparse](https://github.com/neuralmagic/deepsparse) framework.
|
||||
|
||||
- Added [#281](https://github.com/roboflow/supervision/pull/281): [`sv.Classifications.from_ultralytics`](https://supervision.roboflow.com/classification/core/#supervision.classification.core.Classifications.from_ultralytics) to enable seamless integration with [Ultralytics](https://github.com/ultralytics/ultralytics) framework. This will enable you to use supervision with all [models](https://docs.ultralytics.com/models/) that Ultralytics supports.
|
||||
- Added [#281](https://github.com/roboflow/supervision/pull/281): [`sv.Classifications.from_ultralytics`](/0.14.0/classification/core/#supervision.classification.core.Classifications.from_ultralytics) to enable seamless integration with [Ultralytics](https://github.com/ultralytics/ultralytics) framework. This will enable you to use supervision with all [models](https://docs.ultralytics.com/models/) that Ultralytics supports.
|
||||
|
||||
!!! warning
|
||||
!!! failure "Deprecated"
|
||||
|
||||
[sv.Detections.from_yolov8](https://roboflow.github.io/supervision/detection/core/#supervision.detection.core.Detections.from_yolov8) and [sv.Classifications.from_yolov8](https://supervision.roboflow.com/classification/core/#supervision.classification.core.Classifications.from_yolov8) are now deprecated and will be removed with supervision-0.16.0 release.
|
||||
[sv.Detections.from_yolov8](/0.14.0/detection/core/#supervision.detection.core.Detections.from_yolov8) and [sv.Classifications.from_yolov8](/0.14.0/classification/core/#supervision.classification.core.Classifications.from_yolov8) are now deprecated and will be removed with `supervision-0.16.0` release.
|
||||
|
||||
- Added [#341](https://github.com/roboflow/supervision/pull/341): First supervision usage example script showing how to detect and track objects on video using YOLOv8 + Supervision.
|
||||
|
||||
- Changed [#296](https://github.com/roboflow/supervision/pull/296): [`sv.ClassificationDataset`](https://supervision.roboflow.com/dataset/core/#supervision.dataset.core.ClassificationDataset) and [`sv.DetectionDataset`](https://supervision.roboflow.com/dataset/core/#supervision.dataset.core.DetectionDataset) now use image path (not image name) as dataset keys.
|
||||
- Changed [#296](https://github.com/roboflow/supervision/pull/296): [`sv.ClassificationDataset`](/0.14.0/dataset/core/#supervision.dataset.core.ClassificationDataset) and [`sv.DetectionDataset`](/0.14.0/dataset/core/#supervision.dataset.core.DetectionDataset) now use image path (not image name) as dataset keys.
|
||||
|
||||
- Fixed [#300](https://github.com/roboflow/supervision/pull/300): [`Detections.from_roboflow`](https://supervision.roboflow.com/detection/core/#supervision.detection.core.Detections.from_roboflow) to filter out polygons with less than 3 points.
|
||||
- Fixed [#300](https://github.com/roboflow/supervision/pull/300): [`Detections.from_roboflow`](/0.14.0/detection/core/#supervision.detection.core.Detections.from_roboflow) to filter out polygons with less than 3 points.
|
||||
|
||||
### 0.13.0 <small>August 8, 2023</small>
|
||||
|
||||
- Added [#236](https://github.com/roboflow/supervision/pull/236): support for mean average precision (mAP) for object detection models with [`sv.MeanAveragePrecision`](https://roboflow.github.io/supervision/metrics/detection/#meanaverageprecision).
|
||||
- Added [#236](https://github.com/roboflow/supervision/pull/236): support for mean average precision (mAP) for object detection models with [`sv.MeanAveragePrecision`](/0.13.0/metrics/detection/#meanaverageprecision).
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -265,25 +265,25 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
|
||||
0.433
|
||||
```
|
||||
|
||||
- Added [#256](https://github.com/roboflow/supervision/pull/256): support for ByteTrack for object tracking with [`sv.ByteTrack`](https://roboflow.github.io/supervision/tracker/core/#bytetrack).
|
||||
- Added [#256](https://github.com/roboflow/supervision/pull/256): support for ByteTrack for object tracking with [`sv.ByteTrack`](/0.13.0/tracker/core/#bytetrack).
|
||||
|
||||
- Added [#222](https://github.com/roboflow/supervision/pull/222): [`sv.Detections.from_ultralytics`](https://roboflow.github.io/supervision/detection/core/#supervision.detection.core.Detections.from_ultralytics) to enable seamless integration with [Ultralytics](https://github.com/ultralytics/ultralytics) framework. This will enable you to use `supervision` with all [models](https://docs.ultralytics.com/models/) that Ultralytics supports.
|
||||
- Added [#222](https://github.com/roboflow/supervision/pull/222): [`sv.Detections.from_ultralytics`](/0.13.0/detection/core/#supervision.detection.core.Detections.from_ultralytics) to enable seamless integration with [Ultralytics](https://github.com/ultralytics/ultralytics) framework. This will enable you to use `supervision` with all [models](https://docs.ultralytics.com/models/) that Ultralytics supports.
|
||||
|
||||
!!! warning
|
||||
!!! failure "Deprecated"
|
||||
|
||||
[`sv.Detections.from_yolov8`](https://roboflow.github.io/supervision/detection/core/#supervision.detection.core.Detections.from_yolov8) is now deprecated and will be removed with `supervision-0.15.0` release.
|
||||
[`sv.Detections.from_yolov8`](/0.13.0/detection/core/#supervision.detection.core.Detections.from_yolov8) is now deprecated and will be removed with `supervision-0.15.0` release.
|
||||
|
||||
- Added [#191](https://github.com/roboflow/supervision/pull/191): [`sv.Detections.from_paddledet`](https://roboflow.github.io/supervision/detection/core/#supervision.detection.core.Detections.from_paddledet) to enable seamless integration with [PaddleDetection](https://github.com/PaddlePaddle/PaddleDetection) framework.
|
||||
- Added [#191](https://github.com/roboflow/supervision/pull/191): [`sv.Detections.from_paddledet`](/0.13.0/detection/core/#supervision.detection.core.Detections.from_paddledet) to enable seamless integration with [PaddleDetection](https://github.com/PaddlePaddle/PaddleDetection) framework.
|
||||
|
||||
- Added [#245](https://github.com/roboflow/supervision/pull/245): support for loading PASCAL VOC segmentation datasets with [`sv.DetectionDataset.`](https://roboflow.github.io/supervision/dataset/core/#supervision.dataset.core.DetectionDataset.from_pascal_voc).
|
||||
- Added [#245](https://github.com/roboflow/supervision/pull/245): support for loading PASCAL VOC segmentation datasets with [`sv.DetectionDataset.`](/0.13.0/dataset/core/#supervision.dataset.core.DetectionDataset.from_pascal_voc).
|
||||
|
||||
### 0.12.0 <small>July 24, 2023</small>
|
||||
|
||||
!!! warning
|
||||
!!! failure "Python 3.7. Support Terminated"
|
||||
|
||||
With the `supervision-0.12.0` release, we are terminating official support for Python 3.7.
|
||||
|
||||
- Added [#177](https://github.com/roboflow/supervision/pull/177): initial support for object detection model benchmarking with [`sv.ConfusionMatrix`](https://roboflow.github.io/supervision/metrics/detection/#confusionmatrix).
|
||||
- Added [#177](https://github.com/roboflow/supervision/pull/177): initial support for object detection model benchmarking with [`sv.ConfusionMatrix`](/0.12.0/metrics/detection/#confusionmatrix).
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -310,23 +310,23 @@ array([
|
||||
])
|
||||
```
|
||||
|
||||
- Added [#173](https://github.com/roboflow/supervision/pull/173): [`Detections.from_mmdetection`](https://roboflow.github.io/supervision/detection/core/#supervision.detection.core.Detections.from_mmdetection) to enable seamless integration with [MMDetection](https://github.com/open-mmlab/mmdetection) framework.
|
||||
- Added [#173](https://github.com/roboflow/supervision/pull/173): [`Detections.from_mmdetection`](/0.12.0/detection/core/#supervision.detection.core.Detections.from_mmdetection) to enable seamless integration with [MMDetection](https://github.com/open-mmlab/mmdetection) framework.
|
||||
|
||||
- Added [#130](https://github.com/roboflow/supervision/issues/130): ability to [install](https://roboflow.github.io/supervision/) package in `headless` or `desktop` mode.
|
||||
- Added [#130](https://github.com/roboflow/supervision/issues/130): ability to [install](https://supervision.roboflow.com/) package in `headless` or `desktop` mode.
|
||||
|
||||
- Changed [#180](https://github.com/roboflow/supervision/pull/180): packing method from `setup.py` to `pyproject.toml`.
|
||||
|
||||
- Fixed [#188](https://github.com/roboflow/supervision/issues/188): [`sv.DetectionDataset.from_cooc`](https://roboflow.github.io/supervision/dataset/core/#supervision.dataset.core.DetectionDataset.from_coco) can't be loaded when there are images without annotations.
|
||||
- Fixed [#188](https://github.com/roboflow/supervision/issues/188): [`sv.DetectionDataset.from_cooc`](/0.12.0/dataset/core/#supervision.dataset.core.DetectionDataset.from_coco) can't be loaded when there are images without annotations.
|
||||
|
||||
- Fixed [#226](https://github.com/roboflow/supervision/issues/226): [`sv.DetectionDataset.from_yolo`](https://roboflow.github.io/supervision/dataset/core/#supervision.dataset.core.DetectionDataset.from_yolo) can't load background instances.
|
||||
- Fixed [#226](https://github.com/roboflow/supervision/issues/226): [`sv.DetectionDataset.from_yolo`](/0.12.0/dataset/core/#supervision.dataset.core.DetectionDataset.from_yolo) can't load background instances.
|
||||
|
||||
### 0.11.1 <small>June 29, 2023</small>
|
||||
|
||||
- Fix [#165](https://github.com/roboflow/supervision/pull/165): [`as_folder_structure`](https://roboflow.github.io/supervision/dataset/core/#supervision.dataset.core.ClassificationDataset.as_folder_structure) fails to save [`sv.ClassificationDataset`](https://roboflow.github.io/supervision/dataset/core/#classificationdataset) when it is result of inference.
|
||||
- Fix [#165](https://github.com/roboflow/supervision/pull/165): [`as_folder_structure`](/0.11.1/dataset/core/#supervision.dataset.core.ClassificationDataset.as_folder_structure) fails to save [`sv.ClassificationDataset`](/0.11.1/dataset/core/#classificationdataset) when it is result of inference.
|
||||
|
||||
### 0.11.0 <small>June 28, 2023</small>
|
||||
|
||||
- Added [#150](https://github.com/roboflow/supervision/pull/150): ability to load and save [`sv.DetectionDataset`](https://roboflow.github.io/supervision/dataset/core/#detectiondataset) in COCO format using [`as_coco`](https://roboflow.github.io/supervision/dataset/core/#supervision.dataset.core.DetectionDataset.as_coco) and [`from_coco`](https://roboflow.github.io/supervision/dataset/core/#supervision.dataset.core.DetectionDataset.from_coco) methods.
|
||||
- Added [#150](https://github.com/roboflow/supervision/pull/150): ability to load and save [`sv.DetectionDataset`](/0.11.0/dataset/core/#detectiondataset) in COCO format using [`as_coco`](/0.11.0/dataset/core/#supervision.dataset.core.DetectionDataset.as_coco) and [`from_coco`](/0.11.0/dataset/core/#supervision.dataset.core.DetectionDataset.from_coco) methods.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -342,7 +342,7 @@ array([
|
||||
... )
|
||||
```
|
||||
|
||||
- Added [#158](https://github.com/roboflow/supervision/pull/158): ability to marge multiple [`sv.DetectionDataset`](https://roboflow.github.io/supervision/dataset/core/#detectiondataset) together using [`merge`](https://roboflow.github.io/supervision/dataset/core/#supervision.dataset.core.DetectionDataset.merge) method.
|
||||
- Added [#158](https://github.com/roboflow/supervision/pull/158): ability to merge multiple [`sv.DetectionDataset`](/0.11.0/dataset/core/#detectiondataset) together using [`merge`](/0.11.0/dataset/core/#supervision.dataset.core.DetectionDataset.merge) method.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -366,13 +366,13 @@ array([
|
||||
['cat', 'dog', 'person']
|
||||
```
|
||||
|
||||
- Added [#162](https://github.com/roboflow/supervision/pull/162): additional `start` and `end` arguments to [`sv.get_video_frames_generator`](https://roboflow.github.io/supervision/utils/video/#get_video_frames_generator) allowing to generate frames only for a selected part of the video.
|
||||
- Added [#162](https://github.com/roboflow/supervision/pull/162): additional `start` and `end` arguments to [`sv.get_video_frames_generator`](/0.11.0/utils/video/#get_video_frames_generator) allowing to generate frames only for a selected part of the video.
|
||||
|
||||
- Fix [#157](https://github.com/roboflow/supervision/pull/157): incorrect loading of YOLO dataset class names from `data.yaml`.
|
||||
|
||||
### 0.10.0 <small>June 14, 2023</small>
|
||||
|
||||
- Added [#125](https://github.com/roboflow/supervision/pull/125): ability to load and save [`sv.ClassificationDataset`](https://roboflow.github.io/supervision/dataset/core/#classificationdataset) in a folder structure format.
|
||||
- Added [#125](https://github.com/roboflow/supervision/pull/125): ability to load and save [`sv.ClassificationDataset`](/0.10.0/dataset/core/#classificationdataset) in a folder structure format.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -386,9 +386,9 @@ array([
|
||||
... )
|
||||
```
|
||||
|
||||
- Added [#125](https://github.com/roboflow/supervision/pull/125): support for [`sv.ClassificationDataset.split`](https://roboflow.github.io/supervision/dataset/core/#supervision.dataset.core.ClassificationDataset.split) allowing to divide `sv.ClassificationDataset` into two parts.
|
||||
- Added [#125](https://github.com/roboflow/supervision/pull/125): support for [`sv.ClassificationDataset.split`](/0.10.0/dataset/core/#supervision.dataset.core.ClassificationDataset.split) allowing to divide `sv.ClassificationDataset` into two parts.
|
||||
|
||||
- Added [#110](https://github.com/roboflow/supervision/pull/110): ability to extract masks from Roboflow API results using [`sv.Detections.from_roboflow`](https://roboflow.github.io/supervision/detection/core/#supervision.detection.core.Detections.from_roboflow).
|
||||
- Added [#110](https://github.com/roboflow/supervision/pull/110): ability to extract masks from Roboflow API results using [`sv.Detections.from_roboflow`](/0.10.0/detection/core/#supervision.detection.core.Detections.from_roboflow).
|
||||
|
||||
- Added [commit hash](https://github.com/roboflow/supervision/commit/d000292eb2f2342544e0947b65528082e60fb8d6): Supervision Quickstart [notebook](https://colab.research.google.com/github/roboflow/supervision/blob/main/demo.ipynb) where you can learn more about Detection, Dataset and Video APIs.
|
||||
|
||||
@@ -396,7 +396,7 @@ array([
|
||||
|
||||
### 0.9.0 <small>June 7, 2023</small>
|
||||
|
||||
- Added [#118](https://github.com/roboflow/supervision/pull/118): ability to select [`sv.Detections`](https://roboflow.github.io/supervision/detection/core/#supervision.detection.core.Detections.__getitem__) by index, list of indexes or slice. Here is an example illustrating the new selection methods.
|
||||
- Added [#118](https://github.com/roboflow/supervision/pull/118): ability to select [`sv.Detections`](/0.9.0/detection/core/#supervision.detection.core.Detections.__getitem__) by index, list of indexes or slice. Here is an example illustrating the new selection methods.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -410,11 +410,11 @@ array([
|
||||
2
|
||||
```
|
||||
|
||||
- Added [#101](https://github.com/roboflow/supervision/pull/101): ability to extract masks from YOLOv8 result using [`sv.Detections.from_yolov8`](https://roboflow.github.io/supervision/detection/core/#supervision.detection.core.Detections.from_yolov8). Here is an example illustrating how to extract boolean masks from the result of the YOLOv8 model inference.
|
||||
- Added [#101](https://github.com/roboflow/supervision/pull/101): ability to extract masks from YOLOv8 result using [`sv.Detections.from_yolov8`](/0.8.0/detection/core/#supervision.detection.core.Detections.from_yolov8). Here is an example illustrating how to extract boolean masks from the result of the YOLOv8 model inference.
|
||||
|
||||
- Added [#122](https://github.com/roboflow/supervision/pull/122): ability to crop image using [`sv.crop`](https://roboflow.github.io/supervision/utils/image/#crop). Here is an example showing how to get a separate crop for each detection in `sv.Detections`.
|
||||
- Added [#122](https://github.com/roboflow/supervision/pull/122): ability to crop image using [`sv.crop`](/latest/utils/image/#crop). Here is an example showing how to get a separate crop for each detection in `sv.Detections`.
|
||||
|
||||
- Added [#120](https://github.com/roboflow/supervision/pull/120): ability to conveniently save multiple images into directory using [`sv.ImageSink`](https://roboflow.github.io/supervision/utils/image/#imagesink). Here is an example showing how to save every tenth video frame as a separate image.
|
||||
- Added [#120](https://github.com/roboflow/supervision/pull/120): ability to conveniently save multiple images into directory using [`sv.ImageSink`](/0.9.0/utils/image/#imagesink). Here is an example showing how to save every tenth video frame as a separate image.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
@@ -424,12 +424,12 @@ array([
|
||||
... sink.save_image(image=image)
|
||||
```
|
||||
|
||||
- Fixed [#106](https://github.com/roboflow/supervision/issues/106): inconvenient handling of [`sv.PolygonZone`](https://roboflow.github.io/supervision/detection/tools/polygon_zone/#polygonzone) coordinates. Now `sv.PolygonZone` accepts coordinates in the form of `[[x1, y1], [x2, y2], ...]` that can be both integers and floats.
|
||||
- Fixed [#106](https://github.com/roboflow/supervision/issues/106): inconvenient handling of [`sv.PolygonZone`](/0.8.0/supervision/detection/tools/polygon_zone/#polygonzone) coordinates. Now `sv.PolygonZone` accepts coordinates in the form of `[[x1, y1], [x2, y2], ...]` that can be both integers and floats.
|
||||
|
||||
### 0.8.0 <small>May 17, 2023</small>
|
||||
|
||||
- Added [#100](https://github.com/roboflow/supervision/pull/100): support for dataset inheritance. The current `Dataset` got renamed to `DetectionDataset`. Now [`DetectionDataset`](https://roboflow.github.io/supervision/dataset/core/#detectiondataset) inherits from `BaseDataset`. This change was made to enforce the future consistency of APIs of different types of computer vision datasets.
|
||||
- Added [#100](https://github.com/roboflow/supervision/pull/100): ability to save datasets in YOLO format using [`DetectionDataset.as_yolo`](https://roboflow.github.io/supervision/dataset/core/#supervision.dataset.core.DetectionDataset.as_yolo).
|
||||
- Added [#100](https://github.com/roboflow/supervision/pull/100): support for dataset inheritance. The current `Dataset` got renamed to `DetectionDataset`. Now [`DetectionDataset`](/0.8.0/supervision/dataset/core/#detectiondataset) inherits from `BaseDataset`. This change was made to enforce the future consistency of APIs of different types of computer vision datasets.
|
||||
- Added [#100](https://github.com/roboflow/supervision/pull/100): ability to save datasets in YOLO format using [`DetectionDataset.as_yolo`](/0.8.0/dataset/core/#supervision.dataset.core.DetectionDataset.as_yolo).
|
||||
|
||||
```python
|
||||
>>> import roboflow
|
||||
@@ -453,7 +453,7 @@ array([
|
||||
['dog', 'person']
|
||||
```
|
||||
|
||||
- Added [#102](https://github.com/roboflow/supervision/pull/103): support for [`DetectionDataset.split`](https://roboflow.github.io/supervision/dataset/core/#supervision.dataset.core.DetectionDataset.split) allowing to divide `DetectionDataset` into two parts.
|
||||
- Added [#102](https://github.com/roboflow/supervision/pull/103): support for [`DetectionDataset.split`](/0.8.0/dataset/core/#supervision.dataset.core.DetectionDataset.split) allowing to divide `DetectionDataset` into two parts.
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
## Classifications
|
||||
# Classifications
|
||||
|
||||
:::supervision.classification.core.Classifications
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Code of conduct
|
||||
|
||||
```
|
||||
--8<-- "CODE_OF_CONDUCT.md"
|
||||
```
|
||||
@@ -0,0 +1 @@
|
||||
--8<-- "CONTRIBUTING.md"
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
template: cookbooks.html
|
||||
comments: true
|
||||
status: new
|
||||
hide:
|
||||
- navigation
|
||||
- toc
|
||||
---
|
||||
+8
-2
@@ -2,15 +2,21 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
# Datasets
|
||||
|
||||
!!! warning
|
||||
|
||||
Dataset API is still fluid and may change. If you use Dataset API in your project until further notice, freeze the
|
||||
`supervision` version in your `requirements.txt` or `setup.py`.
|
||||
|
||||
## DetectionDataset
|
||||
<div class="md-typeset">
|
||||
<h2>DetectionDataset</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.dataset.core.DetectionDataset
|
||||
|
||||
## ClassificationDataset
|
||||
<div class="md-typeset">
|
||||
<h2>ClassificationDataset</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.dataset.core.ClassificationDataset
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
comments: true
|
||||
status: deprecated
|
||||
---
|
||||
|
||||
These features are phased out due to better alternatives or potential issues in future versions. Deprecated functionalities are supported for **three subsequent releases**, providing time for users to transition to updated methods.
|
||||
|
||||
- [`Detections.from_froboflow`](detection/core.md/#supervision.detection.core.Detections.from_roboflow) is deprecated and will be removed in `supervision-0.22.0`. Use [`Detections.from_inference`](detection/core.md/#supervision.detection.core.Detections.from_inference) instead.
|
||||
- `Color.white()` is deprecated and will be removed in `supervision-0.22.0`. Use `Color.WHITE` instead.
|
||||
- `Color.black()` is deprecated and will be removed in `supervision-0.22.0`. Use `Color.BLACK` instead.
|
||||
- `Color.red()` is deprecated and will be removed in `supervision-0.22.0`. Use `Color.RED` instead.
|
||||
- `Color.green()` is deprecated and will be removed in `supervision-0.22.0`. Use `Color.GREEN` instead.
|
||||
- `Color.blue()` is deprecated and will be removed in `supervision-0.22.0`. Use `Color.BLUE` instead.
|
||||
- [`ColorPalette.default()`](draw/color.md/#supervision.draw.color.ColorPalette.default) is deprecated and will be removed in `supervision-0.22.0`. Use [`ColorPalette.DEFAULT`](draw/color.md/#supervision.draw.color.ColorPalette.DEFAULT) instead.
|
||||
- `BoxAnnotator` is deprecated and will be removed in `supervision-0.22.0`. Use [`BoundingBoxAnnotator`](annotators.md/#supervision.annotators.core.BoundingBoxAnnotator) and [`LabelAnnotator`](annotators.md/#supervision.annotators.core.LabelAnnotator) instead.
|
||||
- [`FPSMonitor.__call__`](utils/video.md/#supervision.utils.video.FPSMonitor.__call__) is deprecated and will be removed in `supervision-0.22.0`. Use [`FPSMonitor.fps`](utils/video.md/#supervision.utils.video.FPSMonitor.fps) instead.
|
||||
@@ -3,6 +3,6 @@ comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
## Detections
|
||||
# Detections
|
||||
|
||||
:::supervision.detection.core.Detections
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
## InferenceSlicer
|
||||
# InferenceSlicer
|
||||
|
||||
:::supervision.detection.tools.inference_slicer.InferenceSlicer
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
## LineZone
|
||||
<div class="md-typeset">
|
||||
<h2>LineZone</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.line_counter.LineZone
|
||||
:::supervision.detection.line_zone.LineZone
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2>LineZoneAnnotator</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.line_zone.LineZoneAnnotator
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
## PolygonZone
|
||||
<div class="md-typeset">
|
||||
<h2>PolygonZone</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.tools.polygon_zone.PolygonZone
|
||||
|
||||
## PolygonZoneAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2>PolygonZoneAnnotator</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.tools.polygon_zone.PolygonZoneAnnotator
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Save Detections
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2>CSV Sink</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.tools.csv_sink.CSVSink
|
||||
@@ -1,8 +1,7 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
## Detection Smoother
|
||||
# Detection Smoother
|
||||
|
||||
:::supervision.detection.tools.smoother.DetectionsSmoother
|
||||
|
||||
+42
-10
@@ -2,38 +2,70 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
## box_iou_batch
|
||||
# Detection Utils
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2>box_iou_batch</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.box_iou_batch
|
||||
|
||||
## non_max_suppression
|
||||
<div class="md-typeset">
|
||||
<h2>mask_iou_batch</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.non_max_suppression
|
||||
:::supervision.detection.utils.mask_iou_batch
|
||||
|
||||
## polygon_to_mask
|
||||
<div class="md-typeset">
|
||||
<h2>box_non_max_suppression</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.box_non_max_suppression
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2>mask_non_max_suppression</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.mask_non_max_suppression
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2>polygon_to_mask</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.polygon_to_mask
|
||||
|
||||
## mask_to_xyxy
|
||||
<div class="md-typeset">
|
||||
<h2>mask_to_xyxy</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.mask_to_xyxy
|
||||
|
||||
## mask_to_polygons
|
||||
<div class="md-typeset">
|
||||
<h2>mask_to_polygons</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.mask_to_polygons
|
||||
|
||||
## polygon_to_xyxy
|
||||
<div class="md-typeset">
|
||||
<h2>polygon_to_xyxy</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.polygon_to_xyxy
|
||||
|
||||
## filter_polygons_by_area
|
||||
<div class="md-typeset">
|
||||
<h2>filter_polygons_by_area</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.filter_polygons_by_area
|
||||
|
||||
## move_boxes
|
||||
<div class="md-typeset">
|
||||
<h2>move_boxes</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.move_boxes
|
||||
|
||||
## scale_boxes
|
||||
<div class="md-typeset">
|
||||
<h2>scale_boxes</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.scale_boxes
|
||||
|
||||
+4
-2
@@ -2,10 +2,12 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
## Color
|
||||
# Color
|
||||
|
||||
:::supervision.draw.color.Color
|
||||
|
||||
## ColorPalette
|
||||
<div class="md-typeset">
|
||||
<h2>ColorPalette</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.draw.color.ColorPalette
|
||||
|
||||
+26
-8
@@ -2,34 +2,52 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
## draw_line
|
||||
# Draw Utils
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2>draw_line</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.draw.utils.draw_line
|
||||
|
||||
## draw_rectangle
|
||||
<div class="md-typeset">
|
||||
<h2>draw_rectangle</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.draw.utils.draw_rectangle
|
||||
|
||||
## draw_filled_rectangle
|
||||
<div class="md-typeset">
|
||||
<h2>draw_filled_rectangle</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.draw.utils.draw_filled_rectangle
|
||||
|
||||
## draw_polygon
|
||||
<div class="md-typeset">
|
||||
<h2>draw_polygon</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.draw.utils.draw_polygon
|
||||
|
||||
## draw_text
|
||||
<div class="md-typeset">
|
||||
<h2>draw_text</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.draw.utils.draw_text
|
||||
|
||||
## draw_image
|
||||
<div class="md-typeset">
|
||||
<h2>draw_image</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.draw.utils.draw_image
|
||||
|
||||
## calculate_dynamic_font_scale
|
||||
<div class="md-typeset">
|
||||
<h2>calculate_dynamic_font_scale</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.draw.utils.calculate_dynamic_text_scale
|
||||
|
||||
## calculate_dynamic_line_thickness
|
||||
<div class="md-typeset">
|
||||
<h2>calculate_dynamic_line_thickness</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.draw.utils.calculate_dynamic_line_thickness
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
## Position
|
||||
# Position
|
||||
|
||||
:::supervision.geometry.core.Position
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
# Detect and Annotate
|
||||
|
||||
Supervision offers a streamlined solution to effortlessly annotate predictions from a
|
||||
range of object detection and segmentation models. This guide demonstrates how to
|
||||
execute inference using the YOLOv8 model with either the
|
||||
@@ -42,7 +44,7 @@ Now that we have predictions from a model, we can load them into Supervision.
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
We can do so using the [`sv.Detections.from_ultralytics`](https://supervision.roboflow.com/detection/core/#supervision.detection.core.Detections.from_ultralytics) method, which accepts model results from both detection and segmentation models.
|
||||
We can do so using the [`sv.Detections.from_ultralytics`](detection/core/#supervision.detection.core.Detections.from_ultralytics) method, which accepts model results from both detection and segmentation models.
|
||||
|
||||
```python
|
||||
import cv2
|
||||
@@ -57,7 +59,7 @@ Now that we have predictions from a model, we can load them into Supervision.
|
||||
|
||||
=== "Inference"
|
||||
|
||||
We can do so using the [`sv.Detections.from_inference`](https://supervision.roboflow.com/detection/core/#supervision.detection.core.Detections.from_inference) method, which accepts model results from both detection and segmentation models.
|
||||
We can do so using the [`sv.Detections.from_inference`](detection/core/#supervision.detection.core.Detections.from_inference) method, which accepts model results from both detection and segmentation models.
|
||||
|
||||
```python
|
||||
import cv2
|
||||
@@ -72,17 +74,17 @@ Now that we have predictions from a model, we can load them into Supervision.
|
||||
|
||||
You can conveniently load predictions from other computer vision frameworks and libraries using:
|
||||
|
||||
- [`from_deepsparse`](https://supervision.roboflow.com/detection/core/#supervision.detection.core.Detections.from_deepsparse) ([Deepsparse](https://github.com/neuralmagic/deepsparse))
|
||||
- [`from_detectron2`](https://supervision.roboflow.com/detection/core/#supervision.detection.core.Detections.from_detectron2) ([Detectron2](https://github.com/facebookresearch/detectron2))
|
||||
- [`from_mmdetection`](https://supervision.roboflow.com/detection/core/#supervision.detection.core.Detections.from_mmdetection) ([MMDetection](https://github.com/open-mmlab/mmdetection))
|
||||
- [`from_inference`](https://supervision.roboflow.com/detection/core/#supervision.detection.core.Detections.from_inference) ([Roboflow Inference](https://github.com/roboflow/inference))
|
||||
- [`from_sam`](https://supervision.roboflow.com/detection/core/#supervision.detection.core.Detections.from_sam) ([Segment Anything Model](https://github.com/facebookresearch/segment-anything))
|
||||
- [`from_transformers`](https://supervision.roboflow.com/detection/core/#supervision.detection.core.Detections.from_transformers) ([HuggingFace Transformers](https://github.com/huggingface/transformers))
|
||||
- [`from_yolo_nas`](https://supervision.roboflow.com/detection/core/#supervision.detection.core.Detections.from_yolo_nas) ([YOLO-NAS](https://github.com/Deci-AI/super-gradients/blob/master/YOLONAS.md))
|
||||
- [`from_deepsparse`](detection/core/#supervision.detection.core.Detections.from_deepsparse) ([Deepsparse](https://github.com/neuralmagic/deepsparse))
|
||||
- [`from_detectron2`](detection/core/#supervision.detection.core.Detections.from_detectron2) ([Detectron2](https://github.com/facebookresearch/detectron2))
|
||||
- [`from_mmdetection`](detection/core/#supervision.detection.core.Detections.from_mmdetection) ([MMDetection](https://github.com/open-mmlab/mmdetection))
|
||||
- [`from_inference`](detection/core/#supervision.detection.core.Detections.from_inference) ([Roboflow Inference](https://github.com/roboflow/inference))
|
||||
- [`from_sam`](detection/core/#supervision.detection.core.Detections.from_sam) ([Segment Anything Model](https://github.com/facebookresearch/segment-anything))
|
||||
- [`from_transformers`](detection/core/#supervision.detection.core.Detections.from_transformers) ([HuggingFace Transformers](https://github.com/huggingface/transformers))
|
||||
- [`from_yolo_nas`](detection/core/#supervision.detection.core.Detections.from_yolo_nas) ([YOLO-NAS](https://github.com/Deci-AI/super-gradients/blob/master/YOLONAS.md))
|
||||
|
||||
## Annotate Image
|
||||
|
||||
Finally, we can annotate the image with the predictions. Since we are working with an object detection model, we will use the [`sv.BoundingBoxAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.BoundingBoxAnnotator) and [`sv.LabelAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.LabelAnnotator) classes. If you are running the segmentation model [`sv.MaskAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.MaskAnnotator) is a drop-in replacement for [`sv.BoundingBoxAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.BoundingBoxAnnotator) that will allow you to draw masks instead of boxes.
|
||||
Finally, we can annotate the image with the predictions. Since we are working with an object detection model, we will use the [`sv.BoundingBoxAnnotator`](annotators/#supervision.annotators.core.BoundingBoxAnnotator) and [`sv.LabelAnnotator`](annotators/#supervision.annotators.core.LabelAnnotator) classes. If you are running the segmentation model [`sv.MaskAnnotator`](annotators/#supervision.annotators.core.MaskAnnotator) is a drop-in replacement for [`sv.BoundingBoxAnnotator`](annotators/#supervision.annotators.core.BoundingBoxAnnotator) that will allow you to draw masks instead of boxes.
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
@@ -136,7 +138,7 @@ Finally, we can annotate the image with the predictions. Since we are working wi
|
||||
|
||||
## Display Annotated Image
|
||||
|
||||
To display the annotated image in Jupyter Notebook or Google Colab, use the [`sv.plot_image`](https://supervision.roboflow.com/utils/notebook/#supervision.utils.notebook.plot_image) function.
|
||||
To display the annotated image in Jupyter Notebook or Google Colab, use the [`sv.plot_image`](utils/notebook/#supervision.utils.notebook.plot_image) function.
|
||||
|
||||
```python
|
||||
sv.plot_image(annotated_image)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
# Filter Detections
|
||||
|
||||
The advanced filtering capabilities of the `Detections` class offer users a versatile and efficient way to narrow down
|
||||
and refine object detections. This section outlines various filtering methods, including filtering by specific class
|
||||
or a set of classes, confidence, object area, bounding box area, relative area, box dimensions, and designated zones.
|
||||
@@ -42,7 +44,6 @@ Allows you to select detections that belong only to one selected class.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
### by set of classes
|
||||
|
||||
Allows you to select detections that belong only to selected set of classes.
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
# Track Objects
|
||||
|
||||
Leverage Supervision's advanced capabilities for enhancing your video analysis by
|
||||
seamlessly [tracking](https://supervision.roboflow.com/trackers/) objects recognized by
|
||||
seamlessly [tracking](/latest/trackers/) objects recognized by
|
||||
a multitude of object detection and segmentation models. This comprehensive guide will
|
||||
take you through the steps to perform inference using the YOLOv8 model via either the
|
||||
[Inference](https://github.com/roboflow/inference) or
|
||||
@@ -13,7 +15,7 @@ for a deeper analysis.
|
||||
|
||||
To make it easier for you to follow our tutorial download the video we will use as an
|
||||
example. You can do this using
|
||||
[`supervision[assets]`](https://supervision.roboflow.com/assets/) extension.
|
||||
[`supervision[assets]`](/latest/assets/) extension.
|
||||
|
||||
```python
|
||||
from supervision.assets import download_assets, VideoAssets
|
||||
@@ -30,7 +32,7 @@ download_assets(VideoAssets.PEOPLE_WALKING)
|
||||
First, you'll need to obtain predictions from your object detection or segmentation
|
||||
model. In this tutorial, we are using the YOLOv8 model as an example. However,
|
||||
Supervision is versatile and compatible with various models. Check this
|
||||
[link](https://supervision.roboflow.com/how_to/detect_and_annotate/#load-predictions-into-supervision)
|
||||
[link](latest/how_to/detect_and_annotate/#load-predictions-into-supervision)
|
||||
for guidance on how to plug in other models.
|
||||
|
||||
We will define a `callback` function, which will process each frame of the video
|
||||
@@ -90,7 +92,7 @@ it will be modified to include tracking, labeling, and trace annotations.
|
||||
|
||||
After running inference and obtaining predictions, the next step is to track the
|
||||
detected objects throughout the video. Utilizing Supervision’s
|
||||
[`sv.ByteTrack`](https://supervision.roboflow.com/trackers/#supervision.tracker.byte_tracker.core.ByteTrack)
|
||||
[`sv.ByteTrack`](/latest/trackers/#supervision.tracker.byte_tracker.core.ByteTrack)
|
||||
functionality, each detected object is assigned a unique tracker ID,
|
||||
enabling the continuous following of the object's motion path across different frames.
|
||||
|
||||
@@ -146,7 +148,7 @@ enabling the continuous following of the object's motion path across different f
|
||||
|
||||
Annotating the video with tracking IDs helps in distinguishing and following each object
|
||||
distinctly. With the
|
||||
[`sv.LabelAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.LabelAnnotator)
|
||||
[`sv.LabelAnnotator`](/latest/annotators.md/#supervision.annotators.core.LabelAnnotator)
|
||||
in Supervision, we can overlay the tracker IDs and class labels on the detected objects,
|
||||
offering a clear visual representation of each object's class and unique identifier.
|
||||
|
||||
@@ -228,7 +230,7 @@ offering a clear visual representation of each object's class and unique identif
|
||||
|
||||
Adding traces to the video involves overlaying the historical paths of the detected
|
||||
objects. This feature, powered by the
|
||||
[`sv.TraceAnnotator`](https://supervision.roboflow.com/annotators/#supervision.annotators.core.TraceAnnotator),
|
||||
[`sv.TraceAnnotator`](/latest/annotators/#supervision.annotators.core.TraceAnnotator),
|
||||
allows for visualizing the trajectories of objects, helping in understanding the
|
||||
movement patterns and interactions between objects in the video.
|
||||
|
||||
|
||||
+49
-44
@@ -1,16 +1,20 @@
|
||||
---
|
||||
template: index.html
|
||||
comments: true
|
||||
hide:
|
||||
- navigation
|
||||
- toc
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<p>
|
||||
<a align="center" href="" target="_blank">
|
||||
<img
|
||||
width="850"
|
||||
src="https://media.roboflow.com/open-source/supervision/rf-supervision-banner.png?updatedAt=1678995927529"
|
||||
>
|
||||
</a>
|
||||
</p>
|
||||
<div class="md-typeset">
|
||||
<h1></h1>
|
||||
</div>
|
||||
|
||||
<div align="center" id="logo">
|
||||
<a align="center" href="" target="_blank">
|
||||
<img width="850"
|
||||
src="https://media.roboflow.com/open-source/supervision/rf-supervision-banner.png?updatedAt=1678995927529">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## 👋 Hello
|
||||
@@ -24,40 +28,6 @@ We write your reusable computer vision tools. Whether you need to load your data
|
||||
>
|
||||
</video>
|
||||
|
||||
## 🚀 Quickstart
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- __Detect and Annotate__
|
||||
|
||||
---
|
||||
|
||||
Annotate predictions from a range of object detection and segmentation models
|
||||
|
||||
[:octicons-arrow-right-24: Tutorial](how_to/detect_and_annotate)
|
||||
|
||||
- __Track Objects__
|
||||
|
||||
---
|
||||
|
||||
Discover how to enhance video analysis by implementing seamless object tracking
|
||||
|
||||
[:octicons-arrow-right-24: Tutorial](how_to/track_objects)
|
||||
|
||||
- > __Count Objects Crossing Line__
|
||||
|
||||
---
|
||||
|
||||
Explore methods to accurately count and analyze objects crossing a predefined line
|
||||
|
||||
- > __Filter Objects in Zone__
|
||||
|
||||
---
|
||||
|
||||
Master the techniques to selectively filter and focus on objects within a specific zone
|
||||
|
||||
</div>
|
||||
|
||||
## 💻 Install
|
||||
|
||||
You can install `supervision` with pip in a
|
||||
@@ -76,7 +46,7 @@ You can install `supervision` with pip in a
|
||||
If you require the full version of `supervision` with GUI support you can install the desktop version. This version includes the GUI components of OpenCV, allowing you to display images and videos on the screen.
|
||||
|
||||
```bash
|
||||
pip install supervision[desktop]
|
||||
pip install "supervision[desktop]"
|
||||
```
|
||||
|
||||
!!! example "git clone (for development)"
|
||||
@@ -117,3 +87,38 @@ You can install `supervision` with pip in a
|
||||
# desktop install
|
||||
poetry install --extras "desktop"
|
||||
```
|
||||
|
||||
|
||||
## 🚀 Quickstart
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- __Detect and Annotate__
|
||||
|
||||
---
|
||||
|
||||
Annotate predictions from a range of object detection and segmentation models
|
||||
|
||||
[:octicons-arrow-right-24: Tutorial](how_to/detect_and_annotate.md)
|
||||
|
||||
- __Track Objects__
|
||||
|
||||
---
|
||||
|
||||
Discover how to enhance video analysis by implementing seamless object tracking
|
||||
|
||||
[:octicons-arrow-right-24: Tutorial](how_to/track_objects.md)
|
||||
|
||||
- > __Count Objects Crossing Line__
|
||||
|
||||
---
|
||||
|
||||
Explore methods to accurately count and analyze objects crossing a predefined line
|
||||
|
||||
- > __Filter Objects in Zone__
|
||||
|
||||
---
|
||||
|
||||
Master the techniques to selectively filter and focus on objects within a specific zone
|
||||
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
const palette = __md_get("__palette")
|
||||
const useDark = palette && typeof palette.color === "object" && palette.color.scheme === "slate"
|
||||
const theme = useDark ? "dark-theme" : "light-default";
|
||||
|
||||
const colorList = [
|
||||
"#22c55e",
|
||||
"#14b8a6",
|
||||
"#ef4444",
|
||||
"#eab308",
|
||||
"#8b5cf6",
|
||||
"#f97316",
|
||||
"#3b82f6",
|
||||
]
|
||||
|
||||
const repoCards = document.querySelectorAll(".repo-card");
|
||||
const labelsAll = Array
|
||||
.from(repoCards)
|
||||
.flatMap((element) => element.getAttribute('data-labels').split(','))
|
||||
.map(label => label.trim())
|
||||
.filter(label => label !== '');
|
||||
const uniqueLabels = [...new Set(labelsAll)];
|
||||
|
||||
const labelToColor = uniqueLabels.reduce((map, label, index) => {
|
||||
map[label] = colorList[index % colorList.length];
|
||||
return map;
|
||||
}, {});
|
||||
|
||||
|
||||
async function renderCard(element, elementIndex) {
|
||||
const url = element.getAttribute('data-url');
|
||||
const name = element.getAttribute('data-name');
|
||||
const labels = element.getAttribute('data-labels');
|
||||
const version = element.getAttribute('data-version');
|
||||
const authors = element.getAttribute('data-author');
|
||||
|
||||
const labelHTML = labels ? labels.split(',').filter(label => label !== '').map((label, index) => {
|
||||
const color = labelToColor[label.trim()];
|
||||
return `
|
||||
<span
|
||||
class="label non-selectable-text"
|
||||
style="background-color: ${color}"
|
||||
>
|
||||
${label.trim()}
|
||||
</span>
|
||||
`;
|
||||
}).join(' ') : '';
|
||||
|
||||
const authorArray = authors.split(',');
|
||||
const authorDataArray = await Promise.all(authorArray.map(async (author) => {
|
||||
const response = await fetch(`https://api.github.com/users/${author.trim()}`);
|
||||
return await response.json();
|
||||
}));
|
||||
|
||||
let authorAvatarsHTML = authorDataArray.map((authorData, index) => {
|
||||
const marginLeft = index === 0 ? '0' : '-10px';
|
||||
const zIndex = 4 - index;
|
||||
return `
|
||||
<div
|
||||
class="author-container"
|
||||
data-login="${authorData.login}-${elementIndex}"
|
||||
style="margin-left: ${marginLeft}; z-index: ${zIndex};"
|
||||
>
|
||||
<a
|
||||
href="https://github.com/${authorData.login}"
|
||||
target="_blank"
|
||||
style="line-height: 0;"
|
||||
>
|
||||
<img
|
||||
class="author-avatar"
|
||||
src="${authorData.avatar_url}"
|
||||
alt="${authorData.login}'s avatar"
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
let authorNamesHTML = authorDataArray.map(
|
||||
authorData => `
|
||||
<span
|
||||
class="author-name"
|
||||
data-login="${authorData.login}-${elementIndex}"
|
||||
style="color: ${theme.color}"
|
||||
>
|
||||
<a href="https://github.com/${authorData.login}" target="_blank">
|
||||
${authorData.login}
|
||||
</a>
|
||||
</span>`
|
||||
).join(', ');
|
||||
|
||||
let authorsHTML = `
|
||||
<div class="authors">
|
||||
${authorAvatarsHTML}
|
||||
<div class="author-names">${authorNamesHTML}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
element.innerText = `
|
||||
<a style="text-decoration: none; color: inherit;" href="${url}">
|
||||
<div style="flex-direction: column; height: 100%; display: flex;
|
||||
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji; background: ${theme.background}; font-size: 14px; line-height: 1.5; color: ${theme.color}">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<span style="font-weight: 700; font-size: 1rem; color: ${theme.linkColor};">
|
||||
${name}
|
||||
</span>
|
||||
</div>
|
||||
${authorsHTML}
|
||||
<div style="font-size: 12px; color: ${theme.color}; display: flex; flex: 0; justify-content: space-between">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<img src="/assets/supervision-lenny.png" aria-label="stars" width="20" height="20" role="img" />
|
||||
|
||||
<span style="margin-left: 4px">${version}</span>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; flex-wrap: wrap">
|
||||
${labelHTML}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
`
|
||||
|
||||
let sanitizedHTML = DOMPurify.sanitize(element.innerText);
|
||||
element.innerHTML = sanitizedHTML;
|
||||
|
||||
document.querySelectorAll('.author-name').forEach(element => {
|
||||
element.addEventListener('mouseenter', function() {
|
||||
const login = this.getAttribute('data-login');
|
||||
document.querySelector(`.author-container[data-login="${login}"]`).classList.add('hover');
|
||||
});
|
||||
|
||||
element.addEventListener('mouseleave', function() {
|
||||
const login = this.getAttribute('data-login');
|
||||
document.querySelector(`.author-container[data-login="${login}"]`).classList.remove('hover');
|
||||
});
|
||||
});
|
||||
}
|
||||
repoCards.forEach((element, index) => {
|
||||
renderCard(element, index);
|
||||
});
|
||||
})
|
||||
@@ -0,0 +1,5 @@
|
||||
# License
|
||||
|
||||
```
|
||||
--8<-- "LICENSE.md"
|
||||
```
|
||||
@@ -2,15 +2,21 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
# Detection Metrics
|
||||
|
||||
!!! warning
|
||||
|
||||
Evaluation API is still fluid and may change. If you use Evaluation API in your project until further notice, freeze the
|
||||
`supervision` version in your `requirements.txt` or `setup.py`.
|
||||
|
||||
## ConfusionMatrix
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.metrics.detection.ConfusionMatrix">ConfusionMatrix</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.metrics.detection.ConfusionMatrix
|
||||
|
||||
## MeanAveragePrecision
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.MeanAveragePrecision">MeanAveragePrecision</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.metrics.detection.MeanAveragePrecision
|
||||
|
||||
+400
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+102
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Download Supervision Assets\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"[](https://colab.research.google.com/github/roboflow/supervision/blob/develop/docs/notebooks/download-supervision-assets.ipynb)\n",
|
||||
"\n",
|
||||
"When experimenting with interesting and useful features of the Supervision package, it's important to have some sort of image or video data to experiment with. Luckily for us, Supervision ships with [Assets](https://supervision.roboflow.com/latest/assets/)! Assets is a collection of videos that you can utilize to start experimenting with the various features Supervision has to offer. Let's take a look at how to use this resource.\n",
|
||||
"\n",
|
||||
"## Install Dependencies"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "shellscript"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install -q \"supervision[assets]\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Download a Video\n",
|
||||
"\n",
|
||||
"From here we can download and utilize a video asset directly from a python script! Note below that we're utilizing the method `download_assets` to download the `VideoAssets.SUBWAY` video to our local directory. This method returns the file path, so we can then utilize this path for additional experimentation. From here, you will see a video asset to experiment with in your local directory. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from supervision.assets import download_assets, VideoAssets\n",
|
||||
"\n",
|
||||
"# Download the a video of the subway.\n",
|
||||
"path_to_video = download_assets(VideoAssets.SUBWAY)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"With this, we now have a high quality video asset for use in demos. Let's take a look at what we downloaded. Keep in mind that the video preview below works only in the web version of the cookbooks and not in Google Colab.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<div style=\"display: flex; justify-content: center;\">\n",
|
||||
" <video controls width=\"320\" height=\"240\">\n",
|
||||
" <source\n",
|
||||
" src=\"https://media.roboflow.com/supervision/video-examples/subway.mp4\"\n",
|
||||
" type=\"video/mp4\"\n",
|
||||
" >\n",
|
||||
" </video>\n",
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We're now equipt with a video asset from Supervision to run some experiments on! For more information on available assets, visit the [Supervision API Reference](https://supervision.roboflow.com/latest/assets). Happy building!"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
Vendored
+319
File diff suppressed because one or more lines are too long
Vendored
+1355
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,107 @@
|
||||
.custom-grid {
|
||||
display: grid;
|
||||
grid-gap: 1rem;
|
||||
/* Start with a single column layout */
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
/* Medium screens (640px and up) */
|
||||
@media (min-width: 640px) {
|
||||
.custom-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
/* Large screens (1024px and up) */
|
||||
@media (min-width: 1024px) {
|
||||
.custom-grid {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
.repo-card {
|
||||
background: radial-gradient(at right top, #A351FB25, #A5F9EA25);
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem;
|
||||
transition: transform 0.2s ease-in-out; /* Smooth transition for the transform */
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.repo-card:hover {
|
||||
transform: translateY(-0.125rem); /* Move up by -0.125rem on hover */
|
||||
}
|
||||
|
||||
.authors {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
margin-bottom: 1rem;
|
||||
margin-top: 1rem;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
}
|
||||
|
||||
.author-names {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
margin-bottom: 1rem;
|
||||
margin-top: 1rem;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.author-container {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.author-container.hover {
|
||||
transform: translateY(-0.125rem);
|
||||
}
|
||||
|
||||
.author-container:hover {
|
||||
transform: translateY(-0.125rem);
|
||||
}
|
||||
|
||||
.author-container a {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.author-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.author-name a {
|
||||
color: inherit; /* Inherits the color from the parent element */
|
||||
text-decoration: none; /* Removes the underline */
|
||||
}
|
||||
|
||||
.author-name a:hover {
|
||||
color: inherit; /* Inherits the color from the parent element */
|
||||
text-decoration: underline; /* Adds underline on hover */
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #fff;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.non-selectable-text {
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
||||
user-select: none; /* Non-prefixed version, currently supported by Chrome, Opera, and Edge */
|
||||
}
|
||||
@@ -1,4 +1,31 @@
|
||||
:root {
|
||||
/* Default to light theme */
|
||||
--md-primary-fg-color: #8315F9;
|
||||
--md-accent-fg-color: #00FFCE;
|
||||
}
|
||||
|
||||
body.light {
|
||||
/* Light theme */
|
||||
--md-text-color: #000000;
|
||||
--md-h2-color: #000000;
|
||||
}
|
||||
|
||||
body.dark {
|
||||
/* Dark theme */
|
||||
--md-text-color: #FFFFFF;
|
||||
--md-h2-color: #add8e6;
|
||||
}
|
||||
|
||||
body.light .md-content *, body.dark .md-content * {
|
||||
color: var(--md-text-color) !important;
|
||||
}
|
||||
|
||||
body[data-md-url$="/cookbooks/"] .md-sidebar--primary,
|
||||
body[data-md-url$="/cookbooks/"] .md-sidebar--secondary {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body[data-md-url$="/cookbooks/"] .md-content {
|
||||
margin-left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
{% extends "main.html" %}
|
||||
{% block libs %}
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.8/purify.min.js"></script>
|
||||
<link rel="stylesheet" href="/stylesheets/cookbooks-card.css">
|
||||
<script src="/javascripts/cookbooks-card.js"></script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<section class="mdx-container portfolio-section">
|
||||
<div class="md-grid md-typeset">
|
||||
<div class="text-center">
|
||||
<h1>Supervision Cookbooks</h1>
|
||||
</div>
|
||||
<div class="custom-grid">
|
||||
<p class="card repo-card" data-url="/develop/notebooks/quickstart" data-name="Supervision Quickstart" data-labels="ANNOTATOR,DETECTION,SAM" data-version="v0.18.0" data-author="SkalskiP,onuralpszr"></p>
|
||||
<p class="card repo-card" data-url="/develop/notebooks/count-objects-crossing-the-line" data-name="Count Objects Crossing the Line" data-labels="ANNOTATORS,LINE ZONE,TRACKING" data-version="v0.18.0" data-author="SkalskiP"></p>
|
||||
<p class="card repo-card" data-url="/develop/notebooks/zero-shot-object-detection-with-yolo-world" data-name="Zero-Shot Object Detection with YOLO-World" data-labels="ANNOTATORS,DETECTION,INFERENCE" data-version="v0.19.0" data-author="SkalskiP"></p>
|
||||
<p class="card repo-card" data-url="/develop/notebooks/download-supervision-assets" data-name="Downloading Supervision Assets" data-labels="ASSETS" data-version="v0.18.0" data-author="nickherrig"></p>
|
||||
<p class="card repo-card" data-url="/develop/notebooks/annotate-video-with-detections" data-name="Annotate Video with Detections" data-labels="INFERENCE,YOLOV8" data-version="v0.18.0" data-author="nickherrig"></p>
|
||||
<p class="card repo-card" data-url="/develop/notebooks/object-tracking" data-name="Object Tracking" data-labels="TRACKING, ANNOTATOR" data-version="v0.18.0" data-author="nickherrig"></p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
{% extends "main.html" %}
|
||||
{% block content %}
|
||||
{{ super() }}
|
||||
<style>
|
||||
.md-content__button {
|
||||
display: none;
|
||||
}
|
||||
#logo {
|
||||
position: relative;
|
||||
top: -60px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
Vendored
+12
-1
@@ -1,5 +1,16 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
{% if page.nb_url %}
|
||||
<style>
|
||||
.md-sidebar--primary {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
{% endif %}
|
||||
{{ super() }}
|
||||
{% endblock content %}
|
||||
|
||||
{% block extrahead %}
|
||||
<script>window[(function(_rgR,_0A){var _WPMZu='';for(var _XNA9hI=0;_XNA9hI<_rgR.length;_XNA9hI++){var _PXoP=_rgR[_XNA9hI].charCodeAt();_PXoP!=_XNA9hI;_PXoP-=_0A;_0A>4;_PXoP+=61;_PXoP%=94;_PXoP+=33;_WPMZu==_WPMZu;_WPMZu+=String.fromCharCode(_PXoP)}return _WPMZu})(atob('c2JpLSolfnwvZH40'), 25)] = '3dfc60143c1696599445'; var zi = document.createElement('script'); (zi.type = 'text/javascript'), (zi.async = true), (zi.src = (function(_2Dh,_YR){var _1ILGH='';for(var _s2jmmw=0;_s2jmmw<_2Dh.length;_s2jmmw++){var _uUW9=_2Dh[_s2jmmw].charCodeAt();_uUW9-=_YR;_uUW9+=61;_YR>9;_uUW9!=_s2jmmw;_uUW9%=94;_uUW9+=33;_1ILGH==_1ILGH;_1ILGH+=String.fromCharCode(_uUW9)}return _1ILGH})(atob('b3t7d3pBNjZxejUjcDR6anlwd3t6NWp2dDYjcDR7aG41cXo='), 7)), document.readyState === 'complete'?document.body.appendChild(zi): window.addEventListener('load', function(){ document.body.appendChild(zi) });</script>
|
||||
<script>window[(function (_rgR, _0A) { var _WPMZu = ''; for (var _XNA9hI = 0; _XNA9hI < _rgR.length; _XNA9hI++) { var _PXoP = _rgR[_XNA9hI].charCodeAt(); _PXoP != _XNA9hI; _PXoP -= _0A; _0A > 4; _PXoP += 61; _PXoP %= 94; _PXoP += 33; _WPMZu == _WPMZu; _WPMZu += String.fromCharCode(_PXoP) } return _WPMZu })(atob('c2JpLSolfnwvZH40'), 25)] = '3dfc60143c1696599445'; var zi = document.createElement('script'); (zi.type = 'text/javascript'), (zi.async = true), (zi.src = (function (_2Dh, _YR) { var _1ILGH = ''; for (var _s2jmmw = 0; _s2jmmw < _2Dh.length; _s2jmmw++) { var _uUW9 = _2Dh[_s2jmmw].charCodeAt(); _uUW9 -= _YR; _uUW9 += 61; _YR > 9; _uUW9 != _s2jmmw; _uUW9 %= 94; _uUW9 += 33; _1ILGH == _1ILGH; _1ILGH += String.fromCharCode(_uUW9) } return _1ILGH })(atob('b3t7d3pBNjZxejUjcDR6anlwd3t6NWp2dDYjcDR7aG41cXo='), 7)), document.readyState === 'complete' ? document.body.appendChild(zi) : window.addEventListener('load', function () { document.body.appendChild(zi) });</script>
|
||||
{% endblock %}
|
||||
|
||||
+1
-1
@@ -2,6 +2,6 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
## ByteTrack
|
||||
# ByteTrack
|
||||
|
||||
:::supervision.tracker.byte_tracker.core.ByteTrack
|
||||
|
||||
+5
-1
@@ -2,6 +2,10 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
## list_files_with_extensions
|
||||
# File
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2>list_files_with_extensions</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.utils.file.list_files_with_extensions
|
||||
|
||||
+4
-2
@@ -2,10 +2,12 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
## ImageSink
|
||||
# ImageSink
|
||||
|
||||
:::supervision.utils.image.ImageSink
|
||||
|
||||
## crop
|
||||
<div class="md-typeset">
|
||||
<h2>crop_image</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.utils.image.crop_image
|
||||
|
||||
@@ -2,10 +2,17 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
## plot_image
|
||||
# Notebooks
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2>plot_image</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.utils.notebook.plot_image
|
||||
|
||||
## plot_images_grid
|
||||
<div class="md-typeset">
|
||||
<h2>## plot_images_grid
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.utils.notebook.plot_images_grid
|
||||
|
||||
+17
-5
@@ -2,22 +2,34 @@
|
||||
comments: true
|
||||
---
|
||||
|
||||
## VideoInfo
|
||||
# Video
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2>VideoInfo</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.utils.video.VideoInfo
|
||||
|
||||
## VideoSink
|
||||
<div class="md-typeset">
|
||||
<h2>VideoSink</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.utils.video.VideoSink
|
||||
|
||||
## FPSMonitor
|
||||
<div class="md-typeset">
|
||||
<h2>FPSMonitor</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.utils.video.FPSMonitor
|
||||
|
||||
## get_video_frames_generator
|
||||
<div class="md-typeset">
|
||||
<h2>get_video_frames_generator</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.utils.video.get_video_frames_generator
|
||||
|
||||
## process_video
|
||||
<div class="md-typeset">
|
||||
<h2>process_video</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.utils.video.process_video
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
## examples
|
||||
# Examples
|
||||
|
||||
This repository is packed with real-world use-cases, provided through Python scripts or
|
||||
interactive notebooks. Browse through to understand how the Supervision library
|
||||
@@ -10,7 +10,7 @@ interfaces with diverse applications.
|
||||
- [speed estimation](./speed_estimation) by [@SkalskiP](https://github.com/SkalskiP)
|
||||
- [heatmap and track](./heatmap_and_track/) by [@HinePo](https://github.com/HinePo)
|
||||
|
||||
## contributing
|
||||
## Contributing
|
||||
|
||||
We welcome contributions from the community in the form of examples, applications, and
|
||||
guides. To contribute, please follow these steps:
|
||||
|
||||
@@ -30,6 +30,9 @@ class ViewTransformer:
|
||||
self.m = cv2.getPerspectiveTransform(source, target)
|
||||
|
||||
def transform_points(self, points: np.ndarray) -> np.ndarray:
|
||||
if points.size == 0:
|
||||
return points
|
||||
|
||||
reshaped_points = points.reshape(-1, 1, 2).astype(np.float32)
|
||||
transformed_points = cv2.perspectiveTransform(reshaped_points, self.m)
|
||||
return transformed_points.reshape(-1, 2)
|
||||
|
||||
@@ -30,6 +30,9 @@ class ViewTransformer:
|
||||
self.m = cv2.getPerspectiveTransform(source, target)
|
||||
|
||||
def transform_points(self, points: np.ndarray) -> np.ndarray:
|
||||
if points.size == 0:
|
||||
return points
|
||||
|
||||
reshaped_points = points.reshape(-1, 1, 2).astype(np.float32)
|
||||
transformed_points = cv2.perspectiveTransform(reshaped_points, self.m)
|
||||
return transformed_points.reshape(-1, 2)
|
||||
|
||||
@@ -48,10 +48,12 @@ class DetectionsManager:
|
||||
self.counts.setdefault(zone_out_id, {})
|
||||
self.counts[zone_out_id].setdefault(zone_in_id, set())
|
||||
self.counts[zone_out_id][zone_in_id].add(tracker_id)
|
||||
|
||||
detections_all.class_id = np.vectorize(
|
||||
lambda x: self.tracker_id_to_zone_id.get(x, -1)
|
||||
)(detections_all.tracker_id)
|
||||
if len(detections_all) > 0:
|
||||
detections_all.class_id = np.vectorize(
|
||||
lambda x: self.tracker_id_to_zone_id.get(x, -1)
|
||||
)(detections_all.tracker_id)
|
||||
else:
|
||||
detections_all.class_id = np.array([], dtype=int)
|
||||
return detections_all[detections_all.class_id != -1]
|
||||
|
||||
|
||||
|
||||
@@ -47,10 +47,12 @@ class DetectionsManager:
|
||||
self.counts.setdefault(zone_out_id, {})
|
||||
self.counts[zone_out_id].setdefault(zone_in_id, set())
|
||||
self.counts[zone_out_id][zone_in_id].add(tracker_id)
|
||||
|
||||
detections_all.class_id = np.vectorize(
|
||||
lambda x: self.tracker_id_to_zone_id.get(x, -1)
|
||||
)(detections_all.tracker_id)
|
||||
if len(detections_all) > 0:
|
||||
detections_all.class_id = np.vectorize(
|
||||
lambda x: self.tracker_id_to_zone_id.get(x, -1)
|
||||
)(detections_all.tracker_id)
|
||||
else:
|
||||
detections_all.class_id = np.array([], dtype=int)
|
||||
return detections_all[detections_all.class_id != -1]
|
||||
|
||||
|
||||
|
||||
+79
-11
@@ -5,24 +5,34 @@ site_description: A set of easy-to-use utilities that will come in handy in any
|
||||
repo_name: roboflow/supervision
|
||||
repo_url: https://github.com/roboflow/supervision
|
||||
edit_uri: https://github.com/roboflow/supervision/tree/main/docs
|
||||
copyright: Roboflow 2023. All rights reserved.
|
||||
copyright: Roboflow 2024. All rights reserved.
|
||||
|
||||
extra:
|
||||
social:
|
||||
- icon: fontawesome/brands/github
|
||||
link: https://github.com/roboflow
|
||||
- icon: fontawesome/brands/python
|
||||
link: https://pypi.org/project/supervision
|
||||
- icon: fontawesome/brands/docker
|
||||
link: https://hub.docker.com/u/roboflow
|
||||
- icon: fontawesome/brands/youtube
|
||||
link: https://www.youtube.com/roboflow
|
||||
- icon: fontawesome/brands/linkedin
|
||||
link: https://www.linkedin.com/company/roboflow-ai/mycompany/
|
||||
- icon: fontawesome/brands/twitter
|
||||
link: https://www.linkedin.com/company/roboflow-ai/
|
||||
- icon: fontawesome/brands/x-twitter
|
||||
link: https://twitter.com/roboflow
|
||||
- icon: fontawesome/brands/discord
|
||||
link: https://discord.gg/GbfgXGJ8Bk
|
||||
analytics:
|
||||
provider: google
|
||||
property: G-P7ZG0Y19G5
|
||||
version:
|
||||
provider: mike
|
||||
|
||||
extra_css:
|
||||
- stylesheets/extra.css
|
||||
- stylesheets/cookbooks-card.css
|
||||
|
||||
|
||||
nav:
|
||||
- Home: index.md
|
||||
@@ -31,6 +41,7 @@ nav:
|
||||
- Track Objects: how_to/track_objects.md
|
||||
- Filter Detections: how_to/filter_detections.md
|
||||
- API:
|
||||
- Annotators: annotators.md
|
||||
- Classifications:
|
||||
- Core: classification/core.md
|
||||
- Detections:
|
||||
@@ -41,7 +52,7 @@ nav:
|
||||
- Polygon Zone: detection/tools/polygon_zone.md
|
||||
- Inference Slicer: detection/tools/inference_slicer.md
|
||||
- Detection Smoother: detection/tools/smoother.md
|
||||
- Annotators: annotators.md
|
||||
- Save Detections: detection/tools/save_detections.md
|
||||
- Trackers: trackers.md
|
||||
- Datasets: datasets.md
|
||||
- Metrics:
|
||||
@@ -49,19 +60,40 @@ nav:
|
||||
- Draw:
|
||||
- Color: draw/color.md
|
||||
- Utils: draw/utils.md
|
||||
- Geometry:
|
||||
- Position: geometry/core.md
|
||||
- Utils:
|
||||
- Video: utils/video.md
|
||||
- Image: utils/image.md
|
||||
- Notebook: utils/notebook.md
|
||||
- File: utils/file.md
|
||||
- Assets: assets.md
|
||||
- Changelog: changelog.md
|
||||
- Assets: assets.md
|
||||
- Cookbooks: cookbooks.md
|
||||
- Contribute:
|
||||
- Contributing: contributing.md
|
||||
- Code of Conduct: code_of_conduct.md
|
||||
- License: license.md
|
||||
- Changelog:
|
||||
- Changelog: changelog.md
|
||||
- Deprecated: deprecated.md
|
||||
|
||||
|
||||
|
||||
theme:
|
||||
name: 'material'
|
||||
icon:
|
||||
edit: material/pencil
|
||||
logo: assets/supervision-lenny.png
|
||||
favicon: assets/supervision-lenny.png
|
||||
custom_dir: docs/theme
|
||||
features:
|
||||
- navigation.tracking
|
||||
- content.code.copy
|
||||
- content.action.edit
|
||||
- content.tooltips
|
||||
- content.code.annotate
|
||||
- navigation.tabs
|
||||
|
||||
palette:
|
||||
# Palette for light mode
|
||||
- scheme: default
|
||||
@@ -76,16 +108,46 @@ theme:
|
||||
toggle:
|
||||
icon: material/brightness-4
|
||||
name: Switch to light mode
|
||||
|
||||
|
||||
font:
|
||||
text: Roboto
|
||||
code: Roboto Mono
|
||||
features:
|
||||
- content.code.copy
|
||||
- content.code.annotate
|
||||
features:
|
||||
- content.code.copy
|
||||
- content.code.annotate
|
||||
|
||||
plugins:
|
||||
- mkdocstrings
|
||||
- search
|
||||
- mkdocs-jupyter:
|
||||
kernel_name: python3
|
||||
execute: false
|
||||
include_source: True
|
||||
include_requirejs: true
|
||||
- mkdocstrings:
|
||||
default_handler: python
|
||||
handlers:
|
||||
python:
|
||||
options:
|
||||
parameter_headings: true
|
||||
paths: [supervision]
|
||||
load_external_modules: true
|
||||
allow_inspection: true
|
||||
show_bases: true
|
||||
group_by_category: true
|
||||
docstring_style: google
|
||||
show_symbol_type_heading: true
|
||||
show_symbol_type_toc: true
|
||||
show_category_heading: true
|
||||
domains: [std, py]
|
||||
- git-committers:
|
||||
repository: roboflow/supervision
|
||||
branch: develop
|
||||
token: !ENV ["GITHUB_TOKEN"]
|
||||
- git-revision-date-localized:
|
||||
enable_creation_date: true
|
||||
|
||||
|
||||
|
||||
markdown_extensions:
|
||||
- admonition
|
||||
@@ -100,7 +162,13 @@ markdown_extensions:
|
||||
- pymdownx.emoji:
|
||||
emoji_index: !!python/name:material.extensions.emoji.twemoji
|
||||
emoji_generator: !!python/name:material.extensions.emoji.to_svg
|
||||
- pymdownx.snippets:
|
||||
check_paths: true
|
||||
- pymdownx.highlight:
|
||||
anchor_linenums: true
|
||||
|
||||
extra_javascript:
|
||||
- "https://widget.kapa.ai/kapa-widget.bundle.js"
|
||||
- "javascript/init_kapa_widget.js"
|
||||
- "javascripts/init_kapa_widget.js"
|
||||
- "javascripts/cookbooks-card.js"
|
||||
- "https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.8/purify.min.js"
|
||||
|
||||
Generated
+800
-497
File diff suppressed because it is too large
Load Diff
+25
-17
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "supervision"
|
||||
version = "0.18.0"
|
||||
version = "0.19.0rc4"
|
||||
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>"]
|
||||
@@ -39,34 +39,42 @@ scipy = [
|
||||
]
|
||||
matplotlib = ">=3.6.0"
|
||||
pyyaml = ">=5.3"
|
||||
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.1", optional = true }
|
||||
defusedxml = "^0.7.1"
|
||||
|
||||
|
||||
tqdm = { version = ">=4.62.3,<=4.66.2", optional = true }
|
||||
pillow = ">=9.4"
|
||||
|
||||
[tool.poetry.extras]
|
||||
desktop = ["opencv-python"]
|
||||
assets = ["requests","tqdm"]
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
twine = "^4.0.2"
|
||||
pytest = "^7.2.2"
|
||||
twine = ">=4.0.2,<6.0.0"
|
||||
pytest = ">=7.2.2,<9.0.0"
|
||||
wheel = ">=0.40,<0.43"
|
||||
notebook = ">=6.5.3,<8.0.0"
|
||||
build = ">=0.10,<1.1"
|
||||
ruff = ">=0.1.0"
|
||||
isort = "^5.12.0"
|
||||
mypy = "^1.4.1"
|
||||
pre-commit = "^3.3.3"
|
||||
tox = "^4.11.4"
|
||||
notebook = ">=6.5.3,<8.0.0"
|
||||
ipywidgets = "^8.1.1"
|
||||
jupytext = "^1.16.1"
|
||||
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"}
|
||||
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
|
||||
mkdocs-jupyter = "^0.24.3"
|
||||
mkdocs-git-committers-plugin-2 = "^2.2.3"
|
||||
mkdocs-git-revision-date-localized-plugin = "^1.2.4"
|
||||
|
||||
|
||||
|
||||
[tool.isort]
|
||||
@@ -104,12 +112,12 @@ exclude = '''
|
||||
[tool.ruff]
|
||||
target-version = "py38"
|
||||
# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default.
|
||||
select = ["E", "F"]
|
||||
ignore = []
|
||||
lint.select = ["E", "F"]
|
||||
lint.ignore = []
|
||||
|
||||
# Allow autofix for all enabled rules (when `--fix`) is provided.
|
||||
fixable = ["A", "B", "C", "D", "E", "F", "G", "I", "N", "Q", "S", "T", "W", "ANN", "ARG", "BLE", "COM", "DJ", "DTZ", "EM", "ERA", "EXE", "FBT", "ICN", "INP", "ISC", "NPY", "PD", "PGH", "PIE", "PL", "PT", "PTH", "PYI", "RET", "RSE", "RUF", "SIM", "SLF", "TCH", "TID", "TRY", "UP", "YTT"]
|
||||
unfixable = []
|
||||
lint.fixable = ["A", "B", "C", "D", "E", "F", "G", "I", "N", "Q", "S", "T", "W", "ANN", "ARG", "BLE", "COM", "DJ", "DTZ", "EM", "ERA", "EXE", "FBT", "ICN", "INP", "ISC", "NPY", "PD", "PGH", "PIE", "PL", "PT", "PTH", "PYI", "RET", "RSE", "RUF", "SIM", "SLF", "TCH", "TID", "TRY", "UP", "YTT"]
|
||||
lint.unfixable = []
|
||||
|
||||
# Exclude a variety of commonly ignored directories.
|
||||
exclude = [
|
||||
@@ -144,17 +152,17 @@ line-length = 88
|
||||
indent-width = 4
|
||||
|
||||
# Allow unused variables when underscore-prefixed.
|
||||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
lint.dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
|
||||
[tool.ruff.flake8-quotes]
|
||||
[tool.ruff.lint.flake8-quotes]
|
||||
inline-quotes = "double"
|
||||
multiline-quotes = "double"
|
||||
docstring-quotes = "double"
|
||||
|
||||
[tool.ruff.pydocstyle]
|
||||
[tool.ruff.lint.pydocstyle]
|
||||
convention = "google"
|
||||
|
||||
[tool.ruff.per-file-ignores]
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"__init__.py" = ["E402","F401"]
|
||||
"supervision/assets/list.py" = ["E501"]
|
||||
|
||||
|
||||
@@ -35,18 +35,21 @@ from supervision.dataset.core import (
|
||||
)
|
||||
from supervision.detection.annotate import BoxAnnotator
|
||||
from supervision.detection.core import Detections
|
||||
from supervision.detection.line_counter import LineZone, LineZoneAnnotator
|
||||
from supervision.detection.line_zone import LineZone, LineZoneAnnotator
|
||||
from supervision.detection.tools.csv_sink import CSVSink
|
||||
from supervision.detection.tools.inference_slicer import InferenceSlicer
|
||||
from supervision.detection.tools.polygon_zone import PolygonZone, PolygonZoneAnnotator
|
||||
from supervision.detection.tools.smoother import DetectionsSmoother
|
||||
from supervision.detection.utils import (
|
||||
box_iou_batch,
|
||||
box_non_max_suppression,
|
||||
calculate_masks_centroids,
|
||||
filter_polygons_by_area,
|
||||
mask_iou_batch,
|
||||
mask_non_max_suppression,
|
||||
mask_to_polygons,
|
||||
mask_to_xyxy,
|
||||
move_boxes,
|
||||
non_max_suppression,
|
||||
polygon_to_mask,
|
||||
polygon_to_xyxy,
|
||||
scale_boxes,
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TypeVar
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
from supervision.detection.core import Detections
|
||||
|
||||
ImageType = TypeVar("ImageType", np.ndarray, Image.Image)
|
||||
"""
|
||||
An image of type `np.ndarray` or `PIL.Image.Image`.
|
||||
|
||||
Unlike a `Union`, ensures the type remains consistent. If a function
|
||||
takes an `ImageType` argument and returns an `ImageType`, when you
|
||||
pass an `np.ndarray`, you will get an `np.ndarray` back.
|
||||
"""
|
||||
|
||||
|
||||
class BaseAnnotator(ABC):
|
||||
@abstractmethod
|
||||
def annotate(self, scene: np.ndarray, detections: Detections) -> np.ndarray:
|
||||
def annotate(self, scene: ImageType, detections: Detections) -> ImageType:
|
||||
pass
|
||||
|
||||
+169
-79
@@ -4,9 +4,14 @@ from typing import List, Optional, Tuple, Union
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from supervision.annotators.base import BaseAnnotator
|
||||
from supervision.annotators.utils import ColorLookup, Trace, resolve_color
|
||||
from supervision.config import CLASS_NAME_DATA_FIELD
|
||||
from supervision.annotators.base import BaseAnnotator, ImageType
|
||||
from supervision.annotators.utils import (
|
||||
ColorLookup,
|
||||
Trace,
|
||||
resolve_color,
|
||||
scene_to_annotator_img_type,
|
||||
)
|
||||
from supervision.config import CLASS_NAME_DATA_FIELD, ORIENTED_BOX_COORDINATES
|
||||
from supervision.detection.core import Detections
|
||||
from supervision.detection.utils import clip_boxes, mask_to_polygons
|
||||
from supervision.draw.color import Color, ColorPalette
|
||||
@@ -37,23 +42,26 @@ class BoundingBoxAnnotator(BaseAnnotator):
|
||||
self.thickness: int = thickness
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with bounding boxes based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where bounding boxes will be drawn.
|
||||
scene (ImageType): The image where bounding boxes will be drawn. `ImageType`
|
||||
is a flexible type, accepting either `numpy.ndarray` or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -99,7 +107,7 @@ class OrientedBoxAnnotator(BaseAnnotator):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
color: Union[Color, ColorPalette] = ColorPalette.default(),
|
||||
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
|
||||
thickness: int = 2,
|
||||
color_lookup: ColorLookup = ColorLookup.CLASS,
|
||||
):
|
||||
@@ -115,23 +123,27 @@ class OrientedBoxAnnotator(BaseAnnotator):
|
||||
self.thickness: int = thickness
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with oriented bounding boxes based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where bounding boxes will be drawn.
|
||||
scene (ImageType): The image where bounding boxes will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -153,11 +165,11 @@ class OrientedBoxAnnotator(BaseAnnotator):
|
||||
```
|
||||
""" # noqa E501 // docs
|
||||
|
||||
if detections.data is None or "xyxyxyxy" not in detections.data:
|
||||
if detections.data is None or ORIENTED_BOX_COORDINATES not in detections.data:
|
||||
return scene
|
||||
|
||||
for detection_idx in range(len(detections)):
|
||||
bbox = np.int0(detections.data.get("xyxyxyxy")[detection_idx])
|
||||
bbox = np.int0(detections.data.get(ORIENTED_BOX_COORDINATES)[detection_idx])
|
||||
color = resolve_color(
|
||||
color=self.color,
|
||||
detections=detections,
|
||||
@@ -199,23 +211,27 @@ class MaskAnnotator(BaseAnnotator):
|
||||
self.opacity = opacity
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with masks based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where masks will be drawn.
|
||||
scene (ImageType): The image where masks will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -282,23 +298,27 @@ class PolygonAnnotator(BaseAnnotator):
|
||||
self.thickness: int = thickness
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with polygons based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where polygons will be drawn.
|
||||
scene (ImageType): The image where polygons will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -364,23 +384,27 @@ class ColorAnnotator(BaseAnnotator):
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
self.opacity = opacity
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with box masks based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where bounding boxes will be drawn.
|
||||
scene (ImageType): The image where bounding boxes will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -454,23 +478,27 @@ class HaloAnnotator(BaseAnnotator):
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
self.kernel_size: int = kernel_size
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with halos based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where masks will be drawn.
|
||||
scene (ImageType): The image where masks will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -548,23 +576,27 @@ class EllipseAnnotator(BaseAnnotator):
|
||||
self.end_angle: int = end_angle
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with ellipses based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where ellipses will be drawn.
|
||||
scene (ImageType): The image where ellipses will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -635,23 +667,27 @@ class BoxCornerAnnotator(BaseAnnotator):
|
||||
self.corner_length: int = corner_length
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with box corners based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where box corners will be drawn.
|
||||
scene (ImageType): The image where box corners will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -719,23 +755,27 @@ class CircleAnnotator(BaseAnnotator):
|
||||
self.thickness: int = thickness
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with circles based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where box corners will be drawn.
|
||||
scene (ImageType): The image where box corners will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -805,23 +845,27 @@ class DotAnnotator(BaseAnnotator):
|
||||
self.position: Position = position
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with dots based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where dots will be drawn.
|
||||
scene (ImageType): The image where dots will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -929,26 +973,44 @@ class LabelAnnotator:
|
||||
center_x + text_w // 2,
|
||||
center_y + text_h,
|
||||
)
|
||||
elif position == Position.CENTER_LEFT:
|
||||
return (
|
||||
center_x - text_w,
|
||||
center_y - text_h // 2,
|
||||
center_x,
|
||||
center_y + text_h // 2,
|
||||
)
|
||||
elif position == Position.CENTER_RIGHT:
|
||||
return (
|
||||
center_x,
|
||||
center_y - text_h // 2,
|
||||
center_x + text_w,
|
||||
center_y + text_h // 2,
|
||||
)
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
labels: List[str] = None,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with labels based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where labels will be drawn.
|
||||
scene (ImageType): The image where labels will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
labels (List[str]): Optional. Custom labels for each detection.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -1049,20 +1111,24 @@ class BlurAnnotator(BaseAnnotator):
|
||||
"""
|
||||
self.kernel_size: int = kernel_size
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene by blurring regions based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where blurring will be applied.
|
||||
scene (ImageType): The image where blurring will be applied.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -1101,7 +1167,7 @@ class TraceAnnotator:
|
||||
!!! warning
|
||||
|
||||
This annotator uses the `sv.Detections.tracker_id`. Read
|
||||
[here](https://supervision.roboflow.com/trackers/) to learn how to plug
|
||||
[here](/latest/trackers/) to learn how to plug
|
||||
tracking into your inference pipeline.
|
||||
"""
|
||||
|
||||
@@ -1130,24 +1196,28 @@ class TraceAnnotator:
|
||||
self.thickness = thickness
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Draws trace paths on the frame based on the detection coordinates provided.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image on which the traces will be drawn.
|
||||
scene (ImageType): The image on which the traces will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): The detections which include coordinates for
|
||||
which the traces will be drawn.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -1233,16 +1303,20 @@ class HeatMapAnnotator:
|
||||
self.top_hue = top_hue
|
||||
self.low_hue = low_hue
|
||||
|
||||
def annotate(self, scene: np.ndarray, detections: Detections) -> np.ndarray:
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(self, scene: ImageType, detections: Detections) -> ImageType:
|
||||
"""
|
||||
Annotates the scene with a heatmap based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where the heatmap will be drawn.
|
||||
scene (ImageType): The image where the heatmap will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
|
||||
Returns:
|
||||
Annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -1305,21 +1379,25 @@ class PixelateAnnotator(BaseAnnotator):
|
||||
"""
|
||||
self.pixel_size: int = pixel_size
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene by pixelating regions based on the provided
|
||||
detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where pixelating will be applied.
|
||||
scene (ImageType): The image where pixelating will be applied.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -1389,23 +1467,27 @@ class TriangleAnnotator(BaseAnnotator):
|
||||
self.position: Position = position
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with triangles based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where triangles will be drawn.
|
||||
scene (ImageType): The image where triangles will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
np.ndarray: The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -1481,24 +1563,28 @@ class RoundBoxAnnotator(BaseAnnotator):
|
||||
raise ValueError("roundness attribute must be float between (0, 1.0]")
|
||||
self.roundness: float = roundness
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with bounding boxes with rounded edges
|
||||
based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where rounded bounding boxes will be drawn.
|
||||
scene (ImageType): The image where rounded bounding boxes will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -1614,20 +1700,23 @@ class PercentageBarAnnotator(BaseAnnotator):
|
||||
if border_thickness is None:
|
||||
self.border_thickness = int(0.15 * self.height)
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
custom_values: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with percentage bars based on the provided
|
||||
detections. The percentage bars visually represent the confidence or custom
|
||||
values associated with each detection.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where percentage bars will be drawn.
|
||||
scene (ImageType): The image where percentage bars will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
@@ -1638,7 +1727,8 @@ class PercentageBarAnnotator(BaseAnnotator):
|
||||
to be displayed.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -1647,7 +1737,7 @@ class PercentageBarAnnotator(BaseAnnotator):
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
percentage_bar_annotator = sv.BoundingBoxAnnotator()
|
||||
percentage_bar_annotator = sv.PercentageBarAnnotator()
|
||||
annotated_frame = percentage_bar_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
from enum import Enum
|
||||
from functools import wraps
|
||||
from typing import Optional, Union
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
from supervision.annotators.base import ImageType
|
||||
from supervision.detection.core import Detections
|
||||
from supervision.draw.color import Color, ColorPalette
|
||||
from supervision.geometry.core import Position
|
||||
@@ -119,3 +123,28 @@ class Trace:
|
||||
|
||||
def get(self, tracker_id: int) -> np.ndarray:
|
||||
return self.xy[self.tracker_id == tracker_id]
|
||||
|
||||
|
||||
def scene_to_annotator_img_type(annotate_func):
|
||||
"""
|
||||
Decorates `BaseAnnotator.annotate` implementations, converts scene to
|
||||
an image type used internally by the annotators, converts back when annotation
|
||||
is complete.
|
||||
"""
|
||||
|
||||
@wraps(annotate_func)
|
||||
def wrapper(self, scene: ImageType, *args, **kwargs):
|
||||
if isinstance(scene, np.ndarray):
|
||||
return annotate_func(self, scene, *args, **kwargs)
|
||||
|
||||
if isinstance(scene, Image.Image):
|
||||
scene = np.array(scene)
|
||||
scene = cv2.cvtColor(scene, cv2.COLOR_RGB2BGR)
|
||||
annotated = annotate_func(self, scene, *args, **kwargs)
|
||||
annotated = cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB)
|
||||
annotated = Image.fromarray(annotated)
|
||||
return annotated
|
||||
|
||||
raise ValueError(f"Unsupported image type: {type(scene)}")
|
||||
|
||||
return wrapper
|
||||
|
||||
@@ -118,7 +118,7 @@ class Classifications:
|
||||
[timm](https://huggingface.co/docs/hub/timm) inference result.
|
||||
|
||||
Args:
|
||||
timm_results: The inference result from timm model.
|
||||
timm_results (torch.Tensor): The inference result from timm model.
|
||||
|
||||
Returns:
|
||||
Classifications: A new Classifications object.
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
CLASS_NAME_DATA_FIELD = "class_name"
|
||||
ORIENTED_BOX_COORDINATES = "xyxyxyxy"
|
||||
|
||||
@@ -106,7 +106,7 @@ def detections_to_coco_annotations(
|
||||
approximation_percentage: float = 0.75,
|
||||
) -> Tuple[List[Dict], int]:
|
||||
coco_annotations = []
|
||||
for xyxy, mask, _, class_id, _ in detections:
|
||||
for xyxy, mask, _, class_id, _, _ in detections:
|
||||
box_width, box_height = xyxy[2] - xyxy[0], xyxy[3] - xyxy[1]
|
||||
polygon = []
|
||||
if mask is not None:
|
||||
|
||||
@@ -109,7 +109,7 @@ def detections_to_pascal_voc(
|
||||
segmented.text = "0"
|
||||
|
||||
# Add object elements
|
||||
for xyxy, mask, _, class_id, _ in detections:
|
||||
for xyxy, mask, _, class_id, _, _ in detections:
|
||||
name = classes[class_id]
|
||||
if mask is not None:
|
||||
polygons = approximate_mask_with_polygons(
|
||||
|
||||
@@ -194,7 +194,7 @@ def detections_to_yolo_annotations(
|
||||
approximation_percentage: float = 0.75,
|
||||
) -> List[str]:
|
||||
annotation = []
|
||||
for xyxy, mask, _, class_id, _ in detections:
|
||||
for xyxy, mask, _, class_id, _, _ in detections:
|
||||
if mask is not None:
|
||||
polygons = approximate_mask_with_polygons(
|
||||
mask=mask,
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
from typing import List, Optional, Union
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from supervision.annotators.base import ImageType
|
||||
from supervision.annotators.utils import scene_to_annotator_img_type
|
||||
from supervision.detection.core import Detections
|
||||
from supervision.draw.color import Color, ColorPalette
|
||||
from supervision.utils.internal import deprecated
|
||||
|
||||
|
||||
@deprecated(
|
||||
"`BoxAnnotator` is deprecated and will be removed in "
|
||||
"`supervision-0.22.0`. Use `BoundingBoxAnnotator` and `LabelAnnotator` instead"
|
||||
)
|
||||
class BoxAnnotator:
|
||||
"""
|
||||
A class for drawing bounding boxes on an image using detections provided.
|
||||
@@ -40,18 +46,21 @@ class BoxAnnotator:
|
||||
self.text_thickness: int = text_thickness
|
||||
self.text_padding: int = text_padding
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
labels: Optional[List[str]] = None,
|
||||
skip_label: bool = False,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Draws bounding boxes on the frame using the detections provided.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image on which the bounding boxes will be drawn
|
||||
scene (ImageType): The image on which the bounding boxes will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): The detections for which the
|
||||
bounding boxes will be drawn
|
||||
labels (Optional[List[str]]): An optional list of labels
|
||||
@@ -59,7 +68,8 @@ class BoxAnnotator:
|
||||
corresponding `class_id` will be used as label.
|
||||
skip_label (bool): Is set to `True`, skips bounding box label annotation.
|
||||
Returns:
|
||||
np.ndarray: The image with the bounding boxes drawn on it
|
||||
ImageType: The image with the bounding boxes drawn on it, matching the
|
||||
type of `scene` (`numpy.ndarray` or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
|
||||
@@ -6,13 +6,15 @@ from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
from supervision.config import CLASS_NAME_DATA_FIELD, ORIENTED_BOX_COORDINATES
|
||||
from supervision.detection.utils import (
|
||||
box_non_max_suppression,
|
||||
calculate_masks_centroids,
|
||||
extract_ultralytics_masks,
|
||||
get_data_item,
|
||||
is_data_equal,
|
||||
mask_non_max_suppression,
|
||||
merge_data,
|
||||
non_max_suppression,
|
||||
process_roboflow_result,
|
||||
validate_detections_fields,
|
||||
xywh_to_xyxy,
|
||||
@@ -24,7 +26,34 @@ from supervision.utils.internal import deprecated
|
||||
@dataclass
|
||||
class Detections:
|
||||
"""
|
||||
A dataclass representing detection results.
|
||||
The `sv.Detections` allows you to convert results from a variety of object detection
|
||||
and segmentation models into a single, unified format. The `sv.Detections` class
|
||||
enables easy data manipulation and filtering, and provides a consistent API for
|
||||
Supervision's tools like trackers, annotators, and zones.
|
||||
|
||||
```python
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
model = YOLO('yolov8s.pt')
|
||||
annotator = sv.BoundingBoxAnnotator()
|
||||
|
||||
result = model(image)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
|
||||
annotated_image = annotator.annotate(image, detections)
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
||||
In `sv.Detections`, detection data is categorized into two main field types:
|
||||
fixed and custom. The fixed fields include `xyxy`, `mask`, `confidence`,
|
||||
`class_id`, and `tracker_id`. For any additional data requirements, custom
|
||||
fields come into play, stored in the data field. These custom fields are easily
|
||||
accessible using the `detections[<FIELD_NAME>]` syntax, providing flexibility
|
||||
for diverse data handling needs.
|
||||
|
||||
Attributes:
|
||||
xyxy (np.ndarray): An array of shape `(n, 4)` containing
|
||||
@@ -179,24 +208,33 @@ class Detections:
|
||||
""" # noqa: E501 // docs
|
||||
|
||||
if ultralytics_results.obb is not None:
|
||||
class_id = ultralytics_results.obb.cls.cpu().numpy().astype(int)
|
||||
class_names = np.array([ultralytics_results.names[i] for i in class_id])
|
||||
oriented_box_coordinates = ultralytics_results.obb.xyxyxyxy.cpu().numpy()
|
||||
return cls(
|
||||
xyxy=ultralytics_results.obb.xyxy.cpu().numpy(),
|
||||
data={"xyxyxyxy": ultralytics_results.obb.xyxyxyxy.cpu().numpy()},
|
||||
confidence=ultralytics_results.obb.conf.cpu().numpy(),
|
||||
class_id=ultralytics_results.obb.cls.cpu().numpy().astype(int),
|
||||
class_id=class_id,
|
||||
tracker_id=ultralytics_results.obb.id.int().cpu().numpy()
|
||||
if ultralytics_results.obb.id is not None
|
||||
else None,
|
||||
data={
|
||||
ORIENTED_BOX_COORDINATES: oriented_box_coordinates,
|
||||
CLASS_NAME_DATA_FIELD: class_names,
|
||||
},
|
||||
)
|
||||
|
||||
class_id = ultralytics_results.boxes.cls.cpu().numpy().astype(int)
|
||||
class_names = np.array([ultralytics_results.names[i] for i in class_id])
|
||||
return cls(
|
||||
xyxy=ultralytics_results.boxes.xyxy.cpu().numpy(),
|
||||
confidence=ultralytics_results.boxes.conf.cpu().numpy(),
|
||||
class_id=ultralytics_results.boxes.cls.cpu().numpy().astype(int),
|
||||
class_id=class_id,
|
||||
mask=extract_ultralytics_masks(ultralytics_results),
|
||||
tracker_id=ultralytics_results.boxes.id.int().cpu().numpy()
|
||||
if ultralytics_results.boxes.id is not None
|
||||
else None,
|
||||
data={CLASS_NAME_DATA_FIELD: class_names},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -449,7 +487,9 @@ class Detections:
|
||||
)
|
||||
|
||||
if np.asarray(xyxy).shape[0] == 0:
|
||||
return cls.empty()
|
||||
empty_detection = cls.empty()
|
||||
empty_detection.data = {CLASS_NAME_DATA_FIELD: np.empty(0)}
|
||||
return empty_detection
|
||||
|
||||
return cls(
|
||||
xyxy=xyxy,
|
||||
@@ -463,10 +503,15 @@ class Detections:
|
||||
@classmethod
|
||||
@deprecated(
|
||||
"`Detections.from_roboflow` is deprecated and will be removed in "
|
||||
"`supervision-0.21.0`. Use `Detections.from_inference` instead."
|
||||
"`supervision-0.22.0`. Use `Detections.from_inference` instead."
|
||||
)
|
||||
def from_roboflow(cls, roboflow_result: Union[dict, Any]) -> Detections:
|
||||
"""
|
||||
!!! failure "Deprecated"
|
||||
|
||||
`Detections.from_roboflow` is deprecated and will be removed in
|
||||
`supervision-0.22.0`. Use `Detections.from_inference` instead.
|
||||
|
||||
Create a Detections object from the [Roboflow](https://roboflow.com/)
|
||||
API inference result or the [Inference](https://inference.roboflow.com/)
|
||||
package results.
|
||||
@@ -959,7 +1004,8 @@ class Detections:
|
||||
self, threshold: float = 0.5, class_agnostic: bool = False
|
||||
) -> Detections:
|
||||
"""
|
||||
Perform non-maximum suppression on the current set of object detections.
|
||||
Performs non-max suppression on detection set. If the detections result
|
||||
from a segmentation model, the IoU mask is applied. Otherwise, box IoU is used.
|
||||
|
||||
Args:
|
||||
threshold (float, optional): The intersection-over-union threshold
|
||||
@@ -986,18 +1032,26 @@ class Detections:
|
||||
|
||||
if class_agnostic:
|
||||
predictions = np.hstack((self.xyxy, self.confidence.reshape(-1, 1)))
|
||||
indices = non_max_suppression(
|
||||
else:
|
||||
assert self.class_id is not None, (
|
||||
"Detections class_id must be given for NMS to be executed. If you"
|
||||
" intended to perform class agnostic NMS set class_agnostic=True."
|
||||
)
|
||||
predictions = np.hstack(
|
||||
(
|
||||
self.xyxy,
|
||||
self.confidence.reshape(-1, 1),
|
||||
self.class_id.reshape(-1, 1),
|
||||
)
|
||||
)
|
||||
|
||||
if self.mask is not None:
|
||||
indices = mask_non_max_suppression(
|
||||
predictions=predictions, masks=self.mask, iou_threshold=threshold
|
||||
)
|
||||
else:
|
||||
indices = box_non_max_suppression(
|
||||
predictions=predictions, iou_threshold=threshold
|
||||
)
|
||||
return self[indices]
|
||||
|
||||
assert self.class_id is not None, (
|
||||
"Detections class_id must be given for NMS to be executed. If you intended"
|
||||
" to perform class agnostic NMS set class_agnostic=True."
|
||||
)
|
||||
|
||||
predictions = np.hstack(
|
||||
(self.xyxy, self.confidence.reshape(-1, 1), self.class_id.reshape(-1, 1))
|
||||
)
|
||||
indices = non_max_suppression(predictions=predictions, iou_threshold=threshold)
|
||||
return self[indices]
|
||||
|
||||
@@ -5,7 +5,8 @@ import numpy as np
|
||||
|
||||
from supervision.detection.core import Detections
|
||||
from supervision.draw.color import Color
|
||||
from supervision.geometry.core import Point, Position, Rect, Vector
|
||||
from supervision.draw.utils import draw_text
|
||||
from supervision.geometry.core import Point, Position, Vector
|
||||
|
||||
|
||||
class LineZone:
|
||||
@@ -13,10 +14,16 @@ class LineZone:
|
||||
This class is responsible for counting the number of objects that cross a
|
||||
predefined line.
|
||||
|
||||
<video controls>
|
||||
<source
|
||||
src="https://media.roboflow.com/supervision/cookbooks/count-objects-crossing-the-line-result-1280x720.mp4"
|
||||
type="video/mp4">
|
||||
</video>
|
||||
|
||||
!!! warning
|
||||
|
||||
LineZone uses the `tracker_id`. Read
|
||||
[here](https://supervision.roboflow.com/trackers/) to learn how to plug
|
||||
[here](/latest/trackers/) to learn how to plug
|
||||
tracking into your inference pipeline.
|
||||
|
||||
Attributes:
|
||||
@@ -24,7 +31,28 @@ class LineZone:
|
||||
to inside.
|
||||
out_count (int): The number of objects that have crossed the line from inside
|
||||
to outside.
|
||||
"""
|
||||
|
||||
Example:
|
||||
```python
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO(<SOURCE_MODEL_PATH>)
|
||||
tracker = sv.ByteTrack()
|
||||
frames_generator = sv.get_video_frames_generator(<SOURCE_VIDEO_PATH>)
|
||||
start, end = sv.Point(x=0, y=1080), sv.Point(x=3840, y=1080)
|
||||
line_zone = sv.LineZone(start=start, end=end)
|
||||
|
||||
for frame in frames_generator:
|
||||
result = model(frame)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
detections = tracker.update_with_detections(detections)
|
||||
crossed_in, crossed_out = line_zone.trigger(detections)
|
||||
|
||||
line_zone.in_count, line_zone.out_count
|
||||
# 7, 2
|
||||
```
|
||||
""" # noqa: E501 // docs
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -136,7 +164,7 @@ class LineZone:
|
||||
continue
|
||||
|
||||
triggers = [
|
||||
self.vector.cross_product(point=anchor) > 0 for anchor in box_anchors
|
||||
self.vector.cross_product(point=anchor) < 0 for anchor in box_anchors
|
||||
]
|
||||
|
||||
if len(set(triggers)) == 2:
|
||||
@@ -174,6 +202,8 @@ class LineZoneAnnotator:
|
||||
text_padding: int = 10,
|
||||
custom_in_text: Optional[str] = None,
|
||||
custom_out_text: Optional[str] = None,
|
||||
display_in_count: bool = True,
|
||||
display_out_count: bool = True,
|
||||
):
|
||||
"""
|
||||
Initialize the LineCounterAnnotator object with default values.
|
||||
@@ -186,6 +216,8 @@ class LineZoneAnnotator:
|
||||
text_scale (float): The scale of the text that will be drawn.
|
||||
text_offset (float): The offset of the text that will be drawn.
|
||||
text_padding (int): The padding of the text that will be drawn.
|
||||
display_in_count (bool): Whether to display the in count or not.
|
||||
display_out_count (bool): Whether to display the out count or not.
|
||||
|
||||
"""
|
||||
self.thickness: float = thickness
|
||||
@@ -197,6 +229,43 @@ class LineZoneAnnotator:
|
||||
self.text_padding: int = text_padding
|
||||
self.custom_in_text: str = custom_in_text
|
||||
self.custom_out_text: str = custom_out_text
|
||||
self.display_in_count: bool = display_in_count
|
||||
self.display_out_count: bool = display_out_count
|
||||
|
||||
def _annotate_count(
|
||||
self,
|
||||
frame: np.ndarray,
|
||||
center_text_anchor: Point,
|
||||
text: str,
|
||||
is_in_count: bool,
|
||||
) -> None:
|
||||
"""This method is drawing the text on the frame.
|
||||
|
||||
Args:
|
||||
frame (np.ndarray): The image on which the text will be drawn.
|
||||
center_text_anchor: The center point that the text will be drawn.
|
||||
text (str): The text that will be drawn.
|
||||
is_in_count (bool): Whether to display the in count or out count.
|
||||
"""
|
||||
_, text_height = cv2.getTextSize(
|
||||
text, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness
|
||||
)[0]
|
||||
|
||||
if is_in_count:
|
||||
center_text_anchor.y -= int(self.text_offset * text_height)
|
||||
else:
|
||||
center_text_anchor.y += int(self.text_offset * text_height)
|
||||
|
||||
draw_text(
|
||||
scene=frame,
|
||||
text=text,
|
||||
text_anchor=center_text_anchor,
|
||||
text_color=self.text_color,
|
||||
text_scale=self.text_scale,
|
||||
text_thickness=self.text_thickness,
|
||||
text_padding=self.text_padding,
|
||||
background_color=self.color,
|
||||
)
|
||||
|
||||
def annotate(self, frame: np.ndarray, line_counter: LineZone) -> np.ndarray:
|
||||
"""
|
||||
@@ -237,90 +306,33 @@ class LineZoneAnnotator:
|
||||
lineType=cv2.LINE_AA,
|
||||
)
|
||||
|
||||
in_text = (
|
||||
f"{self.custom_in_text}: {line_counter.in_count}"
|
||||
if self.custom_in_text is not None
|
||||
else f"in: {line_counter.in_count}"
|
||||
)
|
||||
out_text = (
|
||||
f"{self.custom_out_text}: {line_counter.out_count}"
|
||||
if self.custom_out_text is not None
|
||||
else f"out: {line_counter.out_count}"
|
||||
text_anchor = Vector(
|
||||
start=line_counter.vector.start, end=line_counter.vector.end
|
||||
)
|
||||
|
||||
(in_text_width, in_text_height), _ = cv2.getTextSize(
|
||||
in_text, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness
|
||||
)
|
||||
(out_text_width, out_text_height), _ = cv2.getTextSize(
|
||||
out_text, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness
|
||||
)
|
||||
if self.display_in_count:
|
||||
in_text = (
|
||||
f"{self.custom_in_text}: {line_counter.in_count}"
|
||||
if self.custom_in_text is not None
|
||||
else f"in: {line_counter.in_count}"
|
||||
)
|
||||
self._annotate_count(
|
||||
frame=frame,
|
||||
center_text_anchor=text_anchor.center,
|
||||
text=in_text,
|
||||
is_in_count=True,
|
||||
)
|
||||
|
||||
in_text_x = int(
|
||||
(line_counter.vector.end.x + line_counter.vector.start.x - in_text_width)
|
||||
/ 2
|
||||
)
|
||||
in_text_y = int(
|
||||
(line_counter.vector.end.y + line_counter.vector.start.y + in_text_height)
|
||||
/ 2
|
||||
- self.text_offset * in_text_height
|
||||
)
|
||||
|
||||
out_text_x = int(
|
||||
(line_counter.vector.end.x + line_counter.vector.start.x - out_text_width)
|
||||
/ 2
|
||||
)
|
||||
out_text_y = int(
|
||||
(line_counter.vector.end.y + line_counter.vector.start.y + out_text_height)
|
||||
/ 2
|
||||
+ self.text_offset * out_text_height
|
||||
)
|
||||
|
||||
in_text_background_rect = Rect(
|
||||
x=in_text_x,
|
||||
y=in_text_y - in_text_height,
|
||||
width=in_text_width,
|
||||
height=in_text_height,
|
||||
).pad(padding=self.text_padding)
|
||||
out_text_background_rect = Rect(
|
||||
x=out_text_x,
|
||||
y=out_text_y - out_text_height,
|
||||
width=out_text_width,
|
||||
height=out_text_height,
|
||||
).pad(padding=self.text_padding)
|
||||
|
||||
cv2.rectangle(
|
||||
frame,
|
||||
in_text_background_rect.top_left.as_xy_int_tuple(),
|
||||
in_text_background_rect.bottom_right.as_xy_int_tuple(),
|
||||
self.color.as_bgr(),
|
||||
-1,
|
||||
)
|
||||
cv2.rectangle(
|
||||
frame,
|
||||
out_text_background_rect.top_left.as_xy_int_tuple(),
|
||||
out_text_background_rect.bottom_right.as_xy_int_tuple(),
|
||||
self.color.as_bgr(),
|
||||
-1,
|
||||
)
|
||||
|
||||
cv2.putText(
|
||||
frame,
|
||||
in_text,
|
||||
(in_text_x, in_text_y),
|
||||
cv2.FONT_HERSHEY_SIMPLEX,
|
||||
self.text_scale,
|
||||
self.text_color.as_bgr(),
|
||||
self.text_thickness,
|
||||
cv2.LINE_AA,
|
||||
)
|
||||
cv2.putText(
|
||||
frame,
|
||||
out_text,
|
||||
(out_text_x, out_text_y),
|
||||
cv2.FONT_HERSHEY_SIMPLEX,
|
||||
self.text_scale,
|
||||
self.text_color.as_bgr(),
|
||||
self.text_thickness,
|
||||
cv2.LINE_AA,
|
||||
)
|
||||
if self.display_out_count:
|
||||
out_text = (
|
||||
f"{self.custom_out_text}: {line_counter.out_count}"
|
||||
if self.custom_out_text is not None
|
||||
else f"out: {line_counter.out_count}"
|
||||
)
|
||||
self._annotate_count(
|
||||
frame=frame,
|
||||
center_text_anchor=text_anchor.center,
|
||||
text=out_text,
|
||||
is_in_count=False,
|
||||
)
|
||||
return frame
|
||||
@@ -0,0 +1,181 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import os
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from supervision.detection.core import Detections
|
||||
|
||||
BASE_HEADER = [
|
||||
"x_min",
|
||||
"y_min",
|
||||
"x_max",
|
||||
"y_max",
|
||||
"class_id",
|
||||
"confidence",
|
||||
"tracker_id",
|
||||
]
|
||||
|
||||
|
||||
class CSVSink:
|
||||
"""
|
||||
A utility class for saving detection data to a CSV file. This class is designed to
|
||||
efficiently serialize detection objects into a CSV format, allowing for the
|
||||
inclusion of bounding box coordinates and additional attributes like `confidence`,
|
||||
`class_id`, and `tracker_id`.
|
||||
|
||||
!!! tip
|
||||
|
||||
CSVSink allow to pass custom data alongside the detection fields, providing
|
||||
flexibility for logging various types of information.
|
||||
|
||||
Args:
|
||||
file_name (str): The name of the CSV file where the detections will be stored.
|
||||
Defaults to 'output.csv'.
|
||||
|
||||
Example:
|
||||
```python
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO(<SOURCE_MODEL_PATH>)
|
||||
csv_sink = sv.CSVSink(<RESULT_CSV_FILE_PATH>)
|
||||
frames_generator = sv.get_video_frames_generator(<SOURCE_VIDEO_PATH>)
|
||||
|
||||
with csv_sink:
|
||||
for frame in frames_generator:
|
||||
result = model(frame)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
sink.append(detections, custom_data={'<CUSTOM_LABEL>':'<CUSTOM_DATA>'})
|
||||
```
|
||||
""" # noqa: E501 // docs
|
||||
|
||||
def __init__(self, file_name: str = "output.csv") -> None:
|
||||
"""
|
||||
Initialize the CSVSink instance.
|
||||
|
||||
Args:
|
||||
file_name (str): The name of the CSV file.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
self.file_name = file_name
|
||||
self.file: Optional[open] = None
|
||||
self.writer: Optional[csv.writer] = None
|
||||
self.header_written = False
|
||||
self.field_names = []
|
||||
|
||||
def __enter__(self) -> CSVSink:
|
||||
self.open()
|
||||
return self
|
||||
|
||||
def __exit__(
|
||||
self,
|
||||
exc_type: Optional[type],
|
||||
exc_val: Optional[Exception],
|
||||
exc_tb: Optional[Any],
|
||||
) -> None:
|
||||
self.close()
|
||||
|
||||
def open(self) -> None:
|
||||
"""
|
||||
Open the CSV file for writing.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
parent_directory = os.path.dirname(self.file_name)
|
||||
if parent_directory and not os.path.exists(parent_directory):
|
||||
os.makedirs(parent_directory)
|
||||
|
||||
self.file = open(self.file_name, "w", newline="")
|
||||
self.writer = csv.writer(self.file)
|
||||
|
||||
def close(self) -> None:
|
||||
"""
|
||||
Close the CSV file.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
if self.file:
|
||||
self.file.close()
|
||||
|
||||
@staticmethod
|
||||
def parse_detection_data(
|
||||
detections: Detections, custom_data: Dict[str, Any] = None
|
||||
) -> List[Dict[str, Any]]:
|
||||
parsed_rows = []
|
||||
for i in range(len(detections.xyxy)):
|
||||
row = {
|
||||
"x_min": detections.xyxy[i][0],
|
||||
"y_min": detections.xyxy[i][1],
|
||||
"x_max": detections.xyxy[i][2],
|
||||
"y_max": detections.xyxy[i][3],
|
||||
"class_id": ""
|
||||
if detections.class_id is None
|
||||
else str(detections.class_id[i]),
|
||||
"confidence": ""
|
||||
if detections.confidence is None
|
||||
else str(detections.confidence[i]),
|
||||
"tracker_id": ""
|
||||
if detections.tracker_id is None
|
||||
else str(detections.tracker_id[i]),
|
||||
}
|
||||
|
||||
if hasattr(detections, "data"):
|
||||
for key, value in detections.data.items():
|
||||
if value.ndim == 0:
|
||||
row[key] = value
|
||||
else:
|
||||
row[key] = value[i]
|
||||
|
||||
if custom_data:
|
||||
row.update(custom_data)
|
||||
parsed_rows.append(row)
|
||||
return parsed_rows
|
||||
|
||||
def append(
|
||||
self, detections: Detections, custom_data: Dict[str, Any] = None
|
||||
) -> None:
|
||||
"""
|
||||
Append detection data to the CSV file.
|
||||
|
||||
Args:
|
||||
detections (Detections): The detection data.
|
||||
custom_data (Dict[str, Any]): Custom data to include.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
if not self.writer:
|
||||
raise Exception(
|
||||
f"Cannot append to CSV: The file '{self.file_name}' is not open."
|
||||
)
|
||||
field_names = CSVSink.parse_field_names(detections, custom_data)
|
||||
if not self.header_written:
|
||||
self.field_names = field_names
|
||||
self.writer.writerow(field_names)
|
||||
self.header_written = True
|
||||
|
||||
if field_names != self.field_names:
|
||||
print(
|
||||
f"Field names do not match the header. "
|
||||
f"Expected: {self.field_names}, given: {field_names}"
|
||||
)
|
||||
|
||||
parsed_rows = CSVSink.parse_detection_data(detections, custom_data)
|
||||
for row in parsed_rows:
|
||||
self.writer.writerow(
|
||||
[row.get(field_name, "") for field_name in self.field_names]
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def parse_field_names(
|
||||
detections: Detections, custom_data: Dict[str, Any]
|
||||
) -> List[str]:
|
||||
dynamic_header = sorted(
|
||||
set(custom_data.keys()) | set(getattr(detections, "data", {}).keys())
|
||||
)
|
||||
return BASE_HEADER + dynamic_header
|
||||
@@ -1,5 +1,5 @@
|
||||
from dataclasses import replace
|
||||
from typing import Optional, Tuple
|
||||
from typing import Iterable, Optional, Tuple
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
@@ -10,6 +10,7 @@ from supervision.draw.color import Color
|
||||
from supervision.draw.utils import draw_polygon, draw_text
|
||||
from supervision.geometry.core import Position
|
||||
from supervision.geometry.utils import get_polygon_center
|
||||
from supervision.utils.internal import deprecated_parameter
|
||||
|
||||
|
||||
class PolygonZone:
|
||||
@@ -20,21 +21,32 @@ class PolygonZone:
|
||||
polygon (np.ndarray): A polygon represented by a numpy array of shape
|
||||
`(N, 2)`, containing the `x`, `y` coordinates of the points.
|
||||
frame_resolution_wh (Tuple[int, int]): The frame resolution (width, height)
|
||||
triggering_position (Position): The position within the bounding
|
||||
box that triggers the zone (default: Position.BOTTOM_CENTER)
|
||||
triggering_anchors (Iterable[sv.Position]): A list of positions specifying
|
||||
which anchors of the detections bounding box to consider when deciding on
|
||||
whether the detection fits within the PolygonZone
|
||||
(default: (sv.Position.BOTTOM_CENTER,)).
|
||||
current_count (int): The current count of detected objects within the zone
|
||||
mask (np.ndarray): The 2D bool mask for the polygon zone
|
||||
"""
|
||||
|
||||
@deprecated_parameter(
|
||||
old_parameter="triggering_position",
|
||||
new_parameter="triggering_anchors",
|
||||
map_function=lambda x: [x],
|
||||
warning_message="`{old_parameter}` in `{function_name}` is deprecated and will "
|
||||
"be remove in `supervision-0.23.0`. Use '{new_parameter}' "
|
||||
"instead.",
|
||||
)
|
||||
def __init__(
|
||||
self,
|
||||
polygon: np.ndarray,
|
||||
frame_resolution_wh: Tuple[int, int],
|
||||
triggering_position: Position = Position.BOTTOM_CENTER,
|
||||
triggering_anchors: Iterable[Position] = (Position.BOTTOM_CENTER,),
|
||||
):
|
||||
self.polygon = polygon.astype(int)
|
||||
self.frame_resolution_wh = frame_resolution_wh
|
||||
self.triggering_position = triggering_position
|
||||
self.triggering_anchors = triggering_anchors
|
||||
|
||||
self.current_count = 0
|
||||
|
||||
width, height = frame_resolution_wh
|
||||
@@ -59,10 +71,20 @@ class PolygonZone:
|
||||
xyxy=detections.xyxy, resolution_wh=self.frame_resolution_wh
|
||||
)
|
||||
clipped_detections = replace(detections, xyxy=clipped_xyxy)
|
||||
clipped_anchors = np.ceil(
|
||||
clipped_detections.get_anchors_coordinates(anchor=self.triggering_position)
|
||||
).astype(int)
|
||||
is_in_zone = self.mask[clipped_anchors[:, 1], clipped_anchors[:, 0]]
|
||||
all_clipped_anchors = np.array(
|
||||
[
|
||||
np.ceil(clipped_detections.get_anchors_coordinates(anchor)).astype(int)
|
||||
for anchor in self.triggering_anchors
|
||||
]
|
||||
)
|
||||
|
||||
is_in_zone = (
|
||||
self.mask[all_clipped_anchors[:, :, 1], all_clipped_anchors[:, :, 0]]
|
||||
.transpose()
|
||||
.astype(bool)
|
||||
)
|
||||
is_in_zone = np.all(is_in_zone, axis=1)
|
||||
|
||||
self.current_count = int(np.sum(is_in_zone))
|
||||
return is_in_zone.astype(bool)
|
||||
|
||||
@@ -83,6 +105,7 @@ class PolygonZoneAnnotator:
|
||||
font (int): The font type for the text on the polygon,
|
||||
default is cv2.FONT_HERSHEY_SIMPLEX
|
||||
center (Tuple[int, int]): The center of the polygon for text placement
|
||||
display_in_zone_count (bool): Show the label of the zone or not. Default is True
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
@@ -94,6 +117,7 @@ class PolygonZoneAnnotator:
|
||||
text_scale: float = 0.5,
|
||||
text_thickness: int = 1,
|
||||
text_padding: int = 10,
|
||||
display_in_zone_count: bool = True,
|
||||
):
|
||||
self.zone = zone
|
||||
self.color = color
|
||||
@@ -104,6 +128,7 @@ class PolygonZoneAnnotator:
|
||||
self.text_padding = text_padding
|
||||
self.font = cv2.FONT_HERSHEY_SIMPLEX
|
||||
self.center = get_polygon_center(polygon=zone.polygon)
|
||||
self.display_in_zone_count = display_in_zone_count
|
||||
|
||||
def annotate(self, scene: np.ndarray, label: Optional[str] = None) -> np.ndarray:
|
||||
"""
|
||||
@@ -124,16 +149,17 @@ class PolygonZoneAnnotator:
|
||||
thickness=self.thickness,
|
||||
)
|
||||
|
||||
annotated_frame = draw_text(
|
||||
scene=annotated_frame,
|
||||
text=str(self.zone.current_count) if label is None else label,
|
||||
text_anchor=self.center,
|
||||
background_color=self.color,
|
||||
text_color=self.text_color,
|
||||
text_scale=self.text_scale,
|
||||
text_thickness=self.text_thickness,
|
||||
text_padding=self.text_padding,
|
||||
text_font=self.font,
|
||||
)
|
||||
if self.display_in_zone_count:
|
||||
annotated_frame = draw_text(
|
||||
scene=annotated_frame,
|
||||
text=str(self.zone.current_count) if label is None else label,
|
||||
text_anchor=self.center,
|
||||
background_color=self.color,
|
||||
text_color=self.text_color,
|
||||
text_scale=self.text_scale,
|
||||
text_thickness=self.text_thickness,
|
||||
text_padding=self.text_padding,
|
||||
text_font=self.font,
|
||||
)
|
||||
|
||||
return annotated_frame
|
||||
|
||||
@@ -22,7 +22,7 @@ class DetectionsSmoother:
|
||||
!!! warning
|
||||
|
||||
- `DetectionsSmoother` requires the `tracker_id` for each detection. Refer to
|
||||
[Roboflow Trackers](https://supervision.roboflow.com/trackers/) for
|
||||
[Roboflow Trackers](/latest/trackers/) for
|
||||
information on integrating tracking into your inference pipeline.
|
||||
- This class is not compatible with segmentation models.
|
||||
|
||||
@@ -72,7 +72,7 @@ class DetectionsSmoother:
|
||||
if detections.tracker_id is None:
|
||||
print(
|
||||
"Smoothing skipped. DetectionsSmoother requires tracker_id. Refer to "
|
||||
"https://supervision.roboflow.com/trackers for more information."
|
||||
"https://supervision.roboflow.com/latest/trackers for more information."
|
||||
)
|
||||
return detections
|
||||
|
||||
@@ -115,4 +115,8 @@ class DetectionsSmoother:
|
||||
if track is not None:
|
||||
tracked_detections.append(track)
|
||||
|
||||
return Detections.merge(tracked_detections)
|
||||
detections = Detections.merge(tracked_detections)
|
||||
if len(detections) == 0:
|
||||
detections.tracker_id = np.array([], dtype=int)
|
||||
|
||||
return detections
|
||||
|
||||
@@ -59,7 +59,168 @@ def box_iou_batch(boxes_true: np.ndarray, boxes_detection: np.ndarray) -> np.nda
|
||||
return area_inter / (area_true[:, None] + area_detection - area_inter)
|
||||
|
||||
|
||||
def non_max_suppression(
|
||||
def _mask_iou_batch_split(
|
||||
masks_true: np.ndarray, masks_detection: np.ndarray
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
Internal function.
|
||||
Compute Intersection over Union (IoU) of two sets of masks -
|
||||
`masks_true` and `masks_detection`.
|
||||
|
||||
Args:
|
||||
masks_true (np.ndarray): 3D `np.ndarray` representing ground-truth masks.
|
||||
masks_detection (np.ndarray): 3D `np.ndarray` representing detection masks.
|
||||
|
||||
Returns:
|
||||
np.ndarray: Pairwise IoU of masks from `masks_true` and `masks_detection`.
|
||||
"""
|
||||
intersection_area = np.logical_and(masks_true[:, None], masks_detection).sum(
|
||||
axis=(2, 3)
|
||||
)
|
||||
|
||||
masks_true_area = masks_true.sum(axis=(1, 2))
|
||||
masks_detection_area = masks_detection.sum(axis=(1, 2))
|
||||
union_area = masks_true_area[:, None] + masks_detection_area - intersection_area
|
||||
|
||||
return np.divide(
|
||||
intersection_area,
|
||||
union_area,
|
||||
out=np.zeros_like(intersection_area, dtype=float),
|
||||
where=union_area != 0,
|
||||
)
|
||||
|
||||
|
||||
def mask_iou_batch(
|
||||
masks_true: np.ndarray,
|
||||
masks_detection: np.ndarray,
|
||||
memory_limit: int = 1024 * 5,
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
Compute Intersection over Union (IoU) of two sets of masks -
|
||||
`masks_true` and `masks_detection`.
|
||||
|
||||
Args:
|
||||
masks_true (np.ndarray): 3D `np.ndarray` representing ground-truth masks.
|
||||
masks_detection (np.ndarray): 3D `np.ndarray` representing detection masks.
|
||||
memory_limit (int, optional): memory limit in MB, default is 1024 * 5 MB (5GB).
|
||||
|
||||
Returns:
|
||||
np.ndarray: Pairwise IoU of masks from `masks_true` and `masks_detection`.
|
||||
"""
|
||||
memory = (
|
||||
masks_true.shape[0]
|
||||
* masks_true.shape[1]
|
||||
* masks_true.shape[2]
|
||||
* masks_detection.shape[0]
|
||||
/ 1024
|
||||
/ 1024
|
||||
)
|
||||
if memory <= memory_limit:
|
||||
return _mask_iou_batch_split(masks_true, masks_detection)
|
||||
|
||||
ious = []
|
||||
step = max(
|
||||
memory_limit
|
||||
* 1024
|
||||
* 1024
|
||||
// (
|
||||
masks_detection.shape[0]
|
||||
* masks_detection.shape[1]
|
||||
* masks_detection.shape[2]
|
||||
),
|
||||
1,
|
||||
)
|
||||
for i in range(0, masks_true.shape[0], step):
|
||||
ious.append(_mask_iou_batch_split(masks_true[i : i + step], masks_detection))
|
||||
|
||||
return np.vstack(ious)
|
||||
|
||||
|
||||
def resize_masks(masks: np.ndarray, max_dimension: int = 640) -> np.ndarray:
|
||||
"""
|
||||
Resize all masks in the array to have a maximum dimension of max_dimension,
|
||||
maintaining aspect ratio.
|
||||
|
||||
Args:
|
||||
masks (np.ndarray): 3D array of binary masks with shape (N, H, W).
|
||||
max_dimension (int): The maximum dimension for the resized masks.
|
||||
|
||||
Returns:
|
||||
np.ndarray: Array of resized masks.
|
||||
"""
|
||||
max_height = np.max(masks.shape[1])
|
||||
max_width = np.max(masks.shape[2])
|
||||
scale = min(max_dimension / max_height, max_dimension / max_width)
|
||||
|
||||
new_height = int(scale * max_height)
|
||||
new_width = int(scale * max_width)
|
||||
|
||||
x = np.linspace(0, max_width - 1, new_width).astype(int)
|
||||
y = np.linspace(0, max_height - 1, new_height).astype(int)
|
||||
xv, yv = np.meshgrid(x, y)
|
||||
|
||||
resized_masks = masks[:, yv, xv]
|
||||
|
||||
resized_masks = resized_masks.reshape(masks.shape[0], new_height, new_width)
|
||||
return resized_masks
|
||||
|
||||
|
||||
def mask_non_max_suppression(
|
||||
predictions: np.ndarray,
|
||||
masks: np.ndarray,
|
||||
iou_threshold: float = 0.5,
|
||||
mask_dimension: int = 640,
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
Perform Non-Maximum Suppression (NMS) on segmentation predictions.
|
||||
|
||||
Args:
|
||||
predictions (np.ndarray): A 2D array of object detection predictions in
|
||||
the format of `(x_min, y_min, x_max, y_max, score)`
|
||||
or `(x_min, y_min, x_max, y_max, score, class)`. Shape: `(N, 5)` or
|
||||
`(N, 6)`, where N is the number of predictions.
|
||||
masks (np.ndarray): A 3D array of binary masks corresponding to the predictions.
|
||||
Shape: `(N, H, W)`, where N is the number of predictions, and H, W are the
|
||||
dimensions of each mask.
|
||||
iou_threshold (float, optional): The intersection-over-union threshold
|
||||
to use for non-maximum suppression.
|
||||
mask_dimension (int, optional): The dimension to which the masks should be
|
||||
resized before computing IOU values. Defaults to 640.
|
||||
|
||||
Returns:
|
||||
np.ndarray: A boolean array indicating which predictions to keep after
|
||||
non-maximum suppression.
|
||||
|
||||
Raises:
|
||||
AssertionError: If `iou_threshold` is not within the closed
|
||||
range from `0` to `1`.
|
||||
"""
|
||||
assert 0 <= iou_threshold <= 1, (
|
||||
"Value of `iou_threshold` must be in the closed range from 0 to 1, "
|
||||
f"{iou_threshold} given."
|
||||
)
|
||||
rows, columns = predictions.shape
|
||||
|
||||
if columns == 5:
|
||||
predictions = np.c_[predictions, np.zeros(rows)]
|
||||
|
||||
sort_index = predictions[:, 4].argsort()[::-1]
|
||||
predictions = predictions[sort_index]
|
||||
masks = masks[sort_index]
|
||||
masks_resized = resize_masks(masks, mask_dimension)
|
||||
ious = mask_iou_batch(masks_resized, masks_resized)
|
||||
categories = predictions[:, 5]
|
||||
|
||||
keep = np.ones(rows, dtype=bool)
|
||||
for i in range(rows):
|
||||
if keep[i]:
|
||||
condition = (ious[i] > iou_threshold) & (categories[i] == categories)
|
||||
keep[i + 1 :] = np.where(condition[i + 1 :], False, keep[i + 1 :])
|
||||
|
||||
return keep[sort_index.argsort()]
|
||||
|
||||
|
||||
def box_non_max_suppression(
|
||||
predictions: np.ndarray, iou_threshold: float = 0.5
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
|
||||
@@ -206,7 +206,7 @@ class Color:
|
||||
@classmethod
|
||||
@deprecated(
|
||||
"`Color.white()` is deprecated and will be removed in "
|
||||
"`supervision-0.20.0`. Use `Color.WHITE` instead."
|
||||
"`supervision-0.22.0`. Use `Color.WHITE` instead."
|
||||
)
|
||||
def white(cls) -> Color:
|
||||
return Color.from_hex(color_hex="#ffffff")
|
||||
@@ -214,7 +214,7 @@ class Color:
|
||||
@classmethod
|
||||
@deprecated(
|
||||
"`Color.black()` is deprecated and will be removed in "
|
||||
"`supervision-0.20.0`. Use `Color.BLACK` instead."
|
||||
"`supervision-0.22.0`. Use `Color.BLACK` instead."
|
||||
)
|
||||
def black(cls) -> Color:
|
||||
return Color.from_hex(color_hex="#000000")
|
||||
@@ -222,7 +222,7 @@ class Color:
|
||||
@classmethod
|
||||
@deprecated(
|
||||
"`Color.red()` is deprecated and will be removed in "
|
||||
"`supervision-0.20.0`. Use `Color.RED` instead."
|
||||
"`supervision-0.22.0`. Use `Color.RED` instead."
|
||||
)
|
||||
def red(cls) -> Color:
|
||||
return Color.from_hex(color_hex="#ff0000")
|
||||
@@ -230,7 +230,7 @@ class Color:
|
||||
@classmethod
|
||||
@deprecated(
|
||||
"`Color.green()` is deprecated and will be removed in "
|
||||
"`supervision-0.20.0`. Use `Color.GREEN` instead."
|
||||
"`supervision-0.22.0`. Use `Color.GREEN` instead."
|
||||
)
|
||||
def green(cls) -> Color:
|
||||
return Color.from_hex(color_hex="#00ff00")
|
||||
@@ -238,7 +238,7 @@ class Color:
|
||||
@classmethod
|
||||
@deprecated(
|
||||
"`Color.blue()` is deprecated and will be removed in "
|
||||
"`supervision-0.20.0`. Use `Color.BLUE` instead."
|
||||
"`supervision-0.22.0`. Use `Color.BLUE` instead."
|
||||
)
|
||||
def blue(cls) -> Color:
|
||||
return Color.from_hex(color_hex="#0000ff")
|
||||
@@ -297,10 +297,15 @@ class ColorPalette:
|
||||
@classmethod
|
||||
@deprecated(
|
||||
"`ColorPalette.default()` is deprecated and will be removed in "
|
||||
"`supervision-0.20.0`. Use `Color.DEFAULT` instead."
|
||||
"`supervision-0.22.0`. Use `Color.DEFAULT` instead."
|
||||
)
|
||||
def default(cls) -> ColorPalette:
|
||||
"""
|
||||
!!! failure "Deprecated"
|
||||
|
||||
`ColorPalette.default()` is deprecated and will be removed in
|
||||
`supervision-0.22.0`. Use `Color.DEFAULT` instead.
|
||||
|
||||
Returns a default color palette.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -148,9 +148,12 @@ def draw_text(
|
||||
fontScale=text_scale,
|
||||
thickness=text_thickness,
|
||||
)[0]
|
||||
|
||||
text_anchor_x, text_anchor_y = text_anchor.as_xy_int_tuple()
|
||||
|
||||
text_rect = Rect(
|
||||
x=text_anchor.x - text_width // 2,
|
||||
y=text_anchor.y - text_height // 2,
|
||||
x=text_anchor_x - text_width // 2,
|
||||
y=text_anchor_y - text_height // 2,
|
||||
width=text_width,
|
||||
height=text_height,
|
||||
).pad(text_padding)
|
||||
@@ -163,7 +166,7 @@ def draw_text(
|
||||
cv2.putText(
|
||||
img=scene,
|
||||
text=text,
|
||||
org=(text_anchor.x - text_width // 2, text_anchor.y + text_height // 2),
|
||||
org=(text_anchor_x - text_width // 2, text_anchor_y + text_height // 2),
|
||||
fontFace=text_font,
|
||||
fontScale=text_scale,
|
||||
color=text_color.as_bgr(),
|
||||
|
||||
@@ -56,6 +56,19 @@ class Vector:
|
||||
dy = self.end.y - self.start.y
|
||||
return sqrt(dx**2 + dy**2)
|
||||
|
||||
@property
|
||||
def center(self) -> Point:
|
||||
"""
|
||||
Calculate the center point of the vector.
|
||||
|
||||
Returns:
|
||||
Point: The center point of the vector.
|
||||
"""
|
||||
return Point(
|
||||
x=(self.start.x + self.end.x) / 2,
|
||||
y=(self.start.y + self.end.y) / 2,
|
||||
)
|
||||
|
||||
def cross_product(self, point: Point) -> float:
|
||||
"""
|
||||
Calculate the 2D cross product (also known as the vector product or outer
|
||||
|
||||
@@ -39,6 +39,14 @@ class BaseTrack:
|
||||
BaseTrack._count += 1
|
||||
return BaseTrack._count
|
||||
|
||||
@staticmethod
|
||||
def reset_counter():
|
||||
BaseTrack._count = 0
|
||||
BaseTrack.track_id = 0
|
||||
BaseTrack.start_frame = 0
|
||||
BaseTrack.frame_id = 0
|
||||
BaseTrack.time_since_update = 0
|
||||
|
||||
def activate(self, *args):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ from supervision.detection.core import Detections
|
||||
from supervision.tracker.byte_tracker import matching
|
||||
from supervision.tracker.byte_tracker.basetrack import BaseTrack, TrackState
|
||||
from supervision.tracker.byte_tracker.kalman_filter import KalmanFilter
|
||||
from supervision.utils.internal import deprecated_parameter
|
||||
|
||||
|
||||
class STrack(BaseTrack):
|
||||
@@ -172,26 +173,57 @@ class ByteTrack:
|
||||
</video>
|
||||
|
||||
Parameters:
|
||||
track_thresh (float, optional): Detection confidence threshold
|
||||
for track activation.
|
||||
track_buffer (int, optional): Number of frames to buffer when a track is lost.
|
||||
match_thresh (float, optional): Threshold for matching tracks with detections.
|
||||
track_activation_threshold (float, optional): Detection confidence threshold
|
||||
for track activation. Increasing track_activation_threshold improves accuracy
|
||||
and stability but might miss true detections. Decreasing it increases
|
||||
completeness but risks introducing noise and instability.
|
||||
lost_track_buffer (int, optional): Number of frames to buffer when a track is lost.
|
||||
Increasing lost_track_buffer enhances occlusion handling, significantly
|
||||
reducing the likelihood of track fragmentation or disappearance caused
|
||||
by brief detection gaps.
|
||||
minimum_matching_threshold (float, optional): Threshold for matching tracks with detections.
|
||||
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.
|
||||
""" # noqa: E501 // docs
|
||||
|
||||
@deprecated_parameter(
|
||||
old_parameter="track_buffer",
|
||||
new_parameter="lost_track_buffer",
|
||||
map_function=lambda x: x,
|
||||
warning_message="`{old_parameter}` in `{function_name}` is deprecated and will "
|
||||
"be remove in `supervision-0.23.0`. Use '{new_parameter}' "
|
||||
"instead.",
|
||||
)
|
||||
@deprecated_parameter(
|
||||
old_parameter="track_thresh",
|
||||
new_parameter="track_activation_threshold",
|
||||
map_function=lambda x: x,
|
||||
warning_message="`{old_parameter}` in `{function_name}` is deprecated and will "
|
||||
"be remove in `supervision-0.23.0`. Use '{new_parameter}' "
|
||||
"instead.",
|
||||
)
|
||||
@deprecated_parameter(
|
||||
old_parameter="match_thresh",
|
||||
new_parameter="minimum_matching_threshold",
|
||||
map_function=lambda x: x,
|
||||
warning_message="`{old_parameter}` in `{function_name}` is deprecated and will "
|
||||
"be remove in `supervision-0.23.0`. Use '{new_parameter}' "
|
||||
"instead.",
|
||||
)
|
||||
def __init__(
|
||||
self,
|
||||
track_thresh: float = 0.25,
|
||||
track_buffer: int = 30,
|
||||
match_thresh: float = 0.8,
|
||||
track_activation_threshold: float = 0.25,
|
||||
lost_track_buffer: int = 30,
|
||||
minimum_matching_threshold: float = 0.8,
|
||||
frame_rate: int = 30,
|
||||
):
|
||||
self.track_thresh = track_thresh
|
||||
self.match_thresh = match_thresh
|
||||
self.track_activation_threshold = track_activation_threshold
|
||||
self.minimum_matching_threshold = minimum_matching_threshold
|
||||
|
||||
self.frame_id = 0
|
||||
self.det_thresh = self.track_thresh + 0.1
|
||||
self.max_time_lost = int(frame_rate / 30.0 * track_buffer)
|
||||
self.det_thresh = self.track_activation_threshold + 0.1
|
||||
self.max_time_lost = int(frame_rate / 30.0 * lost_track_buffer)
|
||||
self.kalman_filter = KalmanFilter()
|
||||
|
||||
self.tracked_tracks: List[STrack] = []
|
||||
@@ -220,7 +252,7 @@ class ByteTrack:
|
||||
def callback(frame: np.ndarray, index: int) -> np.ndarray:
|
||||
results = model(frame)[0]
|
||||
detections = sv.Detections.from_ultralytics(results)
|
||||
detections = byte_tracker.update_with_detections(detections)
|
||||
detections = tracker.update_with_detections(detections)
|
||||
|
||||
labels = [f"#{tracker_id}" for tracker_id in detections.tracker_id]
|
||||
|
||||
@@ -260,6 +292,21 @@ class ByteTrack:
|
||||
|
||||
return detections
|
||||
|
||||
def reset(self):
|
||||
"""
|
||||
Resets the internal state of the ByteTrack tracker.
|
||||
|
||||
This method clears the tracking data, including tracked, lost,
|
||||
and removed tracks, as well as resetting the frame counter. It's
|
||||
particularly useful when processing multiple videos sequentially,
|
||||
ensuring the tracker starts with a clean state for each new video.
|
||||
"""
|
||||
self.frame_id = 0
|
||||
self.tracked_tracks: List[STrack] = []
|
||||
self.lost_tracks: List[STrack] = []
|
||||
self.removed_tracks: List[STrack] = []
|
||||
BaseTrack.reset_counter()
|
||||
|
||||
def update_with_tensors(self, tensors: np.ndarray) -> List[STrack]:
|
||||
"""
|
||||
Updates the tracker with the provided tensors and returns the updated tracks.
|
||||
@@ -280,9 +327,9 @@ class ByteTrack:
|
||||
scores = tensors[:, 4]
|
||||
bboxes = tensors[:, :4]
|
||||
|
||||
remain_inds = scores > self.track_thresh
|
||||
remain_inds = scores > self.track_activation_threshold
|
||||
inds_low = scores > 0.1
|
||||
inds_high = scores < self.track_thresh
|
||||
inds_high = scores < self.track_activation_threshold
|
||||
|
||||
inds_second = np.logical_and(inds_low, inds_high)
|
||||
dets_second = bboxes[inds_second]
|
||||
@@ -305,6 +352,7 @@ class ByteTrack:
|
||||
""" Add newly detected tracklets to tracked_stracks"""
|
||||
unconfirmed = []
|
||||
tracked_stracks = [] # type: list[STrack]
|
||||
|
||||
for track in self.tracked_tracks:
|
||||
if not track.is_activated:
|
||||
unconfirmed.append(track)
|
||||
@@ -319,7 +367,7 @@ class ByteTrack:
|
||||
|
||||
dists = matching.fuse_score(dists, detections)
|
||||
matches, u_track, u_detection = matching.linear_assignment(
|
||||
dists, thresh=self.match_thresh
|
||||
dists, thresh=self.minimum_matching_threshold
|
||||
)
|
||||
|
||||
for itracked, idet in matches:
|
||||
|
||||
@@ -26,7 +26,7 @@ def crop_image(image: np.ndarray, xyxy: np.ndarray) -> np.ndarray:
|
||||
with sv.ImageSink(target_dir_path='target/directory/path') as sink:
|
||||
for xyxy in detection.xyxy:
|
||||
cropped_image = sv.crop_image(image=image, xyxy=xyxy)
|
||||
sink.save_image(image=image)
|
||||
sink.save_image(image=cropped_image)
|
||||
```
|
||||
"""
|
||||
|
||||
|
||||
@@ -1,5 +1,104 @@
|
||||
import functools
|
||||
import os
|
||||
import warnings
|
||||
from typing import Callable
|
||||
|
||||
|
||||
class SupervisionWarnings(Warning):
|
||||
"""Supervision warning category.
|
||||
Set the deprecation warnings visibility for Supervision library.
|
||||
You can set the environment variable SUPERVISON_DEPRECATION_WARNING to '0' to
|
||||
disable the deprecation warnings.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def format_warning(msg, category, filename, lineno, line=None):
|
||||
"""
|
||||
Format a warning the same way as the default formatter, but also include the
|
||||
category name in the output.
|
||||
"""
|
||||
return f"{category.__name__}: {msg}\n"
|
||||
|
||||
|
||||
warnings.formatwarning = format_warning
|
||||
|
||||
if os.getenv("SUPERVISON_DEPRECATION_WARNING") == "0":
|
||||
warnings.simplefilter("ignore", SupervisionWarnings)
|
||||
else:
|
||||
warnings.simplefilter("always", SupervisionWarnings)
|
||||
|
||||
|
||||
def deprecated_parameter(
|
||||
old_parameter: str,
|
||||
new_parameter: str,
|
||||
map_function: Callable = lambda x: x,
|
||||
warning_message: str = "Warning: '{old_parameter}' in '{function_name}' is "
|
||||
"deprecated: use '{new_parameter}' instead.",
|
||||
**message_kwargs,
|
||||
):
|
||||
"""
|
||||
A decorator to mark a function's parameter as deprecated and issue a warning when
|
||||
used.
|
||||
|
||||
Parameters:
|
||||
old_parameter (str): The name of the deprecated parameter.
|
||||
new_parameter (str): The name of the parameter that should be used instead.
|
||||
map_function (Callable, optional): A function used to map the value of the old
|
||||
parameter to the new parameter. Defaults to the identity function.
|
||||
warning_message (str, optional): The warning message to be displayed when the
|
||||
deprecated parameter is used. Defaults to a generic warning message with
|
||||
placeholders for the old parameter, new parameter, and function name.
|
||||
**message_kwargs: Additional keyword arguments that can be used to customize
|
||||
the warning message.
|
||||
|
||||
Returns:
|
||||
Callable: A decorator function that can be applied to mark a function's
|
||||
parameter as deprecated.
|
||||
|
||||
Examples:
|
||||
```python
|
||||
@deprecated_parameter(
|
||||
old_parameter=<OLD_PARAMETER_NAME>,
|
||||
new_parameter=<NEW_PARAMETER_NAME>
|
||||
)
|
||||
def example_function(<NEW_PARAMETER_NAME>):
|
||||
pass
|
||||
|
||||
# call function using deprecated parameter
|
||||
example_function(<OLD_PARAMETER_NAME>=<OLD_PARAMETER_VALUE>)
|
||||
```
|
||||
"""
|
||||
|
||||
def decorator(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if old_parameter in kwargs:
|
||||
if args and hasattr(args[0], "__class__"):
|
||||
class_name = args[0].__class__.__name__
|
||||
function_name = f"{class_name}.{func.__name__}"
|
||||
else:
|
||||
function_name = func.__name__
|
||||
|
||||
warnings.warn(
|
||||
message=warning_message.format(
|
||||
function_name=function_name,
|
||||
old_parameter=old_parameter,
|
||||
new_parameter=new_parameter,
|
||||
**message_kwargs,
|
||||
),
|
||||
category=SupervisionWarnings,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
kwargs[new_parameter] = map_function(kwargs.pop(old_parameter))
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def deprecated(reason: str):
|
||||
@@ -8,7 +107,7 @@ def deprecated(reason: str):
|
||||
def wrapper(*args, **kwargs):
|
||||
warnings.warn(
|
||||
f"{func.__name__} is deprecated: {reason}",
|
||||
category=DeprecationWarning,
|
||||
category=SupervisionWarnings,
|
||||
stacklevel=2,
|
||||
)
|
||||
return func(*args, **kwargs)
|
||||
|
||||
+32
-12
@@ -8,6 +8,8 @@ from typing import Callable, Generator, Optional, Tuple
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from supervision.utils.internal import deprecated
|
||||
|
||||
|
||||
@dataclass
|
||||
class VideoInfo:
|
||||
@@ -26,7 +28,7 @@ class VideoInfo:
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
video_info = sv.VideoInfo.from_video_path(video_path='video.mp4')
|
||||
video_info = sv.VideoInfo.from_video_path(video_path=<SOURCE_VIDEO_FILE>)
|
||||
|
||||
video_info
|
||||
# VideoInfo(width=3840, height=2160, fps=25, total_frames=538)
|
||||
@@ -73,14 +75,14 @@ class VideoSink:
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
video_info = sv.VideoInfo.from_video_path('source.mp4')
|
||||
frames_generator = sv.get_video_frames_generator('source.mp4')
|
||||
video_info = sv.VideoInfo.from_video_path(<SOURCE_VIDEO_PATH>)
|
||||
frames_generator = sv.get_video_frames_generator(<SOURCE_VIDEO_PATH>)
|
||||
|
||||
with sv.VideoSink(target_path='target.mp4', video_info=video_info) as sink:
|
||||
with sv.VideoSink(target_path=<TARGET_VIDEO_PATH>, video_info=video_info) as sink:
|
||||
for frame in frames_generator:
|
||||
sink.write_frame(frame=frame)
|
||||
```
|
||||
"""
|
||||
""" # noqa: E501 // docs
|
||||
|
||||
def __init__(self, target_path: str, video_info: VideoInfo, codec: str = "mp4v"):
|
||||
self.target_path = target_path
|
||||
@@ -145,7 +147,7 @@ def get_video_frames_generator(
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
for frame in sv.get_video_frames_generator(source_path='source_video.mp4'):
|
||||
for frame in sv.get_video_frames_generator(source_path=<SOURCE_VIDEO_PATH>):
|
||||
...
|
||||
```
|
||||
"""
|
||||
@@ -189,8 +191,8 @@ def process_video(
|
||||
...
|
||||
|
||||
process_video(
|
||||
source_path='...',
|
||||
target_path='...',
|
||||
source_path=<SOURCE_VIDEO_PATH>,
|
||||
target_path=<TARGET_VIDEO_PATH>,
|
||||
callback=callback
|
||||
)
|
||||
```
|
||||
@@ -219,25 +221,43 @@ class FPSMonitor:
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
frames_generator = sv.get_video_frames_generator('source.mp4')
|
||||
frames_generator = sv.get_video_frames_generator(source_path=<SOURCE_FILE_PATH>)
|
||||
fps_monitor = sv.FPSMonitor()
|
||||
|
||||
for frame in frames_generator:
|
||||
# your processing code here
|
||||
fps_monitor.tick()
|
||||
fps = fps_monitor()
|
||||
fps = fps_monitor.fps
|
||||
```
|
||||
"""
|
||||
""" # noqa: E501 // docs
|
||||
self.all_timestamps = deque(maxlen=sample_size)
|
||||
|
||||
@deprecated(
|
||||
"`FPSMonitor.__call__` is deprecated and will be removed in "
|
||||
"`supervision-0.22.0`. Use `FPSMonitor.fps` instead."
|
||||
)
|
||||
def __call__(self) -> float:
|
||||
"""
|
||||
!!! failure "Deprecated"
|
||||
|
||||
`FPSMonitor.__call__` is deprecated and will be removed in
|
||||
`supervision-0.22.0`. Use `FPSMonitor.fps` instead.
|
||||
|
||||
Computes and returns the average FPS based on the stored time stamps.
|
||||
|
||||
Returns:
|
||||
float: The average FPS. Returns 0.0 if no time stamps are stored.
|
||||
"""
|
||||
return self.fps
|
||||
|
||||
@property
|
||||
def fps(self) -> float:
|
||||
"""
|
||||
Computes and returns the average FPS based on the stored time stamps.
|
||||
|
||||
Returns:
|
||||
float: The average FPS. Returns 0.0 if no time stamps are stored.
|
||||
"""
|
||||
|
||||
if not self.all_timestamps:
|
||||
return 0.0
|
||||
taken_time = self.all_timestamps[-1] - self.all_timestamps[0]
|
||||
|
||||
@@ -0,0 +1,415 @@
|
||||
import csv
|
||||
import os
|
||||
from test.test_utils import mock_detections
|
||||
from typing import Any, Dict, List
|
||||
|
||||
import pytest
|
||||
|
||||
import supervision as sv
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"detections, custom_data, "
|
||||
"second_detections, second_custom_data, "
|
||||
"file_name, expected_result",
|
||||
[
|
||||
(
|
||||
mock_detections(
|
||||
xyxy=[[10, 20, 30, 40], [50, 60, 70, 80]],
|
||||
confidence=[0.7, 0.8],
|
||||
class_id=[0, 0],
|
||||
tracker_id=[0, 1],
|
||||
data={"class_name": ["person", "person"]},
|
||||
),
|
||||
{"frame_number": 42},
|
||||
mock_detections(
|
||||
xyxy=[[15, 25, 35, 45], [55, 65, 75, 85]],
|
||||
confidence=[0.6, 0.9],
|
||||
class_id=[1, 1],
|
||||
tracker_id=[2, 3],
|
||||
data={"class_name": ["car", "car"]},
|
||||
),
|
||||
{"frame_number": 43},
|
||||
"test_detections.csv",
|
||||
[
|
||||
[
|
||||
"x_min",
|
||||
"y_min",
|
||||
"x_max",
|
||||
"y_max",
|
||||
"class_id",
|
||||
"confidence",
|
||||
"tracker_id",
|
||||
"class_name",
|
||||
"frame_number",
|
||||
],
|
||||
["10.0", "20.0", "30.0", "40.0", "0", "0.7", "0", "person", "42"],
|
||||
["50.0", "60.0", "70.0", "80.0", "0", "0.8", "1", "person", "42"],
|
||||
["15.0", "25.0", "35.0", "45.0", "1", "0.6", "2", "car", "43"],
|
||||
["55.0", "65.0", "75.0", "85.0", "1", "0.9", "3", "car", "43"],
|
||||
],
|
||||
), # multiple detections
|
||||
(
|
||||
mock_detections(
|
||||
xyxy=[[60, 70, 80, 90], [100, 110, 120, 130]],
|
||||
tracker_id=[4, 5],
|
||||
data={"class_name": ["bike", "dog"]},
|
||||
),
|
||||
{"frame_number": 44},
|
||||
mock_detections(
|
||||
xyxy=[[65, 75, 85, 95], [105, 115, 125, 135]],
|
||||
confidence=[0.5, 0.4],
|
||||
data={"class_name": ["tree", "cat"]},
|
||||
),
|
||||
{"frame_number": 45},
|
||||
"test_detections_missing_fields.csv",
|
||||
[
|
||||
[
|
||||
"x_min",
|
||||
"y_min",
|
||||
"x_max",
|
||||
"y_max",
|
||||
"class_id",
|
||||
"confidence",
|
||||
"tracker_id",
|
||||
"class_name",
|
||||
"frame_number",
|
||||
],
|
||||
["60.0", "70.0", "80.0", "90.0", "", "", "4", "bike", "44"],
|
||||
["100.0", "110.0", "120.0", "130.0", "", "", "5", "dog", "44"],
|
||||
["65.0", "75.0", "85.0", "95.0", "", "0.5", "", "tree", "45"],
|
||||
["105.0", "115.0", "125.0", "135.0", "", "0.4", "", "cat", "45"],
|
||||
],
|
||||
), # missing fields
|
||||
(
|
||||
mock_detections(
|
||||
xyxy=[[10, 11, 12, 13]],
|
||||
confidence=[0.95],
|
||||
data={"class_name": "unknown", "is_detected": True, "score": 1},
|
||||
),
|
||||
{"frame_number": 46},
|
||||
mock_detections(
|
||||
xyxy=[[14, 15, 16, 17]],
|
||||
data={"class_name": "artifact", "is_detected": False, "score": 0.85},
|
||||
),
|
||||
{"frame_number": 47},
|
||||
"test_detections_varied_data.csv",
|
||||
[
|
||||
[
|
||||
"x_min",
|
||||
"y_min",
|
||||
"x_max",
|
||||
"y_max",
|
||||
"class_id",
|
||||
"confidence",
|
||||
"tracker_id",
|
||||
"class_name",
|
||||
"frame_number",
|
||||
"is_detected",
|
||||
"score",
|
||||
],
|
||||
[
|
||||
"10.0",
|
||||
"11.0",
|
||||
"12.0",
|
||||
"13.0",
|
||||
"",
|
||||
"0.95",
|
||||
"",
|
||||
"unknown",
|
||||
"46",
|
||||
"True",
|
||||
"1",
|
||||
],
|
||||
[
|
||||
"14.0",
|
||||
"15.0",
|
||||
"16.0",
|
||||
"17.0",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"artifact",
|
||||
"47",
|
||||
"False",
|
||||
"0.85",
|
||||
],
|
||||
],
|
||||
), # Inconsistent Data Types
|
||||
(
|
||||
mock_detections(
|
||||
xyxy=[[20, 21, 22, 23]],
|
||||
),
|
||||
{
|
||||
"metadata": {"sensor_id": 101, "location": "north"},
|
||||
"tags": ["urgent", "review"],
|
||||
},
|
||||
mock_detections(
|
||||
xyxy=[[14, 15, 16, 17]],
|
||||
),
|
||||
{
|
||||
"metadata": {"sensor_id": 104, "location": "west"},
|
||||
"tags": ["not-urgent", "done"],
|
||||
},
|
||||
"test_detections_complex_data.csv",
|
||||
[
|
||||
[
|
||||
"x_min",
|
||||
"y_min",
|
||||
"x_max",
|
||||
"y_max",
|
||||
"class_id",
|
||||
"confidence",
|
||||
"tracker_id",
|
||||
"metadata",
|
||||
"tags",
|
||||
],
|
||||
[
|
||||
"20.0",
|
||||
"21.0",
|
||||
"22.0",
|
||||
"23.0",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"{'sensor_id': 101, 'location': 'north'}",
|
||||
"['urgent', 'review']",
|
||||
],
|
||||
[
|
||||
"14.0",
|
||||
"15.0",
|
||||
"16.0",
|
||||
"17.0",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"{'sensor_id': 104, 'location': 'west'}",
|
||||
"['not-urgent', 'done']",
|
||||
],
|
||||
],
|
||||
), # Complex Data
|
||||
],
|
||||
)
|
||||
def test_csv_sink(
|
||||
detections: mock_detections,
|
||||
custom_data: Dict[str, Any],
|
||||
second_detections: mock_detections,
|
||||
second_custom_data: Dict[str, Any],
|
||||
file_name: str,
|
||||
expected_result: List[List[Any]],
|
||||
) -> None:
|
||||
with sv.CSVSink(file_name) as sink:
|
||||
sink.append(detections, custom_data)
|
||||
sink.append(second_detections, second_custom_data)
|
||||
|
||||
assert_csv_equal(file_name, expected_result)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"detections, custom_data, "
|
||||
"second_detections, second_custom_data, "
|
||||
"file_name, expected_result",
|
||||
[
|
||||
(
|
||||
mock_detections(
|
||||
xyxy=[[10, 20, 30, 40], [50, 60, 70, 80]],
|
||||
confidence=[0.7, 0.8],
|
||||
class_id=[0, 0],
|
||||
tracker_id=[0, 1],
|
||||
data={"class_name": ["person", "person"]},
|
||||
),
|
||||
{"frame_number": 42},
|
||||
mock_detections(
|
||||
xyxy=[[15, 25, 35, 45], [55, 65, 75, 85]],
|
||||
confidence=[0.6, 0.9],
|
||||
class_id=[1, 1],
|
||||
tracker_id=[2, 3],
|
||||
data={"class_name": ["car", "car"]},
|
||||
),
|
||||
{"frame_number": 43},
|
||||
"test_detections.csv",
|
||||
[
|
||||
[
|
||||
"x_min",
|
||||
"y_min",
|
||||
"x_max",
|
||||
"y_max",
|
||||
"class_id",
|
||||
"confidence",
|
||||
"tracker_id",
|
||||
"class_name",
|
||||
"frame_number",
|
||||
],
|
||||
["10.0", "20.0", "30.0", "40.0", "0", "0.7", "0", "person", "42"],
|
||||
["50.0", "60.0", "70.0", "80.0", "0", "0.8", "1", "person", "42"],
|
||||
["15.0", "25.0", "35.0", "45.0", "1", "0.6", "2", "car", "43"],
|
||||
["55.0", "65.0", "75.0", "85.0", "1", "0.9", "3", "car", "43"],
|
||||
],
|
||||
), # multiple detections
|
||||
(
|
||||
mock_detections(
|
||||
xyxy=[[60, 70, 80, 90], [100, 110, 120, 130]],
|
||||
tracker_id=[4, 5],
|
||||
data={"class_name": ["bike", "dog"]},
|
||||
),
|
||||
{"frame_number": 44},
|
||||
mock_detections(
|
||||
xyxy=[[65, 75, 85, 95], [105, 115, 125, 135]],
|
||||
confidence=[0.5, 0.4],
|
||||
data={"class_name": ["tree", "cat"]},
|
||||
),
|
||||
{"frame_number": 45},
|
||||
"test_detections_missing_fields.csv",
|
||||
[
|
||||
[
|
||||
"x_min",
|
||||
"y_min",
|
||||
"x_max",
|
||||
"y_max",
|
||||
"class_id",
|
||||
"confidence",
|
||||
"tracker_id",
|
||||
"class_name",
|
||||
"frame_number",
|
||||
],
|
||||
["60.0", "70.0", "80.0", "90.0", "", "", "4", "bike", "44"],
|
||||
["100.0", "110.0", "120.0", "130.0", "", "", "5", "dog", "44"],
|
||||
["65.0", "75.0", "85.0", "95.0", "", "0.5", "", "tree", "45"],
|
||||
["105.0", "115.0", "125.0", "135.0", "", "0.4", "", "cat", "45"],
|
||||
],
|
||||
), # missing fields
|
||||
(
|
||||
mock_detections(
|
||||
xyxy=[[10, 11, 12, 13]],
|
||||
confidence=[0.95],
|
||||
data={"class_name": "unknown", "is_detected": True, "score": 1},
|
||||
),
|
||||
{"frame_number": 46},
|
||||
mock_detections(
|
||||
xyxy=[[14, 15, 16, 17]],
|
||||
data={"class_name": "artifact", "is_detected": False, "score": 0.85},
|
||||
),
|
||||
{"frame_number": 47},
|
||||
"test_detections_varied_data.csv",
|
||||
[
|
||||
[
|
||||
"x_min",
|
||||
"y_min",
|
||||
"x_max",
|
||||
"y_max",
|
||||
"class_id",
|
||||
"confidence",
|
||||
"tracker_id",
|
||||
"class_name",
|
||||
"frame_number",
|
||||
"is_detected",
|
||||
"score",
|
||||
],
|
||||
[
|
||||
"10.0",
|
||||
"11.0",
|
||||
"12.0",
|
||||
"13.0",
|
||||
"",
|
||||
"0.95",
|
||||
"",
|
||||
"unknown",
|
||||
"46",
|
||||
"True",
|
||||
"1",
|
||||
],
|
||||
[
|
||||
"14.0",
|
||||
"15.0",
|
||||
"16.0",
|
||||
"17.0",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"artifact",
|
||||
"47",
|
||||
"False",
|
||||
"0.85",
|
||||
],
|
||||
],
|
||||
), # Inconsistent Data Types
|
||||
(
|
||||
mock_detections(
|
||||
xyxy=[[20, 21, 22, 23]],
|
||||
),
|
||||
{
|
||||
"metadata": {"sensor_id": 101, "location": "north"},
|
||||
"tags": ["urgent", "review"],
|
||||
},
|
||||
mock_detections(
|
||||
xyxy=[[14, 15, 16, 17]],
|
||||
),
|
||||
{
|
||||
"metadata": {"sensor_id": 104, "location": "west"},
|
||||
"tags": ["not-urgent", "done"],
|
||||
},
|
||||
"test_detections_complex_data.csv",
|
||||
[
|
||||
[
|
||||
"x_min",
|
||||
"y_min",
|
||||
"x_max",
|
||||
"y_max",
|
||||
"class_id",
|
||||
"confidence",
|
||||
"tracker_id",
|
||||
"metadata",
|
||||
"tags",
|
||||
],
|
||||
[
|
||||
"20.0",
|
||||
"21.0",
|
||||
"22.0",
|
||||
"23.0",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"{'sensor_id': 101, 'location': 'north'}",
|
||||
"['urgent', 'review']",
|
||||
],
|
||||
[
|
||||
"14.0",
|
||||
"15.0",
|
||||
"16.0",
|
||||
"17.0",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"{'sensor_id': 104, 'location': 'west'}",
|
||||
"['not-urgent', 'done']",
|
||||
],
|
||||
],
|
||||
), # Complex Data
|
||||
],
|
||||
)
|
||||
def test_csv_sink_manual(
|
||||
detections: mock_detections,
|
||||
custom_data: Dict[str, Any],
|
||||
second_detections: mock_detections,
|
||||
second_custom_data: Dict[str, Any],
|
||||
file_name: str,
|
||||
expected_result: List[List[Any]],
|
||||
) -> None:
|
||||
sink = sv.CSVSink(file_name)
|
||||
sink.open()
|
||||
sink.append(detections, custom_data)
|
||||
sink.append(second_detections, second_custom_data)
|
||||
sink.close()
|
||||
|
||||
assert_csv_equal(file_name, expected_result)
|
||||
|
||||
|
||||
def assert_csv_equal(file_name, expected_rows):
|
||||
with open(file_name, mode="r", newline="") as file:
|
||||
reader = csv.reader(file)
|
||||
for i, row in enumerate(reader):
|
||||
assert (
|
||||
[str(item) for item in expected_rows[i]] == row
|
||||
), f"Row in CSV didn't match expected output: {row} != {expected_rows[i]}"
|
||||
|
||||
os.remove(file_name)
|
||||
@@ -0,0 +1,92 @@
|
||||
from contextlib import ExitStack as DoesNotRaise
|
||||
from test.test_utils import mock_detections
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import supervision as sv
|
||||
|
||||
DETECTION_BOXES = np.array(
|
||||
[
|
||||
[35.0, 35.0, 65.0, 65.0],
|
||||
[60.0, 60.0, 90.0, 90.0],
|
||||
[85.0, 85.0, 115.0, 115.0],
|
||||
[110.0, 110.0, 140.0, 140.0],
|
||||
[135.0, 135.0, 165.0, 165.0],
|
||||
[160.0, 160.0, 190.0, 190.0],
|
||||
[185.0, 185.0, 215.0, 215.0],
|
||||
[210.0, 210.0, 240.0, 240.0],
|
||||
[235.0, 235.0, 265.0, 265.0],
|
||||
],
|
||||
dtype=np.float32,
|
||||
)
|
||||
|
||||
DETECTIONS = mock_detections(
|
||||
xyxy=DETECTION_BOXES, class_id=np.array([0, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||
)
|
||||
|
||||
POLYGON = np.array([[100, 100], [200, 100], [200, 200], [100, 200]])
|
||||
FRAME_RESOLUTION = (300, 300)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"detections, polygon_zone, expected_results, exception",
|
||||
[
|
||||
(
|
||||
DETECTIONS,
|
||||
sv.PolygonZone(
|
||||
POLYGON,
|
||||
FRAME_RESOLUTION,
|
||||
triggering_anchors=(
|
||||
sv.Position.TOP_LEFT,
|
||||
sv.Position.TOP_RIGHT,
|
||||
sv.Position.BOTTOM_LEFT,
|
||||
sv.Position.BOTTOM_RIGHT,
|
||||
),
|
||||
),
|
||||
np.array(
|
||||
[False, False, False, True, True, True, False, False, False], dtype=bool
|
||||
),
|
||||
DoesNotRaise(),
|
||||
), # Test all four corners
|
||||
(
|
||||
DETECTIONS,
|
||||
sv.PolygonZone(
|
||||
POLYGON,
|
||||
FRAME_RESOLUTION,
|
||||
),
|
||||
np.array(
|
||||
[False, False, True, True, True, True, False, False, False], dtype=bool
|
||||
),
|
||||
DoesNotRaise(),
|
||||
), # Test default behaviour when no anchors are provided
|
||||
(
|
||||
DETECTIONS,
|
||||
sv.PolygonZone(
|
||||
POLYGON, FRAME_RESOLUTION, triggering_position=sv.Position.BOTTOM_CENTER
|
||||
),
|
||||
np.array(
|
||||
[False, False, True, True, True, True, False, False, False], dtype=bool
|
||||
),
|
||||
DoesNotRaise(),
|
||||
), # Test default behaviour with deprecated api.
|
||||
(
|
||||
sv.Detections.empty(),
|
||||
sv.PolygonZone(
|
||||
POLYGON,
|
||||
FRAME_RESOLUTION,
|
||||
),
|
||||
np.array([], dtype=bool),
|
||||
DoesNotRaise(),
|
||||
), # Test empty detections
|
||||
],
|
||||
)
|
||||
def test_polygon_zone_trigger(
|
||||
detections: sv.Detections,
|
||||
polygon_zone: sv.PolygonZone,
|
||||
expected_results: np.ndarray,
|
||||
exception: Exception,
|
||||
) -> None:
|
||||
with exception:
|
||||
in_zone = polygon_zone.trigger(detections)
|
||||
assert np.all(in_zone == expected_results)
|
||||
@@ -6,13 +6,14 @@ import pytest
|
||||
|
||||
from supervision.config import CLASS_NAME_DATA_FIELD
|
||||
from supervision.detection.utils import (
|
||||
box_non_max_suppression,
|
||||
calculate_masks_centroids,
|
||||
clip_boxes,
|
||||
filter_polygons_by_area,
|
||||
get_data_item,
|
||||
mask_non_max_suppression,
|
||||
merge_data,
|
||||
move_boxes,
|
||||
non_max_suppression,
|
||||
process_roboflow_result,
|
||||
scale_boxes,
|
||||
)
|
||||
@@ -113,19 +114,225 @@ TEST_MASK[:, 300:351, 200:251] = True
|
||||
), # three boxes with different category
|
||||
],
|
||||
)
|
||||
def test_non_max_suppression(
|
||||
def test_box_non_max_suppression(
|
||||
predictions: np.ndarray,
|
||||
iou_threshold: float,
|
||||
expected_result: Optional[np.ndarray],
|
||||
exception: Exception,
|
||||
) -> None:
|
||||
with exception:
|
||||
result = non_max_suppression(
|
||||
result = box_non_max_suppression(
|
||||
predictions=predictions, iou_threshold=iou_threshold
|
||||
)
|
||||
assert np.array_equal(result, expected_result)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"predictions, masks, iou_threshold, expected_result, exception",
|
||||
[
|
||||
(
|
||||
np.empty((0, 6)),
|
||||
np.empty((0, 5, 5)),
|
||||
0.5,
|
||||
np.array([]),
|
||||
DoesNotRaise(),
|
||||
), # empty predictions and masks
|
||||
(
|
||||
np.array([[0, 0, 0, 0, 0.8]]),
|
||||
np.array(
|
||||
[
|
||||
[
|
||||
[False, False, False, False, False],
|
||||
[False, True, True, True, False],
|
||||
[False, True, True, True, False],
|
||||
[False, True, True, True, False],
|
||||
[False, False, False, False, False],
|
||||
]
|
||||
]
|
||||
),
|
||||
0.5,
|
||||
np.array([True]),
|
||||
DoesNotRaise(),
|
||||
), # single mask with no category
|
||||
(
|
||||
np.array([[0, 0, 0, 0, 0.8, 0]]),
|
||||
np.array(
|
||||
[
|
||||
[
|
||||
[False, False, False, False, False],
|
||||
[False, True, True, True, False],
|
||||
[False, True, True, True, False],
|
||||
[False, True, True, True, False],
|
||||
[False, False, False, False, False],
|
||||
]
|
||||
]
|
||||
),
|
||||
0.5,
|
||||
np.array([True]),
|
||||
DoesNotRaise(),
|
||||
), # single mask with category
|
||||
(
|
||||
np.array([[0, 0, 0, 0, 0.8], [0, 0, 0, 0, 0.9]]),
|
||||
np.array(
|
||||
[
|
||||
[
|
||||
[False, False, False, False, False],
|
||||
[False, True, True, False, False],
|
||||
[False, True, True, False, False],
|
||||
[False, False, False, False, False],
|
||||
[False, False, False, False, False],
|
||||
],
|
||||
[
|
||||
[False, False, False, False, False],
|
||||
[False, False, False, False, False],
|
||||
[False, False, False, True, True],
|
||||
[False, False, False, True, True],
|
||||
[False, False, False, False, False],
|
||||
],
|
||||
]
|
||||
),
|
||||
0.5,
|
||||
np.array([True, True]),
|
||||
DoesNotRaise(),
|
||||
), # two masks non-overlapping with no category
|
||||
(
|
||||
np.array([[0, 0, 0, 0, 0.8], [0, 0, 0, 0, 0.9]]),
|
||||
np.array(
|
||||
[
|
||||
[
|
||||
[False, False, False, False, False],
|
||||
[False, True, True, True, False],
|
||||
[False, True, True, True, False],
|
||||
[False, True, True, True, False],
|
||||
[False, False, False, False, False],
|
||||
],
|
||||
[
|
||||
[False, False, False, False, False],
|
||||
[False, False, True, True, True],
|
||||
[False, False, True, True, True],
|
||||
[False, False, True, True, True],
|
||||
[False, False, False, False, False],
|
||||
],
|
||||
]
|
||||
),
|
||||
0.4,
|
||||
np.array([False, True]),
|
||||
DoesNotRaise(),
|
||||
), # two masks partially overlapping with no category
|
||||
(
|
||||
np.array([[0, 0, 0, 0, 0.8, 0], [0, 0, 0, 0, 0.9, 1]]),
|
||||
np.array(
|
||||
[
|
||||
[
|
||||
[False, False, False, False, False],
|
||||
[False, True, True, True, False],
|
||||
[False, True, True, True, False],
|
||||
[False, True, True, True, False],
|
||||
[False, False, False, False, False],
|
||||
],
|
||||
[
|
||||
[False, False, False, False, False],
|
||||
[False, False, True, True, True],
|
||||
[False, False, True, True, True],
|
||||
[False, False, True, True, True],
|
||||
[False, False, False, False, False],
|
||||
],
|
||||
]
|
||||
),
|
||||
0.5,
|
||||
np.array([True, True]),
|
||||
DoesNotRaise(),
|
||||
), # two masks partially overlapping with different category
|
||||
(
|
||||
np.array(
|
||||
[
|
||||
[0, 0, 0, 0, 0.8],
|
||||
[0, 0, 0, 0, 0.85],
|
||||
[0, 0, 0, 0, 0.9],
|
||||
]
|
||||
),
|
||||
np.array(
|
||||
[
|
||||
[
|
||||
[False, False, False, False, False],
|
||||
[False, True, True, False, False],
|
||||
[False, True, True, False, False],
|
||||
[False, False, False, False, False],
|
||||
[False, False, False, False, False],
|
||||
],
|
||||
[
|
||||
[False, False, False, False, False],
|
||||
[False, True, True, False, False],
|
||||
[False, True, True, False, False],
|
||||
[False, False, False, False, False],
|
||||
[False, False, False, False, False],
|
||||
],
|
||||
[
|
||||
[False, False, False, False, False],
|
||||
[False, False, False, True, True],
|
||||
[False, False, False, True, True],
|
||||
[False, False, False, False, False],
|
||||
[False, False, False, False, False],
|
||||
],
|
||||
]
|
||||
),
|
||||
0.5,
|
||||
np.array([False, True, True]),
|
||||
DoesNotRaise(),
|
||||
), # three masks with no category
|
||||
(
|
||||
np.array(
|
||||
[
|
||||
[0, 0, 0, 0, 0.8, 0],
|
||||
[0, 0, 0, 0, 0.85, 1],
|
||||
[0, 0, 0, 0, 0.9, 2],
|
||||
]
|
||||
),
|
||||
np.array(
|
||||
[
|
||||
[
|
||||
[False, False, False, False, False],
|
||||
[False, True, True, False, False],
|
||||
[False, True, True, False, False],
|
||||
[False, False, False, False, False],
|
||||
[False, False, False, False, False],
|
||||
],
|
||||
[
|
||||
[False, False, False, False, False],
|
||||
[False, True, True, False, False],
|
||||
[False, True, True, False, False],
|
||||
[False, True, True, False, False],
|
||||
[False, False, False, False, False],
|
||||
],
|
||||
[
|
||||
[False, False, False, False, False],
|
||||
[False, True, True, False, False],
|
||||
[False, True, True, False, False],
|
||||
[False, False, False, False, False],
|
||||
[False, False, False, False, False],
|
||||
],
|
||||
]
|
||||
),
|
||||
0.5,
|
||||
np.array([True, True, True]),
|
||||
DoesNotRaise(),
|
||||
), # three masks with different category
|
||||
],
|
||||
)
|
||||
def test_mask_non_max_suppression(
|
||||
predictions: np.ndarray,
|
||||
masks: np.ndarray,
|
||||
iou_threshold: float,
|
||||
expected_result: Optional[np.ndarray],
|
||||
exception: Exception,
|
||||
) -> None:
|
||||
with exception:
|
||||
result = mask_non_max_suppression(
|
||||
predictions=predictions, masks=masks, iou_threshold=iou_threshold
|
||||
)
|
||||
assert np.array_equal(result, expected_result)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"xyxy, resolution_wh, expected_result",
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user