内存中numpy数组(image,uint8)的有限压缩

问题描述 投票:0回答:1

我正在尝试将1.000.000图像的数据集加载到内存中。作为标准的numpy数组(uint8),所有图像的组合填充了大约100 GB的RAM,但我需要将其降低到<50 GB,同时仍能快速将图像读回numpy(这就是将所有内容保存在内存中的重点) )。像blosc这样的无损压缩只能将文件大小减少大约10%,所以我进行了JPEG压缩。最小例子:

import io
from PIL import Image

numpy_array = (255 * np.random.rand(256, 256, 3)).astype(np.uint8)
image = Image.fromarray(numpy_array)
output = io.BytesIO()
image.save(output, format='JPEG')

在运行时我正在阅读图像:

[np.array(Image.open(output)) for _ in range(1000)]

JPEG压缩是非常有效的(<10 GB),但是将1000个图像读回到numpy数组所需的时间大约是2.3秒,这严重损害了我的实验性能。我正在寻找能够在压缩和读取速度之间进行更好折衷的建议。

python performance numpy compression image-compression
1个回答
2
投票

我仍然不确定我理解你想要做什么,但我创建了一些虚拟图像,并进行了如下测试。我将展示我是如何做到的,以防其他人想要尝试其他方法并想要一个数据集。

首先,我使用GNU Parallel和ImageMagick创建了1,000个图像,如下所示:

parallel convert -depth 8 -size 256x256 xc:red +noise random -fill white -gravity center -pointsize 72 -annotate 0 "{}" -alpha off s_{}.png ::: {0..999}

这给了我1000张通过s_0.png称为s_999.png的图像,图像663看起来像这样:

enter image description here

然后我做了我认为你想做的事情 - 尽管你的代码很难说清楚:

#!/usr/local/bin/python3

import io
import time
import numpy as np
from PIL import Image

# Create BytesIO object
output = io.BytesIO()

# Load all 1,000 images and write into BytesIO object
for i in range(1000):
   name="s_{}.png".format(i)
   print("Opening image: {}".format(name))
   im = Image.open(name)
   im.save(output, format='JPEG',quality=50)
   nbytes = output.getbuffer().nbytes
   print("BytesIO size: {}".format(nbytes))

# Read back images from BytesIO ito list
start=time.clock()
l=[np.array(Image.open(output)) for _ in range(1000)]
diff=time.clock()-start
print("Time: {}".format(diff))

这需要2.4秒才能从BytesIO对象中读取所有1,000个图像并将它们转换为numpy数组。

然后,我通过减少到256种颜色(我同意是有损的 - 就像你的方法一样)来调整图像,并保存了一个palettised图像对象列表,我可以稍后通过简单地调用以后转换回numpy数组:

np.array(ImageList[i].convert('RGB'))

将数据存储为palettised图像可节省66%的空间,因为每个像素只存储一个字节的调色板索引而不是3个字节的RGB,因此它优于您寻求的50%压缩。

#!/usr/local/bin/python3

import io
import time
import numpy as np
from PIL import Image

# Empty list of images
ImageList = []

# Load all 1,000 images 
for i in range(1000):
   name="s_{}.png".format(i)
   print("Opening image: {}".format(name))
   im = Image.open(name)
   # Add palettised image to list
   ImageList.append(im.quantize(colors=256, method=2))

# Read back images into numpy arrays
start=time.clock()
l=[np.array(ImageList[i].convert('RGB')) for i in range(1000)]
diff=time.clock()-start
print("Time: {}".format(diff))

# Quick test
# Image.fromarray(l[999]).save("result.png")

现在需要0.2s而不是2.4s - 让我们希望你的未说明的应用程序可以接受颜色准确性的损失:-)

© www.soinside.com 2019 - 2024. All rights reserved.