【问题标题】:Load dataset from HDF5 file in C#在 C# 中从 HDF5 文件加载数据集
【发布时间】:2019-12-10 18:13:50
【问题描述】:

我正在尝试从 C# (.NET Framework) 中的 HDF5 文件加载数据集,以便将内容保存在数组中,例如float[,]。我找到了HDF.PInvoke 库,但我发现很难弄清楚如何使用它。

更新

来自Soonts answer,我设法让它工作。这是我的工作 sn-p:

using System;
using System.Runtime.InteropServices;
using HDF.PInvoke;

namespace MyNamespace
{
    class Program
    {
        static void Main()
        {
            string datasetPath = "/dense1/dense1/kernel:0";
            long fileId = H5F.open(@"\path\to\weights.h5", H5F.ACC_RDONLY);
            long dataSetId = H5D.open(fileId, datasetPath);
            long typeId = H5D.get_type(dataSetId);

            // read array (shape may be inferred w/ H5S.get_simple_extent_ndims)
            float[,] arr = new float[162, 128];
            GCHandle gch = GCHandle.Alloc(arr, GCHandleType.Pinned);
            try
            {
                H5D.read(dataSetId, typeId, H5S.ALL, H5S.ALL, H5P.DEFAULT,
                         gch.AddrOfPinnedObject());
            }
            finally
            {
                gch.Free();
            }

            // show one entry
            Console.WriteLine(arr[13, 87].ToString());

            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
}

原来的第一次尝试:

到目前为止我所管理的:

using System;
using System.IO;
using System.Runtime.InteropServices;
using HDF.PInvoke;

namespace MyNamespace
{
    class Program
    {
        static void Main()
        {
            string datasetPath = "/dense1/dense1/bias:0";
            long fileId = H5F.open(@"\path\to\weights.h5", H5F.ACC_RDONLY);
            long dataSetId = H5D.open(fileId, datasetPath);
            long typeId = H5D.get_type(dataSetId);
            long spaceId = H5D.get_space(dataSetId);

            // not sure about this
            TextWriter tw = Console.Out;
            GCHandle gch = GCHandle.Alloc(tw);

            // I was hoping that  this would write to the Console, but the
            // program crashes outside the scope of the c# debugger.
            H5D.read(
                dataSetId,
                typeId,
                H5S.ALL,
                H5S.ALL,
                H5P.DEFAULT,
                GCHandle.ToIntPtr(gch)
            );

            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
}

H5F.read() 的签名是:

Type    Name            Description
--------------------------------------------------------------
long    dset_id         Identifier of the dataset read from.
long    mem_type_id     Identifier of the memory datatype.
long    mem_space_id    Identifier of the memory dataspace.
long    file_space_id   Identifier of the dataset's dataspace in the file.
long    plist_id        Identifier of a transfer property list for this I/O operation.
IntPtr  buf             Buffer to receive data read from file.

问题

谁能帮我填这里的空白?

【问题讨论】:

  • 顺便说一句,HDF5 是垃圾。 API 设计和文档不是很好。它很慢。它是单线程的,当尝试从多个线程使用时崩溃,即使是不同的文件。这是不可靠的,如果出现任何问题,通常会破坏包含所有数据集的完整文件。如果你打算用它来写东西而不仅仅是阅读,我建议你找一个替代品。
  • 相信我,我也对整件事感到非常沮丧。只是我需要它来反序列化keras创建的一些模型权重,它使用h5py。
  • 你可以用一行代码替换你的重塑代码Buffer.BlockCopy( arrFlat, 0, arr, 0, w * h * 4 ); 会快得多。对这么小的数组并不重要,但仍然如此。
  • 另外,由于您不需要转置或填充数据,您可以尝试固定二维数组,并将其传递给原生库。也可能会起作用,这样您根本不需要复制/重塑。
  • @AdamB kernel:0 只是 Keras 在调用 keras_model.save_weights('weights.h5')(在 python 中)时赋予数据集的名称。

标签: c# hdf5 hdf5dotnet


【解决方案1】:

您需要创建一个大小和类型正确的数组(普通的一维数组,而不是二维数组)。然后这样写:

int width = 1920, height = 1080;
float[] data = new float[ width * height ];
var gch = GCHandle.Alloc( data, GCHandleType.Pinned );
try
{
    H5D.read( /* skipped */, gch.AddrOfPinnedObject() );
}
finally
{
    gch.Free();
}

这会将数据集读入data 数组,然后您可以根据需要将各个行复制到另一个二维数组中。

阅读 API 文档如何获取维度(HDF5 支持任意维度的数据集)和数据集的大小(对于 2D 数据集,大小为 2 个整数),即如何找出所需的缓冲区大小(对于 2D 数据集,是width * height)。

至于元素类型,你最好提前知道,例如float 很好。

【讨论】:

  • 非常感谢! using 对我不起作用(它抱怨 GCHandle 不能转换为 IDisposable),但其余的都有效。谢谢!
【解决方案2】:

或者,也许您想看看HDFql,因为它减轻了 HDF5 低级细节。您的(上面发布的)解决方案可以使用 HDFql 重写/简化如下:

using System;
using System.Runtime.InteropServices;
using AS.HDFql;   // use HDFql namespace (make sure it can be found by the C# compiler)

namespace MyNamespace
{
    class Program
    {
        static void Main()
        {
            // dims
            int h = 162;
            int w = 128;

            // read array
            float[] arrFlat = new float[h * w];

            HDFql.Execute("SELECT FROM \\path\\to\\weights.h5 \"/dense1/dense1/kernel:0\" INTO MEMORY " + HDFql.VariableTransientRegister(arrFlat));        

            // reshape
            float[,] arr = new float[h, w];  // row-major
            for (int i = 0; i < h; i++)
            {
                for (int j = 0; j < w; j++)
                {
                    arr[i, j] = arrFlat[i * w + j];
                }
            }

            // show one entry
            Console.WriteLine(arr[13, 87].ToString());
            Console.WriteLine(arrFlat[13 * w + 87].ToString());

            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
}

有关如何使用 HDFql 读取数据集的其他示例可以在 quick start guidereference manual 中找到。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-10-23
    • 2014-12-19
    • 2018-02-08
    • 1970-01-01
    • 1970-01-01
    • 2016-05-06
    • 1970-01-01
    • 2019-06-12
    相关资源
    最近更新 更多