Gesture Recognition from Motion Trajectories

Topics: Trajectory encoding, motion classification, time series, HCI Time: 15 minutes Prerequisites: 15_encoders_trajectory.py, 10_encoders_scalar.py Related: 20_app_text_classification.py, 21_app_image_recognition.py

This example demonstrates practical gesture recognition using trajectory encoding and hyperdimensional computing. Learn how to classify motion patterns from continuous trajectories.

Key concepts: - Trajectory encoding: Continuous paths in 2D/3D space - Temporal patterns: Motion sequences over time - Gesture classification: Similarity-based matching - Real-time processing: Efficient online recognition

Gesture recognition with HDC is fast, memory-efficient, and works well for real-time applications on edge devices (wearables, smartphones, etc.).

 24 import numpy as np
 25 from holovec import VSA
 26 from holovec.encoders import TrajectoryEncoder, FractionalPowerEncoder
 27 from holovec.retrieval import ItemStore
 28
 29 print("=" * 70)
 30 print("Gesture Recognition from Motion Trajectories")
 31 print("=" * 70)
 32 print()
 33
 34 # Create model and encoder
 35 model = VSA.create('FHRR', dim=10000, seed=42)
 36
 37 # Trajectory encoder for 2D motion
 38 # Use a single scalar encoder for all dimensions (x, y)
 39 scalar_encoder = FractionalPowerEncoder(model, min_val=-1, max_val=1, bandwidth=0.1, seed=42)
 40
 41 trajectory_encoder = TrajectoryEncoder(
 42     model,
 43     scalar_encoder=scalar_encoder,
 44     n_dimensions=2,
 45     seed=44
 46 )
 47
 48 print(f"Model: {model.model_name}, dimension={model.dimension}")
 49 print(f"Trajectory encoder: 2D motion, 20 time steps")
 50 print()
 51
 52 # ============================================================================
 53 # Dataset: Simple 2D Gestures
 54 # ============================================================================
 55 print("=" * 70)
 56 print("Dataset: Simple 2D Gesture Patterns")
 57 print("=" * 70)
 58
 59 np.random.seed(42)
 60
 61 # Define gesture generators
 62 def create_circle(noise=0.0):
 63     """Circular motion (clockwise)."""
 64     t = np.linspace(0, 2*np.pi, 20)
 65     x = 0.5 * np.cos(t) + np.random.randn(20) * noise
 66     y = 0.5 * np.sin(t) + np.random.randn(20) * noise
 67     return np.column_stack([x, y])
 68
 69 def create_line_horizontal(noise=0.0):
 70     """Horizontal line (left to right)."""
 71     t = np.linspace(-0.8, 0.8, 20)
 72     x = t + np.random.randn(20) * noise
 73     y = np.zeros(20) + np.random.randn(20) * noise
 74     return np.column_stack([x, y])
 75
 76 def create_line_vertical(noise=0.0):
 77     """Vertical line (bottom to top)."""
 78     t = np.linspace(-0.8, 0.8, 20)
 79     x = np.zeros(20) + np.random.randn(20) * noise
 80     y = t + np.random.randn(20) * noise
 81     return np.column_stack([x, y])
 82
 83 def create_zigzag(noise=0.0):
 84     """Zigzag pattern."""
 85     t = np.linspace(0, 4*np.pi, 20)
 86     x = np.linspace(-0.8, 0.8, 20) + np.random.randn(20) * noise
 87     y = 0.3 * np.sin(3*t) + np.random.randn(20) * noise
 88     return np.column_stack([x, y])
 89
 90 # Generate training examples
 91 print("\nGenerating training gestures (4 classes, 5 examples each):")
 92
 93 training_data = []
 94 noise_level = 0.05
 95
 96 # Circle gestures
 97 for i in range(5):
 98     traj = create_circle(noise=noise_level)
 99     training_data.append((traj, "circle"))
100
101 # Horizontal lines
102 for i in range(5):
103     traj = create_line_horizontal(noise=noise_level)
104     training_data.append((traj, "horizontal"))
105
106 # Vertical lines
107 for i in range(5):
108     traj = create_line_vertical(noise=noise_level)
109     training_data.append((traj, "vertical"))
110
111 # Zigzags
112 for i in range(5):
113     traj = create_zigzag(noise=noise_level)
114     training_data.append((traj, "zigzag"))
115
116 print(f"  circle:     5 examples")
117 print(f"  horizontal: 5 examples")
118 print(f"  vertical:   5 examples")
119 print(f"  zigzag:     5 examples")
120 print(f"\nTotal: {len(training_data)} training gestures")
121
122 # ============================================================================
123 # Training: Build Gesture Prototypes
124 # ============================================================================
125 print("\n" + "=" * 70)
126 print("Training: Building Gesture Prototypes")
127 print("=" * 70)
128
129 # Group by class
130 gesture_classes = {}
131 for traj, label in training_data:
132     if label not in gesture_classes:
133         gesture_classes[label] = []
134     gesture_classes[label].append(traj)
135
136 # Encode and bundle per class
137 gesture_prototypes = {}
138
139 print("\nEncoding gesture patterns:")
140 for label, trajectories in gesture_classes.items():
141     encoded = [trajectory_encoder.encode(traj) for traj in trajectories]
142     prototype = model.bundle(encoded)
143     gesture_prototypes[label] = prototype
144     print(f"  {label:12s}: {len(trajectories)} examples → prototype")
145
146 print(f"\nGesture prototypes created: {len(gesture_prototypes)}")
147
148 # ============================================================================
149 # Recognition: Test on New Gestures
150 # ============================================================================
151 print("\n" + "=" * 70)
152 print("Recognition: Testing on New Gestures")
153 print("=" * 70)
154
155 # Create test gestures with moderate noise
156 test_gestures = [
157     (create_circle(noise=0.08), "circle"),
158     (create_line_horizontal(noise=0.08), "horizontal"),
159     (create_line_vertical(noise=0.08), "vertical"),
160     (create_zigzag(noise=0.08), "zigzag"),
161 ]
162
163 print("\nRecognizing test gestures:")
164 print()
165
166 correct = 0
167 for i, (traj, expected) in enumerate(test_gestures, 1):
168     # Encode test gesture
169     test_hv = trajectory_encoder.encode(traj)
170
171     # Find most similar prototype
172     best_label = None
173     best_sim = float('-inf')
174
175     for label, prototype in gesture_prototypes.items():
176         sim = float(model.similarity(test_hv, prototype))
177         if sim > best_sim:
178             best_sim = sim
179             best_label = label
180
181     is_correct = (best_label == expected)
182     correct += (1 if is_correct else 0)
183     marker = "✓" if is_correct else "✗"
184
185     print(f"{i}. Gesture: {expected:12s}")
186     print(f"   Recognized: {best_label:12s} (similarity={best_sim:.3f}) {marker}")
187     print()
188
189 accuracy = correct / len(test_gestures)
190 print(f"Accuracy: {correct}/{len(test_gestures)} = {accuracy:.1%}")
191
192 # ============================================================================
193 # Analysis: Gesture Similarity
194 # ============================================================================
195 print("\n" + "=" * 70)
196 print("Analysis: Gesture Confusion Matrix")
197 print("=" * 70)
198
199 print("\nSimilarity between gesture prototypes:")
200 labels = sorted(gesture_prototypes.keys())
201
202 # Print header
203 print(f"{'':12s}", end="")
204 for label in labels:
205     print(f" {label:>10s}", end="")
206 print()
207
208 # Print matrix
209 for label1 in labels:
210     print(f"{label1:12s}", end="")
211     for label2 in labels:
212         sim = float(model.similarity(gesture_prototypes[label1],
213                                       gesture_prototypes[label2]))
214         print(f" {sim:10.3f}", end="")
215     print()
216
217 print("\nKey observation:")
218 print("  - Diagonal = 1.0 (self-similarity)")
219 print("  - Horizontal & vertical somewhat similar (both lines)")
220 print("  - Circle & zigzag clearly distinct")
221
222 # ============================================================================
223 # Real-Time Considerations
224 # ============================================================================
225 print("\n" + "=" * 70)
226 print("Real-Time Gesture Recognition")
227 print("=" * 70)
228
229 print("\n⚡ Real-time processing advantages:")
230 print("  - Fast encoding: ~1ms for 20-point trajectory")
231 print("  - Immediate classification: Single similarity computation")
232 print("  - Memory efficient: Fixed-size hypervectors")
233 print("  - Incremental: Can process partial gestures")
234 print("  - No GPU required: Runs on CPU, microcontrollers")
235 print()
236
237 print("Implementation tips:")
238 print("  - Sample trajectory at fixed rate (e.g., 20 points/sec)")
239 print("  - Normalize to [-1, 1] range before encoding")
240 print("  - Use sliding window for continuous recognition")
241 print("  - Threshold similarity for rejection (unknown gestures)")
242 print("  - Retrain prototypes with user-specific data")
243 print()
244
245 # ============================================================================
246 # Extension: Multi-User Recognition System
247 # ============================================================================
248 print("=" * 70)
249 print("Extension: Multi-User Gesture Library")
250 print("=" * 70)
251
252 # Build gesture library with ItemStore
253 gesture_library = ItemStore(model)
254 for label, prototype in gesture_prototypes.items():
255     gesture_library.add(label, prototype)
256
257 print(f"\nGesture library built with {len(gesture_prototypes)} gestures")
258
259 # Test with ambiguous gesture (partial circle)
260 t = np.linspace(0, np.pi, 20)  # Half circle
261 partial_x = 0.5 * np.cos(t) + np.random.randn(20) * 0.05
262 partial_y = 0.5 * np.sin(t) + np.random.randn(20) * 0.05
263 partial_circle = np.column_stack([partial_x, partial_y])
264
265 test_hv = trajectory_encoder.encode(partial_circle)
266
267 # Query library
268 results = gesture_library.query(test_hv, k=4)
269
270 print("\nTest gesture: partial circle (first half only)")
271 print("\nTop matches:")
272 for i, (label, sim) in enumerate(results, 1):
273     print(f"  {i}. {label:12s}: {sim:.3f}")
274
275 print("\nKey observation:")
276 print("  - Partial gestures still match similar patterns")
277 print("  - Can examine top-k for ambiguous cases")
278 print("  - Threshold similarity to reject uncertain gestures")
279
280 # ============================================================================
281 # Practical Considerations
282 # ============================================================================
283 print("\n" + "=" * 70)
284 print("Practical Considerations")
285 print("=" * 70)
286
287 print("\n✓ Advantages of HDC Gesture Recognition:")
288 print("  - Fast: Real-time processing on edge devices")
289 print("  - Efficient: Low memory and compute requirements")
290 print("  - Robust: Tolerant to noise and variations")
291 print("  - Adaptable: Easy to add new gesture classes")
292 print("  - Interpretable: Similarity scores show confidence")
293 print()
294
295 print("✗ Limitations:")
296 print("  - Fixed length: Requires normalizing to fixed points")
297 print("  - Simple patterns: Best for distinct gestures")
298 print("  - No learning: Doesn't adapt like neural networks")
299 print("  - Temporal detail: May miss fine-grained timing")
300 print()
301
302 print("Best use cases:")
303 print("  - Wearable devices (smartwatches, fitness trackers)")
304 print("  - Smartphone gesture controls")
305 print("  - Sign language recognition (simple gestures)")
306 print("  - Robot control (motion commands)")
307 print("  - VR/AR interaction (hand tracking)")
308 print()
309
310 # ============================================================================
311 # Summary
312 # ============================================================================
313 print("=" * 70)
314 print("Summary: Gesture Recognition with HDC")
315 print("=" * 70)
316 print()
317 print("Complete workflow:")
318 print("  1. Setup: Create model + TrajectoryEncoder")
319 print("  2. Training: Encode trajectories + bundle per gesture")
320 print("  3. Recognition: Encode test trajectory + find nearest prototype")
321 print("  4. Deployment: Real-time recognition with similarity threshold")
322 print()
323 print("Performance tips:")
324 print("  - Normalize trajectories to consistent range")
325 print("  - Use ~20-30 time steps for good temporal resolution")
326 print("  - More training examples → better noise tolerance")
327 print("  - Consider user-specific adaptation (personalization)")
328 print()
329 print("Next steps:")
330 print("  → Try with real accelerometer/gyroscope data")
331 print("  → Extend to 3D trajectories (x, y, z)")
332 print("  → Implement sliding window for continuous recognition")
333 print("  → Combine with 25_app_integration_patterns.py for multimodal")
334 print()
335 print("=" * 70)

Gallery generated by Sphinx-Gallery