这里提出了一种矢量化方法。步骤是:
获取内核大小的 2D 滑动窗口,导致 4D 数组。我们可以使用
skimage's view_as_windows
将它们作为视图,从而避免为此创建任何额外的内存。
通过索引到 4D 数组来选择以零为中心的窗口。这会强制复制。但是假设零的数量比输入数组中的元素总数相对较小,这应该没问题。
对于每个选定的窗口,用适当的偏移量偏移每个窗口,np.bincount
以便用于执行计数。因此,使用bincount
并获得不包括零的最大计数。最大计数的 argmax 应该是我们的人!
这是涵盖这些步骤的实现 -
from skimage.util import view_as_windows as viewW
def fill_zero_regions(a, kernel_size=3):
hk = kernel_size//2 # half_kernel_size
a4D = viewW(a, (kernel_size,kernel_size))
sliced_a = a[hk:-hk,hk:-hk]
zeros_mask = sliced_a==0
zero_neighs = a4D[zeros_mask].reshape(-1,kernel_size**2)
n = len(zero_neighs) # num_zeros
scale = zero_neighs.max()+1
zno = zero_neighs + scale*np.arange(n)[:,None] # zero_neighs_offsetted
count = np.bincount(zno.ravel(), minlength=n*scale).reshape(n,-1)
modevals = count[:,1:].argmax(1)+1
sliced_a[zeros_mask] = modevals
return a
样品运行 -
In [23]: a
Out[23]:
array([[9, 9, 9, 0, 0, 0, 0, 1, 1, 1],
[9, 9, 9, 9, 0, 7, 1, 1, 1, 1],
[9, 9, 9, 9, 0, 2, 2, 1, 1, 1],
[9, 9, 9, 8, 0, 2, 2, 1, 1, 1],
[9, 9, 9, 8, 0, 2, 2, 2, 1, 1],
[4, 4, 4, 4, 0, 2, 2, 2, 1, 1],
[4, 6, 6, 4, 0, 0, 0, 0, 0, 0],
[4, 6, 6, 4, 0, 0, 0, 0, 0, 0],
[4, 4, 4, 4, 5, 5, 5, 5, 5, 5],
[4, 4, 4, 4, 5, 5, 5, 5, 5, 5]])
In [24]: fill_zero_regions(a)
Out[24]:
array([[9, 9, 9, 0, 0, 0, 0, 1, 1, 1],
[9, 9, 9, 9, 9, 7, 1, 1, 1, 1],
[9, 9, 9, 9, 2, 2, 2, 1, 1, 1],
[9, 9, 9, 8, 2, 2, 2, 1, 1, 1],
[9, 9, 9, 8, 2, 2, 2, 2, 1, 1],
[4, 4, 4, 4, 2, 2, 2, 2, 1, 1],
[4, 6, 6, 4, 4, 2, 2, 2, 1, 0],
[4, 6, 6, 4, 4, 5, 5, 5, 5, 0],
[4, 4, 4, 4, 5, 5, 5, 5, 5, 5],
[4, 4, 4, 4, 5, 5, 5, 5, 5, 5]])
正如所见,我们没有解决边界情况。如果需要,请使用零填充数组作为输入数组,如下所示 : np.pad(a, (k//2,k//2), 'constant')
,k
作为内核大小(=3
用于示例)。