【问题标题】:How can I write to a png/tiff file patch-by-patch?如何逐个补丁写入 png/tiff 文件?
【发布时间】:2018-06-27 09:52:59
【问题描述】:

我想从一个无法一次全部加载到内存中的非常大的 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))

我正在寻找一种方法来做到这一点,而无需将整个图像加载到内存中。我已经尝试过枕头库,但它会将所有数据加载/保存在内存中。

编辑:这个问题与 h5py 无关,而是我们如何将超大图像(无法加载到内存中)以补丁的形式写入文件 - 类似于如何通过写入行来构造大型文本文件按行。

【问题讨论】:

  • tifffile.memmap 为您提供存储在 TIFF 文件中的内存映射 numpy 数组。不过没有压缩或平铺。
  • 你能不能创建一堆单独的相对较小的 png/tiff 图像,然后将它们组合成一个大的图像?
  • 投票结束。 Does h5py read the whole file into memory? 的可能重复项
  • 刺客:图像太大而无法记忆有什么好处?无论如何,我怀疑您可以通过尝试将一堆图像以这两种压缩格式中的任何一种组合在一起来实现这一点。如果相反,如果未压缩(或仅限于面向行的压缩,如 RLE),则可以通过仅在任何给定时间打开包含相同行的行的行来最小化组合它们时的内存使用量最终图像。这将允许将最终图像的线条连接在一起,而无需一次将所有线条全部读入内存。
  • 我也看不出单张图片太大而无法放入内存的意义。你将如何渲染它?对于地图之类的东西,您在概念上会拥有一张大图像,实际上它们被分解成图块并按需提供。如果您的问题不是特定于 HDF5,那么它似乎与这个问题重复:stackoverflow.com/questions/31806526/…

标签: python python-imaging-library pillow


【解决方案1】:

试试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 标签中的元数据直接从条带中读取。

【讨论】:

  • @gohlke bigtiff=True 在最新版本中似乎不起作用。
【解决方案2】:

“如果 Python 中有一种方法可以在补丁中写入 png 或 tiff 文件?”的简短回答。嗯,是的——只要有足够的时间和技能来实现它,在 Python 中一切皆有可能。另一方面,不,没有现成的解决方案 - 因为它似乎不是很有用。

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

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

所以,如果我必须这样做,我会这样做(注意,您需要了解 Python 的 bytes 类型以及将字节序列转换为 Python 数据类型和从 Python 数据类型转换的方法):

  • 找出我可以放入内存的行数,并将其设为“小图像块”的高度。宽度是整个最终图像的宽度。让我们称之为widthsmall_height

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 2023-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-28
    相关资源
    最近更新 更多