1- # ROS Headers
1+ #!/usr/bin/env python3
2+ """
3+ Capture ROS messages and save each run in its own time-stamped folder.
4+ """
5+
26import rospy
3- from sensor_msgs .msg import Image ,PointCloud2
7+ from sensor_msgs .msg import Image , PointCloud2
48import sensor_msgs .point_cloud2 as pc2
59import ctypes
610import struct
7-
8- # OpenCV and cv2 bridge
911import cv2
1012from cv_bridge import CvBridge
1113import numpy as np
1214import os
15+ import datetime
1316
1417lidar_points = None
1518camera_image = None
1619depth_image = None
1720bridge = CvBridge ()
1821
19- def lidar_callback (lidar : PointCloud2 ):
22+ def lidar_callback (lidar : PointCloud2 ):
2023 global lidar_points
2124 lidar_points = lidar
2225
23- def camera_callback (img : Image ):
26+ def camera_callback (img : Image ):
2427 global camera_image
2528 camera_image = img
2629
27- def depth_callback (img : Image ):
30+ def depth_callback (img : Image ):
2831 global depth_image
2932 depth_image = img
3033
31- def pc2_to_numpy (pc2_msg , want_rgb = False ):
34+ def pc2_to_numpy (pc2_msg , want_rgb = False ):
3235 gen = pc2 .read_points (pc2_msg , skip_nans = True )
3336 if want_rgb :
34- xyzpack = np .array (list (gen ),dtype = np .float32 )
37+ xyzpack = np .array (list (gen ), dtype = np .float32 )
3538 if xyzpack .shape [1 ] != 4 :
3639 raise ValueError ("PointCloud2 does not have points" )
37- xyzrgb = np .empty ((xyzpack .shape [0 ],6 ))
38- xyzrgb [:,:3 ] = xyzpack [:,:3 ]
39- for i ,x in enumerate (xyzpack ):
40- rgb = x [3 ]
41- # cast float32 to int so that bitwise operations are possible
42- s = struct .pack ('>f' ,rgb )
43- i = struct .unpack ('>l' ,s )[0 ]
44- # you can get back the float value by the inverse operations
45- pack = ctypes .c_uint32 (i ).value
46- r = (pack & 0x00FF0000 )>> 16
47- g = (pack & 0x0000FF00 )>> 8
40+ xyzrgb = np .empty ((xyzpack .shape [0 ], 6 ))
41+ xyzrgb [:, :3 ] = xyzpack [:, :3 ]
42+ for i , x in enumerate (xyzpack ):
43+ rgb = x [3 ]
44+ s = struct .pack ('>f' , rgb )
45+ i_val = struct .unpack ('>l' , s )[0 ]
46+ pack = ctypes .c_uint32 (i_val ).value
47+ r = (pack & 0x00FF0000 ) >> 16
48+ g = (pack & 0x0000FF00 ) >> 8
4849 b = (pack & 0x000000FF )
49- #r,g,b values in the 0-255 range
50- xyzrgb [i ,3 :] = (r ,g ,b )
50+ xyzrgb [i , 3 :] = (r , g , b )
5151 return xyzrgb
5252 else :
53- return np .array (list (gen ),dtype = np .float32 )[:,:3 ]
53+ return np .array (list (gen ), dtype = np .float32 )[:, :3 ]
5454
55- def save_scan (lidar_fn ,color_fn ,depth_fn ):
56- print ("Saving scan to" ,lidar_fn ,color_fn ,depth_fn )
57- pc = pc2_to_numpy (lidar_points ,want_rgb = False )
58- np .savez (lidar_fn ,pc )
59- cv2 .imwrite (color_fn ,bridge .imgmsg_to_cv2 (camera_image ))
55+ def save_scan (lidar_fn , color_fn , depth_fn ):
56+ print ("Saving scan to" , lidar_fn , color_fn , depth_fn )
57+ pc = pc2_to_numpy (lidar_points , want_rgb = False )
58+ np .savez (lidar_fn , pc )
59+ cv2 .imwrite (color_fn , bridge .imgmsg_to_cv2 (camera_image ))
6060 dimage = bridge .imgmsg_to_cv2 (depth_image )
6161 dimage_non_nan = dimage [np .isfinite (dimage )]
62- print ("Depth range" ,np .min (dimage_non_nan ),np .max (dimage_non_nan ))
63- dimage = np .nan_to_num (dimage ,nan = 0 ,posinf = 0 ,neginf = 0 )
64- dimage = (dimage / 4000 * 0xffff )
65- print ("Depth pixel range" ,np .min (dimage ),np .max (dimage ))
62+ print ("Depth range" , np .min (dimage_non_nan ), np .max (dimage_non_nan ))
63+ dimage = np .nan_to_num (dimage , nan = 0 , posinf = 0 , neginf = 0 )
64+ dimage = (dimage / 4000 * 0xffff )
65+ print ("Depth pixel range" , np .min (dimage ), np .max (dimage ))
6666 dimage = dimage .astype (np .uint16 )
67- cv2 .imwrite (depth_fn ,dimage )
67+ cv2 .imwrite (depth_fn , dimage )
68+
69+ def main (base_folder = 'data' , start_index = 1 ):
70+ # unique folder for this capture run based on the current date/time.
71+ run_timestamp = datetime .datetime .now ().strftime ("%Y-%m-%d_%H-%M-%S" )
72+ run_folder = os .path .join (base_folder , run_timestamp )
73+ os .makedirs (run_folder , exist_ok = True )
74+ print ("Capture run folder:" , run_folder )
75+
76+ rospy .init_node ("capture_lidar_zed" , disable_signals = True )
77+ rospy .Subscriber ("/lidar1/velodyne_points" , PointCloud2 , lidar_callback )
78+ rospy .Subscriber ("/zed2/zed_node/rgb/image_rect_color" , Image , camera_callback )
79+ rospy .Subscriber ("/zed2/zed_node/depth/depth_registered" , Image , depth_callback )
6880
69- def main (folder = 'data' ,start_index = 1 ):
70- rospy .init_node ("capture_lidar_zed" ,disable_signals = True )
71- lidar_sub = rospy .Subscriber ("/lidar1/velodyne_points" , PointCloud2 , lidar_callback )
72- camera_sub = rospy .Subscriber ("/zed2/zed_node/rgb/image_rect_color" , Image , camera_callback )
73- depth_sub = rospy .Subscriber ("/zed2/zed_node/depth/depth_registered" , Image , depth_callback )
7481 index = start_index
7582 print ("Press any key to:" )
76- print (" store lidar point clouds as npz" )
77- print (" store color images as png" )
78- print (" store depth images (m scaled by 0xffff/4000) as 16-bit tif" )
83+ print (" - store lidar point clouds as npz" )
84+ print (" - store color images as png" )
85+ print (" - store depth images (m scaled by 0xffff/4000) as 16-bit tif" )
7986 print ("Press Escape or Ctrl+C to quit" )
80- while True :
81- if camera_image :
82- cv2 .imshow ("result" ,bridge .imgmsg_to_cv2 (camera_image ))
87+
88+ while not rospy .is_shutdown ():
89+ if camera_image is not None :
90+ cv2 .imshow ("result" , bridge .imgmsg_to_cv2 (camera_image ))
8391 key = cv2 .waitKey (30 )
8492 if key == - 1 :
85- #nothing
86- pass
93+ pass # No key pressed.
8794 elif key == 27 :
88- #escape
89- break
95+ break # Escape key pressed.
9096 else :
9197 if lidar_points is None or camera_image is None or depth_image is None :
9298 print ("Missing some messages?" )
9399 else :
94- files = [os .path .join (folder ,'lidar{}.npz' .format (index )),
95- os .path .join (folder ,'color{}.png' .format (index )),
96- os .path .join (folder ,'depth{}.tif' .format (index ))]
100+ files = [
101+ os .path .join (run_folder , 'lidar{}.npz' .format (index )),
102+ os .path .join (run_folder , 'color{}.png' .format (index )),
103+ os .path .join (run_folder , 'depth{}.tif' .format (index ))
104+ ]
97105 save_scan (* files )
98106 index += 1
99107
@@ -105,4 +113,4 @@ def main(folder='data',start_index=1):
105113 folder = sys .argv [1 ]
106114 if len (sys .argv ) >= 3 :
107115 start_index = int (sys .argv [2 ])
108- main (folder ,start_index )
116+ main (folder , start_index )
0 commit comments