如何使用NetCDF-java高效读取HDF5文件

问题描述 投票:0回答:1

使用 NetCDF-java API 读取 HDF5 的有效方法是什么,给出由以下形式的结构组成的栅格变量?

Structure {
  float depth;
  float uncertainty;
} values(2115, 1635);
:_ChunkSizes = 67U, 103U; // uint

过去,我很幸运地使用 NetCDF-java API 来处理简单的栅格变量(请参阅读取 NetCDF 文件)。但是当我尝试访问结构类型时,我的代码运行速度非常慢。 处理上述结构的数据需要运行 36 分钟。使用JHDF Java HDF 库处理相同的文件只需要大约 2 秒。 显然,我没有按照作者的意图使用 NetCDF-java API。不幸的是,我找不到任何处理结构变量的好例子。我确实阅读了项目中的 Javadoc 和 Junit 测试用例,但我能弄清楚的最好的代码是下面所示的代码。

我正在使用 NetCDF-java 的当前版本,版本 5.6.0。我测试的示例文件是 IHO S-102 格式 (HDF5) 文件,给出港口的底部深度。样本文件可在NOAA S-102 测深表面数据 下载。我测试了许多具有类似结果的文件(上面显示的变量来自102US00_US4NJ1FH.h5)。尽管下面的代码在行和列上循环,但我还尝试了一种基于块大小方案访问网格单元的变体。 查看代码,很明显该循环创建了大量的短期持久对象,但我认为真正的问题在于底层方法。 我非常确定代码会为其检索的每个数据值执行不同的文件访问操作。但我一直没能找到更有效的方法来使用 API。


    NetcdfFile ncfile = NetcdfFiles.open(targetFilePath);
    Variable v = ncfile.findVariable(targetVariableName);
    System.out.println(""+v.toString());
    Structure s = (Structure) v;
    v.setCaching(true);  // does not seem to make a difference
    int[] shape = s.getShape();
    StructureData sd = s.readStructure(0);
    Member m = sd.findMember("depth");
    double sumValid = 0;
    int nValid = 0;
    int nNoData = 0;
    for (int i = 0; i < shape[0]; i++) {
      for (int j = 0; j < shape[1]; j++) {
        int index = i * shape[1] + j;
        sd = s.readStructure(index);
        float[] f = sd.getJavaArrayFloat(m);
        if (f[0] == 1000000) {
          nNoData++;
        } else {
          sumValid += f[0];
          nValid++;
        }
      }
    }
    System.out.println("nValid:  " + nValid);
    System.out.println("nNoData: " + nNoData);
    System.out.println("Mean:    " + (sumValid / nValid));

预先感谢您的帮助。

hdf5 netcdf-java
1个回答
0
投票

我还不确定使用您提供的代码导致速度缓慢的原因,但在

readStructure
调用中有些东西似乎不太正确。似乎每次它从结构中读取单个值时,都会从磁盘加载整个块,解压缩它,读取单个值,然后重复自身(因此 3,458,025 次加载块/解压缩/获取单浮点迭代。 ..不好)。

如果您知道要读取结构变量给定成员的所有数据,则可以采用其他几种方法,这两种方法都快得多。请注意,在这些示例中,我使用此文件 (102US00_US4NJ1FH.h5)

targetVariableName = "BathymetryCoverage/BathymetryCoverage.01/Group_001/values"

首先,您可以使用结构迭代器:

try (NetcdfFile ncfile = NetcdfFiles.open(targetFilePath)) {
    Variable v = ncfile.findVariable(targetVariableName);
    Structure s = (Structure) v;

    double sumValid = 0;
    int nValid = 0;
    int nNoData = 0;

    try (StructureDataIterator siter = s.getStructureIterator()) {
      while (siter.hasNext()) {
        StructureData sd = siter.next();
        float f = sd.getScalarFloat("depth");
        if (f == 1000000) {
          nNoData++;
        } else {
          sumValid += f;
          nValid++;
        }
      }
    }

    System.out.println("nValid:  " + nValid);
    System.out.println("nNoData: " + nNoData);
    System.out.println("Mean:    " + (sumValid / nValid));
}

这在我的机器上只需要不到 4 秒。

其次,您可以一起跳过结构特定的 API,并将成员读取为

Variable
,方法是将
.<memberName>
附加到结构变量名称,例如:

try (NetcdfFile ncfile = NetcdfFiles.open(targetFilePath)) {
    Variable v = ncfile.findVariable(targetVariableName + ".depth");

    double sumValid = 0;
    int nValid = 0;
    int nNoData = 0;

    Array memberArray = v.read();
    Index index = memberArray.getIndex();
    int[] shape = memberArray.getShape();
    for (int i = 0; i < shape[0]; i++) {
      for (int j = 0; j < shape[1]; j++) {
        float f = memberArray.getFloat(index.set(i, j));
        if (f == 1000000) {
          nNoData++;
        } else {
          sumValid += f;
          nValid++;
        }
      }
    }
    System.out.println("nValid:  " + nValid);
    System.out.println("nNoData: " + nNoData);
    System.out.println("Mean:    " + (sumValid / nValid));
}

这在我的机器上大约需要 500 毫秒,尽管需要将整个成员数据数组加载到内存中。

© www.soinside.com 2019 - 2024. All rights reserved.