Skip to content

feat: read torso, head and face to recognise signs better - #23

Merged
Endika merged 1 commit into
mainfrom
feat/body-aware-model
Aug 5, 2026
Merged

feat: read torso, head and face to recognise signs better#23
Endika merged 1 commit into
mainfrom
feat/body-aware-model

Conversation

@Endika

@Endika Endika commented Aug 5, 2026

Copy link
Copy Markdown
Owner

0.632 → 0.741 top-1, 0.826 → 0.870 top-3, on SWL-LSE's held-out test split. The model was reading hands in a vacuum; now it reads them against the body.

Everything measured, nothing assumed

Input top-1 top-3
hands only, 8 frames (what shipped) 0.632 0.826
+ hand position relative to the torso 0.689 0.836
+ 16 frames instead of 8 0.719 0.849
+ torso and head orientation 0.729 0.865
+ facial expression, 6 scalars 0.741 0.870

Rejected with numbers rather than opinions: motion deltas (0.666), raw face coordinates (0.702), input augmentation (0.699).

Why the body matters so much. "Hand at chin height" is a fixed number in body coordinates and a moving one in image coordinates — it changes the moment the signer steps closer. Normalising against shoulder width makes it invariant, and location is phonemic in LSE.

Why six face scalars beat sixty face coordinates. Eyebrow raise, eye and mouth openness, mouth width — as ratios of face width. With ~27 examples per class, handing the model raw coordinates it would have to derive those from is capacity spent memorising faces. Measured: 0.741 with the ratios, 0.702 with the coordinates.

On depth. Dropping z entirely costs 0.003. MediaPipe infers it from a single camera rather than measuring it, so it carries far less than it appears to. Kept, since it does not hurt, but it is not where the gains are.

The design change that matters more than the accuracy

Taught signs and the trained model used to share one signature function, so improving the model invalidated every sign the user had recorded — that already happened once, in #16. They are now vocabularySignature (moves with the model) and windowSignature (frozen, owns user data). This change costs users nothing.

Parity, again

The app and the trainer build the model's 2,384-float input independently, and a mismatch does not throw — it predicts noise. make_parity.py writes a synthetic frame sequence and Python's signature for it; vocabularySignature.test.ts rebuilds the same frames in TypeScript and compares element by element. Caught a real discrepancy while writing it, in the harness rather than the code: the cached dataset stores 21 selected face landmarks while the app indexes the full 478-point mesh, so each side has to be fed the shape it actually receives.

Pose and face degrade gracefully — if either model fails to load, the signature zeroes those blocks and fingerspelling carries on.

@Endika
Endika merged commit 99d79c7 into main Aug 5, 2026
7 checks passed
@Endika
Endika deleted the feat/body-aware-model branch August 5, 2026 22:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant