hand-tracking-teleop 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of hand-tracking-teleop might be problematic. Click here for more details.

@@ -0,0 +1,17 @@
1
+ """Hand tracking package for robot teleoperation.
2
+
3
+ This package provides real-time hand tracking using MediaPipe with
4
+ scale-invariant 3D coordinate normalization for robust robot control.
5
+ """
6
+
7
+ __version__ = "0.1.0"
8
+ __author__ = "Bruk Gebregziabher"
9
+ __email__ = "bruk@signalbotics.com"
10
+
11
+ from .hand_detector import HandDetector, HandLandmarks
12
+
13
+ __all__ = [
14
+ "HandDetector",
15
+ "HandLandmarks",
16
+ "__version__",
17
+ ]
hand_tracking/cli.py ADDED
@@ -0,0 +1,279 @@
1
+ #!/usr/bin/env python3
2
+ """Command-line interface for hand tracking package."""
3
+
4
+ import argparse
5
+ import sys
6
+ from pathlib import Path
7
+ from .hand_detector import HandDetector
8
+
9
+
10
+ def main():
11
+ """Main CLI entry point for 2D hand tracking."""
12
+ parser = argparse.ArgumentParser(
13
+ description="Hand tracking for robot teleoperation"
14
+ )
15
+ parser.add_argument(
16
+ "--config",
17
+ type=str,
18
+ help="Path to configuration YAML file",
19
+ default=None,
20
+ )
21
+ parser.add_argument(
22
+ "--camera",
23
+ type=int,
24
+ help="Camera device ID",
25
+ default=0,
26
+ )
27
+
28
+ args = parser.parse_args()
29
+
30
+ try:
31
+ detector = HandDetector(config_path=args.config)
32
+ print("Starting hand tracking (2D mode)...")
33
+ print("Press 'q' to quit")
34
+ detector.run_interactive(mode='2d')
35
+ except KeyboardInterrupt:
36
+ print("\nShutting down...")
37
+ except Exception as e:
38
+ print(f"Error: {e}")
39
+ sys.exit(1)
40
+
41
+
42
+ def main_3d():
43
+ """CLI entry point for 3D hand tracking visualization."""
44
+ parser = argparse.ArgumentParser(
45
+ description="3D hand tracking visualization"
46
+ )
47
+ parser.add_argument(
48
+ "--config",
49
+ type=str,
50
+ help="Path to configuration YAML file",
51
+ default=None,
52
+ )
53
+
54
+ args = parser.parse_args()
55
+
56
+ try:
57
+ detector = HandDetector(config_path=args.config)
58
+ print("Starting 3D hand tracking visualization...")
59
+ print("Controls: Rotate (drag), Zoom (scroll), Close window to exit")
60
+ detector.run_interactive(mode='3d')
61
+ except KeyboardInterrupt:
62
+ print("\nShutting down...")
63
+ except Exception as e:
64
+ print(f"Error: {e}")
65
+ sys.exit(1)
66
+
67
+
68
+ def main_combined():
69
+ """CLI entry point for combined 2D + 3D visualization."""
70
+ parser = argparse.ArgumentParser(
71
+ description="Combined 2D + 3D hand tracking visualization"
72
+ )
73
+ parser.add_argument(
74
+ "--config",
75
+ type=str,
76
+ help="Path to configuration YAML file",
77
+ default=None,
78
+ )
79
+
80
+ args = parser.parse_args()
81
+
82
+ try:
83
+ detector = HandDetector(config_path=args.config)
84
+ print("Starting combined 2D + 3D visualization...")
85
+ print("Controls: Rotate 3D (drag), Zoom (scroll), Close window to exit")
86
+ detector.run_interactive(mode='combined')
87
+ except KeyboardInterrupt:
88
+ print("\nShutting down...")
89
+ except Exception as e:
90
+ print(f"Error: {e}")
91
+ sys.exit(1)
92
+
93
+
94
+ def main_rerun():
95
+ """CLI entry point for Rerun visualization."""
96
+ parser = argparse.ArgumentParser(
97
+ description="Rerun hand tracking visualization"
98
+ )
99
+ parser.add_argument(
100
+ "--config",
101
+ type=str,
102
+ help="Path to configuration YAML file",
103
+ default=None,
104
+ )
105
+
106
+ args = parser.parse_args()
107
+
108
+ try:
109
+ detector = HandDetector(config_path=args.config)
110
+ print("Starting Rerun visualization...")
111
+ print("The Rerun viewer will open automatically.")
112
+ print("Press Ctrl+C to stop.")
113
+ detector.run_interactive(mode='rerun')
114
+ except KeyboardInterrupt:
115
+ print("\nShutting down...")
116
+ except Exception as e:
117
+ print(f"Error: {e}")
118
+ sys.exit(1)
119
+
120
+
121
+ def calibrate():
122
+ """CLI entry point for hand calibration."""
123
+ parser = argparse.ArgumentParser(
124
+ description="Calibrate hand tracking system"
125
+ )
126
+ parser.add_argument(
127
+ "--output",
128
+ type=str,
129
+ help="Output configuration file path",
130
+ default="config/calibrated_config.yaml",
131
+ )
132
+ parser.add_argument(
133
+ "--distance",
134
+ type=float,
135
+ help="Reference distance from camera in cm (default: 50cm)",
136
+ default=50.0,
137
+ )
138
+ parser.add_argument(
139
+ "--samples",
140
+ type=int,
141
+ help="Number of samples to collect (default: 30)",
142
+ default=30,
143
+ )
144
+
145
+ args = parser.parse_args()
146
+
147
+ print("Hand Tracking Calibration")
148
+ print("=" * 50)
149
+ print(f"\nCalibration parameters:")
150
+ print(f" Reference distance: {args.distance} cm")
151
+ print(f" Samples to collect: {args.samples}")
152
+ print(f" Output file: {args.output}")
153
+ print("\nInstructions:")
154
+ print(f"1. Measure and mark a distance of {args.distance} cm from your camera")
155
+ print("2. Place your hand at this marked distance")
156
+ print("3. Hold your hand steady with fingers spread")
157
+ print("4. Keep hand visible in camera frame")
158
+ print("\nPress Enter to start calibration, or Ctrl+C to cancel...")
159
+
160
+ try:
161
+ input()
162
+
163
+ import cv2
164
+ import yaml
165
+ import numpy as np
166
+
167
+ detector = HandDetector()
168
+ print("\nšŸŽ„ Starting camera...")
169
+ print("Position your hand at the marked distance...")
170
+
171
+ palm_widths = []
172
+ sample_count = 0
173
+
174
+ while sample_count < args.samples:
175
+ frame = detector.get_frame()
176
+ if frame is None:
177
+ print("āŒ Failed to get frame from camera")
178
+ sys.exit(1)
179
+
180
+ hands = detector.detect_hands(frame)
181
+
182
+ # Draw frame
183
+ frame_display = detector.draw_landmarks(frame.copy(), hands)
184
+
185
+ # Add calibration info
186
+ cv2.putText(frame_display, f"Calibration: {sample_count}/{args.samples}",
187
+ (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
188
+ cv2.putText(frame_display, f"Distance: {args.distance} cm",
189
+ (10, 70), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
190
+
191
+ if len(hands) > 0:
192
+ hand = hands[0] # Use first detected hand
193
+ palm_width = hand.get_palm_width()
194
+ palm_widths.append(palm_width)
195
+ sample_count += 1
196
+
197
+ cv2.putText(frame_display, f"Palm width: {palm_width:.1f}px",
198
+ (10, 110), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
199
+ cv2.putText(frame_display, "āœ“ Sample captured",
200
+ (10, 150), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
201
+ else:
202
+ cv2.putText(frame_display, "No hand detected - please show hand",
203
+ (10, 110), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
204
+
205
+ cv2.imshow("Calibration", frame_display)
206
+
207
+ if cv2.waitKey(100) & 0xFF == ord('q'):
208
+ print("\nāŒ Calibration cancelled by user")
209
+ cv2.destroyAllWindows()
210
+ sys.exit(0)
211
+
212
+ cv2.destroyAllWindows()
213
+
214
+ # Calculate calibrated reference palm width
215
+ palm_widths = np.array(palm_widths)
216
+ mean_palm_width = np.mean(palm_widths)
217
+ std_palm_width = np.std(palm_widths)
218
+
219
+ print(f"\nāœ“ Calibration complete!")
220
+ print(f"\nResults:")
221
+ print(f" Mean palm width: {mean_palm_width:.2f} px")
222
+ print(f" Std deviation: {std_palm_width:.2f} px")
223
+ print(f" Min: {np.min(palm_widths):.2f} px")
224
+ print(f" Max: {np.max(palm_widths):.2f} px")
225
+
226
+ # Save calibration
227
+ output_path = Path(args.output)
228
+ output_path.parent.mkdir(parents=True, exist_ok=True)
229
+
230
+ calibration_data = {
231
+ 'calibration': {
232
+ 'reference_palm_width': float(mean_palm_width),
233
+ 'reference_distance_cm': float(args.distance),
234
+ 'std_deviation': float(std_palm_width),
235
+ 'samples_collected': len(palm_widths),
236
+ },
237
+ 'hand_tracking': {
238
+ 'camera': {'device_id': 0, 'width': 1280, 'height': 720, 'fps': 30},
239
+ 'mediapipe': {
240
+ 'max_num_hands': 2,
241
+ 'min_detection_confidence': 0.7,
242
+ 'min_tracking_confidence': 0.5,
243
+ 'model_complexity': 1
244
+ },
245
+ 'landmarks': {'smoothing_factor': 0.8, 'confidence_threshold': 0.6},
246
+ 'display': {
247
+ 'show_landmarks': True,
248
+ 'show_connections': True,
249
+ 'show_bounding_box': True,
250
+ 'window_name': 'Hand Tracking'
251
+ },
252
+ 'normalization': {
253
+ 'use_calibrated_reference': True,
254
+ 'reference_palm_width': float(mean_palm_width),
255
+ }
256
+ }
257
+ }
258
+
259
+ with open(output_path, 'w') as f:
260
+ yaml.dump(calibration_data, f, default_flow_style=False)
261
+
262
+ print(f"\nāœ“ Calibration saved to: {output_path}")
263
+ print(f"\nTo use this calibration:")
264
+ print(f" detector = HandDetector(config_path='{output_path}')")
265
+ print(f" # Or use normalized landmarks with custom reference:")
266
+ print(f" normalized = hand.get_normalized_landmarks(reference_palm_width={mean_palm_width:.2f})")
267
+
268
+ except KeyboardInterrupt:
269
+ print("\n\nāŒ Calibration cancelled")
270
+ sys.exit(0)
271
+ except Exception as e:
272
+ print(f"\nāŒ Error during calibration: {e}")
273
+ import traceback
274
+ traceback.print_exc()
275
+ sys.exit(1)
276
+
277
+
278
+ if __name__ == "__main__":
279
+ main()