如何制作较小的临时文件?

时间:2013-04-09 19:40:05

标签: python temporary-files

所以我正在编写这个程序,它创建了一个mandelbrot集合的图片,并且我已经逐渐改进了它。现在,每个生成的进程都会将一些数据写入临时文件,稍后会将该文件放在一起使用。然而,现在,临时文件比实际图片本身要大,我对如何缩小它们没有任何想法。如何有效地将整数数据写入文件,并将其恢复?我打算最终使这个可扩展,所以我需要能够为像素索引编写任意长整数,但颜色数据总是三个整数,最大值为255.这是我的代码:

import multiprocessing

def pixproc(y0, yn, xsteps, ysteps, fname):
    XMIN, YMIN = -2., -1.
    XLEN, YLEN = 3, 2
    with open(fname, 'w') as f:
        for y in xrange(y0, yn):
            print y
            for x in xrange(xsteps):
                c=complex(XMIN + XLEN*(1.*x/xsteps),
                          YMIN + YLEN*(1.*y/ysteps))
                k=c
                for i in xrange(256):
                    k = k*k + c
                    if abs(k)>2: break
                if 0<i<32:
                    #print 'Success!', i
                    print >>f, x, y, 8*i, 0, 0 #This is that part of
                if 32<=i<255:                  #my code that I am trying
                    #print 'Success!', i       #to improve. The rest of 
                    print >>f, x, y, 255, i, i #the code is given for context
    return                                     #and isn't relevant to my question


def main(xsteps, ysteps):
    pool = multiprocessing.Pool()
    n = multiprocessing.cpu_count()
    step = height / n
    fnames = ["temp" + str(i) for i in xrange(n)]
    for i in xrange(n):
        pool.apply_async(pixproc, 
                         (step*i, 
                          step*(i+1), 
                          xsteps, 
                          ysteps, 
                          fnames[i]))
    pool.close()
    pool.join()
    return fnames


if __name__=="__main__":
    from PIL import Image
    import sys
    width, height = map(int, sys.argv[1:])
    picname = "mandelbrot1.png"
    fnames = main(width, height)
    im = Image.new("RGB", (width, height))
    pp = im.load()
    for name in fnames:
        with open(name) as f:
            for line in f:
                line = map(int, line.rstrip('\n').split(' '))
                pp[line[0], line[1]] = line[2], line[3], line[4]
    im.save(picname)

当我尝试制作3000x2000的图片时,实际图片为672 KB,但临时文件都接近30 MB!有人可以建议更好的方法将数据存储在文件中吗? (重要的部分是函数pixproc)

2 个答案:

答案 0 :(得分:2)

假设您只是想消除使用基于文本的格式而不是二进制格式的临时数据的开销,并且您不想重写所有内容以使用numpy,那么有一些不同的解决方案:


首先,您可以将数据保留为二进制格式:mmap文件,并使用ctypes将其视为某种巨型记录。这通常比它的价值更麻烦,但值得一提。

假设您的数据只是一长串5字节的元组列表:

class Entry(ctypes.Structure):
    _fields_ = [("x", ctypes.c_uint8), ("y", ctypes.c_uint8),
                ("i", ctypes.c_uint8), ("j", ctypes.c_uint8), ("k", ctypes.c_uint8)]
Entries = ctypes.POINTER(Entry)
with open(fname, 'wb') as f:
    f.truncate(ctypes.sizeof(Entry * (yn - y0)))
    m = mmap.mmap(f.fileno(), access=mmap.ACCESS_WRITE)

其次,您可以使用struct。您需要阅读文档以获取完整的详细信息,但我将举一个例子。我们来看看这一行:

print >>f, x, y, 8*i, 0, 0

现在,让我们假设所有5个都保证是字节(0-255)。你可以这样做:

f.write(struct.pack('BBBBB', x, y, 8*i, 0, 0))

稍后再阅读:

x, y, i8, 0, 0 = struct.unpack('BBBBB', f.read(struct.calcsize('BBBBB')))
i = i8//8

如果它们中的任何一个需要长于一个字节,则需要处理字节序,但这非常简单。例如,如果xy的范围是-32768到32767:

f.write(struct.pack('>hhBBB', x, y, 8*i, 0, 0))

并确保以二进制模式打开文件。

如果您愿意,当然可以将其与mmap结合使用,这意味着您可以使用struct.pack_intostruct.unpack_from代替明确使用pack加{{} 1}}和writeunpack


接下来,有pickle。要么直接创建列表而只是read,要么手动pickle.dump每个条目,并在其上方添加一些简单的高级结构(或者只使用shelve,如果更高级别的结构是或者可以是从键到条目的简单映射。这可能更大而不是更小,并且可能更慢,所以在考虑这个之前你总是想做一些测试。但有时这是一个简单的解决方案。


最后,您可能想出一种比仅打印每个对象的pickle.dumps表示更紧凑的文本格式。这通常不值得付出努力,但同样值得思考。

答案 1 :(得分:1)

您可以使用struct模块以二进制格式写入数据:

print >>f, struct.pack('@IIBBB', x, y, 8*i, 0, 0)
print >>f, struct.pack('@IIBBB', x, y, 255, i, i)

您可以使用bz2.BZ2File类编写压缩文件:

  with bz2.BZ2File(fname, 'w') as f:
      ...

你甚至可以把这两个结合起来......