()
| 970 | # ============================================================================ |
| 971 | |
| 972 | def main(): |
| 973 | parser = argparse.ArgumentParser( |
| 974 | description="Interactive 3D visualizer for VisionProTeleop hand tracking data using Viser." |
| 975 | ) |
| 976 | parser.add_argument( |
| 977 | "path", |
| 978 | type=str, |
| 979 | help="Path to the recording directory or tracking.jsonl file" |
| 980 | ) |
| 981 | parser.add_argument( |
| 982 | "--port", "-p", |
| 983 | type=int, |
| 984 | default=8080, |
| 985 | help="Port for the Viser server (default: 8080)" |
| 986 | ) |
| 987 | parser.add_argument( |
| 988 | "--video", "-v", |
| 989 | type=str, |
| 990 | default=None, |
| 991 | help="Path to video file (mp4) to display synchronized with tracking" |
| 992 | ) |
| 993 | |
| 994 | args = parser.parse_args() |
| 995 | |
| 996 | path = Path(args.path) |
| 997 | if not path.exists(): |
| 998 | print(f"Error: Path not found: {path}") |
| 999 | return 1 |
| 1000 | |
| 1001 | # Look for video file |
| 1002 | video_path = None |
| 1003 | if args.video: |
| 1004 | video_path = Path(args.video) |
| 1005 | if not video_path.exists(): |
| 1006 | print(f"Warning: Video file not found: {video_path}") |
| 1007 | video_path = None |
| 1008 | else: |
| 1009 | # Auto-detect video in same directory |
| 1010 | if path.is_dir(): |
| 1011 | for ext in ['.mp4', '.MP4', '.mov', '.MOV']: |
| 1012 | candidate = path / f"video{ext}" |
| 1013 | if candidate.exists(): |
| 1014 | video_path = candidate |
| 1015 | break |
| 1016 | else: |
| 1017 | # Look in parent directory of tracking file |
| 1018 | parent = path.parent |
| 1019 | for ext in ['.mp4', '.MP4', '.mov', '.MOV']: |
| 1020 | candidate = parent / f"video{ext}" |
| 1021 | if candidate.exists(): |
| 1022 | video_path = candidate |
| 1023 | break |
| 1024 | |
| 1025 | try: |
| 1026 | frames, metadata = load_tracking_data(path) |
| 1027 | except FileNotFoundError as e: |
| 1028 | print(f"Error: {e}") |
| 1029 | return 1 |
no test coverage detected