Final changes before merge

This commit is contained in:
SkalskiP 2023-08-07 09:58:46 +02:00
parent 3a31ea2986
commit 7f4662cf3d
3 changed files with 73 additions and 27 deletions

View File

@ -471,13 +471,13 @@ class MeanAveragePrecision:
map (float): mAP value.
map50 (float): mAP value at IoU `threshold = 0.5`.
map75 (float): mAP value at IoU `threshold = 0.75`.
average_precisions (np.ndarray): values for every classes.
per_class_ap (np.ndarray): values for every classes.
"""
map: float
map50: float
map75: float
average_precisions: np.ndarray
per_class_ap: np.ndarray
@classmethod
def from_detections(
@ -644,14 +644,14 @@ class MeanAveragePrecision:
true_batch.shape[0],
detection_batch.shape[0],
)
correct = np.zeros((npr, num_ious), dtype=bool) # init
correct = np.zeros((npr, num_ious), dtype=bool)
if npr == 0:
if nl:
stats.append((correct, *np.zeros((2, 0)), true_batch[:, 4]))
continue
if nl:
correct = cls._match_detection_batch(
correct = MeanAveragePrecision._match_detection_batch(
predictions=detection_batch,
targets=true_batch,
iou_levels=iou_levels,
@ -676,9 +676,7 @@ class MeanAveragePrecision:
)
map50, map75, map = ap50.mean(), ap75.mean(), average_precisions.mean()
return cls(
map=map, map50=map50, map75=map75, average_precisions=average_precisions
)
return cls(map=map, map50=map50, map75=map75, per_class_ap=average_precisions)
@staticmethod
def _match_detection_batch(
@ -706,7 +704,7 @@ class MeanAveragePrecision:
[np.expand_dims(x[0], 1), np.expand_dims(x[1], 1)], axis=1
)
_x2 = iou[x[0], x[1]][:, None]
matches = np.concatenate([_X1, _x2], axis=1) # [label, detect, iou]
matches = np.concatenate([_X1, _x2], axis=1)
if x[0].shape[0] > 1:
matches = matches[matches[:, 2].argsort()[::-1]]
matches = matches[np.unique(matches[:, 1], return_index=True)[1]]
@ -716,24 +714,28 @@ class MeanAveragePrecision:
return correct
@staticmethod
def compute_average_precision(
recall: np.ndarray, precision: np.ndarray
) -> np.ndarray:
"""Compute the average precision using 101-point interpolation (COCO), given the recall and precision curves
Args:
recall (np.ndarray): The recall curve
precision (np.ndarray): The precision curve
Returns:
(np.ndarray): Average precision, precision curve, recall curve
def compute_average_precision(recall: np.ndarray, precision: np.ndarray) -> float:
"""
mrec = np.concatenate(([0.0], recall, [1.0]))
mpre = np.concatenate(([1.0], precision, [0.0]))
Compute the average precision using 101-point interpolation (COCO), given the recall and precision curves.
mpre = np.flip(np.maximum.accumulate(np.flip(mpre)))
Args:
recall (np.ndarray): The recall curve.
precision (np.ndarray): The precision curve.
x = np.linspace(0, 1, 101)
ap = np.trapz(np.interp(x, mrec, mpre), x)
return ap
Returns:
float: Average precision.
"""
extended_recall = np.concatenate(([0.0], recall, [1.0]))
extended_precision = np.concatenate(([1.0], precision, [0.0]))
max_accumulated_precision = np.flip(
np.maximum.accumulate(np.flip(extended_precision))
)
interpolated_recall_levels = np.linspace(0, 1, 101)
interpolated_precision = np.interp(
interpolated_recall_levels, extended_recall, max_accumulated_precision
)
average_precision = np.trapz(interpolated_precision, interpolated_recall_levels)
return average_precision
@staticmethod
def _average_precisions_per_class(
@ -741,7 +743,7 @@ class MeanAveragePrecision:
prediction_confidence: np.ndarray,
prediction_class_ids: np.ndarray,
true_batch_class_ids: np.ndarray,
EPS=1e-16,
eps: float = 1e-16,
) -> np.ndarray:
"""
Compute the average precision, given the recall and precision curves.
@ -775,7 +777,7 @@ class MeanAveragePrecision:
fp_pool = (1 - matches[valid]).cumsum(0)
tp_pool = matches[valid].cumsum(0)
recall = tp_pool / (num_targets + EPS)
recall = tp_pool / (num_targets + eps)
precision = tp_pool / (tp_pool + fp_pool)
for j in range(matches.shape[1]):

View File

@ -5,8 +5,8 @@ import numpy as np
import pytest
from supervision.detection.core import Detections
from supervision.metrics.detection import ConfusionMatrix, detections_to_tensor
from test.utils import mock_detections
from supervision.metrics.detection import ConfusionMatrix, detections_to_tensor, MeanAveragePrecision
from test.utils import mock_detections, assert_almost_equal
CLASSES = np.arange(80)
NUM_CLASSES = len(CLASSES)
@ -400,3 +400,43 @@ def test_drop_extra_matches(
result = ConfusionMatrix._drop_extra_matches(matches)
assert np.array_equal(result, expected_result)
@pytest.mark.parametrize(
'recall, precision, expected_result, exception',
[
(
np.array([1.0]),
np.array([1.0]),
1.0,
DoesNotRaise()
), # perfect recall and precision
(
np.array([0.0]),
np.array([0.0]),
0.0,
DoesNotRaise()
), # no recall and precision
(
np.array([0.0, 0.2, 0.2, 0.8, 0.8, 1.0]),
np.array([0.7, 0.8, 0.4, 0.5, 0.1, 0.2]),
0.5,
DoesNotRaise()
),
(
np.array([0.0, 0.5, 0.5, 1.0]),
np.array([0.75, 0.75, 0.75, 0.75]),
0.75,
DoesNotRaise()
)
]
)
def test_compute_average_precision(
recall: np.ndarray,
precision: np.ndarray,
expected_result: float,
exception: Exception
) -> None:
with exception:
result = MeanAveragePrecision.compute_average_precision(recall=recall, precision=precision)
assert_almost_equal(result, expected_result, tolerance=0.01)

View File

@ -21,3 +21,7 @@ def mock_detections(
if tracker_id is None
else np.array(tracker_id, dtype=int),
)
def assert_almost_equal(actual, expected, tolerance=1e-5):
assert abs(actual - expected) < tolerance, f"Expected {expected}, but got {actual}."