Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29532db02f | ||
|
|
8eb827b31d | ||
|
|
33e43bc92a | ||
|
|
8b5b0a8516 | ||
|
|
9a7b4c34ba | ||
|
|
b2ab398029 | ||
|
|
72463f5f89 | ||
|
|
83342ca36f | ||
|
|
48851b5ae5 | ||
|
|
e5894df156 | ||
|
|
49d268c250 | ||
|
|
a7a7dac216 | ||
|
|
8bca45bde4 | ||
|
|
282db45c00 | ||
|
|
1c5006c013 | ||
|
|
9b4fa8cb89 | ||
|
|
333408d33a | ||
|
|
41a19ac4d6 | ||
|
|
fe0df11918 | ||
|
|
375b5f8f54 | ||
|
|
18fe2e99de | ||
|
|
a83d3b4c7e | ||
|
|
89c9f88aeb | ||
|
|
6ef7bf31d3 | ||
|
|
c3a025cc9d | ||
|
|
f489f944b8 | ||
|
|
cfb1e0ea60 | ||
|
|
5efd535e9d | ||
|
|
a550aaa812 | ||
|
|
30994243ae | ||
|
|
99707582bc | ||
|
|
c0b87a6f04 | ||
|
|
d30d80717c | ||
|
|
4733ebe27d | ||
|
|
44e4eeaa69 | ||
|
|
2952286f95 | ||
|
|
c5e1211c74 | ||
|
|
72dc07e332 | ||
|
|
c5c37e4a6c | ||
|
|
8000043bc8 | ||
|
|
14d0189e2c | ||
|
|
68a761349d | ||
|
|
d5d5725cea | ||
|
|
669c0658fe | ||
|
|
bed28e3db4 | ||
|
|
cc93bba741 | ||
|
|
e7c242bdd4 | ||
|
|
89c1b63979 | ||
|
|
456655c2a4 | ||
|
|
f67a67bd7c | ||
|
|
30687609f5 | ||
|
|
ab1b2d6d80 | ||
|
|
5c6323bed0 | ||
|
|
d2fe2ad40c | ||
|
|
5aaa15c894 | ||
|
|
4bb62f06ff | ||
|
|
4e7a763ad9 | ||
|
|
cb225b26e8 | ||
|
|
07ec604102 | ||
|
|
ef43640c88 | ||
|
|
77c126e421 | ||
|
|
edd8b8f41e | ||
|
|
cad38104e0 | ||
|
|
618fadae2a | ||
|
|
71d23619b0 | ||
|
|
a57176568a | ||
|
|
b482784403 | ||
|
|
3b00e04101 | ||
|
|
5d71ab2961 | ||
|
|
5c9e529743 | ||
|
|
3f7043e780 | ||
|
|
c746a7f9e4 | ||
|
|
04629cd7f4 |
@@ -22,6 +22,7 @@ repos:
|
||||
- id: detect-private-key
|
||||
- id: forbid-new-submodules
|
||||
- id: pretty-format-json
|
||||
exclude: demo.ipynb
|
||||
args: ['--autofix', '--no-sort-keys', '--indent=4']
|
||||
- id: end-of-file-fixer
|
||||
- id: mixed-line-ending
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
.PHONY: style check_code_quality
|
||||
|
||||
export PYTHONPATH = .
|
||||
check_dirs := supervision
|
||||
|
||||
style:
|
||||
black $(check_dirs)
|
||||
isort --profile black $(check_dirs)
|
||||
|
||||
check_code_quality:
|
||||
black --check $(check_dirs)
|
||||
isort --check-only --profile black $(check_dirs)
|
||||
# stop the build if there are Python syntax errors or undefined names
|
||||
flake8 $(check_dirs) --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||
# exit-zero treats all errors as warnings. E203 for black, E501 for docstring, W503 for line breaks before logical operators
|
||||
flake8 $(check_dirs) --count --max-line-length=88 --exit-zero --ignore=D --extend-ignore=E203,E501,W503 --statistics
|
||||
|
||||
publish:
|
||||
poetry build
|
||||
twine upload -r testpypi dist/* -u ${PYPI_USERNAME} -p ${PYPI_TEST_PASSWORD} --verbose
|
||||
twine check dist/*
|
||||
twine upload dist/* -u ${PYPI_USERNAME} -p ${PYPI_PASSWORD} --verbose
|
||||
@@ -19,9 +19,14 @@
|
||||
[](https://github.com/roboflow/supervision/blob/main/LICENSE.md)
|
||||
[](https://badge.fury.io/py/supervision)
|
||||
[](https://colab.research.google.com/github/roboflow/supervision/blob/main/demo.ipynb)
|
||||
[](https://discord.gg/GbfgXGJ8Bk)
|
||||
|
||||
</div>
|
||||
|
||||
<a href="https://github.com/roboflow/supervision/issues?q=is%3Aissue+label%3Ahacktoberfest+">
|
||||
<img width="100%" src="https://media.roboflow.com/open-source/supervision/hacktoberfest-banner-3.png">
|
||||
</a>
|
||||
|
||||
## 👋 hello
|
||||
|
||||
**We write your reusable computer vision tools.** Whether you need to load your dataset from your hard drive, draw detections on an image or video, or count how many detections are in a zone. You can count on us! 🤝
|
||||
@@ -305,6 +310,12 @@ Visit our [documentation](https://roboflow.github.io/supervision) page to learn
|
||||
|
||||
We love your input! Please see our [contributing guide](https://github.com/roboflow/supervision/blob/main/CONTRIBUTING.md) to get started. Thank you 🙏 to all our contributors!
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/roboflow/supervision/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=roboflow/supervision" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<br>
|
||||
|
||||
<div align="center">
|
||||
|
||||
Vendored
+528
-528
File diff suppressed because one or more lines are too long
+110
-60
@@ -15,49 +15,7 @@
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Mask"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
|
||||
>>> mask_annotator = sv.MaskAnnotator()
|
||||
>>> annotated_frame = mask_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Ellipse"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
|
||||
>>> ellipse_annotator = sv.EllipseAnnotator()
|
||||
>>> annotated_frame = ellipse_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -78,7 +36,28 @@
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "BoxMask"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
|
||||
>>> box_mask_annotator = sv.BoxMaskAnnotator()
|
||||
>>> annotated_frame = box_mask_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -99,7 +78,70 @@
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Ellipse"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
|
||||
>>> ellipse_annotator = sv.EllipseAnnotator()
|
||||
>>> annotated_frame = ellipse_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Halo"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
|
||||
>>> halo_annotator = sv.HaloAnnotator()
|
||||
>>> annotated_frame = halo_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
=== "Mask"
|
||||
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
|
||||
>>> mask_annotator = sv.MaskAnnotator()
|
||||
>>> annotated_frame = mask_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
```
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -120,7 +162,7 @@
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -141,7 +183,7 @@
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -162,7 +204,7 @@
|
||||
|
||||
<div class="result" markdown>
|
||||
|
||||
{ align=center width="800" }
|
||||
{ align=center width="800" }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -170,30 +212,38 @@
|
||||
|
||||
:::supervision.annotators.core.BoundingBoxAnnotator
|
||||
|
||||
## MaskAnnotator
|
||||
|
||||
:::supervision.annotators.core.MaskAnnotator
|
||||
|
||||
## EllipseAnnotator
|
||||
|
||||
:::supervision.annotators.core.EllipseAnnotator
|
||||
|
||||
## BoxCornerAnnotator
|
||||
|
||||
:::supervision.annotators.core.BoxCornerAnnotator
|
||||
|
||||
## BoxMaskAnnotator
|
||||
|
||||
:::supervision.annotators.core.BoxMaskAnnotator
|
||||
|
||||
## CircleAnnotator
|
||||
|
||||
:::supervision.annotators.core.CircleAnnotator
|
||||
|
||||
## EllipseAnnotator
|
||||
|
||||
:::supervision.annotators.core.EllipseAnnotator
|
||||
|
||||
## HaloAnnotator
|
||||
|
||||
:::supervision.annotators.core.HaloAnnotator
|
||||
|
||||
## MaskAnnotator
|
||||
|
||||
:::supervision.annotators.core.MaskAnnotator
|
||||
|
||||
## LabelAnnotator
|
||||
|
||||
:::supervision.annotators.core.LabelAnnotator
|
||||
|
||||
## TraceAnnotator
|
||||
|
||||
:::supervision.annotators.core.TraceAnnotator
|
||||
|
||||
## BlurAnnotator
|
||||
|
||||
:::supervision.annotators.core.BlurAnnotator
|
||||
|
||||
## TraceAnnotator
|
||||
|
||||
:::supervision.annotators.core.TraceAnnotator
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
🚧 Page under construction.
|
||||
@@ -0,0 +1 @@
|
||||
🚧 Page under construction.
|
||||
@@ -1,6 +1,3 @@
|
||||
## advanced filtering
|
||||
|
||||
|
||||
The advanced filtering capabilities of the `Detections` class offer users a versatile and efficient way to narrow down
|
||||
and refine object detections. This section outlines various filtering methods, including filtering by specific class
|
||||
or a set of classes, confidence, object area, bounding box area, relative area, box dimensions, and designated zones.
|
||||
@@ -0,0 +1 @@
|
||||
🚧 Page under construction.
|
||||
@@ -0,0 +1 @@
|
||||
🚧 Page under construction.
|
||||
@@ -6,6 +6,10 @@
|
||||
|
||||
:::supervision.utils.video.VideoSink
|
||||
|
||||
## FPSMonitor
|
||||
|
||||
:::supervision.utils.video.FPSMonitor
|
||||
|
||||
## get_video_frames_generator
|
||||
|
||||
:::supervision.utils.video.get_video_frames_generator
|
||||
|
||||
+27
-23
@@ -26,29 +26,33 @@ extra_css:
|
||||
|
||||
nav:
|
||||
- Home: index.md
|
||||
- Quickstart:
|
||||
- Detections: quickstart/detections.md
|
||||
- Classifications:
|
||||
- Core: classification/core.md
|
||||
- Detections:
|
||||
- Core: detection/core.md
|
||||
- Annotate: detection/annotate.md
|
||||
- Utils: detection/utils.md
|
||||
- Tools:
|
||||
- Polygon Zone: detection/tools/polygon_zone.md
|
||||
- Inference Slicer: detection/tools/inference_slicer.md
|
||||
- Annotators: annotators.md
|
||||
- Trackers: trackers.md
|
||||
- Datasets: datasets.md
|
||||
- Metrics:
|
||||
- Object Detection: metrics/detection.md
|
||||
- Draw:
|
||||
- Utils: draw/utils.md
|
||||
- Utils:
|
||||
- Video: utils/video.md
|
||||
- Image: utils/image.md
|
||||
- Notebook: utils/notebook.md
|
||||
- File: utils/file.md
|
||||
- How to:
|
||||
- Detect and Annotate: how_to/detect_and_annotate.md
|
||||
- Process Video: how_to/process_video.md
|
||||
- Track Objects: how_to/track_objects.md
|
||||
- Filter Detections: how_to/filter_detections.md
|
||||
- Evaluate Model: how_to/evaluate_model.md
|
||||
- API:
|
||||
- Classifications:
|
||||
- Core: classification/core.md
|
||||
- Detections:
|
||||
- Core: detection/core.md
|
||||
- Utils: detection/utils.md
|
||||
- Tools:
|
||||
- Polygon Zone: detection/tools/polygon_zone.md
|
||||
- Inference Slicer: detection/tools/inference_slicer.md
|
||||
- Annotators: annotators.md
|
||||
- Trackers: trackers.md
|
||||
- Datasets: datasets.md
|
||||
- Metrics:
|
||||
- Object Detection: metrics/detection.md
|
||||
- Draw:
|
||||
- Utils: draw/utils.md
|
||||
- Utils:
|
||||
- Video: utils/video.md
|
||||
- Image: utils/image.md
|
||||
- Notebook: utils/notebook.md
|
||||
- File: utils/file.md
|
||||
- Changelog: changelog.md
|
||||
|
||||
theme:
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "supervision"
|
||||
version = "0.15.0"
|
||||
version = "0.16.0rc2"
|
||||
description = "A set of easy-to-use utils that will come in handy in any Computer Vision project"
|
||||
authors = ["Piotr Skalski <piotr.skalski92@gmail.com>"]
|
||||
maintainers = ["Piotr Skalski <piotr.skalski92@gmail.com>"]
|
||||
|
||||
@@ -10,12 +10,15 @@ from supervision.annotators.core import (
|
||||
BlurAnnotator,
|
||||
BoundingBoxAnnotator,
|
||||
BoxCornerAnnotator,
|
||||
BoxMaskAnnotator,
|
||||
CircleAnnotator,
|
||||
EllipseAnnotator,
|
||||
HaloAnnotator,
|
||||
LabelAnnotator,
|
||||
MaskAnnotator,
|
||||
TraceAnnotator,
|
||||
)
|
||||
from supervision.annotators.utils import ColorMap
|
||||
from supervision.classification.core import Classifications
|
||||
from supervision.dataset.core import (
|
||||
BaseDataset,
|
||||
@@ -46,6 +49,7 @@ from supervision.utils.file import list_files_with_extensions
|
||||
from supervision.utils.image import ImageSink, crop_image
|
||||
from supervision.utils.notebook import plot_image, plot_images_grid
|
||||
from supervision.utils.video import (
|
||||
FPSMonitor,
|
||||
VideoInfo,
|
||||
VideoSink,
|
||||
get_video_frames_generator,
|
||||
|
||||
+167
-13
@@ -65,7 +65,7 @@ class BoundingBoxAnnotator(BaseAnnotator):
|
||||
```
|
||||
|
||||

|
||||
supervision-annotator-examples/bounding-box-annotator-example-purple.png)
|
||||
"""
|
||||
for detection_idx in range(len(detections)):
|
||||
x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
|
||||
@@ -134,7 +134,7 @@ class MaskAnnotator(BaseAnnotator):
|
||||
```
|
||||
|
||||

|
||||
supervision-annotator-examples/mask-annotator-example-purple.png)
|
||||
"""
|
||||
if detections.mask is None:
|
||||
return scene
|
||||
@@ -149,12 +149,166 @@ class MaskAnnotator(BaseAnnotator):
|
||||
mask = detections.mask[detection_idx]
|
||||
colored_mask = np.zeros_like(scene, dtype=np.uint8)
|
||||
colored_mask[:] = color.as_bgr()
|
||||
scene[mask] = cv2.addWeighted(
|
||||
colored_mask, self.opacity, scene, 1 - self.opacity, 0
|
||||
)[mask]
|
||||
|
||||
scene = np.where(
|
||||
np.expand_dims(mask, axis=-1),
|
||||
np.uint8(self.opacity * colored_mask + (1 - self.opacity) * scene),
|
||||
scene,
|
||||
return scene
|
||||
|
||||
|
||||
class BoxMaskAnnotator(BaseAnnotator):
|
||||
"""
|
||||
A class for drawing box masks on an image using provided detections.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
color: Union[Color, ColorPalette] = ColorPalette.default(),
|
||||
opacity: float = 0.5,
|
||||
color_map: str = "class",
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
color (Union[Color, ColorPalette]): The color or color palette to use for
|
||||
annotating detections.
|
||||
opacity (float): Opacity of the overlay mask. Must be between `0` and `1`.
|
||||
color_map (str): Strategy for mapping colors to annotations.
|
||||
Options are `index`, `class`, or `track`.
|
||||
"""
|
||||
self.color: Union[Color, ColorPalette] = color
|
||||
self.color_map: ColorMap = ColorMap(color_map)
|
||||
self.opacity = opacity
|
||||
|
||||
def annotate(self, scene: np.ndarray, detections: Detections) -> np.ndarray:
|
||||
"""
|
||||
Annotates the given scene with box masks based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where bounding boxes will be drawn.
|
||||
detections (Detections): Object detections to annotate.
|
||||
|
||||
Returns:
|
||||
np.ndarray: The annotated image.
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
|
||||
>>> box_mask_annotator = sv.BoxMaskAnnotator()
|
||||
>>> annotated_frame = box_mask_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
```
|
||||
|
||||

|
||||
"""
|
||||
mask_image = scene.copy()
|
||||
for detection_idx in range(len(detections)):
|
||||
x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
|
||||
idx = resolve_color_idx(
|
||||
detections=detections,
|
||||
detection_idx=detection_idx,
|
||||
color_map=self.color_map,
|
||||
)
|
||||
color = resolve_color(color=self.color, idx=idx)
|
||||
cv2.rectangle(
|
||||
img=scene,
|
||||
pt1=(x1, y1),
|
||||
pt2=(x2, y2),
|
||||
color=color.as_bgr(),
|
||||
thickness=-1,
|
||||
)
|
||||
scene = cv2.addWeighted(
|
||||
scene, self.opacity, mask_image, 1 - self.opacity, gamma=0
|
||||
)
|
||||
return scene
|
||||
|
||||
|
||||
class HaloAnnotator(BaseAnnotator):
|
||||
"""
|
||||
A class for drawing Halos on an image using provided detections.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
color: Union[Color, ColorPalette] = ColorPalette.default(),
|
||||
opacity: float = 0.8,
|
||||
kernel_size: int = 40,
|
||||
color_map: str = "class",
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
color (Union[Color, ColorPalette]): The color or color palette to use for
|
||||
annotating detections.
|
||||
opacity (float): Opacity of the overlay mask. Must be between `0` and `1`.
|
||||
color_map (str): Strategy for mapping colors to annotations.
|
||||
Options are `index`, `class`, or `track`.
|
||||
kernel_size (int): The size of the average pooling kernel used for creating
|
||||
the halo.
|
||||
"""
|
||||
self.color: Union[Color, ColorPalette] = color
|
||||
self.opacity = opacity
|
||||
self.color_map: ColorMap = ColorMap(color_map)
|
||||
self.kernel_size: int = kernel_size
|
||||
|
||||
def annotate(self, scene: np.ndarray, detections: Detections) -> np.ndarray:
|
||||
"""
|
||||
Annotates the given scene with halos based on the provided detections.
|
||||
|
||||
Args:
|
||||
scene (np.ndarray): The image where masks will be drawn.
|
||||
detections (Detections): Object detections to annotate.
|
||||
|
||||
Returns:
|
||||
np.ndarray: The annotated image.
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
|
||||
>>> image = ...
|
||||
>>> detections = sv.Detections(...)
|
||||
|
||||
>>> halo_annotator = sv.HaloAnnotator()
|
||||
>>> annotated_frame = halo_annotator.annotate(
|
||||
... scene=image.copy(),
|
||||
... detections=detections
|
||||
... )
|
||||
```
|
||||
|
||||

|
||||
"""
|
||||
if detections.mask is None:
|
||||
return scene
|
||||
colored_mask = np.zeros_like(scene, dtype=np.uint8)
|
||||
fmask = np.array([False] * scene.shape[0] * scene.shape[1]).reshape(
|
||||
scene.shape[0], scene.shape[1]
|
||||
)
|
||||
|
||||
for detection_idx in np.flip(np.argsort(detections.area)):
|
||||
idx = resolve_color_idx(
|
||||
detections=detections,
|
||||
detection_idx=detection_idx,
|
||||
color_map=self.color_map,
|
||||
)
|
||||
color = resolve_color(color=self.color, idx=idx)
|
||||
mask = detections.mask[detection_idx]
|
||||
fmask = np.logical_or(fmask, mask)
|
||||
color_bgr = color.as_bgr()
|
||||
colored_mask[mask] = color_bgr
|
||||
|
||||
colored_mask = cv2.blur(colored_mask, (self.kernel_size, self.kernel_size))
|
||||
colored_mask[fmask] = [0, 0, 0]
|
||||
gray = cv2.cvtColor(colored_mask, cv2.COLOR_BGR2GRAY)
|
||||
alpha = self.opacity * gray / gray.max()
|
||||
alpha_mask = alpha[:, :, np.newaxis]
|
||||
scene = np.uint8(scene * (1 - alpha_mask) + colored_mask * self.opacity)
|
||||
return scene
|
||||
|
||||
|
||||
@@ -213,7 +367,7 @@ class EllipseAnnotator(BaseAnnotator):
|
||||
```
|
||||
|
||||

|
||||
supervision-annotator-examples/ellipse-annotator-example-purple.png)
|
||||
"""
|
||||
for detection_idx in range(len(detections)):
|
||||
x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
|
||||
@@ -249,7 +403,7 @@ class BoxCornerAnnotator(BaseAnnotator):
|
||||
self,
|
||||
color: Union[Color, ColorPalette] = ColorPalette.default(),
|
||||
thickness: int = 4,
|
||||
corner_length: int = 25,
|
||||
corner_length: int = 15,
|
||||
color_map: str = "class",
|
||||
):
|
||||
"""
|
||||
@@ -292,7 +446,7 @@ class BoxCornerAnnotator(BaseAnnotator):
|
||||
```
|
||||
|
||||

|
||||
supervision-annotator-examples/box-corner-annotator-example-purple.png)
|
||||
"""
|
||||
for detection_idx in range(len(detections)):
|
||||
x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
|
||||
@@ -372,7 +526,7 @@ class CircleAnnotator(BaseAnnotator):
|
||||
|
||||
|
||||

|
||||
supervision-annotator-examples/circle-annotator-example-purple.png)
|
||||
"""
|
||||
for detection_idx in range(len(detections)):
|
||||
x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
|
||||
@@ -512,7 +666,7 @@ class LabelAnnotator:
|
||||
```
|
||||
|
||||

|
||||
supervision-annotator-examples/label-annotator-example-purple.png)
|
||||
"""
|
||||
font = cv2.FONT_HERSHEY_SIMPLEX
|
||||
for detection_idx in range(len(detections)):
|
||||
@@ -607,7 +761,7 @@ class BlurAnnotator(BaseAnnotator):
|
||||
```
|
||||
|
||||

|
||||
supervision-annotator-examples/blur-annotator-example-purple.png)
|
||||
"""
|
||||
for detection_idx in range(len(detections)):
|
||||
x1, y1, x2, y2 = detections.xyxy[detection_idx].astype(int)
|
||||
@@ -683,7 +837,7 @@ class TraceAnnotator:
|
||||
```
|
||||
|
||||

|
||||
supervision-annotator-examples/trace-annotator-example-purple.png)
|
||||
"""
|
||||
self.trace.put(detections)
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ from typing import Any, Optional, Tuple
|
||||
|
||||
import numpy as np
|
||||
|
||||
from supervision.utils.internal import deprecated
|
||||
|
||||
|
||||
def _validate_class_ids(class_id: Any, n: int) -> None:
|
||||
"""
|
||||
@@ -41,41 +39,6 @@ class Classifications:
|
||||
_validate_class_ids(self.class_id, n)
|
||||
_validate_confidence(self.confidence, n)
|
||||
|
||||
@classmethod
|
||||
@deprecated(
|
||||
"""
|
||||
This method is deprecated and removed in 0.16.0 release.
|
||||
Use sv.Classifications.from_ultralytics() instead as it is more generic and
|
||||
can be used for detections from any ultralytics.engine.results.Results Object
|
||||
"""
|
||||
)
|
||||
def from_yolov8(cls, yolov8_results) -> Classifications:
|
||||
"""
|
||||
Creates a Classifications instance from a
|
||||
[YOLOv8](https://github.com/ultralytics/ultralytics) inference result.
|
||||
|
||||
Args:
|
||||
yolov8_results (ultralytics.yolo.engine.results.Results):
|
||||
The output Results instance from YOLOv8
|
||||
|
||||
Returns:
|
||||
Classifications: A new Classifications object.
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import cv2
|
||||
>>> from ultralytics import YOLO
|
||||
>>> import supervision as sv
|
||||
|
||||
>>> image = cv2.imread(SOURCE_IMAGE_PATH)
|
||||
>>> model = YOLO('yolov8s-cls.pt')
|
||||
>>> result = model(image)[0]
|
||||
>>> classifications = sv.Classifications.from_yolov8(result)
|
||||
```
|
||||
"""
|
||||
confidence = yolov8_results.probs.data.cpu().numpy()
|
||||
return cls(class_id=np.arange(confidence.shape[0]), confidence=confidence)
|
||||
|
||||
@classmethod
|
||||
def from_ultralytics(cls, ultralytics_results) -> Classifications:
|
||||
"""
|
||||
@@ -84,7 +47,7 @@ class Classifications:
|
||||
|
||||
Args:
|
||||
ultralytics_results (ultralytics.engine.results.Results):
|
||||
The output Results instance from ultralytics model
|
||||
The output Results instance from ultralytics model
|
||||
|
||||
Returns:
|
||||
Classifications: A new Classifications object.
|
||||
|
||||
@@ -165,9 +165,8 @@ class DetectionDataset(BaseDataset):
|
||||
where the images should be saved.
|
||||
If not provided, images will not be saved.
|
||||
annotations_directory_path (Optional[str]): The path to
|
||||
the directory where the annotations in
|
||||
PASCAL VOC format should be saved. If not provided,
|
||||
annotations will not be saved.
|
||||
the directory where the annotations in PASCAL VOC format should be
|
||||
saved. If not provided, annotations will not be saved.
|
||||
min_image_area_percentage (float): The minimum percentage of
|
||||
detection area relative to
|
||||
the image area for a detection to be included.
|
||||
@@ -178,7 +177,7 @@ class DetectionDataset(BaseDataset):
|
||||
Argument is used only for segmentation datasets.
|
||||
approximation_percentage (float): The percentage of
|
||||
polygon points to be removed from the input polygon,
|
||||
in the range [0, 1). Argument is used only for segmentation datasets.
|
||||
in the range [0, 1). Argument is used only for segmentation datasets.
|
||||
"""
|
||||
if images_directory_path:
|
||||
save_dataset_images(
|
||||
@@ -563,8 +562,7 @@ class ClassificationDataset(BaseDataset):
|
||||
split_ratio (float, optional): The ratio of the training
|
||||
set to the entire dataset.
|
||||
random_state (int, optional): The seed for the
|
||||
random number generator.
|
||||
This is used for reproducibility.
|
||||
random number generator. This is used for reproducibility.
|
||||
shuffle (bool, optional): Whether to shuffle the data before splitting.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -12,7 +12,6 @@ from supervision.detection.utils import (
|
||||
xywh_to_xyxy,
|
||||
)
|
||||
from supervision.geometry.core import Position
|
||||
from supervision.utils.internal import deprecated
|
||||
|
||||
|
||||
def _validate_xyxy(xyxy: Any, n: int) -> None:
|
||||
@@ -185,46 +184,6 @@ class Detections:
|
||||
class_id=yolov5_detections_predictions[:, 5].astype(int),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@deprecated(
|
||||
"""
|
||||
This method is deprecated and removed in 0.15.0 release.
|
||||
Use sv.Detections.from_ultralytics() instead as it is more generic and
|
||||
can be used for detections from any ultralytics.engine.results.Results Object
|
||||
"""
|
||||
)
|
||||
def from_yolov8(cls, yolov8_results) -> Detections:
|
||||
"""
|
||||
Creates a Detections instance from a
|
||||
[YOLOv8](https://github.com/ultralytics/ultralytics) inference result.
|
||||
|
||||
Args:
|
||||
yolov8_results (ultralytics.yolo.engine.results.Results):
|
||||
The output Results instance from YOLOv8
|
||||
|
||||
Returns:
|
||||
Detections: A new Detections object.
|
||||
|
||||
Example:
|
||||
```python
|
||||
>>> import cv2
|
||||
>>> from ultralytics import YOLO
|
||||
>>> import supervision as sv
|
||||
|
||||
>>> image = cv2.imread(SOURCE_IMAGE_PATH)
|
||||
>>> model = YOLO('yolov8s.pt')
|
||||
>>> result = model(image)[0]
|
||||
>>> detections = sv.Detections.from_yolov8(result)
|
||||
```
|
||||
"""
|
||||
|
||||
return cls(
|
||||
xyxy=yolov8_results.boxes.xyxy.cpu().numpy(),
|
||||
confidence=yolov8_results.boxes.conf.cpu().numpy(),
|
||||
class_id=yolov8_results.boxes.cls.cpu().numpy().astype(int),
|
||||
mask=extract_ultralytics_masks(yolov8_results),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_ultralytics(cls, ultralytics_results) -> Detections:
|
||||
"""
|
||||
@@ -296,7 +255,7 @@ class Detections:
|
||||
>>> detections = sv.Detections.from_yolo_nas(result)
|
||||
```
|
||||
"""
|
||||
if np.asarray(yolo_nas_results.bboxes_xyxy).shape[0] == 0:
|
||||
if np.asarray(yolo_nas_results.prediction.bboxes_xyxy).shape[0] == 0:
|
||||
return cls.empty()
|
||||
|
||||
return cls(
|
||||
@@ -467,7 +426,7 @@ class Detections:
|
||||
>>> detections = sv.Detections.from_roboflow(roboflow_result)
|
||||
```
|
||||
"""
|
||||
xyxy, confidence, class_id, masks = process_roboflow_result(
|
||||
xyxy, confidence, class_id, masks, trackers = process_roboflow_result(
|
||||
roboflow_result=roboflow_result
|
||||
)
|
||||
|
||||
@@ -479,6 +438,7 @@ class Detections:
|
||||
confidence=confidence,
|
||||
class_id=class_id,
|
||||
mask=masks,
|
||||
tracker_id=trackers,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -333,14 +333,15 @@ def extract_ultralytics_masks(yolov8_results) -> Optional[np.ndarray]:
|
||||
|
||||
def process_roboflow_result(
|
||||
roboflow_result: dict,
|
||||
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, Optional[np.ndarray]]:
|
||||
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, Optional[np.ndarray], np.ndarray]:
|
||||
if not roboflow_result["predictions"]:
|
||||
return np.empty((0, 4)), np.empty(0), np.empty(0), None
|
||||
return np.empty((0, 4)), np.empty(0), np.empty(0), None, None
|
||||
|
||||
xyxy = []
|
||||
confidence = []
|
||||
class_id = []
|
||||
masks = []
|
||||
tracker_ids = []
|
||||
|
||||
image_width = int(roboflow_result["image"]["width"])
|
||||
image_height = int(roboflow_result["image"]["height"])
|
||||
@@ -359,6 +360,8 @@ def process_roboflow_result(
|
||||
xyxy.append([x_min, y_min, x_max, y_max])
|
||||
class_id.append(prediction["class_id"])
|
||||
confidence.append(prediction["confidence"])
|
||||
if "tracker_id" in prediction:
|
||||
tracker_ids.append(prediction["tracker_id"])
|
||||
elif len(prediction["points"]) >= 3:
|
||||
polygon = np.array(
|
||||
[[point["x"], point["y"]] for point in prediction["points"]], dtype=int
|
||||
@@ -368,13 +371,16 @@ def process_roboflow_result(
|
||||
class_id.append(prediction["class_id"])
|
||||
confidence.append(prediction["confidence"])
|
||||
masks.append(mask)
|
||||
if "tracker_id" in prediction:
|
||||
tracker_ids.append(prediction["tracker_id"])
|
||||
|
||||
xyxy = np.array(xyxy) if len(xyxy) > 0 else np.empty((0, 4))
|
||||
confidence = np.array(confidence) if len(confidence) > 0 else np.empty(0)
|
||||
class_id = np.array(class_id).astype(int) if len(class_id) > 0 else np.empty(0)
|
||||
masks = np.array(masks, dtype=bool) if len(masks) > 0 else None
|
||||
tracker_id = np.array(tracker_ids).astype(int) if len(tracker_ids) > 0 else None
|
||||
|
||||
return xyxy, confidence, class_id, masks
|
||||
return xyxy, confidence, class_id, masks, tracker_id
|
||||
|
||||
|
||||
def move_boxes(xyxy: np.ndarray, offset: np.ndarray) -> np.ndarray:
|
||||
|
||||
@@ -4,6 +4,7 @@ from dataclasses import dataclass
|
||||
from typing import List, Tuple
|
||||
|
||||
DEFAULT_COLOR_PALETTE = [
|
||||
"#a351fb",
|
||||
"#e6194b",
|
||||
"#3cb44b",
|
||||
"#ffe119",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import List, Tuple
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -11,7 +11,7 @@ from supervision.tracker.byte_tracker.kalman_filter import KalmanFilter
|
||||
class STrack(BaseTrack):
|
||||
shared_kalman = KalmanFilter()
|
||||
|
||||
def __init__(self, tlwh, score, class_ids):
|
||||
def __init__(self, tlwh, score, class_ids, mask: Optional[np.array] = None):
|
||||
# wait activate
|
||||
self._tlwh = np.asarray(tlwh, dtype=np.float32)
|
||||
self.kalman_filter = None
|
||||
@@ -21,6 +21,7 @@ class STrack(BaseTrack):
|
||||
self.score = score
|
||||
self.class_ids = class_ids
|
||||
self.tracklet_len = 0
|
||||
self.mask = mask
|
||||
|
||||
def predict(self):
|
||||
mean_state = self.mean.copy()
|
||||
@@ -74,6 +75,7 @@ class STrack(BaseTrack):
|
||||
if new_id:
|
||||
self.track_id = self.next_id()
|
||||
self.score = new_track.score
|
||||
self.mask = new_track.mask
|
||||
|
||||
def update(self, new_track, frame_id):
|
||||
"""
|
||||
@@ -95,6 +97,8 @@ class STrack(BaseTrack):
|
||||
|
||||
self.score = new_track.score
|
||||
|
||||
self.mask = new_track.mask
|
||||
|
||||
@property
|
||||
def tlwh(self):
|
||||
"""Get current position in bounding box format `(top left x, top left y,
|
||||
@@ -231,9 +235,8 @@ class ByteTrack:
|
||||
... )
|
||||
```
|
||||
"""
|
||||
|
||||
tracks = self.update_with_tensors(
|
||||
tensors=detections2boxes(detections=detections)
|
||||
tensors=detections2boxes(detections=detections), masks=detections.mask
|
||||
)
|
||||
detections = Detections.empty()
|
||||
if len(tracks) > 0:
|
||||
@@ -249,15 +252,22 @@ class ByteTrack:
|
||||
detections.confidence = np.array(
|
||||
[t.score for t in tracks], dtype=np.float32
|
||||
)
|
||||
detections.mask = np.array([t.mask for t in tracks], dtype=bool)
|
||||
|
||||
else:
|
||||
detections.tracker_id = np.array([], dtype=int)
|
||||
|
||||
return detections
|
||||
|
||||
def update_with_tensors(self, tensors: np.ndarray) -> List[STrack]:
|
||||
def update_with_tensors(
|
||||
self, tensors: np.ndarray, masks: Optional[np.array] = None
|
||||
) -> List[STrack]:
|
||||
"""
|
||||
Updates the tracker with the provided tensors and returns the updated tracks.
|
||||
|
||||
Parameters:
|
||||
tensors: The new tensors to update with.
|
||||
masks: The new masks associated to new tensors
|
||||
|
||||
Returns:
|
||||
List[STrack]: Updated tracks.
|
||||
@@ -279,6 +289,12 @@ class ByteTrack:
|
||||
inds_second = np.logical_and(inds_low, inds_high)
|
||||
dets_second = bboxes[inds_second]
|
||||
dets = bboxes[remain_inds]
|
||||
if masks is not None:
|
||||
masks_keep = masks[remain_inds]
|
||||
masks_second = masks[inds_second]
|
||||
else:
|
||||
masks_keep = np.array([None] * len(remain_inds))
|
||||
masks_second = np.array([None] * len(inds_second))
|
||||
scores_keep = scores[remain_inds]
|
||||
scores_second = scores[inds_second]
|
||||
|
||||
@@ -288,8 +304,10 @@ class ByteTrack:
|
||||
if len(dets) > 0:
|
||||
"""Detections"""
|
||||
detections = [
|
||||
STrack(STrack.tlbr_to_tlwh(tlbr), s, c)
|
||||
for (tlbr, s, c) in zip(dets, scores_keep, class_ids_keep)
|
||||
STrack(STrack.tlbr_to_tlwh(tlbr), s, c, m)
|
||||
for (tlbr, s, c, m) in zip(
|
||||
dets, scores_keep, class_ids_keep, masks_keep
|
||||
)
|
||||
]
|
||||
else:
|
||||
detections = []
|
||||
@@ -329,8 +347,10 @@ class ByteTrack:
|
||||
if len(dets_second) > 0:
|
||||
"""Detections"""
|
||||
detections_second = [
|
||||
STrack(STrack.tlbr_to_tlwh(tlbr), s, c)
|
||||
for (tlbr, s, c) in zip(dets_second, scores_second, class_ids_second)
|
||||
STrack(STrack.tlbr_to_tlwh(tlbr), s, c, m)
|
||||
for (tlbr, s, c, m) in zip(
|
||||
dets_second, scores_second, class_ids_second, masks_second
|
||||
)
|
||||
]
|
||||
else:
|
||||
detections_second = []
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from collections import deque
|
||||
from dataclasses import dataclass
|
||||
from typing import Callable, Generator, Optional, Tuple
|
||||
|
||||
@@ -71,13 +73,11 @@ class VideoSink:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
|
||||
>>> video_info = sv.VideoInfo.from_video_path(video_path='source_video.mp4')
|
||||
>>> video_info = sv.VideoInfo.from_video_path('source.mp4')
|
||||
>>> frames_generator = get_video_frames_generator('source.mp4')
|
||||
|
||||
>>> with sv.VideoSink(target_path='target_video.mp4',
|
||||
... video_info=video_info,
|
||||
codec='H264') as sink:
|
||||
... for frame in get_video_frames_generator(source_path='source_video.mp4',
|
||||
... stride=2):
|
||||
>>> with sv.VideoSink(target_path='target.mp4', video_info=video_info) as sink:
|
||||
... for frame in frames_generator:
|
||||
... sink.write_frame(frame=frame)
|
||||
```
|
||||
"""
|
||||
@@ -202,3 +202,55 @@ def process_video(
|
||||
):
|
||||
result_frame = callback(frame, index)
|
||||
sink.write_frame(frame=result_frame)
|
||||
|
||||
|
||||
class FPSMonitor:
|
||||
"""
|
||||
A class for monitoring frames per second (FPS) to benchmark latency.
|
||||
"""
|
||||
|
||||
def __init__(self, sample_size: int = 30):
|
||||
"""
|
||||
Args:
|
||||
sample_size (int): The maximum number of observations for latency
|
||||
benchmarking.
|
||||
|
||||
Examples:
|
||||
```python
|
||||
>>> import supervision as sv
|
||||
|
||||
>>> frames_generator = sv.get_video_frames_generator('source.mp4')
|
||||
>>> fps_monitor = sv.FPSMonitor()
|
||||
|
||||
>>> for frame in frames_generator:
|
||||
... # your processing code here
|
||||
... fps_monitor.tick()
|
||||
... fps = fps_monitor()
|
||||
```
|
||||
"""
|
||||
self.all_timestamps = deque(maxlen=sample_size)
|
||||
|
||||
def __call__(self) -> float:
|
||||
"""
|
||||
Computes and returns the average FPS based on the stored time stamps.
|
||||
|
||||
Returns:
|
||||
float: The average FPS. Returns 0.0 if no time stamps are stored.
|
||||
"""
|
||||
|
||||
if not self.all_timestamps:
|
||||
return 0.0
|
||||
taken_time = self.all_timestamps[-1] - self.all_timestamps[0]
|
||||
return (len(self.all_timestamps)) / taken_time if taken_time != 0 else 0.0
|
||||
|
||||
def tick(self) -> None:
|
||||
"""
|
||||
Adds a new time stamp to the deque for FPS calculation.
|
||||
"""
|
||||
self.all_timestamps.append(time.monotonic())
|
||||
|
||||
def reset(self) -> None:
|
||||
"""
|
||||
Clears all the time stamps from the deque.
|
||||
"""
|
||||
self.all_timestamps.clear()
|
||||
|
||||
@@ -264,7 +264,7 @@ def test_filter_polygons_by_area(
|
||||
[
|
||||
(
|
||||
{"predictions": [], "image": {"width": 1000, "height": 1000}},
|
||||
(np.empty((0, 4)), np.empty(0), np.empty(0), None),
|
||||
(np.empty((0, 4)), np.empty(0), np.empty(0), None, None),
|
||||
DoesNotRaise(),
|
||||
), # empty result
|
||||
(
|
||||
@@ -287,6 +287,7 @@ def test_filter_polygons_by_area(
|
||||
np.array([0.9]),
|
||||
np.array([0]),
|
||||
None,
|
||||
None,
|
||||
),
|
||||
DoesNotRaise(),
|
||||
), # single correct object detection result
|
||||
@@ -301,6 +302,7 @@ def test_filter_polygons_by_area(
|
||||
"confidence": 0.9,
|
||||
"class_id": 0,
|
||||
"class": "person",
|
||||
"tracker_id": 1,
|
||||
},
|
||||
{
|
||||
"x": 500.0,
|
||||
@@ -310,6 +312,7 @@ def test_filter_polygons_by_area(
|
||||
"confidence": 0.8,
|
||||
"class_id": 7,
|
||||
"class": "truck",
|
||||
"tracker_id": 2,
|
||||
},
|
||||
],
|
||||
"image": {"width": 1000, "height": 1000},
|
||||
@@ -319,6 +322,7 @@ def test_filter_polygons_by_area(
|
||||
np.array([0.9, 0.8]),
|
||||
np.array([0, 7]),
|
||||
None,
|
||||
np.array([1, 2]),
|
||||
),
|
||||
DoesNotRaise(),
|
||||
), # two correct object detection result
|
||||
@@ -334,11 +338,12 @@ def test_filter_polygons_by_area(
|
||||
"class_id": 0,
|
||||
"class": "person",
|
||||
"points": [],
|
||||
"tracker_id": None,
|
||||
}
|
||||
],
|
||||
"image": {"width": 1000, "height": 1000},
|
||||
},
|
||||
(np.empty((0, 4)), np.empty(0), np.empty(0), None),
|
||||
(np.empty((0, 4)), np.empty(0), np.empty(0), None, None),
|
||||
DoesNotRaise(),
|
||||
), # single incorrect instance segmentation result with no points
|
||||
(
|
||||
@@ -357,7 +362,7 @@ def test_filter_polygons_by_area(
|
||||
],
|
||||
"image": {"width": 1000, "height": 1000},
|
||||
},
|
||||
(np.empty((0, 4)), np.empty(0), np.empty(0), None),
|
||||
(np.empty((0, 4)), np.empty(0), np.empty(0), None, None),
|
||||
DoesNotRaise(),
|
||||
), # single incorrect instance segmentation result with no enough points
|
||||
(
|
||||
@@ -386,6 +391,7 @@ def test_filter_polygons_by_area(
|
||||
np.array([0.9]),
|
||||
np.array([0]),
|
||||
TEST_MASK,
|
||||
None,
|
||||
),
|
||||
DoesNotRaise(),
|
||||
), # single incorrect instance segmentation result with no enough points
|
||||
@@ -425,6 +431,7 @@ def test_filter_polygons_by_area(
|
||||
np.array([0.9]),
|
||||
np.array([0]),
|
||||
TEST_MASK,
|
||||
None,
|
||||
),
|
||||
DoesNotRaise(),
|
||||
), # two instance segmentation results - one correct, one incorrect
|
||||
@@ -432,7 +439,9 @@ def test_filter_polygons_by_area(
|
||||
)
|
||||
def test_process_roboflow_result(
|
||||
roboflow_result: dict,
|
||||
expected_result: Tuple[np.ndarray, np.ndarray, np.ndarray, Optional[np.ndarray]],
|
||||
expected_result: Tuple[
|
||||
np.ndarray, np.ndarray, np.ndarray, Optional[np.ndarray], np.ndarray
|
||||
],
|
||||
exception: Exception,
|
||||
) -> None:
|
||||
with exception:
|
||||
@@ -443,6 +452,9 @@ def test_process_roboflow_result(
|
||||
assert (result[3] is None and expected_result[3] is None) or (
|
||||
np.array_equal(result[3], expected_result[3])
|
||||
)
|
||||
assert (result[4] is None and expected_result[4] is None) or (
|
||||
np.array_equal(result[4], expected_result[4])
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
Reference in New Issue
Block a user