2015-03-08 132 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