使用numpy和scipy计算图像上的窗口概率质量函数

时间:2017-11-07 02:29:51

标签: python numpy scipy

我从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.186*log(1/3)+3*log(1/9))作为我的例子,这是不正确的。如果窗口中的每个值都是唯一的(窗口p(x) = 0.11),这将是正确的,但在上面的示例中是不正确的,因为存在重复的元素。我需要一种方法来在计算它们的概率(一些聪明的np.unique的应用程序)之后将重复值归零,或者以其他方式运行计算。由于log是非线性的,因此我不能简单地对概率求和并进行归一化。正确的结果是s = -8.792*log(1/3)+3*log(1/9))。

编辑:解决方案 我在下面发布了解决方案。对于我的示例图像,它比使用python循环方法快大约10倍。我想要更快的方法,但我怀疑它是否可能,因为log是非线性的,"卷积"是不可分的。

1 个答案:

答案 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)