1

所以我正在编写这个程序来创建曼德布罗集的图片,并且我一直在逐步改进它。现在,产生的每个进程都会将一些数据写入一个临时文件,稍后将使用该文件将图片放在一起。然而,现在临时文件比实际图片本身大很多,我对如何使它们更小没有任何想法。如何有效地将整数数据写入文件并将其取回?我打算最终使它非常可扩展,因此我需要能够为像素索引编写任意长的整数,但颜色数据始终是最大值为 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 中)

4

2 回答 2

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_intoandstruct.unpack_from而不是显式使用packpluswriteunpackplus read


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


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

于 2013-04-09T19:55:26.430 回答
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:
      ...

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

于 2013-04-09T19:43:12.667 回答