feat(pose): wrist-anchored xy mods + wrist markers
CI build oscope-of / build-check (push) Has been cancelled
CI build oscope-of / build-check (push) Has been cancelled
The X/Y voice mods were anchored to the 21-landmark centroid, so opening or pinching the fingers shifted the mod values (gesture/mod cross-talk). cx/cy now track the WRIST (kp 0). The renderer draws a cross marker at each wrist on the video whenever features exist, so the control point stays visible even for far hands the overlay filters out (user request live 2026-07-02).
This commit is contained in:
@@ -62,8 +62,12 @@ class HandFeatureExtractor:
|
|||||||
def _features(self, lm: list) -> dict:
|
def _features(self, lm: list) -> dict:
|
||||||
xs = [_finite(_coord(p, "x", 0), 0.5) for p in lm[:21]]
|
xs = [_finite(_coord(p, "x", 0), 0.5) for p in lm[:21]]
|
||||||
ys = [_finite(_coord(p, "y", 1), 0.5) for p in lm[:21]]
|
ys = [_finite(_coord(p, "y", 1), 0.5) for p in lm[:21]]
|
||||||
cx = _clamp(sum(xs) / len(xs), 0.0, 1.0)
|
# cx/cy anchor = the WRIST (kp 0), not the landmark centroid: the
|
||||||
cy = _clamp(sum(ys) / len(ys), 0.0, 1.0)
|
# centroid shifts when fingers open/pinch, cross-talking gestures
|
||||||
|
# into the X/Y voice mods. The wrist decouples them (user request
|
||||||
|
# live 2026-07-02).
|
||||||
|
cx = _clamp(xs[WRIST], 0.0, 1.0)
|
||||||
|
cy = _clamp(ys[WRIST], 0.0, 1.0)
|
||||||
size = math.hypot(xs[MIDDLE_MCP] - xs[WRIST], ys[MIDDLE_MCP] - ys[WRIST])
|
size = math.hypot(xs[MIDDLE_MCP] - xs[WRIST], ys[MIDDLE_MCP] - ys[WRIST])
|
||||||
size = size if size > 1e-4 else 1e-4
|
size = size if size > 1e-4 else 1e-4
|
||||||
span = math.hypot(xs[THUMB_TIP] - xs[PINKY_TIP],
|
span = math.hypot(xs[THUMB_TIP] - xs[PINKY_TIP],
|
||||||
|
|||||||
@@ -575,6 +575,16 @@ class MetalRenderer(NSObject):
|
|||||||
content_pid=pid_s,
|
content_pid=pid_s,
|
||||||
):
|
):
|
||||||
push_panel(ax, ay, bx, by, gconf, gpid)
|
push_panel(ax, ay, bx, by, gconf, gpid)
|
||||||
|
# Wrist marker ON the video: the X/Y voice mods are anchored
|
||||||
|
# to the wrist, so its control point stays visible whenever
|
||||||
|
# features exist (even for far hands the overlay filters
|
||||||
|
# out). push_seg applies the video mirror like the overlay.
|
||||||
|
if _g_cx is not None and _g_cy is not None:
|
||||||
|
_mx = 0.015 / asp
|
||||||
|
push_seg(_g_cx - _mx, _g_cy, _g_cx + _mx, _g_cy,
|
||||||
|
1.0, pid_s)
|
||||||
|
push_seg(_g_cx, _g_cy - 0.015, _g_cx, _g_cy + 0.015,
|
||||||
|
1.0, pid_s)
|
||||||
if h_kp is not None:
|
if h_kp is not None:
|
||||||
for seg in panel_segments(
|
for seg in panel_segments(
|
||||||
h_kp, side_name, lhand_bones_p, asp,
|
h_kp, side_name, lhand_bones_p, asp,
|
||||||
|
|||||||
Reference in New Issue
Block a user