Scipy ndimage.convolve пропускает суммирование каналов - PullRequest
1 голос
/ 17 января 2020

Я пытаюсь использовать функцию scipy *1001* для выполнения свертки на трехмерном изображении (RGB, ширина, высота).

Взгляните сюда:

http://cs231n.github.io/convolutional-networks/

Очевидно, что для любого ввода каждое ядро ​​/ фильтр должно иметь только выход NxN со строгой глубиной 1.

Это проблема с scipy, как если бы вы делали ndimage.convolve с входом размером (3, 5, 5) и фильтр / ядро ​​размером (3, 3, 3), результат этой операции дает выходной размер (3, 5, 5), явно не суммируя различные каналы.

Есть ли способ принудительно выполнить это суммирование без ручного выполнения? Я стараюсь делать как можно меньше в базе python, так как многие внешние библиотеки написаны на c ++ и выполняют те же операции быстрее. Или есть альтернатива?

1 Ответ

1 голос
/ 17 января 2020

Ни один скупи не пропускает суммирование каналов. Причина, по которой вы получаете вывод (3, 5, 5), заключается в том, что ndimage.convolve дополняет входной массив по всем осям, а затем выполняет свертку в «одинаковом» режиме (т. Е. Выход имеет ту же форму, что и вход, с центром по отношению к вывод «полного» режима корреляции). См. scipy.signal.convolve для более подробной информации о режимах.

Для ввода формы (3 ,5, 5) и фильтра w0 формы (3, 3, 3), ввод дополнен, что приводит к массив (7, 9, 9). См. Ниже (для простоты я использую постоянное заполнение нулями):

a = np.array([[[2, 0, 2, 2, 2],
               [1, 1, 0, 2, 0],
               [0, 0, 1, 2, 2],
               [2, 2, 2, 0, 0],
               [1, 0, 1, 2, 0]],

              [[1, 2, 1, 0, 1],
               [0, 2, 0, 0, 1],
               [0, 0, 2, 2, 1],
               [2, 0, 1, 0, 2],
               [0, 1, 2, 2, 2]],

              [[0, 0, 2, 2, 2],
               [0, 1, 2, 1, 0],
               [0, 0, 0, 2, 0],
               [0, 2, 0, 0, 2],
               [0, 0, 2, 2, 1]]])

w0 = np.array([[[0,  1, -1],
                [1, -1,  0],
                [0,  0,  0]],

               [[1,  0,  0],
                [0, -1,  1],
                [1,  0,  1]],

               [[ 1, -1,  0],
                [-1,  0, -1],
                [-1,  0,  1]]])

k = w0.shape[0]

a_p = np.pad(a, k-1)

array([[[0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0]],

       [[0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0]],

       [[0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 2, 0, 2, 2, 2, 0, 0],
        [0, 0, 1, 1, 0, 2, 0, 0, 0],
        [0, 0, 0, 0, 1, 2, 2, 0, 0],
        [0, 0, 2, 2, 2, 0, 0, 0, 0],
        [0, 0, 1, 0, 1, 2, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0]],

       [[0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 1, 2, 1, 0, 1, 0, 0],
        [0, 0, 0, 2, 0, 0, 1, 0, 0],
        [0, 0, 0, 0, 2, 2, 1, 0, 0],
        [0, 0, 2, 0, 1, 0, 2, 0, 0],
        [0, 0, 0, 1, 2, 2, 2, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0]],

       [[0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 2, 2, 2, 0, 0],
        [0, 0, 0, 1, 2, 1, 0, 0, 0],
        [0, 0, 0, 0, 0, 2, 0, 0, 0],
        [0, 0, 0, 2, 0, 0, 2, 0, 0],
        [0, 0, 0, 0, 2, 2, 1, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0]],

       [[0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0]],

       [[0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0]]])

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

Затем свертка выполняется путем скольжения вдоль первого измерения (ось-0), т. е. (перевернутый) w0 сворачивается с a_p[0:3], затем с a_p[1:4], затем с a_p[2:5], затем с a_p[3:6] и, наконец, с a_p[4:7], каждый из которых приводит к массиву (1, 7, 7) из-за суммирования по каналам. Затем они складываются вместе, в результате чего получается массив (5, 7, 7). Чтобы показать это, я использую scipy.signal.convolve, который позволяет использовать режим full:

