Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
beee1c715d | ||
|
|
d9adc45ca9 | ||
|
|
24df5aa78e | ||
|
|
4a58c0b726 | ||
|
|
9fc789e6cf | ||
|
|
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 | ||
|
|
53f4cde18d | ||
|
|
181724e2d3 | ||
|
|
205b2d200a | ||
|
|
3dcb330822 | ||
|
|
6fa4f25250 | ||
|
|
35d5738622 | ||
|
|
9d56d8a9fe | ||
|
|
c7d7e8b71e | ||
|
|
6565fae1c2 | ||
|
|
a5bef3b15e | ||
|
|
164b9bedce | ||
|
|
aabe60be6a | ||
|
|
533a8a1aa3 | ||
|
|
c7790fdf56 | ||
|
|
dd53cd06b9 | ||
|
|
24593b64d5 | ||
|
|
80b7eecfaf | ||
|
|
6b063736d9 | ||
|
|
7b0d5e729e | ||
|
|
7916d6a6a4 | ||
|
|
79560945f5 | ||
|
|
d4e0d8d46d | ||
|
|
5a89e2170f | ||
|
|
ab5a86596f | ||
|
|
12b42f80de | ||
|
|
8a5cd1bc3e | ||
|
|
e29e960c5f | ||
|
|
f6e5b6dc54 | ||
|
|
e2751368d5 | ||
|
|
955a6d67fb | ||
|
|
833edc95ad | ||
|
|
4df21ded6a | ||
|
|
f03be21a9f | ||
|
|
2d1aa3b996 | ||
|
|
7af914bf56 | ||
|
|
8872151d0c | ||
|
|
d961aa9eb4 | ||
|
|
845c9cc2c2 | ||
|
|
4667e02d79 | ||
|
|
047672d458 | ||
|
|
2622144660 | ||
|
|
fb0500c394 | ||
|
|
10f3b73e79 | ||
|
|
564f979977 | ||
|
|
64302f450c | ||
|
|
239ea649c0 | ||
|
|
626636cfa7 | ||
|
|
6b14cc9572 | ||
|
|
294a476977 | ||
|
|
4fedb87f97 | ||
|
|
a1b2297e9f | ||
|
|
c064c1fc98 | ||
|
|
0f3359c15f | ||
|
|
a9987dbc83 | ||
|
|
b14a035ede | ||
|
|
295e8b725b | ||
|
|
fa8a9cb5ac | ||
|
|
66245dd0df | ||
|
|
2d0a3f9d79 | ||
|
|
7b2c09c0ab | ||
|
|
d314bf0364 | ||
|
|
21791c8522 | ||
|
|
24ca25ef94 | ||
|
|
09d771c139 | ||
|
|
6efd51dfb1 | ||
|
|
c299a929c5 | ||
|
|
f5f068bffd | ||
|
|
fc22a9ccd9 | ||
|
|
45fe3c1c0f | ||
|
|
027b60837a | ||
|
|
fa569cb235 | ||
|
|
2bc58e7938 | ||
|
|
6f87069080 | ||
|
|
c0c541d121 | ||
|
|
08173899a9 | ||
|
|
6e9bfc9a05 | ||
|
|
65eda0b80c | ||
|
|
740076d961 | ||
|
|
f3cb8e9d27 | ||
|
|
846b337155 | ||
|
|
ce19f917e9 | ||
|
|
c70ee87897 | ||
|
|
786942ba7e | ||
|
|
5ed3430b16 | ||
|
|
39ef293926 | ||
|
|
a59d84549f | ||
|
|
f94c0a4930 | ||
|
|
ae7360b5e0 | ||
|
|
e809e424e6 | ||
|
|
b1dbeaf9c4 | ||
|
|
fbcc211079 | ||
|
|
6b7ccfccd9 | ||
|
|
5677b4b5e3 | ||
|
|
c323ecba8a | ||
|
|
995f170e30 | ||
|
|
decbbb62ad | ||
|
|
7d3c2114dd | ||
|
|
b008aadd41 | ||
|
|
cca120915e | ||
|
|
4e4fad9d04 | ||
|
|
38bdeff171 | ||
|
|
6fee626828 | ||
|
|
bb4d741aff | ||
|
|
d503e3aa50 | ||
|
|
3b13fc41a7 | ||
|
|
9e6abc0fff | ||
|
|
3e48708910 | ||
|
|
215741fd83 | ||
|
|
c2a5583aa2 | ||
|
|
c04edbcf85 | ||
|
|
f768347d26 | ||
|
|
ffc8c16993 | ||
|
|
6128576ea1 | ||
|
|
cda5016a5a | ||
|
|
f8ee3a4290 | ||
|
|
5f04836917 | ||
|
|
616fc96ca8 | ||
|
|
dca7fb43e5 | ||
|
|
ec7baf3360 | ||
|
|
9fb97e3c79 | ||
|
|
536e0d86a8 | ||
|
|
3a608eeb93 | ||
|
|
008b0bd984 | ||
|
|
353865e017 | ||
|
|
da4ee62298 | ||
|
|
ae758a655d | ||
|
|
e38256f316 | ||
|
|
713bacae08 | ||
|
|
2367fcac82 | ||
|
|
1f640980ad | ||
|
|
64661a3e4a | ||
|
|
1c414bd70f | ||
|
|
e32b05a636 | ||
|
|
688cf14f2a | ||
|
|
e3529436e1 | ||
|
|
99d1b6d130 | ||
|
|
47eed7257c | ||
|
|
602676bd9c | ||
|
|
0e1f422a75 | ||
|
|
4d8f15f295 | ||
|
|
c78354203d | ||
|
|
310a09859d | ||
|
|
bf427d5061 | ||
|
|
8ab7cb659c | ||
|
|
a58c8ef0fe | ||
|
|
3e0a665ef2 | ||
|
|
3a0682db08 | ||
|
|
a36b426dff | ||
|
|
3f77433f2e | ||
|
|
5d1e76fbd0 | ||
|
|
6600d7533a | ||
|
|
1d4d7dd4ea | ||
|
|
d7420c545e | ||
|
|
729eb16ea6 | ||
|
|
ca6ba30375 | ||
|
|
7f4b27a556 | ||
|
|
ffdab06376 | ||
|
|
b912db7a06 |
@@ -1,10 +1,8 @@
|
||||
name: Docs WorkFlow
|
||||
name: Docs WorkFlow - Develop Tag 📚
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
- develop
|
||||
|
||||
permissions:
|
||||
@@ -16,11 +14,26 @@ jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
- name: 🔄 Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: 🐍 Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
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
|
||||
- name: 📦 Install mkdocs-material
|
||||
run: pip install mkdocs-material
|
||||
- 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: ⚙️ 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: |
|
||||
mike deploy --push develop
|
||||
|
||||
@@ -38,14 +38,13 @@ jobs:
|
||||
numpy==1.21.2 \
|
||||
opencv-python==4.5.5.64 \
|
||||
packaging==23.2 \
|
||||
Pillow==10.1.0 \
|
||||
pluggy==1.3.0 \
|
||||
pyparsing==3.1.1 \
|
||||
pytest==7.2.0 \
|
||||
python-dateutil==2.8.2 \
|
||||
PyYAML==5.3 \
|
||||
requests==2.26.0 \
|
||||
scipy==1.9.0 \
|
||||
scipy==1.10.0 \
|
||||
setuptools-scm==8.0.4 \
|
||||
six==1.16.0 \
|
||||
tomli==2.0.1 \
|
||||
|
||||
+21
@@ -135,3 +135,24 @@ dmypy.json
|
||||
|
||||
# Notebooks
|
||||
notebooks/
|
||||
|
||||
# OSX folder attributes
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear on external disk
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
Desktop.ini
|
||||
|
||||
# Linux
|
||||
*~
|
||||
.directory
|
||||
.Trash-*
|
||||
|
||||
@@ -13,6 +13,7 @@ repos:
|
||||
- id: trailing-whitespace
|
||||
exclude: test/.*\.py
|
||||
- id: check-yaml
|
||||
exclude: mkdocs.yml
|
||||
- id: check-docstring-first
|
||||
- id: check-executables-have-shebangs
|
||||
- id: check-toml
|
||||
@@ -29,7 +30,7 @@ repos:
|
||||
|
||||
|
||||
- repo: https://github.com/PyCQA/bandit
|
||||
rev: '1.7.6'
|
||||
rev: '1.7.7'
|
||||
hooks:
|
||||
- id: bandit
|
||||
args: ["-c", "pyproject.toml"]
|
||||
@@ -46,7 +47,7 @@ repos:
|
||||
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.1.13
|
||||
rev: v0.2.0
|
||||
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
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ git push -u origin main
|
||||
|
||||
### Pre-commit tool
|
||||
|
||||
This project utilizes the [pre-commit](https://pre-commit.com/) tool to maintain code quality and consistency. Before submitting a pull request or making any commits, it is important to run the pre-commit tool to ensure that your changes meet the project's guidelines.
|
||||
This project uses the [pre-commit](https://pre-commit.com/) tool to maintain code quality and consistency. Before submitting a pull request or making any commits, it is important to run the pre-commit tool to ensure that your changes meet the project's guidelines.
|
||||
|
||||
Furthermore, we have integrated a pre-commit GitHub Action into our workflow. This means that with every pull request opened, the pre-commit checks will be automatically enforced, streamlining the code review process and ensuring that all contributions adhere to our quality standards.
|
||||
|
||||
|
||||
@@ -43,20 +43,20 @@ 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
|
||||
>>> import supervision as sv
|
||||
>>> from ultralytics import YOLO
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
>>> image = cv2.imread(...)
|
||||
>>> model = YOLO('yolov8s.pt')
|
||||
>>> result = model(image)[0]
|
||||
>>> detections = sv.Detections.from_ultralytics(result)
|
||||
image = cv2.imread(...)
|
||||
model = YOLO('yolov8s.pt')
|
||||
result = model(image)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
|
||||
>>> len(detections)
|
||||
5
|
||||
len(detections)
|
||||
# 5
|
||||
```
|
||||
|
||||
<details>
|
||||
@@ -67,17 +67,17 @@ Supervision was designed to be model agnostic. Just plug in any classification,
|
||||
Running with [Inference](https://github.com/roboflow/inference) requires a [Roboflow API KEY](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key).
|
||||
|
||||
```python
|
||||
>>> import cv2
|
||||
>>> import supervision as sv
|
||||
>>> from inference.models.utils import get_roboflow_model
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from inference.models.utils import get_roboflow_model
|
||||
|
||||
>>> image = cv2.imread(...)
|
||||
>>> model = get_roboflow_model(model_id="yolov8s-640", api_key=<ROBOFLOW API KEY>)
|
||||
>>> result = model.infer(image)[0]
|
||||
>>> detections = sv.Detections.from_inference(result)
|
||||
image = cv2.imread(...)
|
||||
model = get_roboflow_model(model_id="yolov8s-640", api_key=<ROBOFLOW API KEY>)
|
||||
result = model.infer(image)[0]
|
||||
detections = sv.Detections.from_inference(result)
|
||||
|
||||
>>> len(detections)
|
||||
>>> 5
|
||||
len(detections)
|
||||
# 5
|
||||
|
||||
```
|
||||
|
||||
@@ -85,42 +85,42 @@ Supervision was designed to be model agnostic. Just plug in any classification,
|
||||
|
||||
### 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
|
||||
>>> import supervision as sv
|
||||
import cv2
|
||||
import supervision as sv
|
||||
|
||||
>>> image = cv2.imread(...)
|
||||
>>> detections = sv.Detections(...)
|
||||
image = cv2.imread(...)
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> bounding_box_annotator = sv.BoundingBoxAnnotator()
|
||||
>>> annotated_frame = bounding_box_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
bounding_box_annotator = sv.BoundingBoxAnnotator()
|
||||
annotated_frame = bounding_box_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
https://github.com/roboflow/supervision/assets/26109316/691e219c-0565-4403-9218-ab5644f39bce
|
||||
|
||||
### 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
|
||||
import supervision as sv
|
||||
|
||||
>>> dataset = sv.DetectionDataset.from_yolo(
|
||||
... images_directory_path=...,
|
||||
... annotations_directory_path=...,
|
||||
... data_yaml_path=...
|
||||
... )
|
||||
dataset = sv.DetectionDataset.from_yolo(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...,
|
||||
data_yaml_path=...
|
||||
)
|
||||
|
||||
>>> dataset.classes
|
||||
dataset.classes
|
||||
['dog', 'person']
|
||||
|
||||
>>> len(dataset)
|
||||
1000
|
||||
len(dataset)
|
||||
# 1000
|
||||
```
|
||||
|
||||
<details close>
|
||||
@@ -129,86 +129,86 @@ Supervision provides a set of [utils](https://supervision.roboflow.com/datasets/
|
||||
- load
|
||||
|
||||
```python
|
||||
>>> dataset = sv.DetectionDataset.from_yolo(
|
||||
... images_directory_path=...,
|
||||
... annotations_directory_path=...,
|
||||
... data_yaml_path=...
|
||||
... )
|
||||
dataset = sv.DetectionDataset.from_yolo(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...,
|
||||
data_yaml_path=...
|
||||
)
|
||||
|
||||
>>> dataset = sv.DetectionDataset.from_pascal_voc(
|
||||
... images_directory_path=...,
|
||||
... annotations_directory_path=...
|
||||
... )
|
||||
dataset = sv.DetectionDataset.from_pascal_voc(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...
|
||||
)
|
||||
|
||||
>>> dataset = sv.DetectionDataset.from_coco(
|
||||
... images_directory_path=...,
|
||||
... annotations_path=...
|
||||
... )
|
||||
dataset = sv.DetectionDataset.from_coco(
|
||||
images_directory_path=...,
|
||||
annotations_path=...
|
||||
)
|
||||
```
|
||||
|
||||
- split
|
||||
|
||||
```python
|
||||
>>> train_dataset, test_dataset = dataset.split(split_ratio=0.7)
|
||||
>>> test_dataset, valid_dataset = test_dataset.split(split_ratio=0.5)
|
||||
train_dataset, test_dataset = dataset.split(split_ratio=0.7)
|
||||
test_dataset, valid_dataset = test_dataset.split(split_ratio=0.5)
|
||||
|
||||
>>> len(train_dataset), len(test_dataset), len(valid_dataset)
|
||||
(700, 150, 150)
|
||||
len(train_dataset), len(test_dataset), len(valid_dataset)
|
||||
# (700, 150, 150)
|
||||
```
|
||||
|
||||
- merge
|
||||
|
||||
```python
|
||||
>>> ds_1 = sv.DetectionDataset(...)
|
||||
>>> len(ds_1)
|
||||
100
|
||||
>>> ds_1.classes
|
||||
['dog', 'person']
|
||||
ds_1 = sv.DetectionDataset(...)
|
||||
len(ds_1)
|
||||
# 100
|
||||
ds_1.classes
|
||||
# ['dog', 'person']
|
||||
|
||||
>>> ds_2 = sv.DetectionDataset(...)
|
||||
>>> len(ds_2)
|
||||
200
|
||||
>>> ds_2.classes
|
||||
['cat']
|
||||
ds_2 = sv.DetectionDataset(...)
|
||||
len(ds_2)
|
||||
# 200
|
||||
ds_2.classes
|
||||
# ['cat']
|
||||
|
||||
>>> ds_merged = sv.DetectionDataset.merge([ds_1, ds_2])
|
||||
>>> len(ds_merged)
|
||||
300
|
||||
>>> ds_merged.classes
|
||||
['cat', 'dog', 'person']
|
||||
ds_merged = sv.DetectionDataset.merge([ds_1, ds_2])
|
||||
len(ds_merged)
|
||||
# 300
|
||||
ds_merged.classes
|
||||
# ['cat', 'dog', 'person']
|
||||
```
|
||||
|
||||
- save
|
||||
|
||||
```python
|
||||
>>> dataset.as_yolo(
|
||||
... images_directory_path=...,
|
||||
... annotations_directory_path=...,
|
||||
... data_yaml_path=...
|
||||
... )
|
||||
dataset.as_yolo(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...,
|
||||
data_yaml_path=...
|
||||
)
|
||||
|
||||
>>> dataset.as_pascal_voc(
|
||||
... images_directory_path=...,
|
||||
... annotations_directory_path=...
|
||||
... )
|
||||
dataset.as_pascal_voc(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...
|
||||
)
|
||||
|
||||
>>> dataset.as_coco(
|
||||
... images_directory_path=...,
|
||||
... annotations_path=...
|
||||
... )
|
||||
dataset.as_coco(
|
||||
images_directory_path=...,
|
||||
annotations_path=...
|
||||
)
|
||||
```
|
||||
|
||||
- convert
|
||||
|
||||
```python
|
||||
>>> sv.DetectionDataset.from_yolo(
|
||||
... images_directory_path=...,
|
||||
... annotations_directory_path=...,
|
||||
... data_yaml_path=...
|
||||
... ).as_pascal_voc(
|
||||
... images_directory_path=...,
|
||||
... annotations_directory_path=...
|
||||
... )
|
||||
sv.DetectionDataset.from_yolo(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...,
|
||||
data_yaml_path=...
|
||||
).as_pascal_voc(
|
||||
images_directory_path=...,
|
||||
annotations_directory_path=...
|
||||
)
|
||||
```
|
||||
|
||||
</details>
|
||||
@@ -237,7 +237,7 @@ https://user-images.githubusercontent.com/26109316/207858600-ee862b22-0353-440b-
|
||||
|
||||
https://github.com/roboflow/supervision/assets/26109316/c9436828-9fbf-4c25-ae8c-60e9c81b3900
|
||||
|
||||
https://github.com/roboflow/supervision/assets/26109316/f84db7b5-79e2-4142-a1da-64daa43ce667
|
||||
https://github.com/roboflow/supervision/assets/26109316/3ac6982f-4943-4108-9b7f-51787ef1a69f
|
||||
|
||||
## 📚 documentation
|
||||
|
||||
|
||||
+278
-204
@@ -1,16 +1,23 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Annotators
|
||||
|
||||
=== "BoundingBox"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> bounding_box_annotator = sv.BoundingBoxAnnotator()
|
||||
>>> annotated_frame = bounding_box_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
bounding_box_annotator = sv.BoundingBoxAnnotator()
|
||||
annotated_frame = bounding_box_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
@@ -22,16 +29,16 @@
|
||||
=== "RoundBox"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> round_box_annotator = sv.RoundBoxAnnotator()
|
||||
>>> annotated_frame = round_box_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
round_box_annotator = sv.RoundBoxAnnotator()
|
||||
annotated_frame = round_box_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
@@ -43,16 +50,16 @@
|
||||
=== "BoxCorner"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> corner_annotator = sv.BoxCornerAnnotator()
|
||||
>>> annotated_frame = corner_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
corner_annotator = sv.BoxCornerAnnotator()
|
||||
annotated_frame = corner_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
@@ -64,16 +71,16 @@
|
||||
=== "Color"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> color_annotator = sv.ColorAnnotator()
|
||||
>>> annotated_frame = color_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
color_annotator = sv.ColorAnnotator()
|
||||
annotated_frame = color_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
@@ -85,16 +92,16 @@
|
||||
=== "Circle"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> circle_annotator = sv.CircleAnnotator()
|
||||
>>> annotated_frame = circle_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
circle_annotator = sv.CircleAnnotator()
|
||||
annotated_frame = circle_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
@@ -106,37 +113,37 @@
|
||||
=== "Dot"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> dot_annotator = sv.DotAnnotator()
|
||||
>>> annotated_frame = dot_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
dot_annotator = sv.DotAnnotator()
|
||||
annotated_frame = dot_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Triangle"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> triangle_annotator = sv.TriangleAnnotator()
|
||||
>>> annotated_frame = triangle_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
triangle_annotator = sv.TriangleAnnotator()
|
||||
annotated_frame = triangle_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
@@ -148,16 +155,16 @@
|
||||
=== "Ellipse"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> ellipse_annotator = sv.EllipseAnnotator()
|
||||
>>> annotated_frame = ellipse_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
ellipse_annotator = sv.EllipseAnnotator()
|
||||
annotated_frame = ellipse_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
@@ -169,37 +176,58 @@
|
||||
=== "Halo"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> halo_annotator = sv.HaloAnnotator()
|
||||
>>> annotated_frame = halo_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
halo_annotator = sv.HaloAnnotator()
|
||||
annotated_frame = halo_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "PercentageBar"
|
||||
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
percentage_bar_annotator = sv.PercentageBarAnnotator()
|
||||
annotated_frame = percentage_bar_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Mask"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> mask_annotator = sv.MaskAnnotator()
|
||||
>>> annotated_frame = mask_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
mask_annotator = sv.MaskAnnotator()
|
||||
annotated_frame = mask_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
@@ -211,16 +239,16 @@
|
||||
=== "Polygon"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> polygon_annotator = sv.PolygonAnnotator()
|
||||
>>> annotated_frame = polygon_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
polygon_annotator = sv.PolygonAnnotator()
|
||||
annotated_frame = polygon_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
@@ -232,16 +260,16 @@
|
||||
=== "Label"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> label_annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER)
|
||||
>>> annotated_frame = label_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
label_annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER)
|
||||
annotated_frame = label_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
@@ -253,16 +281,16 @@
|
||||
=== "Blur"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> blur_annotator = sv.BlurAnnotator()
|
||||
>>> annotated_frame = blur_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
blur_annotator = sv.BlurAnnotator()
|
||||
annotated_frame = blur_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
@@ -274,16 +302,16 @@
|
||||
=== "Pixelate"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> pixelate_annotator = sv.PixelateAnnotator()
|
||||
>>> annotated_frame = pixelate_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
pixelate_annotator = sv.PixelateAnnotator()
|
||||
annotated_frame = pixelate_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
@@ -295,26 +323,26 @@
|
||||
=== "Trace"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
>>> from ultralytics import YOLO
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
>>> model = YOLO('yolov8x.pt')
|
||||
model = YOLO('yolov8x.pt')
|
||||
|
||||
>>> trace_annotator = sv.TraceAnnotator()
|
||||
trace_annotator = sv.TraceAnnotator()
|
||||
|
||||
>>> video_info = sv.VideoInfo.from_video_path(video_path='...')
|
||||
>>> frames_generator = get_video_frames_generator(source_path='...')
|
||||
>>> tracker = sv.ByteTrack()
|
||||
video_info = sv.VideoInfo.from_video_path(video_path='...')
|
||||
frames_generator = get_video_frames_generator(source_path='...')
|
||||
tracker = sv.ByteTrack()
|
||||
|
||||
>>> with sv.VideoSink(target_path='...', video_info=video_info) as sink:
|
||||
... for frame in frames_generator:
|
||||
... result = model(frame)[0]
|
||||
... detections = sv.Detections.from_ultralytics(result)
|
||||
... detections = tracker.update_with_detections(detections)
|
||||
... annotated_frame = trace_annotator.annotate(
|
||||
... scene=frame.copy(),
|
||||
... detections=detections)
|
||||
... sink.write_frame(frame=annotated_frame)
|
||||
with sv.VideoSink(target_path='...', video_info=video_info) as sink:
|
||||
for frame in frames_generator:
|
||||
result = model(frame)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
detections = tracker.update_with_detections(detections)
|
||||
annotated_frame = trace_annotator.annotate(
|
||||
scene=frame.copy(),
|
||||
detections=detections)
|
||||
sink.write_frame(frame=annotated_frame)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
@@ -326,96 +354,142 @@
|
||||
=== "HeatMap"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
>>> from ultralytics import YOLO
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
>>> model = YOLO('yolov8x.pt')
|
||||
model = YOLO('yolov8x.pt')
|
||||
|
||||
>>> heat_map_annotator = sv.HeatMapAnnotator()
|
||||
heat_map_annotator = sv.HeatMapAnnotator()
|
||||
|
||||
>>> video_info = sv.VideoInfo.from_video_path(video_path='...')
|
||||
>>> frames_generator = get_video_frames_generator(source_path='...')
|
||||
video_info = sv.VideoInfo.from_video_path(video_path='...')
|
||||
frames_generator = get_video_frames_generator(source_path='...')
|
||||
|
||||
>>> with sv.VideoSink(target_path='...', video_info=video_info) as sink:
|
||||
... for frame in frames_generator:
|
||||
... result = model(frame)[0]
|
||||
... detections = sv.Detections.from_ultralytics(result)
|
||||
... annotated_frame = heat_map_annotator.annotate(
|
||||
... scene=frame.copy(),
|
||||
... detections=detections)
|
||||
... sink.write_frame(frame=annotated_frame)
|
||||
with sv.VideoSink(target_path='...', video_info=video_info) as sink:
|
||||
for frame in frames_generator:
|
||||
result = model(frame)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
annotated_frame = heat_map_annotator.annotate(
|
||||
scene=frame.copy(),
|
||||
detections=detections)
|
||||
sink.write_frame(frame=annotated_frame)
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
## BoundingBoxAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.BoundingBoxAnnotator">BoundingBoxAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.BoundingBoxAnnotator
|
||||
|
||||
## BoxCornerAnnotator
|
||||
|
||||
:::supervision.annotators.core.BoxCornerAnnotator
|
||||
|
||||
## ColorAnnotator
|
||||
|
||||
:::supervision.annotators.core.ColorAnnotator
|
||||
|
||||
## CircleAnnotator
|
||||
|
||||
:::supervision.annotators.core.CircleAnnotator
|
||||
|
||||
## DotAnnotator
|
||||
|
||||
:::supervision.annotators.core.DotAnnotator
|
||||
|
||||
## TriangleAnnotator
|
||||
|
||||
:::supervision.annotators.core.TriangleAnnotator
|
||||
|
||||
## EllipseAnnotator
|
||||
|
||||
:::supervision.annotators.core.EllipseAnnotator
|
||||
|
||||
## HaloAnnotator
|
||||
|
||||
:::supervision.annotators.core.HaloAnnotator
|
||||
|
||||
## HeatMapAnnotator
|
||||
|
||||
:::supervision.annotators.core.HeatMapAnnotator
|
||||
|
||||
## MaskAnnotator
|
||||
|
||||
:::supervision.annotators.core.MaskAnnotator
|
||||
|
||||
## PolygonAnnotator
|
||||
|
||||
:::supervision.annotators.core.PolygonAnnotator
|
||||
|
||||
## LabelAnnotator
|
||||
|
||||
:::supervision.annotators.core.LabelAnnotator
|
||||
|
||||
## BlurAnnotator
|
||||
|
||||
:::supervision.annotators.core.BlurAnnotator
|
||||
|
||||
## PixelateAnnotator
|
||||
|
||||
:::supervision.annotators.core.PixelateAnnotator
|
||||
|
||||
## TraceAnnotator
|
||||
|
||||
:::supervision.annotators.core.TraceAnnotator
|
||||
|
||||
## RoundBoxAnnotator
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.RoundBoxAnnotator">RoundBoxAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.RoundBoxAnnotator
|
||||
|
||||
## ColorLookup
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.BoxCornerAnnotator">BoxCornerAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.BoxCornerAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.OrientedBoxAnnotator">OrientedBoxAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.OrientedBoxAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.ColorAnnotator">ColorAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.ColorAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.CircleAnnotator">CircleAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.CircleAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.DotAnnotator">DotAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.DotAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.TriangleAnnotator">TriangleAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.TriangleAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.EllipseAnnotator">EllipseAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.EllipseAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.HaloAnnotator">HaloAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.HaloAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.PercentageBarAnnotator">PercentageBarAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.PercentageBarAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.HeatMapAnnotator">HeatMapAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.HeatMapAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.MaskAnnotator">MaskAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.MaskAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.PolygonAnnotator">PolygonAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.PolygonAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.LabelAnnotator">LabelAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.LabelAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.BlurAnnotator">BlurAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.BlurAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.PixelateAnnotator">PixelateAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.PixelateAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.TraceAnnotator">TraceAnnotator</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.core.TraceAnnotator
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2><a href="#supervision.annotators.core.ColorLookup">ColorLookup</a></h2>
|
||||
</div>
|
||||
|
||||
:::supervision.annotators.utils.ColorLookup
|
||||
|
||||
+13
-2
@@ -1,8 +1,15 @@
|
||||
---
|
||||
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
|
||||
|
||||
|
||||
To install the Supervision assets utility, you can use `pip`. This utility is available
|
||||
as an extra within the Supervision package.
|
||||
|
||||
@@ -12,10 +19,14 @@ as an extra within the Supervision package.
|
||||
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
|
||||
|
||||
+145
-73
@@ -1,10 +1,82 @@
|
||||
### 0.18.0 <small>January 25, 2024</small>
|
||||
|
||||
- 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
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
|
||||
>>> percentage_bar_annotator = sv.PercentageBarAnnotator()
|
||||
>>> annotated_frame = percentage_bar_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
```
|
||||
|
||||
- Added [#702](https://github.com/roboflow/supervision/pull/702): [`sv.RoundBoxAnnotator`](/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`](/0.18.0/annotators/#orientedboxannotator) allowing to annotate images and videos with OBB (Oriented Bounding Boxes).
|
||||
|
||||
```python
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
model = YOLO("yolov8n-obb.pt")
|
||||
|
||||
result = model(image)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
|
||||
oriented_box_annotator = sv.OrientedBoxAnnotator()
|
||||
annotated_frame = oriented_box_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
- Added [#696](https://github.com/roboflow/supervision/pull/696): [`sv.DetectionsSmoother`](/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`](/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
|
||||
|
||||
>>> sv.ColorPalette.from_matplotlib('viridis', 5)
|
||||
ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
|
||||
```
|
||||
|
||||
- Changed [#770](https://github.com/roboflow/supervision/pull/770): [`sv.Detections.from_ultralytics`](/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`](/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`](/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`).
|
||||
|
||||
!!! failure "Deprecated"
|
||||
|
||||
`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`](/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`](/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).
|
||||
|
||||
!!! failure "Deprecated"
|
||||
|
||||
`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`](/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
|
||||
@@ -19,7 +91,7 @@
|
||||
... )
|
||||
```
|
||||
|
||||
- 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
|
||||
@@ -27,35 +99,35 @@
|
||||
"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
|
||||
@@ -70,46 +142,46 @@
|
||||
... )
|
||||
```
|
||||
|
||||
- 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
|
||||
@@ -126,17 +198,17 @@
|
||||
|
||||
- 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
|
||||
@@ -155,23 +227,23 @@
|
||||
>>> 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
|
||||
@@ -193,25 +265,25 @@
|
||||
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
|
||||
@@ -238,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
|
||||
@@ -270,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
|
||||
@@ -294,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
|
||||
@@ -314,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.
|
||||
|
||||
@@ -324,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
|
||||
@@ -338,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
|
||||
@@ -352,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
|
||||
@@ -381,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
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## Classifications
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# Classifications
|
||||
|
||||
:::supervision.classification.core.Classifications
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Code of conduct
|
||||
|
||||
```
|
||||
--8<-- "CODE_OF_CONDUCT.md"
|
||||
```
|
||||
@@ -0,0 +1 @@
|
||||
--8<-- "CONTRIBUTING.md"
|
||||
+12
-2
@@ -1,12 +1,22 @@
|
||||
---
|
||||
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.
|
||||
@@ -1,3 +1,8 @@
|
||||
## Detections
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Detections
|
||||
|
||||
:::supervision.detection.core.Detections
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## InferenceSlicer
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# InferenceSlicer
|
||||
|
||||
:::supervision.detection.tools.inference_slicer.InferenceSlicer
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## LineZone
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# LineZone
|
||||
|
||||
:::supervision.detection.line_counter.LineZone
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
## PolygonZone
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# PolygonZone
|
||||
|
||||
:::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
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
comments: true
|
||||
status: new
|
||||
---
|
||||
|
||||
# Detection Smoother
|
||||
|
||||
:::supervision.detection.tools.smoother.DetectionsSmoother
|
||||
+33
-9
@@ -1,35 +1,59 @@
|
||||
## box_iou_batch
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# 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>non_max_suppression</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.detection.utils.non_max_suppression
|
||||
|
||||
## polygon_to_mask
|
||||
<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
|
||||
|
||||
+8
-2
@@ -1,7 +1,13 @@
|
||||
## Color
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# Color
|
||||
|
||||
:::supervision.draw.color.Color
|
||||
|
||||
## ColorPalette
|
||||
<div class="md-typeset">
|
||||
<h2>ColorPalette</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.draw.color.ColorPalette
|
||||
|
||||
+30
-8
@@ -1,31 +1,53 @@
|
||||
## draw_line
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# 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
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## Position
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# Position
|
||||
|
||||
:::supervision.geometry.core.Position
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
---
|
||||
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
|
||||
@@ -38,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`](/latest/detection/core/#supervision.detection.core.Detections.from_ultralytics) method, which accepts model results from both detection and segmentation models.
|
||||
|
||||
```python
|
||||
import cv2
|
||||
@@ -53,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`](/latest/detection/core/#supervision.detection.core.Detections.from_inference) method, which accepts model results from both detection and segmentation models.
|
||||
|
||||
```python
|
||||
import cv2
|
||||
@@ -68,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`](/latest/detection/core/#supervision.detection.core.Detections.from_deepsparse) ([Deepsparse](https://github.com/neuralmagic/deepsparse))
|
||||
- [`from_detectron2`](/latest/detection/core/#supervision.detection.core.Detections.from_detectron2) ([Detectron2](https://github.com/facebookresearch/detectron2))
|
||||
- [`from_mmdetection`](/latest/detection/core/#supervision.detection.core.Detections.from_mmdetection) ([MMDetection](https://github.com/open-mmlab/mmdetection))
|
||||
- [`from_inference`](/latest/detection/core/#supervision.detection.core.Detections.from_inference) ([Roboflow Inference](https://github.com/roboflow/inference))
|
||||
- [`from_sam`](/latest/detection/core/#supervision.detection.core.Detections.from_sam) ([Segment Anything Model](https://github.com/facebookresearch/segment-anything))
|
||||
- [`from_transformers`](/latest/detection/core/#supervision.detection.core.Detections.from_transformers) ([HuggingFace Transformers](https://github.com/huggingface/transformers))
|
||||
- [`from_yolo_nas`](/latest/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`](/latest/annotators/#supervision.annotators.core.BoundingBoxAnnotator) and [`sv.LabelAnnotator`](/latest/annotators/#supervision.annotators.core.LabelAnnotator) classes. If you are running the segmentation model [`sv.MaskAnnotator`](/latest/annotators/#supervision.annotators.core.MaskAnnotator) is a drop-in replacement for [`sv.BoundingBoxAnnotator`](/latest/annotators/#supervision.annotators.core.BoundingBoxAnnotator) that will allow you to draw masks instead of boxes.
|
||||
|
||||
=== "Ultralytics"
|
||||
|
||||
@@ -132,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`](/latest/utils/notebook/#supervision.utils.notebook.plot_image) function.
|
||||
|
||||
```python
|
||||
sv.plot_image(annotated_image)
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
🚧 Page under construction.
|
||||
@@ -1,3 +1,9 @@
|
||||
---
|
||||
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.
|
||||
@@ -38,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.
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
🚧 Page under construction.
|
||||
@@ -1,5 +1,11 @@
|
||||
---
|
||||
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
|
||||
@@ -9,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
|
||||
@@ -26,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
|
||||
@@ -86,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.
|
||||
|
||||
@@ -142,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.
|
||||
|
||||
@@ -224,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.
|
||||
|
||||
|
||||
+47
-1
@@ -1,9 +1,13 @@
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<p>
|
||||
<a align="center" href="" target="_blank">
|
||||
<img
|
||||
width="850"
|
||||
src="https://media.roboflow.com/open-source/supervision/roboflow-supervision-banner.png?ik-sdk-version=javascript-1.4.3&updatedAt=1674062891088"
|
||||
src="https://media.roboflow.com/open-source/supervision/rf-supervision-banner.png?updatedAt=1678995927529"
|
||||
>
|
||||
</a>
|
||||
</p>
|
||||
@@ -13,6 +17,13 @@
|
||||
|
||||
We write your reusable computer vision tools. Whether you need to load your dataset from your hard drive, draw detections on an image or video, or count how many detections are in a zone. You can count on us!
|
||||
|
||||
<video controls>
|
||||
<source
|
||||
src="https://media.roboflow.com/traffic_analysis_result.mp4"
|
||||
type="video/mp4"
|
||||
>
|
||||
</video>
|
||||
|
||||
## 💻 Install
|
||||
|
||||
You can install `supervision` with pip in a
|
||||
@@ -72,3 +83,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,5 @@
|
||||
# License
|
||||
|
||||
```
|
||||
--8<-- "LICENSE.md"
|
||||
```
|
||||
@@ -1,12 +1,22 @@
|
||||
---
|
||||
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
|
||||
|
||||
Vendored
+51
@@ -0,0 +1,51 @@
|
||||
{% if page.meta.comments %}
|
||||
<h2 id="__comments">{{ lang.t("meta.comments") }}</h2>
|
||||
|
||||
<!-- Insert Giscus code snippet from https://giscus.app/ here -->
|
||||
<script src="https://giscus.app/client.js"
|
||||
data-repo="roboflow/supervision"
|
||||
data-repo-id="R_kgDOIhIiww"
|
||||
data-category="Docs"
|
||||
data-category-id="DIC_kwDOIhIiw84Ccet_"
|
||||
data-mapping="pathname"
|
||||
data-strict="0"
|
||||
data-reactions-enabled="1"
|
||||
data-emit-metadata="1"
|
||||
data-input-position="top"
|
||||
data-theme="preferred_color_scheme"
|
||||
data-lang="en"
|
||||
data-loading="lazy"
|
||||
crossorigin="anonymous"
|
||||
async>
|
||||
</script>
|
||||
|
||||
<!-- Synchronize Giscus theme with palette -->
|
||||
<script>
|
||||
var giscus = document.querySelector("script[src*=giscus]")
|
||||
|
||||
/* Set palette on initial load */
|
||||
var palette = __md_get("__palette")
|
||||
if (palette && typeof palette.color === "object") {
|
||||
var theme = palette.color.scheme === "slate" ? "dark" : "light"
|
||||
giscus.setAttribute("data-theme", theme)
|
||||
}
|
||||
|
||||
/* Register event handlers after documented loaded */
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
var ref = document.querySelector("[data-md-component=palette]")
|
||||
ref.addEventListener("change", function() {
|
||||
var palette = __md_get("__palette")
|
||||
if (palette && typeof palette.color === "object") {
|
||||
var theme = palette.color.scheme === "slate" ? "dark" : "light"
|
||||
|
||||
/* Instruct Giscus to change theme */
|
||||
var frame = document.querySelector(".giscus-frame")
|
||||
frame.contentWindow.postMessage(
|
||||
{ giscus: { setConfig: { theme } } },
|
||||
"https://giscus.app"
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
{% endif %}
|
||||
+5
-1
@@ -1,3 +1,7 @@
|
||||
## ByteTrack
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# ByteTrack
|
||||
|
||||
:::supervision.tracker.byte_tracker.core.ByteTrack
|
||||
|
||||
+9
-1
@@ -1,3 +1,11 @@
|
||||
## list_files_with_extensions
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# File
|
||||
|
||||
<div class="md-typeset">
|
||||
<h2>list_files_with_extensions</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.utils.file.list_files_with_extensions
|
||||
|
||||
+8
-2
@@ -1,7 +1,13 @@
|
||||
## ImageSink
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# ImageSink
|
||||
|
||||
:::supervision.utils.image.ImageSink
|
||||
|
||||
## crop
|
||||
<div class="md-typeset">
|
||||
<h2>crop_image</h2>
|
||||
</div>
|
||||
|
||||
:::supervision.utils.image.crop_image
|
||||
|
||||
+13
-2
@@ -1,7 +1,18 @@
|
||||
## plot_image
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# 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
|
||||
|
||||
+21
-5
@@ -1,19 +1,35 @@
|
||||
## VideoInfo
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# 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:
|
||||
|
||||
@@ -3,3 +3,11 @@ data/*
|
||||
|
||||
# But re-include all .json files
|
||||
!data/*.json
|
||||
|
||||
*.pt
|
||||
*.pth
|
||||
*.mp4
|
||||
*.mov
|
||||
*.png
|
||||
*.jpg
|
||||
*.jpeg
|
||||
|
||||
@@ -1,2 +1,9 @@
|
||||
data/
|
||||
venv*/
|
||||
*.pt
|
||||
*.pth
|
||||
*.mp4
|
||||
*.mov
|
||||
*.png
|
||||
*.jpg
|
||||
*.jpeg
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
# speed estimation
|
||||
|
||||
[](https://colab.research.google.com/github/roboflow-ai/notebooks/blob/main/notebooks/how-to-estimate-vehicle-speed-with-computer-vision.ipynb)
|
||||
[](https://youtu.be/uWP6UjDeZvY)
|
||||
|
||||
## 👋 hello
|
||||
|
||||
This example performs speed estimation analysis using various object-detection models
|
||||
and ByteTrack - a simple yet effective online multi-object tracking method. It uses the
|
||||
supervision package for multiple tasks such as tracking, annotations, etc.
|
||||
|
||||
https://github.com/roboflow/supervision/assets/26109316/0542fd3c-bb5f-475e-b96c-793560abeb18
|
||||
https://github.com/roboflow/supervision/assets/26109316/d50118c1-2ae4-458d-915a-5d860fd36f71
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Adjust the [`SOURCE`](https://github.com/roboflow/supervision/blob/e32b05a636dab2ea1f39299e529c4b22b8baa8da/examples/speed_estimation/ultralytics_example.py#L10)
|
||||
> and [`TARGET`](https://github.com/roboflow/supervision/blob/e32b05a636dab2ea1f39299e529c4b22b8baa8da/examples/speed_estimation/ultralytics_example.py#L15)
|
||||
> configuration if you plan to run a speed estimation script on your video file. Those must be adjusted separately for each camera view. You can learn more
|
||||
> from our YouTube [tutorial](https://youtu.be/uWP6UjDeZvY).
|
||||
|
||||
## 💻 install
|
||||
|
||||
> [!NOTE]
|
||||
> YOLO-NAS is compatible with Python versions up to and including Python 3.10.
|
||||
|
||||
- clone repository and navigate to example directory
|
||||
|
||||
```bash
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
supervision==0.18.0rc1
|
||||
tqdm
|
||||
tqdm==4.66.1
|
||||
requests
|
||||
ultralytics
|
||||
super-gradients
|
||||
inference
|
||||
ultralytics==8.0.237
|
||||
super-gradients==3.5.0
|
||||
inference==0.9.8
|
||||
|
||||
@@ -29,6 +29,9 @@ class ViewTransformer:
|
||||
self.m = cv2.getPerspectiveTransform(source, target)
|
||||
|
||||
def transform_points(self, points: np.ndarray) -> np.ndarray:
|
||||
if points.size == 0:
|
||||
return points
|
||||
|
||||
reshaped_points = points.reshape(-1, 1, 2).astype(np.float32)
|
||||
transformed_points = cv2.perspectiveTransform(reshaped_points, self.m)
|
||||
return transformed_points.reshape(-1, 2)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,2 +1,9 @@
|
||||
data/
|
||||
venv/
|
||||
*.pt
|
||||
*.pth
|
||||
*.mp4
|
||||
*.mov
|
||||
*.png
|
||||
*.jpg
|
||||
*.jpeg
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
|
||||
+47
-7
@@ -5,21 +5,29 @@ 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
|
||||
@@ -28,10 +36,8 @@ nav:
|
||||
- Home: index.md
|
||||
- How to:
|
||||
- Detect and Annotate: how_to/detect_and_annotate.md
|
||||
- Process Video: how_to/process_video.md
|
||||
- Track Objects: how_to/track_objects.md
|
||||
- Filter Detections: how_to/filter_detections.md
|
||||
- Evaluate Model: how_to/evaluate_model.md
|
||||
- API:
|
||||
- Classifications:
|
||||
- Core: classification/core.md
|
||||
@@ -42,6 +48,8 @@ nav:
|
||||
- Line Zone: detection/tools/line_zone.md
|
||||
- Polygon Zone: detection/tools/polygon_zone.md
|
||||
- Inference Slicer: detection/tools/inference_slicer.md
|
||||
- Detection Smoother: detection/tools/smoother.md
|
||||
- Save Detections: detection/tools/save_detections.md
|
||||
- Annotators: annotators.md
|
||||
- Trackers: trackers.md
|
||||
- Datasets: datasets.md
|
||||
@@ -50,13 +58,22 @@ 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
|
||||
- Development:
|
||||
- Contributing: contributing.md
|
||||
- Code of Conduct: code_of_conduct.md
|
||||
- Changelog:
|
||||
- Changelog: changelog.md
|
||||
- Deprecated: deprecated.md
|
||||
- License: license.md
|
||||
|
||||
|
||||
theme:
|
||||
name: 'material'
|
||||
@@ -82,10 +99,28 @@ theme:
|
||||
code: Roboto Mono
|
||||
features:
|
||||
- content.code.copy
|
||||
- content.code.annotate
|
||||
|
||||
plugins:
|
||||
- mkdocstrings
|
||||
- search
|
||||
- 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
|
||||
import:
|
||||
- https://docs.python-requests.org/en/master/objects.inv
|
||||
domains: [std, py]
|
||||
|
||||
markdown_extensions:
|
||||
- admonition
|
||||
@@ -97,6 +132,11 @@ markdown_extensions:
|
||||
alternate_style: true
|
||||
- toc:
|
||||
permalink: true
|
||||
- pymdownx.emoji:
|
||||
emoji_index: !!python/name:material.extensions.emoji.twemoji
|
||||
emoji_generator: !!python/name:material.extensions.emoji.to_svg
|
||||
- pymdownx.snippets:
|
||||
check_paths: true
|
||||
|
||||
extra_javascript:
|
||||
- "https://widget.kapa.ai/kapa-widget.bundle.js"
|
||||
|
||||
Generated
+658
-496
File diff suppressed because it is too large
Load Diff
+12
-8
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "supervision"
|
||||
version = "0.18.0rc2"
|
||||
version = "0.19.0rc2"
|
||||
description = "A set of easy-to-use utils that will come in handy in any Computer Vision project"
|
||||
authors = ["Piotr Skalski <piotr.skalski92@gmail.com>"]
|
||||
maintainers = ["Piotr Skalski <piotr.skalski92@gmail.com>"]
|
||||
@@ -33,24 +33,27 @@ classifiers=[
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.8"
|
||||
numpy = ">=1.21.2"
|
||||
scipy = ">=1.9.0"
|
||||
matplotlib = ">=3.5.0"
|
||||
scipy = [
|
||||
{ version = "1.10.0", python = "<3.9" },
|
||||
{ version = "^1.10.0", python = ">=3.9" }
|
||||
]
|
||||
matplotlib = ">=3.6.0"
|
||||
pyyaml = ">=5.3"
|
||||
pillow = ">=9.4"
|
||||
opencv-python = { version = ">=4.5.5.64,<=4.8.1.78", optional = true }
|
||||
opencv-python-headless = ">=4.5.5.64,<=4.8.1.78"
|
||||
opencv-python = { version = ">=4.5.5.64", optional = true }
|
||||
opencv-python-headless = ">=4.5.5.64"
|
||||
requests = { version = ">=2.26.0,<=2.31.0", optional = true }
|
||||
tqdm = { version = ">=4.62.3,<=4.66.1", optional = true }
|
||||
defusedxml = "^0.7.1"
|
||||
|
||||
|
||||
|
||||
[tool.poetry.extras]
|
||||
desktop = ["opencv-python"]
|
||||
assets = ["requests","tqdm"]
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
twine = "^4.0.2"
|
||||
pytest = "^7.2.2"
|
||||
pytest = ">=7.2.2,<9.0.0"
|
||||
wheel = ">=0.40,<0.43"
|
||||
notebook = ">=6.5.3,<8.0.0"
|
||||
build = ">=0.10,<1.1"
|
||||
@@ -62,8 +65,9 @@ tox = "^4.11.4"
|
||||
|
||||
|
||||
[tool.poetry.group.docs.dependencies]
|
||||
mkdocs-material = "^9.1.4"
|
||||
mkdocs-material = {extras = ["imaging"], version = "^9.5.5"}
|
||||
mkdocstrings = {extras = ["python"], version = ">=0.20,<0.25"}
|
||||
mike = "^2.0.0"
|
||||
|
||||
|
||||
[tool.isort]
|
||||
|
||||
@@ -18,6 +18,8 @@ from supervision.annotators.core import (
|
||||
HeatMapAnnotator,
|
||||
LabelAnnotator,
|
||||
MaskAnnotator,
|
||||
OrientedBoxAnnotator,
|
||||
PercentageBarAnnotator,
|
||||
PixelateAnnotator,
|
||||
PolygonAnnotator,
|
||||
RoundBoxAnnotator,
|
||||
@@ -34,8 +36,10 @@ 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.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,
|
||||
calculate_masks_centroids,
|
||||
|
||||
+417
-164
@@ -6,7 +6,7 @@ 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.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
|
||||
@@ -21,7 +21,7 @@ class BoundingBoxAnnotator(BaseAnnotator):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
color: Union[Color, ColorPalette] = ColorPalette.default(),
|
||||
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
|
||||
thickness: int = 2,
|
||||
color_lookup: ColorLookup = ColorLookup.CLASS,
|
||||
):
|
||||
@@ -57,16 +57,16 @@ class BoundingBoxAnnotator(BaseAnnotator):
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> bounding_box_annotator = sv.BoundingBoxAnnotator()
|
||||
>>> annotated_frame = bounding_box_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
bounding_box_annotator = sv.BoundingBoxAnnotator()
|
||||
annotated_frame = bounding_box_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
:
|
||||
return scene
|
||||
|
||||
|
||||
class OrientedBoxAnnotator(BaseAnnotator):
|
||||
"""
|
||||
A class for drawing oriented bounding boxes on an image using provided detections.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
|
||||
thickness: int = 2,
|
||||
color_lookup: ColorLookup = ColorLookup.CLASS,
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
color (Union[Color, ColorPalette]): The color or color palette to use for
|
||||
annotating detections.
|
||||
thickness (int): Thickness of the bounding box lines.
|
||||
color_lookup (str): Strategy for mapping colors to annotations.
|
||||
Options are `INDEX`, `CLASS`, `TRACK`.
|
||||
"""
|
||||
self.color: Union[Color, ColorPalette] = color
|
||||
self.thickness: int = thickness
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
Annotates the given scene with oriented bounding boxes based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where bounding boxes will be drawn.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
|
||||
Example:
|
||||
```python
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
model = YOLO("yolov8n-obb.pt")
|
||||
|
||||
result = model(image)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
|
||||
oriented_box_annotator = sv.OrientedBoxAnnotator()
|
||||
annotated_frame = oriented_box_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
""" # noqa E501 // docs
|
||||
|
||||
if detections.data is None or ORIENTED_BOX_COORDINATES not in detections.data:
|
||||
return scene
|
||||
|
||||
for detection_idx in range(len(detections)):
|
||||
bbox = np.int0(detections.data.get(ORIENTED_BOX_COORDINATES)[detection_idx])
|
||||
color = resolve_color(
|
||||
color=self.color,
|
||||
detections=detections,
|
||||
detection_idx=detection_idx,
|
||||
color_lookup=self.color_lookup
|
||||
if custom_color_lookup is None
|
||||
else custom_color_lookup,
|
||||
)
|
||||
|
||||
cv2.drawContours(scene, [bbox], 0, color.as_bgr(), self.thickness)
|
||||
|
||||
return scene
|
||||
|
||||
|
||||
class MaskAnnotator(BaseAnnotator):
|
||||
"""
|
||||
A class for drawing masks on an image using provided detections.
|
||||
|
||||
!!! warning
|
||||
|
||||
This annotator utilizes the `sv.Detections.mask`.
|
||||
This annotator uses `sv.Detections.mask`.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
color: Union[Color, ColorPalette] = ColorPalette.default(),
|
||||
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
|
||||
opacity: float = 0.5,
|
||||
color_lookup: ColorLookup = ColorLookup.CLASS,
|
||||
):
|
||||
@@ -139,16 +219,16 @@ class MaskAnnotator(BaseAnnotator):
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> mask_annotator = sv.MaskAnnotator()
|
||||
>>> annotated_frame = mask_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
mask_annotator = sv.MaskAnnotator()
|
||||
annotated_frame = mask_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
:
|
||||
|
||||
!!! warning
|
||||
|
||||
This annotator utilizes the `sv.Detections.mask`.
|
||||
This annotator uses `sv.Detections.mask`.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
color: Union[Color, ColorPalette] = ColorPalette.default(),
|
||||
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
|
||||
thickness: int = 2,
|
||||
color_lookup: ColorLookup = ColorLookup.CLASS,
|
||||
):
|
||||
@@ -222,16 +302,16 @@ class PolygonAnnotator(BaseAnnotator):
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> polygon_annotator = sv.PolygonAnnotator()
|
||||
>>> annotated_frame = polygon_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
polygon_annotator = sv.PolygonAnnotator()
|
||||
annotated_frame = polygon_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
color: Union[Color, ColorPalette] = ColorPalette.default(),
|
||||
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
|
||||
opacity: float = 0.5,
|
||||
color_lookup: ColorLookup = ColorLookup.CLASS,
|
||||
):
|
||||
@@ -304,16 +384,16 @@ class ColorAnnotator(BaseAnnotator):
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> color_annotator = sv.ColorAnnotator()
|
||||
>>> annotated_frame = color_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
color_annotator = sv.ColorAnnotator()
|
||||
annotated_frame = color_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
:
|
||||
|
||||
!!! warning
|
||||
|
||||
This annotator utilizes the `sv.Detections.mask`.
|
||||
This annotator uses `sv.Detections.mask`.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
color: Union[Color, ColorPalette] = ColorPalette.default(),
|
||||
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
|
||||
opacity: float = 0.8,
|
||||
kernel_size: int = 40,
|
||||
color_lookup: ColorLookup = ColorLookup.CLASS,
|
||||
@@ -394,16 +474,16 @@ class HaloAnnotator(BaseAnnotator):
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> halo_annotator = sv.HaloAnnotator()
|
||||
>>> annotated_frame = halo_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
halo_annotator = sv.HaloAnnotator()
|
||||
annotated_frame = halo_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
color: Union[Color, ColorPalette] = ColorPalette.default(),
|
||||
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
|
||||
thickness: int = 2,
|
||||
start_angle: int = -45,
|
||||
end_angle: int = 235,
|
||||
@@ -488,16 +568,16 @@ class EllipseAnnotator(BaseAnnotator):
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> ellipse_annotator = sv.EllipseAnnotator()
|
||||
>>> annotated_frame = ellipse_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
ellipse_annotator = sv.EllipseAnnotator()
|
||||
annotated_frame = ellipse_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
color: Union[Color, ColorPalette] = ColorPalette.default(),
|
||||
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
|
||||
thickness: int = 4,
|
||||
corner_length: int = 15,
|
||||
color_lookup: ColorLookup = ColorLookup.CLASS,
|
||||
@@ -575,16 +655,16 @@ class BoxCornerAnnotator(BaseAnnotator):
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> corner_annotator = sv.BoxCornerAnnotator()
|
||||
>>> annotated_frame = corner_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
corner_annotator = sv.BoxCornerAnnotator()
|
||||
annotated_frame = corner_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
color: Union[Color, ColorPalette] = ColorPalette.default(),
|
||||
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
|
||||
thickness: int = 2,
|
||||
color_lookup: ColorLookup = ColorLookup.CLASS,
|
||||
):
|
||||
@@ -659,16 +739,16 @@ class CircleAnnotator(BaseAnnotator):
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> circle_annotator = sv.CircleAnnotator()
|
||||
>>> annotated_frame = circle_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
circle_annotator = sv.CircleAnnotator()
|
||||
annotated_frame = circle_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@@ -706,7 +786,7 @@ class DotAnnotator(BaseAnnotator):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
color: Union[Color, ColorPalette] = ColorPalette.default(),
|
||||
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
|
||||
radius: int = 4,
|
||||
position: Position = Position.CENTER,
|
||||
color_lookup: ColorLookup = ColorLookup.CLASS,
|
||||
@@ -745,16 +825,16 @@ class DotAnnotator(BaseAnnotator):
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> dot_annotator = sv.DotAnnotator()
|
||||
>>> annotated_frame = dot_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
dot_annotator = sv.DotAnnotator()
|
||||
annotated_frame = dot_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
,
|
||||
text_color: Color = Color.black(),
|
||||
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
|
||||
text_color: Color = Color.WHITE,
|
||||
text_scale: float = 0.5,
|
||||
text_thickness: int = 1,
|
||||
text_padding: int = 10,
|
||||
@@ -872,16 +952,16 @@ class LabelAnnotator:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> label_annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER)
|
||||
>>> annotated_frame = label_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
label_annotator = sv.LabelAnnotator(text_position=sv.Position.CENTER)
|
||||
annotated_frame = label_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> blur_annotator = sv.BlurAnnotator()
|
||||
>>> annotated_frame = circle_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
blur_annotator = sv.BlurAnnotator()
|
||||
annotated_frame = circle_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
 to learn how to plug
|
||||
This annotator uses the `sv.Detections.tracker_id`. Read
|
||||
[here](/latest/trackers/) to learn how to plug
|
||||
tracking into your inference pipeline.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
color: Union[Color, ColorPalette] = ColorPalette.default(),
|
||||
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
|
||||
position: Position = Position.CENTER,
|
||||
trace_length: int = 30,
|
||||
thickness: int = 2,
|
||||
@@ -1071,26 +1151,25 @@ class TraceAnnotator:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
>>> from ultralytics import YOLO
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
>>> model = YOLO('yolov8x.pt')
|
||||
model = YOLO('yolov8x.pt')
|
||||
trace_annotator = sv.TraceAnnotator()
|
||||
|
||||
>>> trace_annotator = sv.TraceAnnotator()
|
||||
video_info = sv.VideoInfo.from_video_path(video_path='...')
|
||||
frames_generator = sv.get_video_frames_generator(source_path='...')
|
||||
tracker = sv.ByteTrack()
|
||||
|
||||
>>> video_info = sv.VideoInfo.from_video_path(video_path='...')
|
||||
>>> frames_generator = sv.get_video_frames_generator(source_path='...')
|
||||
>>> tracker = sv.ByteTrack()
|
||||
|
||||
>>> with sv.VideoSink(target_path='...', video_info=video_info) as sink:
|
||||
... for frame in frames_generator:
|
||||
... result = model(frame)[0]
|
||||
... detections = sv.Detections.from_ultralytics(result)
|
||||
... detections = tracker.update_with_detections(detections)
|
||||
... annotated_frame = trace_annotator.annotate(
|
||||
... scene=frame.copy(),
|
||||
... detections=detections)
|
||||
... sink.write_frame(frame=annotated_frame)
|
||||
with sv.VideoSink(target_path='...', video_info=video_info) as sink:
|
||||
for frame in frames_generator:
|
||||
result = model(frame)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
detections = tracker.update_with_detections(detections)
|
||||
annotated_frame = trace_annotator.annotate(
|
||||
scene=frame.copy(),
|
||||
detections=detections)
|
||||
sink.write_frame(frame=annotated_frame)
|
||||
```
|
||||
|
||||

|
||||
model = YOLO('yolov8x.pt')
|
||||
|
||||
>>> heat_map_annotator = sv.HeatMapAnnotator()
|
||||
heat_map_annotator = sv.HeatMapAnnotator()
|
||||
|
||||
>>> video_info = sv.VideoInfo.from_video_path(video_path='...')
|
||||
>>> frames_generator = get_video_frames_generator(source_path='...')
|
||||
video_info = sv.VideoInfo.from_video_path(video_path='...')
|
||||
frames_generator = get_video_frames_generator(source_path='...')
|
||||
|
||||
>>> with sv.VideoSink(target_path='...', video_info=video_info) as sink:
|
||||
... for frame in frames_generator:
|
||||
... result = model(frame)[0]
|
||||
... detections = sv.Detections.from_ultralytics(result)
|
||||
... annotated_frame = heat_map_annotator.annotate(
|
||||
... scene=frame.copy(),
|
||||
... detections=detections)
|
||||
... sink.write_frame(frame=annotated_frame)
|
||||
with sv.VideoSink(target_path='...', video_info=video_info) as sink:
|
||||
for frame in frames_generator:
|
||||
result = model(frame)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
annotated_frame = heat_map_annotator.annotate(
|
||||
scene=frame.copy(),
|
||||
detections=detections)
|
||||
sink.write_frame(frame=annotated_frame)
|
||||
```
|
||||
|
||||
:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> pixelate_annotator = sv.PixelateAnnotator()
|
||||
>>> annotated_frame = pixelate_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
pixelate_annotator = sv.PixelateAnnotator()
|
||||
annotated_frame = pixelate_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
color: Union[Color, ColorPalette] = ColorPalette.default(),
|
||||
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
|
||||
base: int = 10,
|
||||
height: int = 10,
|
||||
position: Position = Position.TOP_CENTER,
|
||||
@@ -1330,16 +1409,16 @@ class TriangleAnnotator(BaseAnnotator):
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> triangle_annotator = sv.TriangleAnnotator()
|
||||
>>> annotated_frame = triangle_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
triangle_annotator = sv.TriangleAnnotator()
|
||||
annotated_frame = triangle_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||
:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
color: Union[Color, ColorPalette] = ColorPalette.default(),
|
||||
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
|
||||
thickness: int = 2,
|
||||
color_lookup: ColorLookup = ColorLookup.CLASS,
|
||||
roundness: float = 0.6,
|
||||
@@ -1423,20 +1502,20 @@ class RoundBoxAnnotator(BaseAnnotator):
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> round_box_annotator = sv.RoundBoxAnnotator()
|
||||
>>> annotated_frame = round_box_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
round_box_annotator = sv.RoundBoxAnnotator()
|
||||
annotated_frame = round_box_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||

|
||||
supervision-annotator-examples/round-box-annotator-example-purple.png)
|
||||
"""
|
||||
|
||||
for detection_idx in range(len(detections)):
|
||||
@@ -1496,3 +1575,177 @@ class RoundBoxAnnotator(BaseAnnotator):
|
||||
)
|
||||
|
||||
return scene
|
||||
|
||||
|
||||
class PercentageBarAnnotator(BaseAnnotator):
|
||||
"""
|
||||
A class for drawing percentage bars on an image using provided detections.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
height: int = 16,
|
||||
width: int = 80,
|
||||
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
|
||||
border_color: Color = Color.BLACK,
|
||||
position: Position = Position.TOP_CENTER,
|
||||
color_lookup: ColorLookup = ColorLookup.CLASS,
|
||||
border_thickness: int = None,
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
height (int): The height in pixels of the percentage bar.
|
||||
width (int): The width in pixels of the percentage bar.
|
||||
color (Union[Color, ColorPalette]): The color or color palette to use for
|
||||
annotating detections.
|
||||
border_color (Color): The color of the border lines.
|
||||
position (Position): The anchor position of drawing the percentage bar.
|
||||
color_lookup (str): Strategy for mapping colors to annotations.
|
||||
Options are `INDEX`, `CLASS`, `TRACK`.
|
||||
border_thickness (int): The thickness of the border lines.
|
||||
"""
|
||||
self.height: int = height
|
||||
self.width: int = width
|
||||
self.color: Union[Color, ColorPalette] = color
|
||||
self.border_color: Color = border_color
|
||||
self.position: Position = position
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
if border_thickness is None:
|
||||
self.border_thickness = int(0.15 * self.height)
|
||||
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
custom_values: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
Annotates the given scene with percentage bars based on the provided
|
||||
detections. The percentage bars visually represent the confidence or custom
|
||||
values associated with each detection.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where percentage bars will be drawn.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
custom_values (Optional[np.ndarray]): Custom values array to use instead
|
||||
of the default detection confidences. This array should have the
|
||||
same length as the number of detections and contain a value between
|
||||
0 and 1 (inclusive) for each detection, representing the percentage
|
||||
to be displayed.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
|
||||
Example:
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
percentage_bar_annotator = sv.BoundingBoxAnnotator()
|
||||
annotated_frame = percentage_bar_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
)
|
||||
```
|
||||
|
||||

|
||||
"""
|
||||
self.validate_custom_values(
|
||||
custom_values=custom_values, detections_count=len(detections)
|
||||
)
|
||||
anchors = detections.get_anchors_coordinates(anchor=self.position)
|
||||
for detection_idx in range(len(detections)):
|
||||
anchor = anchors[detection_idx]
|
||||
border_coordinates = self.calculate_border_coordinates(
|
||||
anchor_xy=(int(anchor[0]), int(anchor[1])),
|
||||
border_wh=(self.width, self.height),
|
||||
position=self.position,
|
||||
)
|
||||
border_width = border_coordinates[1][0] - border_coordinates[0][0]
|
||||
|
||||
value = (
|
||||
custom_values[detection_idx]
|
||||
if custom_values is not None
|
||||
else detections.confidence[detection_idx]
|
||||
)
|
||||
|
||||
color = resolve_color(
|
||||
color=self.color,
|
||||
detections=detections,
|
||||
detection_idx=detection_idx,
|
||||
color_lookup=self.color_lookup
|
||||
if custom_color_lookup is None
|
||||
else custom_color_lookup,
|
||||
)
|
||||
cv2.rectangle(
|
||||
img=scene,
|
||||
pt1=border_coordinates[0],
|
||||
pt2=(
|
||||
border_coordinates[0][0] + int(border_width * value),
|
||||
border_coordinates[1][1],
|
||||
),
|
||||
color=color.as_bgr(),
|
||||
thickness=-1,
|
||||
)
|
||||
cv2.rectangle(
|
||||
img=scene,
|
||||
pt1=border_coordinates[0],
|
||||
pt2=border_coordinates[1],
|
||||
color=self.border_color.as_bgr(),
|
||||
thickness=self.border_thickness,
|
||||
)
|
||||
return scene
|
||||
|
||||
@staticmethod
|
||||
def calculate_border_coordinates(
|
||||
anchor_xy: Tuple[int, int], border_wh: Tuple[int, int], position: Position
|
||||
) -> Tuple[Tuple[int, int], Tuple[int, int]]:
|
||||
cx, cy = anchor_xy
|
||||
width, height = border_wh
|
||||
|
||||
if position == Position.TOP_LEFT:
|
||||
return (cx - width, cy - height), (cx, cy)
|
||||
elif position == Position.TOP_CENTER:
|
||||
return (cx - width // 2, cy), (cx + width // 2, cy - height)
|
||||
elif position == Position.TOP_RIGHT:
|
||||
return (cx, cy), (cx + width, cy - height)
|
||||
elif position == Position.CENTER_LEFT:
|
||||
return (cx - width, cy - height // 2), (cx, cy + height // 2)
|
||||
elif position == Position.CENTER or position == Position.CENTER_OF_MASS:
|
||||
return (
|
||||
(cx - width // 2, cy - height // 2),
|
||||
(cx + width // 2, cy + height // 2),
|
||||
)
|
||||
elif position == Position.CENTER_RIGHT:
|
||||
return (cx, cy - height // 2), (cx + width, cy + height // 2)
|
||||
elif position == Position.BOTTOM_LEFT:
|
||||
return (cx - width, cy), (cx, cy + height)
|
||||
elif position == Position.BOTTOM_CENTER:
|
||||
return (cx - width // 2, cy), (cx + width // 2, cy + height)
|
||||
elif position == Position.BOTTOM_RIGHT:
|
||||
return (cx, cy), (cx + width, cy + height)
|
||||
|
||||
@staticmethod
|
||||
def validate_custom_values(
|
||||
custom_values: Optional[Union[np.ndarray, List[float]]], detections_count: int
|
||||
) -> None:
|
||||
if custom_values is not None:
|
||||
if not isinstance(custom_values, (np.ndarray, list)):
|
||||
raise TypeError(
|
||||
"custom_values must be either a numpy array or a list of floats."
|
||||
)
|
||||
|
||||
if len(custom_values) != detections_count:
|
||||
raise ValueError(
|
||||
"The length of custom_values must match the number of detections."
|
||||
)
|
||||
|
||||
if not all(0 <= value <= 1 for value in custom_values):
|
||||
raise ValueError("All values in custom_values must be between 0 and 1.")
|
||||
|
||||
@@ -54,9 +54,9 @@ def download_assets(asset_name: Union[VideoAssets, str]) -> str:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> from supervision.assets import download_assets, VideoAssets
|
||||
from supervision.assets import download_assets, VideoAssets
|
||||
|
||||
>>> download_assets(VideoAssets.VEHICLES)
|
||||
download_assets(VideoAssets.VEHICLES)
|
||||
"vehicles.mp4"
|
||||
```
|
||||
"""
|
||||
|
||||
@@ -59,18 +59,18 @@ class Classifications:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> from PIL import Image
|
||||
>>> import clip
|
||||
>>> import supervision as sv
|
||||
from PIL import Image
|
||||
import clip
|
||||
import supervision as sv
|
||||
|
||||
>>> model, preprocess = clip.load('ViT-B/32')
|
||||
model, preprocess = clip.load('ViT-B/32')
|
||||
|
||||
>>> image = cv2.imread(SOURCE_IMAGE_PATH)
|
||||
>>> image = preprocess(image).unsqueeze(0)
|
||||
image = cv2.imread(SOURCE_IMAGE_PATH)
|
||||
image = preprocess(image).unsqueeze(0)
|
||||
|
||||
>>> text = clip.tokenize(["a diagram", "a dog", "a cat"])
|
||||
>>> output, _ = model(image, text)
|
||||
>>> classifications = sv.Classifications.from_clip(output)
|
||||
text = clip.tokenize(["a diagram", "a dog", "a cat"])
|
||||
output, _ = model(image, text)
|
||||
classifications = sv.Classifications.from_clip(output)
|
||||
```
|
||||
"""
|
||||
|
||||
@@ -97,15 +97,15 @@ class Classifications:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import cv2
|
||||
>>> from ultralytics import YOLO
|
||||
>>> import supervision as sv
|
||||
import cv2
|
||||
from ultralytics import YOLO
|
||||
import supervision as sv
|
||||
|
||||
>>> image = cv2.imread(SOURCE_IMAGE_PATH)
|
||||
>>> model = YOLO('yolov8n-cls.pt')
|
||||
image = cv2.imread(SOURCE_IMAGE_PATH)
|
||||
model = YOLO('yolov8n-cls.pt')
|
||||
|
||||
>>> output = model(image)[0]
|
||||
>>> classifications = sv.Classifications.from_ultralytics(output)
|
||||
output = model(image)[0]
|
||||
classifications = sv.Classifications.from_ultralytics(output)
|
||||
```
|
||||
"""
|
||||
confidence = ultralytics_results.probs.data.cpu().numpy()
|
||||
@@ -118,32 +118,32 @@ 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.
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> from PIL import Image
|
||||
>>> import timm
|
||||
>>> from timm.data import resolve_data_config, create_transform
|
||||
>>> import supervision as sv
|
||||
from PIL import Image
|
||||
import timm
|
||||
from timm.data import resolve_data_config, create_transform
|
||||
import supervision as sv
|
||||
|
||||
>>> model = timm.create_model(
|
||||
... model_name='hf-hub:nateraw/resnet50-oxford-iiit-pet',
|
||||
... pretrained=True
|
||||
... ).eval()
|
||||
model = timm.create_model(
|
||||
model_name='hf-hub:nateraw/resnet50-oxford-iiit-pet',
|
||||
pretrained=True
|
||||
).eval()
|
||||
|
||||
>>> config = resolve_data_config({}, model=model)
|
||||
>>> transform = create_transform(**config)
|
||||
config = resolve_data_config({}, model=model)
|
||||
transform = create_transform(**config)
|
||||
|
||||
>>> image = Image.open(SOURCE_IMAGE_PATH).convert('RGB')
|
||||
>>> x = transform(image).unsqueeze(0)
|
||||
image = Image.open(SOURCE_IMAGE_PATH).convert('RGB')
|
||||
x = transform(image).unsqueeze(0)
|
||||
|
||||
>>> output = model(x)
|
||||
output = model(x)
|
||||
|
||||
>>> classifications = sv.Classifications.from_timm(output)
|
||||
classifications = sv.Classifications.from_timm(output)
|
||||
```
|
||||
"""
|
||||
confidence = timm_results.cpu().detach().numpy()[0]
|
||||
@@ -168,11 +168,11 @@ class Classifications:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> classifications = sv.Classifications(...)
|
||||
classifications = sv.Classifications(...)
|
||||
|
||||
>>> classifications.get_top_k(1)
|
||||
classifications.get_top_k(1)
|
||||
|
||||
(array([1]), array([0.9]))
|
||||
```
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
CLASS_NAME_DATA_FIELD = "class_name"
|
||||
ORIENTED_BOX_COORDINATES = "xyxyxyxy"
|
||||
|
||||
+78
-81
@@ -118,13 +118,13 @@ class DetectionDataset(BaseDataset):
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> ds = sv.DetectionDataset(...)
|
||||
>>> train_ds, test_ds = ds.split(split_ratio=0.7,
|
||||
... random_state=42, shuffle=True)
|
||||
>>> len(train_ds), len(test_ds)
|
||||
(700, 300)
|
||||
ds = sv.DetectionDataset(...)
|
||||
train_ds, test_ds = ds.split(split_ratio=0.7,
|
||||
random_state=42, shuffle=True)
|
||||
len(train_ds), len(test_ds)
|
||||
# (700, 300)
|
||||
```
|
||||
"""
|
||||
|
||||
@@ -231,24 +231,24 @@ class DetectionDataset(BaseDataset):
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import roboflow
|
||||
>>> from roboflow import Roboflow
|
||||
>>> import supervision as sv
|
||||
import roboflow
|
||||
from roboflow import Roboflow
|
||||
import supervision as sv
|
||||
|
||||
>>> roboflow.login()
|
||||
roboflow.login()
|
||||
|
||||
>>> rf = Roboflow()
|
||||
rf = Roboflow()
|
||||
|
||||
>>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
|
||||
>>> dataset = project.version(PROJECT_VERSION).download("voc")
|
||||
project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
|
||||
dataset = project.version(PROJECT_VERSION).download("voc")
|
||||
|
||||
>>> ds = sv.DetectionDataset.from_pascal_voc(
|
||||
... images_directory_path=f"{dataset.location}/train/images",
|
||||
... annotations_directory_path=f"{dataset.location}/train/labels"
|
||||
... )
|
||||
ds = sv.DetectionDataset.from_pascal_voc(
|
||||
images_directory_path=f"{dataset.location}/train/images",
|
||||
annotations_directory_path=f"{dataset.location}/train/labels"
|
||||
)
|
||||
|
||||
>>> ds.classes
|
||||
['dog', 'person']
|
||||
ds.classes
|
||||
# ['dog', 'person']
|
||||
```
|
||||
"""
|
||||
|
||||
@@ -288,25 +288,24 @@ class DetectionDataset(BaseDataset):
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import roboflow
|
||||
>>> from roboflow import Roboflow
|
||||
>>> import supervision as sv
|
||||
import roboflow
|
||||
from roboflow import Roboflow
|
||||
import supervision as sv
|
||||
|
||||
>>> roboflow.login()
|
||||
roboflow.login()
|
||||
rf = Roboflow()
|
||||
|
||||
>>> rf = Roboflow()
|
||||
project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
|
||||
dataset = project.version(PROJECT_VERSION).download("yolov5")
|
||||
|
||||
>>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
|
||||
>>> dataset = project.version(PROJECT_VERSION).download("yolov5")
|
||||
ds = sv.DetectionDataset.from_yolo(
|
||||
images_directory_path=f"{dataset.location}/train/images",
|
||||
annotations_directory_path=f"{dataset.location}/train/labels",
|
||||
data_yaml_path=f"{dataset.location}/data.yaml"
|
||||
)
|
||||
|
||||
>>> ds = sv.DetectionDataset.from_yolo(
|
||||
... images_directory_path=f"{dataset.location}/train/images",
|
||||
... annotations_directory_path=f"{dataset.location}/train/labels",
|
||||
... data_yaml_path=f"{dataset.location}/data.yaml"
|
||||
... )
|
||||
|
||||
>>> ds.classes
|
||||
['dog', 'person']
|
||||
ds.classes
|
||||
# ['dog', 'person']
|
||||
```
|
||||
"""
|
||||
classes, images, annotations = load_yolo_annotations(
|
||||
@@ -394,24 +393,23 @@ class DetectionDataset(BaseDataset):
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import roboflow
|
||||
>>> from roboflow import Roboflow
|
||||
>>> import supervision as sv
|
||||
import roboflow
|
||||
from roboflow import Roboflow
|
||||
import supervision as sv
|
||||
|
||||
>>> roboflow.login()
|
||||
roboflow.login()
|
||||
rf = Roboflow()
|
||||
|
||||
>>> rf = Roboflow()
|
||||
project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
|
||||
dataset = project.version(PROJECT_VERSION).download("coco")
|
||||
|
||||
>>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
|
||||
>>> dataset = project.version(PROJECT_VERSION).download("coco")
|
||||
ds = sv.DetectionDataset.from_coco(
|
||||
images_directory_path=f"{dataset.location}/train",
|
||||
annotations_path=f"{dataset.location}/train/_annotations.coco.json",
|
||||
)
|
||||
|
||||
>>> ds = sv.DetectionDataset.from_coco(
|
||||
... images_directory_path=f"{dataset.location}/train",
|
||||
... annotations_path=f"{dataset.location}/train/_annotations.coco.json",
|
||||
... )
|
||||
|
||||
>>> ds.classes
|
||||
['dog', 'person']
|
||||
ds.classes
|
||||
# ['dog', 'person']
|
||||
```
|
||||
"""
|
||||
classes, images, annotations = load_coco_annotations(
|
||||
@@ -486,25 +484,25 @@ class DetectionDataset(BaseDataset):
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> ds_1 = sv.DetectionDataset(...)
|
||||
>>> len(ds_1)
|
||||
100
|
||||
>>> ds_1.classes
|
||||
['dog', 'person']
|
||||
ds_1 = sv.DetectionDataset(...)
|
||||
len(ds_1)
|
||||
# 100
|
||||
ds_1.classes
|
||||
# ['dog', 'person']
|
||||
|
||||
>>> ds_2 = sv.DetectionDataset(...)
|
||||
>>> len(ds_2)
|
||||
200
|
||||
>>> ds_2.classes
|
||||
['cat']
|
||||
ds_2 = sv.DetectionDataset(...)
|
||||
len(ds_2)
|
||||
# 200
|
||||
ds_2.classes
|
||||
# ['cat']
|
||||
|
||||
>>> ds_merged = sv.DetectionDataset.merge([ds_1, ds_2])
|
||||
>>> len(ds_merged)
|
||||
300
|
||||
>>> ds_merged.classes
|
||||
['cat', 'dog', 'person']
|
||||
ds_merged = sv.DetectionDataset.merge([ds_1, ds_2])
|
||||
len(ds_merged)
|
||||
# 300
|
||||
ds_merged.classes
|
||||
# ['cat', 'dog', 'person']
|
||||
```
|
||||
"""
|
||||
merged_images, merged_annotations = {}, {}
|
||||
@@ -571,13 +569,13 @@ class ClassificationDataset(BaseDataset):
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> cd = sv.ClassificationDataset(...)
|
||||
>>> train_cd,test_cd = cd.split(split_ratio=0.7,
|
||||
... random_state=42,shuffle=True)
|
||||
>>> len(train_cd), len(test_cd)
|
||||
(700, 300)
|
||||
cd = sv.ClassificationDataset(...)
|
||||
train_cd,test_cd = cd.split(split_ratio=0.7,
|
||||
random_state=42,shuffle=True)
|
||||
len(train_cd), len(test_cd)
|
||||
# (700, 300)
|
||||
```
|
||||
"""
|
||||
image_names = list(self.images.keys())
|
||||
@@ -639,20 +637,19 @@ class ClassificationDataset(BaseDataset):
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import roboflow
|
||||
>>> from roboflow import Roboflow
|
||||
>>> import supervision as sv
|
||||
import roboflow
|
||||
from roboflow import Roboflow
|
||||
import supervision as sv
|
||||
|
||||
>>> roboflow.login()
|
||||
roboflow.login()
|
||||
rf = Roboflow()
|
||||
|
||||
>>> rf = Roboflow()
|
||||
project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
|
||||
dataset = project.version(PROJECT_VERSION).download("folder")
|
||||
|
||||
>>> project = rf.workspace(WORKSPACE_ID).project(PROJECT_ID)
|
||||
>>> dataset = project.version(PROJECT_VERSION).download("folder")
|
||||
|
||||
>>> cd = sv.ClassificationDataset.from_folder_structure(
|
||||
... root_directory_path=f"{dataset.location}/train"
|
||||
... )
|
||||
cd = sv.ClassificationDataset.from_folder_structure(
|
||||
root_directory_path=f"{dataset.location}/train"
|
||||
)
|
||||
```
|
||||
"""
|
||||
classes = os.listdir(root_directory_path)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -5,8 +5,13 @@ import numpy as np
|
||||
|
||||
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.
|
||||
@@ -26,9 +31,9 @@ class BoxAnnotator:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
color: Union[Color, ColorPalette] = ColorPalette.default(),
|
||||
color: Union[Color, ColorPalette] = ColorPalette.DEFAULT,
|
||||
thickness: int = 2,
|
||||
text_color: Color = Color.black(),
|
||||
text_color: Color = Color.BLACK,
|
||||
text_scale: float = 0.5,
|
||||
text_thickness: int = 1,
|
||||
text_padding: int = 10,
|
||||
@@ -63,23 +68,22 @@ class BoxAnnotator:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> classes = ['person', ...]
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
classes = ['person', ...]
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
>>> box_annotator = sv.BoxAnnotator()
|
||||
>>> labels = [
|
||||
... f"{classes[class_id]} {confidence:0.2f}"
|
||||
... for _, _, confidence, class_id, _
|
||||
... in detections
|
||||
... ]
|
||||
>>> annotated_frame = box_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections,
|
||||
... labels=labels
|
||||
... )
|
||||
box_annotator = sv.BoxAnnotator()
|
||||
labels = [
|
||||
f"{classes[class_id]} {confidence:0.2f}"
|
||||
for _, _, confidence, class_id, _ in detections
|
||||
]
|
||||
annotated_frame = box_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections,
|
||||
labels=labels
|
||||
)
|
||||
```
|
||||
"""
|
||||
font = cv2.FONT_HERSHEY_SIMPLEX
|
||||
|
||||
+220
-157
@@ -6,6 +6,7 @@ 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 (
|
||||
calculate_masks_centroids,
|
||||
extract_ultralytics_masks,
|
||||
@@ -24,7 +25,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
|
||||
@@ -126,14 +154,14 @@ class Detections:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import cv2
|
||||
>>> import torch
|
||||
>>> import supervision as sv
|
||||
import cv2
|
||||
import torch
|
||||
import supervision as sv
|
||||
|
||||
>>> image = cv2.imread(SOURCE_IMAGE_PATH)
|
||||
>>> model = torch.hub.load('ultralytics/yolov5', 'yolov5s')
|
||||
>>> result = model(image)
|
||||
>>> detections = sv.Detections.from_yolov5(result)
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
model = torch.hub.load('ultralytics/yolov5', 'yolov5s')
|
||||
result = model(image)
|
||||
detections = sv.Detections.from_yolov5(result)
|
||||
```
|
||||
"""
|
||||
yolov5_detections_predictions = yolov5_results.pred[0].cpu().cpu().numpy()
|
||||
@@ -150,6 +178,13 @@ class Detections:
|
||||
Creates a Detections instance from a
|
||||
[YOLOv8](https://github.com/ultralytics/ultralytics) inference result.
|
||||
|
||||
!!! Note
|
||||
|
||||
`from_ultralytics` is compatible with
|
||||
[detection](https://docs.ultralytics.com/tasks/detect/),
|
||||
[segmentation](https://docs.ultralytics.com/tasks/segment/), and
|
||||
[OBB](https://docs.ultralytics.com/tasks/obb/) models.
|
||||
|
||||
Args:
|
||||
ultralytics_results (ultralytics.yolo.engine.results.Results):
|
||||
The output Results instance from YOLOv8
|
||||
@@ -159,25 +194,46 @@ class Detections:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import cv2
|
||||
>>> import supervision as sv
|
||||
>>> from ultralytics import YOLO
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
>>> image = cv2.imread(...)
|
||||
>>> model = YOLO('yolov8s.pt')
|
||||
>>> result = model(image)[0]
|
||||
>>> detections = sv.Detections.from_ultralytics(result)
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
model = YOLO('yolov8s.pt')
|
||||
|
||||
result = model(image)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
```
|
||||
"""
|
||||
""" # noqa: E501 // docs
|
||||
|
||||
if ultralytics_results.obb is not None:
|
||||
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(),
|
||||
confidence=ultralytics_results.obb.conf.cpu().numpy(),
|
||||
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
|
||||
@@ -198,14 +254,15 @@ class Detections:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import cv2
|
||||
>>> from super_gradients.training import models
|
||||
>>> import supervision as sv
|
||||
import cv2
|
||||
from super_gradients.training import models
|
||||
import supervision as sv
|
||||
|
||||
>>> image = cv2.imread(SOURCE_IMAGE_PATH)
|
||||
>>> model = models.get('yolo_nas_l', pretrained_weights="coco")
|
||||
>>> result = list(model.predict(image, conf=0.35))[0]
|
||||
>>> detections = sv.Detections.from_yolo_nas(result)
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
model = models.get('yolo_nas_l', pretrained_weights="coco")
|
||||
|
||||
result = list(model.predict(image, conf=0.35))[0]
|
||||
detections = sv.Detections.from_yolo_nas(result)
|
||||
```
|
||||
"""
|
||||
if np.asarray(yolo_nas_results.prediction.bboxes_xyxy).shape[0] == 0:
|
||||
@@ -235,20 +292,16 @@ class Detections:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import tensorflow as tf
|
||||
>>> import tensorflow_hub as hub
|
||||
>>> import numpy as np
|
||||
>>> import cv2
|
||||
import tensorflow as tf
|
||||
import tensorflow_hub as hub
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
>>> module_handle = "https://tfhub.dev/tensorflow/centernet/hourglass_512x512_kpts/1"
|
||||
|
||||
>>> model = hub.load(module_handle)
|
||||
|
||||
>>> img = np.array(cv2.imread(SOURCE_IMAGE_PATH))
|
||||
|
||||
>>> result = model(img)
|
||||
|
||||
>>> detections = sv.Detections.from_tensorflow(result)
|
||||
module_handle = "https://tfhub.dev/tensorflow/centernet/hourglass_512x512_kpts/1"
|
||||
model = hub.load(module_handle)
|
||||
img = np.array(cv2.imread(SOURCE_IMAGE_PATH))
|
||||
result = model(img)
|
||||
detections = sv.Detections.from_tensorflow(result)
|
||||
```
|
||||
""" # noqa: E501 // docs
|
||||
|
||||
@@ -278,15 +331,15 @@ class Detections:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
>>> from deepsparse import Pipeline
|
||||
import supervision as sv
|
||||
from deepsparse import Pipeline
|
||||
|
||||
>>> yolo_pipeline = Pipeline.create(
|
||||
... task="yolo",
|
||||
... model_path = "zoo:cv/detection/yolov5-l/pytorch/ultralytics/coco/pruned80_quant-none"
|
||||
... )
|
||||
>>> result = yolo_pipeline(<SOURCE IMAGE PATH>)
|
||||
>>> detections = sv.Detections.from_deepsparse(result)
|
||||
yolo_pipeline = Pipeline.create(
|
||||
task="yolo",
|
||||
model_path = "zoo:cv/detection/yolov5-l/pytorch/ultralytics/coco/pruned80_quant-none"
|
||||
)
|
||||
result = yolo_pipeline(<SOURCE IMAGE PATH>)
|
||||
detections = sv.Detections.from_deepsparse(result)
|
||||
```
|
||||
""" # noqa: E501 // docs
|
||||
|
||||
@@ -302,9 +355,9 @@ class Detections:
|
||||
@classmethod
|
||||
def from_mmdetection(cls, mmdet_results) -> Detections:
|
||||
"""
|
||||
Creates a Detections instance from
|
||||
a [mmdetection](https://github.com/open-mmlab/mmdetection) inference result.
|
||||
Also supported for [mmyolo](https://github.com/open-mmlab/mmyolo)
|
||||
Creates a Detections instance from a
|
||||
[mmdetection](https://github.com/open-mmlab/mmdetection) and
|
||||
[mmyolo](https://github.com/open-mmlab/mmyolo) inference result.
|
||||
|
||||
Args:
|
||||
mmdet_results (mmdet.structures.DetDataSample):
|
||||
@@ -315,16 +368,17 @@ class Detections:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import cv2
|
||||
>>> import supervision as sv
|
||||
>>> from mmdet.apis import DetInferencer
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from mmdet.apis import init_detector, inference_detector
|
||||
|
||||
>>> inferencer = DetInferencer(model_name, checkpoint, device)
|
||||
>>> mmdet_result = inferencer(SOURCE_IMAGE_PATH, out_dir='./output',
|
||||
... return_datasamples=True)["predictions"][0]
|
||||
>>> detections = sv.Detections.from_mmdetection(mmdet_result)
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
model = init_detector(<CONFIG_PATH>, <WEIGHTS_PATH>, device=<DEVICE>)
|
||||
|
||||
result = inference_detector(model, image)
|
||||
detections = sv.Detections.from_mmdetection(result)
|
||||
```
|
||||
"""
|
||||
""" # noqa: E501 // docs
|
||||
|
||||
return cls(
|
||||
xyxy=mmdet_results.pred_instances.bboxes.cpu().numpy(),
|
||||
@@ -364,18 +418,20 @@ class Detections:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import cv2
|
||||
>>> from detectron2.engine import DefaultPredictor
|
||||
>>> from detectron2.config import get_cfg
|
||||
>>> import supervision as sv
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from detectron2.engine import DefaultPredictor
|
||||
from detectron2.config import get_cfg
|
||||
|
||||
>>> image = cv2.imread(SOURCE_IMAGE_PATH)
|
||||
>>> cfg = get_cfg()
|
||||
>>> cfg.merge_from_file("path/to/config.yaml")
|
||||
>>> cfg.MODEL.WEIGHTS = "path/to/model_weights.pth"
|
||||
>>> predictor = DefaultPredictor(cfg)
|
||||
>>> result = predictor(image)
|
||||
>>> detections = sv.Detections.from_detectron2(result)
|
||||
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
cfg = get_cfg()
|
||||
cfg.merge_from_file(<CONFIG_PATH>)
|
||||
cfg.MODEL.WEIGHTS = <WEIGHTS_PATH>
|
||||
predictor = DefaultPredictor(cfg)
|
||||
|
||||
result = predictor(image)
|
||||
detections = sv.Detections.from_detectron2(result)
|
||||
```
|
||||
"""
|
||||
|
||||
@@ -412,14 +468,15 @@ class Detections:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import cv2
|
||||
>>> import supervision as sv
|
||||
>>> from inference.models.utils import get_roboflow_model
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from inference.models.utils import get_roboflow_model
|
||||
|
||||
>>> image = cv2.imread(...)
|
||||
>>> model = get_roboflow_model(model_id="yolov8s-640")
|
||||
>>> result = model.infer(image)[0]
|
||||
>>> detections = sv.Detections.from_inference(result)
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
model = get_roboflow_model(model_id="yolov8s-640")
|
||||
|
||||
result = model.infer(image)[0]
|
||||
detections = sv.Detections.from_inference(result)
|
||||
```
|
||||
"""
|
||||
with suppress(AttributeError):
|
||||
@@ -443,10 +500,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.
|
||||
@@ -461,14 +523,15 @@ class Detections:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import cv2
|
||||
>>> import supervision as sv
|
||||
>>> from inference.models.utils import get_roboflow_model
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from inference.models.utils import get_roboflow_model
|
||||
|
||||
>>> image = cv2.imread(...)
|
||||
>>> model = get_roboflow_model(model_id="yolov8s-640")
|
||||
>>> result = model.infer(image)[0]
|
||||
>>> detections = sv.Detections.from_roboflow(result)
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
model = get_roboflow_model(model_id="yolov8s-640")
|
||||
|
||||
result = model.infer(image)[0]
|
||||
detections = sv.Detections.from_roboflow(result)
|
||||
```
|
||||
"""
|
||||
return cls.from_inference(roboflow_result)
|
||||
@@ -488,17 +551,17 @@ class Detections:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
>>> from segment_anything import (
|
||||
... sam_model_registry,
|
||||
... SamAutomaticMaskGenerator
|
||||
... )
|
||||
import supervision as sv
|
||||
from segment_anything import (
|
||||
sam_model_registry,
|
||||
SamAutomaticMaskGenerator
|
||||
)
|
||||
|
||||
>>> sam_model_reg = sam_model_registry[MODEL_TYPE]
|
||||
>>> sam = sam_model_reg(checkpoint=CHECKPOINT_PATH).to(device=DEVICE)
|
||||
>>> mask_generator = SamAutomaticMaskGenerator(sam)
|
||||
>>> sam_result = mask_generator.generate(IMAGE)
|
||||
>>> detections = sv.Detections.from_sam(sam_result=sam_result)
|
||||
sam_model_reg = sam_model_registry[MODEL_TYPE]
|
||||
sam = sam_model_reg(checkpoint=CHECKPOINT_PATH).to(device=DEVICE)
|
||||
mask_generator = SamAutomaticMaskGenerator(sam)
|
||||
sam_result = mask_generator.generate(IMAGE)
|
||||
detections = sv.Detections.from_sam(sam_result=sam_result)
|
||||
```
|
||||
"""
|
||||
|
||||
@@ -535,25 +598,25 @@ class Detections:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import requests
|
||||
>>> import supervision as sv
|
||||
import requests
|
||||
import supervision as sv
|
||||
|
||||
>>> image = open(input, "rb").read()
|
||||
image = open(input, "rb").read()
|
||||
|
||||
>>> endpoint = "https://.cognitiveservices.azure.com/"
|
||||
>>> subscription_key = "..."
|
||||
endpoint = "https://.cognitiveservices.azure.com/"
|
||||
subscription_key = ""
|
||||
|
||||
>>> headers = {
|
||||
... "Content-Type": "application/octet-stream",
|
||||
... "Ocp-Apim-Subscription-Key": subscription_key
|
||||
... }
|
||||
headers = {
|
||||
"Content-Type": "application/octet-stream",
|
||||
"Ocp-Apim-Subscription-Key": subscription_key
|
||||
}
|
||||
|
||||
>>> response = requests.post(endpoint,
|
||||
... headers=self.headers,
|
||||
... data=image
|
||||
... ).json()
|
||||
response = requests.post(endpoint,
|
||||
headers=self.headers,
|
||||
data=image
|
||||
).json()
|
||||
|
||||
>>> detections = sv.Detections.from_azure_analyze_image(response)
|
||||
detections = sv.Detections.from_azure_analyze_image(response)
|
||||
```
|
||||
"""
|
||||
if "error" in azure_result:
|
||||
@@ -617,21 +680,21 @@ class Detections:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
>>> import paddle
|
||||
>>> from ppdet.engine import Trainer
|
||||
>>> from ppdet.core.workspace import load_config
|
||||
import supervision as sv
|
||||
import paddle
|
||||
from ppdet.engine import Trainer
|
||||
from ppdet.core.workspace import load_config
|
||||
|
||||
>>> weights = (...)
|
||||
>>> config = (...)
|
||||
weights = ()
|
||||
config = ()
|
||||
|
||||
>>> cfg = load_config(config)
|
||||
>>> trainer = Trainer(cfg, mode='test')
|
||||
>>> trainer.load_weights(weights)
|
||||
cfg = load_config(config)
|
||||
trainer = Trainer(cfg, mode='test')
|
||||
trainer.load_weights(weights)
|
||||
|
||||
>>> paddledet_result = trainer.predict([images])[0]
|
||||
paddledet_result = trainer.predict([images])[0]
|
||||
|
||||
>>> detections = sv.Detections.from_paddledet(paddledet_result)
|
||||
detections = sv.Detections.from_paddledet(paddledet_result)
|
||||
```
|
||||
"""
|
||||
|
||||
@@ -655,9 +718,9 @@ class Detections:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> from supervision import Detections
|
||||
from supervision import Detections
|
||||
|
||||
>>> empty_detections = Detections.empty()
|
||||
empty_detections = Detections.empty()
|
||||
```
|
||||
"""
|
||||
return cls(
|
||||
@@ -689,29 +752,29 @@ class Detections:
|
||||
import numpy as np
|
||||
import supervision as sv
|
||||
|
||||
>>> detections_1 = sv.Detections(
|
||||
... xyxy=np.array([[15, 15, 100, 100], [200, 200, 300, 300]]),
|
||||
... class_id=np.array([1, 2]),
|
||||
... data={'feature_vector': np.array([0.1, 0.2)])}
|
||||
... )
|
||||
detections_1 = sv.Detections(
|
||||
xyxy=np.array([[15, 15, 100, 100], [200, 200, 300, 300]]),
|
||||
class_id=np.array([1, 2]),
|
||||
data={'feature_vector': np.array([0.1, 0.2)])}
|
||||
)
|
||||
|
||||
>>> detections_2 = sv.Detections(
|
||||
... xyxy=np.array([[30, 30, 120, 120]]),
|
||||
... class_id=np.array([1]),
|
||||
... data={'feature_vector': [np.array([0.3])]}
|
||||
... )
|
||||
detections_2 = sv.Detections(
|
||||
xyxy=np.array([[30, 30, 120, 120]]),
|
||||
class_id=np.array([1]),
|
||||
data={'feature_vector': [np.array([0.3])]}
|
||||
)
|
||||
|
||||
>>> merged_detections = Detections.merge([detections_1, detections_2])
|
||||
merged_detections = Detections.merge([detections_1, detections_2])
|
||||
|
||||
>>> merged_detections.xyxy
|
||||
merged_detections.xyxy
|
||||
array([[ 15, 15, 100, 100],
|
||||
[200, 200, 300, 300],
|
||||
[ 30, 30, 120, 120]])
|
||||
|
||||
>>> merged_detections.class_id
|
||||
merged_detections.class_id
|
||||
array([1, 2, 1])
|
||||
|
||||
>>> merged_detections.data['feature_vector']
|
||||
merged_detections.data['feature_vector']
|
||||
array([0.1, 0.2, 0.3])
|
||||
```
|
||||
"""
|
||||
@@ -844,17 +907,17 @@ class Detections:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> detections = sv.Detections(...)
|
||||
detections = sv.Detections()
|
||||
|
||||
>>> first_detection = detections[0]
|
||||
>>> first_10_detections = detections[0:10]
|
||||
>>> some_detections = detections[[0, 2, 4]]
|
||||
>>> class_0_detections = detections[detections.class_id == 0]
|
||||
>>> high_confidence_detections = detections[detections.confidence > 0.5]
|
||||
first_detection = detections[0]
|
||||
first_10_detections = detections[0:10]
|
||||
some_detections = detections[[0, 2, 4]]
|
||||
class_0_detections = detections[detections.class_id == 0]
|
||||
high_confidence_detections = detections[detections.confidence > 0.5]
|
||||
|
||||
>>> feature_vector = detections['feature_vector']
|
||||
feature_vector = detections['feature_vector']
|
||||
```
|
||||
"""
|
||||
if isinstance(index, str):
|
||||
@@ -880,22 +943,21 @@ class Detections:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import cv2
|
||||
>>> from ultralytics import YOLO
|
||||
>>> import supervision as sv
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
>>> model = YOLO('yolov8s.pt')
|
||||
image = cv2.imread(<SOURCE_IMAGE_PATH>)
|
||||
model = YOLO('yolov8s.pt')
|
||||
|
||||
>>> image = cv2.imread(SOURCE_IMAGE_PATH)
|
||||
result = model(image)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
|
||||
>>> result = model(image)[0]
|
||||
>>> detections = sv.Detections.from_ultralytics(result)
|
||||
|
||||
>>> detections['names'] = [
|
||||
... model.model.names[class_id]
|
||||
... for class_id
|
||||
... in detections.class_id
|
||||
... ]
|
||||
detections['names'] = [
|
||||
model.model.names[class_id]
|
||||
for class_id
|
||||
in detections.class_id
|
||||
]
|
||||
```
|
||||
"""
|
||||
if not isinstance(value, (np.ndarray, list)):
|
||||
@@ -915,7 +977,7 @@ class Detections:
|
||||
|
||||
Returns:
|
||||
np.ndarray: An array of floats containing the area of each detection
|
||||
in the format of `(area_1, area_2, ..., area_n)`,
|
||||
in the format of `(area_1, area_2, , area_n)`,
|
||||
where n is the number of detections.
|
||||
"""
|
||||
if self.mask is not None:
|
||||
@@ -930,7 +992,7 @@ class Detections:
|
||||
|
||||
Returns:
|
||||
np.ndarray: An array of floats containing the area of each bounding
|
||||
box in the format of `(area_1, area_2, ..., area_n)`,
|
||||
box in the format of `(area_1, area_2, , area_n)`,
|
||||
where n is the number of detections.
|
||||
"""
|
||||
return (self.xyxy[:, 3] - self.xyxy[:, 1]) * (self.xyxy[:, 2] - self.xyxy[:, 0])
|
||||
@@ -943,7 +1005,8 @@ class Detections:
|
||||
|
||||
Args:
|
||||
threshold (float, optional): The intersection-over-union threshold
|
||||
to use for non-maximum suppression. Defaults to 0.5.
|
||||
to use for non-maximum suppression. I'm the lower the value the more
|
||||
restrictive the NMS becomes. Defaults to 0.5.
|
||||
class_agnostic (bool, optional): Whether to perform class-agnostic
|
||||
non-maximum suppression. If True, the class_id of each detection
|
||||
will be ignored. Defaults to False.
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
from typing import Dict, Optional, Tuple
|
||||
from typing import Dict, Iterable, Optional, Tuple
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from supervision.detection.core import Detections
|
||||
from supervision.draw.color import Color
|
||||
from supervision.geometry.core import Point, Rect, Vector
|
||||
from supervision.draw.utils import draw_text
|
||||
from supervision.geometry.core import Point, Position, Vector
|
||||
|
||||
|
||||
class LineZone:
|
||||
@@ -15,8 +16,8 @@ class LineZone:
|
||||
|
||||
!!! warning
|
||||
|
||||
LineZone utilizes the `tracker_id`. Read
|
||||
[here](https://supervision.roboflow.com/trackers/) to learn how to plug
|
||||
LineZone uses the `tracker_id`. Read
|
||||
[here](/latest/trackers/) to learn how to plug
|
||||
tracking into your inference pipeline.
|
||||
|
||||
Attributes:
|
||||
@@ -26,16 +27,71 @@ class LineZone:
|
||||
to outside.
|
||||
"""
|
||||
|
||||
def __init__(self, start: Point, end: Point):
|
||||
def __init__(
|
||||
self,
|
||||
start: Point,
|
||||
end: Point,
|
||||
triggering_anchors: Iterable[Position] = (
|
||||
Position.TOP_LEFT,
|
||||
Position.TOP_RIGHT,
|
||||
Position.BOTTOM_LEFT,
|
||||
Position.BOTTOM_RIGHT,
|
||||
),
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
start (Point): The starting point of the line.
|
||||
end (Point): The ending point of the line.
|
||||
triggering_anchors (List[sv.Position]): A list of positions
|
||||
specifying which anchors of the detections bounding box
|
||||
to consider when deciding on whether the detection
|
||||
has passed the line counter or not. By default, this
|
||||
contains the four corners of the detection's bounding box
|
||||
"""
|
||||
self.vector = Vector(start=start, end=end)
|
||||
self.limits = self.calculate_region_of_interest_limits(vector=self.vector)
|
||||
self.tracker_state: Dict[str, bool] = {}
|
||||
self.in_count: int = 0
|
||||
self.out_count: int = 0
|
||||
self.triggering_anchors = triggering_anchors
|
||||
|
||||
@staticmethod
|
||||
def calculate_region_of_interest_limits(vector: Vector) -> Tuple[Vector, Vector]:
|
||||
magnitude = vector.magnitude
|
||||
|
||||
if magnitude == 0:
|
||||
raise ValueError("The magnitude of the vector cannot be zero.")
|
||||
|
||||
delta_x = vector.end.x - vector.start.x
|
||||
delta_y = vector.end.y - vector.start.y
|
||||
|
||||
unit_vector_x = delta_x / magnitude
|
||||
unit_vector_y = delta_y / magnitude
|
||||
|
||||
perpendicular_vector_x = -unit_vector_y
|
||||
perpendicular_vector_y = unit_vector_x
|
||||
|
||||
start_region_limit = Vector(
|
||||
start=vector.start,
|
||||
end=Point(
|
||||
x=vector.start.x + perpendicular_vector_x,
|
||||
y=vector.start.y + perpendicular_vector_y,
|
||||
),
|
||||
)
|
||||
end_region_limit = Vector(
|
||||
start=vector.end,
|
||||
end=Point(
|
||||
x=vector.end.x - perpendicular_vector_x,
|
||||
y=vector.end.y - perpendicular_vector_y,
|
||||
),
|
||||
)
|
||||
return start_region_limit, end_region_limit
|
||||
|
||||
@staticmethod
|
||||
def is_point_in_limits(point: Point, limits: Tuple[Vector, Vector]) -> bool:
|
||||
cross_product_1 = limits[0].cross_product(point)
|
||||
cross_product_2 = limits[1].cross_product(point)
|
||||
return (cross_product_1 > 0) == (cross_product_2 > 0)
|
||||
|
||||
def trigger(self, detections: Detections) -> Tuple[np.ndarray, np.ndarray]:
|
||||
"""
|
||||
@@ -54,20 +110,35 @@ class LineZone:
|
||||
crossed_in = np.full(len(detections), False)
|
||||
crossed_out = np.full(len(detections), False)
|
||||
|
||||
for i, (xyxy, tracker_id) in enumerate(
|
||||
zip(detections.xyxy, detections.tracker_id)
|
||||
):
|
||||
if len(detections) == 0:
|
||||
return crossed_in, crossed_out
|
||||
|
||||
all_anchors = np.array(
|
||||
[
|
||||
detections.get_anchors_coordinates(anchor)
|
||||
for anchor in self.triggering_anchors
|
||||
]
|
||||
)
|
||||
|
||||
for i, tracker_id in enumerate(detections.tracker_id):
|
||||
if tracker_id is None:
|
||||
continue
|
||||
|
||||
x1, y1, x2, y2 = xyxy
|
||||
anchors = [
|
||||
Point(x=x1, y=y1),
|
||||
Point(x=x1, y=y2),
|
||||
Point(x=x2, y=y1),
|
||||
Point(x=x2, y=y2),
|
||||
box_anchors = [Point(x=x, y=y) for x, y in all_anchors[:, i, :]]
|
||||
|
||||
in_limits = all(
|
||||
[
|
||||
self.is_point_in_limits(point=anchor, limits=self.limits)
|
||||
for anchor in box_anchors
|
||||
]
|
||||
)
|
||||
|
||||
if not in_limits:
|
||||
continue
|
||||
|
||||
triggers = [
|
||||
self.vector.cross_product(point=anchor) < 0 for anchor in box_anchors
|
||||
]
|
||||
triggers = [self.vector.is_in(point=anchor) for anchor in anchors]
|
||||
|
||||
if len(set(triggers)) == 2:
|
||||
continue
|
||||
@@ -96,14 +167,16 @@ class LineZoneAnnotator:
|
||||
def __init__(
|
||||
self,
|
||||
thickness: float = 2,
|
||||
color: Color = Color.white(),
|
||||
color: Color = Color.WHITE,
|
||||
text_thickness: float = 2,
|
||||
text_color: Color = Color.black(),
|
||||
text_color: Color = Color.BLACK,
|
||||
text_scale: float = 0.5,
|
||||
text_offset: float = 1.5,
|
||||
text_padding: int = 10,
|
||||
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.
|
||||
@@ -116,6 +189,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
|
||||
@@ -127,6 +202,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:
|
||||
"""
|
||||
@@ -167,90 +279,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
|
||||
@@ -77,20 +77,20 @@ class InferenceSlicer:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import cv2
|
||||
>>> import supervision as sv
|
||||
>>> from ultralytics import YOLO
|
||||
import cv2
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
>>> image = cv2.imread(SOURCE_IMAGE_PATH)
|
||||
>>> model = YOLO(...)
|
||||
image = cv2.imread(SOURCE_IMAGE_PATH)
|
||||
model = YOLO(...)
|
||||
|
||||
>>> def callback(image_slice: np.ndarray) -> sv.Detections:
|
||||
... result = model(image_slice)[0]
|
||||
... return sv.Detections.from_ultralytics(result)
|
||||
def callback(image_slice: np.ndarray) -> sv.Detections:
|
||||
result = model(image_slice)[0]
|
||||
return sv.Detections.from_ultralytics(result)
|
||||
|
||||
>>> slicer = sv.InferenceSlicer(callback = callback)
|
||||
slicer = sv.InferenceSlicer(callback = callback)
|
||||
|
||||
>>> detections = slicer(image)
|
||||
detections = slicer(image)
|
||||
```
|
||||
"""
|
||||
detections_list = []
|
||||
|
||||
@@ -90,7 +90,7 @@ class PolygonZoneAnnotator:
|
||||
zone: PolygonZone,
|
||||
color: Color,
|
||||
thickness: int = 2,
|
||||
text_color: Color = Color.black(),
|
||||
text_color: Color = Color.BLACK,
|
||||
text_scale: float = 0.5,
|
||||
text_thickness: int = 1,
|
||||
text_padding: int = 10,
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
from collections import defaultdict, deque
|
||||
from copy import deepcopy
|
||||
from typing import Optional
|
||||
|
||||
import numpy as np
|
||||
|
||||
from supervision.detection.core import Detections
|
||||
|
||||
|
||||
class DetectionsSmoother:
|
||||
"""
|
||||
A utility class for smoothing detections over multiple frames in video tracking.
|
||||
It maintains a history of detections for each track and provides smoothed
|
||||
predictions based on these histories.
|
||||
|
||||
<video controls>
|
||||
<source
|
||||
src="https://media.roboflow.com/supervision-detection-smoothing.mp4"
|
||||
type="video/mp4">
|
||||
</video>
|
||||
|
||||
!!! warning
|
||||
|
||||
- `DetectionsSmoother` requires the `tracker_id` for each detection. Refer to
|
||||
[Roboflow Trackers](/latest/trackers/) for
|
||||
information on integrating tracking into your inference pipeline.
|
||||
- This class is not compatible with segmentation models.
|
||||
|
||||
Example:
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
from ultralytics import YOLO
|
||||
|
||||
video_info = sv.VideoInfo.from_video_path(video_path=<SOURCE_FILE_PATH>)
|
||||
frame_generator = sv.get_video_frames_generator(source_path=<SOURCE_FILE_PATH>)
|
||||
|
||||
model = YOLO(<MODEL_PATH>)
|
||||
tracker = sv.ByteTrack(frame_rate=video_info.fps)
|
||||
smoother = sv.DetectionsSmoother()
|
||||
|
||||
annotator = sv.BoundingBoxAnnotator()
|
||||
|
||||
with sv.VideoSink(<TARGET_FILE_PATH>, video_info=video_info) as sink:
|
||||
for frame in frame_generator:
|
||||
result = model(frame)[0]
|
||||
detections = sv.Detections.from_ultralytics(result)
|
||||
detections = tracker.update_with_detections(detections)
|
||||
detections = smoother.update_with_detections(detections)
|
||||
|
||||
annotated_frame = bounding_box_annotator.annotate(frame.copy(), detections)
|
||||
sink.write_frame(annotated_frame)
|
||||
```
|
||||
""" # noqa: E501 // docs
|
||||
|
||||
def __init__(self, length: int = 5) -> None:
|
||||
"""
|
||||
Args:
|
||||
length (int): The maximum number of frames to consider for smoothing
|
||||
detections. Defaults to 5.
|
||||
"""
|
||||
self.tracks = defaultdict(lambda: deque(maxlen=length))
|
||||
|
||||
def update_with_detections(self, detections: Detections) -> Detections:
|
||||
"""
|
||||
Updates the smoother with a new set of detections from a frame.
|
||||
|
||||
Args:
|
||||
detections (Detections): The detections to add to the smoother.
|
||||
"""
|
||||
|
||||
if detections.tracker_id is None:
|
||||
print(
|
||||
"Smoothing skipped. DetectionsSmoother requires tracker_id. Refer to "
|
||||
"https://supervision.roboflow.com/latest/trackers for more information."
|
||||
)
|
||||
return detections
|
||||
|
||||
for detection_idx in range(len(detections)):
|
||||
tracker_id = detections.tracker_id[detection_idx]
|
||||
if tracker_id is None:
|
||||
continue
|
||||
|
||||
self.tracks[tracker_id].append(detections[detection_idx])
|
||||
|
||||
for track_id in self.tracks.keys():
|
||||
if track_id not in detections.tracker_id:
|
||||
self.tracks[track_id].append(None)
|
||||
|
||||
for track_id in list(self.tracks.keys()):
|
||||
if all([d is None for d in self.tracks[track_id]]):
|
||||
del self.tracks[track_id]
|
||||
|
||||
return self.get_smoothed_detections()
|
||||
|
||||
def get_track(self, track_id: int) -> Optional[Detections]:
|
||||
track = self.tracks.get(track_id, None)
|
||||
if track is None:
|
||||
return None
|
||||
|
||||
track = [d for d in track if d is not None]
|
||||
if len(track) == 0:
|
||||
return None
|
||||
|
||||
ret = deepcopy(track[0])
|
||||
ret.xyxy = np.mean([d.xyxy for d in track], axis=0)
|
||||
ret.confidence = np.mean([d.confidence for d in track], axis=0)
|
||||
|
||||
return ret
|
||||
|
||||
def get_smoothed_detections(self) -> Detections:
|
||||
tracked_detections = []
|
||||
for track_id in self.tracks:
|
||||
track = self.get_track(track_id)
|
||||
if track is not None:
|
||||
tracked_detections.append(track)
|
||||
|
||||
return Detections.merge(tracked_detections)
|
||||
@@ -415,16 +415,17 @@ def move_boxes(xyxy: np.ndarray, offset: np.ndarray) -> np.ndarray:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import numpy as np
|
||||
>>> import supervision as sv
|
||||
import numpy as np
|
||||
import supervision as sv
|
||||
|
||||
>>> boxes = np.array([[10, 10, 20, 20], [30, 30, 40, 40]])
|
||||
>>> offset = np.array([5, 5])
|
||||
>>> sv.move_boxes(boxes, offset)
|
||||
... array([
|
||||
... [15, 15, 25, 25],
|
||||
... [35, 35, 45, 45]
|
||||
... ])
|
||||
boxes = np.array([[10, 10, 20, 20], [30, 30, 40, 40]])
|
||||
offset = np.array([5, 5])
|
||||
moved_box = sv.move_boxes(boxes, offset)
|
||||
print(moved_box)
|
||||
# np.array([
|
||||
# [15, 15, 25, 25],
|
||||
# [35, 35, 45, 45]
|
||||
# ])
|
||||
```
|
||||
"""
|
||||
return xyxy + np.hstack([offset, offset])
|
||||
@@ -446,16 +447,17 @@ def scale_boxes(xyxy: np.ndarray, factor: float) -> np.ndarray:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import numpy as np
|
||||
>>> import supervision as sv
|
||||
import numpy as np
|
||||
import supervision as sv
|
||||
|
||||
>>> boxes = np.array([[10, 10, 20, 20], [30, 30, 40, 40]])
|
||||
>>> factor = 1.5
|
||||
>>> sv.scale_boxes(boxes, factor)
|
||||
... array([
|
||||
... [ 7.5, 7.5, 22.5, 22.5],
|
||||
... [27.5, 27.5, 42.5, 42.5]
|
||||
... ])
|
||||
boxes = np.array([[10, 10, 20, 20], [30, 30, 40, 40]])
|
||||
factor = 1.5
|
||||
scaled_bb = sv.scale_boxes(boxes, factor)
|
||||
print(scaled_bb)
|
||||
# np.array([
|
||||
# [ 7.5, 7.5, 22.5, 22.5],
|
||||
# [27.5, 27.5, 42.5, 42.5]
|
||||
# ])
|
||||
```
|
||||
"""
|
||||
centers = (xyxy[:, :2] + xyxy[:, 2:]) / 2
|
||||
|
||||
+242
-43
@@ -3,26 +3,56 @@ from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Tuple
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from supervision.utils.internal import classproperty, deprecated
|
||||
|
||||
DEFAULT_COLOR_PALETTE = [
|
||||
"#a351fb",
|
||||
"#e6194b",
|
||||
"#3cb44b",
|
||||
"#ffe119",
|
||||
"#0082c8",
|
||||
"#f58231",
|
||||
"#911eb4",
|
||||
"#46f0f0",
|
||||
"#f032e6",
|
||||
"#d2f53c",
|
||||
"#fabebe",
|
||||
"#008080",
|
||||
"#e6beff",
|
||||
"#aa6e28",
|
||||
"#fffac8",
|
||||
"#800000",
|
||||
"#aaffc3",
|
||||
"A351FB",
|
||||
"FF4040",
|
||||
"FFA1A0",
|
||||
"FF7633",
|
||||
"FFB633",
|
||||
"D1D435",
|
||||
"4CFB12",
|
||||
"94CF1A",
|
||||
"40DE8A",
|
||||
"1B9640",
|
||||
"00D6C1",
|
||||
"2E9CAA",
|
||||
"00C4FF",
|
||||
"364797",
|
||||
"6675FF",
|
||||
"0019EF",
|
||||
"863AFF",
|
||||
"530087",
|
||||
"CD3AFF",
|
||||
"FF97CA",
|
||||
"FF39C9",
|
||||
]
|
||||
|
||||
LEGACY_COLOR_PALETTE = [
|
||||
"#A351FB",
|
||||
"#E6194B",
|
||||
"#3CB44B",
|
||||
"#FFE119",
|
||||
"#0082C8",
|
||||
"#F58231",
|
||||
"#911EB4",
|
||||
"#46F0F0",
|
||||
"#F032E6",
|
||||
"#D2F53C",
|
||||
"#FABEBE",
|
||||
"#008080",
|
||||
"#E6BEFF",
|
||||
"#AA6E28",
|
||||
"#FFFAC8",
|
||||
"#800000",
|
||||
"#AAFFC3",
|
||||
]
|
||||
|
||||
ROBOFLOW_COLOR_PALETTE = ["C28DFC", "A351FB", "8315F9", "6706CE", "5905B3", "4D049A"]
|
||||
|
||||
|
||||
def _validate_color_hex(color_hex: str):
|
||||
color_hex = color_hex.lstrip("#")
|
||||
@@ -37,10 +67,31 @@ class Color:
|
||||
"""
|
||||
Represents a color in RGB format.
|
||||
|
||||
This class provides methods to work with colors, including creating colors from hex
|
||||
codes, converting colors to hex strings, RGB tuples, and BGR tuples.
|
||||
|
||||
Attributes:
|
||||
r (int): Red channel.
|
||||
g (int): Green channel.
|
||||
b (int): Blue channel.
|
||||
r (int): Red channel value (0-255).
|
||||
g (int): Green channel value (0-255).
|
||||
b (int): Blue channel value (0-255).
|
||||
|
||||
Example:
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
sv.Color.WHITE
|
||||
# Color(r=255, g=255, b=255)
|
||||
```
|
||||
|
||||
| Constant | Hex Code | RGB |
|
||||
|------------|------------|------------------|
|
||||
| `WHITE` | `#FFFFFF` | `(255, 255, 255)`|
|
||||
| `BLACK` | `#000000` | `(0, 0, 0)` |
|
||||
| `RED` | `#FF0000` | `(255, 0, 0)` |
|
||||
| `GREEN` | `#00FF00` | `(0, 255, 0)` |
|
||||
| `BLUE` | `#0000FF` | `(0, 0, 255)` |
|
||||
| `YELLOW` | `#FFFF00` | `(255, 255, 0)` |
|
||||
| `ROBOFLOW` | `#A351FB` | `(163, 81, 251)` |
|
||||
"""
|
||||
|
||||
r: int
|
||||
@@ -59,9 +110,11 @@ class Color:
|
||||
Color: Instance representing the color.
|
||||
|
||||
Example:
|
||||
```
|
||||
>>> Color.from_hex('#ff00ff')
|
||||
Color(r=255, g=0, b=255)
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
sv.Color.from_hex('#ff00ff')
|
||||
# Color(r=255, g=0, b=255)
|
||||
```
|
||||
"""
|
||||
_validate_color_hex(color_hex)
|
||||
@@ -79,9 +132,11 @@ class Color:
|
||||
str: The hexadecimal color string.
|
||||
|
||||
Example:
|
||||
```
|
||||
>>> Color(r=255, g=0, b=255).as_hex()
|
||||
'#ff00ff'
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
sv.Color(r=255, g=255, b=0).as_hex()
|
||||
# '#ffff00'
|
||||
```
|
||||
"""
|
||||
return f"#{self.r:02x}{self.g:02x}{self.b:02x}"
|
||||
@@ -94,9 +149,11 @@ class Color:
|
||||
Tuple[int, int, int]: RGB tuple.
|
||||
|
||||
Example:
|
||||
```
|
||||
>>> color.as_rgb()
|
||||
(255, 0, 255)
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
sv.Color(r=255, g=255, b=0).as_rgb()
|
||||
# (255, 255, 0)
|
||||
```
|
||||
"""
|
||||
return self.r, self.g, self.b
|
||||
@@ -109,30 +166,80 @@ class Color:
|
||||
Tuple[int, int, int]: BGR tuple.
|
||||
|
||||
Example:
|
||||
```
|
||||
>>> color.as_bgr()
|
||||
(255, 0, 255)
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
sv.Color(r=255, g=255, b=0).as_bgr()
|
||||
# (0, 255, 255)
|
||||
```
|
||||
"""
|
||||
return self.b, self.g, self.r
|
||||
|
||||
@classproperty
|
||||
def WHITE(cls):
|
||||
return Color.from_hex("#FFFFFF")
|
||||
|
||||
@classproperty
|
||||
def BLACK(cls):
|
||||
return Color.from_hex("#000000")
|
||||
|
||||
@classproperty
|
||||
def RED(cls):
|
||||
return Color.from_hex("#FF0000")
|
||||
|
||||
@classproperty
|
||||
def GREEN(cls):
|
||||
return Color.from_hex("#00FF00")
|
||||
|
||||
@classproperty
|
||||
def BLUE(cls):
|
||||
return Color.from_hex("#0000FF")
|
||||
|
||||
@classproperty
|
||||
def YELLOW(cls):
|
||||
return Color.from_hex("#FFFF00")
|
||||
|
||||
@classproperty
|
||||
def ROBOFLOW(cls):
|
||||
return Color.from_hex("#A351FB")
|
||||
|
||||
@classmethod
|
||||
@deprecated(
|
||||
"`Color.white()` is deprecated and will be removed in "
|
||||
"`supervision-0.22.0`. Use `Color.WHITE` instead."
|
||||
)
|
||||
def white(cls) -> Color:
|
||||
return Color.from_hex(color_hex="#ffffff")
|
||||
|
||||
@classmethod
|
||||
@deprecated(
|
||||
"`Color.black()` is deprecated and will be removed in "
|
||||
"`supervision-0.22.0`. Use `Color.BLACK` instead."
|
||||
)
|
||||
def black(cls) -> Color:
|
||||
return Color.from_hex(color_hex="#000000")
|
||||
|
||||
@classmethod
|
||||
@deprecated(
|
||||
"`Color.red()` is deprecated and will be removed in "
|
||||
"`supervision-0.22.0`. Use `Color.RED` instead."
|
||||
)
|
||||
def red(cls) -> Color:
|
||||
return Color.from_hex(color_hex="#ff0000")
|
||||
|
||||
@classmethod
|
||||
@deprecated(
|
||||
"`Color.green()` is deprecated and will be removed in "
|
||||
"`supervision-0.22.0`. Use `Color.GREEN` instead."
|
||||
)
|
||||
def green(cls) -> Color:
|
||||
return Color.from_hex(color_hex="#00ff00")
|
||||
|
||||
@classmethod
|
||||
@deprecated(
|
||||
"`Color.blue()` is deprecated and will be removed in "
|
||||
"`supervision-0.22.0`. Use `Color.BLUE` instead."
|
||||
)
|
||||
def blue(cls) -> Color:
|
||||
return Color.from_hex(color_hex="#0000ff")
|
||||
|
||||
@@ -141,8 +248,8 @@ class Color:
|
||||
class ColorPalette:
|
||||
colors: List[Color]
|
||||
|
||||
@classmethod
|
||||
def default(cls) -> ColorPalette:
|
||||
@classproperty
|
||||
def DEFAULT(cls) -> ColorPalette:
|
||||
"""
|
||||
Returns a default color palette.
|
||||
|
||||
@@ -150,11 +257,68 @@ class ColorPalette:
|
||||
ColorPalette: A ColorPalette instance with default colors.
|
||||
|
||||
Example:
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
sv.ColorPalette.DEFAULT
|
||||
# ColorPalette(colors=[Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), ...])
|
||||
```
|
||||
>>> ColorPalette.default()
|
||||
ColorPalette(colors=[Color(r=255, g=0, b=0), Color(r=0, g=255, b=0), ...])
|
||||
```
|
||||
|
||||

|
||||
""" # noqa: E501 // docs
|
||||
return ColorPalette.from_hex(color_hex_list=DEFAULT_COLOR_PALETTE)
|
||||
|
||||
@classproperty
|
||||
def ROBOFLOW(cls) -> ColorPalette:
|
||||
"""
|
||||
Returns a Roboflow color palette.
|
||||
|
||||
Returns:
|
||||
ColorPalette: A ColorPalette instance with Roboflow colors.
|
||||
|
||||
Example:
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
sv.ColorPalette.ROBOFLOW
|
||||
# ColorPalette(colors=[Color(r=194, g=141, b=252), Color(r=163, g=81, b=251), ...])
|
||||
```
|
||||
|
||||

|
||||
""" # noqa: E501 // docs
|
||||
return ColorPalette.from_hex(color_hex_list=ROBOFLOW_COLOR_PALETTE)
|
||||
|
||||
@classproperty
|
||||
def LEGACY(cls) -> ColorPalette:
|
||||
return ColorPalette.from_hex(color_hex_list=LEGACY_COLOR_PALETTE)
|
||||
|
||||
@classmethod
|
||||
@deprecated(
|
||||
"`ColorPalette.default()` is deprecated and will be removed in "
|
||||
"`supervision-0.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:
|
||||
ColorPalette: A ColorPalette instance with default colors.
|
||||
|
||||
Example:
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
sv.ColorPalette.default()
|
||||
# ColorPalette(colors=[Color(r=255, g=64, b=64), Color(r=255, g=161, b=160), ...])
|
||||
```
|
||||
""" # noqa: E501 // docs
|
||||
return ColorPalette.from_hex(color_hex_list=DEFAULT_COLOR_PALETTE)
|
||||
|
||||
@classmethod
|
||||
@@ -169,14 +333,46 @@ class ColorPalette:
|
||||
ColorPalette: A ColorPalette instance.
|
||||
|
||||
Example:
|
||||
```
|
||||
>>> ColorPalette.from_hex(['#ff0000', '#00ff00', '#0000ff'])
|
||||
ColorPalette(colors=[Color(r=255, g=0, b=0), Color(r=0, g=255, b=0), ...])
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
sv.ColorPalette.from_hex(['#ff0000', '#00ff00', '#0000ff'])
|
||||
# ColorPalette(colors=[Color(r=255, g=0, b=0), Color(r=0, g=255, b=0), ...])
|
||||
```
|
||||
"""
|
||||
colors = [Color.from_hex(color_hex) for color_hex in color_hex_list]
|
||||
return cls(colors)
|
||||
|
||||
@classmethod
|
||||
def from_matplotlib(cls, palette_name: str, color_count: int) -> ColorPalette:
|
||||
"""
|
||||
Create a ColorPalette instance from a Matplotlib color palette.
|
||||
|
||||
Args:
|
||||
palette_name (str): Name of the Matplotlib palette.
|
||||
color_count (int): Number of colors to sample from the palette.
|
||||
|
||||
Returns:
|
||||
ColorPalette: A ColorPalette instance.
|
||||
|
||||
Example:
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
sv.ColorPalette.from_matplotlib('viridis', 5)
|
||||
# ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
|
||||
```
|
||||
|
||||

|
||||
""" # noqa: E501 // docs
|
||||
mpl_palette = plt.get_cmap(palette_name, color_count)
|
||||
colors = [
|
||||
Color(int(r * 255), int(g * 255), int(b * 255))
|
||||
for r, g, b, _ in mpl_palette.colors
|
||||
]
|
||||
return cls(colors)
|
||||
|
||||
def by_idx(self, idx: int) -> Color:
|
||||
"""
|
||||
Return the color at a given index in the palette.
|
||||
@@ -188,9 +384,12 @@ class ColorPalette:
|
||||
Color: Color at the given index.
|
||||
|
||||
Example:
|
||||
```
|
||||
>>> color_palette.by_idx(1)
|
||||
Color(r=0, g=255, b=0)
|
||||
```python
|
||||
import supervision as sv
|
||||
|
||||
color_palette = sv.ColorPalette.from_hex(['#ff0000', '#00ff00', '#0000ff'])
|
||||
color_palette.by_idx(1)
|
||||
# Color(r=0, g=255, b=0)
|
||||
```
|
||||
"""
|
||||
if idx < 0:
|
||||
|
||||
@@ -105,7 +105,7 @@ def draw_text(
|
||||
scene: np.ndarray,
|
||||
text: str,
|
||||
text_anchor: Point,
|
||||
text_color: Color = Color.black(),
|
||||
text_color: Color = Color.BLACK,
|
||||
text_scale: float = 0.5,
|
||||
text_thickness: int = 1,
|
||||
text_padding: int = 10,
|
||||
@@ -135,9 +135,11 @@ def draw_text(
|
||||
|
||||
Examples:
|
||||
```python
|
||||
>>> scene = np.zeros((100, 100, 3), dtype=np.uint8)
|
||||
>>> text_anchor = Point(x=50, y=50)
|
||||
>>> scene = draw_text(scene=scene, text="Hello, world!",text_anchor=text_anchor)
|
||||
import numpy as np
|
||||
|
||||
scene = np.zeros((100, 100, 3), dtype=np.uint8)
|
||||
text_anchor = Point(x=50, y=50)
|
||||
scene = draw_text(scene=scene, text="Hello, world!",text_anchor=text_anchor)
|
||||
```
|
||||
"""
|
||||
text_width, text_height = cv2.getTextSize(
|
||||
@@ -146,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)
|
||||
@@ -161,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(),
|
||||
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from math import sqrt
|
||||
from typing import Tuple
|
||||
|
||||
|
||||
@@ -43,13 +44,51 @@ class Vector:
|
||||
start: Point
|
||||
end: Point
|
||||
|
||||
def is_in(self, point: Point) -> bool:
|
||||
v1 = Vector(self.start, self.end)
|
||||
v2 = Vector(self.start, point)
|
||||
cross_product = (v1.end.x - v1.start.x) * (v2.end.y - v2.start.y) - (
|
||||
v1.end.y - v1.start.y
|
||||
) * (v2.end.x - v2.start.x)
|
||||
return cross_product < 0
|
||||
@property
|
||||
def magnitude(self) -> float:
|
||||
"""
|
||||
Calculate the magnitude (length) of the vector.
|
||||
|
||||
Returns:
|
||||
float: The magnitude of the vector.
|
||||
"""
|
||||
dx = self.end.x - self.start.x
|
||||
dy = self.end.y - self.start.y
|
||||
return sqrt(dx**2 + dy**2)
|
||||
|
||||
@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
|
||||
product) of the vector and a point, treated as vectors in 2D space.
|
||||
|
||||
Args:
|
||||
point (Point): The point to be evaluated, treated as the endpoint of a
|
||||
vector originating from the 'start' of the main vector.
|
||||
|
||||
Returns:
|
||||
float: The scalar value of the cross product. It is positive if 'point'
|
||||
lies to the left of the vector (when moving from 'start' to 'end'),
|
||||
negative if it lies to the right, and 0 if it is collinear with the
|
||||
vector.
|
||||
"""
|
||||
dx_vector = self.end.x - self.start.x
|
||||
dy_vector = self.end.y - self.start.y
|
||||
dx_point = point.x - self.start.x
|
||||
dy_point = point.y - self.start.y
|
||||
return (dx_vector * dy_point) - (dy_vector * dx_point)
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@@ -22,10 +22,11 @@ def get_polygon_center(polygon: np.ndarray) -> Point:
|
||||
|
||||
Examples:
|
||||
```python
|
||||
>>> from supervision.geometry.utils import get_polygon_center
|
||||
from supervision.geometry.utils import get_polygon_center
|
||||
import numpy as np
|
||||
|
||||
>>> vertices = np.array([[0, 0], [0, 1], [1, 1], [1, 0]])
|
||||
>>> get_center(vertices)
|
||||
vertices = np.array([[0, 0], [0, 1], [1, 1], [1, 0]])
|
||||
get_center(vertices)
|
||||
Point(x=0.5, y=0.5)
|
||||
```
|
||||
"""
|
||||
|
||||
+135
-133
@@ -116,31 +116,31 @@ class ConfusionMatrix:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> targets = [
|
||||
... sv.Detections(...),
|
||||
... sv.Detections(...)
|
||||
... ]
|
||||
targets = [
|
||||
sv.Detections(...),
|
||||
sv.Detections(...)
|
||||
]
|
||||
|
||||
>>> predictions = [
|
||||
... sv.Detections(...),
|
||||
... sv.Detections(...)
|
||||
... ]
|
||||
predictions = [
|
||||
sv.Detections(...),
|
||||
sv.Detections(...)
|
||||
]
|
||||
|
||||
>>> confusion_matrix = sv.ConfusionMatrix.from_detections(
|
||||
... predictions=predictions,
|
||||
... targets=target,
|
||||
... classes=['person', ...]
|
||||
... )
|
||||
confusion_matrix = sv.ConfusionMatrix.from_detections(
|
||||
predictions=predictions,
|
||||
targets=target,
|
||||
classes=['person', ...]
|
||||
)
|
||||
|
||||
>>> confusion_matrix.matrix
|
||||
array([
|
||||
[0., 0., 0., 0.],
|
||||
[0., 1., 0., 1.],
|
||||
[0., 1., 1., 0.],
|
||||
[1., 1., 0., 0.]
|
||||
])
|
||||
print(confusion_matrix.matrix)
|
||||
# np.array([
|
||||
# [0., 0., 0., 0.],
|
||||
# [0., 1., 0., 1.],
|
||||
# [0., 1., 1., 0.],
|
||||
# [1., 1., 0., 0.]
|
||||
# ])
|
||||
```
|
||||
"""
|
||||
|
||||
@@ -191,46 +191,47 @@ class ConfusionMatrix:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
import numpy as np
|
||||
|
||||
>>> targets = (
|
||||
... [
|
||||
... array(
|
||||
... [
|
||||
... [0.0, 0.0, 3.0, 3.0, 1],
|
||||
... [2.0, 2.0, 5.0, 5.0, 1],
|
||||
... [6.0, 1.0, 8.0, 3.0, 2],
|
||||
... ]
|
||||
... ),
|
||||
... array([1.0, 1.0, 2.0, 2.0, 2]),
|
||||
... ]
|
||||
... )
|
||||
targets = (
|
||||
[
|
||||
np.array(
|
||||
[
|
||||
[0.0, 0.0, 3.0, 3.0, 1],
|
||||
[2.0, 2.0, 5.0, 5.0, 1],
|
||||
[6.0, 1.0, 8.0, 3.0, 2],
|
||||
]
|
||||
),
|
||||
np.array([1.0, 1.0, 2.0, 2.0, 2]),
|
||||
]
|
||||
)
|
||||
|
||||
>>> predictions = [
|
||||
... array(
|
||||
... [
|
||||
... [0.0, 0.0, 3.0, 3.0, 1, 0.9],
|
||||
... [0.1, 0.1, 3.0, 3.0, 0, 0.9],
|
||||
... [6.0, 1.0, 8.0, 3.0, 1, 0.8],
|
||||
... [1.0, 6.0, 2.0, 7.0, 1, 0.8],
|
||||
... ]
|
||||
... ),
|
||||
... array([[1.0, 1.0, 2.0, 2.0, 2, 0.8]])
|
||||
... ]
|
||||
predictions = [
|
||||
np.array(
|
||||
[
|
||||
[0.0, 0.0, 3.0, 3.0, 1, 0.9],
|
||||
[0.1, 0.1, 3.0, 3.0, 0, 0.9],
|
||||
[6.0, 1.0, 8.0, 3.0, 1, 0.8],
|
||||
[1.0, 6.0, 2.0, 7.0, 1, 0.8],
|
||||
]
|
||||
),
|
||||
np.array([[1.0, 1.0, 2.0, 2.0, 2, 0.8]])
|
||||
]
|
||||
|
||||
>>> confusion_matrix = sv.ConfusionMatrix.from_tensors(
|
||||
... predictions=predictions,
|
||||
... targets=targets,
|
||||
... classes=['person', ...]
|
||||
... )
|
||||
confusion_matrix = sv.ConfusionMatrix.from_tensors(
|
||||
predictions=predictions,
|
||||
targets=targets,
|
||||
classes=['person', ...]
|
||||
)
|
||||
|
||||
>>> confusion_matrix.matrix
|
||||
array([
|
||||
[0., 0., 0., 0.],
|
||||
[0., 1., 0., 1.],
|
||||
[0., 1., 1., 0.],
|
||||
[1., 1., 0., 0.]
|
||||
])
|
||||
print(confusion_matrix.matrix)
|
||||
# np.array([
|
||||
# [0., 0., 0., 0.],
|
||||
# [0., 1., 0., 1.],
|
||||
# [0., 1., 1., 0.],
|
||||
# [1., 1., 0., 0.]
|
||||
# ])
|
||||
```
|
||||
"""
|
||||
validate_input_tensors(predictions, targets)
|
||||
@@ -365,28 +366,28 @@ class ConfusionMatrix:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
>>> from ultralytics import YOLO
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
>>> dataset = sv.DetectionDataset.from_yolo(...)
|
||||
dataset = sv.DetectionDataset.from_yolo(...)
|
||||
|
||||
>>> model = YOLO(...)
|
||||
>>> def callback(image: np.ndarray) -> sv.Detections:
|
||||
... result = model(image)[0]
|
||||
... return sv.Detections.from_ultralytics(result)
|
||||
model = YOLO(...)
|
||||
def callback(image: np.ndarray) -> sv.Detections:
|
||||
result = model(image)[0]
|
||||
return sv.Detections.from_ultralytics(result)
|
||||
|
||||
>>> confusion_matrix = sv.ConfusionMatrix.benchmark(
|
||||
... dataset = dataset,
|
||||
... callback = callback
|
||||
... )
|
||||
confusion_matrix = sv.ConfusionMatrix.benchmark(
|
||||
dataset = dataset,
|
||||
callback = callback
|
||||
)
|
||||
|
||||
>>> confusion_matrix.matrix
|
||||
array([
|
||||
[0., 0., 0., 0.],
|
||||
[0., 1., 0., 1.],
|
||||
[0., 1., 1., 0.],
|
||||
[1., 1., 0., 0.]
|
||||
])
|
||||
print(confusion_matrix.matrix)
|
||||
# np.array([
|
||||
# [0., 0., 0., 0.],
|
||||
# [0., 1., 0., 1.],
|
||||
# [0., 1., 1., 0.],
|
||||
# [1., 1., 0., 0.]
|
||||
# ])
|
||||
```
|
||||
"""
|
||||
predictions, targets = [], []
|
||||
@@ -532,25 +533,25 @@ class MeanAveragePrecision:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> targets = [
|
||||
... sv.Detections(...),
|
||||
... sv.Detections(...)
|
||||
... ]
|
||||
targets = [
|
||||
sv.Detections(...),
|
||||
sv.Detections(...)
|
||||
]
|
||||
|
||||
>>> predictions = [
|
||||
... sv.Detections(...),
|
||||
... sv.Detections(...)
|
||||
... ]
|
||||
predictions = [
|
||||
sv.Detections(...),
|
||||
sv.Detections(...)
|
||||
]
|
||||
|
||||
>>> mean_average_precision = sv.MeanAveragePrecision.from_detections(
|
||||
... predictions=predictions,
|
||||
... targets=target,
|
||||
... )
|
||||
mean_average_precision = sv.MeanAveragePrecision.from_detections(
|
||||
predictions=predictions,
|
||||
targets=target,
|
||||
)
|
||||
|
||||
>>> mean_average_precison.map50_95
|
||||
0.2899
|
||||
print(mean_average_precison.map50_95)
|
||||
# 0.2899
|
||||
```
|
||||
"""
|
||||
prediction_tensors = []
|
||||
@@ -583,23 +584,23 @@ class MeanAveragePrecision:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
>>> from ultralytics import YOLO
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
>>> dataset = sv.DetectionDataset.from_yolo(...)
|
||||
dataset = sv.DetectionDataset.from_yolo(...)
|
||||
|
||||
>>> model = YOLO(...)
|
||||
>>> def callback(image: np.ndarray) -> sv.Detections:
|
||||
... result = model(image)[0]
|
||||
... return sv.Detections.from_ultralytics(result)
|
||||
model = YOLO(...)
|
||||
def callback(image: np.ndarray) -> sv.Detections:
|
||||
result = model(image)[0]
|
||||
return sv.Detections.from_ultralytics(result)
|
||||
|
||||
>>> mean_average_precision = sv.MeanAveragePrecision.benchmark(
|
||||
... dataset = dataset,
|
||||
... callback = callback
|
||||
... )
|
||||
mean_average_precision = sv.MeanAveragePrecision.benchmark(
|
||||
dataset = dataset,
|
||||
callback = callback
|
||||
)
|
||||
|
||||
>>> mean_average_precision.map50_95
|
||||
0.433
|
||||
print(mean_average_precision.map50_95)
|
||||
# 0.433
|
||||
```
|
||||
"""
|
||||
predictions, targets = [], []
|
||||
@@ -637,40 +638,41 @@ class MeanAveragePrecision:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
import numpy as np
|
||||
|
||||
>>> targets = (
|
||||
... [
|
||||
... array(
|
||||
... [
|
||||
... [0.0, 0.0, 3.0, 3.0, 1],
|
||||
... [2.0, 2.0, 5.0, 5.0, 1],
|
||||
... [6.0, 1.0, 8.0, 3.0, 2],
|
||||
... ]
|
||||
... ),
|
||||
... array([1.0, 1.0, 2.0, 2.0, 2]),
|
||||
... ]
|
||||
... )
|
||||
targets = (
|
||||
[
|
||||
np.array(
|
||||
[
|
||||
[0.0, 0.0, 3.0, 3.0, 1],
|
||||
[2.0, 2.0, 5.0, 5.0, 1],
|
||||
[6.0, 1.0, 8.0, 3.0, 2],
|
||||
]
|
||||
),
|
||||
np.array([[1.0, 1.0, 2.0, 2.0, 2]]),
|
||||
]
|
||||
)
|
||||
|
||||
>>> predictions = [
|
||||
... array(
|
||||
... [
|
||||
... [0.0, 0.0, 3.0, 3.0, 1, 0.9],
|
||||
... [0.1, 0.1, 3.0, 3.0, 0, 0.9],
|
||||
... [6.0, 1.0, 8.0, 3.0, 1, 0.8],
|
||||
... [1.0, 6.0, 2.0, 7.0, 1, 0.8],
|
||||
... ]
|
||||
... ),
|
||||
... array([[1.0, 1.0, 2.0, 2.0, 2, 0.8]])
|
||||
... ]
|
||||
predictions = [
|
||||
np.array(
|
||||
[
|
||||
[0.0, 0.0, 3.0, 3.0, 1, 0.9],
|
||||
[0.1, 0.1, 3.0, 3.0, 0, 0.9],
|
||||
[6.0, 1.0, 8.0, 3.0, 1, 0.8],
|
||||
[1.0, 6.0, 2.0, 7.0, 1, 0.8],
|
||||
]
|
||||
),
|
||||
np.array([[1.0, 1.0, 2.0, 2.0, 2, 0.8]])
|
||||
]
|
||||
|
||||
>>> mean_average_precison = sv.MeanAveragePrecision.from_tensors(
|
||||
... predictions=predictions,
|
||||
... targets=targets,
|
||||
... )
|
||||
mean_average_precison = sv.MeanAveragePrecision.from_tensors(
|
||||
predictions=predictions,
|
||||
targets=targets,
|
||||
)
|
||||
|
||||
>>> mean_average_precison.map50_95
|
||||
0.2899
|
||||
print(mean_average_precison.map50_95)
|
||||
# 0.6649
|
||||
```
|
||||
"""
|
||||
validate_input_tensors(predictions, targets)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -167,13 +167,17 @@ class ByteTrack:
|
||||
"""
|
||||
Initialize the ByteTrack object.
|
||||
|
||||
<video controls>
|
||||
<source src="https://media.roboflow.com/supervision/video-examples/how-to/track-objects/annotate-video-with-traces.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
Parameters:
|
||||
track_thresh (float, optional): Detection confidence threshold
|
||||
for track activation.
|
||||
track_buffer (int, optional): Number of frames to buffer when a track is lost.
|
||||
match_thresh (float, optional): Threshold for matching tracks with detections.
|
||||
frame_rate (int, optional): The frame rate of the video.
|
||||
"""
|
||||
""" # noqa: E501 // docs
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -196,39 +200,41 @@ class ByteTrack:
|
||||
|
||||
def update_with_detections(self, detections: Detections) -> Detections:
|
||||
"""
|
||||
Updates the tracker with the provided detections and
|
||||
returns the updated detection results.
|
||||
Updates the tracker with the provided detections and returns the updated
|
||||
detection results.
|
||||
|
||||
Args:
|
||||
detections (Detections): The detections to pass through the tracker.
|
||||
|
||||
Parameters:
|
||||
detections: The new detections to update with.
|
||||
Returns:
|
||||
Detection: The updated detection results that now include tracking IDs.
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
>>> from ultralytics import YOLO
|
||||
import supervision as sv
|
||||
from ultralytics import YOLO
|
||||
|
||||
>>> model = YOLO(...)
|
||||
>>> byte_tracker = sv.ByteTrack()
|
||||
>>> annotator = sv.BoxAnnotator()
|
||||
model = YOLO(<MODEL_PATH>)
|
||||
tracker = sv.ByteTrack()
|
||||
|
||||
>>> def callback(frame: np.ndarray, index: int) -> np.ndarray:
|
||||
... results = model(frame)[0]
|
||||
... detections = sv.Detections.from_ultralytics(results)
|
||||
... detections = byte_tracker.update_with_detections(detections)
|
||||
... labels = [
|
||||
... f"#{tracker_id} {model.model.names[class_id]} {confidence:0.2f}"
|
||||
... for _, _, confidence, class_id, tracker_id
|
||||
... in detections
|
||||
... ]
|
||||
... return annotator.annotate(scene=frame.copy(),
|
||||
... detections=detections, labels=labels)
|
||||
bounding_box_annotator = sv.BoundingBoxAnnotator()
|
||||
label_annotator = sv.LabelAnnotator()
|
||||
|
||||
>>> sv.process_video(
|
||||
... source_path='...',
|
||||
... target_path='...',
|
||||
... callback=callback
|
||||
... )
|
||||
def callback(frame: np.ndarray, index: int) -> np.ndarray:
|
||||
results = model(frame)[0]
|
||||
detections = sv.Detections.from_ultralytics(results)
|
||||
detections = tracker.update_with_detections(detections)
|
||||
|
||||
labels = [f"#{tracker_id}" for tracker_id in detections.tracker_id]
|
||||
|
||||
annotated_frame = bounding_box_annotator.annotate(
|
||||
scene=frame.copy(), detections=detections)
|
||||
annotated_frame = label_annotator.annotate(
|
||||
scene=annotated_frame, detections=detections, labels=labels)
|
||||
return annotated_frame
|
||||
|
||||
sv.process_video(
|
||||
source_path=<SOURCE_VIDEO_PATH>,
|
||||
target_path=<TARGET_VIDEO_PATH>,
|
||||
callback=callback
|
||||
)
|
||||
```
|
||||
"""
|
||||
|
||||
@@ -254,6 +260,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.
|
||||
@@ -299,6 +320,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)
|
||||
|
||||
@@ -34,14 +34,14 @@ def list_files_with_extensions(
|
||||
|
||||
Examples:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> # List all files in the directory
|
||||
>>> files = sv.list_files_with_extensions(directory='my_directory')
|
||||
# List all files in the directory
|
||||
files = sv.list_files_with_extensions(directory='my_directory')
|
||||
|
||||
>>> # List only files with '.txt' and '.md' extensions
|
||||
>>> files = sv.list_files_with_extensions(
|
||||
... directory='my_directory', extensions=['txt', 'md'])
|
||||
# List only files with '.txt' and '.md' extensions
|
||||
files = sv.list_files_with_extensions(
|
||||
directory='my_directory', extensions=['txt', 'md'])
|
||||
```
|
||||
"""
|
||||
|
||||
|
||||
+12
-12
@@ -20,13 +20,13 @@ def crop_image(image: np.ndarray, xyxy: np.ndarray) -> np.ndarray:
|
||||
|
||||
Examples:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> detection = sv.Detections(...)
|
||||
>>> with sv.ImageSink(target_dir_path='target/directory/path') as sink:
|
||||
... for xyxy in detection.xyxy:
|
||||
... cropped_image = sv.crop_image(image=image, xyxy=xyxy)
|
||||
... sink.save_image(image=image)
|
||||
detection = sv.Detections(...)
|
||||
with sv.ImageSink(target_dir_path='target/directory/path') as sink:
|
||||
for xyxy in detection.xyxy:
|
||||
cropped_image = sv.crop_image(image=image, xyxy=xyxy)
|
||||
sink.save_image(image=cropped_image)
|
||||
```
|
||||
"""
|
||||
|
||||
@@ -55,13 +55,13 @@ class ImageSink:
|
||||
|
||||
Examples:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> with sv.ImageSink(target_dir_path='target/directory/path',
|
||||
... overwrite=True) as sink:
|
||||
... for image in sv.get_video_frames_generator(
|
||||
... source_path='source_video.mp4', stride=2):
|
||||
... sink.save_image(image=image)
|
||||
with sv.ImageSink(target_dir_path='target/directory/path',
|
||||
overwrite=True) as sink:
|
||||
for image in sv.get_video_frames_generator(
|
||||
source_path='source_video.mp4', stride=2):
|
||||
sink.save_image(image=image)
|
||||
```
|
||||
"""
|
||||
|
||||
|
||||
@@ -16,3 +16,29 @@ def deprecated(reason: str):
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
class classproperty(property):
|
||||
"""
|
||||
A decorator that combines @classmethod and @property.
|
||||
It allows a method to be accessed as a property of the class,
|
||||
rather than an instance, similar to a classmethod.
|
||||
|
||||
Usage:
|
||||
@classproperty
|
||||
def my_method(cls):
|
||||
...
|
||||
"""
|
||||
|
||||
def __get__(self, owner_self: object, owner_cls: type) -> object:
|
||||
"""
|
||||
Override the __get__ method to return the result of the function call.
|
||||
|
||||
Args:
|
||||
owner_self: The instance through which the attribute was accessed, or None.
|
||||
owner_cls: The class through which the attribute was accessed.
|
||||
|
||||
Returns:
|
||||
The result of calling the function stored in 'fget' with 'owner_cls'.
|
||||
"""
|
||||
return self.fget(owner_cls)
|
||||
|
||||
@@ -18,13 +18,13 @@ def plot_image(
|
||||
|
||||
Examples:
|
||||
```python
|
||||
>>> import cv2
|
||||
>>> import supervision as sv
|
||||
import cv2
|
||||
import supervision as sv
|
||||
|
||||
>>> image = cv2.imread("path/to/image.jpg")
|
||||
image = cv2.imread("path/to/image.jpg")
|
||||
|
||||
%matplotlib inline
|
||||
>>> sv.plot_image(image=image, size=(16, 16))
|
||||
sv.plot_image(image=image, size=(16, 16))
|
||||
```
|
||||
"""
|
||||
plt.figure(figsize=size)
|
||||
@@ -63,18 +63,18 @@ def plot_images_grid(
|
||||
|
||||
Examples:
|
||||
```python
|
||||
>>> import cv2
|
||||
>>> import supervision as sv
|
||||
import cv2
|
||||
import supervision as sv
|
||||
|
||||
>>> image1 = cv2.imread("path/to/image1.jpg")
|
||||
>>> image2 = cv2.imread("path/to/image2.jpg")
|
||||
>>> image3 = cv2.imread("path/to/image3.jpg")
|
||||
image1 = cv2.imread("path/to/image1.jpg")
|
||||
image2 = cv2.imread("path/to/image2.jpg")
|
||||
image3 = cv2.imread("path/to/image3.jpg")
|
||||
|
||||
>>> images = [image1, image2, image3]
|
||||
>>> titles = ["Image 1", "Image 2", "Image 3"]
|
||||
images = [image1, image2, image3]
|
||||
titles = ["Image 1", "Image 2", "Image 3"]
|
||||
|
||||
%matplotlib inline
|
||||
>>> plot_images_grid(images, grid_size=(2, 2), titles=titles, size=(16, 16))
|
||||
plot_images_grid(images, grid_size=(2, 2), titles=titles, size=(16, 16))
|
||||
```
|
||||
"""
|
||||
nrows, ncols = grid_size
|
||||
|
||||
+52
-32
@@ -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:
|
||||
@@ -24,15 +26,15 @@ class VideoInfo:
|
||||
|
||||
Examples:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> video_info = sv.VideoInfo.from_video_path(video_path='video.mp4')
|
||||
video_info = sv.VideoInfo.from_video_path(video_path='video.mp4')
|
||||
|
||||
>>> video_info
|
||||
VideoInfo(width=3840, height=2160, fps=25, total_frames=538)
|
||||
video_info
|
||||
# VideoInfo(width=3840, height=2160, fps=25, total_frames=538)
|
||||
|
||||
>>> video_info.resolution_wh
|
||||
(3840, 2160)
|
||||
video_info.resolution_wh
|
||||
# (3840, 2160)
|
||||
```
|
||||
"""
|
||||
|
||||
@@ -71,14 +73,14 @@ class VideoSink:
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> video_info = sv.VideoInfo.from_video_path('source.mp4')
|
||||
>>> frames_generator = sv.get_video_frames_generator('source.mp4')
|
||||
video_info = sv.VideoInfo.from_video_path('source.mp4')
|
||||
frames_generator = sv.get_video_frames_generator('source.mp4')
|
||||
|
||||
>>> with sv.VideoSink(target_path='target.mp4', video_info=video_info) as sink:
|
||||
... for frame in frames_generator:
|
||||
... sink.write_frame(frame=frame)
|
||||
with sv.VideoSink(target_path='target.mp4', video_info=video_info) as sink:
|
||||
for frame in frames_generator:
|
||||
sink.write_frame(frame=frame)
|
||||
```
|
||||
"""
|
||||
|
||||
@@ -143,10 +145,10 @@ def get_video_frames_generator(
|
||||
|
||||
Examples:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> for frame in sv.get_video_frames_generator(source_path='source_video.mp4'):
|
||||
... ...
|
||||
for frame in sv.get_video_frames_generator(source_path='source_video.mp4'):
|
||||
...
|
||||
```
|
||||
"""
|
||||
video, start, end = _validate_and_setup_video(source_path, start, end)
|
||||
@@ -183,16 +185,16 @@ def process_video(
|
||||
|
||||
Examples:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> def callback(scene: np.ndarray, index: int) -> np.ndarray:
|
||||
... ...
|
||||
def callback(scene: np.ndarray, index: int) -> np.ndarray:
|
||||
...
|
||||
|
||||
>>> process_video(
|
||||
... source_path='...',
|
||||
... target_path='...',
|
||||
... callback=callback
|
||||
... )
|
||||
process_video(
|
||||
source_path='...',
|
||||
target_path='...',
|
||||
callback=callback
|
||||
)
|
||||
```
|
||||
"""
|
||||
source_video_info = VideoInfo.from_video_path(video_path=source_path)
|
||||
@@ -217,27 +219,45 @@ class FPSMonitor:
|
||||
|
||||
Examples:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
import supervision as sv
|
||||
|
||||
>>> frames_generator = sv.get_video_frames_generator('source.mp4')
|
||||
>>> fps_monitor = sv.FPSMonitor()
|
||||
frames_generator = sv.get_video_frames_generator(source_path=<SOURCE_FILE_PATH>)
|
||||
fps_monitor = sv.FPSMonitor()
|
||||
|
||||
>>> for frame in frames_generator:
|
||||
... # your processing code here
|
||||
... fps_monitor.tick()
|
||||
... fps = fps_monitor()
|
||||
for frame in frames_generator:
|
||||
# your processing code here
|
||||
fps_monitor.tick()
|
||||
fps = fps_monitor.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,72 @@
|
||||
from contextlib import ExitStack as DoesNotRaise
|
||||
from typing import Optional, Tuple
|
||||
|
||||
import pytest
|
||||
|
||||
from supervision import LineZone
|
||||
from supervision.geometry.core import Point, Vector
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"vector, expected_result, exception",
|
||||
[
|
||||
(
|
||||
Vector(start=Point(x=0.0, y=0.0), end=Point(x=0.0, y=0.0)),
|
||||
None,
|
||||
pytest.raises(ValueError),
|
||||
),
|
||||
(
|
||||
Vector(start=Point(x=1.0, y=1.0), end=Point(x=1.0, y=1.0)),
|
||||
None,
|
||||
pytest.raises(ValueError),
|
||||
),
|
||||
(
|
||||
Vector(start=Point(x=0.0, y=0.0), end=Point(x=0.0, y=4.0)),
|
||||
(
|
||||
Vector(start=Point(x=0.0, y=0.0), end=Point(x=-1.0, y=0.0)),
|
||||
Vector(start=Point(x=0.0, y=4.0), end=Point(x=1.0, y=4.0)),
|
||||
),
|
||||
DoesNotRaise(),
|
||||
),
|
||||
(
|
||||
Vector(Point(0.0, 0.0), Point(4.0, 0.0)),
|
||||
(
|
||||
Vector(start=Point(x=0.0, y=0.0), end=Point(x=0.0, y=1.0)),
|
||||
Vector(start=Point(x=4.0, y=0.0), end=Point(x=4.0, y=-1.0)),
|
||||
),
|
||||
DoesNotRaise(),
|
||||
),
|
||||
(
|
||||
Vector(Point(0.0, 0.0), Point(3.0, 4.0)),
|
||||
(
|
||||
Vector(start=Point(x=0, y=0), end=Point(x=-0.8, y=0.6)),
|
||||
Vector(start=Point(x=3, y=4), end=Point(x=3.8, y=3.4)),
|
||||
),
|
||||
DoesNotRaise(),
|
||||
),
|
||||
(
|
||||
Vector(Point(0.0, 0.0), Point(4.0, 3.0)),
|
||||
(
|
||||
Vector(start=Point(x=0, y=0), end=Point(x=-0.6, y=0.8)),
|
||||
Vector(start=Point(x=4, y=3), end=Point(x=4.6, y=2.2)),
|
||||
),
|
||||
DoesNotRaise(),
|
||||
),
|
||||
(
|
||||
Vector(Point(0.0, 0.0), Point(3.0, -4.0)),
|
||||
(
|
||||
Vector(start=Point(x=0, y=0), end=Point(x=0.8, y=0.6)),
|
||||
Vector(start=Point(x=3, y=-4), end=Point(x=2.2, y=-4.6)),
|
||||
),
|
||||
DoesNotRaise(),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_calculate_region_of_interest_limits(
|
||||
vector: Vector,
|
||||
expected_result: Optional[Tuple[Vector, Vector]],
|
||||
exception: Exception,
|
||||
) -> None:
|
||||
with exception:
|
||||
result = LineZone.calculate_region_of_interest_limits(vector=vector)
|
||||
assert result == expected_result
|
||||
+12
-12
@@ -9,13 +9,13 @@ from supervision.draw.color import Color
|
||||
@pytest.mark.parametrize(
|
||||
"color_hex, expected_result, exception",
|
||||
[
|
||||
("fff", Color.white(), DoesNotRaise()),
|
||||
("#fff", Color.white(), DoesNotRaise()),
|
||||
("ffffff", Color.white(), DoesNotRaise()),
|
||||
("#ffffff", Color.white(), DoesNotRaise()),
|
||||
("f00", Color.red(), DoesNotRaise()),
|
||||
("0f0", Color.green(), DoesNotRaise()),
|
||||
("00f", Color.blue(), DoesNotRaise()),
|
||||
("fff", Color.WHITE, DoesNotRaise()),
|
||||
("#fff", Color.WHITE, DoesNotRaise()),
|
||||
("ffffff", Color.WHITE, DoesNotRaise()),
|
||||
("#ffffff", Color.WHITE, DoesNotRaise()),
|
||||
("f00", Color.RED, DoesNotRaise()),
|
||||
("0f0", Color.GREEN, DoesNotRaise()),
|
||||
("00f", Color.BLUE, DoesNotRaise()),
|
||||
("#808000", Color(r=128, g=128, b=0), DoesNotRaise()),
|
||||
("", None, pytest.raises(ValueError)),
|
||||
("00", None, pytest.raises(ValueError)),
|
||||
@@ -35,11 +35,11 @@ def test_color_from_hex(
|
||||
@pytest.mark.parametrize(
|
||||
"color, expected_result, exception",
|
||||
[
|
||||
(Color.white(), "#ffffff", DoesNotRaise()),
|
||||
(Color.black(), "#000000", DoesNotRaise()),
|
||||
(Color.red(), "#ff0000", DoesNotRaise()),
|
||||
(Color.green(), "#00ff00", DoesNotRaise()),
|
||||
(Color.blue(), "#0000ff", DoesNotRaise()),
|
||||
(Color.WHITE, "#ffffff", DoesNotRaise()),
|
||||
(Color.BLACK, "#000000", DoesNotRaise()),
|
||||
(Color.RED, "#ff0000", DoesNotRaise()),
|
||||
(Color.GREEN, "#00ff00", DoesNotRaise()),
|
||||
(Color.BLUE, "#0000ff", DoesNotRaise()),
|
||||
(Color(r=128, g=128, b=0), "#808000", DoesNotRaise()),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import pytest
|
||||
|
||||
from supervision.geometry.core import Point, Vector
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"vector, point, expected_result",
|
||||
[
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=5, y=5)), Point(x=-1, y=1), 10.0),
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=5, y=5)), Point(x=6, y=6), 0.0),
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=5, y=5)), Point(x=3, y=6), 15.0),
|
||||
(Vector(start=Point(x=5, y=5), end=Point(x=0, y=0)), Point(x=-1, y=1), -10.0),
|
||||
(Vector(start=Point(x=5, y=5), end=Point(x=0, y=0)), Point(x=6, y=6), 0.0),
|
||||
(Vector(start=Point(x=5, y=5), end=Point(x=0, y=0)), Point(x=3, y=6), -15.0),
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=1, y=0)), Point(x=0, y=0), 0.0),
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=1, y=0)), Point(x=0, y=-1), -1.0),
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=1, y=0)), Point(x=0, y=1), 1.0),
|
||||
(Vector(start=Point(x=1, y=0), end=Point(x=0, y=0)), Point(x=0, y=0), 0.0),
|
||||
(Vector(start=Point(x=1, y=0), end=Point(x=0, y=0)), Point(x=0, y=-1), 1.0),
|
||||
(Vector(start=Point(x=1, y=0), end=Point(x=0, y=0)), Point(x=0, y=1), -1.0),
|
||||
(Vector(start=Point(x=1, y=1), end=Point(x=1, y=3)), Point(x=0, y=0), 2.0),
|
||||
(Vector(start=Point(x=1, y=1), end=Point(x=1, y=3)), Point(x=1, y=4), 0.0),
|
||||
(Vector(start=Point(x=1, y=1), end=Point(x=1, y=3)), Point(x=2, y=4), -2.0),
|
||||
(Vector(start=Point(x=1, y=3), end=Point(x=1, y=1)), Point(x=0, y=0), -2.0),
|
||||
(Vector(start=Point(x=1, y=3), end=Point(x=1, y=1)), Point(x=1, y=4), 0.0),
|
||||
(Vector(start=Point(x=1, y=3), end=Point(x=1, y=1)), Point(x=2, y=4), 2.0),
|
||||
],
|
||||
)
|
||||
def test_vector_cross_product(
|
||||
vector: Vector, point: Point, expected_result: float
|
||||
) -> None:
|
||||
result = vector.cross_product(point=point)
|
||||
assert result == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"vector, expected_result",
|
||||
[
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=0, y=0)), 0.0),
|
||||
(Vector(start=Point(x=1, y=0), end=Point(x=0, y=0)), 1.0),
|
||||
(Vector(start=Point(x=0, y=1), end=Point(x=0, y=0)), 1.0),
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=1, y=0)), 1.0),
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=0, y=1)), 1.0),
|
||||
(Vector(start=Point(x=-1, y=0), end=Point(x=0, y=0)), 1.0),
|
||||
(Vector(start=Point(x=0, y=-1), end=Point(x=0, y=0)), 1.0),
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=-1, y=0)), 1.0),
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=0, y=-1)), 1.0),
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=3, y=4)), 5.0),
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=-3, y=4)), 5.0),
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=3, y=-4)), 5.0),
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=-3, y=-4)), 5.0),
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=4, y=3)), 5.0),
|
||||
(Vector(start=Point(x=3, y=4), end=Point(x=0, y=0)), 5.0),
|
||||
(Vector(start=Point(x=4, y=3), end=Point(x=0, y=0)), 5.0),
|
||||
],
|
||||
)
|
||||
def test_vector_magnitude(vector: Vector, expected_result: float) -> None:
|
||||
result = vector.magnitude
|
||||
assert result == expected_result
|
||||
@@ -1,31 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from supervision.geometry.core import Point, Vector
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"vector, point, expected_result",
|
||||
[
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=5, y=5)), Point(x=-1, y=1), False),
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=5, y=5)), Point(x=6, y=6), False),
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=5, y=5)), Point(x=3, y=6), False),
|
||||
(Vector(start=Point(x=5, y=5), end=Point(x=0, y=0)), Point(x=-1, y=1), True),
|
||||
(Vector(start=Point(x=5, y=5), end=Point(x=0, y=0)), Point(x=6, y=6), False),
|
||||
(Vector(start=Point(x=5, y=5), end=Point(x=0, y=0)), Point(x=3, y=6), True),
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=1, y=0)), Point(x=0, y=0), False),
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=1, y=0)), Point(x=0, y=-1), True),
|
||||
(Vector(start=Point(x=0, y=0), end=Point(x=1, y=0)), Point(x=0, y=1), False),
|
||||
(Vector(start=Point(x=1, y=0), end=Point(x=0, y=0)), Point(x=0, y=0), False),
|
||||
(Vector(start=Point(x=1, y=0), end=Point(x=0, y=0)), Point(x=0, y=-1), False),
|
||||
(Vector(start=Point(x=1, y=0), end=Point(x=0, y=0)), Point(x=0, y=1), True),
|
||||
(Vector(start=Point(x=1, y=1), end=Point(x=1, y=3)), Point(x=0, y=0), False),
|
||||
(Vector(start=Point(x=1, y=1), end=Point(x=1, y=3)), Point(x=1, y=4), False),
|
||||
(Vector(start=Point(x=1, y=1), end=Point(x=1, y=3)), Point(x=2, y=4), True),
|
||||
(Vector(start=Point(x=1, y=3), end=Point(x=1, y=1)), Point(x=0, y=0), True),
|
||||
(Vector(start=Point(x=1, y=3), end=Point(x=1, y=1)), Point(x=1, y=4), False),
|
||||
(Vector(start=Point(x=1, y=3), end=Point(x=1, y=1)), Point(x=2, y=4), False),
|
||||
],
|
||||
)
|
||||
def test_vector_is_in(vector: Vector, point: Point, expected_result: bool) -> None:
|
||||
result = vector.is_in(point=point)
|
||||
assert result == expected_result
|
||||
Reference in New Issue
Block a user