diff --git a/circuitforge_core/input/gestures/normalizer.py b/circuitforge_core/input/gestures/normalizer.py index 259b4ec..26c33b4 100644 --- a/circuitforge_core/input/gestures/normalizer.py +++ b/circuitforge_core/input/gestures/normalizer.py @@ -4,6 +4,7 @@ Landmark normalization for MediaPipe hand landmarks. Converts raw (21, 3) landmark array into a 63-element translation- and scale-invariant feature vector suitable for gesture classifiers. """ + import numpy as np @@ -25,8 +26,8 @@ def normalize_hand(points: np.ndarray) -> np.ndarray: (63,) float32 feature vector. """ pts = points.astype(np.float32).copy() - pts -= pts[0] # translate: wrist → origin - scale = float(np.linalg.norm(pts[9])) # wrist-to-middle-MCP distance + pts -= pts[0] # translate: wrist → origin + scale = float(np.linalg.norm(pts[9])) # wrist-to-middle-MCP distance if scale > 1e-6: pts /= scale return pts.flatten() diff --git a/tests/test_input/test_gestures/test_normalizer.py b/tests/test_input/test_gestures/test_normalizer.py index cff2d6d..b5bcc7e 100644 --- a/tests/test_input/test_gestures/test_normalizer.py +++ b/tests/test_input/test_gestures/test_normalizer.py @@ -10,7 +10,7 @@ def _synthetic_hand(scale: float = 1.0, offset: float = 0.0) -> np.ndarray: for i in range(21): pts[i] = [offset, 0.0, 0.0] # Then define a few key landmarks relative to wrist - pts[0] = [offset, 0.0, 0.0] # wrist + pts[0] = [offset, 0.0, 0.0] # wrist pts[9] = [offset + scale, 0.0, 0.0] # middle MCP at distance scale from wrist pts[1] = [offset + 0.1 * scale, 0.05 * scale, 0.0] # thumb pts[5] = [offset + 0.4 * scale, 0.2 * scale, 0.0] # index @@ -32,7 +32,9 @@ def test_translation_invariance(): def test_scale_invariance(): pts_small = _synthetic_hand(scale=0.5) pts_large = _synthetic_hand(scale=2.0) - np.testing.assert_allclose(normalize_hand(pts_small), normalize_hand(pts_large), atol=1e-5) + np.testing.assert_allclose( + normalize_hand(pts_small), normalize_hand(pts_large), atol=1e-5 + ) def test_zero_scale_does_not_crash():