A modular, production-ready computer vision and machine learning framework designed to evaluate the failure modes and degradation thresholds of landmark-based hand gesture recognition under contiguous physical occlusions.
In real-world human-computer interaction (HCI) and robotic perception, hand gestures frequently suffer from partial occlusion (e.g., hands holding objects, sleeves, microphone sticks, or camera framing cuts).
Standard vision pipelines rely on intermediate keypoint estimators (such as MediaPipe Hands). This project systematically measures:
-
Landmark Detector Failure Thresholds: At what occlusion percentage (
$0%, 10%, 20%, 30%, 45%, 60%$ ) does MediaPipe fail to detect landmark topologies? - Classifier Robustness: When landmarks are partially extracted under occlusion, how well does the downstream machine learning classifier (Random Forest, MLP, SVM) maintain gesture discrimination?
- Generalization via Invariant Landmark Normalization: Eliminates sensitivity to hand size, position in frame, and distance from camera.
Raw MediaPipe landmarks provide 21 coordinates
-
Translation Invariance (Wrist Centering): Subtract the wrist landmark
$\vec{p}_0 = (x_0, y_0, z_0)$ from all 21 points:$$\vec{p}'_i = \vec{p}_i - \vec{p}_0 \quad \forall i \in {0, 1, \dots, 20}$$ Result: Landmark 0 (wrist) is always located exactly at$(0, 0, 0)$ . -
Scale Invariance (Radial Normalization): Compute the maximum Euclidean distance from the wrist to any keypoint/fingertip:
$$d_{\max} = \max_{i \in {0, \dots, 20}} |\vec{p}'_i|_2$$ Scale all coordinates: $$\vec{p}''_i = \frac{\vec{p}'i}{d{\max} + \epsilon}$$ Result: All landmark coordinates are strictly bounded within$[-1.0, 1.0]$ , independent of hand size or camera distance. -
Vector Flattening: The normalized coordinates are flattened into a 1D feature vector of length 63 (
$21 \times 3$ ).
hand_gesture_occlusion_benchmark/
│
├── data/
│ ├── raw/ # Clean gesture images organized by class subfolders
│ │ ├── fist/
│ │ ├── ok/
│ │ ├── peace/
│ │ ├── pointing/
│ │ ├── thumbs_down/
│ │ └── thumbs_up/
│ └── occluded/ # Generated occluded images across benchmark levels
│
├── src/
│ ├── __init__.py
│ ├── data_fetcher.py # Automated streaming downloader for public datasets (HaGRID)
│ ├── dataset_collector.py # Interactive OpenCV webcam recording utility & mock generator
│ ├── occluder.py # Synthetic contiguous block/cutout occlusion generator
│ ├── feature_extractor.py # MediaPipe Hands wrapper with invariant normalization
│ ├── model.py # Classifier training (RandomForest / MLP / SVM) & export
│ ├── benchmark.py # Systematic stress-testing loop over occlusion levels
│ └── visualize.py # Plots: Accuracy curves, drop-off rates, confusion matrices
│
├── outputs/ # Benchmark CSV logs and publication-ready figures
├── models/ # Serialized classifier bundle & test split manifest
├── main.py # CLI pipeline runner connecting all modules
├── requirements.txt # Core dependencies
└── README.md # Project documentation
-
Clone or navigate to the repository:
cd "hand_gesture_occlusion_benchmark"
-
Create and activate a virtual environment (optional but recommended):
python -m venv venv # On Windows: venv\Scripts\activate # On Linux/macOS: source venv/bin/activate
-
Install dependencies:
pip install -r requirements.txt
main.py provides an intuitive command-line interface for each phase of the workflow:
Automate data collection with diverse public hands from Hugging Face:
python main.py --fetch --samples 200Open the interactive OpenCV tool to record ~50 samples per class with a 3-second countdown:
python main.py --collect --samples 50Webcam Controls:
[SPACE]: Begin 3-second countdown to record a 20-frame burst.[N]: Cycle to next gesture class.[P]: Cycle to previous gesture class.[Q]: Save and quit.
Generate synthetic hand gesture images immediately to test the entire pipeline:
python main.py --mock-data --samples 40Extract 63-dimensional normalized landmarks, split into 80/20 train/test sets, and train the model:
# Default: Random Forest
python main.py --train --model-type random_forest
# Or train Multi-Layer Perceptron (MLP) or Support Vector Machine (SVM):
python main.py --train --model-type mlpOutput: Serialized bundle saved to models/gesture_classifier.joblib and test split to models/test_split.json.
Evaluate held-out test frames against synthetic block occlusions across [0%, 10%, 20%, 30%, 45%, 60%]:
python main.py --benchmarkMetrics track:
- MediaPipe detection rate (% frames where landmarks were successfully recovered).
- Overall classification accuracy (% correct out of all test frames).
- Conditional accuracy (% correct among frames where detector succeeded).
Render high-resolution figures in outputs/:
python main.py --visualizeGenerated charts:
outputs/accuracy_vs_occlusion.png: Line plot of overall accuracy and per-class curves vs. occlusion %.outputs/detection_dropoff.png: MediaPipe detector failure curve highlighting the critical breakdown threshold.outputs/confusion_matrices.png: Side-by-side heatmaps showing misclassification shifts at 0%, 30%, and 60% occlusion.outputs/occlusion_visual_grid.png: Montage grid showing sample images across the occlusion spectrum.
To run data preparation, model training, benchmarking, and visualization in a single command:
python main.py --allUnlike simple salt-and-pepper noise, real physical obstructions (such as hands obstructed by coffee mugs or sleeves) are contiguous.
src/occluder.py calculates the hand region-of-interest (ROI) using landmark bounding boxes and places contiguous rectangular blocks simulating:
- Solid black blocks (simulating opaque physical blockage).
- Random textured noise blocks (simulating irregular surface occlusions).
- Gaussian blur cutouts (simulating depth-of-field or sensor smudge).
The area of the block corresponds strictly to the percentage parameter: $$\text{Area}{\text{block}} = \text{occlusion_pct} \times (\text{Width}{\text{hand}} \times \text{Height}_{\text{hand}})$$
The benchmark logs every individual trial to outputs/benchmark_results.csv:
Aggregated metrics are saved to outputs/benchmark_summary.csv.
