HDF4也被称为HDF(Hierarchical Data Format),可以存储不同类型的图像和数码数据的文件格式,并且可以在不同类型的机器上传输,同时还有统一处理这种文件格式的函数库。随着技术的发展,现存在两个完全不同的HDF文件版本:HDF4和HDF5,其中HDF4是第一个HDF版本。我们可以通过访问HDF4的官方网站(http://www.hdfgroup.org/products/hdf4/)查看其详细介绍。
如果只是需要查看HDF4文件数据的话,我们可以从官网上下载并安装HDFView(一个简单的HDF文件查看工具):
但是如果需要通过编程手段读写HDF4文件数据的话,我们就必须先从官网上下载并安装HDF4的函数库,然后再编程调用该函数库来实现对其的读写。
以下介绍如何通过C#方式读取HDF4文件的属性和数据。
一、安装HDF4函数库:
在HDF4官网下载页(http://www.hdfgroup.org/release4/obtain.html)下载并安装HDF4函数库。官网提供了多个系统平台的编译版本,在这里我们选择“Windows (32-bit)”平台:
如在安装过程中发现环境变量无法写入,我们可以忽略设置环境变量选项。
二、引用HDF4函数库:
从官网中我们可以发现,现在的HDF4函数库只有提供C++和Fortran访问的版本,要想在C#程序中使用的话,我们需要通过DllImport方式引入。HDF4的函数库主要包含在hdf.dll和mfhdf.dll这两个库中,而常用来读取HDF4中数据的SD接口则在mdhdf.dll库中。
// 定义HDF4 SD接口函数库的文件位置
public const string MFHDF4_DLL = @"C:\Program Files (x86)\HDF_Group\HDF\4.2.11\bin\mfhdf.dll";
// 引入SDstart方法
[DllImport(MFHDF4_DLL, EntryPoint = "SDstart", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDstart(string filename, int access_mode);
// 引入SDfindattr方法
[DllImport(MFHDF4_DLL, EntryPoint = "SDfindattr", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDfindattr(int obj_id, string attr_name);
// 引入SDreadattr方法(字符串类型属性)
[DllImport(MFHDF4_DLL, EntryPoint = "SDreadattr", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDreadattr(int obj_id, int attr_index, StringBuilder attr_buffer);
// 引入SDreadattr方法(单精度浮点类型属性)
[DllImport(MFHDF4_DLL, EntryPoint = "SDreadattr", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDreadattr(int obj_id, int attr_index, float[] attr_buffer);
// 引入SDnametoindex方法
[DllImport(MFHDF4_DLL, EntryPoint = "SDnametoindex", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDnametoindex(int sd_id, string sds_name);
// 引入SDselect方法
[DllImport(MFHDF4_DLL, EntryPoint = "SDselect", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDselect(int sd_id, int sds_index);
// 引入SDgetinfo方法
[DllImport(MFHDF4_DLL, EntryPoint = "SDgetinfo", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDgetinfo(int sds_id, StringBuilder sds_name, int[] rank, int[] dimsizes, int[] ntype, int[] num_attrs);
// 引入SDreaddata方法
[DllImport(MFHDF4_DLL, EntryPoint = "SDreaddata", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDreaddata(int sds_id, int[] start, int[] stride, int[] edge, short[,] buffer);
// 引入SDendaccess方法
[DllImport(MFHDF4_DLL, EntryPoint = "SDendaccess", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDendaccess(int sds_id);
// 引入SDend方法
[DllImport(MFHDF4_DLL, EntryPoint = "SDend", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDend(int sd_id);
以上我们通过DllImport的方式引入读取HDF4文件所需要用到的相关SD接口函数,其中EntryPoint属性为对应的函数名称,CallingConvention属性必须设置为CallingConvention.Cdecl。由于个别函数返回的数据是通过指针引用在函数参数中的,因此我们需要通过StringBuilder来接收字符串数据,通过值类型数组(如float[]、int[]、short[]等)来接收数值型数据和数组型数据(不能通过ref或out参数返回)。
三、读取HDF4属性和数据:
// 要读取的HDF4文件
string hdf4File = "C:/test.hdf";
// 字符串类型属性
string sensor;
// 单精度浮点类型属性
float slope;
// 数据行数
int row;
// 数据列数
int column;
// 数据集数据
short[,] data;
// 函数调用状态
int status;
// 只读方式打开HDF4文件
int sd_id = SDstart(hdf4File, 1);
if (sd_id != -1)
{
// 查找属性Sensor的索引号
int attr_index = SDfindattr(sd_id, "Sensor");
if (attr_index != -1)
{
// 读取Sensor属性,通过StringBuilder接收字符串数据
StringBuilder buffer = new StringBuilder();
status = SDreadattr(sd_id, attr_index, buffer);
if (status != -1)
{
sensor = buffer.ToString();
}
}
// 查找属性Slope的索引号
attr_index = SDfindattr(sd_id, "Slope");
if (attr_index != -1)
{
// 读取Slope属性,通过float[]接收单精度浮点数据
float[] buffer = new float[1];
status = SDreadattr(sd_id, attr_index, buffer);
if (status != -1)
{
slope = buffer[0];
}
}
// 查找数据集DATA的索引号
int sds_indes = SDnametoindex(sd_id, "DATA");
// 选中数据集
int sds_id = SDselect(sd_id, sds_indes);
if (sds_id != -1)
{
// 数据集名称
StringBuilder sds_name = new StringBuilder();
// 秩数
int[] rank = new int[1];
// 行列数
int[] dimsizes = new int[2];
// 数据类型
int[] ntype = new int[1];
// 属性数目
int[] num_attrs = new int[1];
// 读取数据集信息
status = SDgetinfo(sds_id, sds_name, rank, dimsizes, ntype, num_attrs);
if (status != -1)
{
row = dimsizes[0];
column = dimsizes[1];
// 读取数据集所有数据
short[,] buffer = new short[row, column];
status = SDreaddata(sds_id, new int[] { 0, 0 }, null, new int[] { row, column }, buffer);
if (status != -1)
{
data = buffer;
}
}
// 结束数据集访问
status = SDendaccess(sds_id);
}
// 关闭HDF4文件
status = SDend(sd_id);
}
从以上方法中可以看出,虽然HDF4官方并没有提供可直接让C#访问的类库,但我们还是可以基于DllImport这种静态函数引入的方式对其进行访问。
更多信息请参阅:The HDF Group – Information, Support, and Software