Высокопроизводительная переменная размытия в очень больших изображениях с использованием Python

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

Из-за большого количества изображений я должен учитывать производительность. Я посмотрел на NumPY / SciPY, у них есть отличные функции, но они, похоже, используют фиксированный размер ядра, и мне нужно уменьшить или увеличить размер ядра в зависимости от расстояния до предыдущих упомянутых областей.

Как я могу решить эту проблему в python?


UPDATE: Мое решение до сих пор основано на ответе rth :

# cython: boundscheck=False # cython: cdivision=True # cython: wraparound=False import numpy as np cimport numpy as np def variable_average(int [:, ::1] data, int[:,::1] kernel_size): cdef int width, height, i, j, ii, jj width = data.shape[1] height = data.shape[0] cdef double [:, ::1] data_blurred = np.empty([width, height]) cdef double res cdef int sigma, weight for i in range(width): for j in range(height): weight = 0 res = 0 sigma = kernel_size[i, j] for ii in range(i - sigma, i + sigma + 1): for jj in range(j - sigma, j + sigma + 1): if ii < 0 or ii >= width or jj < 0 or jj >= height: continue res += data[ii, jj] weight += 1 data_blurred[i, j] = res/weight return data_blurred 

Контрольная работа:

 data = np.random.randint(256, size=(1024,1024)) kernel = np.random.randint(256, size=(1024,1024)) + 1 result = np.asarray(variable_average(data, kernel)) 

Метод, использующий вышеуказанные настройки, занимает около 186 секунд для запуска. Это то, что я могу ожидать, в конечном счете, выжать из метода или есть оптимизации, которые я могу использовать для дальнейшего повышения производительности (все еще используя Python)?

Как вы отметили, связанные функции scipy не поддерживают размытие scipy размеров. Вы можете реализовать это в чистом питоне с помощью циклов, а затем использовать Cython, Numba или PyPy, чтобы получить C-подобную производительность.

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

 import numpy as np def variable_blur(data, kernel_size): """ Blur with a variable window size Parameters: - data: 2D ndarray of floats or integers - kernel_size: 2D ndarray of integers, same shape as data Returns: 2D ndarray """ data_blurred = np.empty(data.shape) Ni, Nj = data.shape for i in range(Ni): for j in range(Nj): res = 0.0 weight = 0 sigma = kernel_size[i, j] for ii in range(i - sigma, i+sigma+1): for jj in range(j - sigma, j+sigma+1): if ii<0 or ii>=Ni or jj < 0 or jj >= Nj: continue res += data[ii, jj] weight += 1 data_blurred[i, j] = res/weight return data_blurred data = np.random.rand(50, 20) kernel_size = 3*np.ones((50, 20), dtype=np.int) variable_blur(data, kernel_size) 

который вычисляет среднее арифметическое пикселей с переменным размером ядра. Это плохая реализация в отношении numpy, в некотором смысле, которая не является векторизованной. Тем не менее, это делает возможным перенос на другие высокопроизводительные решения:

  • Cython : просто статически типизирующие переменные, и компиляция должна давать вам C-подобную производительность,

     def variable_blur(double [:, ::1] data, long [:, ::1] kernel_size): cdef double [:, ::1] data_blurred = np.empty(data.shape) cdef Py_ssize_t Ni, Nj Ni = data.shape[0] Nj = data.shape[1] for i in range(Ni): # [...] etc. 

    см. этот пост для полного примера, а также примечания к компиляции .

  • Numba : Обертывание указанной функции с @jit декоратора @jit должно быть в основном достаточным.

  • PyPy : установка PyPy + экспериментальной ветки numpy может стать еще одной альтернативой, которую стоит попробовать. Хотя, тогда вам придется использовать PyPy для всего вашего кода, что может оказаться невозможным в настоящее время.

После быстрой реализации вы можете использовать multiprocessing и т. Д., Чтобы обрабатывать различные изображения параллельно, если это необходимо. Или даже распараллеливать с OpenMP в Cython внешний цикл.