Python处理HDF5文件
Published:
最近在打微软 COCO 比赛,接触到HDF5
这种格式,发现真的很好用,读写速度,内存占用,压缩率都很强。
HDF5 文件是一种为了储存和处理大数据而设计的文件格式,具有极高的压缩率。H5接受的数据是矩阵,通常跟numpy
库一起使用。
一个HDF5文件包含两种对象: 1. group:类似与文件夹,读取可类比python中的字典。 2. dataset:数据内容,类比numpy
中的array。
h5py 的安装
在 python 中处理 HDF5 文件依赖于h5py
这个库,可以使用pip
在线安装。或者如果你使用的Anaconda
,就会自带h5py库。
pip install h5py
读取 H5 文件
import h5py
import numpy as np
# 打开文件
f = h5py.File('test-dev.h5', 'r')
H5 中的group
可以类比为字典,因此我们可以用keys()
来获取键值。
>>> f.keys()
[u'my_xmax', u'my_xmin', u'my_ymax', u'my_ymin']
如上,我们发现文件里有四个数据集,我们可以像读取字典一样读取文件。
>>> xmax = f['my_xmax']
读取到的xmax
,可以像处理numpy
矩阵那样操作。这里有个小窍门。
>> xmax = f['my_xmax']
>>> type(xmax)
h5py._hl.dataset.Dataset
>>> xmax = f['my_xmax'][:]
>>> type(xmax)
numpy.ndarray
写入H5文件
打开方式用'w'
,利用文件方法写入数据
f = h5py.File('test-dev.h5','w')
f.create_dataset('bndbox', data=h5_bndbox)
f.create_dataset('imgname', data=h5_imgname)
f.create_dataset('part', data=h5_part)
又或者是直接的赋值
f['bndbox'] = h5_bndbox
f['imgname'] = h5_imgname
f['part'] = h5_part
关闭文件
f.close()
关于字符串的特殊处理
正如上文所说,HDF5中的数据的操作是类比numpy的矩阵,但如果我们存储字符串的数据的话,其实也是可以操作的。
解码字符串
比如要训练模型,从一个标注数据的annotation文件中读取图片的文件名。这是一个字符串经过转码得到的矩阵,包含了146545个文件名的字符串。
>>> f = h5py.File('annot.h5','r')
>>> imgname = f['imgname'][:]
>>> imgname.shape
(146545, 31)
我们取出一个文件名来看看
>>> img0 = imgname[0]
>>> img.dtype
dtype('float64')
>>> img0[...]
array([ 67., 79., 67., 79., 95., 116., 114., 97., 105.,
110., 50., 48., 49., 52., 95., 48., 48., 48.,
48., 48., 48., 50., 54., 50., 49., 52., 53.,
46., 106., 112., 103.])
可以发现,字符串被转码成float64
。接下来我们来恢复字符串。其实这里每个数字都是一个ascii
码,其代表的字符合在一起就是原本的字符串。用循环逐个元素进行操作当然是很简单,但我们需要一个向量化的操作,进行快速转换。最好就是用numpy
自带的函数实现转化。
# 先转化编码格式
>>> img0 = img0.astype(np.uint8)
>>> img0.tostring()
'COCO_train2014_000000262145.jpg'
可以看到字符串解码出来了,有时候我们还需要一个后续步骤。
>>> img0.tostring().decode('ascii')
u'COCO_train2014_000000262145.jpg'
编码字符串
基本上就是上面的逆操作
np.fromstring(imagename,dtype=np.uint8).astype('float64')
# 写进h5
f.create_dataset('imgname', data=imgname)
要注意,最后得出来的矩阵长度是字符串的长度。如果想将多个字符串拼成一个大的numpy
矩阵,写到h5
文件中,必须先将字符串转换成相同长度。
我通常的做法是在字符串后面补上\x00
。