process_bags.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 '''
00004 Simple Script to Processes Bag files from RawSnapshot in Release 2.0
00005 and writes the data to file for calibration
00006 
00007 Please direct any question to multisense@carnegierobotics.com or
00008     http://support.carnegierobotics.com
00009 '''
00010 
00011 import sys
00012 import csv
00013 import time
00014 import os
00015 import numpy as np
00016 import string
00017 try:
00018     import rospy
00019     import rosbag
00020 except:
00021     raise Exception("Error importing ROS. Source the ROS environment" \
00022                    +" in the workspace where the multisense stack is located")
00023 
00024 class _BagProcessor():
00025 
00026     def __init__(self, bag_file):
00027         self.bag_file = bag_file
00028 
00029     #Wrapper method to processes the bag file
00030     #Returns bag file name
00031     def process(self, directory='.', rename_file = True,
00032                 namespace = 'multisense'):
00033         self.process_laser(directory, namespace)
00034         self.process_image(directory, namespace)
00035         self.process_camera_yaml(directory, namespace)
00036         self.process_laser_yaml(directory, namespace)
00037 
00038         return_value = self.bag_file
00039         if rename_file:
00040             return_value = self.rename_bag(directory)
00041         # end if
00042         return return_value
00043 
00044     #Method to extract laser data from bag file and save into .csv
00045     def process_laser(self, directory='.', namespace='multisense'):
00046         bag = rosbag.Bag(self.bag_file)
00047         with open(directory + '/' + 'lidarData.csv', 'wb') as laser_file:
00048             topic_name = '/%s/calibration/raw_lidar_data' % namespace
00049             laser_writer = csv.writer(laser_file, delimiter=',')
00050             for topic, msg, t in bag.read_messages(topics=[topic_name]):
00051                 #Unbundle message
00052                 scan_count = msg.scan_count
00053                 time_start = float(msg.time_start.secs +
00054                                         msg.time_start.nsecs * 1e-9)
00055                 time_end = float(msg.time_end.secs + msg.time_end.nsecs * 1e-9)
00056                 angle_start = msg.angle_start
00057                 angle_end = msg.angle_end
00058                 dist = msg.distance
00059                 reflect = msg.intensity
00060 
00061                 #Expected Format: scan_count, time_start, time_end, angle_start,
00062                 #angle_end, , range, , intensity, ,len(range)
00063                 row = [scan_count, time_start, time_end,
00064                        angle_start, angle_end, " "] + list(dist) + [" "] \
00065                        + list(reflect) + [" "] + [len(list(dist))]
00066 
00067                 laser_writer.writerow(row)
00068 
00069         laser_file.close()
00070 
00071 
00072     #Method to extract first instance of disparity and recitified images from
00073     #bag
00074     def process_image(self, directory='.', namespace='multisense'):
00075         topic_name = '/%s/calibration/raw_cam_data' % namespace
00076         bag = rosbag.Bag(self.bag_file)
00077 
00078         #Attempt to find both disparity and rectified images before quitting
00079         found_rectified_image = False
00080         found_disparity_image = False
00081 
00082         for topic, msg, t in bag.read_messages(topics=[topic_name]):
00083             width = msg.width
00084             height = msg.height
00085 
00086             #Write to .pgm file stereo_left_0000.pgm
00087             if not found_rectified_image and len(list(msg.gray_scale_image)) != 0:
00088                 self.write_pgm(np.array(list(msg.gray_scale_image)),
00089                                 directory + '/' + "stereo_left_0000.pgm",
00090                                 width, height, 8)
00091                 found_rectified_image = True
00092 
00093             if not found_disparity_image and len(list(msg.disparity_image)) != 0:
00094                 self.write_pgm(np.array(list(msg.disparity_image),
00095                                 dtype=np.uint16),
00096                                 directory + '/' + "disparity_0000.pgm",
00097                                 width, height, 16)
00098                 found_disparity_image = True
00099 
00100             #Quit once disparity and rectified images have been found
00101             if found_disparity_image and found_rectified_image:
00102                 break
00103 
00104     #Method to write an image to a .pgm file
00105     def write_pgm(self, data, name, width, height, bits):
00106         image = open(name, 'wb')
00107 
00108         #Create .pgm file header
00109         pgm_header = 'P5' + '\n' + str(width) + ' ' \
00110                      + str(height) + '\n' + str(2**bits - 1) + '\n'
00111 
00112         #Data needs to be big endian not little endian for 16bit images
00113         if bits == 16:
00114             data = data.byteswap()
00115 
00116         image.write(pgm_header)
00117         data.tofile(image)
00118         image.close()
00119 
00120     #Extract image intrinsics from RawCamConfig.msg
00121     def process_camera_yaml(self, directory='.', namespace='multisense'):
00122         topic_name = '/%s/calibration/raw_cam_config' % namespace
00123         bag = rosbag.Bag(self.bag_file)
00124         for topic, msg, t in bag.read_messages(topics=[topic_name]):
00125             fx = msg.fx
00126             fy = msg.fy
00127             cx = msg.cx
00128             cy = msg.cy
00129             tx = msg.tx
00130 
00131             #Follow expected format of YAML file
00132             p1 =  "[ %.17e, 0., %d, 0., 0., \n" % (fx, cx) \
00133                  + "       %.17e, %d, 0., 0., 0., 1., 0.]" % (fy, cy) \
00134 
00135             p2 =  "[ %.17e, 0., %d, %.17e, 0., \n" % (fx, cx, tx*fx) \
00136                  + "       %.17e, %d, 0., 0., 0., 1., 0.]" % (fy, cy) \
00137 
00138             self.write_camera_yaml(directory + "/extrinsics_0p5mp.yml", p1, p2)
00139 
00140 
00141     #Writes P1 and P2 in openCV format to be used internally.
00142     #P1 and P2 represent the camera intrinsics
00143     def write_camera_yaml(self, name, p1, p2):
00144         yaml = open(name, 'wb')
00145 
00146         yaml_header = "%YAML:1.0\n"
00147         yaml.write(yaml_header)
00148 
00149         p1_header = "P1: !!opencv-matrix\n" \
00150                      + "   rows: 3\n" \
00151                      + "   cols: 4\n" \
00152                      + "   dt: d\n"
00153 
00154 
00155         p2_header = "P2: !!opencv-matrix\n" \
00156                      + "   rows: 3\n" \
00157                      + "   cols: 4\n" \
00158                      + "   dt: d\n"
00159 
00160         yaml.write(p1_header)
00161         yaml.write("   data: " + p1 + '\n\n')
00162         yaml.write(p2_header)
00163         yaml.write("   data: " + p2 + '\n')
00164         yaml.close()
00165 
00166     #Extract Laser To Spindle and Camera To Spindle Extrinsics from
00167     #RawLidarCal.msg
00168     def process_laser_yaml(self, directory=".", namespace='multisense'):
00169         topic_name = '/%s/calibration/raw_lidar_cal' % namespace
00170         bag = rosbag.Bag(self.bag_file)
00171         i = 0
00172         for topic, msg, t in bag.read_messages(topics=[topic_name]):
00173 
00174             #Only use one message
00175             if i > 0:
00176                 break
00177             #Laser to spindle
00178             lts = list(msg.laserToSpindle)
00179             #Camera to spindle
00180             cts = list(msg.cameraToSpindleFixed)
00181 
00182             #Mimics existing OpenCV format
00183             laser_t_spind = "[ %.17e, %.17e,\n" % (lts[0], lts[1]) \
00184                             +"      %.17e, %.17e,\n" % (lts[2], lts[3]) \
00185                             +"      %.17e, %.17e,\n" % (lts[4], lts[5]) \
00186                             +"      %.17e, %.17e,\n" % (lts[6], lts[7]) \
00187                             +"      %.17e, %.17e,\n" % (lts[8], lts[9]) \
00188                             +"      %.17e, 0., 0., 0., 0., 1. ]" % (lts[10])
00189 
00190 
00191             camera_t_spind = "[ %.17e, %.17e,\n" % (cts[0], cts[1]) \
00192                              +"      %.17e, %.17e,\n" % (cts[2], cts[3]) \
00193                              +"      %.17e, %.17e,\n" % (cts[4], cts[5]) \
00194                              +"      %.17e, %.17e,\n" % (cts[6], cts[7]) \
00195                              +"      %.17e, %.17e,\n" % (cts[8], cts[9]) \
00196                              +"      %.17e, %.17e, 0., 0., 0., 1. ]" % (cts[10], cts[11])
00197 
00198             self.write_laser_yaml(directory + "/laser_cal.yml",
00199                                   laser_t_spind, camera_t_spind)
00200             i = i+1
00201 
00202     #Write laser calibration in expected OpenCV format
00203     def write_laser_yaml(self, name, laser_t_spind, cam_t_spind):
00204         yaml = open(name, 'wb')
00205         yaml_header = "%YAML:1.0\n"
00206         yaml.write(yaml_header)
00207 
00208         laser_t_spind_h = "laser_T_spindle: !!opencv-matrix\n" \
00209                           +"   rows: 4\n" \
00210                           +"   cols: 4\n" \
00211                           +"   dt: d\n"
00212 
00213         cam_t_spind_h = "camera_T_spindle_fixed: !!opencv-matrix\n" \
00214                         +"   rows: 4\n" \
00215                         +"   cols: 4\n" \
00216                         +"   dt: d\n"
00217 
00218         yaml.write(laser_t_spind_h)
00219         yaml.write("   data: " + laser_t_spind + '\n')
00220         yaml.write(cam_t_spind_h)
00221         yaml.write("   data: " + cam_t_spind + '\n')
00222         yaml.close()
00223 
00224     #Writes out sensor information appends SN to bagfile name
00225     #Returns new bag file name
00226     def rename_bag(self, directory=".", namespace='multisense'):
00227         topic_name = '/%s/calibration/device_info' % namespace
00228         bag = rosbag.Bag(self.bag_file)
00229         for topic, msg, t in bag.read_messages(topics=[topic_name]):
00230 
00231             #Extract all the digits in the serial number. The format of the
00232             #serial number varies from unit to unit (SNXXX, SL XXXX, SN SLXXXX)
00233             sn = msg.serialNumber.strip()
00234             exclude_characters = string.letters + string.punctuation + " "
00235             sn = sn.strip(exclude_characters)
00236 
00237             #If for whatever reason there are characters still in our serial
00238             #number (XXXvX) just append SN to the front
00239             try:
00240                 sn = "SN%04d" % int(sn)
00241             except:
00242                 sn = "SN" + sn
00243 
00244             info = open(os.path.join(directory, "dev_info.txt"), 'wb')
00245 
00246             info.write(str(msg))
00247             info.close
00248 
00249 
00250             bag = os.path.basename(self.bag_file)
00251             path = os.path.dirname(self.bag_file)
00252 
00253             fname = os.path.join(path,  os.path.splitext(bag)[0]\
00254                                + "_SL_%s_calCheck.bag" % sn)
00255 
00256 
00257             os.rename(self.bag_file, fname)
00258             self.bag_file = fname
00259             return fname
00260 
00261 
00262 def usage(argv):
00263     print "\nUsage: %s bagFile <outputDirectory> <namespace>" % argv[0]
00264     print ""
00265     print "If no value is specified for outputDirectory, then a "
00266     print "directory will be created using the current time as "
00267     print "part of its name."
00268     print ""
00269     print "If a value is provided for namespace, it will be used "
00270     print "in extracting data from the bag file.  The default namespace "
00271     print "is multisense.  You might want to specify multisense_sl or "
00272     print "laser instead, if you are processing an older bag file."
00273 # end def
00274 
00275 if __name__ == '__main__':
00276     # Set up default arguments
00277     bag_file_name = 'dummy.bag'
00278     output_directory_name = time.strftime('%Y-%m-%d_%H-%M-%S_process_bags')
00279     namespace = 'multisense'
00280     rename_file = True
00281 
00282     # Process command line.
00283     if (len(sys.argv) < 2) | (len(sys.argv) > 4):
00284         usage(sys.argv)
00285         sys.exit(65)
00286     # end if
00287     if len(sys.argv) >= 2:
00288         bag_file_name = sys.argv[1]
00289     # end if
00290     if len(sys.argv) >= 3:
00291         output_directory_name = sys.argv[2]
00292     # end if
00293     if len(sys.argv) >= 4:
00294         namespace = sys.argv[3]
00295     # end if
00296 
00297     # Prepare to run _BagProcessor.
00298     if not os.path.exists(output_directory_name):
00299         os.makedirs(output_directory_name)
00300     # end if
00301     if not os.path.isdir(output_directory_name):
00302         raise IOError('%s is not a directory' % output_directory_name)
00303     # end if
00304 
00305     # Do the processsing.
00306     bagProcessor = _BagProcessor(bag_file_name)
00307     bagProcessor.process(output_directory_name, rename_file, namespace)
00308     sys.exit(0)
00309 # end if


multisense_cal_check
Author(s):
autogenerated on Thu Aug 27 2015 14:01:27