Самый быстрый 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);
}
}
}