2013-04-09 3 views
1

Итак, я пишу эту программу, которая создает картину набора мандельбротов, и я постепенно улучшал ее. прямо сейчас, каждый процесс, который порожден, записывает некоторые данные во временный файл, который используется позже, чтобы сместить изображение вместе. Прямо сейчас, однако, временные файлы ALOT больше, чем фактическое изображение, и у меня нет никаких идей о том, как их уменьшить. Как эффективно записывать целочисленные данные в файл и возвращать его? Я намерен в конечном итоге сделать это очень масштабируемым, поэтому мне нужно было бы писать произвольно длинные целые числа для индексов пикселей, но данные цвета всегда будут иметь три целых числа, которые имеют максимальное значение 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 МБ! Может ли кто-нибудь предложить лучший способ хранения данных в файлах? (Важная часть в функции pixproc)

+0

Первое: фактическое изображение - 672 КБ _compressed_. Если у вас есть файл 3Kx2K с, скажем, RGB8 пикселями, это 18 МБ несжатого. Таким образом, накладные расходы составляют около 75%. Вы хотите уменьшить это на 75% до минимума или сжать временные данные? – abarnert

+0

Во-вторых, вы можете использовать 'numpy' вместо того, чтобы делать все явно? Помимо того факта, что передача по огромному массиву numpy обычно на порядок быстрее, чем итерация по огромному списку Python, у numpy есть встроенный код для сохранения массивов как можно компактнее. – abarnert

+0

Ну, я использовал numpy, поэтому лично знаю, как быстро. Но я никогда не делаю никаких огромных списков python, и у меня всегда были проблемы с использованием numpy с многопроцессорной обработкой. Я пытаюсь сохранить все хранилище данных на своем диске. – Broseph

ответ

2

Предполагая, что вы просто пытаетесь устранить издержки использования текстовой базы d формат вместо двоичного формата для ваших временных данных, и вы не хотите, чтобы переписать все, чтобы использовать 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 

Если какие-либо из них должно быть больше, чем байты, вам придется иметь дело с байтами, но это довольно тривиально. Например, если x и y диапазоне от -32768 до 32767:

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

И убедитесь, чтобы открыть файл в двоичном режиме.

И вы, конечно, можете совместить это с mmap, если вы хотите, что означает, что вы можете просто использовать struct.pack_into и struct.unpack_from вместо явного использования pack плюс write и unpackread плюс.


Дальше, есть pickle. Либо создайте свой список, либо просто введите pickle.dump, либо вручную введите pickle.dumps каждой записи и добавьте над ним некоторую простую структуру более высокого уровня (или просто используйте shelve, если эта структура более высокого уровня является или может быть простым сопоставлением от ключей к записям). Это может быть больше, а не меньше, и может быть медленнее, поэтому вы всегда хотите провести некоторое тестирование, прежде чем рассматривать это. Но иногда это простое решение.


Наконец, вы, вероятно, может придумать более компактный формат текста, чем просто печать str представление каждого объекта. Обычно это не стоит усилий, но опять же, об этом стоит подумать.

+0

Я закончил использовать цветовую палитру с моим рисунком, поэтому я могу просто использовать значение i после проверки итерации в качестве пиксельных данных. Затем я использовал struct для записи i во временный файл. Это сделало временные файлы намного меньшими. Но это было слишком велико, когда я решил сделать массивные снимки (сотни гигапикселей), поэтому я написал некоторые материалы для кодирования хаффмана, чтобы сделать его меньше, и позволить мне читать/записывать данные по одной части за раз. Я еще не смотрел на модуль рассола, поэтому я проверю его. Я также переключился на использование pypng. – Broseph

+0

Я предполагаю, что вы пытались zlib и bz2, прежде чем вручную внедрить кодирование Хаффмана, не так ли? (Это намного проще, поскольку он встроен и намного лучше сжимается для большинства данных. Конечно, если вы сжимаете тонны маленьких блоков отдельно, они могут быть намного хуже. И, очевидно, они намного медленнее Но все же стоит попробовать.) – abarnert

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: 
     ... 

вы могли бы даже объединить эти два ...

Смежные вопросы