fix(pre_commit): 🎨 auto format pre-commit hooks

This commit is contained in:
pre-commit-ci[bot] 2024-08-12 08:17:19 +00:00
parent 4c903cf7f2
commit d6d71a0366
4 changed files with 109 additions and 149 deletions

View File

@ -1,2 +1,2 @@
from supervision.metrics.core import UnsupportedMetricTargetError, Metric, MetricTarget
from supervision.metrics.intersection_over_union import IntersectionOverUnion
from supervision.metrics.core import Metric, MetricTarget, UnsupportedMetricTargetError
from supervision.metrics.intersection_over_union import IntersectionOverUnion

View File

@ -1,4 +1,5 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from enum import Enum
from typing import Any, Dict, Iterator, Optional, Tuple, Union
@ -12,6 +13,7 @@ from supervision.detection.core import Detections
"""Used by metrics module as class ID, when none is present"""
CLASS_ID_NONE = -1
class Metric(ABC):
"""
The base class for all supervision metrics.
@ -43,7 +45,7 @@ class Metric(ABC):
Compute the metric from the internal state and return the result.
"""
raise NotImplementedError
# TODO: determine if this is necessary.
# @abstractmethod
# def to_pandas(self, *args, **kwargs) -> Any:
@ -91,11 +93,7 @@ class InternalMetricDataStore:
* Provides iteration by class
"""
def __init__(
self,
metric_target: MetricTarget,
class_agnostic: bool
):
def __init__(self, metric_target: MetricTarget, class_agnostic: bool):
self._metric_target = metric_target
self._class_agnostic = class_agnostic
self._data_1: Dict[int, npt.NDArray]
@ -133,8 +131,10 @@ class InternalMetricDataStore:
if class_id not in self._data_1:
self._data_1[class_id] = content_of_class
continue
self._data_1[class_id] = np.vstack((self._data_1[class_id], content_of_class))
self._data_1[class_id] = np.vstack(
(self._data_1[class_id], content_of_class)
)
if content_2 is not None and len(content_2) > 0:
assert len(content_2) == len(class_ids_2)
for class_id in set(class_ids_2):
@ -142,17 +142,26 @@ class InternalMetricDataStore:
if class_id not in self._data_2:
self._data_2[class_id] = content_of_class
continue
self._data_2[class_id] = np.vstack((self._data_2[class_id], content_of_class))
self._data_2[class_id] = np.vstack(
(self._data_2[class_id], content_of_class)
)
def __iter__(self) -> Iterator[Tuple[int, Optional[npt.NDArray], Optional[npt.NDArray]]]:
class_ids = sorted(set.union(
set(self._data_1.keys()),
set(self._data_2.keys())
))
def __iter__(
self,
) -> Iterator[Tuple[int, Optional[npt.NDArray], Optional[npt.NDArray]]]:
class_ids = sorted(
set.union(set(self._data_1.keys()), set(self._data_2.keys()))
)
for class_id in class_ids:
yield class_id, self._data_1.get(class_id, None), self._data_2.get(class_id, None)
yield (
class_id,
self._data_1.get(class_id, None),
self._data_2.get(class_id, None),
)
def _get_content(self, data: Union[npt.NDArray, Detections]) -> Optional[npt.NDArray]:
def _get_content(
self, data: Union[npt.NDArray, Detections]
) -> Optional[npt.NDArray]:
"""Return boxes, masks or oriented bounding boxes from the data."""
if isinstance(data, np.ndarray):
return data
@ -169,7 +178,9 @@ class InternalMetricDataStore:
return obb
raise ValueError(f"Invalid metric target: {self._metric_target}")
def _get_class_ids(self, data: Union[npt.NDArray, Detections]) -> npt.NDArray[np.int_]:
def _get_class_ids(
self, data: Union[npt.NDArray, Detections]
) -> npt.NDArray[np.int_]:
if self._class_agnostic or isinstance(data, np.ndarray):
return np.array([CLASS_ID_NONE] * len(data), dtype=int)
assert isinstance(data, Detections)
@ -180,7 +191,9 @@ class InternalMetricDataStore:
def _validate_class_ids(self, class_id: npt.NDArray[np.int_]) -> None:
class_set = set(class_id)
if len(class_set) >= 2 and -1 in class_set:
raise ValueError("Metrics store received results with partially defined classes.")
raise ValueError(
"Metrics store received results with partially defined classes."
)
def _validate_shape(self, data: npt.NDArray) -> None:
if self._datapoint_shape is None:

View File

@ -1,14 +1,13 @@
from typing import Dict, Union, TYPE_CHECKING
from typing import TYPE_CHECKING, Dict, Union
import numpy as np
import numpy.typing as npt
import supervision.config as config
from supervision.detection.core import Detections
from supervision.metrics.core import InternalMetricDataStore, Metric, MetricTarget
if TYPE_CHECKING:
import pandas as pd
pass
class IntersectionOverUnion(Metric):

View File

@ -1,5 +1,4 @@
from contextlib import ExitStack as DoesNotRaise
from test.test_utils import mock_detections
from typing import List, Optional, Tuple, Union
import numpy as np
@ -8,144 +7,117 @@ import pytest
from supervision.detection.core import Detections
from supervision.metrics.core import (
CLASS_ID_NONE,
InternalMetricDataStore,
MetricTarget,
CLASS_ID_NONE
)
# Boxes, class-agnostic
# Boxes, class-agnostic
@pytest.mark.parametrize(
"data_1, data_2, expected_result, exception",
[
# Empty
(
Detections.empty(),
Detections.empty(),
[],
DoesNotRaise()
),
(Detections.empty(), Detections.empty(), [], DoesNotRaise()),
(
np.empty((0, 4), dtype=np.float32),
np.empty((0, 4), dtype=np.float32),
[],
DoesNotRaise()
DoesNotRaise(),
),
(
Detections.empty(),
np.empty((0, 4), dtype=np.float32),
[],
DoesNotRaise()
),
(Detections.empty(), np.empty((0, 4), dtype=np.float32), [], DoesNotRaise()),
# Single box + Empty
(
Detections(
xyxy=np.array([[0, 0, 1, 1]], dtype=np.float32)
),
Detections(xyxy=np.array([[0, 0, 1, 1]], dtype=np.float32)),
Detections.empty(),
[
(
CLASS_ID_NONE,
np.array([[0, 0, 1, 1]], dtype=np.float32),
None
)
],
DoesNotRaise()
[(CLASS_ID_NONE, np.array([[0, 0, 1, 1]], dtype=np.float32), None)],
DoesNotRaise(),
),
(
Detections.empty(),
Detections(
xyxy=np.array([[0, 0, 1, 1]], dtype=np.float32)
),
[
(
CLASS_ID_NONE,
None,
np.array([[0, 0, 1, 1]], dtype=np.float32)
)
],
DoesNotRaise()
Detections(xyxy=np.array([[0, 0, 1, 1]], dtype=np.float32)),
[(CLASS_ID_NONE, None, np.array([[0, 0, 1, 1]], dtype=np.float32))],
DoesNotRaise(),
),
# Multiple boxes
(
Detections(
xyxy=np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32)
),
Detections(xyxy=np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32)),
Detections.empty(),
[
(
CLASS_ID_NONE,
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
None
None,
)
],
DoesNotRaise()
DoesNotRaise(),
),
(
Detections(
xyxy=np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32)
),
Detections(
xyxy=np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32)
),
Detections(xyxy=np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32)),
Detections(xyxy=np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32)),
[
(
CLASS_ID_NONE,
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32)
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
)
],
DoesNotRaise()
DoesNotRaise(),
),
(
Detections(
xyxy=np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32)
),
Detections(xyxy=np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32)),
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
[
(
CLASS_ID_NONE,
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32)
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
)
],
DoesNotRaise()
DoesNotRaise(),
),
# with classes - should be ignored.
(
Detections(
xyxy=np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
class_id=np.array([1, 2], dtype=int)
class_id=np.array([1, 2], dtype=int),
),
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
[
(
CLASS_ID_NONE,
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32)
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
)
],
DoesNotRaise()
DoesNotRaise(),
),
]
],
)
def test_store_boxes_class_agnostic(
data_1: Union[npt.NDArray, Detections],
data_2: Union[npt.NDArray, Detections],
expected_result: List[Tuple[int, Optional[npt.NDArray], Optional[npt.NDArray]]],
exception: Exception
exception: Exception,
) -> None:
store = InternalMetricDataStore(MetricTarget.BOXES, class_agnostic=True)
store.update(data_1, data_2)
result = [result for result in store]
assert len(result) == len(expected_result)
with exception:
for (class_id, content_1, content_2), (expected_class_id, expected_content_1, expected_content_2) in zip(result, expected_result):
for (class_id, content_1, content_2), (
expected_class_id,
expected_content_1,
expected_content_2,
) in zip(result, expected_result):
assert class_id == expected_class_id
assert (content_1 is None and expected_content_1 is None) or np.array_equal(content_1, expected_content_1)
assert (content_2 is None and expected_content_2 is None) or np.array_equal(content_2, expected_content_2)
assert (content_1 is None and expected_content_1 is None) or np.array_equal(
content_1, expected_content_1
)
assert (content_2 is None and expected_content_2 is None) or np.array_equal(
content_2, expected_content_2
)
# Boxes, by-class
@pytest.mark.parametrize(
@ -155,128 +127,104 @@ def test_store_boxes_class_agnostic(
(
Detections(
xyxy=np.array([[0, 0, 1, 1]], dtype=np.float32),
class_id=np.array([1], dtype=int)
class_id=np.array([1], dtype=int),
),
Detections.empty(),
[
(
1,
np.array([[0, 0, 1, 1]], dtype=np.float32),
None
)
],
DoesNotRaise()
[(1, np.array([[0, 0, 1, 1]], dtype=np.float32), None)],
DoesNotRaise(),
),
(
Detections.empty(),
Detections(
xyxy=np.array([[0, 0, 1, 1]], dtype=np.float32),
class_id=np.array([1], dtype=int)
class_id=np.array([1], dtype=int),
),
[
(
1,
None,
np.array([[0, 0, 1, 1]], dtype=np.float32)
)
],
DoesNotRaise()
[(1, None, np.array([[0, 0, 1, 1]], dtype=np.float32))],
DoesNotRaise(),
),
# Multiple classes
(
Detections(
xyxy=np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
class_id=np.array([1, 2], dtype=int)
class_id=np.array([1, 2], dtype=int),
),
Detections.empty(),
[
(
1,
np.array([[0, 0, 1, 1]], dtype=np.float32),
None
),
(
2,
np.array([[0, 0, 2, 2]], dtype=np.float32),
None
)
(1, np.array([[0, 0, 1, 1]], dtype=np.float32), None),
(2, np.array([[0, 0, 2, 2]], dtype=np.float32), None),
],
DoesNotRaise()
DoesNotRaise(),
),
(
Detections(
xyxy=np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
class_id=np.array([1, 2], dtype=int)
class_id=np.array([1, 2], dtype=int),
),
Detections(
xyxy=np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
class_id=np.array([2, 3], dtype=int)
class_id=np.array([2, 3], dtype=int),
),
[
(
1,
np.array([[0, 0, 1, 1]], dtype=np.float32),
None
),
(1, np.array([[0, 0, 1, 1]], dtype=np.float32), None),
(
2,
np.array([[0, 0, 2, 2]], dtype=np.float32),
np.array([[0, 0, 1, 1]], dtype=np.float32)
np.array([[0, 0, 1, 1]], dtype=np.float32),
),
(
3,
None,
np.array([[0, 0, 2, 2]], dtype=np.float32)
)
(3, None, np.array([[0, 0, 2, 2]], dtype=np.float32)),
],
DoesNotRaise()
DoesNotRaise(),
),
(
Detections(
xyxy=np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32)
),
Detections(xyxy=np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32)),
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
[
(
CLASS_ID_NONE,
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32)
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
)
],
DoesNotRaise()
DoesNotRaise(),
),
# with classes - should be ignored.
(
Detections(
xyxy=np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
class_id=np.array([1, 2], dtype=int)
class_id=np.array([1, 2], dtype=int),
),
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
[
(
CLASS_ID_NONE,
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32)
np.array([[0, 0, 1, 1], [0, 0, 2, 2]], dtype=np.float32),
)
],
DoesNotRaise()
DoesNotRaise(),
),
]
],
)
def test_store_boxes_by_class(
data_1: Union[npt.NDArray, Detections],
data_2: Union[npt.NDArray, Detections],
expected_result: List[Tuple[int, Optional[npt.NDArray], Optional[npt.NDArray]]],
exception: Exception
exception: Exception,
) -> None:
store = InternalMetricDataStore(MetricTarget.BOXES, class_agnostic=False)
store.update(data_1, data_2)
result = [result for result in store]
assert len(result) == len(expected_result)
with exception:
for (class_id, content_1, content_2), (expected_class_id, expected_content_1, expected_content_2) in zip(result, expected_result):
for (class_id, content_1, content_2), (
expected_class_id,
expected_content_1,
expected_content_2,
) in zip(result, expected_result):
assert class_id == expected_class_id
assert (content_1 is None and expected_content_1 is None) or np.array_equal(content_1, expected_content_1)
assert (content_2 is None and expected_content_2 is None) or np.array_equal(content_2, expected_content_2)
assert (content_1 is None and expected_content_1 is None) or np.array_equal(
content_1, expected_content_1
)
assert (content_2 is None and expected_content_2 is None) or np.array_equal(
content_2, expected_content_2
)