我的程序经常使用非常大的 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
参数与 memmap
或 imwrite
.但是,由于不支持 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/