Merge branch 'develop' into feature/remove-false-line-counts

This commit is contained in:
Grzegorz Klimaszewski 2024-10-02 10:08:04 +02:00
commit 78fd2147f1
No known key found for this signature in database
42 changed files with 2250 additions and 1398 deletions

View File

@ -1,4 +1,4 @@
name: Supervision Test Releases to PyPi
name: Publish Supervision Pre-Releases to PyPI and TestPyPI
on:
push:
tags:
@ -9,9 +9,11 @@ on:
workflow_dispatch:
jobs:
build-n-publish:
build-and-publish-pre-release-pypi:
name: Build and publish to PyPI
runs-on: ubuntu-latest
permissions:
id-token: write
strategy:
matrix:
python-version: ["3.10"]
@ -30,14 +32,11 @@ jobs:
python -m pip install --upgrade build twine
python -m build
twine check --strict dist/*
- name: 🚀 Publish distribution to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: ${{ secrets.PYPI_USERNAME }}
password: ${{ secrets.PYPI_PASSWORD }}
- name: 🚀 Publish to PyPi
uses: pypa/gh-action-pypi-publish@release/v1.10
- name: 🚀 Publish to Test-PyPi
uses: pypa/gh-action-pypi-publish@release/v1
uses: pypa/gh-action-pypi-publish@release/v1.10
with:
repository-url: https://test.pypi.org/legacy/
user: ${{ secrets.PYPI_TEST_USERNAME }}
password: ${{ secrets.PYPI_TEST_PASSWORD }}

View File

@ -1,4 +1,4 @@
name: Supervision Releases to PyPi
name: Publish Supervision Releases to PyPI and TestPyPI
on:
push:
tags:
@ -7,8 +7,10 @@ on:
workflow_dispatch:
jobs:
build:
build-and-publish-pre-release:
runs-on: ubuntu-latest
permissions:
id-token: write
strategy:
matrix:
python-version: ["3.10"]
@ -27,14 +29,11 @@ jobs:
python -m pip install --upgrade build twine
python -m build
twine check --strict dist/*
- name: 🚀 Publish to PyPi
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: ${{ secrets.PYPI_USERNAME }}
password: ${{ secrets.PYPI_PASSWORD }}
uses: pypa/gh-action-pypi-publish@release/v1.10
- name: 🚀 Publish to Test-PyPi
uses: pypa/gh-action-pypi-publish@release/v1
uses: pypa/gh-action-pypi-publish@release/v1.10
with:
repository-url: https://test.pypi.org/legacy/
user: ${{ secrets.PYPI_TEST_USERNAME }}
password: ${{ secrets.PYPI_TEST_PASSWORD }}

View File

@ -25,16 +25,31 @@ repos:
- id: mixed-line-ending
- repo: https://github.com/PyCQA/bandit
rev: '1.7.9'
rev: '1.7.10'
hooks:
- id: bandit
args: ["-c", "pyproject.toml"]
additional_dependencies: ["bandit[toml]"]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.5
rev: v0.6.8
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format
types_or: [ python, pyi, jupyter ]
# - repo: https://github.com/executablebooks/mdformat
# rev: 0.7.17
# hooks:
# - id: mdformat
# additional_dependencies:
# - "mdformat-mkdocs[recommended]>=2.1.0"
# args: ["--number"]
- repo: https://github.com/codespell-project/codespell
rev: v2.3.0
hooks:
- id: codespell
additional_dependencies:
- tomli

View File

@ -1,4 +1,3 @@
# Contributor Covenant Code of Conduct
## Our Pledge
@ -6,7 +5,7 @@
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
identity and expression, level of experience, education, socioeconomic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
@ -18,23 +17,23 @@ diverse, inclusive, and healthy community.
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
- Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
- The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
@ -121,14 +120,14 @@ version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
[Mozilla's code of conduct enforcement ladder][mozilla coc].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/faq][faq]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[faq]: https://www.contributor-covenant.org/faq
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[mozilla coc]: https://github.com/mozilla/diversity
[translations]: https://www.contributor-covenant.org/translations
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html

View File

@ -11,17 +11,17 @@ Please read and adhere to our [Code of Conduct](https://supervision.roboflow.com
## Table of Contents
- [Contribution Guidelines](#contribution-guidelines)
- [Contributing Features](#contributing-features-)
- [Contributing Features](#contributing-features)
- [How to Contribute Changes](#how-to-contribute-changes)
- [Installation for Contributors](#installation-for-contributors)
- [Code Style and Quality](#-code-style-and-quality)
- [Pre-commit tool](#pre-commit-tool)
- [Docstrings](#docstrings)
- [Type checking](#type-checking)
- [Documentation](#-documentation)
- [Cookbooks](#-cookbooks)
- [Tests](#-tests)
- [License](#-license)
- [Code Style and Quality](#code-style-and-quality)
- [Pre-commit tool](#pre-commit-tool)
- [Docstrings](#docstrings)
- [Type checking](#type-checking)
- [Documentation](#documentation)
- [Cookbooks](#cookbooks)
- [Tests](#tests)
- [License](#license)
## Contribution Guidelines
@ -83,7 +83,7 @@ git push -u origin <your_branch_name>
Use conventional commit messages to clearly describe your changes. The format is:
<type>[optional scope]: <description>
<type>\[optional scope\]: <description>
Common types include:
@ -130,45 +130,46 @@ Before starting your work on the project, set up your development environment:
1. Clone your fork of the project:
```bash
git clone https://github.com/YOUR_USERNAME/supervision.git
cd supervision
```
```bash
git clone https://github.com/YOUR_USERNAME/supervision.git
cd supervision
```
Replace `YOUR_USERNAME` with your GitHub username.
Replace `YOUR_USERNAME` with your GitHub username.
2. Create and activate a virtual environment:
```bash
python3 -m venv .venv
source .venv/bin/activate
```
```bash
python3 -m venv .venv
source .venv/bin/activate
```
3. Install Poetry:
Using pip:
Using pip:
```bash
pip install -U pip setuptools
pip install poetry
```
```bash
pip install -U pip setuptools
pip install poetry
```
Or using pipx (recommended for global installation):
Or using pipx (recommended for global installation):
```bash
pipx install poetry
```
```bash
pipx install poetry
```
4. Install project dependencies:
```bash
poetry install
```
```bash
poetry install
```
5. Run pytest to verify the setup:
```bash
poetry run pytest
```
```bash
poetry run pytest
```
## 🎨 Code Style and Quality

168
README.md
View File

@ -8,11 +8,11 @@
</a>
</p>
<br>
<br>
[notebooks](https://github.com/roboflow/notebooks) | [inference](https://github.com/roboflow/inference) | [autodistill](https://github.com/autodistill/autodistill) | [maestro](https://github.com/roboflow/multimodal-maestro)
<br>
<br>
[![version](https://badge.fury.io/py/supervision.svg)](https://badge.fury.io/py/supervision)
[![downloads](https://img.shields.io/pypi/dm/supervision)](https://pypistats.org/packages/supervision)
@ -23,6 +23,11 @@
[![gradio](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Spaces-blue)](https://huggingface.co/spaces/Roboflow/Annotators)
[![discord](https://img.shields.io/discord/1159501506232451173)](https://discord.gg/GbfgXGJ8Bk)
[![built-with-material-for-mkdocs](https://img.shields.io/badge/Material_for_MkDocs-526CFE?logo=MaterialForMkDocs&logoColor=white)](https://squidfunk.github.io/mkdocs-material/)
<div align="center">
<a href="https://trendshift.io/repositories/124" target="_blank"><img src="https://trendshift.io/api/badge/repositories/124" alt="roboflow%2Fsupervision | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
</div>
</div>
## 👋 hello
@ -54,7 +59,7 @@ import supervision as sv
from ultralytics import YOLO
image = cv2.imread(...)
model = YOLO('yolov8s.pt')
model = YOLO("yolov8s.pt")
result = model(image)[0]
detections = sv.Detections.from_ultralytics(result)
@ -67,21 +72,21 @@ len(detections)
- inference
Running with [Inference](https://github.com/roboflow/inference) requires a [Roboflow API KEY](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key).
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 import get_model
```python
import cv2
import supervision as sv
from inference import get_model
image = cv2.imread(...)
model = get_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_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
```
</details>
@ -98,9 +103,8 @@ detections = sv.Detections(...)
box_annotator = sv.BoxAnnotator()
annotated_frame = box_annotator.annotate(
scene=image.copy(),
detections=detections
)
scene=image.copy(),
detections=detections)
```
https://github.com/roboflow/supervision/assets/26109316/691e219c-0565-4403-9218-ab5644f39bce
@ -133,88 +137,88 @@ for path, image, annotation in ds:
- load
```python
dataset = sv.DetectionDataset.from_yolo(
images_directory_path=...,
annotations_directory_path=...,
data_yaml_path=...
)
```python
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)
```python
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']
```python
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=...
)
```python
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=...
)
```
```python
sv.DetectionDataset.from_yolo(
images_directory_path=...,
annotations_directory_path=...,
data_yaml_path=...
).as_pascal_voc(
images_directory_path=...,
annotations_directory_path=...
)
```
</details>
@ -266,7 +270,7 @@ We love your input! Please see our [contributing guide](https://github.com/robof
<div align="center">
<div align="center">
<div align="center">
<a href="https://youtube.com/roboflow">
<img
src="https://media.roboflow.com/notebooks/template/icons/purple/youtube.png?ik-sdk-version=javascript-1.4.3&updatedAt=1672949634652"

View File

@ -13,7 +13,6 @@ To install the Supervision assets utility, you can use `pip`. This utility is av
as an extra within the Supervision package.
!!! example "pip install"
```bash
pip install "supervision[assets]"
```

View File

@ -1,3 +1,5 @@
# CHANGELOG
### 0.23.0 <small>Aug 28, 2024</small>
- Added [#930](https://github.com/roboflow/supervision/pull/930): `IconAnnotator`, a [new annotator](https://supervision.roboflow.com/0.23.0/detection/annotators/#supervision.annotators.core.IconAnnotator) that allows drawing icons on each detection. Useful if you want to draw a specific icon for each class.
@ -84,7 +86,6 @@ 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)
@ -96,7 +97,7 @@ detections = sv.Detections.from_sam(sam_result=sam_result)
- Added [#1409](https://github.com/roboflow/supervision/pull/1409): `text_color` option for [`VertexLabelAnnotator`](https://supervision.roboflow.com/0.23.0/keypoint/annotators/#supervision.keypoint.annotators.VertexLabelAnnotator) keypoint annotator.
- Changed [#1434](https://github.com/roboflow/supervision/pull/1434): [`InferenceSlicer`](https://supervision.roboflow.com/0.23.0/detection/tools/inference_slicer/) now features an `overlap_ratio_wh` parameter, making it easier to compute slice sizes when handling overlapping slices.
- Changed [#1434](https://github.com/roboflow/supervision/pull/1434): [`InferenceSlicer`](https://supervision.roboflow.com/0.23.0/detection/tools/inference_slicer/) now features an `overlap_wh` parameter, making it easier to compute slice sizes when handling overlapping slices.
- Fix [#1448](https://github.com/roboflow/supervision/pull/1448): Various annotator type issues have been resolved, supporting expanded error handling.
@ -116,19 +117,15 @@ for frame in sv.get_video_frames_generator(
- Fix [#1424](https://github.com/roboflow/supervision/pull/1424): `plot_image` function now clearly indicates that the size is in inches.
!!! failure "Removed"
The `track_buffer`, `track_thresh`, and `match_thresh` parameters in [`ByteTrack`](trackers.md/#supervision.tracker.byte_tracker.core.ByteTrack) are deprecated and were removed as of `supervision-0.23.0`. Use `lost_track_buffer,` `track_activation_threshold`, and `minimum_matching_threshold` instead.
!!! failure "Removed"
The `triggering_position ` parameter in [`sv.PolygonZone`](detection/tools/polygon_zone.md/#supervision.detection.tools.polygon_zone.PolygonZone) was removed as of `supervision-0.23.0`. Use `triggering_anchors ` instead.
The `triggering_position` parameter in [`sv.PolygonZone`](detection/tools/polygon_zone.md/#supervision.detection.tools.polygon_zone.PolygonZone) was removed as of `supervision-0.23.0`. Use `triggering_anchors` instead.
!!! failure "Deprecated"
`overlap_filter_strategy` in `InferenceSlicer.__init__` is deprecated and will be removed in `supervision-0.27.0`. Use `overlap_strategy` instead.
!!! failure "Deprecated"
`overlap_ratio_wh` in `InferenceSlicer.__init__` is deprecated and will be removed in `supervision-0.27.0`. Use `overlap_wh` instead.
### 0.22.0 <small>Jul 12, 2024</small>
@ -136,11 +133,9 @@ for frame in sv.get_video_frames_generator(
- Added [#1326](https://github.com/roboflow/supervision/pull/1326): [`sv.DetectionsDataset`](https://supervision.roboflow.com/0.22.0/datasets/core/#supervision.dataset.core.DetectionDataset) and [`sv.ClassificationDataset`](https://supervision.roboflow.com/0.22.0/datasets/core/#supervision.dataset.core.ClassificationDataset) allowing to load the images into memory only when necessary (lazy loading).
!!! failure "Deprecated"
Constructing `DetectionDataset` with parameter `images` as `Dict[str, np.ndarray]` is deprecated and will be removed in `supervision-0.26.0`. Please pass a list of paths `List[str]` instead.
!!! failure "Deprecated"
The `DetectionDataset.images` property is deprecated and will be removed in `supervision-0.26.0`. Please loop over images with `for path, image, annotation in dataset:`, as that does not require loading all images into memory.
```python
@ -197,7 +192,7 @@ annotated_frame = mask_annotator.annotate(scene=image.copy(), detections=detecti
```
- Added [#1277](https://github.com/roboflow/supervision/pull/1277): if you provide a font that supports symbols of a language, [`sv.RichLabelAnnotator`](https://supervision.roboflow.com/0.22.0/detection/annotators/#supervision.annotators.core.LabelAnnotator.annotate) will draw them on your images.
- Various other annotators have been revised to ensure proper in-place functionality when used with `numpy` arrays. Additionally, we fixed a bug where `sv.ColorAnnotator` was filling boxes with solid color when used in-place.
- Various other annotators have been revised to ensure proper in-place functionality when used with `numpy` arrays. Additionally, we fixed a bug where `sv.ColorAnnotator` was filling boxes with solid color when used in-place.
```python
import cv2
@ -223,7 +218,7 @@ train_ds = sv.DetectionDataset.from_yolo(
images_directory_path="/content/dataset/train/images",
annotations_directory_path="/content/dataset/train/labels",
data_yaml_path="/content/dataset/data.yaml",
is_obb=True
is_obb=True,
)
_, image, detections in train_ds[0]
@ -235,11 +230,9 @@ annotated_image = obb_annotator.annotate(scene=image.copy(), detections=detectio
- Fixed [#1312](https://github.com/roboflow/supervision/pull/1312): Fixed [`CropAnnotator`](https://supervision.roboflow.com/0.22.0/detection/annotators/#supervision.annotators.core.TraceAnnotator.annotate).
!!! failure "Removed"
`BoxAnnotator` was removed, however `BoundingBoxAnnotator` has been renamed to `BoxAnnotator`. Use a combination of [`BoxAnnotator`](https://supervision.roboflow.com/0.22.0/detection/annotators/#supervision.annotators.core.BoxAnnotator) and [`LabelAnnotator`](https://supervision.roboflow.com/0.22.0/detection/annotators/#supervision.annotators.core.LabelAnnotator) to simulate old `BoundingBox` behavior.
!!! failure "Deprecated"
The name `BoundingBoxAnnotator` has been deprecated and will be removed in `supervision-0.26.0`. It has been renamed to [`BoxAnnotator`](https://supervision.roboflow.com/0.22.0/detection/annotators/#supervision.annotators.core.BoxAnnotator).
- Added [#975](https://github.com/roboflow/supervision/pull/975) 📝 New Cookbooks: serialize detections into [json](https://github.com/roboflow/supervision/blob/de896189b83a1f9434c0a37dd9192ee00d2a1283/docs/notebooks/serialise-detections-to-json.ipynb) and [csv](https://github.com/roboflow/supervision/blob/de896189b83a1f9434c0a37dd9192ee00d2a1283/docs/notebooks/serialise-detections-to-csv.ipynb).
@ -249,35 +242,27 @@ annotated_image = obb_annotator.annotate(scene=image.copy(), detections=detectio
- Added [#1340](https://github.com/roboflow/supervision/pull/1340): Two new methods for converting between bounding box formats - [`xywh_to_xyxy`](https://supervision.roboflow.com/0.22.0/detection/utils/#supervision.detection.utils.xywh_to_xyxy) and [`xcycwh_to_xyxy`](https://supervision.roboflow.com/0.22.0/detection/utils/#supervision.detection.utils.xcycwh_to_xyxy)
!!! failure "Removed"
`from_roboflow` method has been removed due to deprecation. Use [from_inference](https://supervision.roboflow.com/0.22.0/detection/core/#supervision.detection.core.Detections.from_inference) instead.
!!! failure "Removed"
`Color.white()` has been removed due to deprecation. Use `color.WHITE` instead.
!!! failure "Removed"
`Color.black()` has been removed due to deprecation. Use `color.BLACK` instead.
!!! failure "Removed"
`Color.red()` has been removed due to deprecation. Use `color.RED` instead.
!!! failure "Removed"
`Color.green()` has been removed due to deprecation. Use `color.GREEN` instead.
!!! failure "Removed"
`Color.blue()` has been removed due to deprecation. Use `color.BLUE` instead.
!!! failure "Removed"
`ColorPalette.default()` has been removed due to deprecation. Use [ColorPalette.DEFAULT](https://supervision.roboflow.com/0.22.0/utils/draw/#supervision.draw.color.ColorPalette.DEFAULT) instead.
!!! failure "Removed"
`FPSMonitor.__call__` has been removed due to deprecation. Use the attribute [FPSMonitor.fps](https://supervision.roboflow.com/0.22.0/utils/video/#supervision.utils.video.FPSMonitor.fps) instead.
### 0.21.0 <small>Jun 5, 2024</small>
@ -294,7 +279,7 @@ detections = sv.Detections.from_lmm(
sv.LMM.PALIGEMMA,
paligemma_result,
resolution_wh=(1000, 1000),
classes=['cat', 'dog']
classes=["cat", "dog"],
)
detections.xyxy
# array([[250., 250., 750., 750.]])
@ -386,7 +371,6 @@ annotated_image = edge_annotators.annotate(image.copy(), keypoints)
- Changed [#1109](https://github.com/roboflow/supervision/pull/1109): [`sv.PolygonZone`](/0.20.0/detection/tools/polygon_zone/#supervision.detection.tools.polygon_zone.PolygonZone) such that the `frame_resolution_wh` argument is no longer required to initialize `sv.PolygonZone`.
!!! failure "Deprecated"
The `frame_resolution_wh` parameter in `sv.PolygonZone` is deprecated and will be removed in `supervision-0.24.0`.
- Changed [#1084](https://github.com/roboflow/supervision/pull/1084): [`sv.get_polygon_center`](/0.20.0/utils/geometry/#supervision.geometry.core.utils.get_polygon_center) to calculate a more accurate polygon centroid.
@ -492,13 +476,11 @@ annotated_frame = crop_annotator.annotate(
- Changed [#787](https://github.com/roboflow/supervision/pull/787): [`sv.ByteTrack`](/0.19.0/trackers/#supervision.tracker.ByteTrack) input arguments and docstrings updated to improve readability and ease of use.
!!! failure "Deprecated"
The `track_buffer`, `track_thresh`, and `match_thresh` parameters in `sv.ByteTrack` are deprecated and will be removed in `supervision-0.23.0`. Use `lost_track_buffer,` `track_activation_threshold`, and `minimum_matching_threshold` instead.
- Changed [#910](https://github.com/roboflow/supervision/pull/910): [`sv.PolygonZone`](/0.19.0/detection/tools/polygon_zone/#supervision.detection.tools.polygon_zone.PolygonZone) to now accept a list of specific box anchors that must be in zone for a detection to be counted.
!!! failure "Deprecated"
The `triggering_position ` parameter in `sv.PolygonZone` is deprecated and will be removed in `supervision-0.23.0`. Use `triggering_anchors` instead.
- Changed [#875](https://github.com/roboflow/supervision/pull/875): annotators adding support for Pillow images. All supervision Annotators can now accept an image as either a numpy array or a Pillow Image. They automatically detect its type, draw annotations, and return the output in the same format as the input.
@ -562,7 +544,6 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
- 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.
@ -570,7 +551,6 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
- 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.
@ -668,7 +648,6 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
- 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.
!!! failure "Deprecated"
`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>
@ -736,7 +715,6 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
- 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.
!!! failure "Deprecated"
[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.
@ -774,7 +752,6 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
- 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.
!!! failure "Deprecated"
[`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`](/0.13.0/detection/core/#supervision.detection.core.Detections.from_paddledet) to enable seamless integration with [PaddleDetection](https://github.com/PaddlePaddle/PaddleDetection) framework.
@ -784,7 +761,6 @@ ColorPalette(colors=[Color(r=68, g=1, b=84), Color(r=59, g=82, b=139), ...])
### 0.12.0 <small>July 24, 2023</small>
!!! 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`](/0.12.0/metrics/detection/#confusionmatrix).

View File

@ -5,7 +5,6 @@ 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`.

View File

@ -10,10 +10,13 @@ These features are phased out due to better alternatives or potential issues in
- The `frame_resolution_wh ` parameter in [`sv.PolygonZone`](detection/tools/polygon_zone.md/#supervision.detection.tools.polygon_zone.PolygonZone) will be removed in `supervision-0.24.0`.
- Constructing [`DetectionDataset`](https://supervision.roboflow.com/latest/datasets/core/#supervision.dataset.core.DetectionDataset) and [`ClassificationDataset`](https://supervision.roboflow.com/latest/datasets/core/#supervision.dataset.core.ClassificationDataset) with parameter `images` as `Dict[str, np.ndarray]` will be removed in `supervision-0.26.0`. Please pass a list of paths `List[str]` instead.
- The `DetectionDataset.images` property will be removed in `supervision-0.26.0`. Please loop over images with `for path, image, annotation in dataset:`, as that does not require loading all images into memory.
- `BoundingBoxAnnotator` has been renamed to `BoxAnnotator` after the old implementation of [`BoxAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator) has been removed. `BoundingBoxAnnotator` will be removed in `supervision-0.26.0`.
- `overlap_filter_strategy` in [`InferenceSlicer.__init__`](https://supervision.roboflow.com/latest/detection/tools/inference_slicer/) is deprecated and will be removed in `supervision-0.27.0`. Use `overlap_strategy` instead.
- `overlap_ratio_wh` in [`InferenceSlicer.__init__`](https://supervision.roboflow.com/latest/detection/tools/inference_slicer/) is deprecated and will be removed in `supervision-0.27.0`. Use `overlap_wh` instead.
# Removed
@ -25,12 +28,12 @@ These features are phased out due to better alternatives or potential issues in
### 0.22.0
- [`Detections.from_roboflow`](detection/core.md/#supervision.detection.core.Detections.from_roboflow) is removed as of `supervision-0.22.0`. Use [`Detections.from_inference`](detection/core.md/#supervision.detection.core.Detections.from_inference) instead.
- `Detections.from_roboflow` is removed as of `supervision-0.22.0`. Use [`Detections.from_inference`](detection/core.md/#supervision.detection.core.Detections.from_inference) instead.
- The method `Color.white()` was removed as of `supervision-0.22.0`. Use the constant `Color.WHITE` instead.
- The method `Color.black()` was removed as of `supervision-0.22.0`. Use the constant `Color.BLACK` instead.
- The method `Color.red()` was removed as of `supervision-0.22.0`. Use the constant `Color.RED` instead.
- The method `Color.green()` was removed as of `supervision-0.22.0`. Use the constant `Color.GREEN` instead.
- The method `Color.blue()` was removed as of `supervision-0.22.0`. Use the constant `Color.BLUE` instead.
- The method `ColorPalette.default()` was removed as of `supervision-0.22.0`. Use the constant [`ColorPalette.DEFAULT`](draw/color/#supervision.draw.color.ColorPalette.DEFAULT) instead.
- The method `ColorPalette.default()` was removed as of `supervision-0.22.0`. Use the constant [`ColorPalette.DEFAULT`](/utils/draw/#supervision.draw.color.ColorPalette.DEFAULT) instead.
- `BoxAnnotator` was removed as of `supervision-0.22.0`, however `BoundingBoxAnnotator` was immediately renamed to `BoxAnnotator`. Use [`BoxAnnotator`](detection/annotators.md/#supervision.annotators.core.BoxAnnotator) and [`LabelAnnotator`](detection/annotators.md/#supervision.annotators.core.LabelAnnotator) instead of the old `BoxAnnotator`.
- The method [`FPSMonitor.__call__`](utils/video.md/#supervision.utils.video.FPSMonitor.__call__) was removed as of `supervision-0.22.0`. Use the attribute [`FPSMonitor.fps`](utils/video.md/#supervision.utils.video.FPSMonitor.fps) instead.
- The method `FPSMonitor.__call__` was removed as of `supervision-0.22.0`. Use the attribute [`FPSMonitor.fps`](utils/video.md/#supervision.utils.video.FPSMonitor.fps) instead.

View File

@ -5,6 +5,10 @@ status: new
# Annotators
Supervision provides a variety of annotators to annotate detections on images and videos. You can try them out below, with a [Workflow](https://roboflow.com/workflows) that runs [Microsoft's COCO](https://cocodataset.org/#home) dataset through a Instance Segmentation model and annotates the detections using supervision's annotators.
<div style="height: 400px; width: 100%; border-radius: 8px; overflow: hidden;"><iframe src="https://app.roboflow.com/workflows/embed/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ3b3JrZmxvd0lkIjoiNDdtd2xuWW16S25VNWtOYUZjMG8iLCJ3b3Jrc3BhY2VJZCI6ImtyT1RBYm5jRmhvUU1DZExPbGU0IiwidXNlcklkIjoiRVJNUFBZY3FQMmZWWjB1NkRpNXZaYXJDdlZPMiIsImlhdCI6MTcyNjgzOTM2N30.gj2F6SnmmURAScJe4PTC1raUXsAK5mZyrUIGIJ44NhM" loading="lazy" title="Roboflow Workflow for Supervision Annotators" style="width: 100%; height: 100%; min-height: 400px; border: none;"></iframe></div>
=== "Box"
```python

View File

@ -13,3 +13,9 @@ comments: true
</div>
:::supervision.detection.line_zone.LineZoneAnnotator
<div class="md-typeset">
<h2>LineZoneAnnotatorMulticlass</h2>
</div>
:::supervision.detection.line_zone.LineZoneAnnotatorMulticlass

View File

@ -16,6 +16,12 @@ comments: true
:::supervision.detection.utils.mask_iou_batch
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.oriented_box_iou_batch">oriented_box_iou_batch</a></h2>
</div>
:::supervision.detection.utils.oriented_box_iou_batch
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.polygon_to_mask">polygon_to_mask</a></h2>
</div>

View File

@ -20,7 +20,6 @@ First, you'll need to obtain predictions from your object detection or segmentat
model.
=== "Inference"
```python
import cv2
from inference import get_model
@ -31,7 +30,6 @@ model.
```
=== "Ultralytics"
```python
import cv2
from ultralytics import YOLO
@ -42,7 +40,6 @@ model.
```
=== "Transformers"
```python
import torch
from PIL import Image
@ -68,7 +65,6 @@ model.
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`](/latest/detection/core/#supervision.detection.core.Detections.from_inference) method, which accepts model results from both detection and segmentation models.
```{ .py hl_lines="2 8" }
@ -83,7 +79,6 @@ 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`](/latest/detection/core/#supervision.detection.core.Detections.from_ultralytics) method, which accepts model results from both detection and segmentation models.
```{ .py hl_lines="2 8" }
@ -98,7 +93,6 @@ Now that we have predictions from a model, we can load them into Supervision.
```
=== "Transformers"
We can do so using the [`sv.Detections.from_transformers`](/latest/detection/core/#supervision.detection.core.Detections.from_transformers) method, which accepts model results from both detection and segmentation models.
```{ .py hl_lines="2 19-21" }
@ -138,7 +132,6 @@ You can load predictions from other computer vision frameworks and libraries usi
Finally, we can annotate the image with the predictions. Since we are working with an object detection model, we will use the [`sv.BoxAnnotator`](/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator) and [`sv.LabelAnnotator`](/latest/detection/annotators/#supervision.annotators.core.LabelAnnotator) classes.
=== "Inference"
```{ .py hl_lines="10-16" }
import cv2
import supervision as sv
@ -159,7 +152,6 @@ Finally, we can annotate the image with the predictions. Since we are working wi
```
=== "Ultralytics"
```{ .py hl_lines="10-16" }
import cv2
import supervision as sv
@ -180,7 +172,6 @@ Finally, we can annotate the image with the predictions. Since we are working wi
```
=== "Transformers"
```{ .py hl_lines="23-30" }
import torch
import supervision as sv
@ -222,7 +213,6 @@ will label each detection with its `class_name` (if possible) or `class_id`. You
override this behavior by passing a list of custom `labels` to the `annotate` method.
=== "Inference"
```{ .py hl_lines="13-17 22" }
import cv2
import supervision as sv
@ -249,7 +239,6 @@ override this behavior by passing a list of custom `labels` to the `annotate` me
```
=== "Ultralytics"
```{ .py hl_lines="13-17 22" }
import cv2
import supervision as sv
@ -276,7 +265,6 @@ override this behavior by passing a list of custom `labels` to the `annotate` me
```
=== "Transformers"
```{ .py hl_lines="26-30 35" }
import torch
import supervision as sv
@ -326,7 +314,6 @@ is a drop-in replacement for
that will allow you to draw masks instead of boxes.
=== "Inference"
```python
import cv2
import supervision as sv
@ -347,7 +334,6 @@ that will allow you to draw masks instead of boxes.
```
=== "Ultralytics"
```python
import cv2
import supervision as sv
@ -368,7 +354,6 @@ that will allow you to draw masks instead of boxes.
```
=== "Transformers"
```python
import torch
import supervision as sv

View File

@ -20,7 +20,6 @@ Small object detection in high-resolution images presents challenges due to the
size relative to the image resolution.
=== "Inference"
```python
import cv2
import supervision as sv
@ -41,7 +40,6 @@ size relative to the image resolution.
```
=== "Ultralytics"
```python
import cv2
import supervision as sv
@ -62,7 +60,6 @@ size relative to the image resolution.
```
=== "Transformers"
```python
import torch
import supervision as sv
@ -108,7 +105,6 @@ identification at the cost of processing speed and increased memory usage. This
is less effective for ultra-high-resolution images (4K and above).
=== "Inference"
```{ .py hl_lines="5" }
import cv2
import supervision as sv
@ -129,7 +125,6 @@ is less effective for ultra-high-resolution images (4K and above).
```
=== "Ultralytics"
```{ .py hl_lines="7" }
import cv2
import supervision as sv
@ -162,7 +157,6 @@ objects within each, and aggregating the results.
</video>
=== "Inference"
```{ .py hl_lines="9-14" }
import cv2
import numpy as np
@ -189,7 +183,6 @@ objects within each, and aggregating the results.
```
=== "Ultralytics"
```{ .py hl_lines="9-14" }
import cv2
import numpy as np
@ -216,7 +209,6 @@ objects within each, and aggregating the results.
```
=== "Transformers"
```{ .py hl_lines="13-28" }
import cv2
import torch
@ -269,7 +261,6 @@ objects within each, and aggregating the results.
[`InferenceSlicer`](/latest/detection/tools/inference_slicer/#supervision.detection.tools.inference_slicer.InferenceSlicer) can perform segmentation tasks too.
=== "Inference"
```{ .py hl_lines="6 16 19-20" }
import cv2
import numpy as np
@ -296,7 +287,6 @@ objects within each, and aggregating the results.
```
=== "Ultralytics"
```{ .py hl_lines="6 16 19-20" }
import cv2
import numpy as np

View File

@ -15,7 +15,6 @@ the filters in their applications.
Allows you to select detections that belong only to one selected class.
=== "After"
```python
import supervision as sv
@ -30,7 +29,6 @@ Allows you to select detections that belong only to one selected class.
</div>
=== "Before"
```python
import supervision as sv
@ -49,7 +47,6 @@ Allows you to select detections that belong only to one selected class.
Allows you to select detections that belong only to selected set of classes.
=== "After"
```python
import numpy as np
import supervision as sv
@ -66,7 +63,6 @@ Allows you to select detections that belong only to selected set of classes.
</div>
=== "Before"
```python
import numpy as np
import supervision as sv
@ -87,7 +83,6 @@ Allows you to select detections that belong only to selected set of classes.
Allows you to select detections with specific confidence value, for example higher than selected threshold.
=== "After"
```python
import supervision as sv
@ -102,7 +97,6 @@ Allows you to select detections with specific confidence value, for example high
</div>
=== "Before"
```python
import supervision as sv
@ -122,7 +116,6 @@ Allows you to select detections based on their size. We define the area as the n
detection in the image. In the example below, we have sifted out the detections that are too small.
=== "After"
```python
import supervision as sv
@ -137,7 +130,6 @@ detection in the image. In the example below, we have sifted out the detections
</div>
=== "Before"
```python
import supervision as sv
@ -159,7 +151,6 @@ but small on a 3840x2160 image. In such cases, we can filter out detections base
occupied by them. In the example below, we remove too large detections.
=== "After"
```python
import supervision as sv
@ -178,7 +169,6 @@ occupied by them. In the example below, we remove too large detections.
</div>
=== "Before"
```python
import supervision as sv
@ -203,7 +193,6 @@ can be criteria for rejecting detection. Implementing such filtering requires a
simple and fast.
=== "After"
```python
import supervision as sv
@ -220,7 +209,6 @@ simple and fast.
</div>
=== "Before"
```python
import supervision as sv
@ -242,7 +230,6 @@ Allows you to use `Detections` in combination with `PolygonZone` to weed out bou
zone. In the example below you can see how to filter out all detections located in the lower part of the image.
=== "After"
```python
import supervision as sv
@ -259,7 +246,6 @@ zone. In the example below you can see how to filter out all detections located
</div>
=== "Before"
```python
import supervision as sv
@ -280,7 +266,6 @@ zone. In the example below you can see how to filter out all detections located
`Detections`' greatest strength, however, is that you can build arbitrarily complex logical conditions by simply combining separate conditions using `&` or `|`.
=== "After"
```python
import supervision as sv
@ -297,7 +282,6 @@ zone. In the example below you can see how to filter out all detections located
</div>
=== "Before"
```python
import supervision as sv

View File

@ -19,7 +19,6 @@ model. You can learn more on this topic in our
[How to Detect and Annotate](/latest/how_to/detect_and_annotate.md) guide.
=== "Inference"
```python
import supervision as sv
from inference import get_model
@ -34,7 +33,6 @@ model. You can learn more on this topic in our
```
=== "Ultralytics"
```python
import supervision as sv
from ultralytics import YOLO
@ -49,7 +47,6 @@ model. You can learn more on this topic in our
```
=== "Transformers"
```python
import torch
import supervision as sv
@ -83,7 +80,6 @@ and then pass the
object resulting from the inference to it. Its fields are parsed and saved on disk.
=== "Inference"
```{ .py hl_lines="7 12" }
import supervision as sv
from inference import get_model
@ -100,7 +96,6 @@ object resulting from the inference to it. Its fields are parsed and saved on di
```
=== "Ultralytics"
```{ .py hl_lines="7 12" }
import supervision as sv
from ultralytics import YOLO
@ -117,7 +112,6 @@ object resulting from the inference to it. Its fields are parsed and saved on di
```
=== "Transformers"
```{ .py hl_lines="9 23" }
import torch
import supervision as sv
@ -144,11 +138,11 @@ object resulting from the inference to it. Its fields are parsed and saved on di
sink.append(detections, {})
```
| x_min | y_min | x_max | y_max | class_id | confidence | tracker_id | class_name |
|---------|----------|---------|----------|----------|------------|------------|------------|
| 2941.14 | 1269.31 | 3220.77 | 1500.67 | 2 | 0.8517 | | car |
| 944.889 | 899.641 | 1235.42 | 1308.80 | 7 | 0.6752 | | truck |
| 1439.78 | 1077.79 | 1621.27 | 1231.40 | 2 | 0.6450 | | car |
| x_min | y_min | x_max | y_max | class_id | confidence | tracker_id | class_name |
| ------- | ------- | ------- | ------- | -------- | ---------- | ---------- | ---------- |
| 2941.14 | 1269.31 | 3220.77 | 1500.67 | 2 | 0.8517 | | car |
| 944.889 | 899.641 | 1235.42 | 1308.80 | 7 | 0.6752 | | truck |
| 1439.78 | 1077.79 | 1621.27 | 1231.40 | 2 | 0.6450 | | car |
## Custom Fields
@ -160,7 +154,6 @@ also allows you to add custom information to each row, which can be passed via t
frame index from which the detections originate.
=== "Inference"
```{ .py hl_lines="8 12" }
import supervision as sv
from inference import get_model
@ -177,7 +170,6 @@ frame index from which the detections originate.
```
=== "Ultralytics"
```{ .py hl_lines="8 12" }
import supervision as sv
from ultralytics import YOLO
@ -194,7 +186,6 @@ frame index from which the detections originate.
```
=== "Transformers"
```{ .py hl_lines="10 23" }
import torch
import supervision as sv
@ -221,11 +212,11 @@ frame index from which the detections originate.
sink.append(detections, {"frame_index": frame_index})
```
| x_min | y_min | x_max | y_max | class_id | confidence | tracker_id | class_name | frame_index |
|---------|----------|---------|----------|----------|------------|------------|------------|-------------|
| 2941.14 | 1269.31 | 3220.77 | 1500.67 | 2 | 0.8517 | | car | 0 |
| 944.889 | 899.641 | 1235.42 | 1308.80 | 7 | 0.6752 | | truck | 0 |
| 1439.78 | 1077.79 | 1621.27 | 1231.40 | 2 | 0.6450 | | car | 0 |
| x_min | y_min | x_max | y_max | class_id | confidence | tracker_id | class_name | frame_index |
| ------- | ------- | ------- | ------- | -------- | ---------- | ---------- | ---------- | ----------- |
| 2941.14 | 1269.31 | 3220.77 | 1500.67 | 2 | 0.8517 | | car | 0 |
| 944.889 | 899.641 | 1235.42 | 1308.80 | 7 | 0.6752 | | truck | 0 |
| 1439.78 | 1077.79 | 1621.27 | 1231.40 | 2 | 0.6450 | | car | 0 |
## Save Detections as JSON
@ -236,7 +227,6 @@ with
[`sv.JSONSink`](/latest/detection/tools/save_detections/#supervision.detection.tools.csv_sink.JSONSink).
=== "Inference"
```{ .py hl_lines="7" }
import supervision as sv
from inference import get_model
@ -253,7 +243,6 @@ with
```
=== "Ultralytics"
```{ .py hl_lines="7" }
import supervision as sv
from ultralytics import YOLO
@ -270,7 +259,6 @@ with
```
=== "Transformers"
```{ .py hl_lines="9" }
import torch
import supervision as sv

View File

@ -41,7 +41,6 @@ This `callback` function will be essential in the subsequent steps of the tutori
it will be modified to include tracking, labeling, and trace annotations.
=== "Ultralytics"
```{ .py }
import numpy as np
import supervision as sv
@ -63,7 +62,6 @@ it will be modified to include tracking, labeling, and trace annotations.
```
=== "Inference"
```{ .py }
import numpy as np
import supervision as sv
@ -97,7 +95,6 @@ functionality, each detected object is assigned a unique tracker ID,
enabling the continuous following of the object's motion path across different frames.
=== "Ultralytics"
```{ .py hl_lines="6 12" }
import numpy as np
import supervision as sv
@ -121,7 +118,6 @@ enabling the continuous following of the object's motion path across different f
```
=== "Inference"
```{ .py hl_lines="6 12" }
import numpy as np
import supervision as sv
@ -153,7 +149,6 @@ in Supervision, we can overlay the tracker IDs and class labels on the detected
offering a clear visual representation of each object's class and unique identifier.
=== "Ultralytics"
```{ .py hl_lines="8 15-19 23-24" }
import numpy as np
import supervision as sv
@ -188,7 +183,6 @@ offering a clear visual representation of each object's class and unique identif
```
=== "Inference"
```{ .py hl_lines="8 15-19 23-24" }
import numpy as np
import supervision as sv
@ -235,7 +229,6 @@ allows for visualizing the trajectories of objects, helping in understanding the
movement patterns and interactions between objects in the video.
=== "Ultralytics"
```{ .py hl_lines="9 26-27" }
import numpy as np
import supervision as sv
@ -273,7 +266,6 @@ movement patterns and interactions between objects in the video.
```
=== "Inference"
```{ .py hl_lines="9 26-27" }
import numpy as np
import supervision as sv

View File

@ -34,9 +34,7 @@ You can install `supervision` in a
[**Python>=3.8**](https://www.python.org/) environment.
!!! example "pip install (recommended)"
=== "pip"
[![version](https://badge.fury.io/py/supervision.svg)](https://badge.fury.io/py/supervision)
[![downloads](https://img.shields.io/pypi/dm/supervision)](https://pypistats.org/packages/supervision)
[![license](https://img.shields.io/pypi/l/supervision)](https://github.com/roboflow/supervision/blob/main/LICENSE.md)
@ -47,9 +45,7 @@ You can install `supervision` in a
```
!!! example "conda/mamba install"
=== "conda"
[![conda-recipe](https://img.shields.io/badge/recipe-supervision-green.svg)](https://anaconda.org/conda-forge/supervision) [![conda-downloads](https://img.shields.io/conda/dn/conda-forge/supervision.svg)](https://anaconda.org/conda-forge/supervision) [![conda-version](https://img.shields.io/conda/vn/conda-forge/supervision.svg)](https://anaconda.org/conda-forge/supervision) [![conda-platforms](https://img.shields.io/conda/pn/conda-forge/supervision.svg)](https://anaconda.org/conda-forge/supervision)
```bash
@ -57,7 +53,6 @@ You can install `supervision` in a
```
=== "mamba"
[![mamba-recipe](https://img.shields.io/badge/recipe-supervision-green.svg)](https://anaconda.org/conda-forge/supervision) [![mamba-downloads](https://img.shields.io/conda/dn/conda-forge/supervision.svg)](https://anaconda.org/conda-forge/supervision) [![mamba-version](https://img.shields.io/conda/vn/conda-forge/supervision.svg)](https://anaconda.org/conda-forge/supervision) [![mamba-platforms](https://img.shields.io/conda/pn/conda-forge/supervision.svg)](https://anaconda.org/conda-forge/supervision)
```bash
@ -65,9 +60,7 @@ You can install `supervision` in a
```
!!! example "git clone (for development)"
=== "virtualenv"
```bash
# clone repository and navigate to root directory
git clone https://github.com/roboflow/supervision.git
@ -83,7 +76,6 @@ You can install `supervision` in a
```
=== "poetry"
```bash
# clone repository and navigate to root directory
git clone https://github.com/roboflow/supervision.git
@ -103,48 +95,48 @@ You can install `supervision` in a
- **Detect and Annotate**
***
---
Annotate predictions from a range of object detection and segmentation models
Annotate predictions from a range of object detection and segmentation models
[:octicons-arrow-right-24: Tutorial](how_to/detect_and_annotate.md)
[:octicons-arrow-right-24: Tutorial](how_to/detect_and_annotate.md)
- **Track Objects**
***
---
Discover how to enhance video analysis by implementing seamless object tracking
Discover how to enhance video analysis by implementing seamless object tracking
[:octicons-arrow-right-24: Tutorial](how_to/track_objects.md)
[:octicons-arrow-right-24: Tutorial](how_to/track_objects.md)
- **Detect Small Objects**
***
---
Learn how to detect small objects in images
Learn how to detect small objects in images
[:octicons-arrow-right-24: Tutorial](how_to/detect_small_objects.md)
[:octicons-arrow-right-24: Tutorial](how_to/detect_small_objects.md)
- **Count Objects Crossing Line**
***
---
Explore methods to accurately count and analyze objects crossing a predefined line
Explore methods to accurately count and analyze objects crossing a predefined line
[:octicons-arrow-right-24: Notebook](https://supervision.roboflow.com/latest/notebooks/count-objects-crossing-the-line/)
[:octicons-arrow-right-24: Notebook](https://supervision.roboflow.com/latest/notebooks/count-objects-crossing-the-line/)
- > **Filter Objects in Zone**
***
---
Master the techniques to selectively filter and focus on objects within a specific zone
Master the techniques to selectively filter and focus on objects within a specific zone
- **Cheatsheet**
***
---
Access a quick reference guide to the most common `supervision` functions
Access a quick reference guide to the most common `supervision` functions
[:octicons-arrow-right-24: Cheatsheet](https://roboflow.github.io/cheatsheet-supervision/)
[:octicons-arrow-right-24: Cheatsheet](https://roboflow.github.io/cheatsheet-supervision/)
</div>

View File

@ -16,67 +16,76 @@ https://github.com/roboflow/supervision/assets/26109316/f84db7b5-79e2-4142-a1da-
- clone repository and navigate to example directory
```bash
git clone https://github.com/roboflow/supervision.git
cd supervision/examples/count_people_in_zone
```
```bash
git clone https://github.com/roboflow/supervision.git
cd supervision/examples/count_people_in_zone
```
- setup python environment and activate it [optional]
- setup python environment and activate it \[optional\]
```bash
python3 -m venv venv
source venv/bin/activate
```
```bash
python3 -m venv venv
source venv/bin/activate
```
- install required dependencies
```bash
pip install -r requirements.txt
```
```bash
pip install -r requirements.txt
```
- download `traffic_analysis.pt` and `traffic_analysis.mov` files
```bash
./setup.sh
```
```bash
./setup.sh
```
## 🛠️ script arguments
- ultralytics
- `--source_weights_path` (optional): The path to the YOLO model's weights file.
Defaults to `"yolov8x.pt"` if not specified.
- `--source_weights_path` (optional): The path to the YOLO model's weights file.
Defaults to `"yolov8x.pt"` if not specified.
- `--zone_configuration_path`: Specifies the path to the JSON file containing zone
configurations. This file defines the polygonal areas in the video where objects will
be counted.
- `--source_video_path`: The path to the source video file that will be analyzed.
- `--target_video_path` (optional): The path to save the output video with annotations.
If not provided, the processed video will be displayed in real-time.
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO model
to filter detections. Default is `0.3`.
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
for the model. Default is `0.7`.
- `--zone_configuration_path`: Specifies the path to the JSON file containing zone
configurations. This file defines the polygonal areas in the video where objects will
be counted.
- `--source_video_path`: The path to the source video file that will be analyzed.
- `--target_video_path` (optional): The path to save the output video with annotations.
If not provided, the processed video will be displayed in real-time.
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO model
to filter detections. Default is `0.3`.
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
for the model. Default is `0.7`.
- inference
- `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided
directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment
variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key)
to acquire your `API KEY`.
- `--model_id` (optional): Designates the Roboflow model ID to be used. The default
value is `"yolov8x-1280"`.
- `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided
directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment
variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key)
to acquire your `API KEY`.
- `--zone_configuration_path`: Specifies the path to the JSON file containing zone
configurations. This file defines the polygonal areas in the video where objects will
be counted.
- `--source_video_path`: The path to the source video file that will be analyzed.
- `--target_video_path` (optional): The path to save the output video with annotations.
If not provided, the processed video will be displayed in real-time.
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO model
to filter detections. Default is `0.3`.
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
for the model. Default is `0.7`.
- `--model_id` (optional): Designates the Roboflow model ID to be used. The default
value is `"yolov8x-1280"`.
- `--zone_configuration_path`: Specifies the path to the JSON file containing zone
configurations. This file defines the polygonal areas in the video where objects will
be counted.
- `--source_video_path`: The path to the source video file that will be analyzed.
- `--target_video_path` (optional): The path to save the output video with annotations.
If not provided, the processed video will be displayed in real-time.
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO model
to filter detections. Default is `0.3`.
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
for the model. Default is `0.7`.
## 📌 zone configuration
@ -89,35 +98,35 @@ https://github.com/roboflow/supervision/assets/26109316/f84db7b5-79e2-4142-a1da-
- ultralytics
```bash
python ultralytics_example.py \
--zone_configuration_path data/multi-zone-config.json \
--source_video_path data/market-square.mp4 \
--confidence_threshold 0.3 \
--iou_threshold 0.5
```
```bash
python ultralytics_example.py \
--zone_configuration_path data/multi-zone-config.json \
--source_video_path data/market-square.mp4 \
--confidence_threshold 0.3 \
--iou_threshold 0.5
```
- inference
```bash
python inference_example.py \
--roboflow_api_key <ROBOFLOW API KEY> \
--zone_configuration_path data/multi-zone-config.json \
--source_video_path data/market-square.mp4 \
--confidence_threshold 0.3 \
--iou_threshold 0.5
```
```bash
python inference_example.py \
--roboflow_api_key <ROBOFLOW API KEY> \
--zone_configuration_path data/multi-zone-config.json \
--source_video_path data/market-square.mp4 \
--confidence_threshold 0.3 \
--iou_threshold 0.5
```
## © license
This demo integrates two main components, each with its own licensing:
- ultralytics: The object detection model used in this demo, YOLOv8, is distributed
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
You can find more details about this license here.
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
You can find more details about this license here.
- supervision: The analytics code that powers the zone-based analysis in this demo is
based on the Supervision library, which is licensed under the
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
makes the Supervision part of the code fully open source and freely usable in your
projects.
based on the Supervision library, which is licensed under the
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
makes the Supervision part of the code fully open source and freely usable in your
projects.

View File

@ -6,7 +6,6 @@ This script performs heatmap and tracking analysis using YOLOv8, an object-detec
ByteTrack, a simple yet effective online multi-object tracking method. It uses the
supervision package for multiple tasks such as drawing heatmap annotations, tracking objects, etc.
## 💻 install
- clone repository and navigate to example directory
@ -16,7 +15,7 @@ supervision package for multiple tasks such as drawing heatmap annotations, trac
cd supervision/examples/heatmap_and_track
```
- setup python environment and activate it [optional]
- setup python environment and activate it \[optional\]
```bash
python3 -m venv venv
@ -32,17 +31,17 @@ supervision package for multiple tasks such as drawing heatmap annotations, trac
## 🛠️ script arguments
- `--source_weights_path`: Required. Specifies the path to the weights file for the
YOLO model. This file contains the trained model data necessary for object detection.
YOLO model. This file contains the trained model data necessary for object detection.
- `--source_video_path` (optional): The path to the source video file that will be
analyzed. This is the input video on which crowd analysis will be performed.
If not specified default is `people-walking.mp4` from supervision assets
analyzed. This is the input video on which crowd analysis will be performed.
If not specified default is `people-walking.mp4` from supervision assets
- `--target_video_path` (optional): The path to save the output.mp4 video with annotations.
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO model
to filter detections. Default is `0.3`. This determines how confident the model should
be to recognize an object in the video.
to filter detections. Default is `0.3`. This determines how confident the model should
be to recognize an object in the video.
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
for the model. Default is 0.7. This value is used to manage object detection accuracy,
particularly in distinguishing between different objects.
for the model. Default is 0.7. This value is used to manage object detection accuracy,
particularly in distinguishing between different objects.
- `--heatmap_alpha` (optional): Opacity of the overlay mask, between 0 and 1.
- `--radius` (optional): Radius of the heat circle.
- `--track_threshold` (optional): Detection confidence threshold for track activation.
@ -53,11 +52,11 @@ particularly in distinguishing between different objects.
```bash
python script.py \
--source_weights_path weight.pt \
--source_video_path input_video.mp4 \
--confidence_threshold 0.3 \
--iou_threshold 0.5 \
--target_video_path output_video.mp4
--source_weights_path weight.pt \
--source_video_path input_video.mp4 \
--confidence_threshold 0.3 \
--iou_threshold 0.5 \
--target_video_path output_video.mp4
```
## © license
@ -65,11 +64,11 @@ python script.py \
This demo integrates two main components, each with its own licensing:
- ultralytics: The object detection model used in this demo, YOLOv8, is distributed
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
You can find more details about this license here.
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
You can find more details about this license here.
- supervision: The analytics code that powers the zone-based analysis in this demo is
based on the Supervision library, which is licensed under the
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
makes the Supervision part of the code fully open source and freely usable in your
projects.
based on the Supervision library, which is licensed under the
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
makes the Supervision part of the code fully open source and freely usable in your
projects.

View File

@ -11,7 +11,7 @@ supervision package for multiple tasks such as tracking, annotations, etc.
https://github.com/roboflow/supervision/assets/26109316/d50118c1-2ae4-458d-915a-5d860fd36f71
> [!IMPORTANT]
> \[!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
@ -21,97 +21,102 @@ https://github.com/roboflow/supervision/assets/26109316/d50118c1-2ae4-458d-915a-
- clone repository and navigate to example directory
```bash
git clone https://github.com/roboflow/supervision.git
cd supervision/examples/speed_estimation
```
```bash
git clone https://github.com/roboflow/supervision.git
cd supervision/examples/speed_estimation
```
- setup python environment and activate it [optional]
- setup python environment and activate it \[optional\]
```bash
python3.10 -m venv venv
source venv/bin/activate
```
```bash
python3.10 -m venv venv
source venv/bin/activate
```
- install required dependencies
```bash
pip install -r requirements.txt
```
```bash
pip install -r requirements.txt
```
- download `vehicles.mp4` file
```bash
python3.10 video_downloader.py
```
```bash
python3.10 video_downloader.py
```
## 🛠️ script arguments
- `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided
directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment
variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key)
to acquire your `API KEY`.
directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment
variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key)
to acquire your `API KEY`.
- `--model_id` (optional): Designates the Roboflow model ID to be used. The default
value is `"yolov8x-1280"`.
value is `"yolov8x-1280"`.
- `--source_weights_path`: Required. Specifies the path to the YOLO model's weights
file, which is essential for the object detection process. This file contains the
data that the model uses to identify objects in the video.
file, which is essential for the object detection process. This file contains the
data that the model uses to identify objects in the video.
- `--source_video_path`: Required. The path to the source video file that will be
analyzed. This is the input video on which traffic flow analysis will be performed.
analyzed. This is the input video on which traffic flow analysis will be performed.
- `--target_video_path`: The path to save the output video with
annotations. If not specified, the processed video will be displayed in real-time
without being saved.
annotations. If not specified, the processed video will be displayed in real-time
without being saved.
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO
model to filter detections. Default is `0.3`. This determines how confident the
model should be to recognize an object in the video.
model to filter detections. Default is `0.3`. This determines how confident the
model should be to recognize an object in the video.
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
for the model. Default is 0.7. This value is used to manage object detection
accuracy, particularly in distinguishing between different objects.
for the model. Default is 0.7. This value is used to manage object detection
accuracy, particularly in distinguishing between different objects.
## ⚙️ run
- yolo-nas
```bash
```bash
python yolo_nas_example.py \
--source_video_path data/vehicles.mp4 \
--target_video_path data/vehicles-result.mp4 \
--confidence_threshold 0.3 \
--iou_threshold 0.5
```
--source_video_path data/vehicles.mp4 \
--target_video_path data/vehicles-result.mp4 \
--confidence_threshold 0.3 \
--iou_threshold 0.5
```
- inference
```bash
```bash
python inference_example.py \
--roboflow_api_key <ROBOFLOW API KEY> \
--source_video_path data/vehicles.mp4 \
--target_video_path data/vehicles-result.mp4 \
--confidence_threshold 0.3 \
--iou_threshold 0.5
```
--roboflow_api_key <ROBOFLOW API KEY> \
--source_video_path data/vehicles.mp4 \
--target_video_path data/vehicles-result.mp4 \
--confidence_threshold 0.3 \
--iou_threshold 0.5
```
- ultralytics
```bash
```bash
python ultralytics_example.py \
--source_video_path data/vehicles.mp4 \
--target_video_path data/vehicles-result.mp4 \
--confidence_threshold 0.3 \
--iou_threshold 0.5
```
--source_video_path data/vehicles.mp4 \
--target_video_path data/vehicles-result.mp4 \
--confidence_threshold 0.3 \
--iou_threshold 0.5
```
## © license
This demo integrates two main components, each with its own licensing:
- ultralytics: The object detection model used in this demo, YOLOv8, is distributed
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
You can find more details about this license here.
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
You can find more details about this license here.
- supervision: The analytics code that powers the zone-based analysis in this demo is
based on the Supervision library, which is licensed under the
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
makes the Supervision part of the code fully open source and freely usable in your
projects.
based on the Supervision library, which is licensed under the
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
makes the Supervision part of the code fully open source and freely usable in your
projects.

View File

@ -15,23 +15,23 @@ https://github.com/roboflow/supervision/assets/26109316/d051cc8a-dd15-41d4-aa36-
- clone repository and navigate to example directory
```bash
git clone https://github.com/roboflow/supervision.git
cd supervision/examples/time_in_zone
```
```bash
git clone https://github.com/roboflow/supervision.git
cd supervision/examples/time_in_zone
```
- setup python environment and activate it [optional]
- setup python environment and activate it \[optional\]
```bash
python3 -m venv venv
source venv/bin/activate
```
```bash
python3 -m venv venv
source venv/bin/activate
```
- install required dependencies
```bash
pip install -r requirements.txt
```
```bash
pip install -r requirements.txt
```
## 🛠 scripts
@ -45,16 +45,16 @@ This script allows you to download a video from YouTube.
```bash
python scripts/download_from_youtube.py \
--url "https://www.youtube.com/watch?v=-8zyEwAa50Q" \
--output_path "data/checkout" \
--file_name "video.mp4"
--url "https://www.youtube.com/watch?v=-8zyEwAa50Q" \
--output_path "data/checkout" \
--file_name "video.mp4"
```
```bash
python scripts/download_from_youtube.py \
--url "https://www.youtube.com/watch?v=MNn9qKG2UFI" \
--output_path "data/traffic" \
--file_name "video.mp4"
--url "https://www.youtube.com/watch?v=MNn9qKG2UFI" \
--output_path "data/traffic" \
--file_name "video.mp4"
```
### `stream_from_file`
@ -68,14 +68,14 @@ mock a live video stream for local testing. Video will be streamed in a loop und
```bash
python scripts/stream_from_file.py \
--video_directory "data/checkout" \
--number_of_streams 1
--video_directory "data/checkout" \
--number_of_streams 1
```
```bash
python scripts/stream_from_file.py \
--video_directory "data/traffic" \
--number_of_streams 1
--video_directory "data/traffic" \
--number_of_streams 1
```
### `draw_zones`
@ -86,24 +86,27 @@ window where you can draw polygons on the source image or video file. The polygo
be saved as a JSON file.
- `--source_path`: Path to the source image or video file for drawing polygons.
- `--zone_configuration_path`: Path where the polygon annotations will be saved as a JSON file.
- `enter` - finish drawing the current polygon.
- `escape` - cancel drawing the current polygon.
- `q` - quit the drawing window.
- `s` - save zone configuration to a JSON file.
```bash
python scripts/draw_zones.py \
--source_path "data/checkout/video.mp4" \
--zone_configuration_path "data/checkout/config.json"
--source_path "data/checkout/video.mp4" \
--zone_configuration_path "data/checkout/config.json"
```
```bash
python scripts/draw_zones.py \
--source_path "data/traffic/video.mp4" \
--zone_configuration_path "data/traffic/config.json"
--source_path "data/traffic/video.mp4" \
--zone_configuration_path "data/traffic/config.json"
```
https://github.com/roboflow/supervision/assets/26109316/9d514c9e-2a61-418b-ae49-6ac1ad6ae5ac
@ -114,33 +117,33 @@ https://github.com/roboflow/supervision/assets/26109316/9d514c9e-2a61-418b-ae49-
Script to run object detection on a video file using the Roboflow Inference model.
- `--zone_configuration_path`: Path to the zone configuration JSON file.
- `--source_video_path`: Path to the source video file.
- `--model_id`: Roboflow model ID.
- `--classes`: List of class IDs to track. If empty, all classes are tracked.
- `--confidence_threshold`: Confidence level for detections (`0` to `1`). Default is `0.3`.
- `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`.
- `--zone_configuration_path`: Path to the zone configuration JSON file.
- `--source_video_path`: Path to the source video file.
- `--model_id`: Roboflow model ID.
- `--classes`: List of class IDs to track. If empty, all classes are tracked.
- `--confidence_threshold`: Confidence level for detections (`0` to `1`). Default is `0.3`.
- `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`.
```bash
python inference_file_example.py \
--zone_configuration_path "data/checkout/config.json" \
--source_video_path "data/checkout/video.mp4" \
--model_id "yolov8x-640" \
--classes 0 \
--confidence_threshold 0.3 \
--iou_threshold 0.7
--zone_configuration_path "data/checkout/config.json" \
--source_video_path "data/checkout/video.mp4" \
--model_id "yolov8x-640" \
--classes 0 \
--confidence_threshold 0.3 \
--iou_threshold 0.7
```
https://github.com/roboflow/supervision/assets/26109316/d051cc8a-dd15-41d4-aa36-d38b86334c39
```bash
python inference_file_example.py \
--zone_configuration_path "data/traffic/config.json" \
--source_video_path "data/traffic/video.mp4" \
--model_id "yolov8x-640" \
--classes 2 5 6 7 \
--confidence_threshold 0.3 \
--iou_threshold 0.7
--zone_configuration_path "data/traffic/config.json" \
--source_video_path "data/traffic/video.mp4" \
--model_id "yolov8x-640" \
--classes 2 5 6 7 \
--confidence_threshold 0.3 \
--iou_threshold 0.7
```
https://github.com/roboflow/supervision/assets/26109316/5ec896d7-4b39-4426-8979-11e71666878b
@ -149,31 +152,31 @@ https://github.com/roboflow/supervision/assets/26109316/5ec896d7-4b39-4426-8979-
Script to run object detection on a video stream using the Roboflow Inference model.
- `--zone_configuration_path`: Path to the zone configuration JSON file.
- `--rtsp_url`: Complete RTSP URL for the video stream.
- `--model_id`: Roboflow model ID.
- `--classes`: List of class IDs to track. If empty, all classes are tracked.
- `--confidence_threshold`: Confidence level for detections (`0` to `1`). Default is `0.3`.
- `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`.
- `--zone_configuration_path`: Path to the zone configuration JSON file.
- `--rtsp_url`: Complete RTSP URL for the video stream.
- `--model_id`: Roboflow model ID.
- `--classes`: List of class IDs to track. If empty, all classes are tracked.
- `--confidence_threshold`: Confidence level for detections (`0` to `1`). Default is `0.3`.
- `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`.
```bash
python inference_stream_example.py \
--zone_configuration_path "data/checkout/config.json" \
--rtsp_url "rtsp://localhost:8554/live0.stream" \
--model_id "yolov8x-640" \
--classes 0 \
--confidence_threshold 0.3 \
--iou_threshold 0.7
--zone_configuration_path "data/checkout/config.json" \
--rtsp_url "rtsp://localhost:8554/live0.stream" \
--model_id "yolov8x-640" \
--classes 0 \
--confidence_threshold 0.3 \
--iou_threshold 0.7
```
```bash
python inference_stream_example.py \
--zone_configuration_path "data/traffic/config.json" \
--rtsp_url "rtsp://localhost:8554/live0.stream" \
--model_id "yolov8x-640" \
--classes 2 5 6 7 \
--confidence_threshold 0.3 \
--iou_threshold 0.7
--zone_configuration_path "data/traffic/config.json" \
--rtsp_url "rtsp://localhost:8554/live0.stream" \
--model_id "yolov8x-640" \
--classes 2 5 6 7 \
--confidence_threshold 0.3 \
--iou_threshold 0.7
```
<details>
@ -183,68 +186,68 @@ python inference_stream_example.py \
Script to run object detection on a video file using the Ultralytics YOLOv8 model.
- `--zone_configuration_path`: Path to the zone configuration JSON file.
- `--source_video_path`: Path to the source video file.
- `--weights`: Path to the model weights file. Default is `'yolov8s.pt'`.
- `--device`: Computation device (`'cpu'`, `'mps'` or `'cuda'`). Default is `'cpu'`.
- `--classes`: List of class IDs to track. If empty, all classes are tracked.
- `--confidence_threshold`: Confidence level for detections (`0` to `1`). Default is `0.3`.
- `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`.
- `--zone_configuration_path`: Path to the zone configuration JSON file.
- `--source_video_path`: Path to the source video file.
- `--weights`: Path to the model weights file. Default is `'yolov8s.pt'`.
- `--device`: Computation device (`'cpu'`, `'mps'` or `'cuda'`). Default is `'cpu'`.
- `--classes`: List of class IDs to track. If empty, all classes are tracked.
- `--confidence_threshold`: Confidence level for detections (`0` to `1`). Default is `0.3`.
- `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`.
```bash
python ultralytics_file_example.py \
--zone_configuration_path "data/checkout/config.json" \
--source_video_path "data/checkout/video.mp4" \
--weights "yolov8x.pt" \
--device "cpu" \
--classes 0 \
--confidence_threshold 0.3 \
--iou_threshold 0.7
--zone_configuration_path "data/checkout/config.json" \
--source_video_path "data/checkout/video.mp4" \
--weights "yolov8x.pt" \
--device "cpu" \
--classes 0 \
--confidence_threshold 0.3 \
--iou_threshold 0.7
```
```bash
python ultralytics_file_example.py \
--zone_configuration_path "data/traffic/config.json" \
--source_video_path "data/traffic/video.mp4" \
--weights "yolov8x.pt" \
--device "cpu" \
--classes 2 5 6 7 \
--confidence_threshold 0.3 \
--iou_threshold 0.7
--zone_configuration_path "data/traffic/config.json" \
--source_video_path "data/traffic/video.mp4" \
--weights "yolov8x.pt" \
--device "cpu" \
--classes 2 5 6 7 \
--confidence_threshold 0.3 \
--iou_threshold 0.7
```
### `ultralytics_stream_example`
Script to run object detection on a video stream using the Ultralytics YOLOv8 model.
- `--zone_configuration_path`: Path to the zone configuration JSON file.
- `--rtsp_url`: Complete RTSP URL for the video stream.
- `--weights`: Path to the model weights file. Default is `'yolov8s.pt'`.
- `--device`: Computation device (`'cpu'`, `'mps'` or `'cuda'`). Default is `'cpu'`.
- `--classes`: List of class IDs to track. If empty, all classes are tracked.
- `--confidence_threshold`: Confidence level for detections (`0` to `1`). Default is `0.3`.
- `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`.
- `--zone_configuration_path`: Path to the zone configuration JSON file.
- `--rtsp_url`: Complete RTSP URL for the video stream.
- `--weights`: Path to the model weights file. Default is `'yolov8s.pt'`.
- `--device`: Computation device (`'cpu'`, `'mps'` or `'cuda'`). Default is `'cpu'`.
- `--classes`: List of class IDs to track. If empty, all classes are tracked.
- `--confidence_threshold`: Confidence level for detections (`0` to `1`). Default is `0.3`.
- `--iou_threshold`: IOU threshold for non-max suppression. Default is `0.7`.
```bash
python ultralytics_stream_example.py \
--zone_configuration_path "data/checkout/config.json" \
--rtsp_url "rtsp://localhost:8554/live0.stream" \
--weights "yolov8x.pt" \
--device "cpu" \
--classes 0 \
--confidence_threshold 0.3 \
--iou_threshold 0.7
--zone_configuration_path "data/checkout/config.json" \
--rtsp_url "rtsp://localhost:8554/live0.stream" \
--weights "yolov8x.pt" \
--device "cpu" \
--classes 0 \
--confidence_threshold 0.3 \
--iou_threshold 0.7
```
```bash
python ultralytics_stream_example.py \
--zone_configuration_path "data/traffic/config.json" \
--rtsp_url "rtsp://localhost:8554/live0.stream" \
--weights "yolov8x.pt" \
--device "cpu" \
--classes 2 5 6 7 \
--confidence_threshold 0.3 \
--iou_threshold 0.7
--zone_configuration_path "data/traffic/config.json" \
--rtsp_url "rtsp://localhost:8554/live0.stream" \
--weights "yolov8x.pt" \
--device "cpu" \
--classes 2 5 6 7 \
--confidence_threshold 0.3 \
--iou_threshold 0.7
```
</details>
@ -254,11 +257,11 @@ python ultralytics_stream_example.py \
This demo integrates two main components, each with its own licensing:
- ultralytics: The object detection model used in this demo, YOLOv8, is distributed
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
You can find more details about this license here.
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
You can find more details about this license here.
- supervision: The analytics code that powers the zone-based analysis in this demo is
based on the Supervision library, which is licensed under the
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
makes the Supervision part of the code fully open source and freely usable in your
projects.
based on the Supervision library, which is licensed under the
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
makes the Supervision part of the code fully open source and freely usable in your
projects.

View File

@ -9,93 +9,100 @@ detection and Supervision for tracking and annotation.
- clone repository and navigate to example directory
```bash
git clone https://github.com/roboflow/supervision.git
cd supervision/examples/tracking
```
```bash
git clone https://github.com/roboflow/supervision.git
cd supervision/examples/tracking
```
- setup python environment and activate it [optional]
- setup python environment and activate it \[optional\]
```bash
python3 -m venv venv
source venv/bin/activate
```
```bash
python3 -m venv venv
source venv/bin/activate
```
- install required dependencies
```bash
pip install -r requirements.txt
```
```bash
pip install -r requirements.txt
```
## 🛠️ script arguments
- ultralytics
- `--source_weights_path`: Required. Specifies the path to the YOLO model's weights
file, which is essential for the object detection process. This file contains the data
that the model uses to identify objects in the video.
- `--source_weights_path`: Required. Specifies the path to the YOLO model's weights
file, which is essential for the object detection process. This file contains the data
that the model uses to identify objects in the video.
- `--source_video_path`: Required. The path to the source video file to be processed.
This is the video on which object detection and annotation will be performed.
- `--target_video_path`: Required. The path where the processed video, with annotations
added, will be saved. This is your output video file.
- `--confidence_threshold` (optional): Sets the confidence level at which the model
identifies objects in the video. Default is `0.3`. A higher threshold makes the model
more selective, while a lower threshold makes it more inclusive in identifying objects.
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
for the model, defaulting to `0.7`. This parameter helps in differentiating between
distinct objects, especially in crowded scenes.
- `--source_video_path`: Required. The path to the source video file to be processed.
This is the video on which object detection and annotation will be performed.
- `--target_video_path`: Required. The path where the processed video, with annotations
added, will be saved. This is your output video file.
- `--confidence_threshold` (optional): Sets the confidence level at which the model
identifies objects in the video. Default is `0.3`. A higher threshold makes the model
more selective, while a lower threshold makes it more inclusive in identifying objects.
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
for the model, defaulting to `0.7`. This parameter helps in differentiating between
distinct objects, especially in crowded scenes.
- inference
- `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided
directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment
variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key)
to acquire your `API KEY`.
- `--model_id` (optional): Designates the Roboflow model ID to be used. The default
value is `"yolov8x-1280"`.
- `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided
directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment
variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key)
to acquire your `API KEY`.
- `--source_video_path`: Required. The path to the source video file to be processed.
This is the video on which object detection and annotation will be performed.
- `--target_video_path`: Required. The path where the processed video, with annotations
added, will be saved. This is your output video file.
- `--confidence_threshold` (optional): Sets the confidence level at which the model
identifies objects in the video. Default is `0.3`. A higher threshold makes the model
more selective, while a lower threshold makes it more inclusive in identifying objects.
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
for the model, defaulting to `0.7`. This parameter helps in differentiating between
distinct objects, especially in crowded scenes.
- `--model_id` (optional): Designates the Roboflow model ID to be used. The default
value is `"yolov8x-1280"`.
- `--source_video_path`: Required. The path to the source video file to be processed.
This is the video on which object detection and annotation will be performed.
- `--target_video_path`: Required. The path where the processed video, with annotations
added, will be saved. This is your output video file.
- `--confidence_threshold` (optional): Sets the confidence level at which the model
identifies objects in the video. Default is `0.3`. A higher threshold makes the model
more selective, while a lower threshold makes it more inclusive in identifying objects.
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
for the model, defaulting to `0.7`. This parameter helps in differentiating between
distinct objects, especially in crowded scenes.
## ⚙️ run
- inference
```bash
python inference_example.py \
--roboflow_api_key <ROBOFLOW API KEY> \
--source_video_path input.mp4 \
--target_video_path tracking_result.mp4
```
```bash
python inference_example.py \
--roboflow_api_key <ROBOFLOW API KEY> \
--source_video_path input.mp4 \
--target_video_path tracking_result.mp4
```
- ultralytics
```bash
python ultralytics_example.py \
--source_weights_path yolov8s.pt \
--source_video_path input.mp4 \
--target_video_path tracking_result.mp4
```
```bash
python ultralytics_example.py \
--source_weights_path yolov8s.pt \
--source_video_path input.mp4 \
--target_video_path tracking_result.mp4
```
## © license
This demo integrates two main components, each with its own licensing:
- ultralytics: The object detection model used in this demo, YOLOv8, is distributed
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
You can find more details about this license here.
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
You can find more details about this license here.
- supervision: The analytics code that powers the zone-based analysis in this demo is
based on the Supervision library, which is licensed under the
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
makes the Supervision part of the code fully open source and freely usable in your
projects.
based on the Supervision library, which is licensed under the
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
makes the Supervision part of the code fully open source and freely usable in your
projects.

View File

@ -12,105 +12,112 @@ https://github.com/roboflow/supervision/assets/26109316/c9436828-9fbf-4c25-ae8c-
- clone repository and navigate to example directory
```bash
git clone https://github.com/roboflow/supervision.git
cd supervision/examples/traffic_analysis
```
```bash
git clone https://github.com/roboflow/supervision.git
cd supervision/examples/traffic_analysis
```
- setup python environment and activate it [optional]
- setup python environment and activate it \[optional\]
```bash
python3 -m venv venv
source venv/bin/activate
```
```bash
python3 -m venv venv
source venv/bin/activate
```
- install required dependencies
```bash
pip install -r requirements.txt
```
```bash
pip install -r requirements.txt
```
- download `traffic_analysis.pt` and `traffic_analysis.mov` files
```bash
./setup.sh
```
```bash
./setup.sh
```
## 🛠️ script arguments
- ultralytics
- `--source_weights_path`: Required. Specifies the path to the YOLO model's weights
file, which is essential for the object detection process. This file contains the
data that the model uses to identify objects in the video.
- `--source_weights_path`: Required. Specifies the path to the YOLO model's weights
file, which is essential for the object detection process. This file contains the
data that the model uses to identify objects in the video.
- `--source_video_path`: Required. The path to the source video file that will be
analyzed. This is the input video on which traffic flow analysis will be performed.
- `--target_video_path` (optional): The path to save the output video with
annotations. If not specified, the processed video will be displayed in real-time
without being saved.
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO
model to filter detections. Default is `0.3`. This determines how confident the
model should be to recognize an object in the video.
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
for the model. Default is 0.7. This value is used to manage object detection
accuracy, particularly in distinguishing between different objects.
- `--source_video_path`: Required. The path to the source video file that will be
analyzed. This is the input video on which traffic flow analysis will be performed.
- `--target_video_path` (optional): The path to save the output video with
annotations. If not specified, the processed video will be displayed in real-time
without being saved.
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO
model to filter detections. Default is `0.3`. This determines how confident the
model should be to recognize an object in the video.
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
for the model. Default is 0.7. This value is used to manage object detection
accuracy, particularly in distinguishing between different objects.
- inference
- `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided
directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment
variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key)
to acquire your `API KEY`.
- `--model_id` (optional): Designates the Roboflow model ID to be used. The default
value is `"vehicle-count-in-drone-video/6"`.
- `--roboflow_api_key` (optional): The API key for Roboflow services. If not provided
directly, the script tries to fetch it from the `ROBOFLOW_API_KEY` environment
variable. Follow [this guide](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key)
to acquire your `API KEY`.
- `--source_video_path`: Required. The path to the source video file that will be
analyzed. This is the input video on which traffic flow analysis will be performed.
- `--target_video_path` (optional): The path to save the output video with
annotations. If not specified, the processed video will be displayed in real-time
without being saved.
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO
model to filter detections. Default is `0.3`. This determines how confident the
model should be to recognize an object in the video.
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
for the model. Default is 0.7. This value is used to manage object detection
accuracy, particularly in distinguishing between different objects.
- `--model_id` (optional): Designates the Roboflow model ID to be used. The default
value is `"vehicle-count-in-drone-video/6"`.
- `--source_video_path`: Required. The path to the source video file that will be
analyzed. This is the input video on which traffic flow analysis will be performed.
- `--target_video_path` (optional): The path to save the output video with
annotations. If not specified, the processed video will be displayed in real-time
without being saved.
- `--confidence_threshold` (optional): Sets the confidence threshold for the YOLO
model to filter detections. Default is `0.3`. This determines how confident the
model should be to recognize an object in the video.
- `--iou_threshold` (optional): Specifies the IOU (Intersection Over Union) threshold
for the model. Default is 0.7. This value is used to manage object detection
accuracy, particularly in distinguishing between different objects.
## ⚙️ run
- ultralytics
```bash
python ultralytics_example.py \
--source_weights_path data/traffic_analysis.pt \
--source_video_path data/traffic_analysis.mov \
--confidence_threshold 0.3 \
--iou_threshold 0.5 \
--target_video_path data/traffic_analysis_result.mov
```
```bash
python ultralytics_example.py \
--source_weights_path data/traffic_analysis.pt \
--source_video_path data/traffic_analysis.mov \
--confidence_threshold 0.3 \
--iou_threshold 0.5 \
--target_video_path data/traffic_analysis_result.mov
```
- inference
```bash
python inference_example.py \
--roboflow_api_key <ROBOFLOW API KEY> \
--source_video_path data/traffic_analysis.mov \
--confidence_threshold 0.3 \
--iou_threshold 0.5 \
--target_video_path data/traffic_analysis_result.mov
```
```bash
python inference_example.py \
--roboflow_api_key <ROBOFLOW API KEY> \
--source_video_path data/traffic_analysis.mov \
--confidence_threshold 0.3 \
--iou_threshold 0.5 \
--target_video_path data/traffic_analysis_result.mov
```
## © license
This demo integrates two main components, each with its own licensing:
- ultralytics: The object detection model used in this demo, YOLOv8, is distributed
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
You can find more details about this license here.
under the [AGPL-3.0 license](https://github.com/ultralytics/ultralytics/blob/main/LICENSE).
You can find more details about this license here.
- supervision: The analytics code that powers the zone-based analysis in this demo is
based on the Supervision library, which is licensed under the
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
makes the Supervision part of the code fully open source and freely usable in your
projects.
based on the Supervision library, which is licensed under the
[MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md). This
makes the Supervision part of the code fully open source and freely usable in your
projects.

1235
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -3,13 +3,16 @@ name = "supervision"
version = "0.24.0rc1"
description = "A set of easy-to-use utils that will come in handy in any Computer Vision project"
authors = ["Piotr Skalski <piotr.skalski92@gmail.com>"]
maintainers = ["Piotr Skalski <piotr.skalski92@gmail.com>"]
maintainers = [
"Piotr Skalski <piotr.skalski92@gmail.com>",
"Linas Kondrackis <linas@roboflow.com>",
]
readme = "README.md"
license = "MIT"
packages = [{ include = "supervision" }]
homepage = "https://github.com/roboflow/supervision"
repository = "https://github.com/roboflow/supervision"
documentation = "https://github.com/roboflow/supervision/blob/main/README.md"
documentation = "https://supervision.roboflow.com/latest/"
keywords = [
"machine-learning",
"deep-learning",
@ -83,7 +86,8 @@ docutils = [
[tool.poetry.group.docs.dependencies]
mkdocs-material = { extras = ["imaging"], version = "^9.5.5" }
mkdocstrings = { extras = ["python"], version = ">=0.25.2,<0.27.0" }
mkdocstrings = ">=0.25.2,<0.27.0"
mkdocstrings-python = "^1.10.9"
mike = "^2.0.0"
# For Documentation Development use Python 3.10 or above
# Use Latest mkdocs-jupyter min 0.24.6 for Jupyter Notebook Theme support
@ -146,7 +150,7 @@ indent-width = 4
[tool.ruff.lint]
# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default.
select = ["E", "F", "I", "A", "Q", "W","RUF"]
select = ["E", "F", "I", "A", "Q", "W", "RUF"]
ignore = []
# Allow autofix for all enabled rules (when `--fix`) is provided.
fixable = [
@ -232,6 +236,12 @@ skip-magic-trailing-comma = false
# Like Black, automatically detect the appropriate line ending.
line-ending = "auto"
[tool.codespell]
skip = "*.ipynb,poetry.lock"
count = true
quiet-level = 3
ignore-words-list = "STrack,sTrack,strack"
[tool.setuptools]
include-package-data = false

View File

@ -6,19 +6,19 @@ It assumes you already have the code changes, as well as a draft of the release
1. Make sure you have all required changes were merged into `develop`.
2. Create and merge a PR, merging `develop` into `main`, containing:
- A commit that updates the project version in `pyproject.toml`.
- All changes made during the release.
- A commit that updates the project version in `pyproject.toml`.
- All changes made during the release.
3. Tag the commit with the new supervision version.
- make sure to pull from `main` !
- Verify that the latest merge commits exists. `git log`.
- Run `git tag x.y.z`, with your version
- Check with `git log`.
- Run `git push origin --tags`
- Upon pushing the tag, the [PyPi](https://pypi.org/project/supervision/) should update to the new version. Check this!
- make sure to pull from `main` !
- Verify that the latest merge commits exists. `git log`.
- Run `git tag x.y.z`, with your version
- Check with `git log`.
- Run `git push origin --tags`
- Upon pushing the tag, the [PyPi](https://pypi.org/project/supervision/) should update to the new version. Check this!
4. Open and merge a PR, merging `main` into `develop`.
5. Update the docs by running the [Supervision Release Documentation Workflow 📚](https://github.com/roboflow/supervision/actions/workflows/publish-release-docs.yml) workflow from GitHub.
- Select the `main` branch from the dropdown.
- Select the `main` branch from the dropdown.
6. Create a release on GitHub.
- Go to releases
- Assign the release notes to the tag created in step 3.
- Publish the release.
- Go to releases
- Assign the release notes to the tag created in step 3.
- Publish the release.

View File

@ -40,7 +40,11 @@ from supervision.dataset.core import (
)
from supervision.dataset.utils import mask_to_rle, rle_to_mask
from supervision.detection.core import Detections
from supervision.detection.line_zone import LineZone, LineZoneAnnotator
from supervision.detection.line_zone import (
LineZone,
LineZoneAnnotator,
LineZoneAnnotatorMulticlass,
)
from supervision.detection.lmm import LMM
from supervision.detection.overlap_filter import (
OverlapFilter,
@ -65,6 +69,7 @@ from supervision.detection.utils import (
mask_to_xyxy,
move_boxes,
move_masks,
oriented_box_iou_batch,
pad_boxes,
polygon_to_mask,
polygon_to_xyxy,

View File

@ -68,7 +68,8 @@ class BoxAnnotator(BaseAnnotator):
Args:
scene (ImageType): The image where bounding boxes will be drawn. `ImageType`
is a flexible type, accepting either `numpy.ndarray` or `PIL.Image.Image`.
is a flexible type, accepting either `numpy.ndarray` or
`PIL.Image.Image`.
detections (Detections): Object detections to annotate.
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
Allows to override the default color mapping strategy.

View File

@ -51,14 +51,17 @@ def resolve_color_idx(
if detections.class_id is None:
raise ValueError(
"Could not resolve color by class because "
"Detections do not have class_id"
"Detections do not have class_id. If using an annotator, "
"try setting color_lookup to sv.ColorLookup.INDEX or "
"sv.ColorLookup.TRACK."
)
return detections.class_id[detection_idx]
elif color_lookup == ColorLookup.TRACK:
if detections.tracker_id is None:
raise ValueError(
"Could not resolve color by track because "
"Detections do not have tracker_id"
"Detections do not have tracker_id. Did you call "
"tracker.update_with_detections(...) before annotating?"
)
return detections.tracker_id[detection_idx]

View File

@ -6,7 +6,10 @@ 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.config import (
CLASS_NAME_DATA_FIELD,
ORIENTED_BOX_COORDINATES,
)
from supervision.detection.lmm import (
LMM,
from_florence_2,
@ -514,14 +517,21 @@ class Detections:
**process_transformers_detection_result(transformers_results, id2label)
)
else:
raise ValueError(
"The provided Transformers results do not contain any valid fields."
" Expected fields are 'boxes', 'masks', 'segments_info' or"
" 'segmentation'."
)
@classmethod
def from_detectron2(cls, detectron2_results) -> Detections:
def from_detectron2(cls, detectron2_results: Any) -> Detections:
"""
Create a Detections object from the
[Detectron2](https://github.com/facebookresearch/detectron2) inference result.
Args:
detectron2_results: The output of a
detectron2_results (Any): The output of a
Detectron2 model containing instances with prediction data.
Returns:
@ -782,7 +792,7 @@ class Detections:
@classmethod
def from_lmm(
cls, lmm: Union[LMM, str], result: Union[str, dict], **kwargs
cls, lmm: Union[LMM, str], result: Union[str, dict], **kwargs: Any
) -> Detections:
"""
Creates a Detections object from the given result string based on the specified
@ -791,7 +801,7 @@ class Detections:
Args:
lmm (Union[LMM, str]): The type of LMM (Large Multimodal Model) to use.
result (str): The result string containing the detection data.
**kwargs: Additional keyword arguments required by the specified LMM.
**kwargs (Any): Additional keyword arguments required by the specified LMM.
Returns:
Detections: A new Detections object.
@ -843,6 +853,110 @@ class Detections:
raise ValueError(f"Unsupported LMM: {lmm}")
@classmethod
def from_easyocr(cls, easyocr_results: list) -> Detections:
"""
Create a Detections object from the
[EasyOCR](https://github.com/JaidedAI/EasyOCR) result.
Results are placed in the `data` field with the key `"class_name"`.
Args:
easyocr_results (List): The output Results instance from EasyOCR
Returns:
Detections: A new Detections object.
Example:
```python
import supervision as sv
import easyocr
reader = easyocr.Reader(['en'])
results = reader.readtext(<SOURCE_IMAGE_PATH>)
detections = sv.Detections.from_easyocr(results)
detected_text = detections["class_name"]
```
"""
if len(easyocr_results) == 0:
return cls.empty()
bbox = np.array([result[0] for result in easyocr_results])
xyxy = np.hstack((np.min(bbox, axis=1), np.max(bbox, axis=1)))
confidence = np.array(
[
result[2] if len(result) > 2 and result[2] else 0
for result in easyocr_results
]
)
ocr_text = np.array([result[1] for result in easyocr_results])
return cls(
xyxy=xyxy.astype(np.float32),
confidence=confidence.astype(np.float32),
data={
CLASS_NAME_DATA_FIELD: ocr_text,
},
)
@classmethod
def from_ncnn(cls, ncnn_results) -> Detections:
"""
Creates a Detections instance from the
[ncnn](https://github.com/Tencent/ncnn) inference result.
Supports object detection models.
Arguments:
ncnn_results (dict): The output Results instance from ncnn.
Returns:
Detections: A new Detections object.
Example:
```python
import cv2
from ncnn.model_zoo import get_model
import supervision as sv
image = cv2.imread(<SOURCE_IMAGE_PATH>)
model = get_model(
"yolov8s",
target_size=640
prob_threshold=0.5,
nms_threshold=0.45,
num_threads=4,
use_gpu=True,
)
result = model(image)
detections = sv.Detections.from_ncnn(result)
```
"""
xywh, confidences, class_ids = [], [], []
if len(ncnn_results) == 0:
return cls.empty()
for ncnn_result in ncnn_results:
rect = ncnn_result.rect
xywh.append(
[
rect.x.astype(np.float32),
rect.y.astype(np.float32),
rect.w.astype(np.float32),
rect.h.astype(np.float32),
]
)
confidences.append(ncnn_result.prob)
class_ids.append(ncnn_result.label)
return cls(
xyxy=xywh_to_xyxy(np.array(xywh, dtype=np.float32)),
confidence=np.array(confidences, dtype=np.float32),
class_id=np.array(class_ids, dtype=int),
)
@classmethod
def empty(cls) -> Detections:
"""

View File

@ -1,17 +1,23 @@
import math
import warnings
from collections import deque
from typing import Deque, Dict, Iterable, Optional, Tuple
from collections import Counter, deque
from functools import lru_cache
from typing import Any, Deque, Dict, Iterable, List, Literal, Optional, Tuple
import cv2
import numpy as np
from supervision.config import CLASS_NAME_DATA_FIELD
from supervision.detection.core import Detections
from supervision.detection.utils import cross_product
from supervision.draw.color import Color
from supervision.draw.utils import draw_text
from supervision.geometry.core import Point, Position, Vector
from supervision.draw.utils import draw_rectangle, draw_text
from supervision.geometry.core import Point, Position, Rect, Vector
from supervision.utils.image import overlay_image
from supervision.utils.internal import SupervisionWarnings
TEXT_MARGIN = 10
class LineZone:
"""
@ -88,12 +94,46 @@ class LineZone:
self.vector = Vector(start=start, end=end)
self.limits = self.calculate_region_of_interest_limits(vector=self.vector)
self.max_linger = max(1, max_linger)
self.crossing_state: Dict[str, Deque[bool]] = {}
self.in_count: int = 0
self.out_count: int = 0
self.crossing_state: Dict[str, Tuple[List[Any], Deque[bool]]] = {}
self.tracker_state: Dict[str, bool] = {}
self._in_count_per_class: Counter = Counter()
self._out_count_per_class: Counter = Counter()
self.triggering_anchors = triggering_anchors
if not list(self.triggering_anchors):
raise ValueError("Triggering anchors cannot be empty.")
self.class_id_to_name: Dict[int, str] = {}
@property
def in_count(self) -> int:
"""
Number of objects that have crossed the line from
outside to inside.
"""
return sum(self._in_count_per_class.values())
@property
def out_count(self) -> int:
"""
Number of objects that have crossed the line from
inside to outside.
"""
return sum(self._out_count_per_class.values())
@property
def in_count_per_class(self) -> Dict[int, int]:
"""
Number of objects of each class that have crossed
the line from outside to inside.
"""
return dict(self._in_count_per_class)
@property
def out_count_per_class(self) -> Dict[int, int]:
"""
Number of objects of each class that have crossed the line
from inside to outside.
"""
return dict(self._out_count_per_class)
@staticmethod
def calculate_region_of_interest_limits(vector: Vector) -> Tuple[Vector, Vector]:
@ -178,7 +218,22 @@ class LineZone:
has_any_left_trigger = np.any(triggers, axis=0)
has_any_right_trigger = np.any(~triggers, axis=0)
is_uniformly_triggered = ~(has_any_left_trigger & has_any_right_trigger)
for i, tracker_id in enumerate(detections.tracker_id):
class_ids = (
list(detections.class_id)
if detections.class_id is not None
else [None] * len(detections)
)
tracker_ids = list(detections.tracker_id)
if CLASS_NAME_DATA_FIELD in detections.data:
class_names = detections.data[CLASS_NAME_DATA_FIELD]
for class_id, class_name in zip(class_ids, class_names):
if class_id is None:
class_name = "No class"
self.class_id_to_name[class_id] = class_name
for i, (class_ids, tracker_id) in enumerate(zip(class_ids, tracker_ids)):
if not in_limits[i]:
continue
@ -187,12 +242,13 @@ class LineZone:
tracker_state = has_any_left_trigger[i]
if tracker_id not in self.crossing_state:
self.crossing_state[tracker_id] = deque(
[tracker_state], maxlen=self.max_linger
self.crossing_state[tracker_id] = (
class_ids,
deque([tracker_state], maxlen=self.max_linger)
)
continue
crossing_state = self.crossing_state[tracker_id]
crossing_state_class_ids, crossing_state = self.crossing_state[tracker_id]
prev_frame_tracker_state = crossing_state[-1]
if self.max_linger == 1 and prev_frame_tracker_state == tracker_state:
continue
@ -202,6 +258,9 @@ class LineZone:
)
crossing_state.appendleft(tracker_state)
all_on_same_side = crossing_state.count(not tracker_state) == 0
if class_ids:
if len(class_ids) != len(crossing_state_class_ids) or not all(class_ids == crossing_state_class_ids):
self.crossing_state[tracker_id] = (class_ids, tracker_state)
if not all_on_same_side:
continue
else:
@ -209,10 +268,10 @@ class LineZone:
continue
if tracker_state:
self.in_count += 1
self._in_count_per_class[class_ids] += 1
crossed_in[i] = True
else:
self.out_count += 1
self._out_count_per_class[class_ids] += 1
crossed_out[i] = True
if self.max_linger == 1:
@ -222,7 +281,7 @@ class LineZone:
for tracker_id in list(self.crossing_state.keys()):
if tracker_id in this_frame_trackers:
continue
crossing_state = self.crossing_state[tracker_id]
crossing_state_class_ids, crossing_state = self.crossing_state[tracker_id]
crossing_in_progress = (
crossing_state.count(True) != 0 and crossing_state.count(False) != 0
)
@ -235,9 +294,9 @@ class LineZone:
continue
if tracker_state:
self.in_count += 1
self._in_count_per_class[crossing_state_class_ids] += 1
else:
self.out_count += 1
self._out_count_per_class[crossing_state_class_ids] += 1
return crossed_in, crossed_out
@ -245,9 +304,9 @@ class LineZone:
class LineZoneAnnotator:
def __init__(
self,
thickness: float = 2,
thickness: int = 2,
color: Color = Color.WHITE,
text_thickness: float = 2,
text_thickness: int = 2,
text_color: Color = Color.BLACK,
text_scale: float = 0.5,
text_offset: float = 1.5,
@ -256,86 +315,67 @@ class LineZoneAnnotator:
custom_out_text: Optional[str] = None,
display_in_count: bool = True,
display_out_count: bool = True,
display_text_box: bool = True,
text_orient_to_line: bool = False,
text_centered: bool = True,
):
"""
Initialize the LineCounterAnnotator object with default values.
A class for drawing the `LineZone` and its detected object count
on an image.
Attributes:
thickness (float): The thickness of the line that will be drawn.
color (Color): The color of the line that will be drawn.
text_thickness (float): The thickness of the text that will be drawn.
text_color (Color): The color of the text that will be drawn.
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.
thickness (int): Line thickness.
color (Color): Line color.
text_thickness (int): Text thickness.
text_color (Color): Text color.
text_scale (float): Text scale.
text_offset (float): How far the text will be from the line.
text_padding (int): The empty space in the text box, surrounding the text.
custom_in_text (Optional[str]): Write something else instead of "in".
custom_out_text (Optional[str]): Write something else instead of "out".
display_in_count (bool): Pass `False` to hide the "in" count.
display_out_count (bool): Pass `False` to hide the "out" count.
display_text_box (bool): Pass `False` to hide the text background box.
text_orient_to_line (bool): Match text orientation to the line.
Recommended to set to `True`.
text_centered (bool): Pass `False` to disable text centering. Useful
when the label overlaps something important.
"""
self.thickness: float = thickness
self.thickness: int = thickness
self.color: Color = color
self.text_thickness: float = text_thickness
self.text_thickness: int = text_thickness
self.text_color: Color = text_color
self.text_scale: float = text_scale
self.text_offset: float = text_offset
self.text_padding: int = text_padding
self.custom_in_text: str = custom_in_text
self.custom_out_text: str = custom_out_text
self.in_text: str = custom_in_text if custom_in_text else "in"
self.out_text: str = custom_out_text if custom_out_text else "out"
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,
)
self.display_text_box: bool = display_text_box
self.text_orient_to_line: bool = text_orient_to_line
self.text_centered: bool = text_centered
def annotate(self, frame: np.ndarray, line_counter: LineZone) -> np.ndarray:
"""
Draws the line on the frame using the line_counter provided.
Draws the line on the frame using the line zone provided.
Attributes:
frame (np.ndarray): The image on which the line will be drawn.
line_counter (LineCounter): The line counter
line_counter (LineZone): The line zone
that will be used to draw the line.
Returns:
np.ndarray: The image with the line drawn on it.
(np.ndarray): The image with the line drawn on it.
"""
line_start = line_counter.vector.start.as_xy_int_tuple()
line_end = line_counter.vector.end.as_xy_int_tuple()
cv2.line(
frame,
line_counter.vector.start.as_xy_int_tuple(),
line_counter.vector.end.as_xy_int_tuple(),
line_start,
line_end,
self.color.as_bgr(),
self.thickness,
lineType=cv2.LINE_AA,
@ -343,7 +383,7 @@ class LineZoneAnnotator:
)
cv2.circle(
frame,
line_counter.vector.start.as_xy_int_tuple(),
line_start,
radius=5,
color=self.text_color.as_bgr(),
thickness=-1,
@ -351,40 +391,443 @@ class LineZoneAnnotator:
)
cv2.circle(
frame,
line_counter.vector.end.as_xy_int_tuple(),
line_end,
radius=5,
color=self.text_color.as_bgr(),
thickness=-1,
lineType=cv2.LINE_AA,
)
text_anchor = Vector(
start=line_counter.vector.start, end=line_counter.vector.end
in_text = f"{self.in_text}: {line_counter.in_count}"
out_text = f"{self.out_text}: {line_counter.out_count}"
line_angle_degrees = self._get_line_angle(line_counter)
for text, is_shown, is_in_count in [
(in_text, self.display_in_count, True),
(out_text, self.display_out_count, False),
]:
if not is_shown:
continue
if line_angle_degrees == 0 or not self.text_orient_to_line:
self._draw_basic_label(
frame=frame,
line_center=line_counter.vector.center,
text=text,
is_in_count=is_in_count,
)
else:
self._draw_oriented_label(
frame=frame,
line_zone=line_counter,
text=text,
is_in_count=is_in_count,
)
return frame
def _get_line_angle(self, line_zone: LineZone) -> float:
"""
Calculate the line counter angle (in degrees).
Args:
line_zone (LineZone): The line zone object.
Returns:
(float): Line counter angle, in degrees.
"""
start_point = line_zone.vector.start.as_xy_int_tuple()
end_point = line_zone.vector.end.as_xy_int_tuple()
delta_x = end_point[0] - start_point[0]
delta_y = end_point[1] - start_point[1]
if delta_x == 0:
line_angle = 90.0
line_angle += 180 if delta_y < 0 else 0
else:
line_angle = math.degrees(math.atan(delta_y / delta_x))
line_angle += 180 if delta_x < 0 else 0
return line_angle
def _calculate_anchor_in_frame(
self,
line_zone: LineZone,
text_width: int,
text_height: int,
is_in_count: bool,
label_dimension: int,
) -> Tuple[int, int]:
"""
Calculate insertion anchor in frame to position the center of the count image.
Args:
line_zone (LineZone): The line counter object used for counting.
text_width (int): Text width.
text_height (int): Text height.
is_in_count (bool): Whether the count should be placed over or below line.
label_dimension (int): Size of the label image. Assumes the
label is rectangular.
Returns:
(Tuple[int, int]): xy, point in an image where the label will be placed.
"""
line_angle = self._get_line_angle(line_zone)
if self.text_centered:
mid_point = Vector(
start=line_zone.vector.start, end=line_zone.vector.end
).center.as_xy_int_tuple()
anchor = list(mid_point)
else:
end_point = line_zone.vector.end.as_xy_int_tuple()
anchor = list(end_point)
move_along_x = int(
math.cos(math.radians(line_angle))
* (text_width / 2 + self.text_padding)
)
move_along_y = int(
math.sin(math.radians(line_angle))
* (text_width / 2 + self.text_padding)
)
anchor[0] -= move_along_x
anchor[1] -= move_along_y
move_perpendicular_x = int(
math.sin(math.radians(line_angle)) * (self.text_offset * text_height)
)
move_perpendicular_y = int(
math.cos(math.radians(line_angle)) * (self.text_offset * text_height)
)
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,
if is_in_count:
anchor[0] += move_perpendicular_x
anchor[1] -= move_perpendicular_y
else:
anchor[0] -= move_perpendicular_x
anchor[1] += move_perpendicular_y
x1 = max(anchor[0] - label_dimension // 2, 0)
y1 = max(anchor[1] - label_dimension // 2, 0)
return x1, y1
def _draw_basic_label(
self,
frame: np.ndarray,
line_center: Point,
text: str,
is_in_count: bool,
) -> np.ndarray:
"""
Draw the count label on the frame. For example: "out: 7".
The label contains horizontal text and is not rotated.
Args:
frame (np.ndarray): The entire scene, on which the label will be placed.
line_center (Point): The center of the line zone.
text (str): The text that will be drawn.
is_in_count (bool): Whether to display the in count (above line)
or out count (below line).
Returns:
(np.ndarray): The scene with the label drawn on it.
"""
_, text_height = cv2.getTextSize(
text, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness
)[0]
if is_in_count:
line_center.y -= int(self.text_offset * text_height)
else:
line_center.y += int(self.text_offset * text_height)
draw_text(
scene=frame,
text=text,
text_anchor=line_center,
text_color=self.text_color,
text_scale=self.text_scale,
text_thickness=self.text_thickness,
text_padding=self.text_padding,
background_color=self.color if self.display_text_box else None,
)
return frame
def _draw_oriented_label(
self,
frame: np.ndarray,
line_zone: LineZone,
text: str,
is_in_count: bool,
) -> np.ndarray:
"""
Draw the count label on the frame. For example: "out: 7".
The label is oriented to match the line angle.
Args:
frame (np.ndarray): The entire scene, on which the label will be placed.
line_zone (LineZone): The line zone responsible for counting
objects crossing it.
text (str): The text that will be drawn.
is_in_count (bool): Whether to display the in count (above line)
or out count (below line).
Returns:
(np.ndarray): The scene with the label drawn on it.
"""
line_angle_degrees = self._get_line_angle(line_zone)
label_image = self._make_label_image(
text,
text_scale=self.text_scale,
text_thickness=self.text_thickness,
text_padding=self.text_padding,
text_color=self.text_color,
text_box_show=self.display_text_box,
text_box_color=self.color,
line_angle_degrees=line_angle_degrees,
)
assert label_image.shape[0] == label_image.shape[1]
text_width, text_height = cv2.getTextSize(
text, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness
)[0]
label_anchor = self._calculate_anchor_in_frame(
line_zone=line_zone,
text_width=text_width,
text_height=text_height,
is_in_count=is_in_count,
label_dimension=label_image.shape[0],
)
frame = overlay_image(frame, label_image, label_anchor)
return frame
@staticmethod
@lru_cache(maxsize=32)
def _make_label_image(
text: str,
*,
text_scale: float,
text_thickness: int,
text_padding: int,
text_color: Color,
text_box_show: bool,
text_box_color: Color,
line_angle_degrees: float,
) -> np.ndarray:
"""
Create the small text box displaying line zone count. E.g. "out: 7".
Args:
text (str): The text to display.
text_scale (float): The scale of the text.
text_thickness (int): The thickness of the text.
text_padding (int): The padding around the text.
text_color (Color): The color of the text.
text_box_show (bool): Whether to display the text box.
text_box_color (Color): The color of the text box.
line_angle_degrees (float): The angle of the line in degrees.
Returns:
(np.ndarray): The label of shape (H, W, 4), in BGRA format.
"""
text_width, text_height = cv2.getTextSize(
text, cv2.FONT_HERSHEY_SIMPLEX, text_scale, text_thickness
)[0]
annotation_dim = int((max(text_width, text_height) + text_padding * 2) * 1.5)
annotation_shape = (annotation_dim, annotation_dim)
annotation_center = Point(annotation_dim // 2, annotation_dim // 2)
annotation = np.zeros((*annotation_shape, 3), dtype=np.uint8)
annotation_alpha = np.zeros((*annotation_shape, 1), dtype=np.uint8)
text_args: Dict[str, Any] = dict(
text=text,
text_anchor=annotation_center,
text_scale=text_scale,
text_thickness=text_thickness,
text_padding=text_padding,
)
draw_text(
scene=annotation,
text_color=text_color,
background_color=text_box_color if text_box_show else None,
**text_args,
)
draw_text(
scene=annotation_alpha,
text_color=Color.WHITE,
background_color=Color.WHITE if text_box_show else None,
**text_args,
)
annotation = np.dstack((annotation, annotation_alpha))
# Make sure text is displayed upright
if 90 < line_angle_degrees % 360 < 270:
annotation = cv2.flip(annotation, flipCode=-1).astype(np.uint8)
rotation_angle = -line_angle_degrees
rotation_matrix = cv2.getRotationMatrix2D(
annotation_center.as_xy_float_tuple(), rotation_angle, scale=1
)
annotation = cv2.warpAffine(annotation, rotation_matrix, annotation_shape)
return annotation
class LineZoneAnnotatorMulticlass:
def __init__(
self,
*,
table_position: Literal[
Position.TOP_LEFT,
Position.TOP_RIGHT,
Position.BOTTOM_LEFT,
Position.BOTTOM_RIGHT,
] = Position.TOP_RIGHT,
table_color: Color = Color.WHITE,
table_margin: int = 10,
table_padding: int = 10,
table_max_width: int = 400,
text_color: Color = Color.BLACK,
text_scale: float = 0.75,
text_thickness: int = 1,
force_draw_class_ids: bool = False,
):
"""
Draw a table showing how many items of each class crossed each line.
Args:
table_position (Position): The position of the table.
table_color (Color): The color of the table.
table_margin (int): The margin of the table from the image border.
table_padding (int): The padding of the table.
table_max_width (int): The maximum width of the table.
text_color (Color): The color of the text.
text_scale (float): The scale of the text.
text_thickness (int): The thickness of the text.
force_draw_class_ids (bool): Instead of writing the class names,
on the table, write the class IDs. E.g. instead of `person: 6`,
write `0: 6`.
"""
if table_position not in {
Position.TOP_LEFT,
Position.TOP_RIGHT,
Position.BOTTOM_LEFT,
Position.BOTTOM_RIGHT,
}:
raise ValueError(
"Invalid table position. Supported values are:"
" TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT."
)
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,
self.table_position = table_position
self.table_color = table_color
self.table_margin = table_margin
self.table_padding = table_padding
self.table_max_width = table_max_width
self.text_color = text_color
self.text_scale = text_scale
self.text_thickness = text_thickness
self.force_draw_class_ids = force_draw_class_ids
def annotate(
self,
frame: np.ndarray,
line_zones: List[LineZone],
line_zone_labels: Optional[List[str]] = None,
) -> np.ndarray:
if line_zone_labels is None:
line_zone_labels = [f"Line {i + 1}:" for i in range(len(line_zones))]
if len(line_zones) != len(line_zone_labels):
raise ValueError("The number of line zones and their labels must match.")
text_lines = ["Line Crossings:"]
for line_zone, line_zone_label in zip(line_zones, line_zone_labels):
text_lines.append(line_zone_label)
class_id_to_name = line_zone.class_id_to_name
for direction, count_per_class in [
("In", line_zone.in_count_per_class),
("Out", line_zone.out_count_per_class),
]:
if not count_per_class:
continue
text_lines.append(f" {direction}:")
for class_id, count in count_per_class.items():
class_name = (
class_id_to_name.get(class_id, str(class_id))
if not self.force_draw_class_ids
else str(class_id)
)
text_lines.append(f" {class_name}: {count}")
table_width, table_height = 0, 0
for line in text_lines:
text_width, text_height = cv2.getTextSize(
line, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness
)[0]
text_height += TEXT_MARGIN
table_width = max(table_width, text_width)
table_height += text_height
table_width += 2 * self.table_padding
table_height += 2 * self.table_padding
table_max_height = frame.shape[0] - 2 * self.table_margin
table_height = min(table_height, table_max_height)
table_width = min(table_width, self.table_max_width)
position_map = {
Position.TOP_LEFT: (self.table_margin, self.table_margin),
Position.TOP_RIGHT: (
frame.shape[1] - table_width - self.table_margin,
self.table_margin,
),
Position.BOTTOM_LEFT: (
self.table_margin,
frame.shape[0] - table_height - self.table_margin,
),
Position.BOTTOM_RIGHT: (
frame.shape[1] - table_width - self.table_margin,
frame.shape[0] - table_height - self.table_margin,
),
}
table_x1, table_y1 = position_map[self.table_position]
table_rect = Rect(
x=table_x1, y=table_y1, width=table_width, height=table_height
)
frame = draw_rectangle(
scene=frame, rect=table_rect, color=self.table_color, thickness=-1
)
for i, line in enumerate(text_lines):
_, text_height = cv2.getTextSize(
line, cv2.FONT_HERSHEY_SIMPLEX, self.text_scale, self.text_thickness
)[0]
text_height += TEXT_MARGIN
anchor_x = table_x1 + self.table_padding
anchor_y = table_y1 + self.table_padding + (i + 1) * text_height
cv2.putText(
img=frame,
text=line,
org=(anchor_x, anchor_y),
fontFace=cv2.FONT_HERSHEY_SIMPLEX,
fontScale=self.text_scale,
color=self.text_color.as_bgr(),
thickness=self.text_thickness,
lineType=cv2.LINE_AA,
)
return frame

View File

@ -66,7 +66,7 @@ def mask_non_max_suppression(
Raises:
AssertionError: If `iou_threshold` is not within the closed
range from `0` to `1`.
range from `0` to `1`.
"""
assert 0 <= iou_threshold <= 1, (
"Value of `iou_threshold` must be in the closed range from 0 to 1, "

View File

@ -11,7 +11,6 @@ from supervision.detection.utils import move_boxes, move_masks, move_oriented_bo
from supervision.utils.image import crop_image
from supervision.utils.internal import (
SupervisionWarnings,
deprecated_parameter,
warn_deprecated,
)
@ -60,13 +59,15 @@ class InferenceSlicer:
Args:
slice_wh (Tuple[int, int]): Dimensions of each slice measured in pixels. The
tuple should be in the format `(width, height)`.
overlap_ratio_wh (Optional[Tuple[float, float]]): A tuple representing the
overlap_ratio_wh (Optional[Tuple[float, float]]): [ Deprecated: please set
to `None` and use `overlap_wh`] A tuple representing the
desired overlap ratio for width and height between consecutive slices.
Each value should be in the range [0, 1), where 0 means no overlap and
a value close to 1 means high overlap.
overlap_wh (Optional[Tuple[int, int]]): A tuple representing the desired
overlap for width and height between consecutive slices measured in pixels.
Each value should be greater than or equal to 0.
Each value should be greater than or equal to 0. Takes precedence over
`overlap_ratio_wh`.
overlap_filter (Union[OverlapFilter, str]): Strategy for
filtering or merging overlapping detections in slices.
iou_threshold (float): Intersection over Union (IoU) threshold
@ -82,14 +83,6 @@ class InferenceSlicer:
not a multiple of the slice's width or height minus the overlap.
"""
@deprecated_parameter(
old_parameter="overlap_filter_strategy",
new_parameter="overlap_filter",
map_function=lambda x: x,
warning_message="`{old_parameter}` in `{function_name}` is deprecated and will "
"be removed in `supervision-0.27.0`. Use '{new_parameter}' "
"instead.",
)
def __init__(
self,
callback: Callable[[np.ndarray], Detections],
@ -103,7 +96,8 @@ class InferenceSlicer:
if overlap_ratio_wh is not None:
warn_deprecated(
"`overlap_ratio_wh` in `InferenceSlicer.__init__` is deprecated and "
"will be removed in `supervision-0.27.0`. Use `overlap_wh` instead."
"will be removed in `supervision-0.27.0`. Please manually set it to "
"`None` and use `overlap_wh` instead."
)
self._validate_overlap(overlap_ratio_wh, overlap_wh)

View File

@ -140,6 +140,45 @@ def mask_iou_batch(
return np.vstack(ious)
def oriented_box_iou_batch(
boxes_true: np.ndarray, boxes_detection: np.ndarray
) -> np.ndarray:
"""
Compute Intersection over Union (IoU) of two sets of oriented bounding boxes -
`boxes_true` and `boxes_detection`. Both sets of boxes are expected to be in
`((x1, y1), (x2, y2), (x3, y3), (x4, y4))` format.
Args:
boxes_true (np.ndarray): a `np.ndarray` representing ground-truth boxes.
`shape = (N, 4, 2)` where `N` is number of true objects.
boxes_detection (np.ndarray): a `np.ndarray` representing detection boxes.
`shape = (M, 4, 2)` where `M` is number of detected objects.
Returns:
np.ndarray: Pairwise IoU of boxes from `boxes_true` and `boxes_detection`.
`shape = (N, M)` where `N` is number of true objects and
`M` is number of detected objects.
"""
boxes_true = boxes_true.reshape(-1, 4, 2)
boxes_detection = boxes_detection.reshape(-1, 4, 2)
max_height = max(boxes_true[:, :, 0].max(), boxes_detection[:, :, 0].max()) + 1
# adding 1 because we are 0-indexed
max_width = max(boxes_true[:, :, 1].max(), boxes_detection[:, :, 1].max()) + 1
mask_true = np.zeros((boxes_true.shape[0], max_height, max_width))
for i, box_true in enumerate(boxes_true):
mask_true[i] = polygon_to_mask(box_true, (max_width, max_height))
mask_detection = np.zeros((boxes_detection.shape[0], max_height, max_width))
for i, box_detection in enumerate(boxes_detection):
mask_detection[i] = polygon_to_mask(box_detection, (max_width, max_height))
ious = mask_iou_batch(mask_true, mask_detection)
return ious
def clip_boxes(xyxy: np.ndarray, resolution_wh: Tuple[int, int]) -> np.ndarray:
"""
Clips bounding boxes coordinates to fit within the frame resolution.
@ -147,7 +186,7 @@ def clip_boxes(xyxy: np.ndarray, resolution_wh: Tuple[int, int]) -> np.ndarray:
Args:
xyxy (np.ndarray): A numpy array of shape `(N, 4)` where each
row corresponds to a bounding box in
the format `(x_min, y_min, x_max, y_max)`.
the format `(x_min, y_min, x_max, y_max)`.
resolution_wh (Tuple[int, int]): A tuple of the form `(width, height)`
representing the resolution of the frame.

View File

@ -255,6 +255,17 @@ class Color:
def ROBOFLOW(cls) -> Color:
return Color.from_hex("#A351FB")
def __hash__(self):
return hash((self.r, self.g, self.b))
def __eq__(self, other):
return (
isinstance(other, Color)
and self.r == other.r
and self.g == other.g
and self.b == other.b
)
@dataclass
class ColorPalette:
@ -386,6 +397,15 @@ class ColorPalette:
idx = idx % len(self.colors)
return self.colors[idx]
def __len__(self) -> int:
"""
Returns the number of colors in the palette.
Returns:
int: The number of colors.
"""
return len(self.colors)
def unify_to_bgr(color: Union[Tuple[int, int, int], Color]) -> Tuple[int, int, int]:
"""

View File

@ -459,13 +459,13 @@ class KeyPoints:
)
@classmethod
def from_detectron2(cls, detectron2_results) -> KeyPoints:
def from_detectron2(cls, detectron2_results: Any) -> KeyPoints:
"""
Create a `sv.KeyPoints` object from the
[Detectron2](https://github.com/facebookresearch/detectron2) inference result.
Args:
detectron2_results: The output of a
detectron2_results (Any): The output of a
Detectron2 model containing instances with prediction data.
Returns:

View File

@ -101,17 +101,20 @@ def get_obb_size_category(xyxyxyxy: npt.NDArray[np.float32]) -> npt.NDArray[np.i
Get the size category of a oriented bounding boxes array.
Args:
xyxyxyxy (np.ndarray): The bounding boxes array shaped (N, 8).
xyxyxyxy (np.ndarray): The bounding boxes array shaped (N, 4, 2).
Returns:
(np.ndarray) The size category of each bounding box, matching
the enum values of ObjectSizeCategory. Shaped (N,).
"""
if len(xyxyxyxy.shape) != 2 or xyxyxyxy.shape[1] != 8:
raise ValueError("Oriented bounding boxes must be shaped (N, 8)")
if len(xyxyxyxy.shape) != 3 or xyxyxyxy.shape[1] != 4 or xyxyxyxy.shape[2] != 2:
raise ValueError("Oriented bounding boxes must be shaped (N, 4, 2)")
# Shoelace formula
x1, y1, x2, y2, x3, y3, x4, y4 = xyxyxyxy.T
x = xyxyxyxy[:, :, 0]
y = xyxyxyxy[:, :, 1]
x1, x2, x3, x4 = x.T
y1, y2, y3, y4 = y.T
areas = 0.5 * np.abs(
(x1 * y2 + x2 * y3 + x3 * y4 + x4 * y1)
- (x2 * y1 + x3 * y2 + x4 * y3 + x1 * y4)

View File

@ -308,7 +308,7 @@ def test_group_coco_annotations_by_image_id(
),
),
DoesNotRaise(),
), # two image annotations with mask, one mask as polygon ans second as RLE
), # two image annotations with mask, one mask as polygon and second as RLE
(
[
mock_coco_annotation(

View File

@ -725,11 +725,7 @@ def test_line_zone_one_detection_long_horizon(
[
[False, False, False],
[False, False, False],
[
False,
False,
False,
],
[False, False, False],
[False, False],
[False, True],
],

View File

@ -0,0 +1,192 @@
from contextlib import ExitStack as DoesNotRaise
from typing import Optional, Tuple
import numpy as np
import pytest
from supervision.detection.core import Detections
from supervision.detection.overlap_filter import OverlapFilter
from supervision.detection.tools.inference_slicer import InferenceSlicer
@pytest.fixture
def mock_callback():
"""Mock callback function for testing."""
def callback(_: np.ndarray) -> Detections:
return Detections(xyxy=np.array([[0, 0, 10, 10]]))
return callback
@pytest.mark.parametrize(
"slice_wh, overlap_ratio_wh, overlap_wh, expected_overlap, exception",
[
# Valid case: overlap_ratio_wh provided, overlap calculated from the ratio
((128, 128), (0.2, 0.2), None, None, DoesNotRaise()),
# Valid case: overlap_wh in pixels, no ratio provided
((128, 128), None, (20, 20), (20, 20), DoesNotRaise()),
# Invalid case: overlap_ratio_wh greater than 1, should raise ValueError
((128, 128), (1.1, 0.5), None, None, pytest.raises(ValueError)),
# Invalid case: negative overlap_wh, should raise ValueError
((128, 128), None, (-10, 20), None, pytest.raises(ValueError)),
# Invalid case:
# overlap_ratio_wh and overlap_wh provided, should raise ValueError
((128, 128), (0.5, 0.5), (20, 20), (20, 20), pytest.raises(ValueError)),
# Valid case: no overlap_ratio_wh, overlap_wh = 50 pixels
((256, 256), None, (50, 50), (50, 50), DoesNotRaise()),
# Valid case: overlap_ratio_wh provided, overlap calculated from (0.3, 0.3)
((200, 200), (0.3, 0.3), None, None, DoesNotRaise()),
# Valid case: small overlap_ratio_wh values
((100, 100), (0.1, 0.1), None, None, DoesNotRaise()),
# Invalid case: negative overlap_ratio_wh value, should raise ValueError
((128, 128), (-0.1, 0.2), None, None, pytest.raises(ValueError)),
# Invalid case: negative overlap_ratio_wh with overlap_wh provided
((128, 128), (-0.1, 0.2), (30, 30), None, pytest.raises(ValueError)),
# Invalid case: overlap_wh greater than slice size, should raise ValueError
((128, 128), None, (150, 150), (150, 150), DoesNotRaise()),
# Valid case: overlap_ratio_wh is 0, no overlap
((128, 128), (0.0, 0.0), None, None, DoesNotRaise()),
# Invalid case: no overlaps defined, no overlap
((128, 128), None, None, None, pytest.raises(ValueError)),
],
)
def test_inference_slicer_overlap(
mock_callback,
slice_wh: Tuple[int, int],
overlap_ratio_wh: Optional[Tuple[float, float]],
overlap_wh: Optional[Tuple[int, int]],
expected_overlap: Optional[Tuple[int, int]],
exception: Exception,
) -> None:
with exception:
slicer = InferenceSlicer(
callback=mock_callback,
slice_wh=slice_wh,
overlap_ratio_wh=overlap_ratio_wh,
overlap_wh=overlap_wh,
overlap_filter=OverlapFilter.NONE,
)
assert slicer.overlap_wh == expected_overlap
@pytest.mark.parametrize(
"resolution_wh, slice_wh, overlap_wh, expected_offsets",
[
# Case 1: No overlap, exact slices fit within image dimensions
(
(256, 256),
(128, 128),
(0, 0),
np.array(
[
[0, 0, 128, 128],
[128, 0, 256, 128],
[0, 128, 128, 256],
[128, 128, 256, 256],
]
),
),
# Case 2: Overlap of 64 pixels in both directions
(
(256, 256),
(128, 128),
(64, 64),
np.array(
[
[0, 0, 128, 128],
[64, 0, 192, 128],
[128, 0, 256, 128],
[192, 0, 256, 128],
[0, 64, 128, 192],
[64, 64, 192, 192],
[128, 64, 256, 192],
[192, 64, 256, 192],
[0, 128, 128, 256],
[64, 128, 192, 256],
[128, 128, 256, 256],
[192, 128, 256, 256],
[0, 192, 128, 256],
[64, 192, 192, 256],
[128, 192, 256, 256],
[192, 192, 256, 256],
]
),
),
# Case 3: Image not perfectly divisible by slice size (no overlap)
(
(300, 300),
(128, 128),
(0, 0),
np.array(
[
[0, 0, 128, 128],
[128, 0, 256, 128],
[256, 0, 300, 128],
[0, 128, 128, 256],
[128, 128, 256, 256],
[256, 128, 300, 256],
[0, 256, 128, 300],
[128, 256, 256, 300],
[256, 256, 300, 300],
]
),
),
# Case 4: Overlap of 32 pixels, image not perfectly divisible by slice size
(
(300, 300),
(128, 128),
(32, 32),
np.array(
[
[0, 0, 128, 128],
[96, 0, 224, 128],
[192, 0, 300, 128],
[288, 0, 300, 128],
[0, 96, 128, 224],
[96, 96, 224, 224],
[192, 96, 300, 224],
[288, 96, 300, 224],
[0, 192, 128, 300],
[96, 192, 224, 300],
[192, 192, 300, 300],
[288, 192, 300, 300],
[0, 288, 128, 300],
[96, 288, 224, 300],
[192, 288, 300, 300],
[288, 288, 300, 300],
]
),
),
# Case 5: Image smaller than slice size (no overlap)
(
(100, 100),
(128, 128),
(0, 0),
np.array(
[
[0, 0, 100, 100],
]
),
),
# Case 6: Overlap_wh is greater than the slice size
((256, 256), (128, 128), (150, 150), np.array([]).reshape(0, 4)),
],
)
def test_generate_offset(
resolution_wh: Tuple[int, int],
slice_wh: Tuple[int, int],
overlap_wh: Optional[Tuple[int, int]],
expected_offsets: np.ndarray,
) -> None:
offsets = InferenceSlicer._generate_offset(
resolution_wh=resolution_wh,
slice_wh=slice_wh,
overlap_ratio_wh=None,
overlap_wh=overlap_wh,
)
# Verify that the generated offsets match the expected offsets
assert np.array_equal(
offsets, expected_offsets
), f"Expected {expected_offsets}, got {offsets}"