Indexando o raster de um array usando strides

Quando os pixels da imagem são armazenados de forma contígua na forma raster unidimensional, o cálculo da indexação N-dimensional $(n_0, n_1, ..., n_{N-1})$ é dada pelo offset:

$$ n_{\mathrm{offset}} = K_{\mathrm{offset}} + \sum_{k=0}^{N-1} s_k n_k $$

onde $s_k$ é o stride da dimensão $k$.

Neste notebook iremos verificar como o stride e $K_{offset}$ são modificados para implementar operações como espelhamento e transposta.


In [1]:
import numpy as np

Vamos criar um array com 4 linhas e 6 colunas criados com o arange assim, o valor de cada pixel é o valor de seu offset raster unidimensional:


In [2]:
H,W = (4,6)
a = np.arange(H*W).reshape(H,W).astype(np.uint8)
print(a)
print(a.size)


[[ 0  1  2  3  4  5]
 [ 6  7  8  9 10 11]
 [12 13 14 15 16 17]
 [18 19 20 21 22 23]]
24

Stride de um array

O stride de um array normal raster é dado por $(W,1)$ e $K_{offset} = 0$;

$$ n_{\mathrm{offset}} = 0 + W n_0 + n_1 $$

onde $n_0$ é o índice das linhas e $n_1$ é o índice das colunas.

No exemplo do array a de dimensões (4,6), o stride é dado por (W,1):


In [3]:
print(a.strides)


(6, 1)

Assim, a indexação [2,3] é igual ao 15:


In [4]:
a[2,3]


Out[4]:
15

In [5]:
(a.strides * np.array([2,3])).sum()


Out[5]:
15

Produto cartesiano entre 2 conjuntos - coordenadas bidimensionais


In [8]:
Rows = np.arange(H)
Cols = np.arange(W)
index = []
for i in Rows:
    for j in Cols:
        index.append([i,j])
print(index)
index_all = np.array(index)
print(index_all)


[[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [0, 5], [1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [1, 5], [2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [3, 0], [3, 1], [3, 2], [3, 3], [3, 4], [3, 5]]
[[0 0]
 [0 1]
 [0 2]
 [0 3]
 [0 4]
 [0 5]
 [1 0]
 [1 1]
 [1 2]
 [1 3]
 [1 4]
 [1 5]
 [2 0]
 [2 1]
 [2 2]
 [2 3]
 [2 4]
 [2 5]
 [3 0]
 [3 1]
 [3 2]
 [3 3]
 [3 4]
 [3 5]]

In [9]:
i = (a.strides * index_all).sum(axis=1)
print(i)


[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]

Indexação com espelhamento na vertical

$$ i = (T-W) -W row + 1 col $$

In [10]:
b = a[::-1,:]
print(b.strides)
print(b.size)
print(b.ravel())


(-6, 1)
24
[18 19 20 21 22 23 12 13 14 15 16 17  6  7  8  9 10 11  0  1  2  3  4  5]

In [11]:
b[2,1]


Out[11]:
7

In [12]:
np.sum(b.strides * np.array([2,1])) + b.size- b.shape[1]


Out[12]:
7

In [13]:
(b.strides * index_all).sum(axis=1) + b.size - b.shape[1]


Out[13]:
array([18, 19, 20, 21, 22, 23, 12, 13, 14, 15, 16, 17,  6,  7,  8,  9, 10,
       11,  0,  1,  2,  3,  4,  5])

Matriz transposta

$$ i = row + W col $$

In [14]:
c = a.T
print(c)
print(c.strides)
print(c.ravel())


[[ 0  6 12 18]
 [ 1  7 13 19]
 [ 2  8 14 20]
 [ 3  9 15 21]
 [ 4 10 16 22]
 [ 5 11 17 23]]
(1, 6)
[ 0  6 12 18  1  7 13 19  2  8 14 20  3  9 15 21  4 10 16 22  5 11 17 23]

In [65]:
(c.strides * index_all).sum(axis=1)


Out[65]:
array([ 0,  6, 12, 18, 24, 30,  1,  7, 13, 19, 25, 31,  2,  8, 14, 20, 26,
       32,  3,  9, 15, 21, 27, 33])

Exercícios

  1. Calcule o stride e offset de um array rotacionado de 90 graus no sentido horário. Verifique se está correto calculando todos índices rasters e comparando com o raster da imagem rotacionada.
  2. Calcule os o stride, offset e novo shape do array d usado para subamostrar uma imagem utilizando o fatiamento: d = a[::2,::2]