我有数千个dtype为np.uint8的256 x 256像素的灰度图块,我想将这些图块尽快合并到一张BigTiff金字塔图像中。
我目前的方法是创建一个具有最终图像大小的numpy数组,在其中粘贴所有图块(这仅需要几秒钟)。为了节省,我研究了多种方法。
1] Tifffile,使用imsave
函数,结果非常慢,我估计至少要花10分钟以上才能找到大约700MB的文件
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×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
我想您没有使用SIMD libjpeg分支libjpeg-turbo。不幸的是,由于brew停留在非SIMD版本上,因此很难在macOS上安装,但是在您的部署系统上应该很容易,只需安装libjpeg-turbo软件包而不是libjpeg(它们是二进制兼容的)。
[各种similar projects for zlib应该可以大大加快放气压缩。