From 4f18a9fdac9ab5fb33da933fd9a39f0ac1ccf16e Mon Sep 17 00:00:00 2001 From: Daniel-dev22 <47092714+Daniel-dev22@users.noreply.github.com> Date: Tue, 23 Jun 2026 17:39:41 -0400 Subject: [PATCH] Drop interpolated_percentile, keep only average_boxes Per review: reimplementing np.percentile hurts readability and risks divergence from numpy (e.g. numpy 2.x). Revert update_position to np.percentile and remove the helper; keep only the average_boxes change (sum()/len() instead of np.mean), which stays bit-identical. Co-Authored-By: Claude Opus 4.8 (1M context) --- frigate/test/test_obects.py | 15 +-------------- frigate/track/norfair_tracker.py | 14 +++++--------- frigate/util/object.py | 19 ------------------- 3 files changed, 6 insertions(+), 42 deletions(-) diff --git a/frigate/test/test_obects.py b/frigate/test/test_obects.py index acafec81a7..ee0162ec01 100644 --- a/frigate/test/test_obects.py +++ b/frigate/test/test_obects.py @@ -4,7 +4,7 @@ import unittest import numpy as np from frigate.track.tracked_object import TrackedObjectAttribute -from frigate.util.object import average_boxes, interpolated_percentile +from frigate.util.object import average_boxes class TestBoxStatistics(unittest.TestCase): @@ -18,19 +18,6 @@ class TestBoxStatistics(unittest.TestCase): expected = [float(np.mean([b[i] for b in boxes])) for i in range(4)] self.assertEqual(average_boxes(boxes), expected) - def test_interpolated_percentile_matches_numpy(self) -> None: - rng = random.Random(1) - for _ in range(5000): - values = [rng.randint(0, 5000) for _ in range(rng.randint(1, 10))] - for q in (15, 85, 50): - self.assertEqual( - interpolated_percentile(values, q), - float(np.percentile(values, q)), - ) - - def test_interpolated_percentile_single_value(self) -> None: - self.assertEqual(interpolated_percentile([42], 15), 42.0) - class TestAttribute(unittest.TestCase): def test_overlapping_object_selection(self) -> None: diff --git a/frigate/track/norfair_tracker.py b/frigate/track/norfair_tracker.py index ef419e81a2..3f11823572 100644 --- a/frigate/track/norfair_tracker.py +++ b/frigate/track/norfair_tracker.py @@ -27,11 +27,7 @@ from frigate.util.image import ( get_histogram, intersection_over_union, ) -from frigate.util.object import ( - average_boxes, - interpolated_percentile, - median_of_boxes, -) +from frigate.util.object import average_boxes, median_of_boxes logger = logging.getLogger(__name__) @@ -432,10 +428,10 @@ class NorfairTracker(ObjectTracker): position["xmaxs"].append(xmax) position["ymaxs"].append(ymax) # by using percentiles here, we hopefully remove outliers - position["xmin"] = interpolated_percentile(position["xmins"], 15) - position["ymin"] = interpolated_percentile(position["ymins"], 15) - position["xmax"] = interpolated_percentile(position["xmaxs"], 85) - position["ymax"] = interpolated_percentile(position["ymaxs"], 85) + position["xmin"] = np.percentile(position["xmins"], 15) + position["ymin"] = np.percentile(position["ymins"], 15) + position["xmax"] = np.percentile(position["xmaxs"], 85) + position["ymax"] = np.percentile(position["ymaxs"], 85) return True diff --git a/frigate/util/object.py b/frigate/util/object.py index ec280a7aa6..2cac434810 100644 --- a/frigate/util/object.py +++ b/frigate/util/object.py @@ -354,25 +354,6 @@ def median_of_boxes(boxes: list[list[int, int, int, int]]) -> list[int, int, int return sorted_boxes[int(len(sorted_boxes) / 2.0)] -def interpolated_percentile(values: list[int], q: float) -> float: - """Linear-interpolated percentile, matching numpy.percentile for the small - lists used in position smoothing without the per-call numpy overhead.""" - ordered = sorted(values) - n = len(ordered) - if n == 1: - return float(ordered[0]) - rank = (q / 100.0) * (n - 1) - lo = int(rank) - frac = rank - lo - if frac == 0.0: - return float(ordered[lo]) - diff = ordered[lo + 1] - ordered[lo] - # numpy's lerp switches sides at frac >= 0.5 to limit rounding error - if frac < 0.5: - return ordered[lo] + diff * frac - return ordered[lo + 1] - diff * (1.0 - frac) - - def intersects_any(box_a, boxes): for box in boxes: if box_overlaps(box_a, box):