在很多工作中都用失去滑动窗口,比方密集人群计数,标签文件是一张与图片尺寸等大的二维矩阵,人头的核心地位为1,其余地位为0。我想求出这张图片中人头最浓密的一块区域(区域尺寸给定),那么怎么求呢?

我想到的方法就是用这个区域的尺寸作为一个固定窗口,在整个标签矩阵中滑动,每滑动到一处,就计算一下以后窗口中 “1” 的个数,数量最多(加和最大)的区域就是人头最浓密的区域,即以后的窗口。

对于滑动窗口计算,最容易想到的就是用两层for循环来实现,但首先它须要解决边界的问题,其次随着图片的尺寸的增大,效率会变地很低,内存占用也十分大。

本文应用numpy来实现滑窗计算,并计算元素值相加的和最大的一块区域(区域的尺寸设定为卷积核的尺寸)。为了减少算法的普遍性,标签文件中元素的值不局限于0,1两个数字,为了解决这个变动带来的问题,须要在窗口滑动计算后减少一个小块区域内元素相加的动作,顺着这个思路能够额定实现应用numpy实现最大和均值两种池化的工作。

拿一个最简略的例子来实现下文的探讨:

$$X=\left[ \begin{matrix} 1 & 2 & 3\\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{matrix} \right]$$

如果kernal的大小是2x2,stride为1,那么滑窗计算的后果就是上面的4个小矩阵组成的新矩阵A:

到这里,手工计算版的滑动窗口就完结了。后续工作,4个小矩阵各自求和,失去[12,16,24,28]四个值,可知元素值相加的和最大的一块区域是最右下角的那一块。

上面用程序来复现下面的探讨。

def pool2d(X, kernel_size, stride, padding, pool_mode='avg'):    # **第一步**    X = np.pad(X, padding, mode='constant')    # 第二步    output_shape = ((X.shape[0] - kernel_size[0] + 2*padding)//stride + 1, (X.shape[1] - kernel_size[1] + 2*padding)//stride + 1)         # 第三步    A = as_strided(X, shape = output_shape + kernel_size, strides = (stride*X.strides[0], stride*X.strides[1]) + X.strides)      # 第四步    A = A.reshape(-1, *kernel_size)    # 把四维压缩到三维      # 第五步    if pool_mode == 'max':        return A, A.max(axis=(1,2)).reshape(output_shape)    # 实现最大池化工作    elif pool_mode == 'avg':        return A, A.mean(axis=(1,2)).reshape(output_shape)   # 实现均值池化工作

第一步:边缘填充

对矩阵进行池化操作,参数constant示意间断填充雷同的值,即padding。个别是全零填充。

第二步:尺寸预计算

事后计算窗口滑动后每个小矩阵Ai的尺寸,这部分就能够依据卷积前后尺寸的变动公式来计算:

new=(old-kennel_size+2*padding)/stride+1

在下面的例子中output_shape就是(2,2)。 【 (4-2+2*0)//2+1=2

第三步:滑窗计算

调用as_strided 函数进行窗口滑动计算。该函数次要的参数有三个:

  • 要操作的矩阵,不必多说了。
  • shape:返回矩阵的尺寸,区别于之前的“output_shape”,这个shape是指矩阵A的尺寸,即所有小矩阵放在一块的尺寸,这个尺寸不肯定等于输出矩阵X的尺寸。比方下面的例子,shape就是(2,2,2,2),而输出矩阵X的尺寸是(3,3)
  • strides:这是numpy数组的一个属性,官网手册给出的解释是逾越数组各个维度所须要通过的字节数(bytes)。用下面的矩阵X当例子阐明:

    • X[0][0]X[0][1] 须要通过4个字节,为什么是4个?因为a的数据类型是int32,正好占4个字节.
    • X[0][0]X[1][0] 须要通过12个字节,为什么是12个?因为python是行程序优先的编程语言,即读取矩阵元素时是一行一行来读的,把矩阵X展平,就是[0 1 2 3 4 5 6 7 8],从0到3就须要遍历3个元素,而每个元素都是4个字节,所以总共须要12个字节。

      注1:常见的编程语言中,只有matlab和fortran是列程序优先。

      注2:想要更深刻地理解strides,还是举荐文章【卷积算法另一种高效实现,as_strided详解】

这一步返回的后果就是上面的矩阵A,尺寸为(2,2,2,2)

![[公式]](https://www.zhihu.com/equatio...

第四步:浓密区域搜查工作

其实滑动窗口计算到第三步就曾经完结了,这一步就是从新调整一下尺寸,将(2,2,2,2)调整成(4,2,2),即第一维变成小矩阵的个数,前面两维是小矩阵的尺寸。对每个小矩阵加和再比拟就能晓得加和最大的一块区域了,继而实现浓密区域搜查工作。

第五步:两类池化工作

以均匀池化为例,调用如下函数

A.mean(axis=(1,2)).reshape(output_shape)

axis=(1,2)是因为此时的矩阵A维度为(4,2,2),要从第二个维度开始解决。

reshape(output_shape)是因为依照池化工作的要求,输入后果要与小矩阵的维度统一,即(2,2)

程序运行后果:


参考:https://zhuanlan.zhihu.com/p/...