From 675a7f9c0e1a48c3355dd8480d43aa74a7e67763 Mon Sep 17 00:00:00 2001 From: mikel-brostrom Date: Fri, 10 Jul 2026 11:01:25 +0200 Subject: [PATCH] fix hota overall aggregation --- motmetrics/io.py | 2 +- motmetrics/metrics.py | 24 ++++++++++-------------- motmetrics/tests/test_metrics.py | 27 +++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/motmetrics/io.py b/motmetrics/io.py index ca29c6a..e236f42 100644 --- a/motmetrics/io.py +++ b/motmetrics/io.py @@ -17,7 +17,6 @@ import numpy as np import pandas as pd import scipy.io -import xmltodict class Format(Enum): @@ -267,6 +266,7 @@ def load_detrac_xml(fname): 'X', 'Y', 'Width', 'Height', 'Confidence', 'ClassId', 'Visibility' The dataframe is indexed by ('FrameId', 'Id') """ + import xmltodict with io.open(fname) as fd: doc = xmltodict.parse(fd.read()) diff --git a/motmetrics/metrics.py b/motmetrics/metrics.py index 4710d45..ae8f9a7 100644 --- a/motmetrics/metrics.py +++ b/motmetrics/metrics.py @@ -619,11 +619,9 @@ def deta_alpha(df, num_detections, num_objects, num_false_positives): return math_util.quiet_divide(num_detections, max(1, num_objects + num_false_positives)) -def deta_alpha_m(partials): - res = 0 - for v in partials: - res += v["deta_alpha"] - return math_util.quiet_divide(res, len(partials)) +def deta_alpha_m(partials, num_detections, num_objects, num_false_positives): + del partials # unused + return math_util.quiet_divide(num_detections, max(1, num_objects + num_false_positives)) def assa_alpha(df, num_detections, num_gt_ids, num_dt_ids): @@ -654,11 +652,11 @@ def assa_alpha(df, num_detections, num_gt_ids, num_dt_ids): return math_util.quiet_divide((ass_a * match_count_array).sum(), max(1, num_detections)) -def assa_alpha_m(partials): - res = 0 +def assa_alpha_m(partials, num_detections): + weighted_sum = 0 for v in partials: - res += v["assa_alpha"] - return math_util.quiet_divide(res, len(partials)) + weighted_sum += v["assa_alpha"] * v["num_detections"] + return math_util.quiet_divide(weighted_sum, max(1, num_detections)) def hota_alpha(df, deta_alpha, assa_alpha): @@ -667,11 +665,9 @@ def hota_alpha(df, deta_alpha, assa_alpha): return (deta_alpha * assa_alpha) ** 0.5 -def hota_alpha_m(partials): - res = 0 - for v in partials: - res += v["hota_alpha"] - return math_util.quiet_divide(res, len(partials)) +def hota_alpha_m(partials, deta_alpha, assa_alpha): + del partials # unused + return (deta_alpha * assa_alpha) ** 0.5 class DataFrameMap: # pylint: disable=too-few-public-methods diff --git a/motmetrics/tests/test_metrics.py b/motmetrics/tests/test_metrics.py index 891154e..562218a 100644 --- a/motmetrics/tests/test_metrics.py +++ b/motmetrics/tests/test_metrics.py @@ -544,6 +544,11 @@ def test_hota(): "TUD-Campus": {"hota": 0.3913974378451139, "deta": 0.418047030142763, "assa": 0.36912068120832836}, "TUD-Stadtmitte": {"hota": 0.3978490169927877, "deta": 0.3922675723693166, "assa": 0.4088407518112996} } + TUD_combined_golden_ans = { # TrackEval combine_sequences over both TUD sequences. + "hota": 0.3999570912884786, + "deta": 0.3976832912424188, + "assa": 0.4124495298453543, + } DATA_DIR = "motmetrics/data" @@ -583,3 +588,25 @@ def compute_motchallenge(dname): assert deta == approx(TUD_golden_ans[dname]["deta"]) assert assa == approx(TUD_golden_ans[dname]["assa"]) assert hota == approx(TUD_golden_ans[dname]["hota"]) + + deta = [] + assa = [] + hota = [] + for alpha_idx in range(len(accs[0])): + summary = mh.compute_many( + [seq_accs[alpha_idx] for seq_accs in accs], + metrics=[ + "deta_alpha", + "assa_alpha", + "hota_alpha", + ], + names=list(TUD_golden_ans), + generate_overall=True, + ) + deta.append(float(summary.loc["OVERALL", "deta_alpha"])) + assa.append(float(summary.loc["OVERALL", "assa_alpha"])) + hota.append(float(summary.loc["OVERALL", "hota_alpha"])) + + assert sum(deta) / len(deta) == approx(TUD_combined_golden_ans["deta"]) + assert sum(assa) / len(assa) == approx(TUD_combined_golden_ans["assa"]) + assert sum(hota) / len(hota) == approx(TUD_combined_golden_ans["hota"])