Python处理HDF5文件

less than 1 minute read

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