arrays - 如何将一个非常大的numpy数组保存为图像,尽可能少地加载到内存中

标签 arrays python-3.x numpy memory-management tiff

我的程序经常使用非常大的 numpy 数组 ((819200, 460800, 4), uint8)。要将其存储在内存中(作为纯零),我需要超过 1.3TB 的内存,这是荒谬的。
我的目标是能够将这些 numpy 数组保存为图像。我也希望它尽可能快,但速度不是问题。

一开始我所做的是将 numpy 数组存储在 HDF5 文件中(使用 H5PY),然后我会对该数组进行处理,然后使用 CV2 进行保存。不仅速度慢,CV2 似乎将图像加载到内存中,所以这个想法很快就消失了。
现在,我已经尝试了 20 多种不同的方法来保存这些大数组,所以为了缩短这篇文章,我只提一些最新的方法。

使用CV2后,我发现了一个名为'numpngw的库'。它是一个基于 numpy 和 python 的 png 编写器。这是我的代码:

f = h5py.File("mytestfile.hdf5", "w")
dset = f.create_dataset("mydataset", (100000,100000,4), dtype=np.uint8, compression='gzip')

shp = dset.shape    
step = 10000


png = open("new.png", "wb")
numpngw._write_header_and_meta(png, 8, shp, color_type=6, bitdepth=8, palette=None, #i'm manually writing to the png file rather than writing all data at once, so i can append data over and over again.
                            interlace=0, text_list=None, timestamp=None, sbit=None, gamma=None, iccp=None,
                            chromaticity=None, trans=None, background=None, phys=None)


for i in range(0, shp[0]+step, step): #from step to 
    numpngw._write_data(png, dset[i:i+step, i:i+step], bitdepth=8, max_chunk_len=step, #writing the data in largest chunks I can
                    filter_type=None, interlace=0)
    png.flush()
    #gc.collect()

    numpngw._write_iend(png)

png.close()
f.close()

这样做的想法是,它只是一遍又一遍地写入 numpy 数组的块,直到写入整个数组。
我什至不知道这个版本是否有效,因为它太慢了。

然后我用 PIL 尝试了相同的块写入方法。我没有使用 PNG,而是使用了 TIFF,因为它看起来要快得多。不幸的是,PIL 不支持以块的形式附加到 TIFF。 “append”参数用于动画 TIFF,所以我不能那样做。

我最近使用的最后一个库是 tifffile .它似乎做了我需要的一切。它还有一个 memmap 实现,可以从内存映射的 numpy 数组中生成 TIFF 文件。

blank = numpy.zeros((256,256,3))
memmap_image = tifffile.memmap('temp.tif', shape=blank.shape, dtype='uint8')
memmap_image[:] = blank[:]
memmap_image.flush()
del memmap_image

这将创建一个空白的 TIFF 文件。将此与 H5PY 相结合,我可以保存大图像 - 或者我是这么认为的。大 TIFF 文件似乎已损坏。我尝试在 (Windows) 照片、Adobe Acrobat Reader DC 和 Affinity Photo 中打开它们。所有人都说文件无法识别(有时 Affinity Photo 甚至在打开时崩溃 - 不过可能是内存问题)。
我不知道什么会使图像损坏,因为它似乎适用于较小的阵列。第二天我又回来了,开始在这条线上出现内存错误(突然)memmap_image[:] = blank[:] .

我尝试的最后一件事是将块方法与 tifffile 结合起来:

f = h5py.File("mytestfile.hdf5", "w")
dset = f.create_dataset("mydataset", (100000,100000,3), dtype=np.uint8)

shp = dset.shape    
step = 10000

a = tiffile.memmap('temp.tif', shape=(100000,100000,3), dtype=np.uint8)

for i in range(0, shp[0]+step, step):
    a[i:i+step,i:i+step] = dset[i:i+step,i:i+step]
    a.flush()
del a

大约需要 2 分钟(还不错!),它创建了一个大文件(~29GB,压缩将使它变小),但是,它再次损坏并且没有任何东西可以读取 TIFF 文件。

我真的不想放弃这个项目,但我仍然坚持我可以尝试的其他方法。
谁能推荐一个支持附加到图像但又不想将其加载到内存中的 TIFF/PNG 库?

最佳答案

标准 TIFF 不能用于存储 100000x100000 RGB 图像,除非它非常可压缩。由于使用 32 位偏移量,TIFF 文件的大小限制为 4 GB。 BigTIFF使用 64 位偏移量。要启用 tifffile 写入 BigTIFF,请使用 bigtiff=True参数与 memmapimwrite .但是,由于不支持 BigTIFF 和/或大 strip 尺寸,因此没有多少软件/库能够读取这些文件。

这么大的图像通常是平铺存储的,通常带有压缩和多种分辨率(金字塔)。 Tifffile 可以从内存映射的 numpy 数组或瓦片生成器创建平铺(大)TIFF,例如:

import numpy
import h5py
import tifffile

dtype = 'uint8'
shape = 100000, 100000, 3
tileshape = 1024, 1024

f = h5py.File('test.hdf5', 'w')
data = f.create_dataset('test', shape, dtype=dtype, compression='gzip')


def tile_generator(data, tileshape):
    for y in range(0, data.shape[0], tileshape[0]):
        for x in range(0, data.shape[1], tileshape[1]):
            tile = data[y: y+tileshape[0], x: x+tileshape[1], :]
            if tile.shape[:2] != tileshape:
                pad = (
                    (0, tileshape[0] - tile.shape[0]),
                    (0, tileshape[1] - tile.shape[1]),
                    (0, 0)
                )
                tile = numpy.pad(tile, pad, 'constant')
            yield tile


tifffile.imwrite(
    'temp.tif', 
    tile_generator(data, tileshape),
    dtype=dtype, 
    shape=shape, 
    tile=tileshape,
    bigtiff=True,
    # compress='jpeg'
)

tifffile 通过 imagecodecs 支持多种压缩选项图书馆,例如放气、LZMA、ZStd、JPEG、JPEG2000、JPEGXR、WebP...

有专门的 TIFF“子格式”、库和工具来处理金字塔 TIFF,通常取决于应用程序域,例如libvips , OpenSlide , GDAL , 或 BioFormats .

关于arrays - 如何将一个非常大的numpy数组保存为图像,尽可能少地加载到内存中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62245475/

相关文章:

java - 在链表末尾插入并不是一个特殊情况。那么数组的末尾呢?

python - 从 DF 中删除包含重复单词的短语(Pandas、Python3)

python - 将 numpy 数组转换为列向量

python - 标签矩阵到邻接矩阵

Javascript:总和或所有数字在for循环中不起作用

javascript - 将字符串转换为数组中的多个字符串

使用指针组合两个不同的数组?

由于 UnboundLocalError,Python3 方法无法调用

python - 在 Python 中使用 Selenium,有没有办法在登录在线帐户时使网络驱动程序/浏览器成为 "recognized device"?

python - 如何在 matlab 和 python 中重现相同的随机整数数组?