From 7390a909d5f53bf8664d534acdb45ce932e58b3d Mon Sep 17 00:00:00 2001 From: Rustem Galiullin Date: Tue, 10 Oct 2023 15:11:17 +0400 Subject: [PATCH] add hota iou calculation along with detection/association accuracy --- motmetrics/metrics.py | 34 +++++++++++++++++++++++++++++ motmetrics/tests/test_metrics.py | 37 ++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/motmetrics/metrics.py b/motmetrics/metrics.py index e16f16c..5efdd35 100644 --- a/motmetrics/metrics.py +++ b/motmetrics/metrics.py @@ -751,6 +751,36 @@ def idf1_m(partials, idtp, num_objects, num_predictions): return math_util.quiet_divide(2 * idtp, num_objects + num_predictions) +def det_acc(df, num_detections, num_objects, num_false_positives): + """Detection accuracy. + + Source: https://jonathonluiten.medium.com/how-to-evaluate-tracking-with-the-hota-metrics-754036d183e1 + """ + + del df # unused + return math_util.quiet_divide(num_detections, num_objects + num_false_positives) + + +def ass_acc(df, idtp, idfn, idfp): + """Association accuracy. + + Source: https://jonathonluiten.medium.com/how-to-evaluate-tracking-with-the-hota-metrics-754036d183e1 + """ + + del df # unused + return math_util.quiet_divide(idtp, idtp + idfn + idfp) + + +def hota_iou(df, det_acc, ass_acc): + """HOTA metric at a specific iou. + + Source: https://jonathonluiten.medium.com/how-to-evaluate-tracking-with-the-hota-metrics-754036d183e1 + """ + + del df + return (det_acc * ass_acc)**0.5 + + for one in simple_add_func: name = one.__name__ @@ -802,6 +832,10 @@ def create(): m.register(idr, formatter="{:.1%}".format) m.register(idf1, formatter="{:.1%}".format) + m.register(det_acc, formatter="{:.1%}".format) + m.register(ass_acc, formatter="{:.1%}".format) + m.register(hota_iou, formatter="{:.1%}".format) + return m diff --git a/motmetrics/tests/test_metrics.py b/motmetrics/tests/test_metrics.py index 504fd28..bf0924e 100644 --- a/motmetrics/tests/test_metrics.py +++ b/motmetrics/tests/test_metrics.py @@ -15,6 +15,7 @@ import numpy as np import pandas as pd +import pytest from pytest import approx import motmetrics as mm @@ -343,6 +344,42 @@ def test_mota_motp(): assert metr["num_frames"] == 6 +def test_hota(): + """Tests values of HOTA and its dependents.""" + acc = mm.MOTAccumulator() + + # All FP + acc.update([], [1, 2], [], frameid=0) + # All miss + acc.update([1, 2], [], [], frameid=1) + # Match + acc.update([1, 2], [1, 2], [[1, 0.5], [0.3, 1]], frameid=2) + # Switch + acc.update([1, 2], [1, 2], [[0.2, np.nan], [np.nan, 0.1]], frameid=3) + # Match. Better new match is available but should prefer history + acc.update([1, 2], [1, 2], [[5, 1], [1, 5]], frameid=4) + # No data + acc.update([], [], [], frameid=5) + + mh = mm.metrics.create() + metr = mh.compute( + acc, + return_dataframe=False, + return_cached=True, + metrics=[ + "hota_iou", + "det_acc", + "ass_acc", + "num_frames", + ], + ) + + assert metr["det_acc"] == approx(6 / (8 + 2)) + assert metr["ass_acc"] == approx(6 / (8 + 2)) + assert metr["hota_iou"] == approx(0.6) + assert metr["num_frames"] == 6 + + def test_ids(): """Test metrics with frame IDs specified manually.""" acc = mm.MOTAccumulator()