Calculating variance image python
有没有一种简单的方法可以使用 Python/NumPy/Scipy 计算图像上的运行方差过滤器?通过运行方差图像,我的意思是计算图像中每个子窗口 I 的 sum((I - mean(I))^2)/nPixels 的结果。
由于图像非常大(12000x12000 像素),我想避免在格式之间转换数组的开销,以便能够使用不同的库然后再转换回来。
我想我可以通过使用类似
的方法找到平均值来手动执行此操作
1 2 3 4 5 | kernel = np.ones((winSize, winSize))/winSize**2 image_mean = scipy.ndimage.convolve(image, kernel) diff = (image - image_mean)**2 # Calculate sum over winSize*winSize sub-images # Subsample result |
但是如果有像 Matlab 中的 stdfilt 函数这样的东西会更好。
任何人都可以指出具有此功能并支持 numpy 数组的库的方向,或者提示/提供一种在 NumPy/SciPy 中执行此操作的方法吗?
更简单的解决方案也更快:使用 SciPy\\'s
1 2 3 4 5 6 7 8 9 | import numpy as np from scipy import ndimage rows, cols = 500, 500 win_rows, win_cols = 5, 5 img = np.random.rand(rows, cols) win_mean = ndimage.uniform_filter(img, (win_rows, win_cols)) win_sqr_mean = ndimage.uniform_filter(img**2, (win_rows, win_cols)) win_var = win_sqr_mean - win_mean**2 |
"跨步技巧"是个漂亮的技巧,但速度慢了 4 且不那么可读。
您可以使用
1 2 3 4 5 6 7 8 9 10 11 | import numpy as np from numpy.lib.stride_tricks import as_strided rows, cols = 500, 500 win_rows, win_cols = 5, 5 img = np.random.rand(rows, cols) win_img = as_strided(img, shape=(rows-win_rows+1, cols-win_cols+1, win_rows, win_cols), strides=img.strides*2) |
现在
1 2 3 4 5 6 7 8 9 10 11 12 | >>> img[100:105, 100:105] array([[ 0.34150754, 0.17888323, 0.67222354, 0.9020784 , 0.48826682], [ 0.68451774, 0.14887515, 0.44892615, 0.33352743, 0.22090103], [ 0.41114758, 0.82608407, 0.77190533, 0.42830363, 0.57300759], [ 0.68435626, 0.94874394, 0.55238567, 0.40367885, 0.42955156], [ 0.59359203, 0.62237553, 0.58428725, 0.58608119, 0.29157555]]) >>> win_img[100,100] array([[ 0.34150754, 0.17888323, 0.67222354, 0.9020784 , 0.48826682], [ 0.68451774, 0.14887515, 0.44892615, 0.33352743, 0.22090103], [ 0.41114758, 0.82608407, 0.77190533, 0.42830363, 0.57300759], [ 0.68435626, 0.94874394, 0.55238567, 0.40367885, 0.42955156], [ 0.59359203, 0.62237553, 0.58428725, 0.58608119, 0.29157555]]) |
但是,您必须小心,不要将图像的窗口视图转换为它的窗口副本:在我的示例中,这将需要 25 倍的存储空间。我相信 numpy 1.7 可以让您选择多个轴,因此您可以简单地执行以下操作:
1 | >>> np.var(win_img, axis=(-1, -2)) |
我被 numpy 1.6.2 卡住了,所以我无法测试它。如果我没记错我的数学,另一个选项可能会因窗口不太大而失败:
1 2 3 | >>> win_mean = np.sum(np.sum(win_img, axis=-1), axis=-1)/win_rows/win_cols >>> win_sqr_mean = np.sum(np.sum(win_img**2, axis=-1), axis=-1)/win_rows/win_cols >>> win_var = win_sqr_mean - win_mean**2 |
现在
1 2 | >>> win_var.shape (496, 496) |
和
经过一些优化,我们想出了一个通用 3D 图像的函数:
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 | def variance_filter( img, VAR_FILTER_SIZE ): from numpy.lib.stride_tricks import as_strided WIN_SIZE=(2*VAR_FILTER_SIZE)+1 if ~ VAR_FILTER_SIZE%2==1: print 'Warning, VAR_FILTER_SIZE must be ODD Integer number ' # hack -- this could probably be an input to the function but Alessandro is lazy WIN_DIMS = [ WIN_SIZE, WIN_SIZE, WIN_SIZE ] # Check that there is a 3D image input. if len( img.shape ) != 3: print"\\t variance_filter: Are you sure that you passed me a 3D image?" return -1 else: DIMS = img.shape # Set up a windowed view on the data... this will have a border removed compared to the img_in img_strided = as_strided(img, shape=(DIMS[0]-WIN_DIMS[0]+1, DIMS[1]-WIN_DIMS[1]+1, DIMS[2]-WIN_DIMS[2]+1, WIN_DIMS[0], WIN_DIMS[1], WIN_DIMS[2] ), strides=img.strides*2) # Calculate variance, vectorially win_mean = numpy.sum(numpy.sum(numpy.sum(img_strided, axis=-1), axis=-1), axis=-1) / (WIN_DIMS[0]*WIN_DIMS[1]*WIN_DIMS[2]) # As per http://en.wikipedia.org/wiki/Variance, we are removing the mean from every window, # then squaring the result. # Casting to 64 bit float inside, because the numbers (at least for our images) get pretty big win_var = numpy.sum(numpy.sum(numpy.sum((( img_strided.T.astype('<f8') - win_mean.T.astype('<f8') )**2).T, axis=-1), axis=-1), axis=-1) / (WIN_DIMS[0]*WIN_DIMS[1]*WIN_DIMS[2]) # Prepare an output image of the right size, in order to replace the border removed with the windowed view call out_img = numpy.zeros( DIMS, dtype='<f8' ) # copy borders out... out_img[ WIN_DIMS[0]/2:DIMS[0]-WIN_DIMS[0]+1+WIN_DIMS[0]/2, WIN_DIMS[1]/2:DIMS[1]-WIN_DIMS[1]+1+WIN_DIMS[1]/2, WIN_DIMS[2]/2:DIMS[2]-WIN_DIMS[2]+1+WIN_DIMS[2]/2, ] = win_var # output return out_img.astype('>f4') |
您可以使用
1 2 3 4 5 | import numpy as np import scipy.ndimage as ndimage subs = 10 # this is the size of the (square) sub-windows img = np.random.rand(500, 500) img_std = ndimage.filters.generic_filter(img, np.std, size=subs) |
您可以使用