在一个二维NumPy数组的滚动窗口中获取最大值

6

我有一个2D的numpy数组,希望获取从左到右,从上到下滚动一行或一列的每个2D滚动窗口中包含的最大值。最简单的方法是遍历所有滚动窗口并获取该滚动窗口中封闭的所有值的最大值。我在下面写下了这种方法:

import numpy as np
shape=(1050,300)
window_size=(120,60)
a = np.arange(shape[1]*shape[0]).reshape(shape[1],shape[0])
max_Map=np.full((shape[1]-window_size[1]+1,shape[0]-window_size[0]+1),0,dtype='uint32')

for i in range(shape[1]-window_size[1]+1):
    for j in range(shape[0]-window_size[0]+1):
        window_max=np.max(a[i:i+window_size[1],j:j+window_size[0]])
        max_Map[i][j]=window_max

但这种方法非常低效,因为在每次滑动时只有2行(或2列)发生了变化,但我的代码没有考虑到任何两个连续滚动窗口之间的相关性。我想到的一种改进方法是对于每个滑动窗口(假设水平滚动),我将计算最左侧列和其余列的最大值,并将这两个值中的最大值作为当前窗口的最大值。对于下一个滚动窗口,最大值将是新添加的列和先前剩余列的最大值...但我仍然认为这不是最优解决方案...

如果有人能指导我正确的方向,我将非常感激,我觉得这应该是一个经过充分研究的问题,但我找不到任何解决方案...提前谢谢!


我认为应该这样写:max_Map=np.full((shape[1]-window_size[1]+1,shape[0]-window_size[0]+1),..for i in range(shape[1]-window_size[1]+1):for j in range(shape[0]-window_size[0]+1): 来覆盖所有元素。 - Divakar
@Divakar,你说得完全正确,我已经编辑了我的帖子。谢谢! - Susie
1个回答

4

方法 #1 使用Scipy的2D最大值滤波器 -


该方法使用Scipy库中的2D最大值滤波器来实现。
from scipy.ndimage.filters import maximum_filter as maxf2D

# Store shapes of inputs
N,M = window_size
P,Q = a.shape

# Use 2D max filter and slice out elements not affected by boundary conditions
maxs = maxf2D(a, size=(M,N))
max_Map_Out = maxs[M//2:(M//2)+P-M+1, N//2:(N//2)+Q-N+1]

方案二 使用Scikit的2D滑动窗口视图 -

from skimage.util.shape import view_as_windows

N,M = window_size
max_Map_Out = view_as_windows(a, (M,N)).max(axis=(-2,-1))

关于窗口大小及其使用的注释:原始方法中,窗口大小按照相反的方式对齐,即window_size的第一个形状参数沿着第二个轴滑动,而第二个形状参数决定窗口沿着第一个轴滑动的方式。对于其他进行滑动最大值过滤的问题,可能不是这种情况,我们通常使用第一个形状参数来操作2D数组的第一个轴,第二个形状参数类似。因此,为了解决这些情况,只需使用:M,N = window_size,然后使用其余的代码。

运行时间测试

方法-

def org_app(a, window_size):
    shape = a.shape[1], a.shape[0]
    max_Map=np.full((shape[1]-window_size[1]+1,
                     shape[0]-window_size[0]+1),0,dtype=a.dtype)
    for i in range(shape[1]-window_size[1]+1):
        for j in range(shape[0]-window_size[0]+1):
            window_max=np.max(a[i:i+window_size[1],j:j+window_size[0]])
            max_Map[i][j]=window_max
    return max_Map

def maxf2D_app(a, window_size):
    N,M = window_size
    P,Q = a.shape
    maxs = maxf2D(a, size=(M,N))
    return maxs[M//2:(M//2)+P-M+1, N//2:(N//2)+Q-N+1]

def view_window_app(a, window_size):
    N,M = window_size
    return view_as_windows(a, (M,N)).max(axis=(-2,-1))

时间和验证 -

In [573]: # Setup inputs
     ...: shape=(1050,300)
     ...: window_size=(120,60)
     ...: a = np.arange(shape[1]*shape[0]).reshape(shape[1],shape[0])
     ...: 

In [574]: np.allclose(org_app(a, window_size), maxf2D_app(a, window_size))
Out[574]: True

In [575]: np.allclose(org_app(a, window_size), view_window_app(a, window_size))
Out[575]: True

In [576]: %timeit org_app(a, window_size)
1 loops, best of 3: 2.11 s per loop

In [577]: %timeit view_window_app(a, window_size)
1 loops, best of 3: 1.14 s per loop

In [578]: %timeit maxf2D_app(a, window_size)
100 loops, best of 3: 3.09 ms per loop

In [579]: 2110/3.09  # Speedup using Scipy's 2D max filter over original approach
Out[579]: 682.8478964401295

2
太。快。了。 :) - Warren Weckesser
非常感谢您提供如此详细的解释!Scipy 2D max filter 确实非常快! - Susie
@Susie:确实是这样,但我实际上是在提到Divakar。 :) - Warren Weckesser

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