Skip to content

Commit 116464a

Browse files
author
spencer@primus
committed
Add v1.0 of ospa metric
1 parent 7de87ac commit 116464a

2 files changed

Lines changed: 46 additions & 0 deletions

File tree

avapi/evaluation/ospa.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Optimal subpattern assignment metric for object tracking
2+
3+
import numpy as np
4+
from avstack.modules.assignment import gnn_single_frame_assign
5+
6+
7+
class OspaMetric:
8+
@staticmethod
9+
def _cost(list_shorter: list, list_longer: list, p: float = 1.0, c: float = 1.0):
10+
n = len(list_longer)
11+
m = len(list_shorter)
12+
A = np.array(
13+
[[np.linalg.norm(l1 - l2) for l1 in list_shorter] for l2 in list_longer]
14+
)
15+
assign = gnn_single_frame_assign(A, cost_threshold=c)
16+
rows, cols = assign.rows_and_cols()
17+
c_una = c * len(assign.unassigned_rows)
18+
distance = (
19+
1 / n * (np.sum(A[rows, cols]) + c_una) ** p + (n - m) * c**p
20+
) ** 1 / p
21+
return distance
22+
23+
@staticmethod
24+
def cost(tracks: list, truths: list):
25+
if len(tracks) <= len(truths):
26+
return OspaMetric._cost(tracks, truths)
27+
else:
28+
return OspaMetric._cost(truths, tracks)

tests/evaluation/test_ospa.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import numpy as np
2+
3+
from avapi.evaluation.ospa import OspaMetric
4+
5+
6+
def test_ospa_metric_one_to_one():
7+
tracks = [np.random.randn(3)]
8+
truths = [np.random.randn(3)]
9+
cost = OspaMetric.cost(tracks, truths)
10+
assert cost > 0
11+
12+
13+
def test_ospa_metric_two_to_one():
14+
tracks = [np.random.randn(3) * i**3 for i in range(2)]
15+
truths = [np.random.randn(3) for _ in range(1)]
16+
cost1 = OspaMetric.cost(tracks[:1], truths)
17+
cost2 = OspaMetric.cost(tracks, truths)
18+
assert 0 < cost1 < cost2

0 commit comments

Comments
 (0)