2015-03-08 45 views
8

我正在研究需要獲取圖像方差的項目。 目前我正在服用2層的方法(這兩個工作,但速度很慢):有效計算圖像python的方差

  1. 計算方差爲每個單獨的像素:

這是使用numpy的代碼,varianceMatrix是輸出

varianceMatrix = np.zeros(im.shape,np.uint8) 
w = 1    # the radius of pixels neighbors 
ny = len(im) 
nx = len(im[0]) 


for i in range(w,nx-w): 
    for j in range(w,ny-w): 

     sampleframe = im[j-w:j+w, i-w:i+w] 
     variance = np.var(sampleframe) 
     varianceMatrix[j][i] = int(variance) 

return varianceMatrix 
  • 使用現有SciPy的功能:
  • 這是SciPy的功能:

    from scipy import ndimage 
    
    varianceMatrix = ndimage.generic_filter(im, np.var, size = 3) 
    

    的SciPy的功能也比較快,但沒有這麼多。我正在尋找更好的方法來計算方差。

    任何想法???

    回答

    0

    如果使用ndimage.generic_filter的方法速度不夠快,您可以在Cython中編寫自己的優化方差計算實施方案。

    1

    您可以使用衆所周知的sliding window stride trick來加速計算。它將兩個「虛擬維度」添加到數組的末尾而不復制數據,然後計算它們之間的差異。

    請注意,在您的代碼中,im[j-w:j+w, ..]超過索引j-w,j-w+1,...,j+w-1,最後一個是排他性的,您可能並不是這個意思。此外,差異大於uint8範圍,所以最終以整數環繞結束。

    import numpy as np 
    import time 
    np.random.seed(1234) 
    
    img = (np.random.rand(200, 200)*256).astype(np.uint8) 
    
    def sliding_window(a, window, axis=-1): 
        shape = list(a.shape) + [window] 
        shape[axis] -= window - 1 
        if shape[axis] < 0: 
         raise ValueError("Array too small") 
        strides = a.strides + (a.strides[axis],) 
        return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides) 
    
    def sliding_img_var(img, window): 
        if window <= 0: 
         raise ValueError("invalid window size") 
        buf = sliding_window(img, 2*window, 0) 
        buf = sliding_window(buf, 2*window, 1) 
    
        out = np.zeros(img.shape, dtype=np.float32) 
        np.var(buf[:-1,:-1], axis=(-1,-2), out=out[window:-window,window:-window]) 
        return out 
    
    def looping_img_var(im, w): 
        nx, ny = img.shape 
        varianceMatrix = np.zeros(im.shape, np.float32) 
        for i in range(w,nx-w): 
         for j in range(w,ny-w): 
          sampleframe = im[j-w:j+w, i-w:i+w] 
          variance = np.var(sampleframe) 
          varianceMatrix[j][i] = variance 
        return varianceMatrix 
    
    np.set_printoptions(linewidth=1000, edgeitems=5) 
    start = time.time() 
    print(sliding_img_var(img, 1)) 
    time_sliding = time.time() - start 
    start = time.time() 
    print(looping_img_var(img, 1)) 
    time_looping = time.time() - start 
    print("duration: sliding: {0} s, looping: {1} s".format(time_sliding, time_looping)) 
    
    +0

    這裏是我機器上輸出的最後一行,顯示了加速:'duration:sliding:0.00510311126709 s,looping:0.955919027328 s'。 – 2016-03-28 15:19:34

    4

    這裏使用的OpenCV一個快速的解決方案:

    import cv2 
    
    def winVar(img, wlen): 
        wmean, wsqrmean = (cv2.boxFilter(x, -1, (wlen, wlen), 
        borderType=cv2.BORDER_REFLECT) for x in (img, img*img)) 
        return wsqrmean - wmean*wmean 
    

    在我的機器,並在下面的例子中,winVar()ndimage.generic_filter()快2915倍和10.8更快倍sliding_img_var()(參見PV的答案):

    In [66]: img = np.random.randint(0, 256, (500,500)).astype(np.float) 
    
    In [67]: %timeit winVar(img, 3) 
    100 loops, best of 3: 1.76 ms per loop 
    
    In [68]: %timeit ndimage.generic_filter(img, np.var, size=3) 
    1 loops, best of 3: 5.13 s per loop 
    
    In [69]: %timeit sliding_img_var(img, 1) 
    100 loops, best of 3: 19 ms per loop 
    

    結果匹配的ndimage.generic_filter()

    In [70]: np.allclose(winVar(img, 3), ndimage.generic_filter(img, np.var, size=3)) 
    Out[70]: True 
    
    +1

    'ndimage.uniform_filter()'可以用來代替'cv2.boxFilter()',參見[這個答案](http://stackoverflow.com/a/33497963/1628638)到一個類似的問題。對於我在這裏使用的例子,OpenCV版本的速度提高了4.1倍。 – 2016-03-30 14:58:12

    +1

    爲了計算標準偏差(即將'winVar()'轉換爲'winStd()'),只需改爲'return np.sqrt(wsqrmean - wmean * wmean)'。 – 2016-05-11 19:19:04