【图像处理】HDF5 C++编程简介

HDF5是一种科学数据格式,其特点是可以将不同类型的数据集整合保存到一个文件,并分别打上标签、注释。相比旧版的HDF格式,HDF5能支持大于4GB的文件读写;HDF5在数据集的基础上增加包Group,类似文件夹。这使得HDF5文件内部像普通的文件系统一样,便于数据的整合管理。HDF5跟数据库有很大区别,HDF5使用树形结构来标注保存的多个数据集,并可使用类似python字典的方法来索引这些数据,与数据库的索引操作类似,然而缺少数据的外键、连接等关键操作,因此HDF5更加类似于zip等压缩文件的专用版,并且HDF5内置支持几种开源无损压缩格式。HDF5原生用C语言编写,支持C++、python、java等多种语言。
这里简单介绍下C++相关的编程,HDF5作为科学计算领域的明星,而科学计算离不开C/C++;HDF5的C语言接口复杂难学,而C++接口则能自行完成大多数工作。因此HDF5的C++编程非常重要。

写数据

首先是用H5File函数打开文件,然后使用createDataSet函数创建数据集,或者使用createGroup创建包/文件夹,再在group下面调用createDataSet函数。写入数据需要传入原始数据指针、原始数据类型、原始数据大小、写入规则。原始数据类型可以直接使用预定义的本地数据类型。数据大小信息保存在DataSpace类中。写入规则保存在DSetCreatPropList中。
HDF5支持直接保存数组数据,或者保存分块数据。分块规则保存在DSetCreatPropList中,分块大小对于数据读写有重大影响。
实例:

H5File *file = new H5File(path.c_str(), H5F_ACC_TRUNC);//不用c字符串貌似有bug
Group g = file->createGroup(gname.c_str());
int dims[2]={10,10};
DataSpace mspace(2,dims);
DSetCreatPropList param;
int chunkDims={256,256};
param.setChunk(2,chunkDims);
int fillVal = 0;
param.setFillValue(&fillVal);//分块多出来部分的填充值
param.setDeflate(6);//deflate无损压缩
int *data = new int[100];
...//make data
int **wdata = new int[10];
for(int i = 0; i < 10; ++i)
  wdata[i] = data+i*10;
DataSet d = g.createDataSet(dname.c_str(),PredType::NATIVE_INT,mspace, param);
d.write(wdata[0], PredType::NATIVE_INT);
g.close()
file->close();

HDF5允许直接写入二维数组,这样要裁剪区域数据的操作api就容易写。而图像处理通常使用一维数组来存储图像,我们可以创建一个二维数组指针来指向正确的位置,然后写入,避免重建二维数组的工作。这里要注意write函数的第一个参数,是wdata[0]而不是wdata。写入操作基本就这些,写注释操作依样画葫芦即可。请注意必须要记得使用close函数关闭group,不然没有数据写入。

读数据

HDF5允许使用缓存技术来加速读取数据。HDF5缓存技术需要指定单个缓存块大小(必须大于文件chunk的尺寸),缓存块数量和缓存策略。缓存块数量是用一个hash值来给定的,官网推荐这个值是目标最大缓存块数量的10倍。缓存策略输入的是一个值,表示缓存块的生存权重默认为0.75,0值表示使用LRU策略来决定缓存块的保留与否。我暂时没有找到缓存函数的C++接口,不过C函数是可以和C++兼容的。实例如下:

H5File *file = new H5File(path.c_str(), H5F_ACC_RDONLY);
H5Pset_cache(file->getAccessPlist().getId(),0,521, 256*256,0.5);//第二个参数被废弃了,后续参数是hash值,缓存块大小,缓存策略值
if(!file->exists(dname.c_str())) return 0;//检测数据是否存在
DataSet d = file->openDataSet(dname.c_str());
hsize_t offset[2]={1,1};
hsize_t cout[2]={3,4};
hszie_t stride[2]={1,1};//默认就是1,可以不用管。这里展示一个完整的代码
hsize_t block[2]={1,1};//默认就是1,可以不用管。这里展示一个完整的代码
DataSpace mspace(2,count,NULL);
DataSpace dataSpace = d.getSpace();
dataSpace.selectHyperslab(H5S_SELECT_SET, count, offset, stride, block);
int **data = .....//prepare data
d.read(data[0], PredType::NATIVE_INT, mspace, dataSpace);
file->close();

注意读图有两个DataSpace,dataSpace指定读取范围,mspace给定读取数据的大小(感觉参数有点重复)。
如果不知道数据集的大小,则需要从DataSet获取相关信息:

int rank = dataSpace.getSimpleExtentNdims();
hsize_t *dims = new hsize_t[rank];
dataSpace.getSimpleExtentdims(dims);

此外,HDF5封装了多种跨平台的数据类型,支持复合数据类型,所以HDF5不会直接返回给用户当前DataSet的数据类型是什么,需要预先告知编程者。如果使用本地数据类型,则相对简单:

DataType type = dataSet.getDataType();
if(type == PredType::NATIVE_INT){
...
}

C++类DataType可以指代任意数据类型,并支持用==符号来选择正确的数据类型。

遍历

遍历没有找到C++接口,只能用C语言编写,需要准备一个回调函数,回调函数用iterateElems函数来调用:

herr_t Op(hid_t group, const char *name, void *data)
{
H5G_stat_t statbuf;
H5Gget_objinfo(group, name, false, & statbuf);
//name是每一轮遍历的名称,statbuf存储文件信息,内部的H5G_obj_t可以鉴别是数据集还是group类型。data是外部传入的数据,可以把遍历文件的信息导出去
std::vector<std::string> *opdata = (std::vector<std::string> *)data;
opdata->push_back(name);
}

int num = file.getNumObjs();//根目录下obj的数量
int *ids=new int[num];
for(int k = 0; k < num; ++k)
  ids[k] = k;
std::vector<std::string> dataList;
file.iterateElems("/",ids, Op,&dataList);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值