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这种静态函数引入的方式对其进行访问。