← Back to portfolio
Case Study · 14 / 17
Computer Vision · Real-Time · 2025

Ten hand signs,
read and spoken aloud.

A real-time gesture recogniser: MediaPipe finds 21 hand landmarks, a small TensorFlow model classifies the pose, and gTTS speaks the result. The interesting lesson lived in one word — normalisation — and why the first version only worked in the middle of the frame.

Role
Solo — CV & tooling
Discipline
Landmark classification
Stack
MediaPipe · TensorFlow · OpenCV · gTTS
Status
Open-source, trainable
Fig. 14 · 21 landmarks → label
14
A gesture is a shape, not a location — the model had to learn the difference.
§ 01 — The problem

Raw pixels are the wrong feature for a hand.

Classifying gestures straight from webcam pixels forces a model to learn lighting, skin tone, backgrounds, and hand position all at once — most of which have nothing to do with the gesture. The signal that actually matters is the geometry of the hand: where the fingers are relative to each other.

MediaPipe hands that geometry over directly as 21 landmark points, so the real design question becomes how to turn those coordinates into a feature a small classifier can learn from reliably.

Recognition shouldn't care where your hand is, only what it's doing.

§ 02 — Constraints

What the recogniser had to do.

§ 03 — Architecture

Detect, normalise, classify, speak.

Each frame runs through MediaPipe for landmarks, a normalisation step to make the pose position- and scale-invariant, a TensorFlow classifier for the label, and a threaded audio manager for spoken feedback.

01 — Detect
MediaPipe
21 hand landmarks extracted per frame from the webcam feed.
02 — Normalise
Wrist-relative
Landmarks re-centred on the wrist and scaled — invariant to position.
03 — Classify
TensorFlow
A model maps the normalised pose to one of ten gestures, with smoothing.
04 — Speak
gTTS audio
Non-blocking text-to-speech announces the recognised gesture.

The codebase is organised into a proper package — tracking, services (audio, smoothing, performance), and UI — rather than one long script, with prediction smoothing to stop the label flickering between frames.

§ 04 — What broke first

The model learned position, not gesture.

The original recorder stored raw pixel coordinates, so an early model quietly learned "thumbs up in the centre of the frame." Move your hand to the corner and recognition fell apart — it had memorised where the training hands sat, not the shape they made.

The fix was wrist-relative, scale-invariant normalisation: re-centre every landmark set on the wrist and normalise its size, so the same gesture looks identical anywhere in the frame at any distance. It's the single change that turned a demo into something that actually generalises — and any model trained before it needs a retrain.

21
Hand landmarks per frame from MediaPipe
10
Gestures recognised out of the box
0
Dependence on where the hand sits in frame
§ 05 — Making it usable

A tool, not a script.

The recogniser ships as a CLI with distinct modes — recognise, record, train, settings — plus a settings dialog and environment-variable config for detection sensitivity and camera index. That's what lets someone extend it to their own gestures instead of forking and editing constants, and it's why the normalisation lives in shared code both training and inference call.

§ 06 — What I'd do differently

Three things, next time.