Processamento de imagens usando fatiamento do Numpy

Uma introdução sobre como representar, ler e exibir imagens no Adessowiki podem ser vista em:

  • master:tutorial_img_ds Representação, Leitura e Visualização de Imagens no Adessowiki.

O conceito de fatiamento (slicing) do Numpy é um dos mais importantes para processamento de imagens, tanto pela sua versatilidade como pela sua eficiência. Reunimos nesta página um conjunto de processamento de imagens utilizando quase que exclusivamente operações de fatiamento.

Para entender melhor como o fatiamento funciona, recomenda-se ver uma explicação didática do fatiamento:

  • tutorial_numpy_1_2 Fatiamentos unidimensionais
  • tutorial_numpy_1_3 Fatiamentos bidimensionais

Sobrepondo reticulado


In [59]:
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
from PIL import Image
from IPython.display import display

In [61]:
f = mpimg.imread('../data/cameraman.tif').copy()
display(Image.fromarray(f))



In [17]:
f[::10,:] = 255 # linhas horizontais
f[:,::10] = 255 # linhas verticais
Image.fromarray(f)


Out[17]:

Sobrepondo frame preto na imagem


In [20]:
f = mpimg.imread('../data/cameraman.tif').copy()
display(Image.fromarray(f))
f[   :10,   :  ] = 0 # frame superior
f[-10:  ,   :  ] = 0 # frame inferior
f[   :  ,   :10] = 0 # frame esquerdo
f[   :  ,-10:  ] = 0 # frame direito
display(Image.fromarray(f))


Rotação 90 graus

Uma técnica simples para se fazer uma rotação antihorária da matriz, é calcular a matriz transposta e depois refleti-la na vertical:


In [22]:
f1 = mpimg.imread('../data/cameraman.tif').copy()
f = f1[:,64:192]
print('original shape=%s' % (f.shape,))
Image.fromarray(f)


original shape=(256, 128)
Out[22]:

In [23]:
g = f.transpose()
print('transposta shape=%s' % (g.shape,))
Image.fromarray(g)


transposta shape=(128, 256)
Out[23]:

In [24]:
gr = g[::-1,:]
#'reflete na vertical'
Image.fromarray(gr)


Out[24]:

In [25]:
gr[:10,:20] = 0
Image.fromarray(gr)


Out[25]:

Subamostragem


In [27]:
g = f1[::2,::2]
print('shape=%s' % (g.shape,) )
display(Image.fromarray(f1))
Image.fromarray(g)


shape=(128, 128)
Out[27]:

Ampliação


In [29]:
f = mpimg.imread('../data/keyb.tif').copy()[:50,:80]
print('original %s' % (f.shape,) )
Image.fromarray(f)


original (50, 80)
Out[29]:

In [30]:
H,W = f.shape
g = np.zeros( (2*H,2*W), 'uint8')
g[ ::2, ::2] = f
g[1::2, ::2] = f
g[1::2,1::2] = f
g[ ::2,1::2] = f
plt.imshow(g, cmap='gray')
print('ampliada por replicação %s' % (g.shape,) )
print(g)


ampliada por replicação (100, 160)
[[156 156 165 ..., 170 178 178]
 [156 156 165 ..., 170 178 178]
 [173 173 165 ..., 173 181 181]
 ..., 
 [165 165 173 ..., 178 165 165]
 [156 156 165 ..., 173 181 181]
 [156 156 165 ..., 173 181 181]]

Separando campos pares e impares entrelaçados


In [31]:
f = mpimg.imread('../data/tvframe.pgm')[100:350,100:300]
plt.imshow(f, cmap='gray')
print('original com dois campos')


original com dois campos

In [32]:
g_par = f[::2,:]
plt.imshow(g_par,cmap='gray')


Out[32]:
<matplotlib.image.AxesImage at 0x11cb81860>

In [33]:
g_impar = f[1::2,:]
plt.imshow(g_impar,cmap='gray')


Out[33]:
<matplotlib.image.AxesImage at 0x11cd98240>

In [34]:
ns1 = np.array(g_par.shape)
ns = ns1 + np.array([g_impar.shape[0], 0])
print(tuple(ns))


(250, 200)

In [35]:
g = np.empty(ns)
g[:g_par.shape[0]] = g_par
g[g_par.shape[0]:] = g_impar
plt.imshow(g,cmap='gray')


Out[35]:
<matplotlib.image.AxesImage at 0x11ce80eb8>

Reescrevendo o código para torná-lo mais legível


