【问题标题】:Improving performance iterating in 2d numpy array提高二维 numpy 数组中的迭代性能
【发布时间】:2014-01-31 19:45:51
【问题描述】:

我有两个 2d numpy 数组(图像)。 image 定义的第一个是存储像素 (i,j)

处的移动总和

nbCameras 定义的第二个参数是存储在该像素 (i,j) 处可以看到运动的摄像机数量

我想创建第三张图像 imgFinal,它只存储像素 (i,j) 的值及其邻居 (3 x 3) 掩码,如果可以看到像素 (i,j) 的相机数量大于 1。

现在我使用两个 for 循环,这不是最好的方法。我想提高计算速度,但我还没有找到最好的方法。 我也有点受阻,因为我想与像素 (i, j) 的邻居进行交流

我也尝试使用bumpy.vectorize,但在这种情况下我可以保留像素的邻居。

提高此功能速度的最佳方法是什么?

感谢您的帮助!

maskWidth = 3
dstCenterMask = int( (maskWidth - 1) / 2)

imgFinal = np.zeros((image.shape),dtype = np.float32) 

 for j in range(dstCenterMask,image.shape[0] - dstCenterMask):
    for i in range(dstCenterMask,image.shape[1] - dstCenterMask):
        if nbCameras[j,i] > 1
           imgFinal[j - dstCenterMask : j + dstCenterMask + 1, i - dstCenterMask : i +     dstCenterMask + 1] =
           image[j - dstCenterMask : j + dstCenterMask + 1, i - dstCenterMask : i + dstCenterMask + 1]

