Signia’s hand-sign recognition pipeline uses a scikit-learnDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/jtapieromalambo-ctrl/Signia/llms.txt
Use this file to discover all available pages before exploring further.
RandomForestClassifier to map sequences of MediaPipe hand landmarks to LSC sign labels. The model is trained on video files uploaded through the admin panel, enriched with eight data augmentation variations per source video, and then combined with any previously accumulated dataset so that each training run is incremental rather than destructive to historical signs. This page documents the model files, feature engineering pipeline, training procedure, and the thread-safety guarantees that govern inference.
Model and Data Files
| Path | Description |
|---|---|
reconocimientos/modelo/model_seq.pkl | Serialized RandomForestClassifier — loaded at Django startup |
reconocimientos/modelo/encoder_seq.pkl | Serialized LabelEncoder — maps integer predictions back to sign label strings |
reconocimientos/datos/X_seq.npy | Accumulated feature matrix (NumPy array, object dtype to handle variable feature lengths across training runs) |
reconocimientos/datos/y_seq.npy | Accumulated label array corresponding to X_seq.npy |
reconocimientos/datos/hand_landmarker.task | MediaPipe HandLandmarker model file used by the server-side detector during training |
Model Hyperparameters
The classifier is instantiated with the following fixed hyperparameters inreconocimientos/views.py:
class_weight='balanced' compensates for classes with fewer training samples. n_jobs=-1 uses all available CPU cores during fitting and prediction.
Training Process
Training is triggered by aPOST request to /reconocimientos/admin-videos/entrenar/ from the admin panel. The entire process runs in a background daemon thread to avoid blocking the HTTP response.
Upload sign videos
Navigate to
/reconocimientos/admin-videos/ and upload one or more sign videos using the Subir video de seña form. Each upload creates a VideoSeña record in the database with a label (the sign name) and the video file stored under media/video_señas/.Trigger training
Click Entrenar modelo in the admin panel. The server spawns a daemon thread and immediately returns
{"ok": true}. Poll /reconocimientos/estado-entrenamiento/ to check {"activo": true|false}.Extract landmarks with MediaPipe
For each
VideoSeña, the thread opens the video file with OpenCV, reads every frame, and passes each frame through the thread-local MediaPipe HandLandmarker. Frames where no hand is detected are skipped. If fewer than 5 frames have detectable hands, the video is skipped entirely.Centroid normalization
Each frame’s raw landmark list (up to 126 floats — 21 landmarks × 3 coordinates × 2 hands) is passed to
_normalizar_landmarks_centroide(). The function subtracts the centroid of each hand’s 21 points, making the features invariant to where on-screen the hands appear.Generate 8 augmentation variations
aumentar_secuencia() takes the normalized frame sequence and produces 8 variations. See the Data Augmentation section below.Normalize to 30 frames
Each variation is passed through
normalizar_secuencia(), which uses numpy.interp to linearly resample the sequence to exactly FRAMES_OBJETIVO = 30 frames regardless of the original video length.Build feature vectors
construir_features() concatenates: flattened normalized positions (30 × 126 floats), frame-to-frame deltas (29 × 126 floats), and delta magnitudes (29 floats) into a single feature vector per sample.Combine with accumulated dataset
If
X_seq.npy and y_seq.npy already exist, the new samples are appended to the historical data. Feature lengths are reconciled by zero-padding shorter vectors to match the widest vector in the combined set.Train RandomForest
A new
RandomForestClassifier is fitted on the full combined dataset with a fresh LabelEncoder. Training uses all CPU cores (n_jobs=-1).Save model and dataset
model_seq.pkl, encoder_seq.pkl, X_seq.npy, and y_seq.npy are written to disk. The in-memory modelo and encoder module globals are updated atomically so that inference immediately picks up the new model.Feature Extraction Pipeline
FRAMES_OBJETIVO = 30 is defined at the top of reconocimientos/views.py and is used consistently during both training and inference.
Data Augmentation
aumentar_secuencia() returns a list of 8 NumPy arrays from a single source sequence:
| # | Variation | Implementation |
|---|---|---|
| 1 | Original | Source sequence unchanged |
| 2 | Gaussian noise (small) | + np.random.normal(0, 0.008, shape) — simulates natural hand tremor |
| 3 | Scale | × uniform(0.93, 1.07) — simulates distance variation from the camera |
| 4 | Speed variation | Resample to a random length of 20–45 frames — simulates signing at different speeds |
| 5 | Horizontal mirror | Invert every X coordinate (x = 1.0 - x) — simulates left-handed signers |
| 6 | Random translation | Add dx, dy ∈ uniform(-0.08, 0.08) to X and Y — simulates hand position shift |
| 7 | Stronger Gaussian noise | + np.random.normal(0, 0.018, shape) — robustness against intense tremor |
| 8 | Temporal reverse | sequence[::-1] — useful for temporal symmetry in symmetric signs |
Incremental Training
Each training run merges new videos with the existing accumulated dataset:DELETE /reconocimientos/sena/<nombre>/ endpoint, which filters the sign from X_seq.npy/y_seq.npy and retrains automatically.
Effectiveness Calculation
The admin panel displays a per-sign effectiveness percentage. This is not cross-validation accuracy — it is computed from the leaf purity of the trained RandomForest estimators and then linearly mapped to the range 55 %–99 %:Thread Safety
MediaPipe
HandLandmarker is not thread-safe. reconocimientos/views.py uses threading.local() to maintain one detector instance per worker thread. The _get_detector() helper creates a new HandLandmarker the first time any given thread calls it, then reuses that instance for all subsequent calls on the same thread._entrenando boolean flag prevents two concurrent training runs from interfering with each other:
{"activo": false} from the status endpoint before redeploying.