2016-04-26 57 views
2

我有一個2d numpy數組,我想從中知道在兩個堆棧內核中指定的兩個值之間的所有像素的計數。邊界應該從計數中排除。Numpy自定義移動窗口計數單元格範圍

例如:

input = np.array([[1, 0, 0, 2, 1], 
    [0, 2, 1, 1, 7], 
    [2, 0, 6, 4, 1], 
    [1, 2, 3, 0, 5], 
    [6, 5, 4, 3, 2]]) 

kernel_min=np.array([[0, 1, 0], 
    [0, 1, 2], 
    [0, 0, 1]]) 

kernel_max=np.array([[2, 3, 2], 
    [2, 2, 4], 
    [2, 2, 3]]) 

min_max = np.dstack((kernel_min,kernel_max)) 

outcome= [[0, 0, 0, 0, 0], 
    [0, 7, 7, 9, 0], 
    [0, 8, 8, 8, 0], 
    [0, 8, 8, 8, 0], 
    [0, 0, 0, 0, 0]] 

要做到這一點,我創建通過所述輸入陣列中的所有元件迴路的腳本。在每個元素周圍提取內核大小的區域,並計算內核範圍內的單元。

def create_heatmap(input,min_max): 
    input_selection=np.zeros(shape(min_max),dtype='f') 
    heatmap = np.zeros(shape(input),dtype='uint16') 
    length,width=shape(input) 
    center=shape(min_max)[0]/2 
    #exclude edge pixels 
    for i in range(0+center,length-center): 
    for j in range(0+center,width-center): 
     # Mask area the size of the kernel around the selected cell 
     input_selection= input[i-center:i+center+1,j-center:j+center+1] 
     # Count the number of cells within kernel range: 
     heatmap[i,j]= shape(np.where((input_selection>=min_max [:,:,0]) & (input_selection<=min_max [:,:,1])))[1] 
    return heatmap` 

然而,循環所有元素是非常耗時的(我有一個巨大的數組)。有沒有辦法來加速兩個內核範圍內的像素數?例如,計算所有值的移動窗口函數。

回答

1

您可以通過使用數組跨步技巧在沒有Python循環的情況下執行此操作。簡單的方法是使用現成的功能,例如來自Scikit Image的view_as_windows或來自Scikit Learn的extract_patches

from skimage.util import view_as_windows 

def with_strides(img, kernel_min, kernel_max): 
    win_w, win_h = kernel_min.shape 
    heatmap = np.zeros(img.shape, dtype='uint16') 
    target = heatmap[win_h//2:-win_h//2+1, win_w//2:-win_w//2+1] 

    windowed = view_as_windows(img, (win_h, win_w)) 
    mask = ((kernel_min <= windowed) & (windowed <= kernel_max)) 
    np.sum(mask, axis=(2,3), out=target) 

    return heatmap 

然而,這種方法在內存方面要求很高。

另一種方法是仍然使用Python循環,但循環遍歷內核而不是圖像。這個想法是通過在較大的數組上調用Numpy函數來減少開銷。

def kernel_loops(img, kernel_min, kernel_max): 
    img_h, img_w = img.shape 
    win_h, win_w = kernel_min.shape 
    heatmap = np.zeros(img.shape, dtype='uint16') 
    target = heatmap[win_h//2:-win_h//2+1, win_w//2:-win_w//2+1] 

    for i in range(win_h): 
     for j in range(win_w): 
      # Negative index fails when slicing to the end 
      source = img[i:i+img_h-win_h+1, j:j+img_w-win_w+1] 
      target += (kernel_min[i,j] <= source) & (source <= kernel_max[i,j]) 

    return heatmap 

我目前無法提供具有代表性的時間點,但加速度應該很高。看到窗口大小對性能的影響會很有趣。

+0

這真的很有用。爲了測試加速度,我使用了1035 * 1400像素的8位圖像。 - 隨着我的腳本計算花了約100秒 - 隨着該view_as_windows功能花了大約20秒 - 隨着花了17秒左右 這將節省大量的時間,當我在處理較大的圖像第二種方法。非常感謝! –

+0

當我增加內核(50至70)的大小,並使用相同的圖像: - with_strides = 56秒(和全memmory) - kernel_loops = 32秒 好像第二種方法是更優! –