内存中numpy数组(图像,uint8)的有损压缩

use*_*893 4 python compression performance numpy image-compression

我正在尝试将 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')
Run Code Online (Sandbox Code Playgroud)

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

[np.array(Image.open(output)) for _ in range(1000)]
Run Code Online (Sandbox Code Playgroud)

JPEG 压缩非常有效(< 10 GB),但将 1000 张图像读回 numpy 数组所需的时间约为 2.3 秒,这严重影响了我的实验性能。我正在寻找可以在压缩和读取速度之间进行更好权衡的建议。

Mar*_*ell 5

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

首先,我使用GNU ParallelImageMagick创建了 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}
Run Code Online (Sandbox Code Playgroud)

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

在此处输入图片说明

然后我做了我认为您正在尝试做的事情-尽管很难从您的代码中看出:

#!/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))
Run Code Online (Sandbox Code Playgroud)

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

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

np.array(ImageList[i].convert('RGB'))
Run Code Online (Sandbox Code Playgroud)

将数据存储为调色板图像可节省 66% 的空间,因为每个像素仅存储 1 个字节的调色板索引而不是 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")
Run Code Online (Sandbox Code Playgroud)

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