繁体   English   中英

在python上处理大型geotiff时出现内存错误

[英]memory error while processing large geotiffs on python

我有两个光栅图像 geotiff,一个 (275521, 329643) 变暗(我们称之为年度图像)和另一个(73583, 152367)变暗(月度图像)。 两个栅格的像元大小已经相同。 使用 Python,我想做以下事情:

  1. 剪裁年度图像,使其与每月图像范围相同。
  2. 将年度影像的范围与每月影像的范围相匹配。
  3. 从年度图像文件创建一个二进制掩码,该文件是一个分类数据集 (vals 0/1/2/3),使得所有包含 3 的单元格返回值 1,所有其他单元格都返回 0。
  4. 将月度图像文件与这个新的蒙版层相乘以过滤掉与 yearly.data == 3 单元格对齐的单元格。

我一直在 Windows 上的 Python 中使用 rasterio 和 rioxarray,并且想学习如何在 Python 中执行此操作,因为我将循环浏览 8 个年度图像文件,并且对于这些年中的每一年,12 个每月图像文件。

可以预见的是,我一直遇到内存错误,特别是当我尝试从年度图像创建蒙版时,

MemoryError:无法为形状为 (1, 73583, 152367) 且数据类型为 uint8 的数组分配 10.4 GiB

我知道我应该尝试使用某种多处理工具,并且一直在尝试使用 Dask 实现我需要的东西,但老实说,我不知道我在做什么或如何开始处理这些数组(即,创建一个庞大的数组 [年掩码],将这个庞大的数组与另一个庞大的数组 [每月图像] 相乘,以创建另一个庞大的数组 [掩码的每月数据])。 是否会感谢所有类型的帮助或建议、代码示例、教程? 非常感谢。

不确定如何提供数据,但如果有帮助,它们是从谷歌地球引擎下载的图像文件。 这是一些示例代码:

import rasterio as rio
import xarray as xr
import rioxarray
import numpy as np

years = np.arange(2012, 2020)
monthly_file_paths = ["\paths\to\monthly\image\tifs"]
yearly_file_paths = ["\paths\to\yearly\image\tifs"]
monthly_sample = xr.open_rasterio(monthly_file_paths[0], chunks={"x":256, "y":256})

for yearly_file in yearly_file_paths:
    yearly = xr.open_rasterio(yearly_file, chunks={"x":256, "y":256}) # !!!: Previously encountered memory error here before including the "chunks" bit
    yearly = yearly.rio.set_nodata(0)
    
    # crop yearly layer to monthly bounding box
    yearly = yearly.rio.clip_box(minx=monthly_sample.x.min().item(),
                    miny=monthly_sample.y.min().item(),
                    maxx=monthly_sample.x.max().item(),
                    maxy=monthly_sample.y.max().item())
    
    # reproject and resample yearly to match monthly extents and crs
    yearly = yearly.rio.reproject_match(monthly_sample )
    yearly = yearly.assign_coords({
            "x": monthly_sample.x,
            "y": monthly_sample.y})

    # create mask.
    yearly.data = xr.where(yearly==3, 1, 0) # !!!: Error occurs here

    for month_file in monthly_file_paths :
        _monthly = xr.open_rasterio(month_file)
        _monthly.data = xr.where(_monthly==2, 1, 0)
        yearly_monthly = yearly * _monthly
        yearly_monthly = yearly_monthly.rio.update_attrs(_monthly.attrs)
        yearly_monthly.rio.to_raster(f'{month_file}.tif', dtype=np.uint8,
                              compress='deflate')```

这将是一个棘手的问题! 处理这么大的光栅文件总是很麻烦。 我没有灵丹妙药的答案,但这里有一些我会考虑的事情。

对齐和分块

我首先会直接检查您的文件,并确保您的数据精确对齐,例如使用_monthly.x.isin(yearly.x.values).all()和 y 相同。

然后,仅在打开数据时使用da.sel读取这些索引。

另外,请确保您将 _monthly 文件分块,而不仅仅是年度文件:

_monthly = xr.open_rasterio(month_file)

执行此操作时,请尝试确保您的块完全对齐(您不希望它们偏移,因此需要加载多个块以进行每个块的合并xr.unify_chunks在这里可能会有所帮助 - 我还没有使用过它,但我认为这是预期的用例。

类型

另一个问题是 xarray 假设您总是需要 64 位数据类型,除非您指定。 线

 _monthly.data = xr.where(_monthly==2, 1, 0)

首先将_monthly==2转换为 bool 类型,然后将 python int s 10转换为与输入数组相同的形状,并猜测它们应该是 64 位整数。 这将您的 10GB 问题变成了 80GB 问题! 看这个简单的例子:

In [10]: data = xr.DataArray(np.array([1], dtype='uint8'))
In [11]: xr.where(data==1, 1, 0).dtype
Out[11]: dtype('int64')

相反,对你的类型要非常迂腐:

In [12]: one_u8 = np.array(1, dtype='uint8')
In [13]: zero_u8 = np.array(0, dtype='uint8')
In [14]: xr.where(data==1, one_u8, zero_u8).dtype
Out[14]: dtype('uint8')

重新投影

最后,任何涉及“重新投影”或重塑数据的事情都可能真的把事情搞砸:

yearly = yearly.rio.reproject_match(monthly_sample)

我对重新投影的担忧是:

  1. 您需要确保重新投影后坐标标签相同,而不是之前。 因此,请确保在合并之前但在重新投影之后在两个数组之间共享每个 x 和 y。 我知道这是 reproject_match 的目标,但是当依赖浮点坐标精确匹配时,这种情况可能会出错。 如果需要,您可以考虑在重新投影后使用整数坐标。
  2. 这是否加载数据? 块会发生什么? 我认为这是应该起作用的事情; 当您谈论这么大的工作时,它是否在实践中起作用始终是另一个问题。 在处理如此大的数组时,您正在推动 xarray/rioxarray 的限制,因为单个加载或随意的 float64 强制可能会破坏您的记忆。

可能的前进方向

好消息是你绝对是在正确的轨道上。 我会用 rioxarray、xarray 和 dask 来完全按照您的方法来执行此操作。 逐行逐步执行工作流程,而不是尝试将其全部写出来并在失败时对其进行调试。 检查xarray对象在每一步,在DASK块,大小,并在dtypes密切关注da.data对象,当您去。 如果它是一个选项,请在 jupyterlab notebook 中对其进行原型设计 - dask-backed xarray.DataArrays 的 html 界面真的很有帮助。

我能提供的最好建议是不要试图一次性完成这一切,至少在您知道工作流程的每个部分都顺利运行之前不要尝试。 相反,将您的工作流程分解为多个步骤,并在每个步骤之后写入磁盘,从中间文件中读取以进行下一步。

  1. 减少您的数据以仅包含所需的形状
  2. 重新投影。 祝你好运:D
  3. 使用分块读取数据,并明确检查数据对齐,例如使用xr.align(..., join='exact')
  4. 掩码和乘法

可能有用的一件事是使用明确支持并行 I/O 和分块的文件类型,例如 zarr。 如果您在缓存步骤中将 zarr 换成 rasterio,这可能会变得容易一些。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM