计算方差图像python

2024-06-25 05:55:29 发布

您现在位置:Python中文网/ 问答频道 /正文

有没有一种简单的方法可以使用Python/NumPy/Scipy计算图像上运行的方差过滤器?通过运行variance image,我是指计算图像中每个子窗口I的sum((I-mean(I))^2)/nPixels的结果。

由于图像非常大(12000x12000像素),我希望避免在格式之间转换数组的开销,以便能够使用不同的库,然后再转换回来。

我想我可以用类似于

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中提示/提供这样做的方法吗?


Tags: 方法图像imagenumpy过滤器scipy数组mean
3条回答

您可以使用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)

现在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]])

不过,您必须小心,不要将图像的窗口化视图转换为窗口化副本:在我的示例中,这需要25倍的存储空间。我相信numpy 1.7允许您选择多个轴,因此您可以简单地:

>>> np.var(win_img, axis=(-1, -2))

我被Numpy1.6.2困住了,所以我无法测试它。另一个可能在没有这么大的窗口时失败的选择是,如果我正确地记住了我的数学公式:

>>> 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

现在win_var是一个形状数组

>>> win_var.shape
(496, 496)

并且win_var[i, j]保持(5, 5)窗口的方差,左上角位于[i, j]

更简单且更快的解决方案:使用SciPy的^{}

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慢,不可读。 generic_filter比跨步慢20倍。。。

经过一些优化之后,我们为一个通用的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')

相关问题 更多 >