【问题讨论】:

    标签: python arrays loops numpy 2d


    【解决方案1】:

    选项 1:尝试以矢量化方式重写代码。您可以使用这样的 3x3 掩码进行卷积:

    import numpy as np
    from scipy.signal import convolve2d
    
    image = np.random.random((100,100))
    nbCameras = np.abs(np.random.normal(size=(100,100)).round())
    
    maskWidth = 3
    mask = np.ones((maskWidth, maskWidth))
    
    visibilityMask = (nbCameras>1).astype(np.float)
    visibilityMask = convolve2d(visibilityMask, mask, mode="same").astype(np.bool)
    
    imgFinal = image.copy()
    imgFinal[~visibilityMask] *= 0
    
    import matplotlib.pyplot as plt
    for i, (im, title) in enumerate([(image, "image"), 
                                     (nbCameras, "nbCameras"), 
                                     (visibilityMask, "visibilityMask"), 
                                     (imgFinal, "imgFinal")]):
        plt.subplot(2,2,i+1)
        plt.title(title)
        plt.imshow(im, cmap=plt.cm.gray)
    
    plt.show()
    

    这将导致这个情节:

    选项 2:使用Numba。这使用了一种先进的即时优化技术,特别适用于加速循环。

    【讨论】:

    • 我喜欢你的第一个选项,谢谢!除了在我的情况下,它将是 imgFinal[visibilityMask] *= 1
    • 我的错应该是 imgFinal[np.invert(visibilityMask)] *= 0 以保持多个相机看到的像素
    • 你也可以写imgFinal[~visibilityMask] *= 0。我会调整答案。
    【解决方案2】:

    这不处理阵列边缘的摄像头,但您的代码也不处理:

    import numpy as np
    from numpy.lib.stride_tricks import as_strided
    
    rows, cols, mask_width = 10, 10, 3
    mask_radius = mask_width // 2
    
    image = np.random.rand(rows, cols)
    nb_cameras = np.random.randint(3 ,size=(rows, cols))
    
    image_view = as_strided(image, shape=image.shape + (mask_width, mask_width),
                            strides=image.strides*2)
    img_final = np.zeros_like(image)
    img_final_view = as_strided(img_final,
                                shape=img_final.shape + (mask_width, mask_width),
                                strides=img_final.strides*2)
    copy_mask = nb_cameras[mask_radius:-mask_radius,
                           mask_radius:-mask_radius] > 1
    
    img_final_view[copy_mask] = image_view[copy_mask]
    

    运行上述代码后:

    >>> nb_cameras
    array([[0, 2, 1, 0, 2, 0, 1, 2, 1, 0],
           [0, 1, 1, 1, 1, 2, 1, 1, 2, 1],
           [1, 2, 2, 2, 1, 2, 1, 0, 2, 0],
           [0, 2, 2, 0, 1, 2, 1, 0, 1, 0],
           [1, 2, 0, 1, 2, 0, 1, 0, 0, 2],
           [2, 0, 1, 1, 1, 1, 1, 1, 0, 1],
           [1, 0, 2, 2, 0, 1, 1, 1, 0, 0],
           [0, 0, 1, 0, 1, 0, 1, 0, 2, 2],
           [0, 1, 0, 1, 1, 2, 1, 1, 2, 2],
           [2, 2, 0, 1, 0, 0, 1, 2, 1, 0]])
    >>> np.round(img_final, 1)
    array([[ 0. ,  0. ,  0. ,  0. ,  0.7,  0.5,  0.6,  0.5,  0.6,  0.9],
           [ 0.1,  0.6,  1. ,  0.2,  0.3,  0.6,  0. ,  0.2,  0.9,  0.9],
           [ 0.2,  0.3,  0.3,  0.5,  0.2,  0.3,  0.4,  0.1,  0.7,  0.5],
           [ 0.9,  0.1,  0.7,  0.8,  0.2,  0.9,  0.9,  0.1,  0.3,  0.3],
           [ 0.8,  0.8,  1. ,  0.9,  0.2,  0.5,  1. ,  0. ,  0. ,  0. ],
           [ 0.2,  0.3,  0.5,  0.4,  0.6,  0.2,  0. ,  0. ,  0. ,  0. ],
           [ 0. ,  0.2,  1. ,  0.2,  0.8,  0. ,  0. ,  0.7,  0.9,  0.6],
           [ 0. ,  0.2,  0.9,  0.9,  0.3,  0.4,  0.6,  0.6,  0.3,  0.6],
           [ 0. ,  0. ,  0. ,  0. ,  0.8,  0.8,  0.1,  0.7,  0.4,  0.4],
           [ 0. ,  0. ,  0. ,  0. ,  0. ,  0.5,  0.1,  0.4,  0.3,  0.9]])
    

    管理边缘的另一种选择是使用来自scipy.ndimage 的卷积函数:

    import scipy.ndimage
    mask = scipy.ndimage.convolve(nb_cameras > 1, np.ones((3,3)),
                                  mode='constant') != 0
    img_final[mask] = image[mask]
    >>> np.round(img_final, 1)
    array([[ 0.6,  0.8,  0.7,  0.9,  0.7,  0.5,  0.6,  0.5,  0.6,  0.9],
           [ 0.1,  0.6,  1. ,  0.2,  0.3,  0.6,  0. ,  0.2,  0.9,  0.9],
           [ 0.2,  0.3,  0.3,  0.5,  0.2,  0.3,  0.4,  0.1,  0.7,  0.5],
           [ 0.9,  0.1,  0.7,  0.8,  0.2,  0.9,  0.9,  0.1,  0.3,  0.3],
           [ 0.8,  0.8,  1. ,  0.9,  0.2,  0.5,  1. ,  0. ,  0.3,  0.8],
           [ 0.2,  0.3,  0.5,  0.4,  0.6,  0.2,  0. ,  0. ,  0.7,  0.6],
           [ 0.2,  0.2,  1. ,  0.2,  0.8,  0. ,  0. ,  0.7,  0.9,  0.6],
           [ 0. ,  0.2,  0.9,  0.9,  0.3,  0.4,  0.6,  0.6,  0.3,  0.6],
           [ 0.4,  1. ,  0.8,  0. ,  0.8,  0.8,  0.1,  0.7,  0.4,  0.4],
           [ 0.9,  0.5,  0.8,  0. ,  0. ,  0.5,  0.1,  0.4,  0.3,  0.9]])
    

    【讨论】:

    • 谢谢詹姆。我不知道 strides,替换 for 循环似乎很感兴趣。但我真的不明白你为什么使用 shape=image.shape + (mask_width, mask_width) 因为这给了我一个 (10,10,3,3) 的形状。另外,您为什么要使用 image.strides*2?不应该是 image.strides 遍历每个像素吗?
    • 这很好:它创建了一个形状为 (10,10,3,3) 的数据视图,其中 a[i,j] 中的 (3,3) 数组是 a[i :i+3,j:j+3]
    • 好的,我明白了。但是这条线呢:copy_mask = nb_cameras[mask_radius:-mask_radius, mask_radius:-mask_radius] > 1 据我所知,copy_mask 是从 nb_cameras 固定的,但它不应该沿着 nb_cameras 移动吗?
    【解决方案3】:

    使用 skimage.morphology 的 binary_dilation 函数,这变得非常优雅。它将采用二进制数组,并将任何真实的像素扩展为真实值(或任何其他大小)的 3x3 网格。这也应该处理边缘的情况。我认为您的实施没有。

    使用这个掩码很容易计算 imgFinal

    from skimage.morphology import binary_dilation, square
    mask = binary_dilation(nbCameras > 1, square(maskWidth))
    imgFinal = np.where(mask, image, 0)
    

    square(3) 只是np.ones((3,3)) 的简写

    http://scikit-image.org/docs/dev/api/skimage.morphology.html?highlight=dilation#skimage.morphology.dilation

    使用膨胀来更好地解释它的作用的示例:

    In [27]: a
    Out[27]:
    array([[ 1.,  0.,  0.,  0.,  0.],
           [ 0.,  0.,  0.,  0.,  0.],
           [ 0.,  0.,  0.,  0.,  0.],
           [ 0.,  0.,  0.,  1.,  0.],
           [ 0.,  0.,  0.,  0.,  0.]])
    
    In [28]: binary_dilation(a, square(3))
    Out[28]:
    array([[1, 1, 0, 0, 0],
           [1, 1, 0, 0, 0],
           [0, 0, 1, 1, 1],
           [0, 0, 1, 1, 1],
           [0, 0, 1, 1, 1]], dtype=uint8)
    

    【讨论】:

    • 感谢您的回答。但我不认为在我的情况下我想使用膨胀,因为我想保持与我的图像相同的像素值。做膨胀会增加我的图像的亮度
    • @RomanzoCriminale 膨胀根本不会改变图像,它只是用于从 nbCameras 创建一个具有所有像素和邻居的掩码,其中 nbCameras > 1。此掩码用于获取值来自图像。
    猜你喜欢
    • 2011-12-28
    • 2021-06-11
    • 1970-01-01
    • 2018-12-01
    • 1970-01-01
    • 2020-10-06
    • 2016-01-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多