Merge pull request #1742 from roboflow/develop

`supervision-0.26.0` release
This commit is contained in:
Piotr Skalski 2025-07-16 02:39:07 +02:00 committed by GitHub
commit d8de58d7f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
183 changed files with 15079 additions and 10397 deletions

4
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1,4 @@
# These owners will be the default owners for everything in
# the repo. They will be requested for review when someone
# opens a pull request.
* @SkalskiP @onuralpszr

View File

@ -4,13 +4,15 @@ updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
interval: "weekly"
commit-message:
prefix: ⬆️
target-branch: "develop"
# Python
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "daily"
interval: "weekly"
commit-message:
prefix: ⬆️
target-branch: "develop"

View File

@ -1,33 +1,42 @@
name: Clear cache
on:
schedule:
- cron: '0 0 1 * *'
workflow_dispatch:
schedule:
- cron: "0 0 1 * *" # Run at midnight on the first day of every month
workflow_dispatch:
# Restrict permissions by default
permissions:
actions: write
actions: write # Required for cache management
jobs:
clear-cache:
name: Clear cache
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Clear cache
uses: actions/github-script@v7
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
console.log("About to clear")
console.log("Starting cache cleanup...")
const caches = await github.rest.actions.getActionsCacheList({
owner: context.repo.owner,
repo: context.repo.repo,
})
let deletedCount = 0
for (const cache of caches.data.actions_caches) {
console.log(cache)
github.rest.actions.deleteActionsCacheById({
owner: context.repo.owner,
repo: context.repo.repo,
cache_id: cache.id,
})
console.log(`Deleting cache: ${cache.key} (${cache.size_in_bytes} bytes)`)
try {
await github.rest.actions.deleteActionsCacheById({
owner: context.repo.owner,
repo: context.repo.repo,
cache_id: cache.id,
})
deletedCount++
} catch (error) {
console.error(`Failed to delete cache ${cache.key}: ${error.message}`)
}
}
console.log("Clear completed")
console.log(`Cache cleanup completed. Deleted ${deletedCount} caches.`)

View File

@ -0,0 +1,22 @@
name: Combine Dependabot PRs
on:
schedule:
- cron: "0 1 * * 3" # Wednesday at 01:00
workflow_dispatch: # allows you to manually trigger the workflow
permissions:
contents: write
pull-requests: write
checks: read
jobs:
combine-prs:
name: Combine
runs-on: ubuntu-latest
steps:
- name: combine-prs
id: combine-prs
uses: github/combine-prs@2909f404763c3177a456e052bdb7f2e85d3a7cb3 # v5.2.0
with:
labels: combined-pr

View File

@ -1,64 +0,0 @@
name: Notebook Check Pull Request
on:
pull_request_target:
types: [opened, reopened]
permissions:
contents: read
jobs:
comment-welcome:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: Fetch pull request branch
uses: actions/checkout@v4
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.sha }}
- name: Fetch base develop branch
run: git fetch -u "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" develop:develop
- name: Create message
env:
HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }}
HEAD_REF: ${{ github.event.pull_request.head.ref }}
PR_NUM: ${{ github.event.pull_request.number }}
run: |
# Preview links and tool usage only needed for notebook changes.
readarray -t changed_notebooks < <(git diff --name-only develop | grep '\.ipynb$' || true)
if [[ ${#changed_notebooks[@]} == 0 ]]; then
echo "No notebooks modified in this pull request."
else
msg="<h4>Preview</h4>\n"
msg+="Preview and run these notebook edits with Google Colab:\n<ul>\n"
# Link to PR branch in user's fork that is always current.
for fp in "${changed_notebooks[@]}"; do
gh_path="${HEAD_REPOSITORY}/blob/${HEAD_REF}/${fp}"
colab_url="https://colab.research.google.com/github/${gh_path}"
msg+="<li><a href='${colab_url}'>${fp}</a></li>\n"
done
msg+="</ul>\n"
reviewnb_url="https://app.reviewnb.com/${GITHUB_REPOSITORY}/pull/${PR_NUM}/files/"
msg+="Rendered <a href='${reviewnb_url}'>notebook diffs</a> available on ReviewNB.com.\n"
msg+="If commits are added to the pull request, synchronize your local branch: <code>git pull origin $HEAD_REF</code>\n"
fi
echo "MESSAGE=$msg" >> $GITHUB_ENV
- name: Post comment
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_URL: ${{ github.event.pull_request.issue_url }}
run: |
# Env var defined in previous step. Escape string for JSON.
body="$(echo -n -e $MESSAGE | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')"
# Add comment to pull request.
curl -X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token $GITHUB_TOKEN" \
"${ISSUE_URL}/comments" \
--data "{\"body\": $body}"

View File

@ -1,37 +0,0 @@
name: 🔧 Poetry Check and Installation Test Workflow
on:
push:
paths:
- 'poetry.lock'
- 'pyproject.toml'
pull_request:
paths:
- 'poetry.lock'
- 'pyproject.toml'
workflow_dispatch:
jobs:
poetry-tests:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
runs-on: ${{ matrix.os }}
steps:
- name: 📥 Checkout the repository
uses: actions/checkout@v4
- name: 🐍 Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: 📦 Install the base dependencies
run: python -m pip install --upgrade poetry
- name: 🔍 Check the correctness of the project config
run: poetry check
- name: 🚀 Do Install the package Test
run: poetry install

View File

@ -1,53 +0,0 @@
name: Docs WorkFlow - Develop Tag 📚
on:
push:
branches:
- develop
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.ref}}
cancel-in-progress: true
permissions:
contents: write
pages: write
pull-requests: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: 🔄 Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 🐍 Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: 📦 Install mkdocs-material
run: pip install "mkdocs-material"
- name: 📦 Install mkdocstrings[python]
run: pip install "mkdocstrings[python]"
- name: 📦 Install mkdocs-material[imaging]
run: pip install "mkdocs-material[imaging]"
- name: 📦 Install mike
run: pip install "mike"
- name: 📦 Install mkdocs-git-revision-date-localized-plugin
run: pip install "mkdocs-git-revision-date-localized-plugin"
- name: 📦 Install JupyterLab
run: pip install jupyterlab
- name: 📦 Install mkdocs-jupyter
run: pip install mkdocs-jupyter
- name: 📦 Install mkdocs-git-committers-plugin-2
run: pip install mkdocs-git-committers-plugin-2
- name: ⚙️ Configure git for github-actions
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: 🚀 Deploy MkDoc-Material with mike
run: |
MKDOCS_GIT_COMMITTERS_APIKEY=${{ secrets.GITHUB_TOKEN }} mike deploy --push develop

72
.github/workflows/publish-docs.yml vendored Normal file
View File

@ -0,0 +1,72 @@
name: Build and Publish Docs
on:
push:
branches:
- develop
workflow_dispatch:
release:
types: [published]
# Ensure only one concurrent deployment
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.ref}}
cancel-in-progress: true
# Restrict permissions by default
permissions:
contents: write # Required for committing to gh-pages
pages: write # Required for deploying to Pages
pull-requests: write # Required for PR comments
jobs:
deploy:
name: Publish Docs
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
matrix:
python-version: ["3.10"]
steps:
- name: 📥 Checkout the repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: 🐍 Install uv and set Python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6.3.1
with:
python-version: ${{ matrix.python-version }}
activate-environment: true
- name: 🔑 Create GitHub App token (mkdocs)
id: mkdocs_token
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
with:
app-id: ${{ secrets.MKDOCS_APP_ID }}
private-key: ${{ secrets.MKDOCS_PEM }}
owner: roboflow
repositories: mkdocs-material-insiders
- name: 🏗️ Install dependencies
run: |
uv pip install -r pyproject.toml --group docs
# Install mkdocs-material-insiders using the GitHub App token
uv pip install "git+https://roboflow:${{ steps.mkdocs_token.outputs.token }}@github.com/roboflow/mkdocs-material-insiders.git@9.5.49-insiders-4.53.14#egg=mkdocs-material[imaging]"
- name: ⚙️ Configure git for github-actions
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: 🚀 Deploy Development Docs
if: (github.event_name == 'push' && github.ref == 'refs/heads/develop') || github.event_name == 'workflow_dispatch'
run: |
MKDOCS_GIT_COMMITTERS_APIKEY=${{ secrets.GITHUB_TOKEN }} uv run mike deploy --push develop
- name: 🚀 Deploy Release Docs
if: github.event_name == 'release' && github.event.action == 'published'
run: |
latest_tag=$(git describe --tags `git rev-list --tags --max-count=1`)
MKDOCS_GIT_COMMITTERS_APIKEY=${{ secrets.GITHUB_TOKEN }} uv run mike deploy --push --update-aliases $latest_tag latest

View File

