Skip to content

Commit

Permalink
Merge branch 'roboflow:develop' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
dsaha21 authored Nov 2, 2024
2 parents 8e58660 + 48c63e1 commit a6ab77c
Show file tree
Hide file tree
Showing 15 changed files with 353 additions and 344 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ repos:
additional_dependencies: ["bandit[toml]"]

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.7.0
rev: v0.7.1
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
Expand Down
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@

**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! 🤝

[![supervision-hackfest](https://media.roboflow.com/supervision/supervision-hacktoberfest-banner-2024.png)](https://github.com/roboflow/supervision/issues?q=is%3Aissue+is%3Aopen+label%3Ahacktoberfest)

## 💻 install

Pip install the supervision package in a
Expand Down
52 changes: 26 additions & 26 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pyyaml = ">=5.3"
defusedxml = "^0.7.1"
pillow = ">=9.4"
requests = { version = ">=2.26.0,<=2.32.3", optional = true }
tqdm = { version = ">=4.62.3,<=4.66.5", optional = true }
tqdm = { version = ">=4.62.3,<=4.66.6", optional = true }
# pandas: picked lowest major version that supports Python 3.8
pandas = { version = ">=2.0.0", optional = true }
pandas-stubs = { version = ">=2.0.0.230412", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion supervision/dataset/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def merge_class_lists(class_lists: List[List[str]]) -> List[str]:

for class_list in class_lists:
for class_name in class_list:
unique_classes.add(class_name.lower())
unique_classes.add(class_name)

return sorted(list(unique_classes))

Expand Down
11 changes: 5 additions & 6 deletions supervision/detection/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,9 @@ def polygon_to_mask(polygon: np.ndarray, resolution_wh: Tuple[int, int]) -> np.n
np.ndarray: The generated 2D mask, where the polygon is marked with
`1`'s and the rest is filled with `0`'s.
"""
width, height = resolution_wh
mask = np.zeros((height, width))

cv2.fillPoly(mask, [polygon], color=1)
width, height = map(int, resolution_wh)
mask = np.zeros((height, width), dtype=np.uint8)
cv2.fillPoly(mask, [polygon.astype(np.int32)], color=1)
return mask


Expand Down Expand Up @@ -163,9 +162,9 @@ def oriented_box_iou_batch(
boxes_true = boxes_true.reshape(-1, 4, 2)
boxes_detection = boxes_detection.reshape(-1, 4, 2)

max_height = max(boxes_true[:, :, 0].max(), boxes_detection[:, :, 0].max()) + 1
max_height = int(max(boxes_true[:, :, 0].max(), boxes_detection[:, :, 0].max()) + 1)
# adding 1 because we are 0-indexed
max_width = max(boxes_true[:, :, 1].max(), boxes_detection[:, :, 1].max()) + 1
max_width = int(max(boxes_true[:, :, 1].max(), boxes_detection[:, :, 1].max()) + 1)

mask_true = np.zeros((boxes_true.shape[0], max_height, max_width))
for i, box_true in enumerate(boxes_true):
Expand Down
37 changes: 25 additions & 12 deletions supervision/metrics/f1_score.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@

from supervision.config import ORIENTED_BOX_COORDINATES
from supervision.detection.core import Detections
from supervision.detection.utils import box_iou_batch, mask_iou_batch
from supervision.detection.utils import (
box_iou_batch,
mask_iou_batch,
oriented_box_iou_batch,
)
from supervision.draw.color import LEGACY_COLOR_PALETTE
from supervision.metrics.core import AveragingMethod, Metric, MetricTarget
from supervision.metrics.utils.object_size import (
Expand Down Expand Up @@ -62,14 +66,9 @@ def __init__(
averaging_method (AveragingMethod): The averaging method used to compute the
F1 scores. Determines how the F1 scores are aggregated across classes.
"""
self._metric_target = metric_target
if self._metric_target == MetricTarget.ORIENTED_BOUNDING_BOXES:
raise NotImplementedError(
"F1 score is not implemented for oriented bounding boxes."
)

self._metric_target = metric_target
self.averaging_method = averaging_method

self._predictions_list: List[Detections] = []
self._targets_list: List[Detections] = []

Expand Down Expand Up @@ -166,8 +165,12 @@ def _compute(
iou = box_iou_batch(target_contents, prediction_contents)
elif self._metric_target == MetricTarget.MASKS:
iou = mask_iou_batch(target_contents, prediction_contents)
elif self._metric_target == MetricTarget.ORIENTED_BOUNDING_BOXES:
iou = oriented_box_iou_batch(
target_contents, prediction_contents
)
else:
raise NotImplementedError(
raise ValueError(
"Unsupported metric target for IoU calculation"
)

Expand Down Expand Up @@ -366,12 +369,22 @@ def _detections_content(self, detections: Detections) -> np.ndarray:
return (
detections.mask
if detections.mask is not None
else np.empty((0, 0, 0), dtype=bool)
else self._make_empty_content()
)
if self._metric_target == MetricTarget.ORIENTED_BOUNDING_BOXES:
if obb := detections.data.get(ORIENTED_BOX_COORDINATES):
return np.ndarray(obb, dtype=np.float32)
return np.empty((0, 8), dtype=np.float32)
obb = detections.data.get(ORIENTED_BOX_COORDINATES)
if obb is not None and len(obb) > 0:
return np.array(obb, dtype=np.float32)
return self._make_empty_content()
raise ValueError(f"Invalid metric target: {self._metric_target}")

def _make_empty_content(self) -> np.ndarray:
if self._metric_target == MetricTarget.BOXES:
return np.empty((0, 4), dtype=np.float32)
if self._metric_target == MetricTarget.MASKS:
return np.empty((0, 0, 0), dtype=bool)
if self._metric_target == MetricTarget.ORIENTED_BOUNDING_BOXES:
return np.empty((0, 4, 2), dtype=np.float32)
raise ValueError(f"Invalid metric target: {self._metric_target}")

def _filter_detections_by_size(
Expand Down
25 changes: 14 additions & 11 deletions supervision/metrics/mean_average_precision.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@

from supervision.config import ORIENTED_BOX_COORDINATES
from supervision.detection.core import Detections
from supervision.detection.utils import box_iou_batch, mask_iou_batch
from supervision.detection.utils import (
box_iou_batch,
mask_iou_batch,
oriented_box_iou_batch,
)
from supervision.draw.color import LEGACY_COLOR_PALETTE
from supervision.metrics.core import Metric, MetricTarget
from supervision.metrics.utils.object_size import (
Expand Down Expand Up @@ -57,11 +61,6 @@ def __init__(
class_agnostic (bool): Whether to treat all data as a single class.
"""
self._metric_target = metric_target
if self._metric_target == MetricTarget.ORIENTED_BOUNDING_BOXES:
raise NotImplementedError(
"Mean Average Precision is not implemented for oriented bounding boxes."
)

self._class_agnostic = class_agnostic

self._predictions_list: List[Detections] = []
Expand Down Expand Up @@ -189,8 +188,12 @@ def _compute(
iou = box_iou_batch(target_contents, prediction_contents)
elif self._metric_target == MetricTarget.MASKS:
iou = mask_iou_batch(target_contents, prediction_contents)
elif self._metric_target == MetricTarget.ORIENTED_BOUNDING_BOXES:
iou = oriented_box_iou_batch(
target_contents, prediction_contents
)
else:
raise NotImplementedError(
raise ValueError(
"Unsupported metric target for IoU calculation"
)

Expand Down Expand Up @@ -264,7 +267,6 @@ def _match_detection_batch(
iou_thresholds.shape[0],
)
correct = np.zeros((num_predictions, num_iou_levels), dtype=bool)

correct_class = target_classes[:, None] == predictions_classes

for i, iou_level in enumerate(iou_thresholds):
Expand Down Expand Up @@ -352,8 +354,9 @@ def _detections_content(self, detections: Detections) -> np.ndarray:
else self._make_empty_content()
)
if self._metric_target == MetricTarget.ORIENTED_BOUNDING_BOXES:
if obb := detections.data.get(ORIENTED_BOX_COORDINATES):
return np.ndarray(obb, dtype=np.float32)
obb = detections.data.get(ORIENTED_BOX_COORDINATES)
if obb is not None and len(obb) > 0:
return np.array(obb, dtype=np.float32)
return self._make_empty_content()
raise ValueError(f"Invalid metric target: {self._metric_target}")

Expand All @@ -363,7 +366,7 @@ def _make_empty_content(self) -> np.ndarray:
if self._metric_target == MetricTarget.MASKS:
return np.empty((0, 0, 0), dtype=bool)
if self._metric_target == MetricTarget.ORIENTED_BOUNDING_BOXES:
return np.empty((0, 8), dtype=np.float32)
return np.empty((0, 4, 2), dtype=np.float32)
raise ValueError(f"Invalid metric target: {self._metric_target}")

def _filter_detections_by_size(
Expand Down
37 changes: 25 additions & 12 deletions supervision/metrics/precision.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@

from supervision.config import ORIENTED_BOX_COORDINATES
from supervision.detection.core import Detections
from supervision.detection.utils import box_iou_batch, mask_iou_batch
from supervision.detection.utils import (
box_iou_batch,
mask_iou_batch,
oriented_box_iou_batch,
)
from supervision.draw.color import LEGACY_COLOR_PALETTE
from supervision.metrics.core import AveragingMethod, Metric, MetricTarget
from supervision.metrics.utils.object_size import (
Expand Down Expand Up @@ -65,14 +69,9 @@ def __init__(
averaging_method (AveragingMethod): The averaging method used to compute the
precision. Determines how the precision is aggregated across classes.
"""
self._metric_target = metric_target
if self._metric_target == MetricTarget.ORIENTED_BOUNDING_BOXES:
raise NotImplementedError(
"Precision is not implemented for oriented bounding boxes."
)

self._metric_target = metric_target
self.averaging_method = averaging_method

self._predictions_list: List[Detections] = []
self._targets_list: List[Detections] = []

Expand Down Expand Up @@ -169,8 +168,12 @@ def _compute(
iou = box_iou_batch(target_contents, prediction_contents)
elif self._metric_target == MetricTarget.MASKS:
iou = mask_iou_batch(target_contents, prediction_contents)
elif self._metric_target == MetricTarget.ORIENTED_BOUNDING_BOXES:
iou = oriented_box_iou_batch(
target_contents, prediction_contents
)
else:
raise NotImplementedError(
raise ValueError(
"Unsupported metric target for IoU calculation"
)

Expand Down Expand Up @@ -369,12 +372,22 @@ def _detections_content(self, detections: Detections) -> np.ndarray:
return (
detections.mask
if detections.mask is not None
else np.empty((0, 0, 0), dtype=bool)
else self._make_empty_content()
)
if self._metric_target == MetricTarget.ORIENTED_BOUNDING_BOXES:
if obb := detections.data.get(ORIENTED_BOX_COORDINATES):
return np.ndarray(obb, dtype=np.float32)
return np.empty((0, 8), dtype=np.float32)
obb = detections.data.get(ORIENTED_BOX_COORDINATES)
if obb is not None and len(obb) > 0:
return np.array(obb, dtype=np.float32)
return self._make_empty_content()
raise ValueError(f"Invalid metric target: {self._metric_target}")

def _make_empty_content(self) -> np.ndarray:
if self._metric_target == MetricTarget.BOXES:
return np.empty((0, 4), dtype=np.float32)
if self._metric_target == MetricTarget.MASKS:
return np.empty((0, 0, 0), dtype=bool)
if self._metric_target == MetricTarget.ORIENTED_BOUNDING_BOXES:
return np.empty((0, 4, 2), dtype=np.float32)
raise ValueError(f"Invalid metric target: {self._metric_target}")

def _filter_detections_by_size(
Expand Down
Loading

0 comments on commit a6ab77c

Please sign in to comment.