Spaces:
Sleeping
Sleeping
fix: model paths (.pth), landmark normalization, WS URL, GPU fallback; add ModelSelector; mobile layout improvements
c476eae | /** | |
| * Landmark utility functions — mirrors the backend preprocessing. | |
| * | |
| * The XGBoost model (Pipeline A) was trained on MediaPipe landmarks that are | |
| * already normalised to [0,1] relative to the image frame. The simplest | |
| * preprocessing that makes the model translation-invariant is to subtract the | |
| * wrist landmark (index 0) so every coordinate is relative to the wrist. | |
| * | |
| * ⚠️ If your training notebook used a different normalisation (e.g. min-max | |
| * scaling or only x/y without z), update normaliseLandmarks() to match. | |
| */ | |
| /** MediaPipe Hands result landmark shape */ | |
| export interface RawLandmark { | |
| x: number; | |
| y: number; | |
| z: number; | |
| } | |
| /** | |
| * Convert MediaPipe NormalizedLandmark[] (21 points) to a flat 63-element | |
| * array of raw [0,1]-normalised coordinates as expected by the trained models. | |
| * | |
| * The XGBoost, Autoencoder+LGBM models were trained directly on the raw | |
| * MediaPipe landmark coordinates (x, y, z per landmark, no wrist-centering). | |
| * Sending wrist-subtracted coords produces incorrect predictions. | |
| */ | |
| export function normaliseLandmarks(raw: RawLandmark[]): number[] { | |
| if (raw.length !== 21) { | |
| throw new Error(`Expected 21 landmarks, got ${raw.length}`); | |
| } | |
| const flat: number[] = []; | |
| for (const lm of raw) { | |
| flat.push(lm.x, lm.y, lm.z); | |
| } | |
| return flat; // length 63 | |
| } | |
| /** | |
| * MediaPipe hand topology — pairs of landmark indices that form bones. | |
| * Used by LandmarkCanvas to draw connections between landmarks. | |
| */ | |
| export const HAND_CONNECTIONS: [number, number][] = [ | |
| // Palm | |
| [0, 1], [1, 2], [2, 3], [3, 4], // Thumb | |
| [0, 5], [5, 6], [6, 7], [7, 8], // Index | |
| [5, 9], [9, 10], [10, 11], [11, 12], // Middle | |
| [9, 13], [13, 14], [14, 15], [15, 16], // Ring | |
| [13, 17], [17, 18], [18, 19], [19, 20], // Pinky | |
| [0, 17], // Wrist–pinky base | |
| ]; | |