@ -0,0 +1,47 @@
name: Publish Supervision Pre-Releases to PyPI
on:
push:
tags:
- "[0-9]+.[0-9]+[0-9]+.[0-9]+a[0-9]"
- "[0-9]+.[0-9]+[0-9]+.[0-9]+b[0-9]"
- "[0-9]+.[0-9]+[0-9]+.[0-9]+rc[0-9]"
workflow_dispatch:
permissions: {} # Explicitly remove all permissions by default
jobs:
publish-pre-release:
name: Publish Pre-release Package
runs-on: ubuntu-latest
environment:
name: test
url: https://pypi.org/project/supervision/
timeout-minutes: 10
permissions:
id-token: write # Required for PyPI publishing
contents: read # Required for checkout
strategy:
matrix:
python-version: ["3.10"]
steps:
- name: 📥 Checkout the repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: 🐍 Install uv and set Python version ${{ matrix.python-version }}
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6.3.1
with:
python-version: ${{ matrix.python-version }}
activate-environment: true
- name: 🏗️ Build source and wheel distributions
run: |
uv pip install -r pyproject.toml --group build
uv build
uv run twine check --strict dist/*
- name: 🚀 Publish to PyPi
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
with:
attestations: true

View File

@ -1,55 +0,0 @@
name: Supervision Release Documentation Workflow 📚
on:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.ref}}
cancel-in-progress: true
permissions:
contents: write
pages: write
pull-requests: write
jobs:
doc-build-deploy:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10"]
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.head_ref }}
- name: 🐍 Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: 📦 Install mkdocs-material
run: pip install "mkdocs-material"
- name: 📦 Install mkdocstrings[python]
run: pip install "mkdocstrings[python]"
- name: 📦 Install mkdocs-material[imaging]
run: pip install "mkdocs-material[imaging]"
- name: 📦 Install mike
run: pip install "mike"
- name: 📦 Install mkdocs-git-revision-date-localized-plugin
run: pip install "mkdocs-git-revision-date-localized-plugin"
- name: 📦 Install JupyterLab
run: pip install jupyterlab
- name: 📦 Install mkdocs-jupyter
run: pip install mkdocs-jupyter
- name: 📦 Install mkdocs-git-committers-plugin-2
run: pip install mkdocs-git-committers-plugin-2
- name: ⚙️ Configure git for github-actions 👷
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: 🚀 Deploy MkDoc-Material 📚
run: |
latest_tag=$(git describe --tags `git rev-list --tags --max-count=1`)
MKDOCS_GIT_COMMITTERS_APIKEY=${{ secrets.GITHUB_TOKEN }} mike deploy --push --update-aliases $latest_tag latest

45
.github/workflows/publish-release.yml vendored Normal file
View File

@ -0,0 +1,45 @@
name: Publish Supervision Releases to PyPI
on:
push:
tags:
- "[0-9]+.[0-9]+[0-9]+.[0-9]"
workflow_dispatch:
permissions: {} # Explicitly remove all permissions by default
jobs:
publish-release:
name: Publish Release Package
runs-on: ubuntu-latest
environment:
name: release
url: https://pypi.org/project/supervision/
timeout-minutes: 10
permissions:
id-token: write # Required for PyPI publishing
contents: read # Required for checkout
strategy:
matrix:
python-version: ["3.10"]
steps:
- name: 📥 Checkout the repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: 🐍 Install uv and set Python version ${{ matrix.python-version }}
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6.3.1
with:
python-version: ${{ matrix.python-version }}
activate-environment: true
- name: 🏗️ Build source and wheel distributions
run: |
uv pip install -r pyproject.toml --group build
uv build
uv run twine check --strict dist/*
- name: 🚀 Publish to PyPi
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
with:
attestations: true

View File

@ -1,43 +0,0 @@
name: Publish Supervision Pre-Releases to PyPI and TestPyPI
on:
push:
tags:
- "[0-9]+.[0-9]+[0-9]+.[0-9]+a[0-9]"
- "[0-9]+.[0-9]+[0-9]+.[0-9]+b[0-9]"
- "[0-9]+.[0-9]+[0-9]+.[0-9]+rc[0-9]"
workflow_dispatch:
jobs:
build-and-publish-pre-release-pypi:
name: Build and publish to PyPI
runs-on: ubuntu-latest
environment: test
permissions:
id-token: write
strategy:
matrix:
python-version: ["3.10"]
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
- name: 🐍 Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: 🏗️ Build source and wheel distributions
run: |
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.10
- name: 🚀 Publish to Test-PyPi
uses: pypa/gh-action-pypi-publish@release/v1.10
with:
repository-url: https://test.pypi.org/legacy/

43
.github/workflows/publish-testpypi.yml vendored Normal file
View File

@ -0,0 +1,43 @@
name: Publish Supervision Releases to TestPyPI
on:
workflow_dispatch:
permissions: {} # Explicitly remove all permissions by default
jobs:
publish-testpypi:
name: Publish Release Package
runs-on: ubuntu-latest
environment:
name: release
url: https://pypi.org/project/supervision/
timeout-minutes: 10
permissions:
id-token: write # Required for PyPI publishing
contents: read # Required for checkout
strategy:
matrix:
python-version: ["3.10"]
steps:
- name: 📥 Checkout the repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: 🐍 Install uv and set Python version ${{ matrix.python-version }}
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6.3.1
with:
python-version: ${{ matrix.python-version }}
activate-environment: true
- name: 🏗️ Build source and wheel distributions
run: |
uv pip install -r pyproject.toml --group build
uv build
uv run twine check --strict dist/*
- name: 🚀 Publish to Test-PyPi
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
with:
repository-url: https://test.pypi.org/legacy/
attestations: true

View File

@ -1,40 +0,0 @@
name: Publish Supervision Releases to PyPI and TestPyPI
on:
push:
tags:
- "[0-9]+.[0-9]+[0-9]+.[0-9]"
workflow_dispatch:
jobs:
build-and-publish-pre-release:
runs-on: ubuntu-latest
environment: release
permissions:
id-token: write
strategy:
matrix:
python-version: ["3.10"]
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
- name: 🐍 Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: 🏗️ Build source and wheel distributions
run: |
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.10
- name: 🚀 Publish to Test-PyPi
uses: pypa/gh-action-pypi-publish@release/v1.10
with:
repository-url: https://test.pypi.org/legacy/

View File

@ -4,18 +4,34 @@ on:
pull_request:
branches: [main, develop]
# Restrict permissions by default
permissions:
contents: read # Required for checkout
checks: write # Required for test reporting
jobs:
docs-build-test:
name: Test docs build
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
matrix:
python-version: ["3.10"]
steps:
- name: 🔄 Checkout code
uses: actions/checkout@v4
- name: 🐍 Set up Python
uses: actions/setup-python@v5
- name: 📥 Checkout the repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
python-version: '3.10'
- name: 🏗️ Install dependencies and Test Docs Build
fetch-depth: 0
- name: 🐍 Install uv and set Python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6.3.1
with:
python-version: ${{ matrix.python-version }}
activate-environment: true
- name: 🏗️ Install dependencies
run: |
python -m pip install --upgrade pip
pip install "mkdocs-material" "mkdocstrings[python]" "mkdocs-material[imaging]" mike "mkdocs-git-revision-date-localized-plugin" jupyterlab mkdocs-jupyter mkdocs-git-committers-plugin-2
mkdocs build --verbose
uv pip install -r pyproject.toml --group docs
- name: 🧪 Test Docs Build
run: uv run mkdocs build --verbose

View File

@ -1,58 +0,0 @@
name: Python 3.8 - Min Dep Test WorkFlow
on:
pull_request:
branches: [main, develop]
jobs:
build-min-dep-test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8"]
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
- name: 🐍 Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
# id based on python version
id: python-setup
with:
python-version: ${{ matrix.python-version }}
check-latest: true
- name: 📦 Install dependencies
run: |
python -m pip install --upgrade pip
pip install \
attrs==23.1.0 \
certifi==2023.7.22 \
charset-normalizer==2.0.12 \
cycler==0.12.1 \
exceptiongroup==1.1.3 \
fonttools==4.43.1 \
idna==3.4 \
iniconfig==2.0.0 \
kiwisolver==1.4.5 \
matplotlib==3.5.0 \
numpy==1.21.2 \
opencv-python==4.5.5.64 \
Pillow==10.1.0 \
packaging==23.2 \
pluggy==1.3.0 \
pyparsing==3.1.1 \
pytest==7.2.0 \
python-dateutil==2.8.2 \
PyYAML==5.3 \
requests==2.26.0 \
scipy==1.10.0 \
setuptools-scm==8.0.4 \
six==1.16.0 \
tomli==2.0.1 \
tqdm==4.62.3 \
typing_extensions==4.8.0 \
urllib3==1.26.18 \
defusedxml==0.7.1
- name: 🧪 Test
run: "python -m pytest ./test"

View File

@ -1,29 +0,0 @@
name: Test WorkFlow
on:
pull_request:
branches: [main, develop]
jobs:
build-dev-test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
- name: 🐍 Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
check-latest: true
- name: 📦 Install dependencies
run: |
python -m pip install --upgrade pip
pip install .
pip install pytest
- name: 🧪 Test
run: "python -m pytest ./test"

35
.github/workflows/uv-test.yml vendored Normal file
View File

@ -0,0 +1,35 @@
name: 🔧 Pytest/Test Workflow
on:
pull_request:
branches: [main, develop]
jobs:
run-tests:
name: Import Test and Pytest Run
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
runs-on: ${{ matrix.os }}
steps:
- name: 📥 Checkout the repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: 🐍 Install uv and set Python version ${{ matrix.python-version }}
uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba # v6.3.1
with:
python-version: ${{ matrix.python-version }}
activate-environment: true
- name: 🚀 Install Packages
run: uv pip install -r pyproject.toml --group dev --group docs --extra metrics
- name: 🧪 Run the Import test
run: uv run python -c "import supervision; from supervision import assets; from supervision import metrics; print(supervision.__version__)"
- name: 🧪 Run the Test
run: uv run pytest

View File

@ -25,14 +25,14 @@ repos:
- id: mixed-line-ending
- repo: https://github.com/PyCQA/bandit
rev: '1.7.10'
rev: '1.8.6'
hooks:
- id: bandit
args: ["-c", "pyproject.toml"]
additional_dependencies: ["bandit[toml]"]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.7.3
rev: v0.12.3
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
@ -48,8 +48,16 @@ repos:
# args: ["--number"]
- repo: https://github.com/codespell-project/codespell
rev: v2.3.0
rev: v2.4.1
hooks:
- id: codespell
additional_dependencies:
- tomli
- repo: https://github.com/asottile/pyupgrade
rev: v3.20.0
hooks:
- id: pyupgrade
args: ["--py310-plus"]
additional_dependencies:
- tomli

View File

@ -128,15 +128,26 @@ PRs must pass all tests and linting requirements before they can be merged.
Before starting your work on the project, set up your development environment:
1. Clone your fork of the project:
1. Clone your fork of the project (recommended to use shallow clone of develop branch):
**Option A: Recommended for most contributors (shallow clone of develop branch):**
```bash
git clone --depth 1 -b develop https://github.com/YOUR_USERNAME/supervision.git
cd supervision
```
Replace `YOUR_USERNAME` with your GitHub username.
> Note: Using `--depth 1` creates a shallow clone with minimal history and `-b develop` ensures you start with the development branch. This significantly reduces download size while providing everything needed to contribute.
**Option B: Full repository clone (if you need complete history):**
```bash
git clone https://github.com/YOUR_USERNAME/supervision.git
cd supervision
```
Replace `YOUR_USERNAME` with your GitHub username.
2. Create and activate a virtual environment:
```bash
@ -144,31 +155,20 @@ Before starting your work on the project, set up your development environment:
source .venv/bin/activate
```
3. Install Poetry:
3. Install `uv`:
Using pip:
```bash
pip install -U pip setuptools
pip install poetry
```
Or using pipx (recommended for global installation):
```bash
pipx install poetry
```
Follow the instructions on the [uv installation page](https://docs.astral.sh/uv/getting-started/installation/).
4. Install project dependencies:
```bash
poetry install
uv pip install -r pyproject.toml --extra dev --extra docs --extra metrics
```
5. Run pytest to verify the setup:
```bash
poetry run pytest
uv run pytest
```
## 🎨 Code Style and Quality
@ -181,7 +181,7 @@ Furthermore, we have integrated a pre-commit GitHub Action into our workflow. Th
To run the pre-commit tool, follow these steps:
1. Install pre-commit by running the following command: `poetry install --with dev`. It will not only install pre-commit but also install all the deps and dev-deps of project
1. Install pre-commit by running the following command: `uv pip install -r pyproject.toml --extra dev`. It will not only install pre-commit but also install all the deps and dev-deps of project
2. Once pre-commit is installed, navigate to the project's root directory.
@ -203,7 +203,7 @@ So far, **there is no type checking with mypy**. See [issue](https://github.com/
The `supervision` documentation is stored in a folder called `docs`. The project documentation is built using `mkdocs`.
To run the documentation, install the project requirements with `poetry install --with dev`. Then, run `mkdocs serve` to start the documentation server.
To run the documentation, install the project requirements with `uv pip install -r pyproject.toml --extra dev --extra docs`. Then, run `mkdocs serve` to start the documentation server.
You can learn more about mkdocs on the [mkdocs website](https://www.mkdocs.org/).

View File

@ -37,7 +37,7 @@
## 💻 install
Pip install the supervision package in a
[**Python>=3.8**](https://www.python.org/) environment.
[**Python>=3.9**](https://www.python.org/) environment.
```bash
pip install supervision

2
demo.ipynb vendored
View File

@ -353,7 +353,7 @@
},
"outputs": [],
"source": [
"!pip install -q ultralytics"
"!pip install -q \"ultralytics<=8.3.40\""
]
},
{

View File

@ -1,6 +1,5 @@
---
comments: true
status: new
---
# Assets
@ -8,17 +7,6 @@ status: new
Supervision offers an assets download utility that allows you to download video files
that you can use in your demos.
## Install extra
To install the Supervision assets utility, you can use `pip`. This utility is available
as an extra within the Supervision package.
!!! example "pip install"
```bash
pip install "supervision[assets]"
```
<div class="md-typeset">
<h2><a href="#supervision.assets.downloader.download_assets.download_assets">download_assets</a></h2>
</div>

View File

@ -1,4 +1,179 @@
# CHANGELOG
# Changelog
### 0.26.0 <small>Jul 16, 2025</small>
!!! failure "Removed"
`supervision-0.26.0` drops `python3.8` support and upgrade all codes to `python3.9` syntax style.
!!! info "Tip"
Supervisions documentation theme now has a fresh look that is consistent with the documentations of all Roboflow open-source projects. ([#1858](https://github.com/roboflow/supervision/pull/1858))
- Added [#1774](https://github.com/roboflow/supervision/pull/1774): Support for the IOS (Intersection over Smallest) overlap metric that measures how much of the smaller object is covered by the larger one in [`sv.Detections.with_nms`](https://supervision.roboflow.com/0.26.0/detection/core/#supervision.detection.core.Detections.with_nms), [`sv.Detections.with_nmm`](https://supervision.roboflow.com/0.26.0/detection/core/#supervision.detection.core.Detections.with_nmm), [`sv.box_iou_batch`](https://supervision.roboflow.com/0.26.0/detection/utils/iou_and_nms/#supervision.detection.utils.iou_and_nms.box_iou_batch), and [`sv.mask_iou_batch`](https://supervision.roboflow.com/0.26.0/detection/utils/iou_and_nms/#supervision.detection.utils.iou_and_nms.mask_iou_batch).
```python
import numpy as np
import supervision as sv
boxes_true = np.array([
[100, 100, 200, 200],
[300, 300, 400, 400]
])
boxes_detection = np.array([
[150, 150, 250, 250],
[320, 320, 420, 420]
])
sv.box_iou_batch(
boxes_true=boxes_true,
boxes_detection=boxes_detection,
overlap_metric=sv.OverlapMetric.IOU
)
# array([[0.14285714, 0. ],
# [0. , 0.47058824]])
sv.box_iou_batch(
boxes_true=boxes_true,
boxes_detection=boxes_detection,
overlap_metric=sv.OverlapMetric.IOS
)
# array([[0.25, 0. ],
# [0. , 0.64]])
```
- Added [#1874](https://github.com/roboflow/supervision/pull/1874): [`sv.box_iou`](https://supervision.roboflow.com/0.26.0/detection/utils/iou_and_nms/#supervision.detection.utils.iou_and_nms.box_iou) that efficiently computes the Intersection over Union (IoU) between two individual bounding boxes.
- Added [#1816](https://github.com/roboflow/supervision/pull/1816): Support for frame limitations and progress bar in [`sv.process_video`](https://supervision.roboflow.com/0.26.0/utils/video/#supervision.utils.video.process_video).
- Added [#1788](https://github.com/roboflow/supervision/pull/1788): Support for creating [`sv.KeyPoints`](https://supervision.roboflow.com/0.26.0/keypoint/core/#supervision.keypoint.core.KeyPoints) objects from [ViTPose](https://huggingface.co/docs/transformers/en/model_doc/vitpose) and [ViTPose++](https://huggingface.co/docs/transformers/en/model_doc/vitpose#vitpose-models) inference results via [`sv.KeyPoints.from_transformers`](https://supervision.roboflow.com/0.26.0/keypoint/core/#supervision.keypoint.core.KeyPoints.from_transformers).
- Added [#1823](https://github.com/roboflow/supervision/pull/1823): [`sv.xyxy_to_xcycarh`](https://supervision.roboflow.com/0.26.0/detection/utils/converters/#supervision.detection.utils.converters.xyxy_to_xcycarh) function to convert bounding box coordinates from `(x_min, y_min, x_max, y_max)` into measurement space to format `(center x, center y, aspect ratio, height)`, where the aspect ratio is `width / height`.
- Added [#1788](https://github.com/roboflow/supervision/pull/1788): [`sv.xyxy_to_xywh`](https://supervision.roboflow.com/0.26.0/detection/utils/converters/#supervision.detection.utils.converters.xyxy_to_xywh) function to convert bounding box coordinates from `(x_min, y_min, x_max, y_max)` format to `(x, y, width, height)` format.
- Changed [#1820](https://github.com/roboflow/supervision/pull/1820): [`sv.LabelAnnotator`](https://supervision.roboflow.com/0.26.0/detection/annotators/#supervision.annotators.core.LabelAnnotator) now supports the `smart_position` parameter to automatically keep labels within frame boundaries, and the `max_line_length` parameter to control text wrapping for long or multi-line labels.
- Changed [#1825](https://github.com/roboflow/supervision/pull/1825): [`sv.LabelAnnotator`](https://supervision.roboflow.com/0.26.0/detection/annotators/#supervision.annotators.core.LabelAnnotator) now supports non-string labels.
- Changed [#1792](https://github.com/roboflow/supervision/pull/1792): [`sv.Detections.from_vlm`](https://supervision.roboflow.com/0.26.0/detection/core/#supervision.detection.core.Detections.from_vlm) now supports parsing bounding boxes and segmentation masks from responses generated by [Google Gemini models](https://ai.google.dev/gemini-api/docs/vision).
```python
import supervision as sv
gemini_response_text = """```json
[
{"box_2d": [543, 40, 728, 200], "label": "cat", "id": 1},
{"box_2d": [653, 352, 820, 522], "label": "dog", "id": 2}
]
```"""
detections = sv.Detections.from_vlm(
sv.VLM.GOOGLE_GEMINI_2_5,
gemini_response_text,
resolution_wh=(1000, 1000),
classes=['cat', 'dog'],
)
detections.xyxy
# array([[543., 40., 728., 200.], [653., 352., 820., 522.]])
detections.data
# {'class_name': array(['cat', 'dog'], dtype='<U26')}
detections.class_id
# array([0, 1])
```
- Changed [#1878](https://github.com/roboflow/supervision/pull/1878): [`sv.Detections.from_vlm`](https://supervision.roboflow.com/0.26.0/detection/core/#supervision.detection.core.Detections.from_vlm) now supports parsing bounding boxes from responses generated by [Moondream](https://github.com/vikhyat/moondream).
```python
import supervision as sv
moondream_result = {
'objects': [
{
'x_min': 0.5704046934843063,
'y_min': 0.20069346576929092,
'x_max': 0.7049859315156937,
'y_max': 0.3012596592307091
},
{
'x_min': 0.6210969910025597,
'y_min': 0.3300672620534897,
'x_max': 0.8417936339974403,
'y_max': 0.4961046129465103
}
]
}
detections = sv.Detections.from_vlm(
sv.VLM.MOONDREAM,
moondream_result,
resolution_wh=(1000, 1000),
)
detections.xyxy
# array([[1752.28, 818.82, 2165.72, 1229.14],
# [1908.01, 1346.67, 2585.99, 2024.11]])
```
- Changed [#1709](https://github.com/roboflow/supervision/pull/1790): [`sv.Detections.from_vlm`](https://supervision.roboflow.com/0.26.0/detection/core/#supervision.detection.core.Detections.from_vlm) now supports parsing bounding boxes from responses generated by [Qwen-2.5 VL](https://github.com/QwenLM/Qwen2.5-VL).
```python
import supervision as sv
qwen_2_5_vl_result = """```json
[
{"bbox_2d": [139, 768, 315, 954], "label": "cat"},
{"bbox_2d": [366, 679, 536, 849], "label": "dog"}
]
```"""
detections = sv.Detections.from_vlm(
sv.VLM.QWEN_2_5_VL,
qwen_2_5_vl_result,
input_wh=(1000, 1000),
resolution_wh=(1000, 1000),
classes=['cat', 'dog'],
)
detections.xyxy
# array([[139., 768., 315., 954.], [366., 679., 536., 849.]])
detections.class_id
# array([0, 1])
detections.data
# {'class_name': array(['cat', 'dog'], dtype='<U10')}
detections.class_id
# array([0, 1])
```
- Changed [#1786](https://github.com/roboflow/supervision/pull/1786): Significantly improved the speed of HSV color mapping in [`sv.HeatMapAnnotator`](https://supervision.roboflow.com/0.26.0/detection/annotators/#supervision.annotators.core.HeatMapAnnotator), achieving approximately 28x faster performance on 1920x1080 frames.
- Fix [#1834](https://github.com/roboflow/supervision/pull/1834): Supervisions [`sv.MeanAveragePrecision`](https://supervision.roboflow.com/0.26.0/metrics/mean_average_precision/#supervision.metrics.mean_average_precision.MeanAveragePrecision) is now fully aligned with [pycocotools](https://github.com/ppwwyyxx/cocoapi), the official COCO evaluation tool, ensuring accurate and standardized metrics. This update enabled us to launch a new version of the [Computer Vision Model Leaderboard](https://leaderboard.roboflow.com/).
```python
import supervision as sv
from supervision.metrics import MeanAveragePrecision
predictions = sv.Detections(...)
targets = sv.Detections(...)
map_metric = MeanAveragePrecision()
map_metric.update(predictions, targets).compute()
# Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.464
# Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.637
# Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.203
# Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.284
# Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.497
# Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.629
```
- Fix [#1767](https://github.com/roboflow/supervision/pull/1767): Fixed losing `sv.Detections.data` when detections filtering.
### 0.25.0 <small>Nov 12, 2024</small>

View File

@ -7,21 +7,19 @@ status: deprecated
These features are phased out due to better alternatives or potential issues in future versions. Deprecated functionalities are supported for **five subsequent releases**, providing time for users to transition to updated methods.
- 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.
- `sv.LMM` enum is deprecated and will be removed in `supervision-0.31.0`. Use `sv.VLM` instead.
- [`sv.Detections.from_lmm`](https://supervision.roboflow.com/0.26.0/detection/core/#supervision.detection.core.Detections.from_lmm) property is deprecated and will be removed in `supervision-0.31.0`. Use [`sv.Detections.from_vlm`](https://supervision.roboflow.com/0.26.0/detection/core/#supervision.detection.core.Detections.from_vlm) instead.
# Removed
### 0.25.0
### 0.26.0
- The `sv.DetectionDataset.images` property has been 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. Also, constructing `sv.DetectionDataset` with parameter `images` as `Dict[str, np.ndarray]` is deprecated and has been removed in `supervision-0.26.0`. Please pass a list of paths `List[str]` instead.
- The name `sv.BoundingBoxAnnotator` is deprecated and has been removed in `supervision-0.26.0`. It has been renamed to [`sv.BoxAnnotator`](https://supervision.roboflow.com/0.22.0/detection/annotators/#supervision.annotators.core.BoxAnnotator).
No removals in this version!
### 0.24.0
@ -35,12 +33,12 @@ No removals in this version!
### 0.22.0
- `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`](/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__` was removed as of `supervision-0.22.0`. Use the attribute [`FPSMonitor.fps`](utils/video.md/#supervision.utils.video.FPSMonitor.fps) instead.
- `sv.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 `sv.Color.white()` was removed as of `supervision-0.22.0`. Use the constant `sv.Color.WHITE` instead.
- The method `sv.Color.black()` was removed as of `supervision-0.22.0`. Use the constant `sv.Color.BLACK` instead.
- The method `sv.Color.red()` was removed as of `supervision-0.22.0`. Use the constant `sv.Color.RED` instead.
- The method `sv.Color.green()` was removed as of `supervision-0.22.0`. Use the constant `sv.Color.GREEN` instead.
- The method `sv.Color.blue()` was removed as of `supervision-0.22.0`. Use the constant `sv.Color.BLUE` instead.
- The method `sv.ColorPalette.default()` was removed as of `supervision-0.22.0`. Use the constant [`ColorPalette.DEFAULT`](/utils/draw/#supervision.draw.color.ColorPalette.DEFAULT) instead.
- `sv.BoxAnnotator` was removed as of `supervision-0.22.0`, however `sv.BoundingBoxAnnotator` was immediately renamed to `sv.BoxAnnotator`. Use [`BoxAnnotator`](detection/annotators.md/#supervision.annotators.core.BoxAnnotator) and [`LabelAnnotator`](detection/annotators.md/#supervision.annotators.core.LabelAnnotator) instead of the old `sv.BoxAnnotator`.
- The method `sv.FPSMonitor.__call__` was removed as of `supervision-0.22.0`. Use the attribute [`sv.FPSMonitor.fps`](utils/video.md/#supervision.utils.video.FPSMonitor.fps) instead.

View File

@ -234,7 +234,8 @@ Annotators accept detections and apply box or mask visualizations to the detecti
<div class="result" markdown>
![mask-annotator-example](https://media.roboflow.com/supervision-annotator-examples/mask-annotator-example-purple.png){ align=center width="800" }
![mask-annotator-example](https://media.roboflow.com/supervision-annotator-examples/
mask-annotator-example-purple.png){ align=center width="800" }
</div>
@ -255,7 +256,8 @@ Annotators accept detections and apply box or mask visualizations to the detecti
<div class="result" markdown>
![polygon-annotator-example](https://media.roboflow.com/supervision-annotator-examples/polygon-annotator-example-purple.png){ align=center width="800" }
![polygon-annotator-example](https://media.roboflow.com/supervision-annotator-examples/
polygon-annotator-example-purple.png){ align=center width="800" }
</div>
@ -283,7 +285,8 @@ Annotators accept detections and apply box or mask visualizations to the detecti
<div class="result" markdown>
![label-annotator-example](https://media.roboflow.com/supervision-annotator-examples/label-annotator-example-purple.png){ align=center width="800" }
![label-annotator-example](https://media.roboflow.com/supervision-annotator-examples/
label-annotator-example-purple.png){ align=center width="800" }
</div>
@ -314,7 +317,8 @@ Annotators accept detections and apply box or mask visualizations to the detecti
<div class="result" markdown>
![label-annotator-example](https://media.roboflow.com/supervision-annotator-examples/label-annotator-example-purple.png){ align=center width="800" }
![label-annotator-example](https://media.roboflow.com/supervision-annotator-examples/
label-annotator-example-purple.png){ align=center width="800" }
</div>
@ -341,24 +345,32 @@ Annotators accept detections and apply box or mask visualizations to the detecti
<div class="result" markdown>
![icon-annotator-example](https://media.roboflow.com/supervision-annotator-examples/icon-annotator-example.png){ align=center width="800" }
![icon-annotator-example](https://media.roboflow.com/supervision-annotator-examples/
icon-annotator-example.png){ align=center width="800" }
</div>
=== "Crop"
<!-- === "Crop"
```python
import supervision as sv
```python
import supervision as sv
image = ...
detections = sv.Detections(...)
image = ...
detections = sv.Detections(...)
crop_annotator = sv.CropAnnotator()
annotated_frame = crop_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
crop_annotator = sv.CropAnnotator()
annotated_frame = crop_annotator.annotate(
scene=image.copy(),
detections=detections
)
```
<div class="result" markdown>
![crop-annotator-example](https://media.roboflow.com/supervision-annotator-examples/
crop-annotator-example.png){ align=center width="800" }
</div>
-->
=== "Blur"
@ -377,7 +389,8 @@ Annotators accept detections and apply box or mask visualizations to the detecti
<div class="result" markdown>
![blur-annotator-example](https://media.roboflow.com/supervision-annotator-examples/blur-annotator-example-purple.png){ align=center width="800" }
![blur-annotator-example](https://media.roboflow.com/supervision-annotator-examples/
blur-annotator-example-purple.png){ align=center width="800" }
</div>
@ -398,7 +411,8 @@ Annotators accept detections and apply box or mask visualizations to the detecti
<div class="result" markdown>
![pixelate-annotator-example](https://media.roboflow.com/supervision-annotator-examples/pixelate-annotator-example-10.png){ align=center width="800" }
![pixelate-annotator-example](https://media.roboflow.com/supervision-annotator-examples/
pixelate-annotator-example-10.png){ align=center width="800" }
</div>
@ -429,7 +443,8 @@ Annotators accept detections and apply box or mask visualizations to the detecti
<div class="result" markdown>
![trace-annotator-example](https://media.roboflow.com/supervision-annotator-examples/trace-annotator-example-purple.png){ align=center width="800" }
![trace-annotator-example](https://media.roboflow.com/supervision-annotator-examples/
trace-annotator-example-purple.png){ align=center width="800" }
</div>
@ -458,7 +473,8 @@ Annotators accept detections and apply box or mask visualizations to the detecti
<div class="result" markdown>
![heat-map-annotator-example](https://media.roboflow.com/supervision-annotator-examples/heat-map-annotator-example-purple.png){ align=center width="800" }
![heat-map-annotator-example](https://media.roboflow.com/supervision-annotator-examples/
heat-map-annotator-example-purple.png){ align=center width="800" }
</div>
@ -479,7 +495,31 @@ Annotators accept detections and apply box or mask visualizations to the detecti
<div class="result" markdown>
![background-overlay-annotator-example](https://media.roboflow.com/supervision-annotator-examples/background-color-annotator-example-purple.png)
![background-overlay-annotator-example](https://media.roboflow.com/supervision-annotator-examples/background-color-annotator-example-purple.png){ align=center width="800" }
</div>
=== "Comparison"
```python
import supervision as sv
image = ...
detections_1 = sv.Detections(...)
detections_2 = sv.Detections(...)
comparison_annotator = sv.ComparisonAnnotator()
annotated_frame = comparison_annotator.annotate(
scene=image.copy(),
detections_1=detections_1,
detections_2=detections_2
)
```
<div class="result" markdown>
![comparison-annotator-example](https://media.roboflow.com/supervision-annotator-examples/
comparison-annotator-example.png){ align=center width="800" }
</div>
@ -622,6 +662,12 @@ Annotators accept detections and apply box or mask visualizations to the detecti
:::supervision.annotators.core.BackgroundOverlayAnnotator
<div class="md-typeset">
<h2><a href="#supervision.annotators.core.ComparisonAnnotator">ComparisonAnnotator</a></h2>
</div>
:::supervision.annotators.core.ComparisonAnnotator
<div class="md-typeset">
<h2><a href="#supervision.annotators.core.ColorLookup">ColorLookup</a></h2>
</div>

View File

@ -1,29 +0,0 @@
---
comments: true
---
# Double Detection Filter
<div class="md-typeset">
<h2><a href="#supervision.detection.overlap_filter.OverlapFilter">OverlapFilter</a></h2>
</div>
:::supervision.detection.overlap_filter.OverlapFilter
<div class="md-typeset">
<h2><a href="#supervision.detection.overlap_filter.box_non_max_suppression">box_non_max_suppression</a></h2>
</div>
:::supervision.detection.overlap_filter.box_non_max_suppression
<div class="md-typeset">
<h2><a href="#supervision.detection.overlap_filter.mask_non_max_suppression">mask_non_max_suppression</a></h2>
</div>
:::supervision.detection.overlap_filter.mask_non_max_suppression
<div class="md-typeset">
<h2><a href="#supervision.detection.overlap_filter.box_non_max_merge">box_non_max_merge</a></h2>
</div>
:::supervision.detection.overlap_filter.box_non_max_merge

View File

@ -1,6 +1,5 @@
---
comments: true
status: new
---
<div class="md-typeset">

View File

@ -1,108 +0,0 @@
---
comments: true
status: new
---
# Detection Utils
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.box_iou_batch">box_iou_batch</a></h2>
</div>
:::supervision.detection.utils.box_iou_batch
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.mask_iou_batch">mask_iou_batch</a></h2>
</div>
:::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>
:::supervision.detection.utils.polygon_to_mask
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.mask_to_xyxy">mask_to_xyxy</a></h2>
</div>
:::supervision.detection.utils.mask_to_xyxy
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.mask_to_polygons">mask_to_polygons</a></h2>
</div>
:::supervision.detection.utils.mask_to_polygons
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.polygon_to_xyxy">polygon_to_xyxy</a></h2>
</div>
:::supervision.detection.utils.polygon_to_xyxy
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.filter_polygons_by_area">filter_polygons_by_area</a></h2>
</div>
:::supervision.detection.utils.filter_polygons_by_area
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.move_boxes">move_boxes</a></h2>
</div>
:::supervision.detection.utils.move_boxes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.move_masks">move_masks</a></h2>
</div>
:::supervision.detection.utils.move_masks
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.scale_boxes">scale_boxes</a></h2>
</div>
:::supervision.detection.utils.scale_boxes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.clip_boxes">clip_boxes</a></h2>
</div>
:::supervision.detection.utils.clip_boxes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.pad_boxes">pad_boxes</a></h2>
</div>
:::supervision.detection.utils.pad_boxes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.xywh_to_xyxy">xywh_to_xyxy</a></h2>
</div>
:::supervision.detection.utils.xywh_to_xyxy
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.xcycwh_to_xyxy">xcycwh_to_xyxy</a></h2>
</div>
:::supervision.detection.utils.xcycwh_to_xyxy
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.contains_holes">contains_holes</a></h2>
</div>
:::supervision.detection.utils.contains_holes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.contains_multiple_segments">contains_multiple_segments</a></h2>
</div>
:::supervision.detection.utils.contains_multiple_segments

View File

@ -0,0 +1,36 @@
---
comments: true
status: new
---
# Boxes Utils
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.boxes.move_boxes">move_boxes</a></h2>
</div>
:::supervision.detection.utils.boxes.move_boxes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.boxes.scale_boxes">scale_boxes</a></h2>
</div>
:::supervision.detection.utils.boxes.scale_boxes
<div class="md-typeset">
<h2><a href="#supervision.detection.boxes.utils.clip_boxes">clip_boxes</a></h2>
</div>
:::supervision.detection.utils.boxes.clip_boxes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.boxes.pad_boxes">pad_boxes</a></h2>
</div>
:::supervision.detection.utils.boxes.pad_boxes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.boxes.denormalize_boxes">denormalize_boxes</a></h2>
</div>
:::supervision.detection.utils.boxes.denormalize_boxes

View File

@ -0,0 +1,60 @@
---
comments: true
status: new
---
# Converters Utils
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.converters.xyxy_to_xywh">xyxy_to_xywh</a></h2>
</div>
:::supervision.detection.utils.converters.xyxy_to_xywh
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.converters.xywh_to_xyxy">xywh_to_xyxy</a></h2>
</div>
:::supervision.detection.utils.converters.xywh_to_xyxy
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.converters.xyxy_to_xcycarh">xyxy_to_xcycarh</a></h2>
</div>
:::supervision.detection.utils.converters.xyxy_to_xcycarh
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.converters.xcycwh_to_xyxy">xcycwh_to_xyxy</a></h2>
</div>
:::supervision.detection.utils.converters.xcycwh_to_xyxy
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.converters.xyxy_to_polygons">xyxy_to_polygons</a></h2>
</div>
:::supervision.detection.utils.converters.xyxy_to_polygons
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.converters.mask_to_xyxy">mask_to_xyxy</a></h2>
</div>
:::supervision.detection.utils.converters.mask_to_xyxy
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.converters.mask_to_polygons">mask_to_polygons</a></h2>
</div>
:::supervision.detection.utils.converters.mask_to_polygons
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.converters.polygon_to_mask">polygon_to_mask</a></h2>
</div>
:::supervision.detection.utils.converters.polygon_to_mask
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.converters.polygon_to_xyxy">polygon_to_xyxy</a></h2>
</div>
:::supervision.detection.utils.converters.polygon_to_xyxy

View File

@ -0,0 +1,72 @@
---
comments: true
status: new
---
# IoU and NMS Utils
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.OverlapFilter">OverlapFilter</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.OverlapFilter
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.OverlapMetric">OverlapMetric</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.OverlapMetric
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.utils.box_iou">box_iou</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.box_iou
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.box_iou_batch">box_iou_batch</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.box_iou_batch
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.box_iou_batch_with_jaccard">box_iou_batch_with_jaccard</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.box_iou_batch_with_jaccard
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.mask_iou_batch">mask_iou_batch</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.mask_iou_batch
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.oriented_box_iou_batch">oriented_box_iou_batch</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.oriented_box_iou_batch
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.box_non_max_suppression">box_non_max_suppression</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.box_non_max_suppression
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.mask_non_max_suppression">mask_non_max_suppression</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.mask_non_max_suppression
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.box_non_max_merge">box_non_max_merge</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.box_non_max_merge
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.iou_and_nms.mask_non_max_merge">mask_non_max_merge</a></h2>
</div>
:::supervision.detection.utils.iou_and_nms.mask_non_max_merge

View File

@ -0,0 +1,24 @@
---
comments: true
status: new
---
# Masks Utils
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.masks.move_masks">move_masks</a></h2>
</div>
:::supervision.detection.utils.masks.move_masks
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.masks.contains_holes">contains_holes</a></h2>
</div>
:::supervision.detection.utils.masks.contains_holes
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.masks.contains_multiple_segments">contains_multiple_segments</a></h2>
</div>
:::supervision.detection.utils.masks.contains_multiple_segments

View File

@ -0,0 +1,18 @@
---
comments: true
status: new
---
# Polygons Utils
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.polygons.filter_polygons_by_area">filter_polygons_by_area</a></h2>
</div>
:::supervision.detection.utils.polygons.filter_polygons_by_area
<div class="md-typeset">
<h2><a href="#supervision.detection.utils.polygons.approximate_polygon">approximate_polygon</a></h2>
</div>
:::supervision.detection.utils.polygons.approximate_polygon

View File

@ -0,0 +1,441 @@
---
comments: true
status: new
---
![Corgi Example](https://media.roboflow.com/supervision/image-examples/how-to/benchmark-models/corgi-sorted-2.png)
# Benchmark a Model
Have you ever trained multiple detection models and wondered which one performs best on your specific use case? Or maybe you've downloaded a pre-trained model and want to verify its performance on your dataset? Model benchmarking is essential for making informed decisions about which model to deploy in production.
This guide will show an easy way to benchmark your results using `supervision`. It will go over:
1. [Loading a dataset](#loading-a-dataset)
2. [Loading a model](#loading-a-model)
3. [Benchmarking Basics](#benchmarking-basics)
4. [Running a Model](#running-a-model)
5. [Remapping Classes](#remapping-classes)
6. [Visual Benchmarking](#visual-benchmarking)
7. [Benchmarking Metrics](#benchmarking-metrics)
8. [Mean Average Precision (mAP)](#mean-average-precision-map)
9. [F1 Score](#f1-score)
10. [Bonus: Model Leaderboard](#model-leaderboard)
This guide will use an instance segmentation model, but it applies to object detection, instance segmentation, and oriented bounding box models (OBB) too.
A condensed version of this guide is available as a [Colab Notebook](https://colab.research.google.com/drive/1HoOY9pZoVwGiRMmLHtir0qT6Uj45w6Ps?usp=sharing).
## Loading a Dataset
Suppose you start with a dataset. Perhaps you found it on [Universe](https://universe.roboflow.com/); perhaps you [labeled your own](https://roboflow.com/how-to-label/yolo11). In either case, this guide assumes you know of a labelled dataset at hand.
We'll use the following libraries:
- `roboflow` to manage the dataset and deploy models
- `inference` to run the models
- `supervision` to evaluate the model results
```bash
pip install roboflow supervision
pip install git+https://github.com/roboflow/inference.git@linas/allow-latest-rc-supervision
```
!!! info
We're updating `inference` at the moment. Please install it as shown above.
Here's how you can download a dataset:
```python
from roboflow import Roboflow
rf = Roboflow(api_key="<YOUR_API_KEY>")
project = rf.workspace("<WORKSPACE_NAME>").project("<PROJECT_NAME>")
dataset = project.version(<DATASET_VERSION_NUMBER>).download("<FORMAT>")
```
If your dataset is from Universe, go to `Dataset` > `Download Dataset` > select the format (e.g. `YOLOv11`) > `Show download code`.
If labeling your own data, go to the [dashboard](https://app.roboflow.com/) and check this [guide](https://docs.roboflow.com/api-reference/workspace-and-project-ids) to find your workspace and project IDs.
In this guide, we shall use a small [Corgi v2](https://universe.roboflow.com/model-examples/segmented-animals-basic) dataset. It is well-labeled and comes with a test set.
```python
from roboflow import Roboflow
rf = Roboflow(api_key="<YOUR_API_KEY>")
project = rf.workspace("fbamse1-gm2os").project("corgi-v2")
dataset = project.version(4).download("yolov11")
```
This will create a folder called `Corgi-v2-4` with the dataset in the current working directory, with `train`, `test`, and `valid` folders and a `data.yaml` file.
## Loading a Model
Let's load a model.
=== "Inference, Local"
Roboflow supports a range of state-of-the-art [pre-trained models](https://inference.roboflow.com/quickstart/aliases/) for object detection, instance segmentation, and pose tracking. You don't even need an API key!
Let's load such a model with inference [`inference`](https://inference.roboflow.com/).
```python
from inference import get_model
model = get_model(model_id="yolov11s-seg-640")
```
=== "Inference, Deployed"
You can train and deploy a model without leaving the Roboflow platform. See this [guide](https://docs.roboflow.com/train/train/train-from-scratch) for more details.
To load a model, you can use inference:
```python
from inference import get_model
model_id = "<PROJECT_NAME>/<MODEL_VERSION>"
model = get_model(model_id=model_id)
```
=== "Ultralytics"
Similarly to Inference, Ultralytics allows you to run a variety of models.
```bash
pip install "ultralytics<=8.3.40"
```
```python
from ultralytics import YOLO
model = YOLO("yolo11s-seg.pt")
```
## Benchmarking Basics
Evaluating your model requires careful selection of the dataset. Which images should you use?Let's go over the different scenarios.
- **Unrelated Dataset**: If you have a dataset that was not used to train the model, this is the best choice.
- **Training Set**: This is the set of images used to train the model. This is fine if the model was not trained on this dataset. Otherwise, **never** use it for benchmarking - the results will seem unrealistically good.
- **Validation Set**: This is the set of images used to validate the model during training. Every Nth training epoch, the model is evaluated on the validation set. Often the training is stopped once the validation loss stops improving. Therefore, even while the images aren't used to train the model, it still indirectly influences the training outcome.
- **Test Set**: This is the set of images kept aside for model testing. It is exactly the set you should use for benchmarking. If the dataset was split correctly, none of these images would be shown to the model during training.
Therefore, an unrelated dataset or the `test` set is the best choice for benchmarking.
Several other problems may arise:
- **Extra Classes**: An unrelated dataset may contain additional classes which you may need to [filter out](https://supervision.roboflow.com/how_to/filter_detections/#by-set-of-classes) before computing metrics.
- **Class Mismatch**: In an unrelated dataset, the class names or IDs may be different to what your model produces, you'll need to remap them, which is [shown in this guide](#running-a-model).
- **Data Contamination**: The `test` set may not be split correctly, with images from the test set also present in `training` or `validation` set and used during training. In this case, the results will be overly optimistic. This also applies when **very similar** images are used for training and testing - e.g. those taken in the same environment, same lighting conditions, similar angle, etc.
- **Missing Test Set**: Some datasets do not come with a test set. In this case, you should collect and [label](https://roboflow.com/annotate) your own data. Alternatively, a validation set could be used, but the results could be overly optimistic. Make sure to test in the real world as soon as possible.
## Running a Model
At this stage, you should have:
- A dataset of labeled images to evaluate the model.
- A model prepared for benchmarking.
With these ready, we can now run the model and obtain predictions.
We'll use `supervision` to create a dataset iterator, and then run the model on each image.
=== "Inference"
```python
import supervision as sv
test_set = sv.DetectionDataset.from_yolo(
images_directory_path=f"{dataset.location}/test/images",
annotations_directory_path=f"{dataset.location}/test/labels",
data_yaml_path=f"{dataset.location}/data.yaml"
)
image_paths = []
predictions_list = []
targets_list = []
for image_path, image, label in test_set:
result = model.infer(image)[0]
predictions = sv.Detections.from_inference(result)
image_paths.append(image_path)
predictions_list.append(predictions)
targets_list.append(label)
```
=== "Ultralytics"
```python
import supervision as sv
test_set = sv.DetectionDataset.from_yolo(
images_directory_path=f"{dataset.location}/test/images",
annotations_directory_path=f"{dataset.location}/test/labels",
data_yaml_path=f"{dataset.location}/data.yaml"
)
image_paths = []
predictions_list = []
targets_list = []
for image_path, image, label in test_set:
result = model(image)[0]
predictions = sv.Detections.from_ultralytics(result)
image_paths.append(image_path)
predictions_list.append(predictions)
targets_list.append(label)
```
## Remapping classes
Did you notice an issue in the above logic?
Since we're using an unrelated dataset, the class names and IDs may be different from what the model was trained on.
We need to remap them to match the dataset classes. Here's how to do it:
```python
def remap_classes(
detections: sv.Detections,
class_ids_from_to: dict[int, int],
class_names_from_to: dict[str, str]
) -> None:
new_class_ids = [
class_ids_from_to.get(class_id, class_id) for class_id in detections.class_id]
detections.class_id = np.array(new_class_ids)
new_class_names = [
class_names_from_to.get(name, name) for name in detections["class_name"]]
predictions["class_name"] = np.array(new_class_names)
```
Let's also remove the predictions that are not in the dataset classes.
=== "Inference"
Dataset class names and IDs can be found in the `data.yaml` file, or by printing `dataset.classes`.
```python
import supervision as sv
test_set = sv.DetectionDataset.from_yolo(
images_directory_path=f"{dataset.location}/test/images",
annotations_directory_path=f"{dataset.location}/test/labels",
data_yaml_path=f"{dataset.location}/data.yaml"
)
image_paths = []
predictions_list = []
targets_list = []
for image_path, image, label in test_set:
result = model.infer(image)[0]
predictions = sv.Detections.from_inference(result)
remap_classes(
detections=predictions,
class_ids_from_to={16: 0},
class_names_from_to={"dog": "Corgi"}
)
predictions = predictions[
np.isin(predictions["class_name"], test_set.classes)
]
image_paths.append(image_path)
predictions_list.append(predictions)
targets_list.append(label)
```
=== "Ultralytics"
Dataset class names and IDs can be found in the `data.yaml` file, or by printing `dataset.classes`.
Each model will have a different class mapping, so make sure to check the model's documentation. In this case, the model was trained on the COCO dataset, with a class
configuration found [here](https://github.com/ultralytics/ultralytics/blob/main/ultralytics/cfg/datasets/coco8.yaml).
```python
import supervision as sv
test_set = sv.DetectionDataset.from_yolo(
images_directory_path=f"{dataset.location}/test/images",
annotations_directory_path=f"{dataset.location}/test/labels",
data_yaml_path=f"{dataset.location}/data.yaml"
)
image_paths = []
predictions_list = []
targets_list = []
for image_path, image, label in test_set:
result = model(image)[0]
predictions = sv.Detections.from_ultralytics(result)
remap_classes(
detections=predictions,
class_ids_from_to={16: 0},
class_names_from_to={"dog": "Corgi"}
)
predictions = predictions[
np.isin(predictions["class_name"], test_set.classes)
]
image_paths.append(image_path)
predictions_list.append(predictions)
targets_list.append(label)
```
## Visualizing Predictions
The first step in evaluating your models performance is to visualize its predictions.
This gives an intuitive sense of how well your model is detecting objects and where it might be failing.
```python
import supervision as sv
N = 9
GRID_SIZE = (3, 3)
target_annotator = sv.PolygonAnnotator(color=sv.Color.from_hex("#8315f9"), thickness=8)
prediction_annotator = sv.PolygonAnnotator(color=sv.Color.from_hex("#00cfc6"), thickness=6)
annotated_images = []
for image_path, predictions, targets in zip(
image_paths[:N], predictions_list[:N], targets_list[:N]
):
annotated_image = cv2.imread(image_path)
annotated_image = target_annotator.annotate(scene=annotated_image, detections=targets)
annotated_image = prediction_annotator.annotate(scene=annotated_image, detections=prediction)
annotated_images.append(annotated_image)
sv.plot_images_grid(images=annotated_images, grid_size=GRID_SIZE)
```
Here, predictions in purple are targets (ground truth), and predictions in teal are model predictions.
![Basic Model Comparison](https://media.roboflow.com/supervision/image-examples/how-to/benchmark-models/basic-model-comparison-corgi.png)
!!! tip
Use `sv.BoxAnnotator` for object detection and `sv.OrientedBoxAnnotator` for OBB.
See [annotator documentation](https://supervision.roboflow.com/latest/detection/annotators/) for even more options.
## Benchmarking Metrics
With multiple models, fine details matter. Visual inspection may not be enough. `supervision` provides a collection of metrics that help obtain precise numerical results of model performance.
### Mean Average Precision (mAP)
We'll start with [MeanAveragePrecision (mAP)](https://supervision.roboflow.com/latest/metrics/mean_average_precision/#supervision.metrics.mean_average_precision.MeanAveragePrecision), which is the most commonly used metric for object detection. It measures the average precision across all classes and IoU thresholds.
For a thorough explanation, check out our [blog](https://blog.roboflow.com/mean-average-precision/) and [Youtube video](https://www.youtube.com/watch?v=oqXDdxF_Wuw).
Here, the most popular value is `mAP 50:95`. It represents the average precision across all classes and IoU thresholds (`0.5` to `0.95`), whereas other values such as `mAP 50` or `mAP 75` only consider a single IoU threshold (`0.5` and `0.75` respectively).
Let's compute the mAP:
```python
from supervision.metrics import MeanAveragePrecision, MetricTarget
map_metric = MeanAveragePrecision(metric_target=MetricTarget.MASKS)
map_result = map_metric.update(predictions_list, targets_list).compute()
```
Try printing the result to see it at a glance:
```python
print(map_result)
```
```
MeanAveragePrecisionResult:
Metric target: MetricTarget.MASKS
Class agnostic: False
mAP @ 50:95: 0.2409
mAP @ 50: 0.3591
mAP @ 75: 0.2915
mAP scores: [0.35909 0.3468 0.34556 ...]
IoU thresh: [0.5 0.55 0.6 ...]
AP per class:
0: [0.35909 0.3468 0.34556 ...]
...
Small objects: ...
Medium objects: ...
Large objects: ...
```
You can also plot the results:
```python
map_result.plot()
```
![mAP Plot](https://media.roboflow.com/supervision/image-examples/how-to/benchmark-models/mAP-plot-corgi.png)
The metric also breaks down the results by detected object area. Small, medium and large are simply those with area less than 32², between 32² and 96², and greater than 96² pixels respectively.
### F1 Score
The [F1 Score](https://supervision.roboflow.com/latest/metrics/f1_score/) is another useful metric, especially when dealing with an imbalance between false positives and false negatives. Its the harmonic mean of **precision** (how many predictions are correct) and **recall** (how many actual instances were detected).
Here's how you can compute the F1 score:
```python
from supervision.metrics import F1Score, MetricTarget
f1_metric = F1Score(metric_target=MetricTarget.MASKS)
f1_result = f1_metric.update(predictions_list, targets_list).compute()
```
As with mAP, you can also print the result:
```python
print(f1_result)
```
```
F1ScoreResult:
Metric target: MetricTarget.MASKS
Averaging method: AveragingMethod.WEIGHTED
F1 @ 50: 0.5341
F1 @ 75: 0.4636
F1 @ thresh: [0.53406 0.5278 0.52153 ...]
IoU thresh: [0.5 0.55 0.6 ...]
F1 per class:
0: [0.53406 0.5278 0.52153 ...]
...
Small objects: ...
Medium objects: ...
Large objects: ...
```
Similarly, you can plot the results:
```python
f1_result.plot()
```
![F1 Plot](https://media.roboflow.com/supervision/image-examples/how-to/benchmark-models/f1-score-corgi.png)
As with mAP, the metric also breaks down the results by detected object area. Small, medium and large are simply those with area less than 32², between 32² and 96², and greater than 96² pixels respectively.
## Model Leaderboard
Here to compare the basic models? We've got you covered. Check out our [Model Leaderboard](https://leaderboard.roboflow.com/) to see how different models perform and to get a sense of the state-of-the-art results. It's a great place to understand what the leading models can achieve and to compare your own results.
Even better, the repository is open source! You can see how the models were benchmarked, run the evaluation yourself, and even add your own models to the leaderboard. Check it out on [GitHub](https://github.com/roboflow/model-leaderboard)!
![Model Leaderboard Example](https://media.roboflow.com/model-leaderboard/model-leaderboard-example.png)
## Conclusion
In this guide, you've learned how to set up your environment, train or use pre-trained models, visualize predictions, and evaluate model performance with metrics like [mAP](https://supervision.roboflow.com/latest/metrics/mean_average_precision/), [F1 score](https://supervision.roboflow.com/latest/metrics/f1_score/), and got to know our Model Leaderboard.
A condensed version of this guide is also available as a [Colab Notebook](https://colab.research.google.com/drive/1HoOY9pZoVwGiRMmLHtir0qT6Uj45w6Ps?usp=sharing).
For more details, be sure to check out our [documentation](https://supervision.roboflow.com/latest/) and join our community discussions. If you find any issues, please let us know on [GitHub](https://github.com/roboflow/supervision/issues).
Best of luck with your benchmarking!

View File

@ -55,7 +55,7 @@ it will be modified to include tracking, labeling, and trace annotations.
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
box_annotator = sv.BoundingBoxAnnotator()
box_annotator = sv.BoxAnnotator()
def callback(frame: np.ndarray, _: int) -> np.ndarray:
results = model(frame)[0]
@ -77,7 +77,7 @@ it will be modified to include tracking, labeling, and trace annotations.
from inference.models.utils import get_roboflow_model
model = get_roboflow_model(model_id="yolov8n-640", api_key=<ROBOFLOW API KEY>)
box_annotator = sv.BoundingBoxAnnotator()
box_annotator = sv.BoxAnnotator()
def callback(frame: np.ndarray, _: int) -> np.ndarray:
results = model.infer(frame)[0]
@ -112,7 +112,7 @@ enabling the continuous following of the object's motion path across different f
model = YOLO("yolov8n.pt")
tracker = sv.ByteTrack()
box_annotator = sv.BoundingBoxAnnotator()
box_annotator = sv.BoxAnnotator()
def callback(frame: np.ndarray, _: int) -> np.ndarray:
results = model(frame)[0]
@ -136,7 +136,7 @@ enabling the continuous following of the object's motion path across different f
model = get_roboflow_model(model_id="yolov8n-640", api_key=<ROBOFLOW API KEY>)
tracker = sv.ByteTrack()
box_annotator = sv.BoundingBoxAnnotator()
box_annotator = sv.BoxAnnotator()
def callback(frame: np.ndarray, _: int) -> np.ndarray:
results = model.infer(frame)[0]
@ -168,7 +168,7 @@ offering a clear visual representation of each object's class and unique identif
model = YOLO("yolov8n.pt")
tracker = sv.ByteTrack()
box_annotator = sv.BoundingBoxAnnotator()
box_annotator = sv.BoxAnnotator()
label_annotator = sv.LabelAnnotator()
def callback(frame: np.ndarray, _: int) -> np.ndarray:
@ -177,9 +177,9 @@ offering a clear visual representation of each object's class and unique identif
detections = tracker.update_with_detections(detections)
labels = [
f"#{tracker_id} {results.names[class_id]}"
for class_id, tracker_id
in zip(detections.class_id, detections.tracker_id)
f"#{tracker_id} {class_name}"
for class_name, tracker_id
in zip(detections.data["class_name"], detections.tracker_id)
]
annotated_frame = box_annotator.annotate(
@ -203,7 +203,7 @@ offering a clear visual representation of each object's class and unique identif
model = get_roboflow_model(model_id="yolov8n-640", api_key=<ROBOFLOW API KEY>)
tracker = sv.ByteTrack()
box_annotator = sv.BoundingBoxAnnotator()
box_annotator = sv.BoxAnnotator()
label_annotator = sv.LabelAnnotator()
def callback(frame: np.ndarray, _: int) -> np.ndarray:
@ -212,9 +212,9 @@ offering a clear visual representation of each object's class and unique identif
detections = tracker.update_with_detections(detections)
labels = [
f"#{tracker_id} {results.names[class_id]}"
for class_id, tracker_id
in zip(detections.class_id, detections.tracker_id)
f"#{tracker_id} {class_name}"
for class_name, tracker_id
in zip(detections.data["class_name"], detections.tracker_id)
]
annotated_frame = box_annotator.annotate(
@ -250,7 +250,7 @@ movement patterns and interactions between objects in the video.
model = YOLO("yolov8n.pt")
tracker = sv.ByteTrack()
box_annotator = sv.BoundingBoxAnnotator()
box_annotator = sv.BoxAnnotator()
label_annotator = sv.LabelAnnotator()
trace_annotator = sv.TraceAnnotator()
@ -260,9 +260,9 @@ movement patterns and interactions between objects in the video.
detections = tracker.update_with_detections(detections)
labels = [
f"#{tracker_id} {results.names[class_id]}"
for class_id, tracker_id
in zip(detections.class_id, detections.tracker_id)
f"#{tracker_id} {class_name}"
for class_name, tracker_id
in zip(detections.data["class_name"], detections.tracker_id)
]
annotated_frame = box_annotator.annotate(
@ -288,7 +288,7 @@ movement patterns and interactions between objects in the video.
model = get_roboflow_model(model_id="yolov8n-640", api_key=<ROBOFLOW API KEY>)
tracker = sv.ByteTrack()
box_annotator = sv.BoundingBoxAnnotator()
box_annotator = sv.BoxAnnotator()
label_annotator = sv.LabelAnnotator()
trace_annotator = sv.TraceAnnotator()
@ -298,9 +298,9 @@ movement patterns and interactions between objects in the video.
detections = tracker.update_with_detections(detections)
labels = [
f"#{tracker_id} {results.names[class_id]}"
for class_id, tracker_id
in zip(detections.class_id, detections.tracker_id)
f"#{tracker_id} {class_name}"
for class_name, tracker_id
in zip(detections.data["class_name"], detections.tracker_id)
]
annotated_frame = box_annotator.annotate(

View File

@ -10,13 +10,19 @@ hide:
<h1></h1>
</div>
<div align="center" id="logo">
<div align="center" id="logo" style="padding-top: 1rem;">
<a align="center" href="" target="_blank">
<img width="850"
src="https://media.roboflow.com/open-source/supervision/rf-supervision-banner.png?updatedAt=1678995927529">
</a>
</div>
<style>
#hello {
margin: 0;
}
</style>
## 👋 Hello
We write your reusable computer vision tools. Whether you need to load your dataset from your hard drive, draw detections on an image or video, or count how many detections are in a zone. You can count on us!
@ -31,10 +37,11 @@ We write your reusable computer vision tools. Whether you need to load your data
## 💻 Install
You can install `supervision` in a
[**Python>=3.8**](https://www.python.org/) environment.
[**Python>=3.9**](https://www.python.org/) environment.
!!! example "pip install (recommended)"
=== "pip"
!!! example "Installation"
=== "pip (recommended)"
[![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)
@ -44,6 +51,43 @@ You can install `supervision` in a
pip install supervision
```
=== "poetry"
[![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)
[![python-version](https://img.shields.io/pypi/pyversions/supervision)](https://badge.fury.io/py/supervision)
```bash
poetry add supervision
```
=== "uv"
[![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)
[![python-version](https://img.shields.io/pypi/pyversions/supervision)](https://badge.fury.io/py/supervision)
```bash
uv pip install supervision
```
For uv projects:
```bash
uv add supervision
```
=== "rye"
[![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)
[![python-version](https://img.shields.io/pypi/pyversions/supervision)](https://badge.fury.io/py/supervision)
```bash
rye add supervision
```
!!! 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)
@ -63,7 +107,7 @@ You can install `supervision` in a
=== "virtualenv"
```bash
# clone repository and navigate to root directory
git clone https://github.com/roboflow/supervision.git
git clone --depth 1 -b develop https://github.com/roboflow/supervision.git
cd supervision
# setup python environment and activate it
@ -75,18 +119,19 @@ You can install `supervision` in a
pip install -e "."
```
=== "poetry"
=== "uv"
```bash
# clone repository and navigate to root directory
git clone https://github.com/roboflow/supervision.git
git clone --depth 1 -b develop https://github.com/roboflow/supervision.git
cd supervision
# setup python environment and activate it
poetry env use python3.10
poetry shell
uv venv
source .venv/bin/activate
# installation
poetry install
uv pip install -r pyproject.toml -e . --all-extras
```
## 🚀 Quickstart

View File

@ -59,7 +59,7 @@ document.addEventListener("DOMContentLoaded", function () {
<div
class="author-container"
data-login="${authorData.login}-${elementIndex}"
style="margin-left: ${marginLeft}; z-index: ${zIndex};"
style="margin-left: ${marginLeft};"
>
<a
href="https://github.com/${authorData.login}"
@ -90,14 +90,17 @@ document.addEventListener("DOMContentLoaded", function () {
).join(',&nbsp;');
let authorsHTML = `
<div class="authors">
<div class="authors" style="margin: 0;">
${authorAvatarsHTML}
<div class="author-names">${authorNamesHTML}</div>
</div>
`;
element.innerText = `
<div style="flex-direction: column; height: 100%; display: flex;
<div style="
display: grid !important;
grid-template-rows: auto;
height: 100%;
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji; background: ${theme.background}; font-size: 14px; line-height: 1.5; color: ${theme.color}">
<div style="display: flex; align-items: center;">
<span style="font-weight: 700; font-size: 1rem; color: ${theme.linkColor};">
@ -105,13 +108,14 @@ document.addEventListener("DOMContentLoaded", function () {
</span>
</div>
${authorsHTML}
<div style="font-size: 12px; color: ${theme.color}; display: flex; flex: 0; justify-content: space-between">
<div style="font-size: 12px; color: ${theme.color}; display: grid; grid-template-columns: auto 3fr; justify-content: space-between; gap: 1rem;">
<div style="display: flex; align-items: center;">
<img src="/assets/supervision-lenny.png" aria-label="stars" width="20" height="20" role="img" />
&nbsp;
<span style="margin-left: 4px">${version}</span>
</div>
<div style="display: flex; align-items: center; flex-wrap: wrap">
<div style="display: flex; align-items: center; flex-wrap: wrap; align-content: right;
gap: 0.1rem;">
${labelHTML}
</div>
</div>

View File

@ -0,0 +1,19 @@
window.MathJax = {
tex: {
inlineMath: [["\\(", "\\)"]],
displayMath: [["\\[", "\\]"]],
processEscapes: true,
processEnvironments: true
},
options: {
ignoreHtmlClass: ".*|",
processHtmlClass: "arithmatex"
}
};
document$.subscribe(() => {
MathJax.startup.output.clearCache()
MathJax.typesetClear()
MathJax.texReset()
MathJax.typesetPromise()
})

View File

@ -0,0 +1,4 @@
!function(){var i="analytics",analytics=window[i]=window[i]||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","screen","once","off","on","addSourceMiddleware","addIntegrationMiddleware","setAnonymousId","addDestinationMiddleware","register"];analytics.factory=function(e){return function(){if(window[i].initialized)return window[i][e].apply(window[i],arguments);var n=Array.prototype.slice.call(arguments);if(["track","screen","alias","group","page","identify"].indexOf(e)>-1){var c=document.querySelector("link[rel='canonical']");n.push({__t:"bpc",c:c&&c.getAttribute("href")||void 0,p:location.pathname,u:location.href,s:location.search,t:document.title,r:document.referrer})}n.unshift(e);analytics.push(n);return analytics}};for(var n=0;n<analytics.methods.length;n++){var key=analytics.methods[n];analytics[key]=analytics.factory(key)}analytics.load=function(key,n){var t=document.createElement("script");t.type="text/javascript";t.async=!0;t.setAttribute("data-global-segment-analytics-key",i);t.src="https://cdn.segment.com/analytics.js/v1/" + key + "/analytics.min.js";var r=document.getElementsByTagName("script")[0];r.parentNode.insertBefore(t,r);analytics._loadOptions=n};analytics._writeKey="rMvrPeZBJYyOPJSGCNhMlnTJb8VhFiWU";;analytics.SNIPPET_VERSION="5.2.0";
analytics.load("eohFog7VZiAhGJGEr5Sh7BM1mFKmUvDC");
document$.subscribe(analytics.page);
}}();

View File

@ -1,6 +1,5 @@
---
comments: true
status: new
---
# Annotators

View File

@ -1,6 +1,5 @@
---
comments: true
status: new
---
# Common Values

View File

@ -1,6 +1,5 @@
---
comments: true
status: new
---
# F1 Score

View File

@ -16,3 +16,9 @@ status: new
</div>
:::supervision.metrics.mean_average_precision.MeanAveragePrecisionResult
<div class="md-typeset">
<h2><a href="#supervision.dataset.formats.coco.get_coco_class_index_mapping">get_coco_class_index_mapping</a></h2>
</div>
:::supervision.dataset.formats.coco.get_coco_class_index_mapping

View File

@ -1,6 +1,5 @@
---
comments: true
status: new
---
# Mean Average Recall

View File

@ -1,6 +1,5 @@
---
comments: true
status: new
---
# Precision

View File

@ -1,6 +1,5 @@
---
comments: true
status: new
---
# Recall

View File

@ -93,7 +93,7 @@
},
"outputs": [],
"source": [
"!pip install -q inference-gpu \"supervision[assets]\""
"!pip install -q inference-gpu \"supervision\""
]
},
{
@ -156,7 +156,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
@ -207,7 +207,6 @@
],
"source": [
"import supervision as sv\n",
"from supervision.assets import download_assets, VideoAssets\n",
"from inference.models.utils import get_roboflow_model\n",
"\n",
"\n",

File diff suppressed because one or more lines are too long

View File

@ -17,7 +17,7 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": null,
"metadata": {
"vscode": {
"languageId": "shellscript"
@ -25,7 +25,7 @@
},
"outputs": [],
"source": [
"!pip install -q \"supervision[assets]\""
"pip install -q \"supervision\""
]
},
{

View File

@ -101,7 +101,7 @@
},
"outputs": [],
"source": [
"!pip install -q torch diffusers accelerate inference-gpu[yolo-world] dill git+https://github.com/openai/CLIP.git supervision==0.19.0rc5"
"!pip install -q torch diffusers accelerate inference-gpu[yolo-world] dill git+https://github.com/openai/CLIP.git supervision"
]
},
{

View File

@ -92,7 +92,7 @@
},
"outputs": [],
"source": [
"!pip install -q inference-gpu \"supervision[assets]\""
"!pip install -q inference-gpu \"supervision\""
]
},
{

View File

@ -72,7 +72,7 @@
},
"outputs": [],
"source": [
"!pip install roboflow supervision==0.19.0 -q"
"!pip install roboflow supervision -q"
]
},
{

View File

@ -495,7 +495,7 @@
"source": [
"from ultralytics import YOLO\n",
"\n",
"model = YOLO(\"yolov8x.pt\")\n",
"model = YOLO(\"yolo11x.pt\")\n",
"result = model(image, verbose=False)[0]\n",
"detections = sv.Detections.from_ultralytics(result)"
]
@ -594,7 +594,7 @@
},
{
"cell_type": "code",
"execution_count": 33,
"execution_count": null,
"metadata": {
"id": "yM6dmicTRGl6"
},
@ -602,7 +602,7 @@
"source": [
"from ultralytics import YOLO\n",
"\n",
"model = YOLO(\"yolov8x-seg.pt\")\n",
"model = YOLO(\"yolo11x-seg.pt\")\n",
"result = model(image, verbose=False)[0]\n",
"detections = sv.Detections.from_ultralytics(result)"
]
@ -926,7 +926,7 @@
},
"outputs": [],
"source": [
"!pip install -q supervision[assets]"
"!pip install -q supervision"
]
},
{

File diff suppressed because one or more lines are too long

View File

@ -32,7 +32,7 @@
},
"outputs": [],
"source": [
"!pip install -q inference requests tqdm supervision==0.21.0"
"!pip install -q inference requests tqdm supervision"
]
},
{
@ -695,12 +695,12 @@
"source": [
"###\u00a0Annotate Image with Detections\n",
"\n",
"Finally, we can annotate the image with the predictions. Since we are working with an object detection model, we will use the [`sv.BoundingBoxAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.BoundingBoxAnnotator) and [`sv.LabelAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.LabelAnnotator) classes."
"Finally, we can annotate the image with the predictions. Since we are working with an object detection model, we will use the [`sv.BoxAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.BoxAnnotator) and [`sv.LabelAnnotator`](https://supervision.roboflow.com/latest/detection/annotators/#supervision.annotators.core.LabelAnnotator) classes."
]
},
{
"cell_type": "code",
"execution_count": 49,
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
@ -722,7 +722,7 @@
}
],
"source": [
"bounding_box_annotator = sv.BoundingBoxAnnotator()\n",
"bounding_box_annotator = sv.BoxAnnotator()\n",
"label_annotator = sv.LabelAnnotator()\n",
"\n",
"annotated_frame = frame.copy()\n",

View File

@ -15,7 +15,7 @@
/* Large screens (1024px and up) */
@media (min-width: 1024px) {
.custom-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}

View File

@ -1,7 +1,10 @@
:root {
:root, body {
/* Default to light theme */
--md-primary-fg-color: #8315F9;
--md-accent-fg-color: #00FFCE;
--md-code-hl-color: #8315F9 !important;
--md-accent-fg-color: #8315F9 !important;
--md-code-hl-color--light: #e8d2ff89 !important;
--md-footer-fg-color--light: rgb(111, 108, 121) !important;
}
body.light {
@ -9,6 +12,202 @@ body.light {
--md-text-color: #000000;
--md-h2-color: #000000;
}
.md-grid {
max-width: 85%;
margin: auto;
}
.sublist {
display: none;
list-style: none;
padding-left: 0;
background: white;
position: absolute;
border-radius: 8px;
margin-top: 0.25rem;
}
.sublist {
transition: opacity 0.5s ease-in-out;
display: none;
position: absolute; /* Ensure it overlaps and doesn't break flow */
background: white; /* So it's visible */
z-index: 1000;
}
.sublist li {
padding: 0.5rem;
}
#products-list *:hover .products-sublist {
display: block;
}
#resources-list *, #products-list * {
cursor: pointer;
}
.products-sublist, .resources-sublist {
padding: 0.25rem;
}
.products-sublist li:hover, .resources-sublist li:hover, .md-nav__link[href]:hover {
background: rgb(242, 241, 247) !important;
border-radius: 6px;
color: initial !important;
}
.md-search {
flex-grow: 2;
}
.portfolio-section .md-grid {
max-width: 100%;
}
.md-header__inner {
align-items: center;
display: grid;
grid-template-columns: 0.1fr 1.4fr 2fr 2fr;
padding-right: 1rem;
}
.md-search__inner {
max-width: 600px;
width: 100%;
min-width: 100%;
}
.md-search__input {
background: white;
border: 1px solid rgb(229, 231, 235);
border-radius: 8px;
color: rgb(111, 108, 121);
}
.md-search__form *, .md-search__icon, .md-search__input {
color: rgb(111, 108, 121);
}
.md-search__input::placeholder {
color: rgb(156, 163, 175);
}
.md-search__form {
background: none !important;
}
.md-footer, .md-footer-meta {
background-color: transparent;
color: rgb(111, 108, 121);
}
.md-typeset .tabbed-set > input:first-child:checked ~ .tabbed-labels > :first-child, .md-typeset .tabbed-set > input:nth-child(10):checked ~ .tabbed-labels > :nth-child(10), .md-typeset .tabbed-set > input:nth-child(11):checked ~ .tabbed-labels > :nth-child(11), .md-typeset .tabbed-set > input:nth-child(12):checked ~ .tabbed-labels > :nth-child(12), .md-typeset .tabbed-set > input:nth-child(13):checked ~ .tabbed-labels > :nth-child(13), .md-typeset .tabbed-set > input:nth-child(14):checked ~ .tabbed-labels > :nth-child(14), .md-typeset .tabbed-set > input:nth-child(15):checked ~ .tabbed-labels > :nth-child(15), .md-typeset .tabbed-set > input:nth-child(16):checked ~ .tabbed-labels > :nth-child(16), .md-typeset .tabbed-set > input:nth-child(17):checked ~ .tabbed-labels > :nth-child(17), .md-typeset .tabbed-set > input:nth-child(18):checked ~ .tabbed-labels > :nth-child(18), .md-typeset .tabbed-set > input:nth-child(19):checked ~ .tabbed-labels > :nth-child(19), .md-typeset .tabbed-set > input:nth-child(2):checked ~ .tabbed-labels > :nth-child(2), .md-typeset .tabbed-set > input:nth-child(20):checked ~ .tabbed-labels > :nth-child(20), .md-typeset .tabbed-set > input:nth-child(3):checked ~ .tabbed-labels > :nth-child(3), .md-typeset .tabbed-set > input:nth-child(4):checked ~ .tabbed-labels > :nth-child(4), .md-typeset .tabbed-set > input:nth-child(5):checked ~ .tabbed-labels > :nth-child(5), .md-typeset .tabbed-set > input:nth-child(6):checked ~ .tabbed-labels > :nth-child(6), .md-typeset .tabbed-set > input:nth-child(7):checked ~ .tabbed-labels > :nth-child(7), .md-typeset .tabbed-set > input:nth-child(8):checked ~ .tabbed-labels > :nth-child(8), .md-typeset .tabbed-set > input:nth-child(9):checked ~ .tabbed-labels > :nth-child(9) {
color: #8315F9;
border-bottom: 1px solid #8315F9;
}
.md-footer *, html .md-footer-meta.md-typeset a {
color: rgb(111, 108, 121);
}
.repo-card {
height: 100%;
}
.header-btn {
text-align: center;
}
.header-btn, .sublist {
box-shadow: rgb(255, 255, 255) 0px 0px 0px 0px, rgb(217, 215, 226) 0px 0px 0px 1px, rgb(217, 215, 226) 0px 1px 2px 0px;
}
.header-btn:hover {
box-shadow: rgb(255, 255, 255) 0px 0px 0px 0px, rgb(217, 215, 226) 0px 0px 0px 1px, rgb(217, 215, 226) 0px 1.0001px 2.00013px -0.0000327245px, rgba(0, 0, 0, 0) 0px 0.000065449px 0.000130898px -0.000065449px;
}
.md-typeset .headerlink:hover, .md-typeset .headerlink:target {
color: #8315F9;
}
.md-typeset h1, .md-header__title {
color: black;
font-weight: 800;
}
.md-typeset h1 {
font-weight: normal;
margin-bottom: 1rem;
}
body {
background: linear-gradient(to left bottom, rgb(243, 238, 255), rgb(255, 255, 255) 60%) no-repeat;
}
/* .md-nav__link:has([tabindex=""]) {
text-transform: uppercase;
} */
.header-list {
display: flex;
align-items: center;
gap: 1rem;
list-style: none;
font-size: 0.75rem;
justify-content: flex-end;
}
.md-nav__list label, .md-nav--secondary label {
/* text-transform: uppercase; */
color: rgb(29, 29, 31) !important;
font-size: 0.7rem;
margin-bottom: 0;
}
.md-nav--secondary label {
margin-left: 0.5rem;
}
.md-nav__link {
padding: 0.25rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.md-nav__link--active {
background: rgb(243, 238, 255);
border-radius: 6px;
padding-top: 0.25rem;
}
.md-tabs__item--active {
color: var(--md-primary-fg-color);
border-bottom: 2px solid var(--md-primary-fg-color);
}
.md-nav--secondary .md-nav__title {
background: transparent;
box-shadow: none;
}
.md-header, .md-tabs {
color: rgb(111, 108, 121);
background-color: transparent;
}
.md-header--shadow {
background: linear-gradient(to left bottom, rgb(243, 238, 255), rgb(255, 255, 255) 60%);
box-shadow: none;
border-bottom: 1px solid rgb(229, 231, 235);
}
#item-logo {
display: none;
}
.md-main__inner, .md-header__inner, .md-grid {
max-width: 100%;
}
@media (max-width: 1200px) {
.md-header__inner {
display: flex;
}
.header-list {
display: none;
}
#item-logo {
display: block;
}
}
.md-content {
max-width: 40rem;
margin: auto;
}
/* // if no md-sidebar--primary, make .md-content full width */
.md-main__inner:has(.md-sidebar--primary[hidden]) .md-content {
max-width: 100%;
}
.md-sidebar--primary {
flex: 0 20%;
}
.md-tabs {
border-bottom: 1px solid rgb(229, 231, 235);
}
.md-main__inner {
padding-top: 1rem;
margin-top: 0;
}
body.dark {
/* Dark theme */
@ -29,3 +228,43 @@ body[data-md-url$="/cookbooks/"] .md-content {
margin-left: 0;
width: 100%;
}
.md-main, nav .md-grid, .md-header__inner {
max-width: 1600px;
width: 100%;
margin: auto;
}
.md-search__scrollwrap {
width: 100% !important;
}
.md-nav--secondary .md-nav__title {
position: initial !important;
}
.md-header__title .md-ellipsis {
overflow: initial !important;
text-overflow: initial !important;
}
.md-search {
flex-grow: 0;
}
/* Table style */
th, td {
border: 1px solid var(--md-typeset-table-color);
}
.md-typeset__table {
line-height: 1.5;
}
.md-typeset__table table:not([class]) {
font-size: 0.6rem;
border-collapse: collapse;
}
.md-typeset__table table:not([class]) td,
.md-typeset__table table:not([class]) th {
padding: 10px;
}

View File

@ -12,15 +12,15 @@
</div>
<div class="custom-grid">
<a href="/develop/notebooks/quickstart"> <p class="card repo-card" data-name="Supervision Quickstart" data-labels="ANNOTATOR,DETECTION,SAM"
data-version="v0.18.0" data-author="SkalskiP,onuralpszr"></p>
data-version="v0.26.0" data-author="SkalskiP,onuralpszr"></p>
</a>
<a href="/develop/notebooks/count-objects-crossing-the-line">
<p class="card repo-card" data-name="Count Objects Crossing the Line"
data-labels="ANNOTATORS,LINE ZONE,TRACKING" data-version="v0.18.0" data-author="SkalskiP"></p>
data-labels="ANNOTATORS,LINE ZONE,TRACKING" data-version="v0.26.0" data-author="SkalskiP"></p>
</a>
<a href="/develop/notebooks/zero-shot-object-detection-with-yolo-world">
<p class="card repo-card" data-name="Zero-Shot Object Detection with YOLO-World"
data-labels="ANNOTATORS,DETECTION,INFERENCE" data-version="v0.19.0" data-author="SkalskiP"></p>
data-labels="ANNOTATORS,DETECTION,INFERENCE" data-version="v0.26.0" data-author="SkalskiP"></p>
</a>
<a href="/develop/notebooks/download-supervision-assets">
<p class="card repo-card" data-name="Downloading Supervision Assets" data-labels="ASSETS" data-version="v0.18.0"
@ -28,7 +28,7 @@
</a>
<a href="/develop/notebooks/annotate-video-with-detections">
<p class="card repo-card" data-name="Annotate Video with Detections" data-labels="INFERENCE,YOLOV8"
data-version="v0.18.0" data-author="nickherrig"></p>
data-version="v0.26.0" data-author="nickherrig"></p>
</a>
<a href="/develop/notebooks/object-tracking">
<p class="card repo-card" data-name="Object Tracking" data-labels="TRACKING, ANNOTATOR" data-version="v0.18.0"
@ -36,23 +36,23 @@
</a>
<a href="/develop/notebooks/occupancy_analytics">
<p class="card repo-card" data-name="Analyzing Zone Occupancy" data-labels="ANNOTATOR,DETECTION,ZONES"
data-version="v0.19.0" data-author="stellasphere"></p>
data-version="v0.26.0" data-author="stellasphere"></p>
</a>
<a href="/develop/notebooks/evaluating-alignment-of-text-to-image-diffusion-models">
<p class="card repo-card" data-name="Evaluating Alignment of Text-to-image Diffusion Models"
data-labels="ANNOTATORS,YOLO WORLD" data-version="v0.19.0rc5" data-author="iamhatesz"></p>
data-labels="ANNOTATORS,YOLO WORLD" data-version="v0.26.0" data-author="iamhatesz"></p>
</a>
<a href="/develop/notebooks/serialise-detections-to-csv">
<p class="card repo-card" data-name="Serialise Detections to a CSV File"
data-labels="DETECTIONS,CSV SINK,INFERENCE" data-version="v0.21.0" data-author="onuralpszr"></p>
data-labels="DETECTIONS,CSV SINK,INFERENCE" data-version="v0.26.0" data-author="onuralpszr"></p>
</a>
<a href="/develop/notebooks/serialise-detections-to-json">
<p class="card repo-card" data-name="Serialise Detections to a JSON File"
data-labels="DETECTIONS,JSON SINK,INFERENCE" data-version="v0.21.0" data-author="onuralpszr"></p>
data-labels="DETECTIONS,JSON SINK,INFERENCE" data-version="v0.26.0" data-author="onuralpszr"></p>
</a>
<a href="/develop/notebooks/small-object-detection-with-sahi">
<p class="card repo-card" data-name="Small Object Detection with SAHI"
data-labels="DETECTIONS,SAHI,SMALL,OBJECT,INFERENCE" data-version="v0.23.0" data-author="ediardo"></p>
data-labels="DETECTIONS,SAHI,SMALL,OBJECT,INFERENCE" data-version="v0.26.0" data-author="ediardo"></p>
</a>
</div>
</div>

181
docs/theme/partials/header.html vendored Normal file
View File

@ -0,0 +1,181 @@
{% set class = "md-header" %}
{% if "navigation.tabs.sticky" in features %}
{% set class = class ~ " md-header--shadow md-header--lifted" %}
{% elif "navigation.tabs" not in features %}
{% set class = class ~ " md-header--shadow" %}
{% endif %}
<!-- Header -->
<header class="{{ class }}" data-md-component="header">
<nav
class="md-header__inner md-grid"
aria-label="{{ lang.t('header') }}"
>
<!-- Link to home -->
<a
href="{{ config.extra.homepage | d(nav.homepage.url, true) | url }}"
title="{{ config.site_name | e }}"
class="md-header__button md-logo"
aria-label="{{ config.site_name }}"
data-md-component="logo"
>
{% include "partials/logo.html" %}
</a>
<!-- Button to open drawer -->
<label class="md-header__button md-icon" for="__drawer">
{% set icon = config.theme.icon.menu or "material/menu" %}
{% include ".icons/" ~ icon ~ ".svg" %}
</label>
<!-- Header title -->
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
{{ config.site_name }} Docs
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis" style="display: flex; align-items: center; gap: 0.5rem;">
<a
href="{{ config.extra.homepage | d(nav.homepage.url, true) | url }}"
title="{{ config.site_name | e }}"
class="md-header__button md-logo"
aria-label="{{ config.site_name }}"
data-md-component="logo"
id="item-logo"
>
{% include "partials/logo.html" %}
</a>
{% if page.meta and page.meta.title %}
{{ page.meta.title }}
{% else %}
{{ page.title }}
{% endif %}
</span>
</div>
</div>
</div>
<!-- Button to open search modal -->
{% if "material/search" in config.plugins %}
{% set search = config.plugins["material/search"] | attr("config") %}
<!-- Check if search is actually enabled - see https://t.ly/DT_0V -->
{% if search.enabled %}
<label class="md-header__button md-icon" for="__search">
{% set icon = config.theme.icon.search or "material/magnify" %}
{% include ".icons/" ~ icon ~ ".svg" %}
</label>
<!-- Search interface -->
{% include "partials/search.html" %}
{% endif %}
{% endif %}
<ul class="header-list">
<li style="align-items: center;" id="resources-list">
<label for="dropdown-resources"><span>Resources <img src="https://ka-p.fontawesome.com/releases/v6.6.0/svgs/regular/chevron-down.svg?v=2&token=a463935e93" style="height: 0.5rem;" /></span></label>
<input type="radio" name="dropdown" id="dropdown-resources" style="display: none;" />
<ul class="resources-sublist sublist">
<li><a href="https://blog.roboflow.com">Blog</a></li>
<li><a href="https://discuss.roboflow.com">Community Forum</a></li>
<li><a href="https://roboflow.com/sales">Contact Sales</a></li>
<li><a href="https://universe.roboflow.com">Universe</a></li>
</ul>
</li>
<li style="align-items: center;" id="products-list">
<label for="dropdown-products"><span>Docs <img src="https://ka-p.fontawesome.com/releases/v6.6.0/svgs/regular/chevron-down.svg?v=2&token=a463935e93" style="height: 0.5rem;" /></span></label>
<input type="radio" name="dropdown" id="dropdown-products" style="display: none;" />
<ul class="products-sublist sublist">
<li><a href="https://inference.roboflow.com">Inference</a></li>
<li><a href="https://supervision.roboflow.com">Supervision</a></li>
<li><a href="https://trackers.roboflow.com">Trackers</a></li>
<li><a href="https://maestro.roboflow.com">Maestro</a></li>
<li><a href="https://docs.roboflow.com">Roboflow</a></li>
</ul>
</li>
<script>
document.addEventListener('click', function(event) {
const resourcesList = document.getElementById('resources-list');
const productsList = document.getElementById('products-list');
const dropdownResources = document.getElementById('dropdown-resources');
const dropdownProducts = document.getElementById('dropdown-products');
if (!resourcesList.contains(event.target)) {
dropdownResources.checked = false;
}
if (!productsList.contains(event.target)) {
dropdownProducts.checked = false;
}
});
// on mouse over
document.getElementById('resources-list').addEventListener('mouseover', function() {
document.getElementById('dropdown-resources').checked = true;
});
document.getElementById('products-list').addEventListener('mouseover', function() {
document.getElementById('dropdown-products').checked = true;
});
// on mouse out
document.getElementById('resources-list').addEventListener('mouseout', function() {
// if not hovering over the sublist or the label, uncheck the dropdown
// wait 1 sec
setTimeout(function() {
if (!document.querySelector('.resources-sublist:hover') && !document.querySelector('#resources-list:hover')) {
document.getElementById('dropdown-resources').checked = false;
}
}, 350);
});
// if mouseout of sublist, uncheck immediately
document.querySelector('.resources-sublist').addEventListener('mouseout', function() {
setTimeout(function() {
if (!document.querySelector('.resources-sublist:hover') && !document.querySelector('#resources-list:hover')) {
document.getElementById('dropdown-resources').checked = false;
}
}, 450);
});
document.getElementById('products-list').addEventListener('mouseout', function() {
// if not hovering over the sublist, uncheck the dropdown
// wait 1 sec
setTimeout(function() {
if (!document.querySelector('.products-sublist:hover') && !document.querySelector('#products-list:hover')) {
document.getElementById('dropdown-products').checked = false;
}
}, 500);
});
// if mouseout of sublist, uncheck immediately
document.querySelector('.products-sublist').addEventListener('mouseout', function() {
setTimeout(function() {
if (!document.querySelector('.products-sublist:hover') && !document.querySelector('#products-list:hover')) {
document.getElementById('dropdown-products').checked = false;
}
}, 500);
});
</script>
<style>
#dropdown-resources:checked ~ .resources-sublist {
display: block;
}
#dropdown-products:checked ~ .products-sublist {
display: block;
}
/* Hide dropdown if clicking outside */
body:not(:has(#dropdown-resources:checked)) .resources-sublist,
body:not(:has(#dropdown-products:checked)) .products-sublist {
display: none;
}
</style>
<a href="https://github.com/roboflow/supervision"><li class="header-btn" style="border-radius: 5px; color: white; background: var(--md-typeset-a-color); padding-top: 0.25rem; padding-left: 0.5rem; padding-bottom: 0.25rem; padding-right: 0.5rem; border: 1px solid #8315F9;">Go to GitHub</li></a>
</ul>
</nav>
<!-- Navigation tabs (sticky) -->
{% if "navigation.tabs.sticky" in features %}
{% if "navigation.tabs" in features %}
{% include "partials/tabs.html" %}
{% endif %}
{% endif %}
</header>

View File

@ -1,6 +1,5 @@
---
comments: true
status: new
---
# ByteTrack

View File

@ -17,7 +17,7 @@ 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
git clone --depth 1 -b develop https://github.com/roboflow/supervision.git
cd supervision/examples/count_people_in_zone
```

View File

@ -1,7 +1,6 @@
import argparse
import json
import os
from typing import List, Tuple
import cv2
import numpy as np
@ -14,7 +13,7 @@ import supervision as sv
COLORS = sv.ColorPalette.DEFAULT
def load_zones_config(file_path: str) -> List[np.ndarray]:
def load_zones_config(file_path: str) -> list[np.ndarray]:
"""
Load polygon zone configurations from a JSON file.
@ -28,16 +27,14 @@ def load_zones_config(file_path: str) -> List[np.ndarray]:
Returns:
List[np.ndarray]: A list of polygons, each represented as a NumPy array.
"""
with open(file_path, "r") as file:
with open(file_path) as file:
data = json.load(file)
return [np.array(polygon, np.int32) for polygon in data["polygons"]]
def initiate_annotators(
polygons: List[np.ndarray], resolution_wh: Tuple[int, int]
) -> Tuple[
List[sv.PolygonZone], List[sv.PolygonZoneAnnotator], List[sv.BoundingBoxAnnotator]
]:
polygons: list[np.ndarray], resolution_wh: tuple[int, int]
) -> tuple[list[sv.PolygonZone], list[sv.PolygonZoneAnnotator], list[sv.BoxAnnotator]]:
line_thickness = sv.calculate_optimal_line_thickness(resolution_wh=resolution_wh)
text_scale = sv.calculate_optimal_text_scale(resolution_wh=resolution_wh)
@ -54,7 +51,7 @@ def initiate_annotators(
text_thickness=line_thickness * 2,
text_scale=text_scale * 2,
)
box_annotator = sv.BoundingBoxAnnotator(
box_annotator = sv.BoxAnnotator(
color=COLORS.by_idx(index), thickness=line_thickness
)
zones.append(zone)
@ -95,9 +92,9 @@ def detect(
def annotate(
frame: np.ndarray,
zones: List[sv.PolygonZone],
zone_annotators: List[sv.PolygonZoneAnnotator],
box_annotators: List[sv.BoundingBoxAnnotator],
zones: list[sv.PolygonZone],
zone_annotators: list[sv.PolygonZoneAnnotator],
box_annotators: list[sv.BoxAnnotator],
detections: sv.Detections,
) -> np.ndarray:
"""
@ -108,7 +105,7 @@ def annotate(
zones (List[sv.PolygonZone]): A list of polygon zones used for detection.
zone_annotators (List[sv.PolygonZoneAnnotator]): A list of annotators for
drawing zone annotations.
box_annotators (List[sv.BoundingBoxAnnotator]): A list of annotators for
box_annotators (List[sv.BoxAnnotator]): A list of annotators for
drawing box annotations.
detections (sv.Detections): Detections to be used for annotation.

View File

@ -1,5 +1,5 @@
gdown
inference==0.9.17
supervision>=0.20.0
inference
supervision
tqdm
ultralytics

View File

@ -1,6 +1,5 @@
import argparse
import json
from typing import List, Tuple
import cv2
import numpy as np
@ -12,7 +11,7 @@ import supervision as sv
COLORS = sv.ColorPalette.DEFAULT
def load_zones_config(file_path: str) -> List[np.ndarray]:
def load_zones_config(file_path: str) -> list[np.ndarray]:
"""
Load polygon zone configurations from a JSON file.
@ -26,16 +25,14 @@ def load_zones_config(file_path: str) -> List[np.ndarray]:
Returns:
List[np.ndarray]: A list of polygons, each represented as a NumPy array.
"""
with open(file_path, "r") as file:
with open(file_path) as file:
data = json.load(file)
return [np.array(polygon, np.int32) for polygon in data["polygons"]]
def initiate_annotators(
polygons: List[np.ndarray], resolution_wh: Tuple[int, int]
) -> Tuple[
List[sv.PolygonZone], List[sv.PolygonZoneAnnotator], List[sv.BoundingBoxAnnotator]
]:
polygons: list[np.ndarray], resolution_wh: tuple[int, int]
) -> tuple[list[sv.PolygonZone], list[sv.PolygonZoneAnnotator], list[sv.BoxAnnotator]]:
line_thickness = sv.calculate_optimal_line_thickness(resolution_wh=resolution_wh)
text_scale = sv.calculate_optimal_text_scale(resolution_wh=resolution_wh)
@ -52,7 +49,7 @@ def initiate_annotators(
text_thickness=line_thickness * 2,
text_scale=text_scale * 2,
)
box_annotator = sv.BoundingBoxAnnotator(
box_annotator = sv.BoxAnnotator(
color=COLORS.by_idx(index), thickness=line_thickness
)
zones.append(zone)
@ -92,9 +89,9 @@ def detect(
def annotate(
frame: np.ndarray,
zones: List[sv.PolygonZone],
zone_annotators: List[sv.PolygonZoneAnnotator],
box_annotators: List[sv.BoundingBoxAnnotator],
zones: list[sv.PolygonZone],
zone_annotators: list[sv.PolygonZoneAnnotator],
box_annotators: list[sv.BoxAnnotator],
detections: sv.Detections,
) -> np.ndarray:
"""
@ -105,7 +102,7 @@ def annotate(
zones (List[sv.PolygonZone]): A list of polygon zones used for detection.
zone_annotators (List[sv.PolygonZoneAnnotator]): A list of annotators for
drawing zone annotations.
box_annotators (List[sv.BoundingBoxAnnotator]): A list of annotators for
box_annotators (List[sv.BoxAnnotator]): A list of annotators for
drawing box annotations.
detections (sv.Detections): Detections to be used for annotation.
@ -137,7 +134,7 @@ if __name__ == "__main__":
)
parser.add_argument(
"--source_weights_path",
default="yolov8x.pt",
default="yolo11x.pt",
help="Path to the source weights file",
type=str,
)

View File

@ -11,7 +11,7 @@ supervision package for multiple tasks such as drawing heatmap annotations, trac
- clone repository and navigate to example directory
```bash
git clone https://github.com/roboflow/supervision.git
git clone --depth 1 -b develop https://github.com/roboflow/supervision.git
cd supervision/examples/heatmap_and_track
```

View File

@ -1,2 +1,2 @@
supervision[assets]==0.19.0
supervision
ultralytics

View File

@ -22,7 +22,7 @@ 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
git clone --depth 1 -b develop https://github.com/roboflow/supervision.git
cd supervision/examples/speed_estimation
```

View File

@ -1,6 +1,6 @@
supervision>=0.20.0
tqdm==4.66.3
supervision
tqdm
requests
ultralytics==8.0.237
ultralytics
super-gradients==3.5.0
inference==0.9.17
inference

View File

@ -70,7 +70,7 @@ if __name__ == "__main__":
args = parse_arguments()
video_info = sv.VideoInfo.from_video_path(video_path=args.source_video_path)
model = YOLO("yolov8x.pt")
model = YOLO("yolo11x.pt")
byte_track = sv.ByteTrack(
frame_rate=video_info.fps, track_activation_threshold=args.confidence_threshold

View File

@ -16,7 +16,7 @@ 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
git clone --depth 1 -b develop https://github.com/roboflow/supervision.git
cd supervision/examples/time_in_zone
```

View File

@ -1,5 +1,4 @@
import argparse
from typing import List
import cv2
import numpy as np
@ -22,7 +21,7 @@ def main(
model_id: str,
confidence: float,
iou: float,
classes: List[int],
classes: list[int],
) -> None:
model = get_model(model_id=model_id)
tracker = sv.ByteTrack(minimum_matching_threshold=0.5)

View File

@ -1,5 +1,4 @@
import argparse
from typing import List
import cv2
import numpy as np
@ -22,7 +21,7 @@ def main(
model_id: str,
confidence: float,
iou: float,
classes: List[int],
classes: list[int],
) -> None:
model = get_model(model_id=model_id)
tracker = sv.ByteTrack(minimum_matching_threshold=0.5)

View File

@ -1,5 +1,4 @@
import argparse
from typing import List
import cv2
import numpy as np
@ -18,7 +17,7 @@ LABEL_ANNOTATOR = sv.LabelAnnotator(
class CustomSink:
def __init__(self, zone_configuration_path: str, classes: List[int]):
def __init__(self, zone_configuration_path: str, classes: list[int]):
self.classes = classes
self.tracker = sv.ByteTrack(minimum_matching_threshold=0.5)
self.fps_monitor = sv.FPSMonitor()
@ -83,7 +82,7 @@ def main(
model_id: str,
confidence: float,
iou: float,
classes: List[int],
classes: list[int],
) -> None:
sink = CustomSink(zone_configuration_path=zone_configuration_path, classes=classes)

View File

@ -1,5 +1,4 @@
opencv-python
supervision>=0.20.0
supervision
ultralytics
inference==0.9.17
inference
pytube

View File

@ -1,11 +1,12 @@
from __future__ import annotations
import argparse
import os
from typing import Optional
from pytube import YouTube
def main(url: str, output_path: Optional[str], file_name: Optional[str]) -> None:
def main(url: str, output_path: str | None, file_name: str | None) -> None:
yt = YouTube(url)
stream = yt.streams.get_highest_resolution()

View File

@ -1,7 +1,9 @@
from __future__ import annotations
import argparse
import json
import os
from typing import Any, Optional, Tuple
from typing import Any
import cv2
import numpy as np
@ -19,10 +21,10 @@ COLORS = sv.ColorPalette.DEFAULT
WINDOW_NAME = "Draw Zones"
POLYGONS = [[]]
current_mouse_position: Optional[Tuple[int, int]] = None
current_mouse_position: tuple[int, int] | None = None
def resolve_source(source_path: str) -> Optional[np.ndarray]:
def resolve_source(source_path: str) -> np.ndarray | None:
if not os.path.exists(source_path):
return None

View File

@ -1,5 +1,4 @@
import argparse
from typing import List
import cv2
import numpy as np
@ -23,7 +22,7 @@ def main(
device: str,
confidence: float,
iou: float,
classes: List[int],
classes: list[int],
) -> None:
model = YOLO(weights)
tracker = sv.ByteTrack(minimum_matching_threshold=0.5)

View File

@ -1,5 +1,4 @@
import argparse
from typing import List
import cv2
import numpy as np
@ -23,7 +22,7 @@ def main(
device: str,
confidence: float,
iou: float,
classes: List[int],
classes: list[int],
) -> None:
model = YOLO(weights)
tracker = sv.ByteTrack(minimum_matching_threshold=0.5)

View File

@ -1,5 +1,4 @@
import argparse
from typing import List
import cv2
import numpy as np
@ -19,7 +18,7 @@ LABEL_ANNOTATOR = sv.LabelAnnotator(
class CustomSink:
def __init__(self, zone_configuration_path: str, classes: List[int]):
def __init__(self, zone_configuration_path: str, classes: list[int]):
self.classes = classes
self.tracker = sv.ByteTrack(minimum_matching_threshold=0.8)
self.fps_monitor = sv.FPSMonitor()
@ -84,7 +83,7 @@ def main(
device: str,
confidence: float,
iou: float,
classes: List[int],
classes: list[int],
) -> None:
model = YOLO(weights)

View File

@ -1,11 +1,11 @@
import json
from typing import Generator, List
from collections.abc import Generator
import cv2
import numpy as np
def load_zones_config(file_path: str) -> List[np.ndarray]:
def load_zones_config(file_path: str) -> list[np.ndarray]:
"""
Load polygon zone configurations from a JSON file.
@ -19,12 +19,12 @@ def load_zones_config(file_path: str) -> List[np.ndarray]:
Returns:
List[np.ndarray]: A list of polygons, each represented as a NumPy array.
"""
with open(file_path, "r") as file:
with open(file_path) as file:
data = json.load(file)
return [np.array(polygon, np.int32) for polygon in data]
def find_in_list(array: np.ndarray, search_list: List[int]) -> np.ndarray:
def find_in_list(array: np.ndarray, search_list: list[int]) -> np.ndarray:
"""Determines if elements of a numpy array are present in a list.
Args:

View File

@ -1,5 +1,4 @@
from datetime import datetime
from typing import Dict
import numpy as np
@ -26,7 +25,7 @@ class FPSBasedTimer:
"""
self.fps = fps
self.frame_id = 0
self.tracker_id2frame_id: Dict[int, int] = {}
self.tracker_id2frame_id: dict[int, int] = {}
def tick(self, detections: sv.Detections) -> np.ndarray:
"""Processes the current frame, updating time durations for each tracker.
@ -63,7 +62,7 @@ class ClockBasedTimer:
def __init__(self) -> None:
"""Initializes the ClockBasedTimer."""
self.tracker_id2start_time: Dict[int, datetime] = {}
self.tracker_id2start_time: dict[int, datetime] = {}
def tick(self, detections: sv.Detections) -> np.ndarray:
"""Processes the current frame, updating time durations for each tracker.

View File

@ -10,7 +10,7 @@ detection and Supervision for tracking and annotation.
- clone repository and navigate to example directory
```bash
git clone https://github.com/roboflow/supervision.git
git clone --depth 1 -b develop https://github.com/roboflow/supervision.git
cd supervision/examples/tracking
```

View File

@ -18,7 +18,7 @@ def process_video(
model = get_roboflow_model(model_id=model_id, api_key=roboflow_api_key)
tracker = sv.ByteTrack()
box_annotator = sv.BoundingBoxAnnotator()
box_annotator = sv.BoxAnnotator()
label_annotator = sv.LabelAnnotator()
frame_generator = sv.get_video_frames_generator(source_path=source_video_path)
video_info = sv.VideoInfo.from_video_path(video_path=source_video_path)

View File

@ -1,4 +1,4 @@
inference==0.9.17
supervision==0.19.0
inference
supervision
tqdm
ultralytics

View File

@ -16,7 +16,7 @@ def process_video(
model = YOLO(source_weights_path)
tracker = sv.ByteTrack()
box_annotator = sv.BoundingBoxAnnotator()
box_annotator = sv.BoxAnnotator()
label_annotator = sv.LabelAnnotator()
frame_generator = sv.get_video_frames_generator(source_path=source_video_path)
video_info = sv.VideoInfo.from_video_path(video_path=source_video_path)

View File

@ -13,7 +13,7 @@ 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
git clone --depth 1 -b develop https://github.com/roboflow/supervision.git
cd supervision/examples/traffic_analysis
```

View File

@ -1,6 +1,8 @@
from __future__ import annotations
import argparse
import os
from typing import Dict, Iterable, List, Optional, Set
from collections.abc import Iterable
import cv2
import numpy as np
@ -29,14 +31,14 @@ ZONE_OUT_POLYGONS = [
class DetectionsManager:
def __init__(self) -> None:
self.tracker_id_to_zone_id: Dict[int, int] = {}
self.counts: Dict[int, Dict[int, Set[int]]] = {}
self.tracker_id_to_zone_id: dict[int, int] = {}
self.counts: dict[int, dict[int, set[int]]] = {}
def update(
self,
detections_all: sv.Detections,
detections_in_zones: List[sv.Detections],
detections_out_zones: List[sv.Detections],
detections_in_zones: list[sv.Detections],
detections_out_zones: list[sv.Detections],
) -> sv.Detections:
for zone_in_id, detections_in_zone in enumerate(detections_in_zones):
for tracker_id in detections_in_zone.tracker_id:
@ -59,9 +61,9 @@ class DetectionsManager:
def initiate_polygon_zones(
polygons: List[np.ndarray],
polygons: list[np.ndarray],
triggering_anchors: Iterable[sv.Position] = [sv.Position.CENTER],
) -> List[sv.PolygonZone]:
) -> list[sv.PolygonZone]:
return [
sv.PolygonZone(
polygon=polygon,
@ -77,7 +79,7 @@ class VideoProcessor:
roboflow_api_key: str,
model_id: str,
source_video_path: str,
target_video_path: Optional[str] = None,
target_video_path: str | None = None,
confidence_threshold: float = 0.3,
iou_threshold: float = 0.7,
) -> None:

View File

@ -1,5 +1,5 @@
gdown
inference==0.9.17
supervision>=0.20.0
inference
supervision
tqdm
ultralytics

View File

@ -1,5 +1,7 @@
from __future__ import annotations
import argparse
from typing import Dict, Iterable, List, Optional, Set
from collections.abc import Iterable
import cv2
import numpy as np
@ -27,14 +29,14 @@ ZONE_OUT_POLYGONS = [
class DetectionsManager:
def __init__(self) -> None:
self.tracker_id_to_zone_id: Dict[int, int] = {}
self.counts: Dict[int, Dict[int, Set[int]]] = {}
self.tracker_id_to_zone_id: dict[int, int] = {}
self.counts: dict[int, dict[int, set[int]]] = {}
def update(
self,
detections_all: sv.Detections,
detections_in_zones: List[sv.Detections],
detections_out_zones: List[sv.Detections],
detections_in_zones: list[sv.Detections],
detections_out_zones: list[sv.Detections],
) -> sv.Detections:
for zone_in_id, detections_in_zone in enumerate(detections_in_zones):
for tracker_id in detections_in_zone.tracker_id:
@ -57,9 +59,9 @@ class DetectionsManager:
def initiate_polygon_zones(
polygons: List[np.ndarray],
polygons: list[np.ndarray],
triggering_anchors: Iterable[sv.Position] = [sv.Position.CENTER],
) -> List[sv.PolygonZone]:
) -> list[sv.PolygonZone]:
return [
sv.PolygonZone(
polygon=polygon,
@ -74,7 +76,7 @@ class VideoProcessor:
self,
source_weights_path: str,
source_video_path: str,
target_video_path: Optional[str] = None,
target_video_path: str | None = None,
confidence_threshold: float = 0.3,
iou_threshold: float = 0.7,
) -> None:

View File

@ -3,9 +3,8 @@ site_url: https://supervision.roboflow.com/
site_author: Roboflow
site_description: A set of easy-to-use utilities that will come in handy in any computer vision project.
repo_name: roboflow/supervision
repo_url: https://github.com/roboflow/supervision
edit_uri: https://github.com/roboflow/supervision/tree/main/docs
copyright: Roboflow 2024. All rights reserved.
copyright: Roboflow 2025. All rights reserved.
extra:
social:
@ -13,12 +12,8 @@ extra:
link: https://github.com/roboflow
- icon: fontawesome/brands/python
link: https://pypi.org/project/supervision
- icon: fontawesome/brands/docker
link: https://hub.docker.com/u/roboflow
- icon: fontawesome/brands/youtube
link: https://www.youtube.com/roboflow
- icon: fontawesome/brands/linkedin
link: https://www.linkedin.com/company/roboflow-ai/
- icon: fontawesome/brands/x-twitter
link: https://twitter.com/roboflow
- icon: fontawesome/brands/discord
@ -34,7 +29,7 @@ extra_css:
- stylesheets/cookbooks-card.css
nav:
- Supervision: index.md
- Home: index.md
- Learn:
- Detect and Annotate: how_to/detect_and_annotate.md
- Save Detections: how_to/save_detections.md
@ -42,13 +37,16 @@ nav:
- Detect Small Objects: how_to/detect_small_objects.md
- Track Objects on Video: how_to/track_objects.md
- Process Datasets: how_to/process_datasets.md
- Reference - Code API:
- Benchmark a Model: how_to/benchmark_a_model.md
- Reference:
- Detection and Segmentation:
- Core: detection/core.md
- Annotators: detection/annotators.md
- Double Detection Filter: detection/double_detection_filter.md
- Utils: detection/utils.md
- Converters: detection/utils/converters.md
- IoU and NMS: detection/utils/iou_and_nms.md
- Boxes: detection/utils/boxes.md
- Masks: detection/utils/masks.md
- Polygons: detection/utils/polygons.md
- Keypoint Detection:
- Core: keypoint/core.md
- Annotators: keypoint/annotators.md
@ -82,12 +80,7 @@ nav:
- Geometry: utils/geometry.md
- Assets: assets.md
- Cookbooks: cookbooks.md
- Cheatsheet: https://roboflow.github.io/cheatsheet-supervision/
- Contribute:
- Contributing: contributing.md
- Code of Conduct: code_of_conduct.md
- License: license.md
- Release Notes:
- Changelog:
- Changelog: changelog.md
- Deprecated: deprecated.md
@ -105,6 +98,7 @@ theme:
- content.tooltips
- content.code.annotate
- navigation.tabs
- navigation.tabs.sticky
palette:
# Palette for light mode
@ -122,11 +116,8 @@ theme:
name: Switch to light mode
font:
text: Roboto
code: Roboto Mono
features:
- content.code.copy
- content.code.annotate
text: Inter
code: IBM Plex Mono
plugins:
- search
@ -148,9 +139,12 @@ plugins:
group_by_category: true
docstring_style: google
show_symbol_type_heading: true
show_root_heading: True
show_symbol_type_toc: true
show_category_heading: true
domains: [std, py]
inventories:
- url: https://docs.python-requests.org/en/master/objects.inv
domains: [std, py]
- git-committers:
repository: roboflow/supervision
branch: develop
@ -175,12 +169,18 @@ markdown_extensions:
check_paths: true
- pymdownx.highlight:
anchor_linenums: true
line_spans: __span
pygments_lang_class: true
- pymdownx.arithmatex:
generic: true
extra_javascript:
- "https://widget.kapa.ai/kapa-widget.bundle.js"
- "javascripts/init_kapa_widget.js"
- "javascripts/cookbooks-card.js"
- "javascripts/segment.js"
- "javascripts/mathjax.js"
- "https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.8/purify.min.js"
- "https://unpkg.com/mathjax@3/es5/tex-mml-chtml.js"
# Messages shown during document build
# Reference: https://www.mkdocs.org/user-guide/configuration/#validation

5084
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,16 @@
[tool.poetry]
[project]
name = "supervision"
version = "0.25.1"
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>",
"Linas Kondrackis <linas@roboflow.com>",
]
license = { text = "MIT" }
version = "0.26.0"
readme = "README.md"
license = "MIT"
packages = [{ include = "supervision" }, { include = "supervision/py.typed" }]
homepage = "https://github.com/roboflow/supervision"
repository = "https://github.com/roboflow/supervision"
documentation = "https://supervision.roboflow.com/latest/"
requires-python = ">=3.9"
authors = [
{ name = "Piotr Skalski", email = "piotr.skalski92@gmail.com" }
]
maintainers = [
{ name = "Piotr Skalski", email = "piotr.skalski92@gmail.com" },
]
keywords = [
"machine-learning",
"deep-learning",
@ -22,7 +20,6 @@ keywords = [
"AI",
"Roboflow",
]
classifiers = [
'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
@ -40,118 +37,63 @@ classifiers = [
'Operating System :: POSIX :: Linux',
'Operating System :: MacOS',
]
[tool.poetry.dependencies]
python = "^3.8"
# For Python versions 3.13 and above, use numpy versions 2.1.0,
# required for poetry to not fail.
numpy = [
{ version = ">=1.21.2", python = "<3.13" },
{ version = ">=2.1.0", python = ">=3.13" },
dependencies = [
"numpy>=1.21.2",
"scipy>=1.10.0",
"matplotlib>=3.6.0",
"pyyaml>=5.3",
"defusedxml>=0.7.1",
"pillow>=9.4",
"requests>=2.26.0",
"tqdm>=4.62.3",
"opencv-python>=4.5.5.64"
]
scipy = [
{ version = "1.10.0", python = "<3.9" },
{ version = "^1.10.0", python = ">=3.9" },
{ version = ">=1.14.1", python = ">=3.13" },
[project.urls]
Homepage = "https://github.com/roboflow/supervision"
Repository = "https://github.com/roboflow/supervision"
Documentation = "https://supervision.roboflow.com/latest/"
[project.optional-dependencies]
metrics = [
"pandas>=2.0.0",
]
# Matplotlib sub-dependency
# The 'contourpy' package is required by Matplotlib for contour plotting.
# We need to ensure compatibility with both Python 3.8 and Python 3.13.
#
# For Python 3.8 and above, we use version 1.0.7 or higher, as it is the lowest major version that supports Python 3.8.
# For Python 3.13 and above, we use version 1.3.0 or higher, as it is the first version that explicitly supports Python 3.13.
contourpy = [
{ version = ">=1.0.7", python = ">=3.8,<3.13" },
{ version = ">=1.3.0", python = ">=3.13" },
[dependency-groups]
dev = [
"pytest>=7.2.2,<9.0.0",
"tox>=4.11.4",
"notebook>=6.5.3,<8.0.0",
"ipywidgets>=8.1.1",
"jupytext>=1.16.1",
"nbconvert>=7.14.2",
"docutils!=0.21"
]
matplotlib = [
{ version = ">=3.6.0,<3.8.0", python = "3.8" },
{ version = ">=3.6.0", python = ">=3.9" },
{ version = ">=3.7.3", python = ">=3.12" },
{ version = ">=3.9.2", python = ">=3.13" },
docs = [
"mkdocs-material[imaging]>=9.5.5",
"mkdocstrings>=0.25.2,<0.30.0",
"mkdocstrings-python>=1.10.9",
"mike>=2.0.0",
"mkdocs-jupyter>=0.24.3",
"mkdocs-git-committers-plugin-2>=2.4.1; python_version >= '3.9' and python_version < '4'",
"mkdocs-git-revision-date-localized-plugin>=1.2.4"
]
pyyaml = ">=5.3"
defusedxml = "^0.7.1"
pillow = ">=9.4"
requests = ">=2.26.0"
tqdm = ">=4.62.3"
# pandas: picked lowest major version that supports Python 3.8
# pandas 2.2.3 has been released with support for Python 3.13
pandas = [
{ version = ">=2.0.0", python = "<3.13", optional = true },
{ version = ">=2.2.3", python = ">=3.13", optional = true },
build = [
"twine>=5.1.1,<7.0.0",
"wheel>=0.40,<0.46",
"build>=0.10,<1.3"
]
opencv-python = ">=4.5.5.64"
[tool.poetry.extras]
metrics = ["pandas"]
[tool.poetry.group.test.dependencies]
pytest = ">=7.2.2,<9.0.0"
pytest-md = "^0.2.0"
pytest-emoji = "^0.2.0"
[tool.poetry.group.dev.dependencies]
twine = ">=5.1.1,<7.0.0"
pytest = ">=7.2.2,<9.0.0"
wheel = ">=0.40,<0.46"
build = ">=0.10,<1.3"
ruff = ">=0.1.0"
mypy = "^1.4.1"
pre-commit = "^3.3.3"
tox = "^4.11.4"
notebook = ">=6.5.3,<8.0.0"
ipywidgets = "^8.1.1"
jupytext = "^1.16.1"
nbconvert = "^7.14.2"
docutils = [
{ version = "^0.20.1", python = "<3.9" },
{ version = "^0.21.1", python = ">=3.9" },
]
[tool.poetry.group.docs.dependencies]
mkdocs-material = { extras = ["imaging"], version = "^9.5.5" }
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
mkdocs-jupyter = "^0.24.3"
mkdocs-git-committers-plugin-2 = "^2.4.1"
mkdocs-git-revision-date-localized-plugin = "^1.2.4"
[tool.poetry.group.typecheck]
optional = true
[tool.poetry.group.typecheck.dependencies]
types-pyyaml = "^6.0.12.20240808"
types-cffi = "^1.16.0.20240331"
types-requests = "^2.32.0.20240712"
types-tqdm = "^4.66.0.20240417"
pandas-stubs = ">=2.0.0.230412"
[tool.poetry.group.build.dependencies]
twine = ">=5.1.1,<7.0.0"
[tool.bandit]
target = ["test", "supervision"]
tests = ["B201", "B301", "B318", "B314", "B303", "B413", "B412", "B410"]
tests = ["B201", "B301", "B318", "B314", "B303", "B413", "B412"]
[tool.autoflake]
check = true
imports = ["cv2", "supervision"]
[tool.ruff]
target-version = "py38"
target-version = "py39"
# Exclude a variety of commonly ignored directories.
exclude = [
@ -186,7 +128,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", "UP"]
ignore = []
# Allow autofix for all enabled rules (when `--fix`) is provided.
fixable = [
@ -273,7 +215,7 @@ skip-magic-trailing-comma = false
line-ending = "auto"
[tool.codespell]
skip = "*.ipynb,poetry.lock"
skip = "*.ipynb"
count = true
quiet-level = 3
ignore-words-list = "STrack,sTrack,strack"
@ -282,8 +224,12 @@ ignore-words-list = "STrack,sTrack,strack"
include-package-data = false
[tool.setuptools.packages.find]
include = ["supervision*"]
exclude = ["docs*", "test*", "examples*"]
[tool.setuptools.package-data]
supervision = ["py.typed"]
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
requires = ["setuptools >= 61.0"]
build-backend = "setuptools.build_meta"

View File

@ -9,11 +9,11 @@ except importlib_metadata.PackageNotFoundError:
from supervision.annotators.core import (
BackgroundOverlayAnnotator,
BlurAnnotator,
BoundingBoxAnnotator,
BoxAnnotator,
BoxCornerAnnotator,
CircleAnnotator,
ColorAnnotator,
ComparisonAnnotator,
CropAnnotator,
DotAnnotator,
EllipseAnnotator,
@ -38,6 +38,7 @@ from supervision.dataset.core import (
ClassificationDataset,
DetectionDataset,
)
from supervision.dataset.formats.coco import get_coco_class_index_mapping
from supervision.dataset.utils import mask_to_rle, rle_to_mask
from supervision.detection.core import Detections
from supervision.detection.line_zone import (
@ -45,38 +46,53 @@ from supervision.detection.line_zone import (
LineZoneAnnotator,
LineZoneAnnotatorMulticlass,
)
from supervision.detection.lmm import LMM
from supervision.detection.overlap_filter import (
OverlapFilter,
box_non_max_merge,
box_non_max_suppression,
mask_non_max_suppression,
)
from supervision.detection.tools.csv_sink import CSVSink
from supervision.detection.tools.inference_slicer import InferenceSlicer
from supervision.detection.tools.json_sink import JSONSink
from supervision.detection.tools.polygon_zone import PolygonZone, PolygonZoneAnnotator
from supervision.detection.tools.smoother import DetectionsSmoother
from supervision.detection.utils import (
box_iou_batch,
calculate_masks_centroids,
from supervision.detection.utils.boxes import (
clip_boxes,
contains_holes,
contains_multiple_segments,
filter_polygons_by_area,
mask_iou_batch,
denormalize_boxes,
move_boxes,
pad_boxes,
scale_boxes,
)
from supervision.detection.utils.converters import (
mask_to_polygons,
mask_to_xyxy,
move_boxes,
move_masks,
oriented_box_iou_batch,
pad_boxes,
polygon_to_mask,
polygon_to_xyxy,
scale_boxes,
xcycwh_to_xyxy,
xywh_to_xyxy,
xyxy_to_polygons,
xyxy_to_xcycarh,
xyxy_to_xywh,
)
from supervision.detection.utils.iou_and_nms import (
OverlapFilter,
OverlapMetric,
box_iou,
box_iou_batch,
box_iou_batch_with_jaccard,
box_non_max_merge,
box_non_max_suppression,
mask_iou_batch,
mask_non_max_merge,
mask_non_max_suppression,
oriented_box_iou_batch,
)
from supervision.detection.utils.masks import (
calculate_masks_centroids,
contains_holes,
contains_multiple_segments,
move_masks,
)
from supervision.detection.utils.polygons import (
approximate_polygon,
filter_polygons_by_area,
)
from supervision.detection.vlm import LMM, VLM
from supervision.draw.color import Color, ColorPalette
from supervision.draw.utils import (
calculate_optimal_line_thickness,
@ -118,3 +134,117 @@ from supervision.utils.video import (
get_video_frames_generator,
process_video,
)
__all__ = [
"LMM",
"BackgroundOverlayAnnotator",
"BaseDataset",
"BlurAnnotator",
"BoxAnnotator",
"BoxCornerAnnotator",
"ByteTrack",
"CSVSink",
"CircleAnnotator",
"ClassificationDataset",
"Classifications",
"Color",
"ColorAnnotator",
"ColorLookup",
"ColorPalette",
"ComparisonAnnotator",
"ConfusionMatrix",
"CropAnnotator",
"DetectionDataset",
"Detections",
"DetectionsSmoother",
"DotAnnotator",
"EdgeAnnotator",
"EllipseAnnotator",
"FPSMonitor",
"HaloAnnotator",
"HeatMapAnnotator",
"IconAnnotator",
"ImageSink",
"InferenceSlicer",
"JSONSink",
"KeyPoints",
"LabelAnnotator",
"LineZone",
"LineZoneAnnotator",
"LineZoneAnnotatorMulticlass",
"MaskAnnotator",
"MeanAveragePrecision",
"OrientedBoxAnnotator",
"OverlapFilter",
"OverlapMetric",
"PercentageBarAnnotator",
"PixelateAnnotator",
"Point",
"PolygonAnnotator",
"PolygonZone",
"PolygonZoneAnnotator",
"Position",
"Rect",
"RichLabelAnnotator",
"RoundBoxAnnotator",
"TraceAnnotator",
"TriangleAnnotator",
"VertexAnnotator",
"VertexLabelAnnotator",
"VideoInfo",
"VideoSink",
"approximate_polygon",
"box_iou",
"box_iou_batch",
"box_iou_batch_with_jaccard",
"box_non_max_merge",
"box_non_max_suppression",
"calculate_masks_centroids",
"calculate_optimal_line_thickness",
"calculate_optimal_text_scale",
"clip_boxes",
"contains_holes",
"contains_multiple_segments",
"create_tiles",
"crop_image",
"cv2_to_pillow",
"draw_filled_polygon",
"draw_filled_rectangle",
"draw_image",
"draw_line",
"draw_polygon",
"draw_rectangle",
"draw_text",
"filter_polygons_by_area",
"get_coco_class_index_mapping",
"get_polygon_center",
"get_video_frames_generator",
"letterbox_image",
"list_files_with_extensions",
"mask_iou_batch",
"mask_non_max_merge",
"mask_non_max_suppression",
"mask_to_polygons",
"mask_to_rle",
"mask_to_xyxy",
"move_boxes",
"move_masks",
"oriented_box_iou_batch",
"overlay_image",
"pad_boxes",
"pillow_to_cv2",
"plot_image",
"plot_images_grid",
"polygon_to_mask",
"polygon_to_xyxy",
"process_video",
"resize_image",
"rle_to_mask",
"scale_boxes",
"scale_image",
"xcycwh_to_xyxy",
"xywh_to_xyxy",
"xyxy_to_polygons",
"xyxy_to_xcycarh",
"xyxy_to_xywh",
]

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,18 @@
from __future__ import annotations
import textwrap
from enum import Enum
from typing import Optional, Tuple, Union
import numpy as np
from supervision.config import CLASS_NAME_DATA_FIELD
from supervision.detection.core import Detections
from supervision.draw.color import Color, ColorPalette
from supervision.geometry.core import Position
PENDING_TRACK_COLOR = Color.GREY
PENDING_TRACK_ID = -1
class ColorLookup(Enum):
"""
@ -30,7 +36,7 @@ class ColorLookup(Enum):
def resolve_color_idx(
detections: Detections,
detection_idx: int,
color_lookup: Union[ColorLookup, np.ndarray] = ColorLookup.CLASS,
color_lookup: ColorLookup | np.ndarray = ColorLookup.CLASS,
) -> int:
if detection_idx >= len(detections):
raise ValueError(
@ -67,10 +73,10 @@ def resolve_color_idx(
def resolve_text_background_xyxy(
center_coordinates: Tuple[int, int],
text_wh: Tuple[int, int],
center_coordinates: tuple[int, int],
text_wh: tuple[int, int],
position: Position,
) -> Tuple[int, int, int, int]:
) -> tuple[int, int, int, int]:
center_x, center_y = center_coordinates
text_w, text_h = text_wh
@ -119,30 +125,192 @@ def resolve_text_background_xyxy(
)
def get_color_by_index(color: Union[Color, ColorPalette], idx: int) -> Color:
def get_color_by_index(color: Color | ColorPalette, idx: int) -> Color:
if isinstance(color, ColorPalette):
return color.by_idx(idx)
return color
def resolve_color(
color: Union[Color, ColorPalette],
color: Color | ColorPalette,
detections: Detections,
detection_idx: int,
color_lookup: Union[ColorLookup, np.ndarray] = ColorLookup.CLASS,
color_lookup: ColorLookup | np.ndarray = ColorLookup.CLASS,
) -> Color:
idx = resolve_color_idx(
detections=detections,
detection_idx=detection_idx,
color_lookup=color_lookup,
)
if color_lookup == ColorLookup.TRACK and idx == PENDING_TRACK_ID:
return PENDING_TRACK_COLOR
return get_color_by_index(color=color, idx=idx)
def wrap_text(text: str, max_line_length=None) -> list[str]:
"""
Wraps text to the specified maximum line length, respecting existing newlines.
Uses the textwrap library for robust text wrapping.
Args:
text (str): The text to wrap.
Returns:
List[str]: A list of text lines after wrapping.
"""
if not text:
return [""]
if max_line_length is None:
return text.splitlines() or [""]
paragraphs = text.split("\n")
all_lines = []
for paragraph in paragraphs:
if not paragraph:
# Keep empty lines
all_lines.append("")
continue
wrapped = textwrap.wrap(
paragraph,
width=max_line_length,
break_long_words=True,
replace_whitespace=False,
drop_whitespace=True,
)
if wrapped:
all_lines.extend(wrapped)
else:
all_lines.append("")
return all_lines if all_lines else [""]
def validate_labels(labels: list[str] | None, detections: Detections):
"""
Validates that the number of provided labels matches the number of detections.
Args:
labels (Optional[List[str]]): A list of labels, one for each detection. Can
be None.
detections (Detections): The detections to be labeled.
Raises:
ValueError: If `labels` is not None and its length does not match the number
of detections.
"""
if labels is not None and len(labels) != len(detections):
raise ValueError(
f"The number of labels ({len(labels)}) does not match the "
f"number of detections ({len(detections)}). Each detection "
f"should have exactly 1 label."
)
def get_labels_text(
detections: Detections, custom_labels: list[str] | None
) -> list[str]:
"""
Retrieves the text labels for the detections.
If `custom_labels` are provided, they are used. Otherwise, the labels are
extracted from the `detections` object, prioritizing the 'class_name' field,
then the `class_id`, and finally using the detection index as a string.
Args:
detections (Detections): The detections to get labels for.
custom_labels (Optional[List[str]]): An optional list of custom labels.
Returns:
List[str]: A list of text labels for each detection.
"""
if custom_labels is not None:
return custom_labels
labels = []
for idx in range(len(detections)):
if CLASS_NAME_DATA_FIELD in detections.data:
labels.append(detections.data[CLASS_NAME_DATA_FIELD][idx])
elif detections.class_id is not None:
labels.append(str(detections.class_id[idx]))
else:
labels.append(str(idx))
return labels
def snap_boxes(xyxy: np.ndarray, resolution_wh: tuple[int, int]) -> np.ndarray:
"""
Shifts `label` bounding boxes into the frame so that they are fully contained
within the given resolution, prioritizing the top/left edge.
Unlike `clip_boxes`, this function does not crop boxes.
It moves them entirely if they exceed the frame boundaries.
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)`.
resolution_wh (Tuple[int, int]): A tuple `(width, height)`
representing the resolution of the frame.
Returns:
np.ndarray: A numpy array of shape `(N, 4)` with boxes shifted into frame.
Examples:
```python
import numpy as np
# Example boxes:
xyxy = np.array([
[-10, 10, 30, 50], # Off left edge
[310, 200, 350, 250], # Off right edge
[100, -20, 150, 30], # Off top edge
[200, 220, 250, 270], # Off bottom edge
[-20, 10, 350, 50], # Wider than frame (370 vs 320)
[10, -20, 30, 260] # Taller than frame (280 vs 240)
])
resolution_wh = (320, 240)
snapped_boxes = snap_boxes(xyxy=xyxy, resolution_wh=resolution_wh)
# Results:
# [[ 0 10 40 50] # Left edge shifted right
# [280 200 320 250] # Right edge shifted left
# [100 0 150 50] # Top edge shifted down
# [200 190 250 240] # Bottom edge shifted up
# [ 0 10 370 50] # Wide box aligned to left edge
# [ 10 0 30 280]] # Tall box aligned to top edge
```
"""
result = np.copy(xyxy)
width, height = resolution_wh
# X-axis (prioritize left edge)
left_overflow = result[:, 0] < 0
result[left_overflow, 0:3:2] -= result[left_overflow, 0:1]
right_overflow = (~left_overflow) & (result[:, 2] > width)
right_shift = width - result[right_overflow, 2]
result[right_overflow, 0:3:2] += right_shift[:, np.newaxis]
# Y-axis (prioritize top edge)
top_overflow = result[:, 1] < 0
result[top_overflow, 1:4:2] -= result[top_overflow, 1:2]
bottom_overflow = (~top_overflow) & (result[:, 3] > height)
bottom_shift = height - result[bottom_overflow, 3]
result[bottom_overflow, 1:4:2] += bottom_shift[:, np.newaxis]
return result
class Trace:
def __init__(
self,
max_size: Optional[int] = None,
max_size: int | None = None,
start_frame_id: int = 0,
anchor: Position = Position.CENTER,
) -> None:
@ -158,7 +326,10 @@ class Trace:
frame_id = np.full(len(detections), self.current_frame_id, dtype=int)
self.frame_id = np.concatenate([self.frame_id, frame_id])
self.xy = np.concatenate(
[self.xy, detections.get_anchors_coordinates(self.anchor)]
[
self.xy,
detections.get_anchors_coordinates(self.anchor),
]
)
self.tracker_id = np.concatenate([self.tracker_id, detections.tracker_id])

View File

@ -1,2 +1,4 @@
from supervision.assets.downloader import download_assets
from supervision.assets.list import VideoAssets
__all__ = ["VideoAssets", "download_assets"]

View File

@ -1,23 +1,15 @@
from __future__ import annotations
import os
from hashlib import new as hash_new
from pathlib import Path
from shutil import copyfileobj
from typing import Union
from requests import get
from tqdm.auto import tqdm
from supervision.assets.list import VIDEO_ASSETS, VideoAssets
try:
from requests import get
from tqdm.auto import tqdm
except ImportError:
raise ValueError(
"\n"
"Please install requests and tqdm to download assets \n"
"or install supervision with assets \n"
"pip install supervision[assets] \n"
"\n"
)
def is_md5_hash_matching(filename: str, original_md5_hash: str) -> bool:
"""
@ -41,7 +33,7 @@ def is_md5_hash_matching(filename: str, original_md5_hash: str) -> bool:
return computed_md5_hash.hexdigest() == original_md5_hash
def download_assets(asset_name: Union[VideoAssets, str]) -> str:
def download_assets(asset_name: VideoAssets | str) -> str:
"""
Download a specified asset if it doesn't already exist or is corrupted.

View File

@ -1,5 +1,4 @@
from enum import Enum
from typing import Dict, Tuple
BASE_VIDEO_URL = "https://media.roboflow.com/supervision/video-examples/"
@ -39,7 +38,7 @@ class VideoAssets(Enum):
return list(map(lambda c: c.value, cls))
VIDEO_ASSETS: Dict[str, Tuple[str, str]] = {
VIDEO_ASSETS: dict[str, tuple[str, str]] = {
VideoAssets.VEHICLES.value: (
f"{BASE_VIDEO_URL}{VideoAssets.VEHICLES.value}",
"8155ff4e4de08cfa25f39de96483f918",

View File

@ -1,7 +1,7 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Optional, Tuple
from typing import Any
import numpy as np
@ -28,7 +28,7 @@ def _validate_confidence(confidence: Any, n: int) -> None:
@dataclass
class Classifications:
class_id: np.ndarray
confidence: Optional[np.ndarray] = None
confidence: np.ndarray | None = None
def __post_init__(self) -> None:
"""
@ -154,7 +154,7 @@ class Classifications:
class_id = np.arange(len(confidence))
return cls(class_id=class_id, confidence=confidence)
def get_top_k(self, k: int) -> Tuple[np.ndarray, np.ndarray]:
def get_top_k(self, k: int) -> tuple[np.ndarray, np.ndarray]:
"""
Retrieve the top k class IDs and confidences,
ordered in descending order by confidence.

Some files were not shown because too many files have changed in this diff Show More