【问题标题】:Summing each 3x3 window of a M*N matrix, into a M/3*N/3 matrix with numpy用 numpy 将 M*N 矩阵的每个 3x3 窗口求和成 M/3*N/3 矩阵
【发布时间】:2013-11-30 13:11:57
【问题描述】:

我正在尝试实现一个求和(或最终平均)的函数 给定矩阵的每个 3x3 窗口,并使用每个窗口的结果创建一个小 9 倍的矩阵。

我想不出用 numpy 做到这一点的有效而简洁的方法。

有什么想法吗?

谢谢!

【问题讨论】:

  • scipy.misc.imresize(arr, size, interp='bilinear', mode=None)

标签: python image-processing numpy scipy


【解决方案1】:

为了准确实现您的要求,我将在图像上应用 [3x3] 框过滤器,然后使用最近邻插值调整矩阵大小。

# Pseudo code
kernel = np.array([[1/9, 1/9, 1/9],
                   [1/9, 1/9, 1/9],
                   [1/9, 1/9, 1/9]])
avg_data= ndimage.convolve(data, kernel)

smaller_data = scipy.misc.imresize(avg_data, org_size/3, interp='nearest', mode=None)

如果你想要更高效的东西——正如@Jaime 所指出的——你可以这样做How can I efficiently process a numpy array in blocks similar to Matlab's blkproc (blockproc) function

from numpy.lib.stride_tricks import as_strided as ast

def block_view(A, block= (3, 3)):
    """Provide a 2D block view to 2D array. No error checking made.
    Therefore meaningful (as implemented) only for blocks strictly
    compatible with the shape of A."""
    # simple shape and strides computations may seem at first strange
    # unless one is able to recognize the 'tuple additions' involved ;-)
    shape= (A.shape[0]/ block[0], A.shape[1]/ block[1])+ block
    strides= (block[0]* A.strides[0], block[1]* A.strides[1])+ A.strides
    return ast(A, shape= shape, strides= strides)

if __name__ == '__main__':
    B = block_view(A).sum(axis=(2,3))

当您尝试了解发生了什么时,请记住,步幅表示我们需要在内存中偏移的字节量,以遍历每个维度中的下一个单元格。因此,如果您正在处理 int32 数据类型,它将是 4 的乘积。

【讨论】:

  • 你可以通过切片来完成最后一步,即avg_data[1::3, 1::3] 给你正确的结果,几乎可以肯定更快。它有点指出,您必须丢弃 89% 的计算值,因此在这里使用过滤器可能远非最佳。
  • 它有效,但我认为正确的总和是 block_view(A).sum(axis=(2,3))。顺便说一句,在某个地方对人类的“as_strided”有很好的解释吗?
【解决方案2】:

最简单的仅 numpy 方法,它比卷积做的工作少得多,因此可能比基于过滤器的方法更快,是将原始数组的大小调整为具有额外维度的一个,然后通过对新数组求和将其还原为正常尺寸:

>>> arr = np.arange(108).reshape(9, 12)
>>> rows, cols = arr.shape
>>> arr.reshape(rows//3, 3, cols//3, 3).sum(axis=(1, 3))
array([[117, 144, 171, 198],
       [441, 468, 495, 522],
       [765, 792, 819, 846]])

如果您想要均值,只需将结果数组除以元素数即可:

>>> arr.reshape(rows//3, 3, cols//3, 3).sum(axis=(1, 3)) / 9
array([[ 13.,  16.,  19.,  22.],
       [ 49.,  52.,  55.,  58.],
       [ 85.,  88.,  91.,  94.]])

此方法仅适用于您的数组的形状本身是 3 的倍数。

【讨论】:

  • 我花了很长时间想知道为什么这不起作用,结果 sum() 是用 numpy 1.7 更新的,而 axis=(1,3) 在我的版本 (1.6) 中不起作用
  • 是的,你是对的。在使用 .sum(axis=3).sum(axis=1) 而不是 .sum(axis=(1, 3)) 的早期版本中,您可以获得相同的结果。请注意,顺序很重要,即.sum(axis=1).sum(axis=3) 会引发错误,因为在第一次求和后数组只有 3D,因此axis=3 超出范围。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-31
相关资源
最近更新 更多