calibrate-suite 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.
Files changed (47) hide show
  1. calibrate_suite-0.1.0.dist-info/METADATA +761 -0
  2. calibrate_suite-0.1.0.dist-info/RECORD +47 -0
  3. calibrate_suite-0.1.0.dist-info/WHEEL +5 -0
  4. calibrate_suite-0.1.0.dist-info/entry_points.txt +3 -0
  5. calibrate_suite-0.1.0.dist-info/licenses/LICENSE +201 -0
  6. calibrate_suite-0.1.0.dist-info/top_level.txt +4 -0
  7. fleet_server/__init__.py +32 -0
  8. fleet_server/app.py +377 -0
  9. fleet_server/config.py +91 -0
  10. fleet_server/templates/error.html +57 -0
  11. fleet_server/templates/index.html +137 -0
  12. fleet_server/templates/viewer.html +490 -0
  13. fleet_server/utils.py +178 -0
  14. gui/__init__.py +2 -0
  15. gui/assets/2d-or-3d-fleet-upload.png +0 -0
  16. gui/assets/2d_3d_overlay_output.jpg +0 -0
  17. gui/assets/3d-or-2d-overlay_page.png +0 -0
  18. gui/assets/3d-or-2d-record-page.png +0 -0
  19. gui/assets/3d_3d_overlay_output.png +0 -0
  20. gui/assets/3d_or_2d_calibrate-page.png +0 -0
  21. gui/assets/GUI_homepage.png +0 -0
  22. gui/assets/hardware_setup.jpeg +0 -0
  23. gui/assets/single_lidar_calibrate_page.png +0 -0
  24. gui/assets/single_lidar_output.png +0 -0
  25. gui/assets/single_lidar_record_page.png +0 -0
  26. gui/assets/virya.jpg +0 -0
  27. gui/main.py +23 -0
  28. gui/widgets/calibrator_widget.py +977 -0
  29. gui/widgets/extractor_widget.py +561 -0
  30. gui/widgets/home_widget.py +117 -0
  31. gui/widgets/recorder_widget.py +127 -0
  32. gui/widgets/single_lidar_widget.py +673 -0
  33. gui/widgets/three_d_calib_widget.py +87 -0
  34. gui/widgets/two_d_calib_widget.py +86 -0
  35. gui/widgets/uploader_widget.py +151 -0
  36. gui/widgets/validator_widget.py +614 -0
  37. gui/windows/main_window.py +56 -0
  38. gui/windows/main_window_ui.py +65 -0
  39. rviz_configs/2D-3D.rviz +183 -0
  40. rviz_configs/3D-3D.rviz +184 -0
  41. rviz_configs/default_calib.rviz +167 -0
  42. utils/__init__.py +13 -0
  43. utils/calibration_common.py +23 -0
  44. utils/cli_calibrate.py +53 -0
  45. utils/cli_fleet_server.py +64 -0
  46. utils/data_extractor_common.py +87 -0
  47. utils/gui_helpers.py +25 -0
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Entry point for calibrate-suite fleet server (REST API).
4
+
5
+ Usage: fleet-server [--host HOST] [--port PORT] [--debug]
6
+ """
7
+
8
+ import sys
9
+ import argparse
10
+
11
+
12
+ def main():
13
+ """Launch the fleet server REST API."""
14
+ parser = argparse.ArgumentParser(
15
+ prog="fleet-server",
16
+ description="Calibrate-Suite Fleet Server: REST API for calibration management",
17
+ )
18
+ parser.add_argument(
19
+ "--host",
20
+ default="127.0.0.1",
21
+ help="Server host address (default: 127.0.0.1)",
22
+ )
23
+ parser.add_argument(
24
+ "--port",
25
+ type=int,
26
+ default=5000,
27
+ help="Server port (default: 5000)",
28
+ )
29
+ parser.add_argument(
30
+ "--debug",
31
+ action="store_true",
32
+ help="Enable debug mode",
33
+ )
34
+ parser.add_argument(
35
+ "--version",
36
+ action="version",
37
+ version="fleet-server (calibrate-suite 0.1.0)",
38
+ )
39
+
40
+ args = parser.parse_args()
41
+
42
+ # Import here to avoid Flask dependencies when not needed
43
+ try:
44
+ from fleet_server.app import app
45
+
46
+ print(f"Starting Fleet Server on {args.host}:{args.port}")
47
+ print(f"Dashboard: http://{args.host}:{args.port}")
48
+ print(f"API Docs: http://{args.host}:{args.port}/api/docs")
49
+ print("Press Ctrl+C to stop server\n")
50
+
51
+ app.run(host=args.host, port=args.port, debug=args.debug)
52
+
53
+ except ImportError as e:
54
+ print(
55
+ f"Error: Fleet server dependencies not installed. Please install with:\n"
56
+ f" pip install calibrate-suite\n"
57
+ f"If Flask is missing, try:\n"
58
+ f" pip install flask requests"
59
+ )
60
+ sys.exit(1)
61
+
62
+
63
+ if __name__ == "__main__":
64
+ main()
@@ -0,0 +1,87 @@
1
+ """Shared data extractor utilities"""
2
+
3
+ import sys
4
+ import struct
5
+ import numpy as np
6
+ import open3d as o3d
7
+
8
+ def flush_print(msg):
9
+ """Print with immediate stdout flush."""
10
+ print(msg)
11
+ sys.stdout.flush()
12
+
13
+
14
+ def save_pc2_to_pcd(msg, filename):
15
+ """
16
+ Convert ROS PointCloud2 message to PCD file.
17
+ """
18
+ try:
19
+ field_names = [f.name for f in msg.fields]
20
+ offsets = {f.name: f.offset for f in msg.fields}
21
+
22
+ if 'x' not in offsets:
23
+ flush_print("[WARN] PointCloud has no X field.")
24
+ return
25
+
26
+ n_points = msg.width * msg.height
27
+ if n_points == 0:
28
+ flush_print(f"[WARN] Empty point cloud (0 points) in {filename}")
29
+ pc = o3d.geometry.PointCloud()
30
+ o3d.io.write_point_cloud(filename, pc)
31
+ return
32
+
33
+ step = msg.point_step
34
+ data = msg.data
35
+
36
+ # Extract XYZ points
37
+ off_x = offsets.get('x', 0)
38
+ off_y = offsets.get('y', 4)
39
+ off_z = offsets.get('z', 8)
40
+
41
+ pts = np.zeros((n_points, 3), dtype=np.float32)
42
+
43
+ for i in range(n_points):
44
+ base = i * step
45
+ try:
46
+ pts[i, 0] = struct.unpack_from('f', data, base + off_x)[0]
47
+ pts[i, 1] = struct.unpack_from('f', data, base + off_y)[0]
48
+ pts[i, 2] = struct.unpack_from('f', data, base + off_z)[0]
49
+ except struct.error as e:
50
+ pts[i] = [0, 0, 0]
51
+
52
+ # Filter out NaN and Inf points
53
+ valid_mask = np.isfinite(pts).all(axis=1)
54
+ valid_pts = pts[valid_mask]
55
+
56
+ pc = o3d.geometry.PointCloud()
57
+ if len(valid_pts) > 0:
58
+ pc.points = o3d.utility.Vector3dVector(valid_pts.astype(np.float64))
59
+
60
+ o3d.io.write_point_cloud(filename, pc)
61
+
62
+ except Exception as e:
63
+ flush_print(f"[ERR] Failed to save PCD {filename}: {e}")
64
+
65
+
66
+ def depth_to_camera_cloud(depth: np.ndarray, K: np.ndarray):
67
+ """
68
+ Convert depth image to camera point cloud using intrinsics.
69
+ """
70
+ h, w = depth.shape
71
+ fx, fy = K[0, 0], K[1, 1]
72
+ cx, cy = K[0, 2], K[1, 2]
73
+
74
+ u, v = np.meshgrid(np.arange(w), np.arange(h))
75
+ z = depth.flatten()
76
+ valid = np.isfinite(z) & (z > 0)
77
+
78
+ if not np.any(valid):
79
+ return o3d.geometry.PointCloud()
80
+
81
+ x = (u.flatten()[valid] - cx) * z[valid] / fx
82
+ y = (v.flatten()[valid] - cy) * z[valid] / fy
83
+ pts = np.vstack((x, y, z[valid])).T
84
+
85
+ pc = o3d.geometry.PointCloud()
86
+ pc.points = o3d.utility.Vector3dVector(pts.astype(np.float64))
87
+ return pc
utils/gui_helpers.py ADDED
@@ -0,0 +1,25 @@
1
+ """Shared GUI helper utilities."""
2
+
3
+ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton
4
+
5
+
6
+ def wrap_with_back(widget, go_home_signal):
7
+ """
8
+ Wrap a widget with a Back button at the bottom.
9
+ """
10
+ wrapper = QWidget()
11
+ layout = QVBoxLayout()
12
+ layout.setContentsMargins(0, 0, 0, 0)
13
+ layout.addWidget(widget)
14
+
15
+ btn_back = QPushButton("Back")
16
+ btn_back.clicked.connect(go_home_signal.emit)
17
+ # Match style
18
+ btn_back.setStyleSheet("""
19
+ QPushButton { background-color: #2E8B57; border-radius: 8px; padding: 5px; color: white; }
20
+ QPushButton:hover { background-color: #689F38; }
21
+ """)
22
+
23
+ layout.addWidget(btn_back)
24
+ wrapper.setLayout(layout)
25
+ return wrapper