mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-06-24 05:11:54 +03:00
perf(track): use sum()/len() instead of np.mean in average_boxes (#23521)
Some checks are pending
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
Some checks are pending
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
* perf(track): avoid numpy reductions on tiny box lists in position smoothing update_position runs per tracked object per frame. While a position has fewer than 10 samples it calls np.percentile four times, and average_boxes (per stationary object per frame) calls np.mean four times - all on lists of at most 10 ints, where numpy's per-call dispatch/validation overhead dominates the actual work. Replace them with pure-Python equivalents: - average_boxes: sum()/len() instead of np.mean (bit-identical output) - interpolated_percentile(): linear-interpolated percentile matching numpy.percentile (including its lerp branch at frac>=0.5) for the small lists used here, in place of np.percentile Measured in the release image (numpy 1.26.4) on a 10-element list: np.percentile 18735 ns -> 191 ns/call (98x); np.mean-based average_boxes 7480 ns -> 591 ns (12.7x); ~74 us saved per object-frame in update_position. A live py-spy --gil profile of a camera process_frames worker showed np.percentile (update_position) and np.mean (average_boxes) among the top Frigate-owned on-CPU frames. Output is unchanged: added tests assert both helpers are bit-identical to numpy over randomized small inputs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * 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) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
081d6f95ef
commit
ec3fb00494
@ -1,6 +1,22 @@
|
||||
import random
|
||||
import unittest
|
||||
|
||||
import numpy as np
|
||||
|
||||
from frigate.track.tracked_object import TrackedObjectAttribute
|
||||
from frigate.util.object import average_boxes
|
||||
|
||||
|
||||
class TestBoxStatistics(unittest.TestCase):
|
||||
def test_average_boxes_matches_numpy(self) -> None:
|
||||
rng = random.Random(0)
|
||||
for _ in range(5000):
|
||||
boxes = [
|
||||
[rng.randint(0, 4000) for _ in range(4)]
|
||||
for _ in range(rng.randint(1, 10))
|
||||
]
|
||||
expected = [float(np.mean([b[i] for b in boxes])) for i in range(4)]
|
||||
self.assertEqual(average_boxes(boxes), expected)
|
||||
|
||||
|
||||
class TestAttribute(unittest.TestCase):
|
||||
|
||||
@ -339,18 +339,13 @@ def reduce_boxes(boxes, iou_threshold=0.0):
|
||||
|
||||
def average_boxes(boxes: list[list[int, int, int, int]]) -> list[int, int, int, int]:
|
||||
"""Return a box that is the average of a list of boxes."""
|
||||
x_mins = []
|
||||
y_mins = []
|
||||
x_max = []
|
||||
y_max = []
|
||||
|
||||
for box in boxes:
|
||||
x_mins.append(box[0])
|
||||
y_mins.append(box[1])
|
||||
x_max.append(box[2])
|
||||
y_max.append(box[3])
|
||||
|
||||
return [np.mean(x_mins), np.mean(y_mins), np.mean(x_max), np.mean(y_max)]
|
||||
n = len(boxes)
|
||||
return [
|
||||
sum(box[0] for box in boxes) / n,
|
||||
sum(box[1] for box in boxes) / n,
|
||||
sum(box[2] for box in boxes) / n,
|
||||
sum(box[3] for box in boxes) / n,
|
||||
]
|
||||
|
||||
|
||||
def median_of_boxes(boxes: list[list[int, int, int, int]]) -> list[int, int, int, int]:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user