2016-08-23 4 views
-1

У меня много 750x750 изображений. Я хочу взять среднее геометрическое из неперекрывающихся патчей 5х5 от каждого изображения, а затем для каждого изображения средние геометрические средства для создания одной функции для каждого изображения. Я написал код ниже, и, похоже, он работает нормально. Но я знаю, что это не очень эффективно. Запуск на 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-го порядка и намного больше похож на выравнивание гистограммы был еще быстрее). Первое, что я сделал бы: try [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

Code-review не имеет большого количества «numpy» трафика; а «векторизация» - это регулярный вопрос о SO. Кроме того, CR является более четким в отношении формата. – hpaulj

ответ

2

Я могу понять предложение, чтобы переместить это сайт обзора кода, но эта проблема дает хороший пример силы с использованием векторизации Numpy и SciPy функции, так что я дам ответ.

Функция, приведенная ниже, умно называется func, вычисляет желаемое значение. Ключ состоит в том, чтобы преобразовать изображение в четырехмерный массив. Затем его можно интерпретировать как двумерный массив двумерных массивов , где внутренние массивы представляют собой блоки 5x5.

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' выполняет некоторые литья типов, которые не согласуются с его docstring. Если вы передадите 'gmean' массив с типом' np.uint8' (типичный для изображения), результат, который он возвращает, имеет тип данных 'np.float16'! Я избегал этого, поставив 'img = np.asarray (img, dtype = np.float64) 'как первую строку' func() ', но, оказывается, вы также можете указать тип данных вычисления' gmean', используя его аргумент 'dtype'. Попробуйте изменить вызов 'gmean' на' gmean (myarray [n: n + 5, p: p + 5], None, dtype = np.float64) '. Я обновил свою версию, чтобы использовать ее. –

Смежные вопросы