numpy скользящие расчеты 2d окна

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

У меня есть большая 2d-матрица, и я хотел бы выполнить вычисления для соседей каждого элемента в матрице. Например, я могу захотеть найти максимальное значение, исключая какое-то специальное значение негиборов по индексам (x-1,y)(x+1,y+1) по каждому индексу, и поместить результат в другой другой 2d " Матрица решения.

Обратите внимание, что convolution2d, хотя и полезен, не будет работать для меня в этой ситуации, потому что у меня есть определенные операции, которые нужно выполнять для каждого пикселя, и я хочу делать это только для определенных соседей (каждого пикселя).

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

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

Вот пример:

Window:

0 0 1
1 0 0
0 0 0


Input:

0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 9 9 0 0 9 0 0
0 0 0 0 0 0 0 0 0

Output:

0 0 0 0 0 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 1 0 0 2 0 0
0 0 0 0 0 0 0 0 0

person Andrew Hundt    schedule 18.09.2015    source источник


Ответы (4)


Используйте np.roll() для создания вторичных матриц. Затем выполните любые операции, которые вам нужны между исходной и вторичной матрицами. Например, чтобы взять среднее значение центральной ячейки и двух соседей:

sec_a = np.roll(mtrx, -1, axis=0)
sec_b = np.roll(mtrx, -1, axis=1)

result = (mtrx + sec_a + sec_b) / 3

Кроме того, roll() перемещается по краям, поэтому не нужно беспокоиться о границах.

person Chad Kennedy    schedule 18.09.2015

Однажды я создал эту функцию для хранения скользящих блоков из 2D-массива в столбцы, чтобы любая операция, которую мы когда-то думали применить в скользящем окне к 2D-массиву, могла быть легко применена к столбцам. Подробнее об этом читайте в this solution до Implement Matlab's im2col 'sliding' in python.

Теперь NumPy поддерживает применение большинства своих функций вдоль указанной оси. Таким образом, с помощью этого инструмента мы могли бы эффективно применять почти любую операцию в скользящем окне vectorized способом. Вот формальное определение этого -

def im2col(A,BLKSZ):   

    # Parameters
    M,N = A.shape
    col_extent = N - BLKSZ[1] + 1
    row_extent = M - BLKSZ[0] + 1

    # Get Starting block indices
    start_idx = np.arange(BLKSZ[0])[:,None]*N + np.arange(BLKSZ[1])

    # Get offsetted indices across the height and width of input array
    offset_idx = np.arange(row_extent)[:,None]*N + np.arange(col_extent)

    # Get all actual indices & index into input array for final output
    return np.take (A,start_idx.ravel()[:,None] + offset_idx.ravel())

Вот как мы можем использовать этот инструмент для решения поставленной задачи, предполагая, что A является входным массивом 2D:

# Get 3x3 sliding blocks from A and set them as columns.
Acol = im2col(A,[3,3])

# Setup kernel mask
kernel = np.ones((3,3),dtype=bool)
kernel[2,1:] = 0

# Mask rows of Acol with kernel and perform any operation, let's say MAX
out = Acol[kernel.ravel()].max(0).reshape(A.shape[0]-2,A.shape[1]-2)

Пробный запуск -

In [365]: A
Out[365]: 
array([[83, 84, 46,  9, 25],
       [32,  8, 31, 45, 58],
       [14,  8,  0, 51, 27],
       [31, 40,  7, 27, 71]])

In [366]: kernel = np.ones((3,3),dtype=bool)
     ...: kernel[2,1:] = 0
     ...: 

In [367]: im2col(A,[3,3])[kernel.ravel()].max(0).reshape(A.shape[0]-2,A.shape[1]-2)
Out[367]: 
array([[84, 84, 58],
       [32, 51, 58]])
person Divakar    schedule 18.09.2015
comment
Возможно, есть способ передать лямбда-класс, в котором выполняется вызов .max(0)? Я забыл включить компонент состояния в свой вопрос (который я добавил). - person Andrew Hundt; 19.09.2015
comment
@AndrewHundt Извините, я не совсем знаком с лямбдами :) - person Divakar; 19.09.2015
comment
ну, любой механизм, который обрабатывает компонент состояния, который я добавил выше, не относящийся к лямбда-выражениям. :-) Например, любой класс, который нужно передать и применить к каждому окну, или какой-то другой метод, о котором я не знаю. - person Andrew Hundt; 19.09.2015

Предполагая, что ваша исходная 2D-матрица называется A и имеет размер (n, m)

# extraction of 3x3 sub-matrices and storage in a new 2D matrix
B = [ [ A[i-1:i+2, j-1:j+2] for i in range(1, n-1) ] for j in range(1, m-1) ]
# conversion to a mask array
B = np.ma.array( B, mask=False )
# masking the unwanted elements of each sub-matrix
B.mask[:, :, 1, 2] = True
B.mask[:, :, 2, 2] = True

NB: диапазоны i и j при создании подматриц были выбраны, чтобы избежать границ.

Операции над подматрицей B[i, j] игнорируют замаскированные элементы.

Теперь, чтобы выполнить операцию numpy (например, максимальное значение подматрицы) для каждой подматрицы и сохранить результат в 2D-матрице:

C = [ [ np.max(B[i,j]) for i in range(n-2) ] for j in range(m-2) ]
person Bertrand Gazanion    schedule 18.09.2015
comment
Круто, это интересно и охватывает первые пару шагов, как вы выполняете максимум, исключая определенные значения, и загружаете его в матрицу результатов? - person Andrew Hundt; 19.09.2015
comment
Я предлагаю использовать понимание списка, так же, как была создана матрица «B». Я обновил свой ответ. - person Bertrand Gazanion; 19.09.2015

Я использовал следующее как удобочитаемое решение:

import numpy as np

def get_windows(arr, window_size=64, step=32):
  windows = []
  row = 0
  col = 0
  max_row, max_col = arr.shape
  while row < max_row:
    while col < max_col:
      windows.append(arr[row:row+window_size, col:col+window_size])
      col += step
    row += step
    col = 0
  return windows

a = np.random.rand(4, 4)
windows = get_windows(a, window_size=2, step=1)
person duhaime    schedule 31.03.2018