37 lines
1.4 KiB
Python
37 lines
1.4 KiB
Python
"""Small Pi-side gate to keep both eyes on one person through brief misses."""
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
class TrackingState:
|
|
def __init__(self, confirm_hits: int = 2, hold_seconds: float = 2.0) -> None:
|
|
if confirm_hits < 1 or hold_seconds <= 0:
|
|
raise ValueError("confirmation and hold must be positive")
|
|
self.confirm_hits = confirm_hits
|
|
self.hold_seconds = hold_seconds
|
|
self.hits = 0
|
|
self.last_seen: float | None = None
|
|
self.valid = False
|
|
self.x = 500
|
|
self.y = 500
|
|
|
|
def update(self, candidate: tuple[int, int] | None, now: float) -> tuple[int, int, bool]:
|
|
if candidate is not None:
|
|
x, y = candidate
|
|
if not 0 <= x <= 1000 or not 0 <= y <= 1000:
|
|
raise ValueError("candidate gaze coordinates out of range")
|
|
if self.last_seen is None or now - self.last_seen > 0.75:
|
|
self.hits = 0
|
|
self.hits += 1
|
|
self.last_seen = now
|
|
if self.valid or self.hits >= self.confirm_hits:
|
|
self.valid = True
|
|
self.x, self.y = x, y
|
|
elif self.last_seen is not None:
|
|
if self.valid and now - self.last_seen >= self.hold_seconds:
|
|
self.valid = False
|
|
self.x = self.y = 500
|
|
if not self.valid and now - self.last_seen > 0.75:
|
|
self.hits = 0
|
|
return self.x, self.y, self.valid
|