高效地将平铺保存到bigtiff图像

2024-06-13 20:46:08 发布

您现在位置:Python中文网/ 问答频道 /正文

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

我目前的方法是创建一个具有最终图像大小的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×45k,但我也希望将此代码用于更大的图像,例如150k×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

我没有等到比赛结束,因为比赛需要几分钟的时间


Tags: 文件方法图像numpypyramidtrueimgtime
1条回答
网友
1楼 · 发布于 2024-06-13 20:46:08

我在我的笔记本电脑(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可以显著加快放气压缩

相关问题 更多 >