out = scipy.signal.convolve(a, np.flip(w0), mode='full')

array([[[ 2,  0,  0,  2,  0, -2, -2],
        [-1,  1, -5, -1, -4, -4, -2],
        [-1, -3,  2, -3,  1, -4,  0],
        [ 2,  1, -1, -3, -7,  0, -2],
        [-1, -2, -4, -1, -4, -2,  2],
        [-1, -2, -2, -2,  1, -2,  0],
        [ 0, -1,  1, -1, -1,  2,  0]],

       [[ 3,  2,  4,  0,  4,  2,  1],
        [ 2, -1,  1, -1, -1,  0, -2],
        [ 1, -3,  3,  5,  2,  1,  3],
        [ 4,  2,  1,  4,  0, -3, -2],
        [ 1,  1,  1, -1, -1,  3, -1],
        [ 1, -4,  3, -1, -3, -4,  0],
        [ 0,  0,  0, -1,  1,  2,  2]],

       [[ 1,  2,  4,  4,  2, -2, -1],
        [ 1,  2,  1, -3, -4, -4,  1],
        [-2,  2, -3,  3,  1,  2,  4],
        [ 1,  2,  5, -6,  6, -2,  3],
        [ 2, -5,  4,  1,  5,  4,  0],
        [-2,  0,  0,  1, -3, -4,  3],
        [-1,  1, -1, -2,  4,  3,  3]],

       [[ 0,  0,  2,  2,  4,  2,  2],
        [ 0,  0,  3,  3,  3, -2,  1],
        [-1,  0,  0,  4,  0,  4,  3],
        [ 0,  0,  2,  3,  1,  3,  3],
        [ 0,  0,  0,  1,  7,  1,  3],
        [-2,  2,  0,  2, -3,  1,  4],
        [ 0, -1, -1,  0,  2,  4,  1]],

       [[ 0,  0,  0,  0,  0,  0,  0],
        [ 0,  0,  0, -2,  0,  0,  2],
        [ 0,  0, -3, -1,  1,  3,  0],
        [ 0, -1, -1,  1, -1,  2,  0],
        [ 0,  0, -2,  0,  2, -2,  2],
        [ 0, -2,  2, -2, -2,  3,  1],
        [ 0,  0, -2,  0,  1,  1,  0]]])

Чтобы перейти в "тот же" режим ndimage.convolve, нам нужно отцентрировать out:

out = out[1:-1, 1:-1, 1:-1]

array([[[-1,  1, -1, -1,  0],
        [-3,  3,  5,  2,  1],
        [ 2,  1,  4,  0, -3],
        [ 1,  1, -1, -1,  3],
        [-4,  3, -1, -3, -4]],

       [[ 2,  1, -3, -4, -4],
        [ 2, -3,  3,  1,  2],
        [ 2,  5, -6,  6, -2],
        [-5,  4,  1,  5,  4],
        [ 0,  0,  1, -3, -4]],

       [[ 0,  3,  3,  3, -2],
        [ 0,  0,  4,  0,  4],
        [ 0,  2,  3,  1,  3],
        [ 0,  0,  1,  7,  1],
        [ 2,  0,  2, -3,  1]]])

Это именно то, что вы получите, если вы запустите scipy.ndimage.convolve(a, np.flip(w0), mode='constant', cval=0). Наконец, чтобы получить желаемый результат, нам нужно игнорировать элементы, которые полагались на заполнение по первому измерению (т.е. оставить только среднюю часть выхода), также использовать шаги s=2 (то есть out[1][::s, ::s]) и, наконец, добавить смещение b = 1:

out[1][::s, ::s] + b

array([[ 3, -2, -3],
       [ 3, -5, -1],
       [ 1,  2, -3]])

Все в одной строке:

scipy.ndimage.convolve(a, np.flip(w0), mode='constant', cval=0)[1][::2, ::2] + b

# or using scipy.signal.convolve
# scipy.signal.convolve(a, np.flip(w0), 'full')[2][1:-1,1:-1][::2, ::2] + b
# or
# scipy.signal.convolve(a, np.flip(w0), 'same')[1][::2, ::2] + b
...