Merge branch 'develop' into update-byte-track-class-argument-names
This commit is contained in:
22 files changed
+2539
-760
No files matched your search
@@ -37,6 +37,7 @@ jobs:
|
||||
matplotlib==3.5.0 \
|
||||
numpy==1.21.2 \
|
||||
opencv-python==4.5.5.64 \
|
||||
Pillow==10.1.0 \
|
||||
packaging==23.2 \
|
||||
pluggy==1.3.0 \
|
||||
pyparsing==3.1.1 \
|
||||
|
||||
@@ -14,7 +14,6 @@ repos:
|
||||
exclude: test/.*\.py
|
||||
- id: check-yaml
|
||||
exclude: mkdocs.yml
|
||||
- id: check-docstring-first
|
||||
- id: check-executables-have-shebangs
|
||||
- id: check-toml
|
||||
- id: check-case-conflict
|
||||
@@ -47,7 +46,7 @@ repos:
|
||||
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.2.0
|
||||
rev: v0.2.2
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix, --exit-non-zero-on-fix]
|
||||
|
||||
+11
-10
@@ -1,19 +1,20 @@
|
||||
---
|
||||
template: index.html
|
||||
comments: true
|
||||
hide:
|
||||
- navigation
|
||||
- toc
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<p>
|
||||
<a align="center" href="" target="_blank">
|
||||
<img
|
||||
width="850"
|
||||
src="https://media.roboflow.com/open-source/supervision/rf-supervision-banner.png?updatedAt=1678995927529"
|
||||
>
|
||||
</a>
|
||||
</p>
|
||||
<div class="md-typeset">
|
||||
<h1></h1>
|
||||
</div>
|
||||
|
||||
<div align="center" id="logo">
|
||||
<a align="center" href="" target="_blank">
|
||||
<img width="850"
|
||||
src="https://media.roboflow.com/open-source/supervision/rf-supervision-banner.png?updatedAt=1678995927529">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## 👋 Hello
|
||||
@@ -45,7 +46,7 @@ You can install `supervision` with pip in a
|
||||
If you require the full version of `supervision` with GUI support you can install the desktop version. This version includes the GUI components of OpenCV, allowing you to display images and videos on the screen.
|
||||
|
||||
```bash
|
||||
pip install supervision[desktop]
|
||||
pip install "supervision[desktop]"
|
||||
```
|
||||
|
||||
!!! example "git clone (for development)"
|
||||
|
||||
@@ -55,7 +55,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
let authorAvatarsHTML = authorDataArray.map((authorData, index) => {
|
||||
const marginLeft = index === 0 ? '0' : '-10px';
|
||||
const zIndex = 100 - index;
|
||||
const zIndex = 4 - index;
|
||||
return `
|
||||
<div
|
||||
class="author-container"
|
||||
|
||||
+277
-82
@@ -2,7 +2,9 @@
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "TfUZYrKbqlF7"
|
||||
},
|
||||
"source": [
|
||||
"# Annotate Video with Detections\n",
|
||||
"\n",
|
||||
@@ -10,9 +12,72 @@
|
||||
"\n",
|
||||
"[](https://colab.research.google.com/github/roboflow/supervision/blob/develop/docs/notebooks/annotate-video-with-detections.ipynb)\n",
|
||||
"\n",
|
||||
"One of the most common requirements of computer vision applications is detecting objects in images and displaying bounding boxes around those objects. In this cookbook we'll walk through the steps on how to utilize the open source Roboflow ecosystem to accomplish this task on a video. Let's dive in! \n",
|
||||
"One of the most common requirements of computer vision applications is detecting objects in images and displaying bounding boxes around those objects. In this cookbook we'll walk through the steps on how to utilize the open source Roboflow ecosystem to accomplish this task on a video. Let's dive in!"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "rUo1u17yqlF9"
|
||||
},
|
||||
"source": [
|
||||
"## Before you start\n",
|
||||
"\n",
|
||||
"## Installing Dependencies \n",
|
||||
"Let's make sure that we have access to GPU. We can use `nvidia-smi` command to do that. In case of any problems navigate to `Edit` -> `Notebook settings` -> `Hardware accelerator`, set it to `GPU`, and then click `Save`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "0Er370WTqlF9",
|
||||
"outputId": "57b00a1e-87ea-4912-b4f9-5b94f0e6bf48",
|
||||
"vscode": {
|
||||
"languageId": "shellscript"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Fri Feb 23 03:15:00 2024 \n",
|
||||
"+---------------------------------------------------------------------------------------+\n",
|
||||
"| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n",
|
||||
"|-----------------------------------------+----------------------+----------------------+\n",
|
||||
"| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n",
|
||||
"| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n",
|
||||
"| | | MIG M. |\n",
|
||||
"|=========================================+======================+======================|\n",
|
||||
"| 0 Tesla V100-SXM2-16GB Off | 00000000:00:04.0 Off | 0 |\n",
|
||||
"| N/A 33C P0 24W / 300W | 0MiB / 16384MiB | 0% Default |\n",
|
||||
"| | | N/A |\n",
|
||||
"+-----------------------------------------+----------------------+----------------------+\n",
|
||||
" \n",
|
||||
"+---------------------------------------------------------------------------------------+\n",
|
||||
"| Processes: |\n",
|
||||
"| GPU GI CI PID Type Process name GPU Memory |\n",
|
||||
"| ID ID Usage |\n",
|
||||
"|=======================================================================================|\n",
|
||||
"| No running processes found |\n",
|
||||
"+---------------------------------------------------------------------------------------+\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"!nvidia-smi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "WuSnN7ejqlF-"
|
||||
},
|
||||
"source": [
|
||||
"## Installing Dependencies\n",
|
||||
"\n",
|
||||
"In this cookbook we'll be utilizing the open source packages [Inference](https://inference.roboflow.com/) and [Supervision](https://supervision.roboflow.com/latest/) to accomplish our goals. Let's get those installed in our notebook with pip."
|
||||
]
|
||||
@@ -21,40 +86,68 @@
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "1YgQGum4qlF-",
|
||||
"vscode": {
|
||||
"languageId": "shellscript"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\n",
|
||||
"!pip install -q inference \"supervision[assets]\""
|
||||
"!pip install -q inference-gpu \"supervision[assets]\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "PZRnzh7EqlF-"
|
||||
},
|
||||
"source": [
|
||||
"## Download a Video Asset\n",
|
||||
"\n",
|
||||
"First, let's download a video that we can detect objects in. Supervision comes with a great utility called Assets to help us hit the ground running. Wehn we run this script, the video is saved in our local directory and can be accessed with the variable `path_to_video`."
|
||||
"First, let's download a video that we can detect objects in. Supervision comes with a great utility called Assets to help us hit the ground running. When we run this script, the video is saved in our local directory and can be accessed with the variable `path_to_video`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "NMg4KOFiqlF-"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from supervision.assets import download_assets, VideoAssets\n",
|
||||
"\n",
|
||||
"# Download a supervision video asset \n",
|
||||
"# Download a supervision video asset\n",
|
||||
"path_to_video = download_assets(VideoAssets.PEOPLE_WALKING)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "x93oxF0zrQOU"
|
||||
},
|
||||
"source": [
|
||||
"As a result, we've downloaded a video. Let's take a look at the video below. Keep in mind that the video preview below works only in the web version of the cookbooks and not in Google Colab.\n",
|
||||
"\n",
|
||||
"<div style=\"display: flex; justify-content: center;\">\n",
|
||||
" <video controls width=\"1000\"\">\n",
|
||||
" <source\n",
|
||||
" src=\"https://media.roboflow.com/supervision/video-examples/people-walking.mp4\"\n",
|
||||
" type=\"video/mp4\"\n",
|
||||
" >\n",
|
||||
" </video>\n",
|
||||
"</div>\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "T8bU7fQ6qlF-"
|
||||
},
|
||||
"source": [
|
||||
"## Detecting Objects\n",
|
||||
"\n",
|
||||
@@ -63,128 +156,230 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "hlONWHfQqlF-",
|
||||
"outputId": "81b277fb-ea7c-4242-f49d-5af0c665ea58"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Detections(xyxy=array([[1140., 951., 1245., 1079.],\n",
|
||||
" [ 666., 648., 745., 854.],\n",
|
||||
" [ 34., 794., 142., 990.],\n",
|
||||
" [1140., 505., 1211., 657.],\n",
|
||||
" [ 260., 438., 332., 612.],\n",
|
||||
" [1413., 702., 1523., 887.],\n",
|
||||
" [1462., 472., 1543., 643.],\n",
|
||||
" [1446., 318., 1516., 483.],\n",
|
||||
" [ 753., 451., 821., 623.],\n",
|
||||
" [ 924., 172., 983., 307.],\n",
|
||||
" [1791., 144., 1852., 275.],\n",
|
||||
" [ 93., 132., 146., 251.],\n",
|
||||
" [ 708., 240., 765., 388.],\n",
|
||||
" [ 200., 44., 267., 161.],\n",
|
||||
" [1204., 131., 1255., 266.],\n",
|
||||
" [ 569., 267., 628., 408.],\n",
|
||||
" [1163., 150., 1210., 280.],\n",
|
||||
" [ 799., 78., 847., 204.],\n",
|
||||
" [1690., 152., 1751., 283.],\n",
|
||||
" [ 344., 495., 396., 641.],\n",
|
||||
" [1722., 77., 1782., 178.]]),\n",
|
||||
" mask=None,\n",
|
||||
" confidence=array([0.83215541, 0.80572134, 0.7919845 , 0.7912274 , 0.77121079,\n",
|
||||
" 0.7599591 , 0.75711554, 0.75494027, 0.73076195, 0.71452248,\n",
|
||||
" 0.69572842, 0.65269446, 0.63952065, 0.62914598, 0.61361706,\n",
|
||||
" 0.5968492 , 0.55311316, 0.5470854 , 0.54070991, 0.52209878,\n",
|
||||
" 0.41217673]),\n",
|
||||
" class_id=array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),\n",
|
||||
" tracker_id=None,\n",
|
||||
" data={'class_name': array(['person', 'person', 'person', 'person', 'person', 'person',\n",
|
||||
" 'person', 'person', 'person', 'person', 'person', 'person',\n",
|
||||
" 'person', 'person', 'person', 'person', 'person', 'person',\n",
|
||||
" 'person', 'person', 'person'], dtype='<U6')})\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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",
|
||||
"if __name__ == \"__main__\":\n",
|
||||
" # Download the video asset from Supervision assets.\n",
|
||||
" PATH_TO_VIDEO = download_assets(VideoAssets.PEOPLE_WALKING)\n",
|
||||
"# Load a yolov8 model from roboflow.\n",
|
||||
"model = get_roboflow_model(\"yolov8s-640\")\n",
|
||||
"\n",
|
||||
" # Load a yolov8 nano model from roboflow.\n",
|
||||
" model = get_roboflow_model(\"yolov8n-640\")\n",
|
||||
"# Create a frame generator and video info object from supervision utilities.\n",
|
||||
"frame_generator = sv.get_video_frames_generator(path_to_video)\n",
|
||||
"\n",
|
||||
" # Create a frame generator and video info object from supervision utilities.\n",
|
||||
" frame_generator = sv.get_video_frames_generator(PATH_TO_VIDEO)\n",
|
||||
"# Yield a single frame from the generator.\n",
|
||||
"frame = next(frame_generator)\n",
|
||||
"\n",
|
||||
" # Yield a single frame from the generator.\n",
|
||||
" frame = next(frame_generator)\n",
|
||||
"# Run inference on our frame\n",
|
||||
"result = model.infer(frame)[0]\n",
|
||||
"\n",
|
||||
" # Run inference on our frame.\n",
|
||||
" result = model.infer(frame)[0]\n",
|
||||
"# Parse result into detections data model.\n",
|
||||
"detections = sv.Detections.from_inference(result)\n",
|
||||
"\n",
|
||||
" # Parse result into detections data model.\n",
|
||||
" detections = sv.Detections.from_inference(result)\n",
|
||||
"\n",
|
||||
" # Display the detections data model.\n",
|
||||
" print(detections)"
|
||||
"# Pretty Print the resulting detections.\n",
|
||||
"from pprint import pprint\n",
|
||||
"pprint(detections)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "mL7QrQvyqlF-"
|
||||
},
|
||||
"source": [
|
||||
"First, we load our model using the method `get_roboflow_model()`. Notice how we pass in a `model_id`? We're using an [alias](https://inference.roboflow.com/reference_pages/model_aliases/) here. This is where we can pass in other models from Roboflow Universe like this [rock, paper, scissors](https://universe.roboflow.com/roboflow-58fyf/rock-paper-scissors-sxsw) model utilizing our roboflow api key. \n",
|
||||
"First, we load our model using the method `get_roboflow_model()`. Notice how we pass in a `model_id`? We're using an [alias](https://inference.roboflow.com/reference_pages/model_aliases/) here. This is where we can pass in other models from Roboflow Universe like this [rock, paper, scissors](https://universe.roboflow.com/roboflow-58fyf/rock-paper-scissors-sxsw) model utilizing our roboflow api key.\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"model = get_roboflow_mode(\n",
|
||||
" model_id=\"rock-paper-scissors-sxsw/11\", \n",
|
||||
" model_id=\"rock-paper-scissors-sxsw/11\",\n",
|
||||
" api_key=\"roboflow_private_api_key\"\n",
|
||||
")\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"If you don't have an api key, you can [create an free Roboflow account](https://app.roboflow.com/login). This model wouldn't be much help with detecting people, but it's a nice exercise to see how our code becomes model agnostic!\n",
|
||||
"\n",
|
||||
"We then create a `frame_generator` object and yeild a single frame for inference using `next()`. We pass our frame to `model.infer()` to run inference, then pass that data into a little helpfer function called `sv.Detections.from_inference()` to parse it. Lastly we print our detections to show we are in fact detecting a few people in the frame! \n",
|
||||
"We then create a `frame_generator` object and yeild a single frame for inference using `next()`. We pass our frame to `model.infer()` to run inference. After, we pass that data into a little helpfer function called `sv.Detections.from_inference()` to parse it. Lastly we print our detections to show we are in fact detecting a few people in the frame!"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "tM6geyUBwdej"
|
||||
},
|
||||
"source": [
|
||||
"## Annotaing the Frame with Bounding Boxes\n",
|
||||
"\n",
|
||||
"## Saving Bounding Boxes to the Video\n",
|
||||
"Now that we're detecting images, let's get to the fun part. Let's annotate the frame and display the bounding boxes on the frame."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/",
|
||||
"height": 560
|
||||
},
|
||||
"id": "8gkUxKei0RaV",
|
||||
"outputId": "84a95332-70cf-4f26-c4bb-15479afafd04"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"image/png": "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 truncated
|
||||
"text/plain": [
|
||||
"<Figure size 1200x1200 with 1 Axes>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create a bounding box annotator object.\n",
|
||||
"bounding_box = sv.BoundingBoxAnnotator()\n",
|
||||
"\n",
|
||||
"Now comes the fun part. Let's wrap up our code by utilizing `Annotators` and a `VideoSink` to draw bounding boxes and save the resulting video respectively. Take a peak at the final code example below. This might can take up to a minute to run, since we're processing a full video. "
|
||||
"# Annotate our frame with detections.\n",
|
||||
"annotated_frame = bounding_box.annotate(scene=frame.copy(), detections=detections)\n",
|
||||
"\n",
|
||||
"# Display the frame.\n",
|
||||
"sv.plot_image(annotated_frame)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "o8SsyCid6YV3"
|
||||
},
|
||||
"source": [
|
||||
"Notice that we create a `box_annoator` variable by initalizing a [BoundingBoxAnnotator](https://supervision.roboflow.com/latest/annotators/#boundingboxannotator). We can change the color and thickness, but for simplicity we keep the defaults. There are a ton of easy to use [annotators](https://supervision.roboflow.com/latest/annotators/) available in the Supervision package other than a bounding box that are fun to play with."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "gKHSYO14vGC7"
|
||||
},
|
||||
"source": [
|
||||
"## Saving Bounding Boxes to a Video\n",
|
||||
"\n",
|
||||
"Let's wrap up our code by utilizing a `VideoSink` to draw bounding boxes and save the resulting video. Take a peak at the final code example below. This can take a couple minutes deppending on your runtime and since since we're processing a full video. Feel free to skip ahead to see the resulting video."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "Es4SUdPGqlF-"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import supervision as sv\n",
|
||||
"from supervision.assets import download_assets, VideoAssets\n",
|
||||
"from inference.models.utils import get_roboflow_model\n",
|
||||
"from tqdm import tqdm\n",
|
||||
"\n",
|
||||
"# Create a video_info object for use in the VideoSink.\n",
|
||||
"video_info = sv.VideoInfo.from_video_path(video_path=path_to_video)\n",
|
||||
"\n",
|
||||
"if __name__ == \"__main__\":\n",
|
||||
" # Download the video asset from Supervision assets.\n",
|
||||
" PATH_TO_VIDEO = download_assets(VideoAssets.PEOPLE_WALKING)\n",
|
||||
"# Create a VideoSink context manager to save our frames.\n",
|
||||
"with sv.VideoSink(target_path=\"output.mp4\", video_info=video_info) as sink:\n",
|
||||
"\n",
|
||||
" # Load a yolov8 nano model from roboflow.\n",
|
||||
" model = get_roboflow_model(\"yolov8n-640\")\n",
|
||||
" # Iterate through frames yielded from the frame_generator.\n",
|
||||
" for frame in tqdm(frame_generator, total=video_info.total_frames):\n",
|
||||
"\n",
|
||||
" # Initalize the bounding box frame annotator.\n",
|
||||
" box_annotator = sv.BoundingBoxAnnotator()\n",
|
||||
" # Run inference on our frame.\n",
|
||||
" result = model.infer(frame)[0]\n",
|
||||
"\n",
|
||||
" # Create a frame generator object from video path.\n",
|
||||
" frame_generator = sv.get_video_frames_generator(PATH_TO_VIDEO)\n",
|
||||
" # Parse the result into the detections data model.\n",
|
||||
" detections = sv.Detections.from_inference(result)\n",
|
||||
"\n",
|
||||
" # Create a video info object from video path.\n",
|
||||
" video_info = sv.VideoInfo.from_video_path(PATH_TO_VIDEO)\n",
|
||||
" # Apply bounding box to detections on a copy of the frame.\n",
|
||||
" annotated_frame = bounding_box.annotate(\n",
|
||||
" scene=frame.copy(),\n",
|
||||
" detections=detections\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Use a VideoSink context manager for saving frames of a video.\n",
|
||||
" with sv.VideoSink(target_path=\"output.mp4\", video_info=video_info) as sink:\n",
|
||||
"\n",
|
||||
" # Iterate through frames yielded from the frame_generator.\n",
|
||||
" for frame in frame_generator:\n",
|
||||
"\n",
|
||||
" # Run inference on our frame.\n",
|
||||
" result = model.infer(frame)[0]\n",
|
||||
"\n",
|
||||
" # Parse the result into the detections data model.\n",
|
||||
" detections = sv.Detections.from_inference(result)\n",
|
||||
"\n",
|
||||
" # Apply bounding box to detections on a copy of the frame.\n",
|
||||
" annotated_frame = box_annotator.annotate(\n",
|
||||
" scene=frame.copy(), \n",
|
||||
" detections=detections\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Write the annotated frame to the video sink.\n",
|
||||
" sink.write_frame(frame=annotated_frame)"
|
||||
" # Write the annotated frame to the video sink.\n",
|
||||
" sink.write_frame(frame=annotated_frame)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "EXTE3zWxqlF-"
|
||||
},
|
||||
"source": [
|
||||
" Notice that we create a `box_annoator` variable by initalizing a [BoundingBoxAnnotator](https://supervision.roboflow.com/latest/annotators/#boundingboxannotator). We can change the color and thickness, but for simplicity we keep the defaults. There are a ton of easy to use [annotators](https://supervision.roboflow.com/latest/annotators/) available in the Supervision package other than a bounding box that are fun to play with. Next, we create a `video_info` variable to pass information about the video to our `VideoSink`. The `VideoSink` is a cool little context manager that allows us to `write_frames()` to a video ouput file. Prior to writing the frame, we annotate a copy of it utilizing `box_annotator.annotate()`. Let's take a look at the resulting video. It will be installed locally and is called `output.mp4`. When run, you will see bounding boxes around the detections. Pretty awesome! "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We only scratched the surface of all of the customizable Annotators and additional features that Supervision and Inference have to offer. Stay tuned for more cookbooks on how to take advantge of them in your computer vision applications. Happy building! "
|
||||
"<div style=\"display: flex; justify-content: center;\">\n",
|
||||
" <video controls width=\"1000\">\n",
|
||||
" <source\n",
|
||||
" src=\"https://storage.googleapis.com/com-roboflow-marketing/supervision/cookbooks/annotate-video-with-detections.mp4\"\n",
|
||||
" type=\"video/mp4\"\n",
|
||||
" >\n",
|
||||
" </video>\n",
|
||||
"</div>\n",
|
||||
"<br>\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"In the code above we've created a`video_info` variable to pass information about the video to our `VideoSink`. The `VideoSink` is a cool little context manager that allows us to `write_frames()` to a video ouput file. We're also optionally using `tqdm` to display a progress bar with a % complete. We only scratched the surface of all of the customizable Annotators and additional features that Supervision and Inference have to offer. Stay tuned for more cookbooks on how to take advantge of them in your computer vision applications. Happy building! \ud83d\ude80"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"accelerator": "GPU",
|
||||
"colab": {
|
||||
"gpuType": "V100",
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
@@ -197,9 +392,9 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.7"
|
||||
"version": "3.11.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
+25
-3
@@ -17,7 +17,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 10,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "shellscript"
|
||||
@@ -45,6 +45,7 @@
|
||||
"source": [
|
||||
"from supervision.assets import download_assets, VideoAssets\n",
|
||||
"\n",
|
||||
"# Download the a video of the subway.\n",
|
||||
"path_to_video = download_assets(VideoAssets.SUBWAY)"
|
||||
]
|
||||
},
|
||||
@@ -52,7 +53,28 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We're now equipt with a video asset from Supervision to run some experiments on! For more information on available video assets, visit the [Supervision API Reference](https://supervision.roboflow.com/latest/assets/#videoassets). Happy building!"
|
||||
"With this, we now have a high quality video asset for use in demos. Let's take a look at what we downloaded. Keep in mind that the video preview below works only in the web version of the cookbooks and not in Google Colab.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<div style=\"display: flex; justify-content: center;\">\n",
|
||||
" <video controls width=\"320\" height=\"240\">\n",
|
||||
" <source\n",
|
||||
" src=\"https://media.roboflow.com/supervision/video-examples/subway.mp4\"\n",
|
||||
" type=\"video/mp4\"\n",
|
||||
" >\n",
|
||||
" </video>\n",
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We're now equipt with a video asset from Supervision to run some experiments on! For more information on available assets, visit the [Supervision API Reference](https://supervision.roboflow.com/latest/assets). Happy building!"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -72,7 +94,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.7"
|
||||
"version": "3.11.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
Vendored
+223
-56
@@ -2,20 +2,85 @@
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "BsraWznGULVc"
|
||||
},
|
||||
"source": [
|
||||
"# Object Tracking\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"[](https://colab.research.google.com/github/roboflow/supervision/blob/main/docs/notebooks/object-tracking.ipynb)\n",
|
||||
"[](https://colab.research.google.com/github/roboflow/supervision/blob/develop/docs/notebooks/object-tracking.ipynb)\n",
|
||||
"\n",
|
||||
"In some cases, it's important for us to track objects across multiple frames of a video. For example, we may need to figure out the direction a vehicle is moving, or count objects in a frame. Some Supervision [Annotators](https://supervision.roboflow.com/latest/annotators/) and Tools like [LineZone](https://supervision.roboflow.com/latest/detection/tools/line_zone/) require tracking to be setup. In this cookbook, we'll cover how to get a tracker up and running for use in your computer vision applications.\n",
|
||||
"\n",
|
||||
"## What is a Tracker?\n",
|
||||
"\n",
|
||||
"Trackers are a piece of code that identifies objects across frames and assigns them a unique `tracker_id`. There are a few popular trackers at the time of writing this including ByteTrack and Bot-SORT. Supervision makes using trackers a breeze and comes with ByteTrack built-in. But first, let's get our deppendencies installed. \n",
|
||||
"Trackers are a piece of code that identifies objects across frames and assigns them a unique `tracker_id`. There are a few popular trackers at the time of writing this including ByteTrack and Bot-SORT. Supervision makes using trackers a breeze and comes with ByteTrack built-in."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "7h__vutkUZSY"
|
||||
},
|
||||
"source": [
|
||||
"## Before you start\n",
|
||||
"\n",
|
||||
"Let's make sure that we have access to GPU. We can use `nvidia-smi` command to do that. In case of any problems navigate to `Edit` -> `Notebook settings` -> `Hardware accelerator`, set it to `GPU`, and then click `Save`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "pzm2gQlYUbmd",
|
||||
"outputId": "71217cbf-f7d0-4496-c150-1c363386a16f",
|
||||
"vscode": {
|
||||
"languageId": "shellscript"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Fri Feb 23 03:18:02 2024 \n",
|
||||
"+---------------------------------------------------------------------------------------+\n",
|
||||
"| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n",
|
||||
"|-----------------------------------------+----------------------+----------------------+\n",
|
||||
"| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n",
|
||||
"| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n",
|
||||
"| | | MIG M. |\n",
|
||||
"|=========================================+======================+======================|\n",
|
||||
"| 0 Tesla V100-SXM2-16GB Off | 00000000:00:04.0 Off | 0 |\n",
|
||||
"| N/A 33C P0 24W / 300W | 0MiB / 16384MiB | 0% Default |\n",
|
||||
"| | | N/A |\n",
|
||||
"+-----------------------------------------+----------------------+----------------------+\n",
|
||||
" \n",
|
||||
"+---------------------------------------------------------------------------------------+\n",
|
||||
"| Processes: |\n",
|
||||
"| GPU GI CI PID Type Process name GPU Memory |\n",
|
||||
"| ID ID Usage |\n",
|
||||
"|=======================================================================================|\n",
|
||||
"| No running processes found |\n",
|
||||
"+---------------------------------------------------------------------------------------+\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"!nvidia-smi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ZI4EwAnEUTFC"
|
||||
},
|
||||
"source": [
|
||||
"## Install Dependencies"
|
||||
]
|
||||
},
|
||||
@@ -23,18 +88,18 @@
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "powershell"
|
||||
}
|
||||
"id": "NaEjHlAWULVd"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install -q inference \"supervision[assets]\""
|
||||
"!pip install -q inference-gpu \"supervision[assets]\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "zUKICBWFULVe"
|
||||
},
|
||||
"source": [
|
||||
"## Download a Video Asset\n",
|
||||
"\n",
|
||||
@@ -44,94 +109,196 @@
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "MyNzGpzOULVe"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from supervision.assets import download_assets, VideoAssets\n",
|
||||
"\n",
|
||||
"# Download a supervision video asset \n",
|
||||
"# Download a supervision video asset\n",
|
||||
"path_to_video = download_assets(VideoAssets.PEOPLE_WALKING)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "0cMveUy5U-tN"
|
||||
},
|
||||
"source": [
|
||||
"## Tracking Objects\n",
|
||||
"<div style=\"display: flex; justify-content: center;\">\n",
|
||||
" <video controls width=\"1000\"\">\n",
|
||||
" <source\n",
|
||||
" src=\"https://media.roboflow.com/supervision/video-examples/people-walking.mp4\"\n",
|
||||
" type=\"video/mp4\"\n",
|
||||
" >\n",
|
||||
" </video>\n",
|
||||
"</div>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "lowrxifpULVe"
|
||||
},
|
||||
"source": [
|
||||
"## Tracking Objects in a Frame\n",
|
||||
"\n",
|
||||
"Now that we have our video installed, let's get to work on tracking objects. We'll first pull in a model from roboflow Inference to detect people in our video. Then let's create a `byte_tracker` object that we'll pass our detections to. This will give us a `tracker_id`. We'll then utilize that tracker id to label our detections with a `label_annotator` to display the tracker id. Finally, we'll use a utility called `VideoSink` to save the annotated frames to a video. Let's dive in to the code."
|
||||
"Now that we have our video installed, let's get to work on tracking objects. We'll first pull in a model from roboflow Inference to detect people in our video. Then let's create a `byte_tracker` object that we'll pass our detections to. This will give us a `tracker_id`. We'll then utilize that tracker id to label our detections with a `label_annotator` to display the tracker id."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/",
|
||||
"height": 560
|
||||
},
|
||||
"id": "gtbnWeT8ULVe",
|
||||
"outputId": "0fbc468d-ea0d-4d23-a44f-1d50e37f95bf"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "display_data",
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<Figure size 1200x1200 with 1 Axes>"
|
||||
],
|
||||
"image/png": "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 truncated
|
||||
},
|
||||
"metadata": {}
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import supervision as sv\n",
|
||||
"from inference.models.utils import get_roboflow_model\n",
|
||||
"\n",
|
||||
"# Load a pre trained yolov8 nano model from Roboflow Inference.\n",
|
||||
"model = get_roboflow_model('yolov8n-640')\n",
|
||||
"\n",
|
||||
"# Create a video info object from the video path.\n",
|
||||
"video_info = sv.VideoInfo.from_video_path(path_to_video)\n",
|
||||
"\n",
|
||||
"# Create a label annotator for labeling detections with our tracker_id.\n",
|
||||
"label = sv.LabelAnnotator()\n",
|
||||
"\n",
|
||||
"# Create a ByteTrack object to track detections.\n",
|
||||
"byte_tracker = sv.ByteTrack(frame_rate=video_info.fps)\n",
|
||||
"\n",
|
||||
"# Create a frame generator from video path for iteration of frames.\n",
|
||||
"frame_generator = sv.get_video_frames_generator(path_to_video)\n",
|
||||
"\n",
|
||||
"# Grab a frame from the frame_generator.\n",
|
||||
"frame = next(frame_generator)\n",
|
||||
"\n",
|
||||
"# Run inference on the frame by passing it to our model.\n",
|
||||
"result = model.infer(frame)[0]\n",
|
||||
"\n",
|
||||
"# Convert model results to a supervision detection object.\n",
|
||||
"detections = sv.Detections.from_inference(result)\n",
|
||||
"\n",
|
||||
"# Update detections with tracker ids fro byte_tracker.\n",
|
||||
"tracked_detections = byte_tracker.update_with_detections(detections)\n",
|
||||
"\n",
|
||||
"# Create labels with tracker_id for label annotator.\n",
|
||||
"labels = [ f\"{tracker_id}\" for tracker_id in tracked_detections.tracker_id ]\n",
|
||||
"\n",
|
||||
"# Apply label annotator to frame.\n",
|
||||
"annotated_frame = label.annotate(scene=frame.copy(), detections=tracked_detections, labels=labels)\n",
|
||||
"\n",
|
||||
"# Display the frame.\n",
|
||||
"sv.plot_image(annotated_frame)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "wR2nrNUiVOGq"
|
||||
},
|
||||
"source": [
|
||||
"## Tracking Objects in a Video\n",
|
||||
"\n",
|
||||
"Finally, we'll use a utility called `VideoSink` to save the annotated frames to a video. Let's dive in to the code."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "b4lOhjcfVYAl"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import supervision as sv\n",
|
||||
"from supervision.assets import download_assets, VideoAssets\n",
|
||||
"from inference.models.utils import get_roboflow_model\n",
|
||||
"from tqdm import tqdm\n",
|
||||
"\n",
|
||||
"# Load a pre trained yolov8 nano model from Roboflow Inference.\n",
|
||||
"model = get_roboflow_model('yolov8n-640')\n",
|
||||
"\n",
|
||||
"if __name__ == '__main__':\n",
|
||||
"# Create a video info object from the video path.\n",
|
||||
"video_info = sv.VideoInfo.from_video_path(path_to_video)\n",
|
||||
"\n",
|
||||
" # Install our video from supervision assets.\n",
|
||||
" PATH_TO_VIDEO = download_assets(VideoAssets.PEOPLE_WALKING)\n",
|
||||
"# Create a label annotator for labeling detections with our tracker_id.\n",
|
||||
"label = sv.LabelAnnotator()\n",
|
||||
"\n",
|
||||
" # Load a pre trained yolov8 nano model from Roboflow Inference.\n",
|
||||
" model = get_roboflow_model('yolov8n-640')\n",
|
||||
"# Create a ByteTrack object to track detections.\n",
|
||||
"byte_tracker = sv.ByteTrack(frame_rate=video_info.fps)\n",
|
||||
"\n",
|
||||
" # Create a video info object from the video path.\n",
|
||||
" video_info = sv.VideoInfo.from_video_path(PATH_TO_VIDEO)\n",
|
||||
"# Create a frame generator from video path for iteration of frames.\n",
|
||||
"frame_generator = sv.get_video_frames_generator(path_to_video)\n",
|
||||
"\n",
|
||||
" # Create a label annotator for labeling detections with our tracker_id.\n",
|
||||
" label = sv.LabelAnnotator()\n",
|
||||
"# Create a video sink context manager to save resulting video.\n",
|
||||
"with sv.VideoSink(target_path=\"output.mp4\", video_info=video_info) as sink:\n",
|
||||
"\n",
|
||||
" # Create a ByteTrack object to track detections.\n",
|
||||
" byte_tracker = sv.ByteTrack(frame_rate=video_info.fps)\n",
|
||||
" # Iterate through frames yielded from the frame_generator.\n",
|
||||
" for frame in tqdm(frame_generator, total=video_info.total_frames):\n",
|
||||
"\n",
|
||||
" # Create a frame generator from video path for iteration of frames.\n",
|
||||
" frame_generator = sv.get_video_frames_generator(PATH_TO_VIDEO)\n",
|
||||
" # Run inference on the frame by passing it to our model.\n",
|
||||
" result = model.infer(frame)[0]\n",
|
||||
"\n",
|
||||
" # Create a video sink context manager to save resulting video.\n",
|
||||
" with sv.VideoSink(target_path=\"output.mp4\", video_info=video_info) as sink:\n",
|
||||
" # Convert model results to a supervision detection object.\n",
|
||||
" detections = sv.Detections.from_inference(result)\n",
|
||||
"\n",
|
||||
" # Iterate over frames of the video.\n",
|
||||
" for frame in frame_generator:\n",
|
||||
" # Update detections with tracker ids fro byte_tracker.\n",
|
||||
" tracked_detections = byte_tracker.update_with_detections(detections)\n",
|
||||
"\n",
|
||||
" # Run inference on the frame by passing it to our model.\n",
|
||||
" result = model.infer(frame)[0]\n",
|
||||
" # Create labels with tracker_id for label annotator.\n",
|
||||
" labels = [ f\"{tracker_id}\" for tracker_id in tracked_detections.tracker_id ]\n",
|
||||
"\n",
|
||||
" # Convert model results to a supervision detection object.\n",
|
||||
" detections = sv.Detections.from_inference(result)\n",
|
||||
" # Apply label annotator to frame.\n",
|
||||
" annotated_frame = label.annotate(scene=frame.copy(), detections=tracked_detections, labels=labels)\n",
|
||||
"\n",
|
||||
" # Update detections with tracker ids fro byte_tracker.\n",
|
||||
" tracked_detections = byte_tracker.update_with_detections(detections)\n",
|
||||
"\n",
|
||||
" # Create labels with tracker_id for label annotator.\n",
|
||||
" labels = [ f\"{tracker_id}\" for tracker_id in tracked_detections.tracker_id ]\n",
|
||||
"\n",
|
||||
" # Apply label annotator to frame.\n",
|
||||
" annotated_frame = label.annotate(scene=frame.copy(), detections=tracked_detections, labels=labels)\n",
|
||||
"\n",
|
||||
" # Save the annotated frame to an output video.\n",
|
||||
" sink.write_frame(frame=annotated_frame)"
|
||||
" # Save the annotated frame to an output video.\n",
|
||||
" sink.write_frame(frame=annotated_frame)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "0J10FczGULVe"
|
||||
},
|
||||
"source": [
|
||||
"# View the Video\n",
|
||||
"Let's take a look at our resulting video. It will also be created in your current directory with the name `output.mp4` Notice how even with a little flicker, we can see the `tracker_id` on the people walking in the video. With trackers under your belt, there are now a wide variety of use cases you can solve for! Happy building!\n",
|
||||
"\n",
|
||||
"Let's take a look at our resulting video. It will be created in your current directory with the name `output.mp4` Notice how even with a little flicker, we can see the `tracker_id` on the people walking in the video. With trackers under your belt, there are now a wide variety of use cases you can solve for! Happy building! "
|
||||
"<div style=\"display: flex; justify-content: center;\">\n",
|
||||
" <video controls width=\"1000\">\n",
|
||||
" <source\n",
|
||||
" src=\"https://media.roboflow.com/supervision/cookbooks/object-tracking.mp4\"\n",
|
||||
" type=\"video/mp4\"\n",
|
||||
" >\n",
|
||||
" </video>\n",
|
||||
"</div>"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"accelerator": "GPU",
|
||||
"colab": {
|
||||
"gpuType": "V100",
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "venv",
|
||||
"language": "python",
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
@@ -144,9 +311,9 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.7"
|
||||
"version": "3.11.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,894 @@
|
||||
{
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": [],
|
||||
"gpuType": "T4"
|
||||
},
|
||||
"kernelspec": {
|
||||
"name": "python3",
|
||||
"display_name": "Python 3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
},
|
||||
"accelerator": "GPU"
|
||||
},
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# Zero-Shot Object Detection with YOLO-World\n",
|
||||
"---\n",
|
||||
"[](https://colab.research.google.com/github/roboflow/supervision/blob/develop/docs/notebooks/zero-shot-object-detection-with-yolo-world.ipynb)\n",
|
||||
"[](https://blog.roboflow.com/what-is-yolo-world/)\n",
|
||||
"[](https://github.com/AILab-CVC/YOLO-World)\n",
|
||||
"[](https://arxiv.org/abs/2401.17270)\n",
|
||||
"\n",
|
||||
"Click the `Open in Colab` button to run the cookbook on Google Colab.\n",
|
||||
"\n",
|
||||
"<br>\n",
|
||||
"\n",
|
||||
"YOLO-World was designed to solve a limitation of existing zero-shot object detection models: speed. Whereas other state-of-the-art models use Transformers, a powerful but typically slower architecture, YOLO-World uses the faster CNN-based YOLO architecture.\n",
|
||||
"\n",
|
||||
"According to the paper YOLO-World reached between 35.4 AP with 52.0 FPS for the large version and 26.2 AP with 74.1 FPS for the small version. While the V100 is a powerful GPU, achieving such high FPS on any device is impressive.\n",
|
||||
"\n",
|
||||
""
|
||||
],
|
||||
"metadata": {
|
||||
"id": "kR-PyK7YXPVZ"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"## Before you start\n",
|
||||
"\n",
|
||||
"Let's make sure that we have access to GPU. We can use `nvidia-smi` command to do that. In case of any problems navigate to `Edit` -> `Notebook settings` -> `Hardware accelerator`, set it to `GPU`, and then click `Save`."
|
||||
],
|
||||
"metadata": {
|
||||
"id": "T-lkdAZUY73s"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "mP1Bv5jBXNbw",
|
||||
"outputId": "43cc5b50-9958-447d-f597-9ee510e52792"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Fri Feb 16 12:46:14 2024 \n",
|
||||
"+---------------------------------------------------------------------------------------+\n",
|
||||
"| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n",
|
||||
"|-----------------------------------------+----------------------+----------------------+\n",
|
||||
"| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n",
|
||||
"| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n",
|
||||
"| | | MIG M. |\n",
|
||||
"|=========================================+======================+======================|\n",
|
||||
"| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n",
|
||||
"| N/A 65C P8 13W / 70W | 0MiB / 15360MiB | 0% Default |\n",
|
||||
"| | | N/A |\n",
|
||||
"+-----------------------------------------+----------------------+----------------------+\n",
|
||||
" \n",
|
||||
"+---------------------------------------------------------------------------------------+\n",
|
||||
"| Processes: |\n",
|
||||
"| GPU GI CI PID Type Process name GPU Memory |\n",
|
||||
"| ID ID Usage |\n",
|
||||
"|=======================================================================================|\n",
|
||||
"| No running processes found |\n",
|
||||
"+---------------------------------------------------------------------------------------+\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"!nvidia-smi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"**NOTE:** To make it easier for us to manage datasets, images and models we create a `HOME` constant."
|
||||
],
|
||||
"metadata": {
|
||||
"id": "nySApmvdZDEd"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"import os\n",
|
||||
"HOME = os.getcwd()\n",
|
||||
"print(HOME)"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "JGXqXrQfY2yo",
|
||||
"outputId": "161ad18e-a296-4f27-b9c4-2c1fb68a2fc3"
|
||||
},
|
||||
"execution_count": 2,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"/content\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"## Install required packages\n",
|
||||
"\n",
|
||||
"In this guide, we utilize two Python packages: `inference`, for executing zero-shot object detection using YOLO-World, and `supervision`, for post-processing and visualizing the detected objects."
|
||||
],
|
||||
"metadata": {
|
||||
"id": "h1jNp-i3ZL0D"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"!pip install -q inference-gpu[yolo-world]==0.9.12rc1"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "zfc06V9QZFup"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"!pip install -q supervision==0.19.0rc3"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "44LZf5qb3bet"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"## Imports"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "dnEI8EOpalbe"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"import cv2\n",
|
||||
"import supervision as sv\n",
|
||||
"\n",
|
||||
"from tqdm import tqdm\n",
|
||||
"from inference.models.yolo_world.yolo_world import YOLOWorld"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "UIcZyEhuacFT"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"## Download example data"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "ULzC4SZdbO-L"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"!wget -P {HOME} -q https://media.roboflow.com/notebooks/examples/dog.jpeg\n",
|
||||
"!wget -P {HOME} -q https://media.roboflow.com/supervision/cookbooks/yellow-filling.mp4"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "29c6jMS1a7NS"
|
||||
},
|
||||
"execution_count": 6,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"SOURCE_IMAGE_PATH = f\"{HOME}/dog.jpeg\"\n",
|
||||
"SOURCE_VIDEO_PATH = f\"{HOME}/yellow-filling.mp4\""
|
||||
],
|
||||
"metadata": {
|
||||
"id": "Kvq4jtTjg31-"
|
||||
},
|
||||
"execution_count": 7,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"**NOTE:** If you want to run the cookbook using your own file as input, simply upload video to Google Colab and replace `SOURCE_IMAGE_PATH` and `SOURCE_VIDEO_PATH` with the path to your file."
|
||||
],
|
||||
"metadata": {
|
||||
"id": "M0_JmZ08gyYg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"## Run Object Detection\n",
|
||||
"\n",
|
||||
"The Inference package provides the YOLO-World model in three versions: `S`, `M`, and `L`. You can load them by defining model_id as `yolo_world/s`, `yolo_world/m`, and `yolo_world/l`, respectively. The `ROBOFLOW_API_KEY` is not required to utilize this model.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "NLP6DK4YbVpm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"model = YOLOWorld(model_id=\"yolo_world/l\")"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "osrplWqebju0"
|
||||
},
|
||||
"execution_count": 8,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"YOLO-World is a zero-shot model, enabling object detection without any training. You only need to define a prompt as a list of classes (things) you are searching for."
|
||||
],
|
||||
"metadata": {
|
||||
"id": "vzZgYhZodzgS"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"classes = [\"person\", \"backpack\", \"dog\", \"eye\", \"nose\", \"ear\", \"tongue\"]\n",
|
||||
"model.set_classes(classes)"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "UZTYuZlAdYhC",
|
||||
"outputId": "89dba6b8-5f80-4802-fe85-27b4b5c66bb3"
|
||||
},
|
||||
"execution_count": 9,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stderr",
|
||||
"text": [
|
||||
"100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 338M/338M [00:03<00:00, 106MiB/s]\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"We perform detection on our sample image. Then, we convert the result into a [`sv.Detections`](https://supervision.roboflow.com/latest/detection/core/) object, which will be useful in the later parts of the cookbook."
|
||||
],
|
||||
"metadata": {
|
||||
"id": "fXLWvBQNgoV_"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"image = cv2.imread(SOURCE_IMAGE_PATH)\n",
|
||||
"results = model.infer(image)\n",
|
||||
"detections = sv.Detections.from_inference(results)"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "ZinEbFJsdp9m"
|
||||
},
|
||||
"execution_count": 10,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"The results we've obtained can be easily visualized with [`sv.BoundingBoxAnnotator`](https://supervision.roboflow.com/latest/annotators/#supervision.annotators.core.BoundingBoxAnnotator) and [`sv.LabelAnnotator`](https://supervision.roboflow.com/latest/annotators/#supervision.annotators.core.LabelAnnotator). We can adjust parameters such as line thickness, text scale, line and text color allowing for a highly tailored visualization experience."
|
||||
],
|
||||
"metadata": {
|
||||
"id": "jiYw1EXzhpbu"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"BOUNDING_BOX_ANNOTATOR = sv.BoundingBoxAnnotator(thickness=2)\n",
|
||||
"LABEL_ANNOTATOR = sv.LabelAnnotator(text_thickness=2, text_scale=1, text_color=sv.Color.BLACK)"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "37CMTxw0jSyH"
|
||||
},
|
||||
"execution_count": 11,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"annotated_image = image.copy()\n",
|
||||
"annotated_image = BOUNDING_BOX_ANNOTATOR.annotate(annotated_image, detections)\n",
|
||||
"annotated_image = LABEL_ANNOTATOR.annotate(annotated_image, detections)\n",
|
||||
"sv.plot_image(annotated_image, (10, 10))"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/",
|
||||
"height": 807
|
||||
},
|
||||
"id": "YkVoWxchher5",
|
||||
"outputId": "ff1dce17-5584-496a-9113-e4d5123489b4"
|
||||
},
|
||||
"execution_count": 12,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "display_data",
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<Figure size 1000x1000 with 1 Axes>"
|
||||
],
|
||||
"image/png": "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 truncated
|
||||
},
|
||||
"metadata": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"## Adjusting Confidence Level"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "KrferzhUnOk1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"Note that many classes from our prompt were not detected. This is because the default confidence threshold in Inference is set to `0.5`. Let's try significantly lowering this value. We've observed that the confidence returned by YOLO-World is significantly lower when querying for classes outside the [COCO](https://universe.roboflow.com/microsoft/coco) dataset."
|
||||
],
|
||||
"metadata": {
|
||||
"id": "-aBRGrJnlvjN"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"image = cv2.imread(SOURCE_IMAGE_PATH)\n",
|
||||
"results = model.infer(image, confidence=0.003)\n",
|
||||
"detections = sv.Detections.from_inference(results)"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "XKAJJQVLiE6y"
|
||||
},
|
||||
"execution_count": 13,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"By default, `sv.LabelAnnotator` displays only the names of objects. To also view the confidence levels associated with each detection, we must define custom `labels` and pass them to `sv.LabelAnnotator`."
|
||||
],
|
||||
"metadata": {
|
||||
"id": "E2UGr46TmiaQ"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"labels = [\n",
|
||||
" f\"{classes[class_id]} {confidence:0.3f}\"\n",
|
||||
" for class_id, confidence\n",
|
||||
" in zip(detections.class_id, detections.confidence)\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"annotated_image = image.copy()\n",
|
||||
"annotated_image = BOUNDING_BOX_ANNOTATOR.annotate(annotated_image, detections)\n",
|
||||
"annotated_image = LABEL_ANNOTATOR.annotate(annotated_image, detections, labels=labels)\n",
|
||||
"sv.plot_image(annotated_image, (10, 10))"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/",
|
||||
"height": 807
|
||||
},
|
||||
"id": "Zw9tE3fKl4R3",
|
||||
"outputId": "857333d8-7279-4a5c-9bb8-8be8584b82c2"
|
||||
},
|
||||
"execution_count": 14,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "display_data",
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<Figure size 1000x1000 with 1 Axes>"
|
||||
],
|
||||
"image/png": "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 truncated
|
||||
},
|
||||
"metadata": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"## Using Non-Max Suppression (NMS) to Eliminate Double Detections\n",
|
||||
"\n",
|
||||
"To eliminate duplicates, we will use [Non-Max Suppression (NMS)](https://blog.roboflow.com/how-to-code-non-maximum-suppression-nms-in-plain-numpy). NMS evaluates the extent to which detections overlap using the Intersection over Union metric and, upon exceeding a defined threshold, treats them as duplicates. Duplicates are then discarded, starting with those of the lowest confidence. The value should be within the range `[0, 1]`. The smaller the value, the more restrictive the NMS.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "t88HE-7FofwJ"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"image = cv2.imread(SOURCE_IMAGE_PATH)\n",
|
||||
"results = model.infer(image, confidence=0.003)\n",
|
||||
"detections = sv.Detections.from_inference(results).with_nms(threshold=0.1)"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "pyyjlCkAl9g6"
|
||||
},
|
||||
"execution_count": 15,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"labels = [\n",
|
||||
" f\"{classes[class_id]} {confidence:0.3f}\"\n",
|
||||
" for class_id, confidence\n",
|
||||
" in zip(detections.class_id, detections.confidence)\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"annotated_image = image.copy()\n",
|
||||
"annotated_image = BOUNDING_BOX_ANNOTATOR.annotate(annotated_image, detections)\n",
|
||||
"annotated_image = LABEL_ANNOTATOR.annotate(annotated_image, detections, labels=labels)\n",
|
||||
"sv.plot_image(annotated_image, (10, 10))"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/",
|
||||
"height": 807
|
||||
},
|
||||
"id": "no-BoxjTpJt1",
|
||||
"outputId": "30322755-e956-4093-e302-785258476849"
|
||||
},
|
||||
"execution_count": 16,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "display_data",
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<Figure size 1000x1000 with 1 Axes>"
|
||||
],
|
||||
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAcUAAAMWCAYAAACA9J0sAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOy9ebxvx1Ef+K0+v3vv23c9rZYlL7Il23g3xou8YAhgthAGEkIgMZgtIWSZZEgmCclMMplPyMwQ1izAQIxxwCzGNmYwNmBsvEjebVmSLVmLbT3p6e3bfffe3+maP6qru7pPn/M7d3l670m3pPt+v99Zeu/6VlVXVxMzMzZpkzZpkzZpkzYJ7mIXYJM2aZM2aZM26VKhTVDcpE3apE3apE0KtAmKm7RJm7RJm7RJgTZBcZM2aZM2aZM2KdAmKG7SJm3SJm3SJgXaBMVN2qRN2qRN2qRAm6C4SZu0SZu0SZsUaBMUN2mTNmmTNmmTAm2C4iZt0iZt0iZtUqDJ2Ad/8ldu61zz3gMAiEj+AEyc6zxDRHDOYTKZwDkCOcA+pkF1mDn/a1P6Nh/nHJqmCd+BxlFMS591zsXvAMDhz3sf02/bFsycrkkK4Q9ZmsyM6XSKtm0xbYHWI6Zh22GIasGDmAHwjPdCsTzlbdX7IFZfjj7K2nAdwY/W+u6FCLjU7afZbQZ4gBj2VS2bcy6OtwkAB+6kVo7hejny8Vm+Y//KtMtrmk785BZEAHPbeU/Lrp+18ul9W9danbRdmBkeU7R0Dg1th1/ehjnMo8EZtCtfwuGHPoMH7r0Lh7/yMK6++mnYte9JWG4ZR48dB7sJdu7ZhwNXXo2rrrkWC9t2wjkHjzBfCor5hXlY9o/3Hm3bdvqh/LRtLukx2BOYKUvP5inXGJOJA1G9L2Lbypu9+ac/jhW1/WLLwMwAu869tm0jn9L69NU5qxMFHsmMtvXw3sO3Oo4AIgeQB9DGMQUg8vWmadA0Tagnx7ZQsn1Tax9bnto4r2GEfc57gD1nZevgCTN+6vu/BrNoNCiWNJZZdScLAAa8TwNQG6xk+Fpx+8lmsDRNkz0/C5TWSlwOUK8QW58Em3S5UerPWc+Uw74EDoCrSZUMd2jM1ASfMc/XKAqJcCDHYHYZQ3FBOq2Brv1eA86yTp284dC4+SD0tSBaBvtFLC6exvETJ3Dy5Gns2XMQ1z7pRuw9+GTATbD76DF4ELbv3I1tO3djYev2xPRWMc2G2nD8JwdZSdPI6yhCBsV7mlWtf5kZ5AiEumCTPYd+/hfrxPk7gPDRPsGg7K/yk6E8WOvCAOyYcAKc7DvjQgFR+TGFtrFlqI6PgTHdB4xlOuXvcjzadhtLo0FxIyR26TArSVgpT5hObIRigNlKJu1TpDhfSEQbSd1OSNfX0/CbdPkRVbrYkTA6F/5qiFiOYaB/zJQTvwY6tXE+xHSIhM0SCeO2oBgZWY/2aUHQ/g1ZLNJcdPDtBGCCwxSuWcF0egqnTh3GsePH0Ey24sqrb8Tuvddg594DoGYOCzv3wDOhmczDzS8AboLWMzwYEaMGpnlNmyvbohRkau0Zrxf1TffzgjB72L6vARrBwbm8HGV5EVPpAmOnzYtr1srQ1yZlW+inZ+W/iGMElGpIJADo2WXlyjREbbPQFrbcfda0vnFX+12rl9aZtJCmbOX3sfSYgaIWznsPMIfBkTq0bdsoaSdgrEt51hwymQSm5PLJvpGkHdq2bfjLzTiboHi505i+YzhKY1ZIlgwcyR+Bexfp+6T+2nPl85ZWO7ZdXKcggHwvKJakgFguQ2jZ+jQQW3ZCA/gGjhnOLYPbMzh//lGcOXsMZ86cxZ6dB7H/iidj596rQHNbwUTYMr8NTA6eCZ4hnwAYFDSo2X3VZ3HS8vZ91jRfcpKl3E+alGhRVmjp5l3vy247dZg9OGMwVeAMAoI1DVpQrAGI5qVKRW4pQDSfAh7MPhQhiHxBInGUj5fSlC756MxApzx27PUBZM1iUWurrI5EsT1qY3I1tGbz6VooFjSaTyHWJrYSbP4OmXftBNWO9d6hcfbJ4v0ZUm23jEAp8emfgiKzi/mNAcQ+jSCv4QiaVfy6orIuemKAPcdu6Jeqde06swuBY4On8WzHLCrfy7RXVdIKw+ujUuKWAg6vT2oe5f0awNjrtXwJhDm3AIcVgM9gaekoTp54CEePPoKFLdtx4OCTsGXHATQLO8FNAwZhuZW1W+caeDC8Za7I+cOQ6azWTiWj7ZS3+HTOobSGqyalrydgbDr5WlOmlr+m8Zc8iopJXAJCTRuuAeOQRUH5qfdeFBQXLCGUBA/RfrVNCWD1B3HDYzDH/k49anWo9UHZn2X5s/ZWAB8Ym2Pn24aD4pDqnkwGQC4MEZyq2l7ZjEfjkjRmwVDT0kHgKK2L2DzLBlKbe3k/aaZpApSTKqrpFNYFzGStmUJW0WAYA4xlyn0dzKtZeNkkoagB1s2bojERGkLW15b5EPX3YsksZ2lo9nNWOkPP6r2kLabfQ2Yqm+awQFe/lgDVwXlC0xCm7SLOnzuOY0cexuLZRezZcy127TmI3fuuhHdz8GHuMTVgBnw7BUO0RKigSqpF1dvF5l/2T16uYRNe1h6BT6V0OGitFMyLFAAyz9P6SZRamu2PatkrjlodBl88YcGidGrpE2z003MQPYjhnGjHriHo5gQihOuJH9vxVQPh2li1vxVYLf9UKp2H+pSavK1dBJQOYK6SHlNNEUgaXkm1yusg1Gs18GEzQ8oG0LyYOXiudSfKEJXSWR/g10C4Vr+NoOE81p7GhdYIL0aeffkWpQiiv0jFqlCJRkCRITROBLcaKNp8KDDKkvGMBZcxdailPWvsMTgImV1A6APXIWGvdOiopQdmMLdo23NYOX8KJ44+jONHjmBhsg0H9l+HfQeuRrNlC5aC052a78KsEtM08jlWa7EhLcK2V195a3WPaRsLQOzrqFERQAJhXACRamEWHL330QPeCiuddg9crU8wUbC2da0910f6jpaRCIBToZCDr0YwWHMys6pwWObbLVu3vLU+svdqfVQ+U867fA2V47wbUszG0AXXFIHUuTkglg2QPu33cqyWTCmZX7vgmElP6A6gPgnRpmNNKbHzjKZo3+sbnH2dvUmXCJFOvjS+tL/1+8Q5TAhhXTGwD5/Pf9FiyvWV8YA4RlCraUT2d28a0byUa0x92tNQOfvudZg4MeYaxnR5EadOHcXRI49guuxx9bXX4cB+2WrBDQEISxKhUdV8mDNYBnNpWOyWpaYlzJrnQ+BfEzqsyRzFmsWgEMGqVSIbX2X5HPWPm1ieQjNdLU/J8iQdqxyEwGAOJg6g6LJtcEk7Wx0vK3l0n9LRl6bdZlSaplWQKNNZrcAAbDQoMncGbU11HzIX2sKLqSE3+ZTPMqMzMKzkYfcgaqP1TgLqmiXK8nvPIJbFZw4IbjujBrpDzHGTLgUS01zUEh3gmiDFQ7435NAYUAQAT7kmoK4g6pgRU+8BxTEg2FviHnDsBTlKlhf7TLklo8xjrUxPsvRYac/h3JnjePTwQzhz6jT27N6PvXuuxPZtewGawxQt0HhgCsTGYx/cO1i2CsgEw5QJTHVXpg7z4/z6EDAO94N6ZKZPRG6SBPzS3FcV0k2+tfuxXK77nB0vRFRi8ZqAMabJgKwfIgCg5OFJhTwH55o4VtYCNGX5avywL10LhN4zvG8rClPxDurgOIY2DBTLjleKQ7gyEPJGArraI4sECQeiJKUEN6NgugDCGnCS34y5wwPw7MUY0wFBc40oKAw62vI1g6ZpgqchwzOBdN9U8JJjQHf6RKBWiI1Au0Zg1FYZ9WZnfnPnRY7/PPGoziRTnxMRHDkxlYaJp25Vslcr9QY5Bwd1Z/dmKHXNpkNMYIgp95mdZ73Tvd/dUF0rT5/JrkvKuVsw+bjS5tCAmNBgDsQtVhYfxukTX8bRI0fBbgd27bse2/ddDbdlO1oQpisrYGI0ydMpmk89xP8gQLmgRTGc1bSpZWYGvHqHczH/snoxhK8Ma8hyu9YWucBbA7rObwpiE1G0TnjOtxMI/3BhNHJcu0yIGp7ywf+ih+H3CeVWy9NrLprVNR0XeJ1IiTInKBMOpEdYZ03yDK40Vd9Yspd9yyAHNE2cbQAzfBgPbRvMpT7wVzDY67wklLISm6KwSrsjmd5oUBxl2qlc8zDStZ2QPlRYB58OfSMNSbPLyi+TC9dkvw+c3CUns4ZTt8TGjoPAoICuXYQHYrliyQwwxqschgm5kL+YGlQDBQn4ClaT9lRIPtXHGN5Sa41QFuriRpdi39t3wwDqJjhsUpJ3L23kXI+mldLQ2cQAOzh2aEBo4oxikOfAjxszkWXiionJh3Gq6eTmdi3rrPIOmcv6y5+vCfabRPNxVzNfDVGn7ByYLImPKJNHQ43M9SlAnuCXl7F89is4cfQBLJ1fwZ4DT8LczmtA2/ZhkQiNc6AAnmzW5yJvZSCBL6SNmeP6HcU5Jg+kiCYOzHJPLEyIQnIyEyZht6xjDmYh86y9c61G/nxvW8a2o1Ad4nwKxjTlZ8sc98Tqq6KlkWwBhLQB+36hqhR+LCBaS5qk6yJP0DVErWdKj5WtwTPDkUTYQmhPAGDf5jze5GfLJJ9BkwnkQ50dxaKIgAPAMzBtfdzLLkpHLrg4I/RxSK9sD+qxMpT0mDvaAKgOHjtJ41pCnNi6hhe8VGGfTZtmZSJ1BwCQM4wxZRv6LukBEfDsH8mMVrPEpUi5ZLpJQj0aGdJ47JPIM/CrpbxGCwFQhseqA5/VVFYrKIwpVzVN8iIYUgNCA+IkRBJawJ/F0tJxnDxxHCeOn8DWbbuxd98V2LNvP+BcYbkJAoYKs6a+eWPUzZL6Pc17I3RmdbDts77xXwOdskylBilvmH2j8dPUndEJb2nTjekZVagcB32ezTVzYgTIEe3RZ262IDukLXfT0/onUjNpXPoKzLine truncated
|
||||
},
|
||||
"metadata": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"## Video Processing"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "S-LLI26VvF7B"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"The [`get_video_frames_generator`](https://supervision.roboflow.com/latest/utils/video/#supervision.utils.video.get_video_frames_generator) enables us to easily iterate over video frames. Let's create a video generator for our sample input file and display its first frame on the screen."
|
||||
],
|
||||
"metadata": {
|
||||
"id": "E0rhepjGv9oe"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"generator = sv.get_video_frames_generator(SOURCE_VIDEO_PATH)\n",
|
||||
"frame = next(generator)\n",
|
||||
"\n",
|
||||
"sv.plot_image(frame, (10, 10))"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/",
|
||||
"height": 472
|
||||
},
|
||||
"id": "J2Tm9lcGpLF1",
|
||||
"outputId": "94629d68-9266-474e-e311-cd311457816f"
|
||||
},
|
||||
"execution_count": 17,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "display_data",
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<Figure size 1000x1000 with 1 Axes>"
|
||||
],
|
||||
"image/png": "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 truncated
|
||||
},
|
||||
"metadata": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"Let's update our list of classes. This time we are looking for `yellow filling`. The rest of the code performing detection, filtering and visualization remains unchanged."
|
||||
],
|
||||
"metadata": {
|
||||
"id": "Su3lwi6D62sR"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"classes = [\"yellow filling\"]\n",
|
||||
"model.set_classes(classes)"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "bddF-z6WwAkD"
|
||||
},
|
||||
"execution_count": 23,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"results = model.infer(frame, confidence=0.002)\n",
|
||||
"detections = sv.Detections.from_inference(results).with_nms(threshold=0.1)"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "xQ0kV9huwmoM"
|
||||
},
|
||||
"execution_count": 38,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"annotated_image = frame.copy()\n",
|
||||
"annotated_image = BOUNDING_BOX_ANNOTATOR.annotate(annotated_image, detections)\n",
|
||||
"annotated_image = LABEL_ANNOTATOR.annotate(annotated_image, detections)\n",
|
||||
"sv.plot_image(annotated_image, (10, 10))"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/",
|
||||
"height": 472
|
||||
},
|
||||
"id": "AfWCMuq5wsuY",
|
||||
"outputId": "2a1ede5e-8e19-4d65-90a7-4579f0115183"
|
||||
},
|
||||
"execution_count": 39,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "display_data",
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<Figure size 1000x1000 with 1 Axes>"
|
||||
],
|
||||
"image/png": "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 truncated
|
||||
},
|
||||
"metadata": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"## Filtering Detectuions by Area"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "CwI09wuYCztj"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"Our prompt allowed us to locate all filled holes, but we also accidentally marked the entire high-level element. To address this issue, we'll filter detections based on their relative area in relation to the entire video frame. If a detection occupies more than 10% of the frame's total area, it will be discarded."
|
||||
],
|
||||
"metadata": {
|
||||
"id": "TVAulRgE-idz"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"We can use [`VideoInfo.from_video_path`](https://supervision.roboflow.com/develop/utils/video/#supervision.utils.video.VideoInfo) to learn basic information about our video, such as duration, resolution, or FPS."
|
||||
],
|
||||
"metadata": {
|
||||
"id": "OX4l0ofl_Pe4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"video_info = sv.VideoInfo.from_video_path(SOURCE_VIDEO_PATH)\n",
|
||||
"video_info"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "LV99lGQJ_JzT",
|
||||
"outputId": "0683435f-a3cb-418f-b1b9-f94d299192f3"
|
||||
},
|
||||
"execution_count": 40,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "execute_result",
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"VideoInfo(width=1280, height=720, fps=25, total_frames=442)"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"execution_count": 40
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"Knowing the frame's resolution allows us to easily calculate its total area, expressed in pixels."
|
||||
],
|
||||
"metadata": {
|
||||
"id": "IGckLMVR_4kp"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"width, height = video_info.resolution_wh\n",
|
||||
"frame_area = width * height\n",
|
||||
"frame_area"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "8aJBl-Rc_xUP",
|
||||
"outputId": "c4b2798b-c5c6-429c-8886-02b4f7af3924"
|
||||
},
|
||||
"execution_count": 41,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "execute_result",
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"921600"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"execution_count": 41
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"On the other hand, by using [`sv.Detections.area`](https://supervision.roboflow.com/develop/detection/core/#supervision.detection.core.Detections.area) property, we can learn the area of each individual bounding box."
|
||||
],
|
||||
"metadata": {
|
||||
"id": "Y_LO4ODXAntj"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"results = model.infer(frame, confidence=0.002)\n",
|
||||
"detections = sv.Detections.from_inference(results).with_nms(threshold=0.1)\n",
|
||||
"detections.area"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "B9PQhXf__TNW",
|
||||
"outputId": "dc096dd6-a93d-4599-e6d8-9d475a54b999"
|
||||
},
|
||||
"execution_count": 45,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "execute_result",
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"array([ 7.5408e+05, 92844, 11255, 12969, 9875.9, 8007.7, 5433.5])"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"execution_count": 45
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"Now, we can combine these two pieces of information to construct a filtering condition for detections with an area greater than 10% of the entire frame."
|
||||
],
|
||||
"metadata": {
|
||||
"id": "LAnsLumrBZNw"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"(detections.area / frame_area) < 0.10"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "lD9ONsS9BRov",
|
||||
"outputId": "100d1d15-eba3-47ed-823b-b8756e13c123"
|
||||
},
|
||||
"execution_count": 46,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "execute_result",
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"array([False, False, True, True, True, True, True])"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"execution_count": 46
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"detections = detections[(detections.area / frame_area) < 0.10]\n",
|
||||
"\n",
|
||||
"annotated_image = frame.copy()\n",
|
||||
"annotated_image = BOUNDING_BOX_ANNOTATOR.annotate(annotated_image, detections)\n",
|
||||
"annotated_image = LABEL_ANNOTATOR.annotate(annotated_image, detections)\n",
|
||||
"sv.plot_image(annotated_image, (10, 10))"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/",
|
||||
"height": 472
|
||||
},
|
||||
"id": "XzE7wjwE_bOR",
|
||||
"outputId": "5df62911-a001-46a4-a16c-8cdab7608e2e"
|
||||
},
|
||||
"execution_count": 47,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "display_data",
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<Figure size 1000x1000 with 1 Axes>"
|
||||
],
|
||||
"image/png": "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 truncated
|
||||
},
|
||||
"metadata": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"## Final Result"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "7SjPoFnLC9aI"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"Finally, we are ready to process our entire video. Now in truth we can appreciate the speed of YOLO-World."
|
||||
],
|
||||
"metadata": {
|
||||
"id": "WxwNMXGECodq"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"TARGET_VIDEO_PATH = f\"{HOME}/yellow-filling-output.mp4\""
|
||||
],
|
||||
"metadata": {
|
||||
"id": "p0aooVpaCSH3"
|
||||
},
|
||||
"execution_count": 49,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"frame_generator = sv.get_video_frames_generator(SOURCE_VIDEO_PATH)\n",
|
||||
"video_info = sv.VideoInfo.from_video_path(SOURCE_VIDEO_PATH)\n",
|
||||
"\n",
|
||||
"width, height = video_info.resolution_wh\n",
|
||||
"frame_area = width * height\n",
|
||||
"frame_area\n",
|
||||
"\n",
|
||||
"with sv.VideoSink(target_path=TARGET_VIDEO_PATH, video_info=video_info) as sink:\n",
|
||||
" for frame in tqdm(frame_generator, total=video_info.total_frames):\n",
|
||||
" results = model.infer(frame, confidence=0.002)\n",
|
||||
" detections = sv.Detections.from_inference(results).with_nms(threshold=0.1)\n",
|
||||
" detections = detections[(detections.area / frame_area) < 0.10]\n",
|
||||
"\n",
|
||||
" annotated_frame = frame.copy()\n",
|
||||
" annotated_frame = BOUNDING_BOX_ANNOTATOR.annotate(annotated_frame, detections)\n",
|
||||
" annotated_frame = LABEL_ANNOTATOR.annotate(annotated_frame, detections)\n",
|
||||
" sink.write_frame(annotated_frame)"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "A8TzekpEwtTO",
|
||||
"outputId": "cf71b8e7-00af-4b7b-c912-9157ced6b6c4"
|
||||
},
|
||||
"execution_count": 50,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stderr",
|
||||
"text": [
|
||||
"100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 442/442 [00:31<00:00, 13.90it/s]\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
" Keep in mind that the video preview below works only in the web version of the cookbooks and not in Google Colab.\n",
|
||||
"\n",
|
||||
"<video controls>\n",
|
||||
" <source\n",
|
||||
" src=\"https://storage.googleapis.com/com-roboflow-marketing/supervision/cookbooks/yellow-filling-output-1280x720.mp4\"\n",
|
||||
" type=\"video/mp4\"\n",
|
||||
" >\n",
|
||||
"</video>"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "vMp9mSj1DGJ9"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+1
@@ -13,6 +13,7 @@
|
||||
<div class="custom-grid">
|
||||
<p class="card repo-card" data-url="/develop/notebooks/quickstart" data-name="Supervision Quickstart" data-labels="ANNOTATOR,DETECTION,SAM" data-version="v0.18.0" data-author="SkalskiP,onuralpszr"></p>
|
||||
<p class="card repo-card" data-url="/develop/notebooks/count-objects-crossing-the-line" data-name="Count Objects Crossing the Line" data-labels="ANNOTATORS,LINE ZONE,TRACKING" data-version="v0.18.0" data-author="SkalskiP"></p>
|
||||
<p class="card repo-card" data-url="/develop/notebooks/zero-shot-object-detection-with-yolo-world" data-name="Zero-Shot Object Detection with YOLO-World" data-labels="ANNOTATORS,DETECTION,INFERENCE" data-version="v0.19.0" data-author="SkalskiP"></p>
|
||||
<p class="card repo-card" data-url="/develop/notebooks/download-supervision-assets" data-name="Downloading Supervision Assets" data-labels="ASSETS" data-version="v0.18.0" data-author="nickherrig"></p>
|
||||
<p class="card repo-card" data-url="/develop/notebooks/annotate-video-with-detections" data-name="Annotate Video with Detections" data-labels="INFERENCE,YOLOV8" data-version="v0.18.0" data-author="nickherrig"></p>
|
||||
<p class="card repo-card" data-url="/develop/notebooks/object-tracking" data-name="Object Tracking" data-labels="TRACKING, ANNOTATOR" data-version="v0.18.0" data-author="nickherrig"></p>
|
||||
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
{% extends "main.html" %}
|
||||
{% block content %}
|
||||
{{ super() }}
|
||||
<style>
|
||||
.md-content__button {
|
||||
display: none;
|
||||
}
|
||||
#logo {
|
||||
position: relative;
|
||||
top: -60px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
Generated
+597
-494
File diff suppressed because it is too large.
Load diff
+9
-8
@@ -44,6 +44,7 @@ opencv-python = { version = ">=4.5.5.64", optional = true }
|
||||
opencv-python-headless = ">=4.5.5.64"
|
||||
requests = { version = ">=2.26.0,<=2.31.0", optional = true }
|
||||
tqdm = { version = ">=4.62.3,<=4.66.2", optional = true }
|
||||
pillow = ">=9.4"
|
||||
|
||||
[tool.poetry.extras]
|
||||
desktop = ["opencv-python"]
|
||||
@@ -111,12 +112,12 @@ exclude = '''
|
||||
[tool.ruff]
|
||||
target-version = "py38"
|
||||
# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default.
|
||||
select = ["E", "F"]
|
||||
ignore = []
|
||||
lint.select = ["E", "F"]
|
||||
lint.ignore = []
|
||||
|
||||
# Allow autofix for all enabled rules (when `--fix`) is provided.
|
||||
fixable = ["A", "B", "C", "D", "E", "F", "G", "I", "N", "Q", "S", "T", "W", "ANN", "ARG", "BLE", "COM", "DJ", "DTZ", "EM", "ERA", "EXE", "FBT", "ICN", "INP", "ISC", "NPY", "PD", "PGH", "PIE", "PL", "PT", "PTH", "PYI", "RET", "RSE", "RUF", "SIM", "SLF", "TCH", "TID", "TRY", "UP", "YTT"]
|
||||
unfixable = []
|
||||
lint.fixable = ["A", "B", "C", "D", "E", "F", "G", "I", "N", "Q", "S", "T", "W", "ANN", "ARG", "BLE", "COM", "DJ", "DTZ", "EM", "ERA", "EXE", "FBT", "ICN", "INP", "ISC", "NPY", "PD", "PGH", "PIE", "PL", "PT", "PTH", "PYI", "RET", "RSE", "RUF", "SIM", "SLF", "TCH", "TID", "TRY", "UP", "YTT"]
|
||||
lint.unfixable = []
|
||||
|
||||
# Exclude a variety of commonly ignored directories.
|
||||
exclude = [
|
||||
@@ -151,17 +152,17 @@ line-length = 88
|
||||
indent-width = 4
|
||||
|
||||
# Allow unused variables when underscore-prefixed.
|
||||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
lint.dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
|
||||
[tool.ruff.flake8-quotes]
|
||||
[tool.ruff.lint.flake8-quotes]
|
||||
inline-quotes = "double"
|
||||
multiline-quotes = "double"
|
||||
docstring-quotes = "double"
|
||||
|
||||
[tool.ruff.pydocstyle]
|
||||
[tool.ruff.lint.pydocstyle]
|
||||
convention = "google"
|
||||
|
||||
[tool.ruff.per-file-ignores]
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"__init__.py" = ["E402","F401"]
|
||||
"supervision/assets/list.py" = ["E501"]
|
||||
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TypeVar
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
from supervision.detection.core import Detections
|
||||
|
||||
ImageType = TypeVar("ImageType", np.ndarray, Image.Image)
|
||||
"""
|
||||
An image of type `np.ndarray` or `PIL.Image.Image`.
|
||||
|
||||
Unlike a `Union`, ensures the type remains consistent. If a function
|
||||
takes an `ImageType` argument and returns an `ImageType`, when you
|
||||
pass an `np.ndarray`, you will get an `np.ndarray` back.
|
||||
"""
|
||||
|
||||
|
||||
class BaseAnnotator(ABC):
|
||||
@abstractmethod
|
||||
def annotate(self, scene: np.ndarray, detections: Detections) -> np.ndarray:
|
||||
def annotate(self, scene: ImageType, detections: Detections) -> ImageType:
|
||||
pass
|
||||
+164
-74
@@ -4,8 +4,13 @@ from typing import List, Optional, Tuple, Union
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from supervision.annotators.base import BaseAnnotator
|
||||
from supervision.annotators.utils import ColorLookup, Trace, resolve_color
|
||||
from supervision.annotators.base import BaseAnnotator, ImageType
|
||||
from supervision.annotators.utils import (
|
||||
ColorLookup,
|
||||
Trace,
|
||||
resolve_color,
|
||||
scene_to_annotator_img_type,
|
||||
)
|
||||
from supervision.config import CLASS_NAME_DATA_FIELD, ORIENTED_BOX_COORDINATES
|
||||
from supervision.detection.core import Detections
|
||||
from supervision.detection.utils import clip_boxes, mask_to_polygons
|
||||
@@ -37,23 +42,26 @@ class BoundingBoxAnnotator(BaseAnnotator):
|
||||
self.thickness: int = thickness
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with bounding boxes based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where bounding boxes will be drawn.
|
||||
scene (ImageType): The image where bounding boxes will be drawn. `ImageType`
|
||||
is a flexible type, accepting either `numpy.ndarray` or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -115,23 +123,27 @@ class OrientedBoxAnnotator(BaseAnnotator):
|
||||
self.thickness: int = thickness
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with oriented bounding boxes based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where bounding boxes will be drawn.
|
||||
scene (ImageType): The image where bounding boxes will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -199,23 +211,27 @@ class MaskAnnotator(BaseAnnotator):
|
||||
self.opacity = opacity
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with masks based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where masks will be drawn.
|
||||
scene (ImageType): The image where masks will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -282,23 +298,27 @@ class PolygonAnnotator(BaseAnnotator):
|
||||
self.thickness: int = thickness
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with polygons based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where polygons will be drawn.
|
||||
scene (ImageType): The image where polygons will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -364,23 +384,27 @@ class ColorAnnotator(BaseAnnotator):
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
self.opacity = opacity
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with box masks based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where bounding boxes will be drawn.
|
||||
scene (ImageType): The image where bounding boxes will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -454,23 +478,27 @@ class HaloAnnotator(BaseAnnotator):
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
self.kernel_size: int = kernel_size
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with halos based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where masks will be drawn.
|
||||
scene (ImageType): The image where masks will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -548,23 +576,27 @@ class EllipseAnnotator(BaseAnnotator):
|
||||
self.end_angle: int = end_angle
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with ellipses based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where ellipses will be drawn.
|
||||
scene (ImageType): The image where ellipses will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -635,23 +667,27 @@ class BoxCornerAnnotator(BaseAnnotator):
|
||||
self.corner_length: int = corner_length
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with box corners based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where box corners will be drawn.
|
||||
scene (ImageType): The image where box corners will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -719,23 +755,27 @@ class CircleAnnotator(BaseAnnotator):
|
||||
self.thickness: int = thickness
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with circles based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where box corners will be drawn.
|
||||
scene (ImageType): The image where box corners will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -805,23 +845,27 @@ class DotAnnotator(BaseAnnotator):
|
||||
self.position: Position = position
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with dots based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where dots will be drawn.
|
||||
scene (ImageType): The image where dots will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -929,26 +973,44 @@ class LabelAnnotator:
|
||||
center_x + text_w // 2,
|
||||
center_y + text_h,
|
||||
)
|
||||
elif position == Position.CENTER_LEFT:
|
||||
return (
|
||||
center_x - text_w,
|
||||
center_y - text_h // 2,
|
||||
center_x,
|
||||
center_y + text_h // 2,
|
||||
)
|
||||
elif position == Position.CENTER_RIGHT:
|
||||
return (
|
||||
center_x,
|
||||
center_y - text_h // 2,
|
||||
center_x + text_w,
|
||||
center_y + text_h // 2,
|
||||
)
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
labels: List[str] = None,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with labels based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where labels will be drawn.
|
||||
scene (ImageType): The image where labels will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
labels (List[str]): Optional. Custom labels for each detection.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -1049,20 +1111,24 @@ class BlurAnnotator(BaseAnnotator):
|
||||
"""
|
||||
self.kernel_size: int = kernel_size
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene by blurring regions based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where blurring will be applied.
|
||||
scene (ImageType): The image where blurring will be applied.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -1130,24 +1196,28 @@ class TraceAnnotator:
|
||||
self.thickness = thickness
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Draws trace paths on the frame based on the detection coordinates provided.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image on which the traces will be drawn.
|
||||
scene (ImageType): The image on which the traces will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): The detections which include coordinates for
|
||||
which the traces will be drawn.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -1233,16 +1303,20 @@ class HeatMapAnnotator:
|
||||
self.top_hue = top_hue
|
||||
self.low_hue = low_hue
|
||||
|
||||
def annotate(self, scene: np.ndarray, detections: Detections) -> np.ndarray:
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(self, scene: ImageType, detections: Detections) -> ImageType:
|
||||
"""
|
||||
Annotates the scene with a heatmap based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where the heatmap will be drawn.
|
||||
scene (ImageType): The image where the heatmap will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
|
||||
Returns:
|
||||
Annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -1305,21 +1379,25 @@ class PixelateAnnotator(BaseAnnotator):
|
||||
"""
|
||||
self.pixel_size: int = pixel_size
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene by pixelating regions based on the provided
|
||||
detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where pixelating will be applied.
|
||||
scene (ImageType): The image where pixelating will be applied.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -1389,23 +1467,27 @@ class TriangleAnnotator(BaseAnnotator):
|
||||
self.position: Position = position
|
||||
self.color_lookup: ColorLookup = color_lookup
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with triangles based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where triangles will be drawn.
|
||||
scene (ImageType): The image where triangles will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
np.ndarray: The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -1481,24 +1563,28 @@ class RoundBoxAnnotator(BaseAnnotator):
|
||||
raise ValueError("roundness attribute must be float between (0, 1.0]")
|
||||
self.roundness: float = roundness
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with bounding boxes with rounded edges
|
||||
based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where rounded bounding boxes will be drawn.
|
||||
scene (ImageType): The image where rounded bounding boxes will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -1614,20 +1700,23 @@ class PercentageBarAnnotator(BaseAnnotator):
|
||||
if border_thickness is None:
|
||||
self.border_thickness = int(0.15 * self.height)
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
custom_color_lookup: Optional[np.ndarray] = None,
|
||||
custom_values: Optional[np.ndarray] = None,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Annotates the given scene with percentage bars based on the provided
|
||||
detections. The percentage bars visually represent the confidence or custom
|
||||
values associated with each detection.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where percentage bars will be drawn.
|
||||
scene (ImageType): The image where percentage bars will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): Object detections to annotate.
|
||||
custom_color_lookup (Optional[np.ndarray]): Custom color lookup array.
|
||||
Allows to override the default color mapping strategy.
|
||||
@@ -1638,7 +1727,8 @@ class PercentageBarAnnotator(BaseAnnotator):
|
||||
to be displayed.
|
||||
|
||||
Returns:
|
||||
The annotated image.
|
||||
The annotated image, matching the type of `scene` (`numpy.ndarray`
|
||||
or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
@@ -1647,7 +1737,7 @@ class PercentageBarAnnotator(BaseAnnotator):
|
||||
image = ...
|
||||
detections = sv.Detections(...)
|
||||
|
||||
percentage_bar_annotator = sv.BoundingBoxAnnotator()
|
||||
percentage_bar_annotator = sv.PercentageBarAnnotator()
|
||||
annotated_frame = percentage_bar_annotator.annotate(
|
||||
scene=image.copy(),
|
||||
detections=detections
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
from enum import Enum
|
||||
from functools import wraps
|
||||
from typing import Optional, Union
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
from supervision.annotators.base import ImageType
|
||||
from supervision.detection.core import Detections
|
||||
from supervision.draw.color import Color, ColorPalette
|
||||
from supervision.geometry.core import Position
|
||||
@@ -119,3 +123,28 @@ class Trace:
|
||||
|
||||
def get(self, tracker_id: int) -> np.ndarray:
|
||||
return self.xy[self.tracker_id == tracker_id]
|
||||
|
||||
|
||||
def scene_to_annotator_img_type(annotate_func):
|
||||
"""
|
||||
Decorates `BaseAnnotator.annotate` implementations, converts scene to
|
||||
an image type used internally by the annotators, converts back when annotation
|
||||
is complete.
|
||||
"""
|
||||
|
||||
@wraps(annotate_func)
|
||||
def wrapper(self, scene: ImageType, *args, **kwargs):
|
||||
if isinstance(scene, np.ndarray):
|
||||
return annotate_func(self, scene, *args, **kwargs)
|
||||
|
||||
if isinstance(scene, Image.Image):
|
||||
scene = np.array(scene)
|
||||
scene = cv2.cvtColor(scene, cv2.COLOR_RGB2BGR)
|
||||
annotated = annotate_func(self, scene, *args, **kwargs)
|
||||
annotated = cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB)
|
||||
annotated = Image.fromarray(annotated)
|
||||
return annotated
|
||||
|
||||
raise ValueError(f"Unsupported image type: {type(scene)}")
|
||||
|
||||
return wrapper
|
||||
@@ -1,8 +1,9 @@
|
||||
from typing import List, Optional, Union
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from supervision.annotators.base import ImageType
|
||||
from supervision.annotators.utils import scene_to_annotator_img_type
|
||||
from supervision.detection.core import Detections
|
||||
from supervision.draw.color import Color, ColorPalette
|
||||
from supervision.utils.internal import deprecated
|
||||
@@ -45,18 +46,21 @@ class BoxAnnotator:
|
||||
self.text_thickness: int = text_thickness
|
||||
self.text_padding: int = text_padding
|
||||
|
||||
@scene_to_annotator_img_type
|
||||
def annotate(
|
||||
self,
|
||||
scene: np.ndarray,
|
||||
scene: ImageType,
|
||||
detections: Detections,
|
||||
labels: Optional[List[str]] = None,
|
||||
skip_label: bool = False,
|
||||
) -> np.ndarray:
|
||||
) -> ImageType:
|
||||
"""
|
||||
Draws bounding boxes on the frame using the detections provided.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image on which the bounding boxes will be drawn
|
||||
scene (ImageType): The image on which the bounding boxes will be drawn.
|
||||
`ImageType` is a flexible type, accepting either `numpy.ndarray`
|
||||
or `PIL.Image.Image`.
|
||||
detections (Detections): The detections for which the
|
||||
bounding boxes will be drawn
|
||||
labels (Optional[List[str]]): An optional list of labels
|
||||
@@ -64,7 +68,8 @@ class BoxAnnotator:
|
||||
corresponding `class_id` will be used as label.
|
||||
skip_label (bool): Is set to `True`, skips bounding box label annotation.
|
||||
Returns:
|
||||
np.ndarray: The image with the bounding boxes drawn on it
|
||||
ImageType: The image with the bounding boxes drawn on it, matching the
|
||||
type of `scene` (`numpy.ndarray` or `PIL.Image.Image`)
|
||||
|
||||
Example:
|
||||
```python
|
||||
|
||||
@@ -487,7 +487,9 @@ class Detections:
|
||||
)
|
||||
|
||||
if np.asarray(xyxy).shape[0] == 0:
|
||||
return cls.empty()
|
||||
empty_detection = cls.empty()
|
||||
empty_detection.data = {CLASS_NAME_DATA_FIELD: np.empty(0)}
|
||||
return empty_detection
|
||||
|
||||
return cls(
|
||||
xyxy=xyxy,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from dataclasses import replace
|
||||
from typing import Optional, Tuple
|
||||
from typing import Iterable, Optional, Tuple
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
@@ -10,6 +10,7 @@ from supervision.draw.color import Color
|
||||
from supervision.draw.utils import draw_polygon, draw_text
|
||||
from supervision.geometry.core import Position
|
||||
from supervision.geometry.utils import get_polygon_center
|
||||
from supervision.utils.internal import deprecated_parameter
|
||||
|
||||
|
||||
class PolygonZone:
|
||||
@@ -20,21 +21,32 @@ class PolygonZone:
|
||||
polygon (np.ndarray): A polygon represented by a numpy array of shape
|
||||
`(N, 2)`, containing the `x`, `y` coordinates of the points.
|
||||
frame_resolution_wh (Tuple[int, int]): The frame resolution (width, height)
|
||||
triggering_position (Position): The position within the bounding
|
||||
box that triggers the zone (default: Position.BOTTOM_CENTER)
|
||||
triggering_anchors (Iterable[sv.Position]): A list of positions specifying
|
||||
which anchors of the detections bounding box to consider when deciding on
|
||||
whether the detection fits within the PolygonZone
|
||||
(default: (sv.Position.BOTTOM_CENTER,)).
|
||||
current_count (int): The current count of detected objects within the zone
|
||||
mask (np.ndarray): The 2D bool mask for the polygon zone
|
||||
"""
|
||||
|
||||
@deprecated_parameter(
|
||||
old_parameter="triggering_position",
|
||||
new_parameter="triggering_anchors",
|
||||
map_function=lambda x: [x],
|
||||
warning_message="`{old_parameter}` in `{function_name}` is deprecated and will "
|
||||
"be remove in `supervision-0.23.0`. Use '{new_parameter}' "
|
||||
"instead.",
|
||||
)
|
||||
def __init__(
|
||||
self,
|
||||
polygon: np.ndarray,
|
||||
frame_resolution_wh: Tuple[int, int],
|
||||
triggering_position: Position = Position.BOTTOM_CENTER,
|
||||
triggering_anchors: Iterable[Position] = (Position.BOTTOM_CENTER,),
|
||||
):
|
||||
self.polygon = polygon.astype(int)
|
||||
self.frame_resolution_wh = frame_resolution_wh
|
||||
self.triggering_position = triggering_position
|
||||
self.triggering_anchors = triggering_anchors
|
||||
|
||||
self.current_count = 0
|
||||
|
||||
width, height = frame_resolution_wh
|
||||
@@ -59,10 +71,20 @@ class PolygonZone:
|
||||
xyxy=detections.xyxy, resolution_wh=self.frame_resolution_wh
|
||||
)
|
||||
clipped_detections = replace(detections, xyxy=clipped_xyxy)
|
||||
clipped_anchors = np.ceil(
|
||||
clipped_detections.get_anchors_coordinates(anchor=self.triggering_position)
|
||||
).astype(int)
|
||||
is_in_zone = self.mask[clipped_anchors[:, 1], clipped_anchors[:, 0]]
|
||||
all_clipped_anchors = np.array(
|
||||
[
|
||||
np.ceil(clipped_detections.get_anchors_coordinates(anchor)).astype(int)
|
||||
for anchor in self.triggering_anchors
|
||||
]
|
||||
)
|
||||
|
||||
is_in_zone = (
|
||||
self.mask[all_clipped_anchors[:, :, 1], all_clipped_anchors[:, :, 0]]
|
||||
.transpose()
|
||||
.astype(bool)
|
||||
)
|
||||
is_in_zone = np.all(is_in_zone, axis=1)
|
||||
|
||||
self.current_count = int(np.sum(is_in_zone))
|
||||
return is_in_zone.astype(bool)
|
||||
|
||||
@@ -83,6 +105,7 @@ class PolygonZoneAnnotator:
|
||||
font (int): The font type for the text on the polygon,
|
||||
default is cv2.FONT_HERSHEY_SIMPLEX
|
||||
center (Tuple[int, int]): The center of the polygon for text placement
|
||||
display_in_zone_count (bool): Show the label of the zone or not. Default is True
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
@@ -94,6 +117,7 @@ class PolygonZoneAnnotator:
|
||||
text_scale: float = 0.5,
|
||||
text_thickness: int = 1,
|
||||
text_padding: int = 10,
|
||||
display_in_zone_count: bool = True,
|
||||
):
|
||||
self.zone = zone
|
||||
self.color = color
|
||||
@@ -104,6 +128,7 @@ class PolygonZoneAnnotator:
|
||||
self.text_padding = text_padding
|
||||
self.font = cv2.FONT_HERSHEY_SIMPLEX
|
||||
self.center = get_polygon_center(polygon=zone.polygon)
|
||||
self.display_in_zone_count = display_in_zone_count
|
||||
|
||||
def annotate(self, scene: np.ndarray, label: Optional[str] = None) -> np.ndarray:
|
||||
"""
|
||||
@@ -124,16 +149,17 @@ class PolygonZoneAnnotator:
|
||||
thickness=self.thickness,
|
||||
)
|
||||
|
||||
annotated_frame = draw_text(
|
||||
scene=annotated_frame,
|
||||
text=str(self.zone.current_count) if label is None else label,
|
||||
text_anchor=self.center,
|
||||
background_color=self.color,
|
||||
text_color=self.text_color,
|
||||
text_scale=self.text_scale,
|
||||
text_thickness=self.text_thickness,
|
||||
text_padding=self.text_padding,
|
||||
text_font=self.font,
|
||||
)
|
||||
if self.display_in_zone_count:
|
||||
annotated_frame = draw_text(
|
||||
scene=annotated_frame,
|
||||
text=str(self.zone.current_count) if label is None else label,
|
||||
text_anchor=self.center,
|
||||
background_color=self.color,
|
||||
text_color=self.text_color,
|
||||
text_scale=self.text_scale,
|
||||
text_thickness=self.text_thickness,
|
||||
text_padding=self.text_padding,
|
||||
text_font=self.font,
|
||||
)
|
||||
|
||||
return annotated_frame
|
||||
@@ -115,4 +115,8 @@ class DetectionsSmoother:
|
||||
if track is not None:
|
||||
tracked_detections.append(track)
|
||||
|
||||
return Detections.merge(tracked_detections)
|
||||
detections = Detections.merge(tracked_detections)
|
||||
if len(detections) == 0:
|
||||
detections.tracker_id = np.array([], dtype=int)
|
||||
|
||||
return detections
|
||||
@@ -59,8 +59,11 @@ def box_iou_batch(boxes_true: np.ndarray, boxes_detection: np.ndarray) -> np.nda
|
||||
return area_inter / (area_true[:, None] + area_detection - area_inter)
|
||||
|
||||
|
||||
def mask_iou_batch(masks_true: np.ndarray, masks_detection: np.ndarray) -> np.ndarray:
|
||||
def _mask_iou_batch_split(
|
||||
masks_true: np.ndarray, masks_detection: np.ndarray
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
Internal function.
|
||||
Compute Intersection over Union (IoU) of two sets of masks -
|
||||
`masks_true` and `masks_detection`.
|
||||
|
||||
@@ -74,9 +77,9 @@ def mask_iou_batch(masks_true: np.ndarray, masks_detection: np.ndarray) -> np.nd
|
||||
intersection_area = np.logical_and(masks_true[:, None], masks_detection).sum(
|
||||
axis=(2, 3)
|
||||
)
|
||||
|
||||
masks_true_area = masks_true.sum(axis=(1, 2))
|
||||
masks_detection_area = masks_detection.sum(axis=(1, 2))
|
||||
|
||||
union_area = masks_true_area[:, None] + masks_detection_area - intersection_area
|
||||
|
||||
return np.divide(
|
||||
@@ -87,6 +90,52 @@ def mask_iou_batch(masks_true: np.ndarray, masks_detection: np.ndarray) -> np.nd
|
||||
)
|
||||
|
||||
|
||||
def mask_iou_batch(
|
||||
masks_true: np.ndarray,
|
||||
masks_detection: np.ndarray,
|
||||
memory_limit: int = 1024 * 5,
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
Compute Intersection over Union (IoU) of two sets of masks -
|
||||
`masks_true` and `masks_detection`.
|
||||
|
||||
Args:
|
||||
masks_true (np.ndarray): 3D `np.ndarray` representing ground-truth masks.
|
||||
masks_detection (np.ndarray): 3D `np.ndarray` representing detection masks.
|
||||
memory_limit (int, optional): memory limit in MB, default is 1024 * 5 MB (5GB).
|
||||
|
||||
Returns:
|
||||
np.ndarray: Pairwise IoU of masks from `masks_true` and `masks_detection`.
|
||||
"""
|
||||
memory = (
|
||||
masks_true.shape[0]
|
||||
* masks_true.shape[1]
|
||||
* masks_true.shape[2]
|
||||
* masks_detection.shape[0]
|
||||
/ 1024
|
||||
/ 1024
|
||||
)
|
||||
if memory <= memory_limit:
|
||||
return _mask_iou_batch_split(masks_true, masks_detection)
|
||||
|
||||
ious = []
|
||||
step = max(
|
||||
memory_limit
|
||||
* 1024
|
||||
* 1024
|
||||
// (
|
||||
masks_detection.shape[0]
|
||||
* masks_detection.shape[1]
|
||||
* masks_detection.shape[2]
|
||||
),
|
||||
1,
|
||||
)
|
||||
for i in range(0, masks_true.shape[0], step):
|
||||
ious.append(_mask_iou_batch_split(masks_true[i : i + step], masks_detection))
|
||||
|
||||
return np.vstack(ious)
|
||||
|
||||
|
||||
def resize_masks(masks: np.ndarray, max_dimension: int = 640) -> np.ndarray:
|
||||
"""
|
||||
Resize all masks in the array to have a maximum dimension of max_dimension,
|
||||
|
||||
@@ -1,5 +1,77 @@
|
||||
import functools
|
||||
import warnings
|
||||
from typing import Callable
|
||||
|
||||
|
||||
def deprecated_parameter(
|
||||
old_parameter: str,
|
||||
new_parameter: str,
|
||||
map_function: Callable = lambda x: x,
|
||||
warning_message: str = "Warning: '{old_parameter}' in '{function_name}' is "
|
||||
"deprecated: use '{new_parameter}' instead.",
|
||||
**message_kwargs,
|
||||
):
|
||||
"""
|
||||
A decorator to mark a function's parameter as deprecated and issue a warning when
|
||||
used.
|
||||
|
||||
Parameters:
|
||||
old_parameter (str): The name of the deprecated parameter.
|
||||
new_parameter (str): The name of the parameter that should be used instead.
|
||||
map_function (Callable, optional): A function used to map the value of the old
|
||||
parameter to the new parameter. Defaults to the identity function.
|
||||
warning_message (str, optional): The warning message to be displayed when the
|
||||
deprecated parameter is used. Defaults to a generic warning message with
|
||||
placeholders for the old parameter, new parameter, and function name.
|
||||
**message_kwargs: Additional keyword arguments that can be used to customize
|
||||
the warning message.
|
||||
|
||||
Returns:
|
||||
Callable: A decorator function that can be applied to mark a function's
|
||||
parameter as deprecated.
|
||||
|
||||
Examples:
|
||||
```python
|
||||
@deprecated_parameter(
|
||||
old_parameter=<OLD_PARAMETER_NAME>,
|
||||
new_parameter=<NEW_PARAMETER_NAME>
|
||||
)
|
||||
def example_function(<NEW_PARAMETER_NAME>):
|
||||
pass
|
||||
|
||||
# call function using deprecated parameter
|
||||
example_function(<OLD_PARAMETER_NAME>=<OLD_PARAMETER_VALUE>)
|
||||
```
|
||||
"""
|
||||
|
||||
def decorator(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if old_parameter in kwargs:
|
||||
if args and hasattr(args[0], "__class__"):
|
||||
class_name = args[0].__class__.__name__
|
||||
function_name = f"{class_name}.{func.__name__}"
|
||||
else:
|
||||
function_name = func.__name__
|
||||
|
||||
warnings.warn(
|
||||
message=warning_message.format(
|
||||
function_name=function_name,
|
||||
old_parameter=old_parameter,
|
||||
new_parameter=new_parameter,
|
||||
**message_kwargs,
|
||||
),
|
||||
category=DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
kwargs[new_parameter] = map_function(kwargs.pop(old_parameter))
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def deprecated(reason: str):
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
from contextlib import ExitStack as DoesNotRaise
|
||||
from test.test_utils import mock_detections
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import supervision as sv
|
||||
|
||||
DETECTION_BOXES = np.array(
|
||||
[
|
||||
[35.0, 35.0, 65.0, 65.0],
|
||||
[60.0, 60.0, 90.0, 90.0],
|
||||
[85.0, 85.0, 115.0, 115.0],
|
||||
[110.0, 110.0, 140.0, 140.0],
|
||||
[135.0, 135.0, 165.0, 165.0],
|
||||
[160.0, 160.0, 190.0, 190.0],
|
||||
[185.0, 185.0, 215.0, 215.0],
|
||||
[210.0, 210.0, 240.0, 240.0],
|
||||
[235.0, 235.0, 265.0, 265.0],
|
||||
],
|
||||
dtype=np.float32,
|
||||
)
|
||||
|
||||
DETECTIONS = mock_detections(
|
||||
xyxy=DETECTION_BOXES, class_id=np.array([0, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||
)
|
||||
|
||||
POLYGON = np.array([[100, 100], [200, 100], [200, 200], [100, 200]])
|
||||
FRAME_RESOLUTION = (300, 300)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"detections, polygon_zone, expected_results, exception",
|
||||
[
|
||||
(
|
||||
DETECTIONS,
|
||||
sv.PolygonZone(
|
||||
POLYGON,
|
||||
FRAME_RESOLUTION,
|
||||
triggering_anchors=(
|
||||
sv.Position.TOP_LEFT,
|
||||
sv.Position.TOP_RIGHT,
|
||||
sv.Position.BOTTOM_LEFT,
|
||||
sv.Position.BOTTOM_RIGHT,
|
||||
),
|
||||
),
|
||||
np.array(
|
||||
[False, False, False, True, True, True, False, False, False], dtype=bool
|
||||
),
|
||||
DoesNotRaise(),
|
||||
), # Test all four corners
|
||||
(
|
||||
DETECTIONS,
|
||||
sv.PolygonZone(
|
||||
POLYGON,
|
||||
FRAME_RESOLUTION,
|
||||
),
|
||||
np.array(
|
||||
[False, False, True, True, True, True, False, False, False], dtype=bool
|
||||
),
|
||||
DoesNotRaise(),
|
||||
), # Test default behaviour when no anchors are provided
|
||||
(
|
||||
DETECTIONS,
|
||||
sv.PolygonZone(
|
||||
POLYGON, FRAME_RESOLUTION, triggering_position=sv.Position.BOTTOM_CENTER
|
||||
),
|
||||
np.array(
|
||||
[False, False, True, True, True, True, False, False, False], dtype=bool
|
||||
),
|
||||
DoesNotRaise(),
|
||||
), # Test default behaviour with deprecated api.
|
||||
(
|
||||
sv.Detections.empty(),
|
||||
sv.PolygonZone(
|
||||
POLYGON,
|
||||
FRAME_RESOLUTION,
|
||||
),
|
||||
np.array([], dtype=bool),
|
||||
DoesNotRaise(),
|
||||
), # Test empty detections
|
||||
],
|
||||
)
|
||||
def test_polygon_zone_trigger(
|
||||
detections: sv.Detections,
|
||||
polygon_zone: sv.PolygonZone,
|
||||
expected_results: np.ndarray,
|
||||
exception: Exception,
|
||||
) -> None:
|
||||
with exception:
|
||||
in_zone = polygon_zone.trigger(detections)
|
||||
assert np.all(in_zone == expected_results)
|
||||
Reference in new issue
Block a user