numpy中使用as_strided函数实现滑动窗口?

8

我正在使用Python实现滑动窗口以检测静态图像中的对象,我了解到了一个很好的函数:

numpy.lib.stride_tricks.as_strided

所以我试图制定一般规则,以避免在更改我需要的滑动窗口大小时可能会失败的错误。最终我得到了这个表述:

all_windows = as_strided(x,((x.shape[0] - xsize)/xstep ,(x.shape[1] - ysize)/ystep ,xsize,ysize), (x.strides[0]*xstep,x.strides[1]*ystep,x.strides[0],x.strides[1])

这将导致一个4维矩阵。前两个数字表示图像x和y轴上窗口的数量,其他数字表示窗口的大小(xsize,ysize)。 step表示相邻两个窗口之间的位移量。
如果我选择一个正方形滑动窗口,这种表示方式就可以很好地工作。但是,对于例如(128,64)大小的窗口,我通常会得到与图像无关的数据。
我的代码有什么问题?有什么更好的方法在Python中进行图像处理时获取漂亮整齐的滑动窗口吗?
谢谢

由于您正在寻找模板匹配算法,这篇文章可能值得一看,它使用了步幅。 - Divakar
4个回答

4

您的代码存在问题。实际上,这份代码在二维情况下表现良好,没有使用多维版本的必要 (使用步幅进行高效的移动平均滤波器)。以下是修复后的代码:

A = np.arange(100).reshape((10, 10))
print A
all_windows = as_strided(A, ((A.shape[0] - xsize + 1) / xstep, (A.shape[1] - ysize + 1) / ystep, xsize, ysize),
      (A.strides[0] * xstep, A.strides[1] * ystep, A.strides[0], A.strides[1]))
print all_windows

3

那么,有没有更好的替代方案,可以快速而整洁地提取不同尺度的滑动窗口,以支持检测和模板匹配算法? - JustInTime
我需要提取这样的子窗口,在窗口上获取一些HOG特征,并使用已经训练好的分类器对该实例进行分类,以检查它是否是我关心的子窗口。 - JustInTime
1
为了论证而言,关于HOG,我之前使用嵌套循环来提取其单元直方图进行了实现。但我认为,采用步幅技巧能够提高性能,并消除在使用Python或Matlab等语言上非常耗费资源的两个循环的使用。 - JustInTime
@JustInTime 你好,你解决了你的问题吗?关于滑动窗口方法,我还有一些问题,可以联系你吗? - user961627
1
请查看view_as_windows,希望能对您有所帮助。 - Gilberto

1
我有一个类似的用例,需要在一批多通道图像上创建滑动窗口,最终编写了以下函数。我撰写了一篇更深入的博客文章,介绍了手动创建卷积层的相关内容。该函数实现了滑动窗口,并可对输入数组进行膨胀或添加填充。
该函数的输入为:
input - (Batch, Channel, Height, Width) 的大小 output_size - 取决于使用情况,下面有注释。 kernel_size - 您希望创建的滑动窗口的大小(正方形) padding - 添加到(H,W)维度外部的零填充量 stride - 滑动窗口在输入上应采取的步幅 dilate - 扩展输入单元格的数量。这会在元素之间添加0填充的行/列
通常,在执行前向卷积时,您不需要执行膨胀,因此可以使用以下公式找到输出大小(将x替换为输入维度):
(x-kernel_size+2*padding)//stride+1

使用此函数执行卷积的反向传递时,步长为1,并将输出大小设置为正向传递的x输入大小。

使用此函数的示例代码可在此链接中找到。

def getWindows(input, output_size, kernel_size, padding=0, stride=1, dilate=0):
    working_input = input
    working_pad = padding
    # dilate the input if necessary
    if dilate != 0:
        working_input = np.insert(working_input, range(1, input.shape[2]), 0, axis=2)
        working_input = np.insert(working_input, range(1, input.shape[3]), 0, axis=3)

    # pad the input if necessary
    if working_pad != 0:
        working_input = np.pad(working_input, pad_width=((0,), (0,), (working_pad,), (working_pad,)), mode='constant', constant_values=(0.,))

    in_b, in_c, out_h, out_w = output_size
    out_b, out_c, _, _ = input.shape
    batch_str, channel_str, kern_h_str, kern_w_str = working_input.strides

    return np.lib.stride_tricks.as_strided(
        working_input,
        (out_b, out_c, out_h, out_w, kernel_size, kernel_size),
        (batch_str, channel_str, stride * kern_h_str, stride * kern_w_str, kern_h_str, kern_w_str)
    )

1

为了后续参考:

在scikit-learn中,这个功能被实现在sklearn.feature_extraction.image.extract_patches函数中。


我认为这种方法会为补丁从原始图像中制作副本,而步幅方法似乎是专门为避免这样做而设计的。 - Alex Klibisz
作为替代方案,请参阅 skimage.util.view_as_windows(http://scikit-image.org/docs/dev/api/skimage.util.html#skimage.util.view_as_windows) - Alex Klibisz
@AlexKlibisz 的 extract_patches 函数使用最通用的步幅(适用于任意维度的所有 ndarray,并且您可以在任意步长下提取任意形状的补丁)。 extract_patches_2d 使用此函数,但调用了一个重塑操作,从而引入了一次复制(这在 2D 情况下是有意为之的)。[完全披露:我编写了 extract_patches] - eickenberg
看起来skimage函数实现了完全相同的功能。为了完整起见,这是extract_patches的源代码链接:https://github.com/scikit-learn/scikit-learn/blob/0.18.X/sklearn/feature_extraction/image.py#L242 - eickenberg
你是对的,我的错误。我错误地使用了extract_patches_2d而不是extract_patches - Alex Klibisz

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