feat: read torso, head and face to recognise signs better - #23
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
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
zentirely 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) andwindowSignature(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.pywrites a synthetic frame sequence and Python's signature for it;vocabularySignature.test.tsrebuilds 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.