在图像上找到一个3x3的滑动窗口

6

我有一张图片。

我想要获取每个像素的3x3窗口(相邻像素)。

我有以下Python代码:

for x in range(2,r-1,1):
    for y in range(2,c-1,1):
        mask5=numpy.array([cv.Get2D(copy_img,x-1,y-1),cv.Get2D(copy_img,x-1,y),cv.Get2D(copy_img,x-1,y+1),cv.Get2D(copy_img,x,y-1),cv.Get2D(copy_img,x,y),cv.Get2D(copy_img,x,y+1),cv.Get2D(copy_img,x+1,y-1),cv.Get2D(copy_img,x+1,y),cv.Get2D(copy_img,x+1,y+1)])
        cent=[cv.Get2D(copy_img,x,y)]

mask5是3x3的窗口。cent是中心像素。

有没有更有效的方法来完成这个任务 - 例如使用映射、迭代器等,而不是我使用的两个嵌套循环?


你的意图是什么?可能你想要执行卷积操作吧?告诉我们你打算如何使用 mask5,这样我们可以更好地帮助你,加油! - fraxel
@frexel:不是很确定。有没有一种方法可以简单地优化上面的循环,比如使用映射或迭代器等方式来避免使用for循环? - Velvet Ghost
那个循环到底是做什么的?它只是将以(x,y)为中心的3x3矩阵复制到掩码y中吗?而且你想对所有像素都这样做吗?还是只对一个像素(x,y)进行操作? - Abid Rahman K
哦,好吧...取3*3的数组切片而不是获取9个点。将您的图像用作numpy数组,并将主行替换为:copy_img[x:x+3,y:y+3] - fraxel
1
没有充分理解你正在做的事情,这就是我能做到的最好的了! :) - fraxel
显示剩余7条评论
3个回答

3
这可以更快地完成,通过重新塑形和交换轴,然后对所有内核元素进行重复,像这样:

(可以通过重新塑造和交换轴来加速此过程,并在所有内核元素上重复此操作,如下所示:


im = np.arange(81).reshape(9,9)
print np.swapaxes(im.reshape(3,3,3,-1),1,2)

这将为您提供一个3*3瓷砖数组,可以在表面上平铺:
[[[[ 0  1  2]   [[ 3  4  5]   [[ 6  7  8]
   [ 9 10 11]    [12 13 14]    [15 16 17]
   [18 19 20]]   [21 22 23]]   [24 25 26]]]

 [[[27 28 29]   [[30 31 32]   [[33 34 35]
   [36 37 38]    [39 40 41]    [42 43 44]
   [45 46 47]]   [48 49 50]]   [51 52 53]]]

 [[[54 55 56]   [[57 58 59]   [[60 61 62]
   [63 64 65]    [66 67 68]    [69 70 71]
   [72 73 74]]   [75 76 77]]   [78 79 80]]]]

为了获得重叠的瓦片,我们需要重复这个过程8次,但是通过使用vstackcolumn_stack的组合来“包装”数组。请注意,右侧和底部的瓦片数组会环绕(这可能是您想要的,具体取决于如何处理边缘条件)。
im =  np.vstack((im[1:],im[0]))
im =  np.column_stack((im[:,1:],im[:,0]))
print np.swapaxes(im.reshape(3,3,3,-1),1,2)

#Output:
[[[[10 11 12]   [[13 14 15]   [[16 17  9]
   [19 20 21]    [22 23 24]    [25 26 18]
   [28 29 30]]   [31 32 33]]   [34 35 27]]]

 [[[37 38 39]   [[40 41 42]   [[43 44 36]
   [46 47 48]    [49 50 51]    [52 53 45]
   [55 56 57]]   [58 59 60]]   [61 62 54]]]

 [[[64 65 66]   [[67 68 69]   [[70 71 63]
   [73 74 75]    [76 77 78]    [79 80 72]
   [ 1  2  3]]   [ 4  5  6]]   [ 7  8  0]]]]

用这种方法,您最终得到9组数组,因此您需要将它们重新压缩在一起。对于所有维度可被3整除的数组,这个过程和所有的重塑过程都是通用的:
def new(im):
    rows,cols = im.shape
    final = np.zeros((rows, cols, 3, 3))
    for x in (0,1,2):
        for y in (0,1,2):
            im1 = np.vstack((im[x:],im[:x]))
            im1 = np.column_stack((im1[:,y:],im1[:,:y]))
            final[x::3,y::3] = np.swapaxes(im1.reshape(rows/3,3,cols/3,-1),1,2)
    return final

比较这个new函数与循环遍历所有切片(如下所示),使用timeit,对于一个300*300的数组来说,它大约快了4倍。
def old(im):
    rows,cols = im.shape
    s = []
    for x in xrange(1,rows):
        for y in xrange(1,cols):
            s.append(im[x-1:x+2,y-1:y+2])
    return s

谢谢。我成功将所有内容简化为一行代码 - 不幸的是,这是在MATLAB中(使用im2col函数)。Python中是否有类似的直接等效函数? - Velvet Ghost
@VelvetGhost im2col只是一个函数,它包装了一个实现像我的或上面的实现(尽管更加完善)。MATLAB实现并不能神奇地删除代码。如果你可以使用自己的函数,那么最好不过了,因为你不需要依赖于昂贵的工具箱才能使其工作(这就是MATLAB的乐趣所在)。此外,不要认为因为它在工具箱中,它就快速或高效。 - Henry Gomersall
@HenryGomersall:谢谢!是的,我知道MATLAB并不能神奇地删除代码。但在紧急情况下编写代码时,我发现这更容易和更快速。非常感谢你的Python实现。当我有一点时间时,它将帮助我更多地了解Python。 - Velvet Ghost
3
问题是:我们如何修改这种方法,使其适用于任意(而不仅仅是3的倍数)图像尺寸? - Magsol
@Magsol,我已经创建了一些Cython代码(https://gist.github.com/notmatthancock/554ad960ee04c84503d96ef8e292595a),用于“窗口化”3D图像体积,适用于任何窗口大小。您可以轻松修改此代码以适用于2D图像。 - Matt Hancock

1

我认为以下代码可以实现你想要的功能。循环仅针对9个元素。我相信有一种向量化的方法,但这可能不值得努力。

import numpy

im = numpy.random.randint(0,50,(5,7))

# idx_2d contains the indices of each position in the array
idx_2d = numpy.mgrid[0:im.shape[0],0:im.shape[1]]

# We break that into 2 sub arrays
x_idx = idx_2d[1]
y_idx = idx_2d[0]

# The mask is used to ignore the edge values (or indeed any values).
mask = numpy.ones(im.shape, dtype='bool')
mask[0, :] = False
mask[:, 0] = False
mask[im.shape[0] - 1, :] = False
mask[:, im.shape[1] - 1] = False

# We create and fill an array that contains the lookup for every
# possible 3x3 array.
idx_array = numpy.zeros((im[mask].size, 3, 3), dtype='int64')

# Compute the flattened indices for each position in the 3x3 grid
for n in range(0, 3):
    for m in range(0, 3):
        # Compute the flattened indices for each position in the 
        # 3x3 grid
        idx = (x_idx + (n-1)) + (y_idx  + (m-1)) * im.shape[1]

        # mask it, and write it to the big array
        idx_array[:, m, n] = idx[mask]


# sub_images contains every valid 3x3 sub image
sub_images = im.ravel()[idx_array]

# Finally, we can flatten and sort each sub array quickly
sorted_sub_images = numpy.sort(sub_images.reshape((idx[mask].size, 9)))

0

请尝试将以下代码作为 matlab 函数 im2col(...) 运行

import numpy as np

def im2col(Im, block, style='sliding'):
    """block = (patchsize, patchsize)
        first do sliding
    """
    bx, by = block
    Imx, Imy = Im.shape
    Imcol = []
    for j in range(0, Imy):
        for i in range(0, Imx):
            if (i+bx <= Imx) and (j+by <= Imy):
                Imcol.append(Im[i:i+bx, j:j+by].T.reshape(bx*by))
            else:
                break
    return np.asarray(Imcol).T

if __name__ == '__main__':
    Im = np.reshape(range(6*6), (6,6))
    patchsize = 3
    print Im
    out =  im2col(Im, (patchsize, patchsize))
    print out
    print out.shape
    print len(out)

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