有效地将平铺保存为大图像



我有数千个具有dtype np.uint8的256 x 256像素的灰度瓦片,我想尽快将它们组合成一个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乘45k,但我也想将此代码用于较大的图像,例如,高达150k乘150k。

对于55k乘45k的图像和256乘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结束,因为它需要几分钟的时间。

我在我的笔记本电脑上尝试了你的测试程序(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(它们是二进制兼容的(。

对于zlib,有各种类似的项目可以显著地加速deflate压缩。

相关内容

  • 没有找到相关文章

最新更新