feat(viz): perf-safe keys, frozen pinch tuning
CI build oscope-of / build-check (push) Has been cancelled

Live session outcomes: (1) pinch thresholds validated for inner
fingers frozen as defaults (ratio_on 0.50, margin 0.05, ext_min 1);
(2) panel frames + X/Y gauges now permanent (no early-return when
nothing is detected); (3) wxcvbn source-bundle shortcuts gated off by
default (VIZ_SOURCE_KEYS=1 re-enables) — a stray key mid-performance
switched the audio scene.
This commit is contained in:
L'électron rare
2026-07-02 16:44:28 +02:00
parent 6fe98c6b00
commit 1c07211dd8
5 changed files with 37 additions and 15 deletions
+3 -2
View File
@@ -74,11 +74,12 @@ Toujours répondre en français à l'utilisateur. Code, commentaires de code, co
| `FINGER_STRIKE_REFRACTORY_MS` | `120` | min ms between strikes per finger |
| `FINGER_SOURCE` | `auto` | hand source for finger gestures: `auto` (iPhone Vision hands if fresh, else MediaPipe), `iphone`, `mediapipe` |
| `FINGER_DEBUG` | `0` | `1` logs each detected strike/pinch (hand, finger) — for threshold tuning |
| `PINCH_RATIO_ON` | `0.45` | thumb-to-finger distance / hand size below which a pinch engages (clip toggle) |
| `PINCH_RATIO_ON` | `0.50` | thumb-to-finger distance / hand size below which a pinch engages (0.50 validated live 2026-07-02 for ring/pinky reach) |
| `PINCH_RATIO_OFF` | `0.65` | distance/size above which the pinch re-arms (hysteresis) |
| `PINCH_REFRACTORY_MS` | `250` | min ms between pinches per finger |
| `PINCH_MARGIN` | `0.05` | closest-wins margin between nearest and runner-up fingertip (0.05 validated live: 0.20 rejected middle/ring/pinky pinches) |
| `PINCH_EXT_RATIO` | `1.35` | tip-to-wrist distance / hand size above which a finger counts as extended (open-hand pinch gate) |
| `PINCH_EXT_MIN` | `2` | min extended non-pinching fingers required for a pinch to engage; `0` disables the open-hand gate |
| `PINCH_EXT_MIN` | `1` | min extended non-pinching fingers required for a pinch to engage; `0` disables the open-hand gate (1 validated live: inner-finger pinches curl neighbours) |
| `PINCH_DEBOUNCE_FRAMES` | `3` | consecutive qualifying frames before a pinch engage fires (release stays immediate); `1` disables |
| `HAND_CONF_MIN` | `0.45` | min Vision hand confidence to draw (raised from 0.3; applies to both overlay and side panels) |
| `HAND_PERSIST_FRAMES` | `3` | consecutive frames a hand track must match before being drawn (1 = off, all hands drawn immediately) |
+12 -6
View File
@@ -42,6 +42,7 @@ HAND_HOLD_FRAMES 2 int gesture slot hold: frames to carry
ARKIT_BONE_MAX 0.5 float max bone length (norm) for ARKit skeleton
ARKIT_FULL_SKELETON "1" bool draw full 91-joint ARKit body (!=0 = True)
VIZ_HUD "0" bool show debug HUD overlay
VIZ_SOURCE_KEYS "0" bool wxcvbn source-bundle shortcuts (off: perf safety)
IPHONE_OSC_PORT 57128 int UDP port for iPhone ARBodyTracker /body3d/kp
CONCERT_MIRROR "1" bool mirror video horizontally (!=0 = True)
ICP_FUSION "0" bool enable ICP LiDAR fusion (only "1" = True)
@@ -110,12 +111,12 @@ class VizConfig:
# ---- Pinch detection -------------------------------------------------
pinch_enable: bool = False
pinch_ratio_on: float = 0.45
pinch_ratio_on: float = 0.50
pinch_ratio_off: float = 0.65
pinch_refractory_ms: float = 250.0
pinch_margin: float = 0.20
pinch_margin: float = 0.05
pinch_ext_ratio: float = 1.35
pinch_ext_min: int = 2
pinch_ext_min: int = 1
pinch_debounce_frames: int = 3
# ---- Hand display / gesture ------------------------------------------
@@ -132,6 +133,10 @@ class VizConfig:
# ---- HUD -------------------------------------------------------------
viz_hud: bool = False
# Source-bundle keyboard shortcuts (wxcvbn): OFF by default — a stray
# keypress during a matrix performance switches the audio scene
# (user-disabled live 2026-07-02). VIZ_SOURCE_KEYS=1 re-enables.
viz_source_keys: bool = False
# ---- iPhone OSC ------------------------------------------------------
iphone_osc_port: int = 57128
@@ -260,12 +265,12 @@ class VizConfig:
finger_source=_str_lower("FINGER_SOURCE", "auto"),
finger_debug=_bool_std("FINGER_DEBUG", False),
pinch_enable=_bool_std("PINCH_ENABLE", False),
pinch_ratio_on=_float("PINCH_RATIO_ON", 0.45),
pinch_ratio_on=_float("PINCH_RATIO_ON", 0.50),
pinch_ratio_off=_float("PINCH_RATIO_OFF", 0.65),
pinch_refractory_ms=_float("PINCH_REFRACTORY_MS", 250.0),
pinch_margin=_float("PINCH_MARGIN", 0.20),
pinch_margin=_float("PINCH_MARGIN", 0.05),
pinch_ext_ratio=_float("PINCH_EXT_RATIO", 1.35),
pinch_ext_min=_int("PINCH_EXT_MIN", 2),
pinch_ext_min=_int("PINCH_EXT_MIN", 1),
pinch_debounce_frames=_int("PINCH_DEBOUNCE_FRAMES", 3),
hand_conf_min=_float("HAND_CONF_MIN", 0.45),
hand_persist_frames=_int("HAND_PERSIST_FRAMES", 3),
@@ -279,6 +284,7 @@ class VizConfig:
# ARKIT_FULL_SKELETON uses != "0" convention (empty = True)
arkit_full_skeleton=_bool_ne0("ARKIT_FULL_SKELETON", True),
viz_hud=_bool_std("VIZ_HUD", False),
viz_source_keys=_bool_std("VIZ_SOURCE_KEYS", False),
iphone_osc_port=_int("IPHONE_OSC_PORT", 57128),
concert_mirror=_bool_ne0("CONCERT_MIRROR", True),
# ICP_FUSION / MULTIHMR_AUTOCAST use strict "1" convention
+9 -2
View File
@@ -71,6 +71,8 @@ class AppDelegate(NSObject):
# Sender vers sclang pour les changements de scene depuis le clavier
self._scClient = SimpleUDPClient("127.0.0.1", opts.sclang_port)
self._pose_worker = None
from .config import VizConfig as _VizConfig
self._source_keys = _VizConfig.from_env().viz_source_keys
return self
def applicationDidFinishLaunching_(self, notification): # noqa: N802
@@ -627,8 +629,13 @@ class AppDelegate(NSObject):
self._state.active_scene = scene
LOG.info("[audio] scene -> %s", scene)
return None
# wxcvbn + 0-9 -> preset bundle (source + scene audio + viz video)
for kk, source, scene, viz in (*KEYMAP_SOURCE, *KEYMAP_SOURCE_NUM):
# wxcvbn -> preset bundle: gated OFF by default (VIZ_SOURCE_KEYS=1
# re-enables) — a stray keypress mid-performance switches the audio
# scene. The 0-9 numeric bundles stay active.
_src_maps = ((*KEYMAP_SOURCE, *KEYMAP_SOURCE_NUM)
if getattr(self, "_source_keys", False)
else KEYMAP_SOURCE_NUM)
for kk, source, scene, viz in _src_maps:
if key == kk or k == kk:
# Audio : envoie a sclang
self._scClient.send_message("/control/doScene", [scene])
+4 -2
View File
@@ -343,8 +343,10 @@ class MetalRenderer(NSObject):
_arkit_now = time.perf_counter()
use_arkit = (getattr(self, '_arkit_full', ARKIT_FULL) and bool(s.arkit_parents)
and arkit_2d_fresh(s.persons_arkit_2d_t, _arkit_now))
if not use_arkit and not s.pose_alive():
return 0
# NO early-return when nothing is detected: the panel frames and the
# X/Y gauges are permanent screen furniture (user request: indicators
# must stay visible with no hands). The overlay loops below iterate
# empty collections at negligible cost.
buf = self._skel_cpu_buf
segs = 0
+9 -3
View File
@@ -48,12 +48,12 @@ def test_defaults_finger_piano():
def test_defaults_pinch():
cfg = VizConfig.from_env({})
assert cfg.pinch_enable is False
assert cfg.pinch_ratio_on == pytest.approx(0.45)
assert cfg.pinch_ratio_on == pytest.approx(0.50)
assert cfg.pinch_ratio_off == pytest.approx(0.65)
assert cfg.pinch_refractory_ms == pytest.approx(250.0)
assert cfg.pinch_margin == pytest.approx(0.20)
assert cfg.pinch_margin == pytest.approx(0.05)
assert cfg.pinch_ext_ratio == pytest.approx(1.35)
assert cfg.pinch_ext_min == 2
assert cfg.pinch_ext_min == 1
assert cfg.pinch_debounce_frames == 3
@@ -407,3 +407,9 @@ def test_hand_face_min_env_override():
"""HAND_FACE_MIN env var is parsed as float and stored in hand_face_min."""
cfg = VizConfig.from_env({"HAND_FACE_MIN": "0.3"})
assert cfg.hand_face_min == pytest.approx(0.3)
def test_viz_source_keys_default_off():
cfg = VizConfig.from_env({})
assert cfg.viz_source_keys is False
assert VizConfig.from_env({"VIZ_SOURCE_KEYS": "1"}).viz_source_keys is True