[英]Calculating variance image python
Is there an easy way to calculate a running variance filter on an image using Python/NumPy/Scipy? 有没有一种简单的方法可以使用Python / NumPy / Scipy计算图像上的运行方差滤镜? By running variance image I mean the result of calculating sum((I - mean(I))^2)/nPixels for each sub-window I in the image. 通过运行方差图像,I表示计算图像中每个子窗口I的和((I - mean(I))^ 2)/ nPixels的结果。
Since the images are quite large (12000x12000 pixels), I want to avoid the overhead of converting the arrays between formats just to be able to use a different library and then convert back. 由于图像非常大(12000x12000像素),我想避免在格式之间转换数组的开销,只是为了能够使用不同的库然后转换回来。
I guess I could do this manually by finding the mean using something like 我想我可以通过使用类似的东西找到平均值来手动执行此操作
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
but it would be much nicer to have something like the stdfilt-function from Matlab. 但是从matlab获得类似stdfilt-function的东西要好得多。
Can anyone point me in the direction of a library that has this functionality AND supports numpy arrays, or hint at/provide a way to do this in NumPy/SciPy? 任何人都可以指向具有此功能的库并且支持numpy数组的方向,或提示/提供在NumPy / SciPy中执行此操作的方法吗?
Simpler solution and also faster: use SciPy's ndimage.uniform_filter
更简单的解决方案也更快:使用SciPy的ndimage.uniform_filter
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
The "stride trick" is beautiful trick, but 4 slower and not that readable. “步幅技巧”是一个很好的技巧,但是速度慢4而且不那么可读。 the generic_filter
is 20 times slower than the strides... generic_filter
比步幅慢20倍......
You can use numpy.lib.stride_tricks.as_strided
to get a windowed view of your image: 您可以使用numpy.lib.stride_tricks.as_strided
来获取图像的窗口视图:
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)
And now win_img[i, j]
is the (win_rows, win_cols)
array with the top left corner at position [i, j]
: 现在win_img[i, j]
是(win_rows, win_cols)
数组,左上角位于[i, j]
位置:
>>> 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]])
You have to be careful, though, with not converting your windowed view of the image, into a windowed copy of it: in my example that would require 25 times more storage. 但是,您必须小心,不要将窗口的窗口视图转换为窗口的副本 :在我的示例中,需要25倍的存储空间。 I believe numpy 1.7 lets you select more than one axis, so you could then simply do: 我相信numpy 1.7允许你选择多个轴,所以你可以简单地做:
>>> np.var(win_img, axis=(-1, -2))
I am stuck with numpy 1.6.2, so I cannot test that. 我陷入了numpy 1.6.2,所以我无法测试。 The other option, which may fail with not-so-large windows, would be to do, if I remember my math correctly: 如果我正确地记住我的数学,那么另一个选项可能会因为没有那么大的窗口而失败。
>>> 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
And now win_var
is an array of shape 现在win_var
是一个形状的数组
>>> win_var.shape
(496, 496)
and win_var[i, j]
holds the variance of the (5, 5)
window with top left corner at [i, j]
. 和win_var[i, j]
保持的方差(5, 5)
窗口左上角在[i, j]
After a bit of optimization we came up with this function for a generic 3D image: 经过一些优化后,我们为通用3D图像提出了这个功能:
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')
You can use scipy.ndimage.generic_filter
. 您可以使用scipy.ndimage.generic_filter
。 I can't test with matlab, but perhaps this gives you what you're looking for: 我不能用matlab测试,但也许这会给你你想要的东西:
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)
You can make the sub-windows of arbitrary sizes using the footprint
keyword. 您可以使用footprint
关键字创建任意大小的子窗口。 See this question for an example. 请参阅此问题以获取示例。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.