在Skimage imread中将Python字符串作为文件
我正在Kaggle上参加CIFAR挑战。
他们给了我一个.7z的压缩文件,里面有5万张图片作为训练数据。我花了一个小时来解压这个文件,然后又花了40分钟来读取所有的文件并把它们放到内存里。
我尽量不想创建5万个文件,因为这会成为一个瓶颈。我安装了pylzma和其他一些库,但它们都告诉我这个文件无效。
我用bash的7z命令可以正常读取这个文件,并列出里面的文件。所以我使用了Popen
来解压所有文件,并通过bash的7z程序把它们放到内存中的一个字符串里。
import subprocess
p = subprocess.Popen(["7z", "e", "-so", "awa.7z"], stdout=subprocess.PIPE).communicate()[0]
我已经成功地通过查看每个文件的大小,来单独获取每个文件的内容,然后从字符串中提取出相应的字节。
f1 = p[0][0:2105]
现在我想让Python相信这是一个文件指针F1,这样我就可以调用skimage.io.imread,它会把数据转换成合适的结构。或者我也可以直接把内存中的值传给skimage,让它为我转换。
3 个回答
skimage.io.imread()
的第一个参数是你要读取的图像文件的名字,所以你不能用字符串里的图像数据来欺骗它。这里有几种选择(按方便程度排序):
直接使用
imread
这个包 - 可以看看imread.imread_from_blob()
。这个方法会返回一个numpy.ndarray
(和skimage.io.imread
一样)。你需要知道图像文件的类型(比如jpg、png、gif等),因为这需要作为第二个参数传入:from imread import imread_from_blob img_data = imread_from_blob(f1, 'jpg') >>> img_data array([[[ 23, 123, 149], [ 22, 120, 147], [ 22, 118, 143], ..., etc.
把数据写入一个临时文件,然后用
imread()
打开这个临时文件。imread()
在处理URL时会自动这样做。- 使用命名管道。用
imread()
打开管道进行读取,然后把数据写入管道。你可能需要使用线程或多进程来实现这一点。
虽然skimage.io.imread的文档说第一个参数应该是文件名的字符串,但我发现它也可以接受类似文件的对象(在skimage 0.10.0版本中)。
所以你可以像这样把图像数据读入内存:
from StringIO import StringIO
with open(filename) as f:
img_data = f.read()
decoded_img_data = skimage.io.imread(StringIO(img_data))
print decoded_img_data
>> OUTPUT:
array([[[235, 230, 234],
[233, 228, 232],
[231, 226, 230],
...,
在浏览skimage的代码时,我发现他们可以和PIL这个图像库进行整合。PIL库有一个功能,可以直接从打开的文件指针获取图像信息。
在我的情况下,文件指针是一个StringIO对象,所以它可以读取数据并识别这些数据的内容。
mhawke,谢谢你的帮助。对我来说,你的解决方案似乎也能奏效,但我不想直接处理图像数据。
我把代码放在了github上(这只是一个框架,但可以运行),如果有人感兴趣,可以看看这个页面 http://adrianow.github.io/7z_on_array/
下面是解决方案的一小部分:
import numpy as np
from PIL import Image
from StringIO import StringIO
# begin and end of each file
low = 0
up = 0
images = [0] *len(p_f_list)
# get each file from the byte file
for i, f in enumerate(p_f_list):
up += int(f[0])
# get bytes from the array
raw_img = p_f_data.data[low:up]
low = up
# Convert rawImage to Mat
pil_image = Image.open(StringIO(raw_img))
np_image = np.array(pil_image)
images[i] = np_image