Files
infra-docker-configs/servers/unraid/tail2-pose-server/app/tail2/core.py
T

92 lines
2.5 KiB
Python

from __future__ import annotations
import asyncio
import math
import queue
import threading
from dataclasses import dataclass
from typing import Any
import numpy as np
def finite01(value: float) -> float:
value = float(value)
return min(1.0, max(0.0, value)) if math.isfinite(value) else 0.0
def normalize_bbox(bbox: list[float] | np.ndarray, width: int, height: int) -> list[float]:
return [finite01(bbox[0] / width), finite01(bbox[1] / height),
finite01(bbox[2] / width), finite01(bbox[3] / height)]
def normalize_keypoints(points: np.ndarray, width: int, height: int, count: int) -> list[list[float]]:
out = [[finite01(p[0] / width), finite01(p[1] / height), finite01(p[2])] for p in points[:count]]
out.extend([[0.0, 0.0, 0.0] for _ in range(count - len(out))])
return out
def inverse_letterbox(points: np.ndarray, scale: float, pad_x: float, pad_y: float) -> np.ndarray:
out = points.copy().astype(float)
out[..., 0] = (out[..., 0] - pad_x) / scale
out[..., 1] = (out[..., 1] - pad_y) / scale
return out
class LatestQueue:
def __init__(self) -> None:
self._q: queue.Queue[Any] = queue.Queue(maxsize=1)
def put(self, item: Any) -> None:
try:
self._q.put_nowait(item)
except queue.Full:
try:
self._q.get_nowait()
except queue.Empty:
pass
self._q.put_nowait(item)
def get(self, timeout: float | None = None) -> Any:
return self._q.get(timeout=timeout)
def qsize(self) -> int:
return self._q.qsize()
class BroadcastHub:
def __init__(self) -> None:
self._clients: set[asyncio.Queue] = set()
self._lock = threading.Lock()
def add(self) -> asyncio.Queue:
q: asyncio.Queue = asyncio.Queue(maxsize=1)
with self._lock:
self._clients.add(q)
return q
def remove(self, q: asyncio.Queue) -> None:
with self._lock:
self._clients.discard(q)
def publish_on_loop(self, loop: asyncio.AbstractEventLoop, message: dict) -> None:
def publish() -> None:
with self._lock:
clients = list(self._clients)
for q in clients:
if q.full():
try:
q.get_nowait()
except asyncio.QueueEmpty:
pass
q.put_nowait(message)
loop.call_soon_threadsafe(publish)
@dataclass
class Frame:
pixels: np.ndarray
received_ns: int
decode_ms: float