Самый быстрый 2D сверток или фильтр изображений в Python

Несколько пользователей задали вопрос о скорости или потреблении памяти для сверток изображений в numpy или scipy [1, 2, 3, 4]. Из ответов и моего опыта использования Numpy я считаю, что это может быть основным недостатком numpy по сравнению с Matlab или IDL.

До сих пор ни один из ответов не затронул общий вопрос, так вот вот: "Какой самый быстрый метод вычисления двумерной свертки в Python?" Общие модули python - это честная игра: numpy, scipy и PIL (другие?). Для сложного сравнения я хотел бы предложить следующие правила:

  • Входные матрицы - 2048x2048 и 32x32 соответственно.
  • Плавающая точка с одинарной или двойной точностью приемлема.
  • Время, затрачиваемое на преобразование матрицы ввода в соответствующий формат, не учитывается - только шаг свертки.
  • Замена входной матрицы на ваш выход приемлема (поддерживает ли какая-либо библиотека python?)
  • Прямые вызовы DLL для общих библиотек C в порядке - lapack или scalapack
  • PyCUDA сразу. Нечестно использовать ваше собственное оборудование графического процессора.

Ответы

Ответ 1

Это действительно зависит от того, что вы хотите сделать... Много времени вам не нужна полностью родовая (читая: медленная) 2D-свертка... (т.е. если фильтр является разделимым, вы используете два 1D свертки... Вот почему различные scipy.ndimage.gaussian, scipy.ndimage.uniform, намного быстрее, чем то же самое, что и общие nD-свертки.)

Во всяком случае, как точка сравнения:

t = timeit.timeit(stmt='ndimage.convolve(x, y, output=x)', number=1,
setup="""
import numpy as np
from scipy import ndimage
x = np.random.random((2048, 2048)).astype(np.float32)
y = np.random.random((32, 32)).astype(np.float32)
""")
print t

Это займет 6,9 секунды на моей машине...

Сравните это с fftconvolve

t = timeit.timeit(stmt="signal.fftconvolve(x, y, mode='same')", number=1,
setup="""
import numpy as np
from scipy import signal
x = np.random.random((2048, 2048)).astype(np.float32)
y = np.random.random((32, 32)).astype(np.float32)
""")
print t

Это займет около 10,8 секунд. Тем не менее, с разными размерами ввода, использование fft для свертки может быть значительно быстрее (хотя, похоже, я не могу придумать хороший пример, на данный момент...).

Ответ 2

На моей машине голосовой круговой сверток с использованием БПФ, по-видимому, постится:

import numpy
x = numpy.random.random((2048, 2048)).astype(numpy.float32)
y = numpy.random.random((32, 32)).astype(numpy.float32)
z = numpy.fft.irfft2(numpy.fft.rfft2(x) * numpy.fft.rfft2(y, x.shape))

Обратите внимание, что это может обрабатывать области, близкие к краям, по-другому, чем другие способы, потому что это круговая свертка.

Ответ 3

Я тоже экспериментировал с этим. Я предполагаю, что свертка SciPy не использует библиотеку BLAS для ускорения вычисления. Используя BLAS, я смог закодировать двумерную свертку, которая была сопоставима по скорости с MATLAB. Это больше работает, но лучше всего переделать свертку в С++.

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

for(int n = 0; n < filt.numCols; n++)
  {
    for(int m = 0; m < filt.numRows; m++)
    {
      const double filt_val = filt(filt.numRows-1-m,filt.numCols-1-n);
      for (int i =0; i < diffN; i++)
      {
        double *out_ptr = &outImage(0,i);
        const double *im_ptr = &image(m,i+n);
        cblas_daxpy(diffM,filt_val,im_ptr, 1, out_ptr,1);

      }
   }
 }