Использование Numpy stride_tricks для получения блоков без перекрывания блоков
Я пытаюсь использовать numpy.lib.stride_tricks.as_strided для итерации по неперекрывающимся блокам массива, но мне трудно найти документацию о параметрах, поэтому мне удалось получить перекрывающиеся блоки.
Например, у меня есть массив 4x5, из которого я бы хотел получить 4 блока 2x2. Я в порядке с лишними ячейками справа и снизу.
До сих пор мой код:
import sys
import numpy as np
a = np.array([
[1,2,3,4,5],
[6,7,8,9,10],
[11,12,13,14,15],
[16,17,18,19,20],
])
sz = a.itemsize
h,w = a.shape
bh,bw = 2,2
shape = (h/bh, w/bw, bh, bw)
strides = (w*sz, sz, w*sz, sz)
blocks = np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
print blocks[0][0]
assert blocks[0][0].tolist() == [[1, 2], [6,7]]
print blocks[0][1]
assert blocks[0][1].tolist() == [[3,4], [8,9]]
print blocks[1][0]
assert blocks[1][0].tolist() == [[11, 12], [16, 17]]
Форма результирующего массива блоков кажется правильной, но последние два утверждения терпят неудачу, по-видимому, потому, что мои параметры формы или шага неверны. Какие значения для них я должен установить для получения неперекрывающихся блоков?
Ответы
Ответ 1
import numpy as np
n=4
m=5
a = np.arange(1,n*m+1).reshape(n,m)
print(a)
# [[ 1 2 3 4 5]
# [ 6 7 8 9 10]
# [11 12 13 14 15]
# [16 17 18 19 20]]
sz = a.itemsize
h,w = a.shape
bh,bw = 2,2
shape = (h/bh, w/bw, bh, bw)
print(shape)
# (2, 2, 2, 2)
strides = sz*np.array([w*bh,bw,w,1])
print(strides)
# [40 8 20 4]
blocks=np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
print(blocks)
# [[[[ 1 2]
# [ 6 7]]
# [[ 3 4]
# [ 8 9]]]
# [[[11 12]
# [16 17]]
# [[13 14]
# [18 19]]]]
Начиная с 1
в a
(т.е. blocks[0,0,0,0]
), добраться до 2
(т.е. blocks[0,0,0,1]
) - это один элемент. Поскольку (на моей машине) a.itemsize
составляет 4 байта, шаг 1 * 4 = 4. Это дает нам последнее значение в strides = (10,2,5,1)*a.itemsize = (40,8,20,4)
.
Начиная с 1
снова, чтобы добраться до 6
(т.е. blocks[0,0,1,0]
), удаляется 5 (т.е. w
) элементов, поэтому шаг 5 * 4 = 20. Это объясняет от второго до последнего значения в strides
.
Начиная с 1
еще раз, чтобы добраться до 3
(т.е. blocks[0,1,0,0]
), уходит 2 (т.е. bw
), поэтому шаг 2 * 4 = 8. Это объясняет второе значение в strides
.
Наконец, начиная с 1
, чтобы добраться до 11
(т.е. blocks[1,0,0,0]
), удаляется 10 (т.е. w*bh
) элементов, поэтому шаг равен 10 * 4 = 40. Итак strides = (40,8,20,4)
.
Ответ 2
Используя ответ @unutbu в качестве примера, я написал функцию, которая реализует этот трюк для любого ND-массива. См. Ниже ссылку на источник.
>>> a = numpy.arange(1,21).reshape(4,5)
>>> print a
[[ 1 2 3 4 5]
[ 6 7 8 9 10]
[11 12 13 14 15]
[16 17 18 19 20]]
>>> blocks = blockwise_view(a, blockshape=(2,2), require_aligned_blocks=False)
>>> print blocks
[[[[ 1 2]
[ 6 7]]
[[ 3 4]
[ 8 9]]]
[[[11 12]
[16 17]]
[[13 14]
[18 19]]]]
[ blockwise_view.py
] [ test_blockwise_view.py
]
Ответ 3
scikit-image имеет функцию с именем view_as_blocks()
это делает практически то, что вам нужно. Единственная проблема заключается в том, что у него есть дополнительный assert
, который запрещает ваш прецедент, так как ваши блоки не делятся равномерно на вашу форму массива. Но в вашем случае assert
не требуется, поэтому вы можете скопировать исходный код функции и безопасно удалить отвратительное утверждение самостоятельно.