【问题标题】:Efficiently saving tiles to a bigtiff image有效地将图块保存到 bigtiff 图像
【发布时间】:2020-04-10 10:21:57
【问题描述】:

我有数千个 256 x 256 像素的灰度图块,dtype 为 np.uint8,我想尽快将它们组合成一个 BigTiff 金字塔图像。

我目前的方法是创建一个具有最终图像大小的 numpy 数组,我在其中粘贴所有图块(这只需要几秒钟)。为了节省,我研究了多种方法。

1) Tifffile,使用 imsave 函数,结果非常慢,对于一个最终大小约为 700MB 的文件,我估计至少需要 10 多分钟

2)pyvips,通过使用pyvips.Image.new_from_memory将海量的numpy图像转换为pyvips图像,然后使用以下方法保存:

vips_img.tiffsave(filename, tile=True, compression='lzw', bigtiff=True, pyramid=True, Q=80)

构建 vips_img 大约需要 42 秒,将其保存到磁盘需要大约 30 秒,但这一切都是使用单个线程完成的。我想知道是否有任何方法可以更有效地做到这一点,或者使用不同的方法或利用多线程。可以使用高速存储,因此可以先以不同的格式保存内容,或者在需要时将其转换为不同的编程语言。

只是头脑风暴:所有图块都来自现有的 BigTiff 图像,并且已经通过预处理管道,现在需要再次保存。我想知道是否有一种方法可以有效地复制原始文件并替换其中的数据。

编辑更多信息:

图像的尺寸大约为 55k x 45k,但我也想将此代码用于更大的图像,例如最大 150k x 150k。

对于 55k x 45k 的图像和 256 x 256 的图块,我们谈论的是大约 53k 的图块。这些图块并不都包含我感兴趣的信息,所以最后我可能会得到我想再次保存的 50% 的图块,剩下的图像可能是黑色的。以相同格式保存处理后的文件对我来说似乎是最方便的方法,因为我想将其显示为叠加层

使用中间解决方案编辑

之前我提到从 numpy 数组创建 pyvips 图像需​​要 40 秒。原因是我的输入是一个转置的 numpy 数组。转置操作本身非常快,但我怀疑它像以前一样保留在内存中,这在以转置形式读取时导致了很多缓存未命中。

所以目前以下行需要 30 秒(写入 200MB 文件)

    vips_img.tiffsave(filename, tile=True, compression='lzw', bigtiff=True, pyramid=True, Q=80)

如果这可以更快,那就太好了,但这似乎是合理的。

代码示例

在我的例子中,只有约 15% 的图块是有趣的并且会被预处理。这些都在图像中。我仍然希望将其保存为千兆像素格式,因为这允许我使用 openslide 使用其方便的库检索图像的部分内容。在示例中,我只是生成了约 15% 的随机数据来模拟黑色/信息的百分比,示例的性能类似于数据更分散在图像上的实际实现。

import numpy as np
import pyvips

def numpy2vips(a):
    dtype_to_format = {
    'uint8': 'uchar',
    'int8': 'char',
    'uint16': 'ushort',
    'int16': 'short',
    'uint32': 'uint',
    'int32': 'int',
    'float32': 'float',
    'float64': 'double',
    'complex64': 'complex',
    'complex128': 'dpcomplex',
    }
    height, width, bands = a.shape
    linear = a.reshape(width * height * bands)
    vi = pyvips.Image.new_from_memory(linear.data, width, height, bands,
                                      dtype_to_format[str(a.dtype)])
    return vi

left = np.random.randint(0, 256, (7500, 45000), np.uint8)
right = np.zeros((50000, 45000), np.uint8)
img = np.vstack((left, right))
vips_img = numpy2vips(np.expand_dims(img, axis=2))

start = time.time()
vips_img.tiffsave("t1", tile=True, compression='deflate', bigtiff=True, pyramid=True)
print("pyramid deflate took: ", time.time() - start)

start = time.time()
vips_img.tiffsave("t2", tile=True, compression='lzw', bigtiff=True, pyramid=True)
print("pyramid lzw took: ", time.time() - start)

start = time.time()
vips_img.tiffsave("t3", tile=True, compression='jpeg', bigtiff=True, pyramid=True)
print("pyramid jpg took: ", time.time() - start)

start = time.time()
vips_img.dzsave("t4", tile_size=256, depth='one', overlap=0, suffix='.jpg[Q=75]')
print("dzi took: ", time.time() - start)

输出

pyramid deflate took:  32.69183301925659
pyramid lzw took:  32.10764741897583
pyramid jpg took:  59.79427194595337

我没有等待 dzsave 完成,因为它需要花费几分钟。

【问题讨论】:

  • 欢迎来到 Stackoverflow!为了帮助其他人为您的问题找到更好的解决方案,请在您的帖子中分享有关您的问题的更多信息,并为您迄今为止尝试过的代码编写 sn-ps。
  • 你能说明一下图片的数量吗?以及输出文件的像素尺寸。谢谢。
  • 哦,请问你用的是什么操作系统?
  • MacOS 在我的笔记本电脑和 linux 上进行部署
  • 如果没有具体的基准,很难给出具体的建议。我会制作一个小型的、独立的程序,它可以做你想做的更快的事情,然后请求 cmets。尝试在没有优化的情况下进行优化只是猜测。您可以尝试 1)使用 deflate,而不是 lzw,以及带有 SIMD 路径的 libz,2)在 pyvips 中进行转置(两次翻转),它应该得到并行化,3)将更多的处理移动到 pyvips(它通常比numpy) 4) 使用 dzi 而不是 pyr tiff(你可以并行编写图块)

标签: python image gdal libtiff vips


【解决方案1】:

我在我的笔记本电脑(ubuntu 19.10)上尝试了你的测试程序,我看到了:

pyramid deflate took:  35.757954359054565
pyramid lzw took:  42.69455623626709
pyramid jpg took:  26.614688634872437
dzi took:  44.16632699966431

我猜你没有使用libjpeg-turbo,SIMD libjpeg fork。不幸的是,由于 brew 卡在非 SIMD 版本上,在 macOS 上安装非常困难,但在您的部署系统上应该很容易,只需安装 libjpeg-turbo 包而不是 libjpeg(它们是二进制兼容的)。

有各种similar projects for zlib 可以显着加快放气压缩。

【讨论】:

  • 感谢您的测试,我一定会研究 libjpeg-turbo。我刚刚注意到输出文件包含 10 个不同的缩放级别,这超出了我的要求,我想知道减少它是否可以更快地保存。在文档中,我注意到 tiffsave 的深度参数,但是在 python 中使用它或使用 vips 命令行工具时,深度参数是未知的。 ` vips_img.tiffsave("depth3.tif", tile=True, compression='deflate', bigtiff=True, pyramid=True, depth=3)` 给出深度的关键错误。你知道是否有另一种方法可以减少金字塔中的层数?
  • 顶部的几个金字塔层很小,我认为它们不会增加任何显着的处理时间。 depth 已添加到 libvips 8.10 中,尚未发布。它允许您选择缩小到一个像素、缩小到一个平铺、仅一层。
  • 我想知道您是否仍然知道一种方法来规避创建它们,或者如何从金字塔中删除图层。我的源图像只有 3 个缩放级别(以 4 的幂为增量并跳过高缩放级别),但使用 vips,相同大小的图像有 10 个缩放级别。对于我的特定用例,如果缩放级别和缩放级别数量与源图像相同,那将非常方便。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多