Эффективный комплексный анализ замаскированного движущегося окна - PullRequest
0 голосов
/ 03 октября 2018

Я разрабатываю алгоритм движущегося окна в Python, который будет использоваться для скольжения по большим массивам массивов (например, размеры моего тестового массива 6349x9849).Мне нужно вычислить количество различных статистических данных в движущемся окне 25 x 25, которое маскируется окном 9x9, в каждом месте расположения пикселя.

Также есть одна оговорка, которая остановила меня при использовании convolve (который супернапример, для быстрого вычисления среднего значения в движущемся окне): если центральный пиксель в движущемся окне равен 0, я устанавливаю значение статистики -9999 в качестве флага или если движущееся окно 25x25 содержит более половины значений 0, яустановите значение статистики -1 как флаг.Затем я смогу разобраться с флагами позже.

Я написал код Python, чтобы сделать это, и это работает, но впервые в моем изучении Python я работал с особенно большими объемами данных и, таким образом, столкнулся с эффективностью.проблемы - это занимает очень много времени с написанным мною кодом (около 6 часов на статистику для этого размера изображения ...!).

Я хотел бы попросить любые предложения, как яможет сделать это намного эффективнее. Сначала я хочу оптимизировать код, а затем просто использовать больше вычислительной мощности (я полагаю, что я мог бы использовать многопроцессорный модуль, например, для этого).

Мой код,для одного примера статистика (стандартное отклонение) ниже.Я повторяю эту строку для каждой статистики (у меня есть 6 статистик для вычисления):

# Calculate the standard deviation of the masked moving window
stats_std = [-9999 if ds_array[row,column] == 0
else -1 if np.count_nonzero(ds_array[row-border_buff:row+border_buff+1,column-border_buff:column+border_buff+1]) < (outer_box**2)/2
else np.std([i for i in np.ma.compressed(np.ma.masked_array(ds_array[row-border_buff:row+border_buff+1,column-border_buff:column+border_buff+1],mask)) if i!=0])
for row in range(border_buff,m-border_buff)
for column in range(border_buff,n-border_buff)]

# Reshape the list into the image dimensions
stats_std = np.reshape(stats_std,(m-2*border_buff,n-2*border_buff))

Я могу предоставить массив подмножеств выборки и желаемый результат для пробного использования при необходимости, но не уверен, что лучший способ обеспечить это, однакоПожалуйста, дайте мне знать, если это необходимо, чтобы сделать вышеуказанное более понятным.

Ps Я попробовал один из способов преобразования 2d-массива в 3d из всех 25x25 подмножеств массива, а затем эффективно вычислить 6 статистик водин шаг на подмножество в надежде на значительную экономию вычислений ... но это привело к MemoryError.

1 Ответ

0 голосов
/ 03 октября 2018

pyvips позволяет эффективно рассчитывать сложные вещи на больших изображениях и использовать очень мало памяти .Это LGPL, работает на Linux, MacOS и Windows, и работает на всех версиях Python.У большинства linux это есть в менеджере пакетов.

Когда вы выполняете операцию, подобную a + b в pyvips, она фактически не выполняет никакой обработки, она просто добавляет еще один узел в график операций обработки изображения.Когда вы, наконец, записываете результат где-то, весь график оценивается, и он передает изображение через вашу систему в виде набора небольших частей и параллельно.

Поскольку промежуточные изображения на самом деле не существуют, вам нужен только небольшой объем памяти, и поскольку он параллелен, он быстрый.

Например, вы можете выполнить расчет sdev следующим образом:

import sys
import pyvips

# load the input image ... the access hint means we will only make a single
# top-to-bottom pass over image, and it can therefore be streamed 
image = pyvips.Image.new_from_file(sys.argv[1], access='sequential')

# our convolution ... total pixels in an M x M window
# it's a simple box filter, so we can use a seperable convolution 
# (two 1D filters at 90 degrees)
window_size = 25
size = window_size * window_size
sum_mask = pyvips.Image.new_from_array([1] * window_size)

# standard deviation ... sum and sum of squares
s = image.convsep(sum_mask)
s2 = (image * image).convsep(sum_mask)
sdev = ((s2 - (s * s / size)).abs() / (size - 1)) ** 0.5

# find all zero input pixels ... these become -9999 in the output
sdev = (image == 0).ifthenelse(-9999, sdev)

# find all pixels where more than half of the window is zero ... these become
# -1 in the output
# pyvips uses 255 for TRUE and 0 for FALSE
more_than_half_zero = (image == 0).convsep(sum_mask) > 255 * size / 2
sdev = more_than_half_zero.ifthenelse(-1, sdev)

sdev.write_to_file(sys.argv[2])

Я могу запустить его так:

$ vipsheader x.jpg 
x.jpg: 10000x10000 uchar, 1 band, b-w, jpegload
$ /usr/bin/time -f %M:%e python3 sdev.py x.jpg x.pfm
81432:4.11

Чтобы сделать PFM размером 10 000 x 10 000 пикселей (простой формат, который может хранить значения с плавающей точкой) из 10 000 x 10 000 пикселейИзображение jpg, где каждый пиксель в PFM - это sdev соответствующего окна 25 x 25, плюс ваши правила для нулей.На этом рабочем столе требуется 4 секунды и максимум 81 МБ памяти.

Я могу немного уменьшить использование памяти, используя только один поток, но, конечно, он намного медленнее:

$ VIPS_CONCURRENCY=1 /usr/bin/time -f %M:%e python3 sdev.py x.jpg x.pfm
54128:16.92

Сейчас только 54 МБ памяти, но 17 секунд времени.

Вы также можете читать и записывать изображения в массивы и из них, см. Главу в документации .

...