Python处理HDF5文件

最近在做mscoco的比赛,接触到HDF5这种格式,发现真的很好用。有多好用?读写速度,内存占用,压缩率都很强。

HDF5文件是一种为了储存和处理大数据而设计的文件格式,具有极高的压缩率。H5接受的数据是矩阵,通常跟numpy库一起使用。

一个HDF5文件包含两种对象:

  1. group:类似与文件夹,读取可类比python中的字典。
  2. dataset:数据内容,类比numpy中的array。

H5py的安装

在python中处理HDF5文件依赖于h5py这个库,可以使用pip在线安装。或者如果你使用的Anaconda,就会自带h5py库。

1
pip install h5py

读取H5文件

1
2
3
4
>>> import h5py
>>> import numpy as np
>>> # 打开文件
>>> f = h5py.File('test-dev.h5', 'r')

H5中的group可以类比为字典,因此我们可以用keys()来获取键值。

1
2
>>> f.keys()
[u'my_xmax', u'my_xmin', u'my_ymax', u'my_ymin']

如上,我们发现文件里有四个数据集,我们可以像读取字典一样读取文件。

1
>>> xmax = f['my_xmax']

读取到的xmax,可以像处理numpy矩阵那样操作。这里有个小窍门。

1
2
3
4
5
6
7
>>> xmax = f['my_xmax']
>>> type(xmax)
h5py._hl.dataset.Dataset
>>> xmax = f['my_xmax'][:]
>>> type(xmax)
numpy.ndarray

类比numpy的操作

1
2
3
4
5
6
>>> xmax.shape
(1257351,)
>>> xmax.dtype
dtype('int64')
>>> xmax[...]
array([527, 260, 638, ..., 365, 334, 262])

写入H5文件

打开方式用'w'

1
>>> f = h5py.File('test-dev.h5','w')

利用文件方法写入数据

1
2
3
>>> f.create_dataset('bndbox', data=h5_bndbox)
>>> f.create_dataset('imgname', data=h5_imgname)
>>> f.create_dataset('part', data=h5_part)

又或者是直接的赋值

1
2
3
>>> f['bndbox'] = h5_bndbox
>>> f['imgname'] = h5_imgname
>>> f['part'] = h5_part

关闭文件

1
>>> f.close()

关于字符串的特殊处理

正如上文所说,HDF5中的数据的操作是类比numpy的矩阵,但如果我们存储字符串的数据的话,其实也是可以操作的。

解码字符串

比如要训练模型,从一个标注数据的annotation文件中读取图片的文件名。这是一个字符串经过转码得到的矩阵,包含了146545个文件名的字符串。

1
2
3
4
>>> f = h5py.File('annot.h5','r')
>>> imgname = f['imgname'][:]
>>> imgname.shape
(146545, 31)

我们取出一个文件名来看看

1
2
3
4
5
6
7
8
9
>>> 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自带的函数实现转化。

1
2
3
4
# 先转化编码格式
>>> img0 = img0.astype(np.uint8)
>>> img0.tostring()
'COCO_train2014_000000262145.jpg'

可以看到字符串解码出来了,有时候我们还需要一个后续步骤。

1
2
>>> img0.tostring().decode('ascii')
u'COCO_train2014_000000262145.jpg'

编码字符串

基本上就是上面的逆操作

1
2
3
np.fromstring(imagename,dtype=np.uint8).astype('float64')
# 写进h5
f.create_dataset('imgname', data=imgname)

要注意,最后得出来的矩阵长度是字符串的长度。如果想将多个字符串拼成一个大的numpy矩阵,写到h5文件中,必须先将字符串转换成相同长度。

我通常的做法是在字符串后面补上\x00

0%