Примечание: я использую Python 2.x, переносить его на 3.x не должно быть сложно.
Моя идея проста - дискового пространства достаточно, поэтому давайте сделаем некоторую предварительную обработку и превратим этот большой файл pickle во что-то, что легче обрабатывать небольшими порциями.
Подготовка
Чтобы проверить это, я написал небольшой скрипт, который генерирует файл рассола, похожий на ваш. Я предположил, что ваши входные изображения имеют оттенки серого и имеют глубину 8 бит, и сгенерировали 10000 случайных изображений, используя numpy.random.randint
.
Этот скрипт будет служить эталоном, с которым мы можем сравнивать этапы предварительной обработки и обработки.
import numpy as np
import pickle
import time
IMAGE_WIDTH = 600
IMAGE_HEIGHT = 600
FILE_COUNT = 10000
t1 = time.time()
with open('data/raw_data.pickle', 'wb') as f:
for i in range(FILE_COUNT):
data = np.random.randint(256, size=IMAGE_WIDTH*IMAGE_HEIGHT, dtype=np.uint8)
data = data.reshape(IMAGE_HEIGHT, IMAGE_WIDTH)
pickle.dump(data, f)
print i,
t2 = time.time()
print '\nDone in %0.3f seconds' % (t2 - t1)
В тестовом запуске этот скрипт завершился за 372 секунды, создав файл ~ 10 ГБ.
Препроцессирование
Давайте разделим входные изображения построчно - у нас будет 600 файлов, где файл N
содержит строку N
из каждого входного изображения. Мы можем хранить данные строки в двоичном формате, используя numpy.ndarray.tofile
(и позже загружать эти файлы, используя numpy.fromfile
).
import numpy as np
import pickle
import time
# Increase open file limit
# See /7318117/pochemu-python-imeet-ogranichenie-na-kolichestvo-deskriptorov-failov
import win32file
win32file._setmaxstdio(1024)
IMAGE_WIDTH = 600
IMAGE_HEIGHT = 600
FILE_COUNT = 10000
t1 = time.time()
outfiles = []
for i in range(IMAGE_HEIGHT):
outfilename = 'data/row_%03d.dat' % i
outfiles.append(open(outfilename, 'wb'))
with open('data/raw_data.pickle', 'rb') as f:
for i in range(FILE_COUNT):
data = pickle.load(f)
for j in range(IMAGE_HEIGHT):
data[j].tofile(outfiles[j])
print i,
for i in range(IMAGE_HEIGHT):
outfiles[i].close()
t2 = time.time()
print '\nDone in %0.3f seconds' % (t2 - t1)
В тестовом прогоне этот скрипт завершился за 134 секунды, создав 600 файлов по 6 миллионов байт каждый. Он использовал ~ 30 МБ или RAM.
Обработка
Просто, просто загрузите каждый массив, используя numpy.fromfile
, затем используйте numpy.median
, чтобы получить медианы для каждого столбца, сократив его обратно до одной строки и накапливая такие строки в списке.
Наконец, используйте numpy.vstack
, чтобы собрать медианное изображение.
import numpy as np
import time
IMAGE_WIDTH = 600
IMAGE_HEIGHT = 600
t1 = time.time()
result_rows = []
for i in range(IMAGE_HEIGHT):
outfilename = 'data/row_%03d.dat' % i
data = np.fromfile(outfilename, dtype=np.uint8).reshape(-1, IMAGE_WIDTH)
median_row = np.median(data, axis=0)
result_rows.append(median_row)
print i,
result = np.vstack(result_rows)
print result
t2 = time.time()
print '\nDone in %0.3f seconds' % (t2 - t1)
В тестовом прогоне этот скрипт завершился за 74 секунды. Вы могли бы даже распараллелить это довольно легко, но, похоже, оно того не стоит. Скрипт использовал ~ 40 МБ ОЗУ.
Учитывая, что оба этих сценария линейны, используемое время также должно масштабироваться линейно. Для 50000 изображений это около 11 минут для предварительной обработки и 6 минут для окончательной обработки. Это на i7-4930K @ 3,4 ГГц, специально использует 32-битный Python.