python pickle - 转储一个非常大的列表

2 投票
2 回答
2040 浏览
提问于 2025-04-18 16:00

我有两个文件夹,每个文件夹里大约有50,000张图片,主要都是240x180的大小。

我想把这些图片的像素信息保存成训练集、验证集和测试集,

但这样做显然会产生非常非常大的文件,最后导致电脑要么崩溃,要么磁盘空间用完。

当电脑卡住的时候,正在生成的pkl文件已经有28GB了。

我不确定这是不是正常的大小。

我是不是做错了什么?或者有没有更有效的方法来处理这个问题?

from PIL import Image
import pickle
import os

indir1 = 'Positive'
indir2 = 'Negative'

trainimage = []
trainpixels = []
trainlabels = []
validimage = []
validpixels = []
validlabels = []
testimage = []
testpixels = []
testlabels = []


i=0
for (root, dirs, filenames) in os.walk(indir1):
    print 'hello'
    for f in filenames:
        try:
            im = Image.open(os.path.join(root,f))
            if i<40000:
                trainpixels.append(im.tostring())
                trainlabels.append(0)
            elif i<45000:
                validpixels.append(im.tostring())
                validlabels.append(0)
            else:
                testpixels.append(im.tostring())
                testlabels.append(0)
            print str(i)+'\t'+str(f)
            i+=1
        except IOError:
            continue

i=0
for (root, dirs, filenames) in os.walk(indir2):
print 'hello'
    for f in filenames:
        try:
            im = Image.open(os.path.join(root,f))
            if i<40000:
                trainpixels.append(im.tostring())
                trainlabels.append(1)
            elif i<45000:
                validpixels.append(im.tostring())
                validlabels.append(1)
            else:
                testpixels.append(im.tostring())
                testlabels.append(1)
            print str(i)+'\t'+str(f)
            i+=1
        except IOError:
            continue

trainimage.append(trainpixels)
trainimage.append(trainlabels)
validimage.append(validpixels)
validimage.append(validlabels)
testimage.append(testpixels)
testimage.append(testlabels)

output=open('data.pkl','wb')

pickle.dump(trainimage,output)
pickle.dump(validimage,output)
pickle.dump(testimage,output)

2 个回答

1

我同意,你可能不应该把大量的图片存储到硬盘上……除非你真的有必要这样做(不管是什么原因)。你可能需要一个非常大的硬盘,配上很好的内存和强大的处理能力。

不过,如果你把图片数据转移到一个numpy.array里,使用 scipy.ndimage.imread,那么你就可以利用numpy内部的格式加上压缩来把图片存储到硬盘上。

还有一些像 klepto 这样的工具,可以让这个过程变得简单。

>>> from klepto.archives import dir_archive
>>> from scipy import ndimage
>>> demo = dir_archive('demo', {}, serialized=True, compression=9, cached=False)
>>> demo['image1'] = ndimage.imread('image1')
>>> demo['image2'] = ndimage.imread('image2')

现在你有了一个字典接口,可以用来访问压缩过的pickle格式的图片文件,每个文件里存一张图片,放在一个叫 demo 的文件夹里(也许你需要加上 fast=True 这个选项,我记不太清楚了)。所有的字典方法基本上都可以用,所以你可以根据需要访问图片进行分析,然后用 del demo['image1'] 或类似的方式把不需要的图片删除。

你还可以使用 klepto 来轻松提供自定义的编码,这样你的数据就可以比较安全地存储。你甚至可以选择不加密或不pickle你的数据,而只是用字典接口来管理硬盘上的文件——这本身就很方便。

如果你不关闭缓存,可能会遇到电脑内存或硬盘空间的限制,除非你在存取图片时小心顺序。在上面的例子中,我关闭了内存缓存,所以数据直接写入硬盘。还有其他选项,比如使用内存映射模式,或者写入HDF文件。我通常会用上面提到的方式来处理大数组数据,适合在单台机器上处理,而对于较小的数据,我可能会选择MySQL作为后端,这样可以让多台机器并行访问。

你可以在这里获取 kleptohttps://github.com/uqfoundation

3

pickle文件格式其实不是很高效,特别是对于图片来说。即使你的每个像素只占用1个字节,计算一下:

50,000 × 240 × 180 = 2,160,000,000

这就相当于2GB。实际上,你的像素可能占用的空间还要更多。我不太确定PIL的tostring()方法在处理图片时具体做了什么。很有可能,最终生成的文件会达到十几GB。

你可能需要考虑其他的存储方式,而不是用pickle。比如,直接把图片以它们原本的格式存储在硬盘上,然后再把文件名放到一个列表里进行pickle,这样会不会更好呢?

撰写回答