我有数千个 dtype np.uint8 的 256 x 256 像素的灰度图块,我想尽快将它们组合成一个 BigTiff 金字塔图像。
我目前的方法是创建一个具有最终图像大小的 numpy 数组,我在其中粘贴所有图块(这只需要几秒钟)。为了节省,我研究了多种方法。
1)Tifffile,使用该imsave
功能,结果非常慢,我估计至少 10 分钟以上的文件最终会在 700MB 左右
2) pyvips,通过使用 将大量 numpy 图像转换为 pyvips 图像pyvips.Image.new_from_memory
,然后使用以下方法保存它:
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 完成,因为它需要几分钟。