前一段时间做项目时遇到了一个图像处理的问题,当时的任务是要在ROS中实时地获取图像并作为网络的输入进行前向计算。如果是使用C++的话将没有什么问题,直接获取图像处理就是了,但是如果使用Python的话,就会遇到数据格式的问题。下面我们对此进行简要介绍。
在使用ROS时,常常会自定义消息类型,即.msg文件,我们需要遵守一定的规则来对此进行定义,比如:
geometry_msgs/Vector3.msg
float64 x
float64 y
float64 z
其中会用到一些原子类型,比如float64。那这些类型在C++或者是Python中分别对应什么类型的数据呢?对照关系列表如下:
上表中小括号里的数字代表下列注释:
从上面的列表中可以看出,rosmsg中的uint8虽然对应Python中的int,但是却与一般的int稍有不同。rosmsg中的uint8[]表示的并不是int[],而是被作为Python bytes对待,这一点尤其需要注意。具体请参考rosmsg。
通常,在计算机中,为了对不同类型的数据进行表示,我们需要对数据进行编码。常见的编码方式有ASCII码,UTF-8编码等。这些编码将不同类型的数据如“a、b、c”、“1、2、3”、“&、*、^”等编码为二进制数,以便于在计算机中进行存储和表示。Python bytes就是这样一种编码之后的数据类型,也即二进制数据类型,一般以16进制进行表示:
>>> '€20'.encode('utf-8')
b'\xe2\x82\xac20'
>>> b'\xe2\x82\xac20'.decode('utf-8')
'€20'
结合前面对rosmsg的介绍,我们可以发现rosmsg中的uint8[]是将uint数组直接以二进制方式表示了,从而实现更快地存取操作。
在rospy中,我们的sensor_msgs/Image消息类型定义如下:
std_msgs/Header header
uint32 height
uint32 width
string encoding
uint8 is_bigendian
uint32 step
uint8[] data
用于存储图像数据的是uint8,而刚刚我们谈到uint8[]在Python中将被当做bytes处理。如果我们想要使用NumPy对图像数据进行处理,则需要先转换为10进制数据。最开始我是这样做的:
start_time = time.time()
bytes_list = list(img_data)
temp = []
for i in range(len(img_data)):
temp.append(ord(bytes_list[i]))
img_array = np.array(temp, dtype=np.uint8)
array_reshaped = img_array.reshape([480, 640, 3])
end_time = time.time()
print "Elapsed time: ", end_time - start_time
这段代码测试耗时约为0.12s,也即最多能够达到8Hz的频率,这样的话实时性就得不到保证了。
幸好NumPy提供了frombuffer函数用于对这类bytes数据进行处理:
>>> np.frombuffer(b'\x01\x02', dtype=np.uint8)
array([1, 2], dtype=uint8)
>>> np.frombuffer(b'\x01\x02\x03\x04\x05', dtype=np.uint8, count=3)
array([1, 2, 3], dtype=uint8)
我们可以将这一函数用于处理上面的图像数据:
start_time = time.time()
img_array = np.frombuffer(img_data, dtype=np.uint8)
array_reshaped = img_array.reshape([480, 640, 3])
end_time = time.time()
print "Elapsed time: ", end_time - start_time
处理时间为 10−5 10 − 5 s数量级,这样一来,上面的问题就解决了。