2013-11-17 3 views
5

Я пытаюсь реализовать функцию, которая будет суммировать (или в конечном итоге среднюю) Каждое окно 3x3 данной матрицы , и создать матрицу на 9x меньше с результатом каждого окна.Суммирование каждого окна 3x3 матрицы M * N в матрицу M/3 * N/3 с numpy

Я не могу найти эффективный и лаконичный способ сделать это с помощью numpy.

Любые идеи?

Спасибо!

+0

scipy.misc.imresize (arr, size, interp = 'bilinear', mode = None) – zenpoy

ответ

7

Простейший метод numpy only, который делает гораздо меньше работы с этой сверткой и, следовательно, скорее всего быстрее, чем методы на основе файлов, - это изменить размер исходного массива на один с дополнительными измерениями, а затем уменьшить его до нормального значения, суммируя его новые размеры:

>>> arr = np.arange(108).reshape(9, 12) 
>>> rows, cols = arr.shape 
>>> arr.reshape(rows//3, 3, cols//3, 3).sum(axis=(1, 3)) 
array([[117, 144, 171, 198], 
     [441, 468, 495, 522], 
     [765, 792, 819, 846]]) 

Если вы хотите, среднее, вы бы просто разделить полученный массив по количеству элементов:

>>> arr.reshape(rows//3, 3, cols//3, 3).sum(axis=(1, 3))/9 
array([[ 13., 16., 19., 22.], 
     [ 49., 52., 55., 58.], 
     [ 85., 88., 91., 94.]]) 

Этот метод работает только если массив имеет форму, которая сама по себе несколько 3.

+1

Я долгое время думал, почему это не работает, получилось sum() обновлено с помощью numpy 1.7 и axis = (1,3) не работал в моей версии (1.6) – mtourne

+0

Да, вы правы. Вы можете получить тот же результат в более ранних версиях, используя '.sum (axis = 3) .sum (axis = 1)' вместо '.sum (axis = (1, 3))'. Обратите внимание, что порядок важен, т. Е. '.Sum (axis = 1) .sum (axis = 3)' вызовет ошибку, потому что после первого суммирования массив является только 3D, и, следовательно, 'axis = 3' выходит за пределы , – Jaime

3

Чтобы добиться именно того, о чем вы просите, я бы применил фильтр-фильтр [3x3] на изображении, и я бы изменил размер матрицы, используя интерполяцию ближайшего соседа.

# Pseudo code 
kernel = np.array([[1/9, 1/9, 1/9], 
        [1/9, 1/9, 1/9], 
        [1/9, 1/9, 1/9]]) 
avg_data= ndimage.convolve(data, kernel) 

smaller_data = scipy.misc.imresize(avg_data, org_size/3, interp='nearest', mode=None) 

В случае, если вы хотите что-то более эффективным - как указано на @Jaime - вы можете сделать что-то вроде этого How can I efficiently process a numpy array in blocks similar to Matlab's blkproc (blockproc) function:

from numpy.lib.stride_tricks import as_strided as ast 

def block_view(A, block= (3, 3)): 
    """Provide a 2D block view to 2D array. No error checking made. 
    Therefore meaningful (as implemented) only for blocks strictly 
    compatible with the shape of A.""" 
    # simple shape and strides computations may seem at first strange 
    # unless one is able to recognize the 'tuple additions' involved ;-) 
    shape= (A.shape[0]/ block[0], A.shape[1]/ block[1])+ block 
    strides= (block[0]* A.strides[0], block[1]* A.strides[1])+ A.strides 
    return ast(A, shape= shape, strides= strides) 

if __name__ == '__main__': 
    B = block_view(A).sum(axis=(2,3)) 

Когда вы пытаетесь понять, что происходит, помните, что шаги представляют количество байтов, которое нам нужно компенсировать в памяти, чтобы перейти к следующей ячейке в каждом измерении. Поэтому, если вы имеете дело с типом данных int32, это будет умножено на 4.

+0

Вы можете сделать последний шаг, нарезая, то есть 'avg_data [1 :: 3, 1 :: 3]' дает вам правильный результат, почти наверняка намного быстрее. И это отчасти указывает, что вам нужно выбросить 89% значений, которые вы вычисляете, поэтому использование фильтра здесь, вероятно, далека от оптимального. – Jaime

+0

Это работает, но я думаю, что правильная сумма - block_view (A) .sum (axis = (2,3)). Кстати, есть ли хорошие объяснения для «где-то» для людей где-то? – mtourne

+0

http://scipy-lectures.github.io/advanced/advanced_numpy/#indexing-scheme-strides – zenpoy

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