NumPy滑动2D窗口计算

4
我正在尝试学习使用numpy在各种情况下高效地解决涉及到滑动窗口的问题。以下是一个示例,展示了我感兴趣的问题类型:
我有一个大的二维矩阵,我想要对矩阵中每个元素周围的邻居进行计算。例如,我可能想要查找最大值,但要在每个索引处排除一些特殊邻居值(如(x-1,y)和(x+1,y+1)),然后将结果放入另一个不同的二维“解决方案”矩阵中。
需要注意的是,虽然convolution2d很有用,但在这种情况下不适用,因为我需要对每个像素进行特定操作,并且只想针对特定邻居(每个像素的邻居)执行此操作。
另外,最好确保不会超出边界。
最后,是否可能也使用任何状态?在所有邻居都是0的情况下,我希望分配一个新的整数ID,并在每次出现这种情况时递增它。
以下是一个示例:
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

你能否为编辑部分添加样例输入和输出数据 - “所有邻居都是0...”? - Divakar
对于编辑中描述的行为,也许scipy.ndimage.measurements.label能够满足你的需求?虽然我不清楚这与滑动窗口有什么关系。 - user2379410
@moarningsun 这是一个合成的例子,旨在了解如何使用numpy有效地进行滑动窗口操作,而不是解决我所描述的实际问题。 - Andrew Hundt
4个回答

3
使用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() 方法会在边缘滚动,因此不必担心边界问题。


1
我曾经创建了这个函数,将二维数组中的滑动块存储到列中,以便可以在列上轻松应用我们曾经想过应用于二维数组滑动窗口的任何操作。在this solutionImplement Matlab's im2col 'sliding' in python中了解更多信息。
现在,NumPy支持大多数函数沿指定轴应用。因此,使用这个工具,我们有效地能够以向量化的方式应用几乎任何滑动窗口操作。以下是它的正式定义 -
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是二维输入数组 -

# 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]])

也许有一种方法可以在进行 .max(0) 调用时传递一个 lambda 类?我忘记包含我的问题的状态组件(我已经添加了)。 - Andrew Hundt
@AndrewHundt 抱歉,我不太熟悉lambda表达式 :) - Divakar
嗯,任何处理我上面添加的状态组件的机制都可以,不仅限于lambda。 :-) 例如,可以传入任何类型的类并应用于每个窗口,或者其他我不知道的技术。 - Andrew Hundt

1

假设你的原始二维矩阵名称为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

注意:在创建子矩阵时,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) ]

很酷,这很有趣并涵盖了前几个步骤,那么如何执行排除特定值的最大值并将其加载到结果矩阵中? - Andrew Hundt
我建议使用列表推导式,就像矩阵'B'被创建的方式一样。我更新了我的答案。 - Bertrand Gazanion

0
我已经使用以下内容作为可读解决方案:
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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接