我从numpy中的2D图像数组开始,我想要执行一些窗口处理。我正在寻找一种矢量化方法,对于每个窗口,首先计算概率质量函数,然后用该质量函数上的运算替换中心像素。此外,它需要使用非线性操作,如log(x)
。例如:给定以下3x3
窗口:
[[1, 1, 1],
[2, 2, 2],
[3, 4, 5]]
概率质量函数类似于:p(1) = 0.33, p(2) = 0.33, p(3) = p(4) = p(5) = 0.11
。我昨天开始使用this question,这使我进入了以下过程,为我提供了窗口ksize
(上例中的3x3
)中每个值的概率,相对于其他值那个窗口。
def process(a, ksize=3):
# get the number of times a value occurs in each kernel window
b = windowed_occurences(a, ksize)
# convert to probabilities
p = a / (ksize * ksize)
def windowed_occurences(a, ksize):
window_shape = (ksize, ksize)
d = math.floor(ksize / 2)
a = np.pad(a, (d, d), 'constant', constant_values=(0, 0))
# get the windowed array of shape (a.height, a.width, ksize, ksize)
b = skimage.util.shape.view_as_windows(a, window_shape)
# replace each element of ksize x ksize kernel with the number of occurances
# of that element in the kernel
# process from https://stackoverflow.com/questions/47130141/ @Divakar
ar2D = b.reshape(-1, b.shape[-2] * b.shape[-1])
c = bincount2D_vectorized(ar2D)
d = c[np.arange(ar2D.shape[0])[:, None], ar2D].reshape(b.shape)
return d
# bincount2D_vectorized from https://stackoverflow.com/a/46256361/ @Divakar
def bincount2D_vectorized(a):
N = a.max()+1
a_offs = a + np.arange(a.shape[0])[:,None]*N
return np.bincount(a_offs.ravel(), minlength=a.shape[0]*N).reshape(-1,N)
对于示例窗口,这给出:
[[0.33, 0.33, 0.33]
[0.33, 0.33, 0.33]
[0.11, 0.11, 0.11]]
现在,如果我想要这些概率的对数之和,我可以做
s = (np.log(p)).sum((-2, -1))
给我s = -13.18
(6*log(1/3)+3*log(1/9)
)作为我的例子,这是不正确的。如果窗口中的每个值都是唯一的(窗口p(x) = 0.11
),这将是正确的,但在上面的示例中是不正确的,因为存在重复的元素。我需要一种方法来在计算它们的概率(一些聪明的np.unique
的应用程序)之后将重复值归零,或者以其他方式运行计算。由于log
是非线性的,因此我不能简单地对概率求和并进行归一化。正确的结果是s = -8.79
(2*log(1/3)+3*log(1/9)
)。
编辑:解决方案
我在下面发布了解决方案。对于我的示例图像,它比使用python循环方法快大约10倍。我想要更快的方法,但我怀疑它是否可能,因为log
是非线性的,"卷积"是不可分的。
答案 0 :(得分:1)
经过长时间的仔细观察@Divakar's bincount2D_vectorized
后,我意识到概率质量函数已经在那里,只需要重新塑造。要替换原始图像a
中的每个像素,并在其log(p(x))
内核上求和,其中p(x)
是在该内核中找到像素值x
的概率,我们可以做到:
def process(a, ksize=3):
# get the number of times a value occurs in each kernel window
window_shape = (ksize, ksize)
d = math.floor(ksize / 2)
a = np.pad(a, (d, d), 'constant', constant_values=(0, 0))
# get the windowed array of shape (a.height, a.width, ksize, ksize)
b = skimage.util.shape.view_as_windows(a, window_shape)
# process from https://stackoverflow.com/questions/47130141/ @Divakar
ar2D = b.reshape(-1, b.shape[-2] * b.shape[-1])
c = bincount2D_vectorized(ar2D)
# convert to probabilities
p = c / (ksize * ksize)
p[p == 0] = 1 # divide by 0 error
# sum and reshape
return np.sum(np.log(p), axis=1).reshape(a.shape)