style: black format normalizer.py and test_normalizer.py

This commit is contained in:
pyr0ball 2026-04-26 20:05:54 -07:00
parent 460530bb03
commit 5a4917d455
2 changed files with 7 additions and 4 deletions

View file

@ -4,6 +4,7 @@ Landmark normalization for MediaPipe hand landmarks.
Converts raw (21, 3) landmark array into a 63-element translation- and Converts raw (21, 3) landmark array into a 63-element translation- and
scale-invariant feature vector suitable for gesture classifiers. scale-invariant feature vector suitable for gesture classifiers.
""" """
import numpy as np import numpy as np
@ -25,8 +26,8 @@ def normalize_hand(points: np.ndarray) -> np.ndarray:
(63,) float32 feature vector. (63,) float32 feature vector.
""" """
pts = points.astype(np.float32).copy() pts = points.astype(np.float32).copy()
pts -= pts[0] # translate: wrist → origin pts -= pts[0] # translate: wrist → origin
scale = float(np.linalg.norm(pts[9])) # wrist-to-middle-MCP distance scale = float(np.linalg.norm(pts[9])) # wrist-to-middle-MCP distance
if scale > 1e-6: if scale > 1e-6:
pts /= scale pts /= scale
return pts.flatten() return pts.flatten()

View file

@ -10,7 +10,7 @@ def _synthetic_hand(scale: float = 1.0, offset: float = 0.0) -> np.ndarray:
for i in range(21): for i in range(21):
pts[i] = [offset, 0.0, 0.0] pts[i] = [offset, 0.0, 0.0]
# Then define a few key landmarks relative to wrist # 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[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[1] = [offset + 0.1 * scale, 0.05 * scale, 0.0] # thumb
pts[5] = [offset + 0.4 * scale, 0.2 * scale, 0.0] # index pts[5] = [offset + 0.4 * scale, 0.2 * scale, 0.0] # index
@ -32,7 +32,9 @@ def test_translation_invariance():
def test_scale_invariance(): def test_scale_invariance():
pts_small = _synthetic_hand(scale=0.5) pts_small = _synthetic_hand(scale=0.5)
pts_large = _synthetic_hand(scale=2.0) 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(): def test_zero_scale_does_not_crash():