Использование Numpy для более эффективной генерации фильтров - PullRequest
1 голос
/ 21 апреля 2019

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

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

def gaussian_filter(mode, size, cutoff):
    filterImage = np.zeros(size, np.float64)    
    cutoffTerm = 2 * (cutoff ** 2)
    v = np.asarray([size[0] // 2, size[1] // 2])

    for px in range(0, size[0]):
        for py in range(0, size[1]):
            u = np.asarray([px, py])
            Duv = np.linalg.norm(u - v)
            distance = -1 * (Duv ** 2)
            result = pow(np.e, distance / cutoffTerm)
            if mode == 'low':
                filterImage.itemset((px, py), result)
            elif mode == 'high':
                filterImage.itemset((px, py), 1 - result)

    return filterImage

Генерация фильтра размером 1920 x 1080 занимает 70,36 секунды, что совершенно недопустимо. Любые идеи будут высоко оценены.

1 Ответ

2 голосов
/ 21 апреля 2019

Вот векторизация с использованием broadcasting -

def gaussian_filter_vectorized(mode, size, cutoff):
    cutoffTerm = 2 * (cutoff ** 2)
    v = np.asarray([size[0] // 2, size[1] // 2])

    I,J = np.ogrid[:size[0],:size[1]]
    p,q = I-v[0],J-v[1]
    Dsq = p**2 + q**2
    d = -1 * Dsq
    R = np.power(np.e,d/cutoffTerm)
    if mode == 'low':
        return R
    elif mode == 'high':
        return 1-R

Сроки на больших size х

In [80]: N = 100
    ...: %timeit gaussian_filter(mode='low', size=(N,N), cutoff=N)
    ...: %timeit gaussian_filter_vectorized(mode='low', size=(N,N), cutoff=N)
10 loops, best of 3: 65.2 ms per loop
1000 loops, best of 3: 225 µs per loop

In [81]: N = 1000
    ...: %timeit gaussian_filter(mode='low', size=(N,N), cutoff=N)
    ...: %timeit gaussian_filter_vectorized(mode='low', size=(N,N), cutoff=N)
1 loop, best of 3: 6.5 s per loop
10 loops, best of 3: 29.8 ms per loop

200x+ ускорения!

Использование numexpr для больших вычислений данных для дальнейшей работы. увеличить

При работе с большими данными мы также можем использовать numexpr модуль , который поддерживает многоядерную обработку, если предполагаемые операции могут быть выражены как арифметические. Чтобы решить наш случай, мы можем заменить шаги в Dsq = p**2 + q**2 и R = np.power(np.e,d/cutoffTerm) эквивалентными numexpr, используя функцию numexpr.evaluate.

Итак, мы бы получили что-то вроде этого -

import numexpr as ne

def gaussian_filter_vectorized_numexpr(mode, size, cutoff):
    cutoffTerm = 2 * (cutoff ** 2)

    I,J = np.ogrid[:size[0],:size[1]]
    v0,v1 = size[0] // 2, size[1] // 2
    p,q = I-v0,J-v1    
    E = np.e
    if mode == 'low':
        return ne.evaluate('E**(-1*(p**2+q**2)/cutoffTerm)')
    elif mode == 'high':
        return ne.evaluate('1-E**(-1*(p**2+q**2)/cutoffTerm)')

Время на 1920x1080 изображении размера -

In [2]: M,N=1920,1080
   ...: %timeit gaussian_filter(mode='low', size=(M,N), cutoff=N)
   ...: %timeit gaussian_filter_vectorized(mode='low', size=(M,N), cutoff=N)
   ...: %timeit gaussian_filter_vectorized_numexpr(mode='low',size=(M,N),cutoff=N)
1 loop, best of 3: 13.9 s per loop
10 loops, best of 3: 63.3 ms per loop
100 loops, best of 3: 9.48 ms per loop

Близко к 1500x ускорение здесь!

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...