Files
root 95bb7c50ee feat: point the fleet at wss LiveKit room uwh-telhai
Publish and subscribe over wss://livekit.uni-wh.de:7800 and refuse
cleartext ws://. Conference room is uwh-telhai. Includes the uncommitted
encoded H.264 publish path, Rally hairpin, and KMS wall overlay.
2026-10-11 00:48:50 +00:00

172 lines
5.9 KiB
Python

"""Hand-raise rule, latch, and yellow I420 chrome."""
import time
import numpy as np
from display_grid import FRAME_RAISE, CameraGrid, bgra_to_i420, i420_nbytes
from hand_raise import RaiseLatch, letterbox_rgb, wrist_is_raised
def _kpts() -> np.ndarray:
k = np.zeros((17, 3), dtype=np.float32)
k[:, 2] = 0.8
k[0] = [0.35, 0.50, 0.9] # nose
k[5] = [0.50, 0.35, 0.9] # L shoulder
k[6] = [0.50, 0.65, 0.9] # R shoulder
k[7] = [0.62, 0.30, 0.9] # L elbow
k[8] = [0.62, 0.70, 0.9]
k[9] = [0.70, 0.28, 0.9] # L wrist down
k[10] = [0.70, 0.72, 0.9]
return k
def test_wrist_down_is_not_raised():
assert wrist_is_raised(_kpts()) is False
def test_left_wrist_above_shoulder_is_raised():
k = _kpts()
k[7] = [0.32, 0.30, 0.9] # elbow up
k[9] = [0.18, 0.32, 0.9] # wrist above shoulder (y 0.50)
assert wrist_is_raised(k) is True
def test_modest_wrist_lift_is_raised():
"""Webcam framing: hand just above the shoulder still counts."""
k = _kpts()
k[7] = [0.46, 0.30, 0.9]
k[9] = [0.43, 0.32, 0.9] # 0.07 above shoulder y=0.50
assert wrist_is_raised(k) is True
def test_wrist_on_face_rejected():
k = _kpts()
k[7] = [0.38, 0.48, 0.9]
k[9] = [0.28, 0.50, 0.9] # up, but next to the nose
assert wrist_is_raised(k) is False
def test_wrist_above_head_without_shoulders():
"""Close-up webcam: shoulders off-frame, hand still clearly up."""
k = np.zeros((17, 3), dtype=np.float32)
k[0] = [0.55, 0.50, 0.85] # nose
k[9] = [0.18, 0.62, 0.7] # wrist high, not on the face
assert wrist_is_raised(k) is True
def test_low_score_not_raised():
k = _kpts()
k[:, 2] = 0.05
assert wrist_is_raised(k) is False
assert wrist_is_raised(np.zeros((17, 3), dtype=np.float32)) is False
def test_raise_latch_needs_hits_then_holds():
latch = RaiseLatch(hits=2, hold_s=0.4, window=2)
t = 10.0
assert latch.update(True, t) is False
assert latch.update(True, t + 0.1) is True
assert latch.update(False, t + 0.2) is True
assert latch.update(False, t + 0.55) is False
def test_raise_latch_survives_one_miss():
latch = RaiseLatch(hits=3, hold_s=1.0, window=5)
t = 10.0
assert latch.update(True, t) is False
assert latch.update(True, t + 0.1) is False
assert latch.update(False, t + 0.2) is False
assert latch.update(True, t + 0.3) is True
assert latch.update(True, t + 0.4) is True
assert latch.update(False, t + 0.5) is True
def test_letterbox_rgb_pads_16x9():
rgb = np.full((360, 640, 3), 200, dtype=np.uint8)
out = letterbox_rgb(rgb, 192)
assert out.shape == (192, 192, 3)
assert int(out[0, 96, 0]) == 0
assert int(out[96, 96, 0]) == 200
thunder = letterbox_rgb(rgb, 256)
assert thunder.shape == (256, 256, 3)
def test_resolve_movenet_thunder_is_256():
from pathlib import Path
from hand_raise import resolve_movenet
path, size, name = resolve_movenet("thunder")
assert name == "thunder"
assert size == 256
assert path.name == "movenet_singlepose_thunder.onnx"
path, size, name = resolve_movenet("lightning")
assert name == "lightning" and size == 192
assert Path(path).name == "movenet_singlepose_lightning.onnx"
def test_set_highlight_paints_yellow_i420_border():
g = CameraGrid(width=1920, height=1080, cols=5, rows=4)
src = np.zeros((360, 640, 4), dtype=np.uint8)
src[:] = (40, 40, 40, 255)
g.set_frame_i420("cam-07", bgra_to_i420(src), 640, 360)
before = np.frombuffer(g.render_i420(), dtype=np.uint8).copy()
g.set_highlight("cam-07", True)
assert g.highlighted("cam-07") is True
after = np.frombuffer(g.render_i420(), dtype=np.uint8)
assert not np.array_equal(before, after)
idx = g.identities.index("cam-07")
ox, oy = g.tile_origin(idx)
yplane = after[: g.width * g.height].reshape(g.height, g.width)
yv, _, _ = g._yuv_raise
# Top edge of the tile chrome should be yellow-luma, not gray live.
assert int(yplane[oy, ox + g.tile_w // 2]) == yv
assert int(yplane[oy - 2, ox + g.tile_w // 2]) == yv
ix, iy, iw, _ih = g.inner_rect(idx)
assert int(yplane[iy, ix + iw // 2]) != yv
g.set_highlight("cam-07", False)
assert g.highlighted("cam-07") is False
restored = np.frombuffer(g.render_i420(), dtype=np.uint8)
assert int(restored[: g.width * g.height].reshape(g.height, g.width)[oy, ox + g.tile_w // 2]) != yv
def test_clear_drops_yellow():
g = CameraGrid(width=1920, height=1080, cols=5, rows=4)
src = np.zeros((360, 640, 4), dtype=np.uint8)
g.set_frame_i420("cam-01", bgra_to_i420(src), 640, 360)
g.set_highlight("cam-01", True)
g.clear("cam-01")
assert g.highlighted("cam-01") is False
assert len(g.render_i420()) == i420_nbytes(1920, 1080)
def test_fp32_movenet_sees_person_not_raised():
"""INT8 Xenova weights scored ~0.05 on a real person; FP32 must lock on."""
from pathlib import Path
from hand_raise import HandRaiseMonitor, MODEL_PATH
fixture = Path(__file__).parent / "fixtures" / "movenet_warrior_192.npy"
assert MODEL_PATH.name == "movenet_singlepose_lightning.onnx"
rgb = np.load(fixture)
mon = HandRaiseMonitor(enabled=True, model="lightning")
assert mon.enabled
k = mon.infer_rgb(rgb)
assert float(k[:, 2].max()) > 0.4
assert float(k[0, 2]) > 0.3 # nose
assert wrist_is_raised(k) is False # warrior: arms out, not up
def test_fp32_thunder_sees_person_not_raised():
from pathlib import Path
from hand_raise import HandRaiseMonitor, resolve_movenet
path, size, name = resolve_movenet("thunder")
assert path.is_file()
assert size == 256 and name == "thunder"
rgb = np.load(Path(__file__).parent / "fixtures" / "movenet_warrior_192.npy")
mon = HandRaiseMonitor(enabled=True, model="thunder")
assert mon.enabled
assert mon._input_size == 256
k = mon.infer_rgb(rgb)
assert float(k[:, 2].max()) > 0.4
assert float(k[0, 2]) > 0.3
assert wrist_is_raised(k) is False