2016-08-23 73 views
-1

我有很多750x750图像。我想从每幅图像中获取非重叠5x5块的几何平均值,然后对每幅图像平均使用这些几何平均值来为每幅图像创建一个特征。我写了下面的代码,它似乎工作得很好。但是,我知道这不是很有效率。在300张左右的图片上运行大约需要60秒。我有大约3000张图片。所以,虽然它适用于我的目的,但效率并不高。我怎样才能改进这个代码?如何让此代码更快?

#each sublist of gmeans will contain a list of 22500 geometric means 
#corresponding to the non-overlapping 5x5 patches for a given image. 
gmeans = [[],[],[],[],[],[],[],[],[],[],[],[]] 
#the loop here populates gmeans. 
for folder in range(len(subfolders)): 
    just_thefilename, colorsourceimages, graycroppedfiles = get_all_images(folder) 
    for items in graycroppedfiles: 
     myarray = misc.imread(items) 
     area_of_big_matrix=750*750 
     area_of_small_matrix= 5*5 
     how_many = area_of_big_matrix/area_of_small_matrix 
     n = 0 
     p = 0 
     mylist=[] 
     while len(mylist) < how_many: 
      mylist.append(gmean(myarray[n:n+5,p:p+5],None)) 
      n=n+5 
      if n == 750: 
       p = p+5 
       n = 0 
     gmeans[folder].append(my list) 
#each sublist of mean_of_gmeans will contain just one feature per image, the mean of the geometric means of the 5x5 patches. 
mean_of_gmeans = [[],[],[],[],[],[],[],[],[],[],[],[]] 
for folder in range(len(subfolders)): 
    for items in range(len(gmeans[0])): 
     mean_of_gmeans[folder].append((np.mean(gmeans[folder][items],dtype=np.float64))) 
+1

可能是代码审查合作伙伴网站的一个很好的候选人。不过,请确保先查看他们的网站规则。 – cel

+0

对于这样一个简单的操作,这确实听起来很慢(我认为我的一些代码计算高达4阶累积量,更像直方图均衡更快)。我会做的第一件事:尝试[scikit-image](http://scikit-image.org/),它给你一个名为[view_as_block]的函数(http://scikit-image.org/docs/stable/api /skimage.util.html#view-as-blocks)。这是由一些先进的numpy函数[as_strided]实现的(http://www.scipy-lectures.org/advanced/advanced_numpy/#example-fake-dimensions-with-strides)。我无法保证任何事情,但速度可能会更快! – sascha

+0

代码审查没有太多的'numpy'流量;而“矢量化”是关于SO的常规问题。 CR对格式也很挑剔。 – hpaulj

回答

2

我可以理解的建议,这个移动到代码审查现场, 但这个问题提供了使用矢量 numpy的和SciPy的功能电源的一个很好的例子,所以我会给出一个答案。

下面的函数,巧妙地称为func,计算所需的值。 关键是将图像重塑为四维数组。然后它可以被解释为二维数组的二维阵列,其中内部阵列是5×5块。

scipy.stats.gmean可以计算的几何平均超过一个 尺寸,以便被用于降低四维阵列的几何平均值的 期望的二维数组。返回值是这些几何平均值的 (算术)平均值。

import numpy as np 
from scipy.stats import gmean 


def func(img, blocksize=5): 
    # img must be a 2-d array whose dimensions are divisible by blocksize. 
    if (img.shape[0] % blocksize) != 0 or (img.shape[1] % blocksize) != 0: 
     raise ValueError("blocksize does not divide the shape of img.") 

    # Reshape 'img' into a 4-d array 'blocks', so blocks[i, :, j, :] is 
    # the subarray with shape (blocksize, blocksize). 
    blocks_nrows = img.shape[0] // blocksize 
    blocks_ncols = img.shape[1] // blocksize 
    blocks = img.reshape(blocks_nrows, blocksize, blocks_ncols, blocksize) 

    # Compute the geometric mean over axes 1 and 3 of 'blocks'. This results 
    # in the array of geometric means with size (blocks_nrows, blocks_ncols). 
    gmeans = gmean(blocks, axis=(1, 3), dtype=np.float64) 

    # The return value is the average of 'gmeans'. 
    avg = gmeans.mean() 

    return avg 

例如,此处的函数应用于形状为(750,750)的数组。

In [358]: np.random.seed(123) 

In [359]: img = np.random.randint(1, 256, size=(750, 750)).astype(np.uint8) 

In [360]: func(img) 
Out[360]: 97.035648309350179 

这是不容易验证,这是正确的结果,所以这里是一个小得多的例子:

In [365]: np.random.seed(123) 

In [366]: img = np.random.randint(1, 4, size=(3, 6)) 

In [367]: img 
Out[367]: 
array([[3, 2, 3, 3, 1, 3], 
     [3, 2, 3, 2, 3, 2], 
     [1, 2, 3, 2, 1, 3]]) 

In [368]: func(img, blocksize=3) 
Out[368]: 2.1863131342986666 

这里是直接计算:

In [369]: 0.5*(gmean(img[:,:3], axis=None) + gmean(img[:, 3:], axis=None)) 
Out[369]: 2.1863131342986666 
+0

哇。我只用3000张图像计时了我的功能。炎热的13分钟!你的功能 - 1。惊人。我完全不理解,我相信你的技能超过我的技能。但是,我的循环和你的函数在小数点后的第二个数字结尾。我不知道这是为什么?我在一些小的5x5矩阵上测试了我的循环,并且你的函数也是一样的,最后我们得到了相同的值。看起来这些更大的矩阵发生了一些事情。 – dcook

+0

在处理这件事时,我发现'gmean'做了一些不符合其文档字符串的类型转换。如果您向类型为'np.uint8'的数组传递'gmean'(通常用于图像),则返回的结果的数据类型为'np.float16'!我通过把'img = np.asarray(img,dtype = np。float64)'作为'func()'的第一行,但事实证明你也可以使用它的'dtype'参数来指定'gmean'计算的数据类型。尝试将'gmean'调用更改为'gmean(myarray [n:n + 5,p:p + 5],None,dtype = np.float64)''。我更新了我的版本以使用它。 –