如何逐个补丁写入 png/tiff 文件?

How can I write to a png/tiff file patch-by-patch?

我想从无法一次加载到内存中的非常大的 h5py 数据集创建一个 png 或 tiff 图像文件。所以,我想知道 python 中是否有一种方法可以在补丁中写入 png 或 tiff 文件? (我可以将切片中的 h5py 数据集加载到 numpy.ndarray)。 我试过使用枕头库并PIL.Image.paste给出盒子坐标,但是对于大图像它会耗尽内存。

基本上,我想知道是否有办法做类似的事情:

for y in range(0, height, patch_size):
    for x in range(0, width, patch_size):
        y2 = min(y + patch_size, height)
        x2 = min(x + patch_size, width)
        # image_arr is an h5py dataset that cannot be loaded completely
        # in memory, so load it in slices
        image_file.write(image_arr[y:y2, x:x2], box=(y, x, y2, x2))

我正在寻找一种方法来执行此操作,而无需将整个图像加载到内存中。我试过枕头库,但它 loads/keeps 内存中的所有数据。

编辑:这个问题不是关于 h5py,而是关于我们可以将多大的图像(无法加载到内存中)写入补丁中的文件 - 类似于通过写入可以构造多大的文本文件一行一行。

对 "if there is a way in Python to write to a png or tiff file in patches?" 的简短回答。嗯,是的 - 在 Python 中一切皆有可能,只要有足够的时间和技能来实施它。另一方面,不,没有现成的解决方案 - 因为它似乎不是很有用。

我不知道 TIFF,这里的评论说它限制为 4GB,所以这种格式可能不是一个好的选择。 PNG 没有实际限制,可以 以块的形式写入,因此理论上是可行的 - 前提是生成的图像的至少一条扫描线适合内存。

如果您真的想继续这个,这里是您需要的信息: PNG 文件由一些元数据块和一系列图像数据块组成。后者彼此独立,因此您可以通过简单地将它们的图像数据块 (IDAT) 连接在一起并添加所需的元数据块(您可以从第一个小图像中选择那些,除了 IHDR 块 - 需要构造一个以包含最终图像大小)。

所以,如果必须的话,我会这样做(请注意,您需要了解 Python 的 bytes 类型以及将字节序列转换为和来自 Python 数据类型来实现这一点):

  • 找出我可以放入内存的行数,并使其成为我的 "small image chunk" 的高度。宽度是整个最终图像的宽度。我们称它们为 widthsmall_height

  • 一次一个块地浏览我在 h5py 中的巨型数据集 (width * small_height),将其转换为 PNG 并将其保存到磁盘的临时文件中,或者如果您的图像转换库允许它 - 直接到内存中的 bytes 字符串。然后对字节数据进行如下处理,最后删除:

    -- 在第一次迭代中:一次遍历 PNG 数据一条记录(请参阅 PNG 规范:http://www.libpng.org/pub/png/spec/1.2/png-1.2-pdg.html,它采用长度-标签-值形式,并且非常容易编写代码有效地逐条记录地遍历文件),将所有记录保存到我的目标文件中,除了:修改 IHDR 以获得最终图像大小并跳过 IEND 记录。

    -- 在所有后续迭代中:扫描 PNG 数据并仅选择 IDAT 记录,将这些记录写入输出文件。

  • 将 IEND 记录附加到目标文件。

全部完成 - 您现在应该拥有一个有效的巨大 PNG。不过,我想知道谁或什么能读到它。

尝试 tifffile.memmap:

from tifffile import memmap

image_file = memmap('temp.tif', shape=(height, width), dtype=image_arr.dtype,
                    bigtiff=True)

for y in range(0, height, patch_size):
    for x in range(0, width, patch_size):
        y2 = min(y + patch_size, height)
        x2 = min(x + patch_size, width)
        image_file[y:y2, x:x2] = image_arr[y:y2, x:x2]

image_file.flush()

这将创建一个带有一个条带的未压缩 BigTIFF 文件。内存映射图块尚未实现。不确定有多少图书馆可以处理这种文件,但您始终可以使用 TIFF 标签中的元数据直接从片段中读取。