Note
Go to the end to download the full example code.
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)