diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/importRosbag.iml b/.idea/importRosbag.iml new file mode 100644 index 0000000..cf07444 --- /dev/null +++ b/.idea/importRosbag.iml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..e03dac5 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,14 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..91a2157 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..2523385 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/importRosbag/importRosbag.py b/importRosbag/importRosbag.py index 0fbf1c0..97d5a03 100644 --- a/importRosbag/importRosbag.py +++ b/importRosbag/importRosbag.py @@ -40,6 +40,7 @@ from struct import error as structError from tqdm import tqdm +import time # Local imports from .messageTypes.common import unpackHeader @@ -59,6 +60,7 @@ import logging def importTopic(topic, **kwargs): + time_start = time.time() msgs = topic['msgs'] topicType = topic['type'].replace('/','_') if topicType == 'dvs_msgs_EventArray': topicDict = import_dvs_msgs_EventArray(msgs, **kwargs) @@ -76,6 +78,8 @@ def importTopic(topic, **kwargs): return None if topicDict: topicDict['rosbagType'] = topic['type'] + elapsed_time = time.time() - time_start + print("Tiempo transcurrido: {} segundos".format(elapsed_time)) return topicDict def readFile(filePathOrName): @@ -177,6 +181,7 @@ def rekeyConnsByTopic(connDict): def importRosbag(filePathOrName, **kwargs): global disable_bar + kwargs.setdefault('log', 'ERROR') disable_bar = kwargs.get('disable_bar') loglevel = kwargs.get('log') numeric_level = getattr(logging, loglevel.upper(), None) diff --git a/importRosbag/messageTypes/common.py b/importRosbag/messageTypes/common.py index f3925ea..cf4f19a 100644 --- a/importRosbag/messageTypes/common.py +++ b/importRosbag/messageTypes/common.py @@ -43,6 +43,9 @@ def unpackRosUint32(data, ptr): def unpackRosUint8(data, ptr): return unpack('=B', data[ptr:ptr+1])[0], ptr+1 +def unpackRosUint16(data, ptr): + return unpack('=B', data[ptr:ptr+2])[0], ptr+2 + def unpackRosString(data, ptr): stringLen = unpack('=L', data[ptr:ptr+4])[0] ptr += 4 @@ -53,6 +56,14 @@ def unpackRosString(data, ptr): ptr += stringLen return outStr, ptr +def unpackStringData(data, stringLen, ptr): + try: + outStr = data[ptr:ptr+stringLen].decode('utf-8') + except UnicodeDecodeError: + outStr = 'UnicodeDecodeError' + ptr += stringLen + return outStr, ptr + def unpackRosFloat64Array(data, num, ptr): return np.frombuffer(data[ptr:ptr+num*8], dtype=np.float64), ptr+num*8 diff --git a/importRosbag/messageTypes/sensor_msgs_PointCloud2.py b/importRosbag/messageTypes/sensor_msgs_PointCloud2.py index 02d027a..5f23b16 100644 --- a/importRosbag/messageTypes/sensor_msgs_PointCloud2.py +++ b/importRosbag/messageTypes/sensor_msgs_PointCloud2.py @@ -22,79 +22,135 @@ This function imports the ros message type defined at: http://docs.ros.org/api/sensor_msgs/html/msg/PointCloud2.html -For simplicity, we're currently directly unpacking the format that we are -encountering in the data, which is x,y,z,_,rgb,_,_,_ -each as 32-bit little-endian floats +In this new version of the pointcloud2 extract, we can automated unpacking +all the fields that are included in pointcloud2 message in the format that is required. """ -#%% +# %% from tqdm import tqdm import numpy as np +import time + +from .common import unpackRosString, unpackRosUint8, unpackRosUint32, unpackRosTimestamp, unpackRosFloat32, \ + unpackRosUint16, unpackStringData -from .common import unpackRosString, unpackRosUint8, unpackRosUint32, \ - unpackRosTimestamp def importTopic(msgs, **kwargs): ''' ros message is defined here: - http://docs.ros.org/api/geometry_msgs/html/msg/PoseStamped.html - the result is are np arrays of float64 for: - rotQ (4 cols, quaternion) - angV (3 cols) - acc (3 cols) - mag (3 cols) - temp (1 cols) - but I'll probably ignore this to start with + http://docs.ros.org/api/sensor_msgs/html/msg/PointCloud2.html + the result is a variable dimension dictionary with + - timestamp + - point structure (include all field that declares in pointcloud2 msg) ''' - #tempAll = np.zeros((sizeOfArray, 1), dtype=np.float64) - #for msg in tqdm(msgs, position=0, leave=True): disable_bar = kwargs.get('disable_bar') - tsByMessage = [] - pointsByMessage = [] + DTYPE_VAR = { + '1': '=i1', # INT8 + '2': '=u1', # UINT8 + '3': '=i2', # INT16 + '4': '=u2', # UINT16 + '5': '=i4', # INT32 + '6': '=u4', # UINT32 + '7': '=f4', # FLOAT32 + '8': '=f8', # FLOAT64 + } + dtype_header_1 = { + 'names': ['sequence_id', 'timestamp_seconds', 'timestamp_nseconds', # List of Strings + 'frame_len'], + 'formats': ['=u4','=u4','=u4','=u4'], # List of Strings + 'offsets': [0, 4, 8, 12], # List of INT + 'itemsize': 16 # INT + } + dtype_header_2 = { + 'names': ['height', 'width', 'fieldSize'], # List of Strings + 'formats': [ '=u4', '=u4', '=u4'], # List of Strings + 'offsets': [0, 4, 8], # List of INT + 'itemsize': 12 # INT + } + dtype_header_3 = { + 'names': ['is_Bigendian', 'PointStep', 'RowStep', 'data_size'], # List of Strings + 'formats': [ '=u1', '=u4', '=u4','=u4'], # List of Strings + 'offsets': [0, 1, 5, 9], # List of INT + 'itemsize': 13 # INT + } + dtype_fields = { + 'names': [], # List of Strings + 'formats': [], # List of Strings + 'offsets': [], # List of INT + 'itemsize': 0, # INT + 'count': [] # List of INT + } + + tsByMessage_2 = [] + pointsByMessage_2 = [] + for msg in tqdm(msgs, disable=disable_bar): - data = msg['data'] ptr = 0 - seq, ptr = unpackRosUint32(data, ptr) - ts, ptr = unpackRosTimestamp(data, ptr) - frame_id, ptr = unpackRosString(data, ptr) - height, ptr = unpackRosUint32(data, ptr) - width, ptr = unpackRosUint32(data, ptr) + # Reading first header (with numpy) + header_1 = np.frombuffer(data[ptr:ptr + dtype_header_1['itemsize']], dtype=dtype_header_1) + timestamp = np.float64(header_1['timestamp_seconds'][0])+np.float64(header_1['timestamp_nseconds'][0])*0.000000001 + ptr += dtype_header_1['itemsize'] + # Unpacking frame_id with unknown dimension + frame_len = header_1['frame_len'][0].astype(int) + frame_id, ptr = unpackStringData(data, frame_len, ptr) + # Reading second header + header_2 = np.frombuffer(data[ptr:ptr + dtype_header_2['itemsize']], dtype=dtype_header_2) + ptr += dtype_header_2['itemsize'] - if width > 0 and height > 0: + #Assign variables + height = header_2['height'][0] + width = header_2['width'][0] + fieldSize = header_2['fieldSize'][0] - arraySize, ptr = unpackRosUint32(data, ptr) - for element in range(arraySize): - # Move through the field definitions - we'll ignore these - # until we encounter a file that uses a different set - name, ptr = unpackRosString(data, ptr) - offset, ptr = unpackRosUint32(data, ptr) - datatype, ptr = unpackRosUint8(data, ptr) - count, ptr = unpackRosUint32(data, ptr) - - isBigendian, ptr = unpackRosUint8(data, ptr) - pointStep, ptr = unpackRosUint32(data, ptr) - rowStep, ptr = unpackRosUint32(data, ptr) + #Reading fields of pointcloud2 + if width > 0 and height > 0: + for element in range(fieldSize): + names, ptr = unpackRosString(data, ptr) + offset = np.frombuffer(data[ptr:ptr+4], dtype='=u4') + ptr += 4 + datatype = np.frombuffer(data[ptr:ptr+1], dtype='=u1') + ptr += 1 + count = np.frombuffer(data[ptr:ptr + 4], dtype='=u4') + ptr += 4 + #APPEND TO THE DICTIONARY ('dtype_fields') + dtype_fields['names'].append(names) + dtype_fields['offsets'].append(offset[0]) + dtype_fields['formats'].append(str(DTYPE_VAR[str(datatype[0])])) + dtype_fields['count'].append(count[0]) + # Reading third header + header_3 = np.frombuffer(data[ptr:ptr + dtype_header_3['itemsize']], dtype=dtype_header_3) + ptr += dtype_header_3['itemsize'] + #Assign variables + PointStep = header_3['PointStep'][0] + dtype_fields['itemsize'] = PointStep numPoints = width * height - points = np.empty((numPoints, 3), dtype=np.float32) - arraySize, ptr = unpackRosUint32(data, ptr) - # assert arraySize = width*height + + #Read pointcloud2 data with datatype obtained before + data_array = np.empty((numPoints, 1), dtype=dtype_fields) for x in range(width): - for y in range(height): - points[x*height + y, :] = np.frombuffer(data[ptr:ptr+12], dtype=np.float32) - ptr += pointStep - pointsByMessage.append(points) - tsByMessage.append(np.ones((numPoints), dtype=np.float64) * ts) - if not pointsByMessage: # None of the messages contained any points + for y in range(height): + data_array[x * height + y, :] = np.frombuffer(data[ptr:ptr+dtype_fields['itemsize']], dtype=dtype_fields) + ptr += PointStep + + pointsByMessage_2.append(data_array) + tsByMessage_2.append(np.ones(numPoints, dtype=np.float64) * timestamp) #Meter aquĆ­ la traspuesta de la matriz??? + #Reset dtype_fields variables for the next msg + dtype_fields['names'] = [] + dtype_fields['offsets'] = [] + dtype_fields['formats'] = [] + dtype_fields['count'] = [] + + if not pointsByMessage_2: # None of the messages contained any points return None - points = np.concatenate(pointsByMessage) - ts = np.concatenate(tsByMessage) - + data_array = np.concatenate(pointsByMessage_2) + timestamp = np.concatenate(tsByMessage_2) # Crop arrays to number of events outDict = { - 'ts': ts, - 'point': points, + 'ts': timestamp, + 'point': data_array, } return outDict