In [40]:
H_par,W_par = g_par.shape
H_impar,W_impar = g_impar.shape
F = np.empty((H_par+H_impar, W_par))
F[:H_par] = g_par
F[H_par:] = g_impar
plt.imshow(F,cmap='gray')


Out[40]:
<matplotlib.image.AxesImage at 0x11c6e48d0>

In [41]:
g_even = np.zeros_like(f)
g_even[::2] = f[::2]
g_even[1::2] = f[::2]
display(Image.fromarray(g_even))
print('campo linhas pares')


campo linhas pares

In [43]:
g_odd = np.zeros_like(f)
g_odd[::2]  = f[1::2]
g_odd[1::2] = f[1::2]
print('campo linhas ímpares')
Image.fromarray(g_odd)


campo linhas ímpares
Out[43]:

Combinando duas imagens linhas pares de uma e ímpares de outra


In [44]:
f1 = mpimg.imread('../data/woodlog.tif')
plt.imshow(f1,cmap='gray')
print('f1: woodlog')


f1: woodlog

In [45]:
f2 = mpimg.imread('../data/keyb.tif')
plt.imshow(f2,cmap='gray')
print('f2: keyb')


f2: keyb

In [46]:
H,W = np.minimum(np.array(f1.shape),np.array(f2.shape))
g = np.empty((H,W))
g[::2,:] = f1[:H:2,:W]
g[1::2,:] = f2[1:H:2,:W]
plt.imshow(g[:,:],cmap='gray')
print('linhas ímpares de f1 e pares de f2')


linhas ímpares de f1 e pares de f2

In [47]:
H,W = np.minimum(np.array(f1.shape),np.array(f2.shape))
g = f1[:H,:W]//2 + f2[:H,:W]//2
plt.imshow(g,cmap='gray')
plt.colorbar()
print('combinando f1 + f2 usando cálculo em inteiros')


combinando f1 + f2 usando cálculo em inteiros

In [48]:
H,W = np.minimum(np.array(f1.shape),np.array(f2.shape))
g = f1[:H,:W]/f1.max() + f2[:H,:W]/f2.max()
plt.imshow(g,cmap='gray')
plt.colorbar()
print('combinando f1 + f2 usando ponto flutuante')


combinando f1 + f2 usando ponto flutuante

Montagem e reflexão vertical e horizontal


In [49]:
f = mpimg.imread('../data/keyb.tif')[:50,:50]
plt.imshow(f,cmap='gray')
print('original')


original

In [50]:
H,W = f.shape
g = np.empty( (2*H,2*W), 'uint8')
g[:H,:W] = f          # original no quadrante superior esquerdo
g[H:,:W] = f[::-1,:]  # refletida vertical no quadrante inferior esquerdo
g[:H,W:] = f[:,::-1]  # refletida horizontal no quadrante superior direito
g[H:,W:] = f[::-1,::-1] # refletida vert. e hor. no quadrante inferior direito
plt.imshow(g,cmap='gray') 
print('refletidas')


refletidas

Translação

Uma operação primitiva que é utilizada na construção de várias outras operações é a translação de uma imagem por um fator de deslocamento na vertical e na horizontal. Numpy permite fazer translações extremamente eficientes utilizando fatiamento. O exemplo abaixo, a imagem g é deslocada de (dH,dW).


In [51]:
H,W = 100,200
f = np.zeros((H,W),'uint8')
f[::10,:] = 255
f[:,::10] = 255
plt.imshow(f,cmap='gray')
print('reticulado de passo 10 pixels')


reticulado de passo 10 pixels

In [55]:
g = mpimg.imread('../data/lenina.pgm').copy()
plt.imshow(g,cmap='gray')


Out[55]:
<matplotlib.image.AxesImage at 0x11d45bac8>

In [56]:
f = g[:H,:W]
plt.imshow(f,cmap='gray')
print('imagem g na origem (0,0)')


imagem g na origem (0,0)

In [58]:
dH,dW = (50,100)
f[dH:H+dH,dW:W+dW] = g[:H,:W]
adshow(f, 'imagem g deslocada por (%d,%d)' % (dH,dW))


---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-58-2bda4bcb5d87> in <module>()
      1 dH,dW = (50,100)
----> 2 f[dH:H+dH,dW:W+dW] = g[:H,:W]
      3 adshow(f, 'imagem g deslocada por (%d,%d)' % (dH,dW))

ValueError: could not broadcast input array from shape (100,200) into shape (50,100)

Outros exemplos

  • ia636:iaprofiledemo - Ilustração para observar valores de cinza linha vertical na imagem

In [ ]: