Uma outra forma de equacionar o problema de equalizar a distribuição dos pixels de uma imagem é supor que você tem um conjunto de pixels do tamanho da imagem onde a distribuição de pixels seja uniforme.
Pensando num modelo simplificado, imagine que se deseja reproduzir uma fotografia num mosaico construído de ladrilhos onde existe o conjunto de ladrilhos para serem usados. Neste caso da equalização, existe um mesmo número de ladrilhos para cada tom de cinza.
Qual é o procedimento para montar o mosaico e saber exatamente onde se coloca cada ladrilho no mosaico? Existe uma esquema simples e intuitivo:
Podemos fazer o mesmo procedimento de forma computacional, de forma muito mais eficiente, porém usando o mesmo princípio.
Usamos aqui a indexação por arrays, trabalhando sempre com a indexação do
array de forma linearizada. f.ravel()
é a visão da imagem de forma linearizada.
In [1]:
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
import sys,os
ia898path = os.path.abspath('/etc/jupyterhub/ia898_1s2017/')
if ia898path not in sys.path:
sys.path.append(ia898path)
import ia898.src as ia
In [2]:
f = mpimg.imread('../data/cameraman.tif')
ia.adshow(f, 'f: imagem original')
plt.plot(ia.histogram(f)),plt.title('h: histograma original');
Ordena os pixels da imagem original, sabendo-se seu endereço (posição em fsi
).
Isto é obtido com a função argsort
. Veja como se usa o argsort
: tutorial_numpy_argsort argsort
.
In [6]:
fsi = np.argsort(f.ravel())
fs = (f.ravel()[fsi]).reshape(f.shape)
ia.adshow(fs, 'fs: imagem com pixels ordenados')
ia.adshow(ia.normalize(fsi.reshape(f.shape)),'fsi:endereço na imagem original')
Cria uma imagem de mesmas dimensões, porém com os pixels ordenados e com
distribuição uniforme de tons de cinza. Usamos a função linspace
e
depois damos reshape
na imagem para ficar bidimensional. Estes são os ladrilhos
disponíveis:
In [9]:
gs = np.linspace(0,255,f.size).astype(np.uint8)
ia.adshow(gs.reshape(f.shape), 'gs: distribuição uniforme, pixels ordenados')
Agora temos a imagem original ordenada e os tons de cinza uniformemente distribuídos, também ordenados:
In [10]:
nb=ia.nbshow(3)
nb.nbshow(fs,'fs: imagem original pixels ordenados')
nb.nbshow(ia.normalize(fsi.reshape(f.shape)),'fsi:endereço na imagem original')
nb.nbshow(gs.reshape(f.shape),'gs: distribuição uniforme desejada, pixels ordenados')
nb.nbshow()
Como sabemos o endereço original destes pixels, pois são indicados pelo
argsort
feito acima, podemos agora atribuir os pixels ordenados para os
pixels ordenados da imagem uniforme.
In [12]:
g = np.empty( (f.size,), np.uint8)
g[fsi] = gs
Pronto, o mosaico está montado e a image g está equalizada.
In [13]:
ia.adshow(g.reshape(f.shape),'g[fsi] = gs, imagem equalizada')
Para mostrar o seu histograma e comprovar a equalização feita:
In [16]:
h = ia.histogram(g)
plt.bar( np.arange(h.size), h)
plt.title('histograma de g');
Out[16]:
Para facilitar o entendimento, é repetido a seguir o mesmo procedimento acima, porém com uma imagem numérica de uma dimensão com 16 pixels de valores entre 0 a 7. Fazendo a analogia com a construção do mosaico, existem 2 pastilhas de cada tom de cinza entre 0 a 7 para construir o mosaico com 16 pastilhas.
Calculando a imagem ordenada de f
e o endereço dos pixels fsi
para ordenar f
:
In [17]:
f = np.array([1, 7, 3, 0, 2, 2, 4, 3, 2, 0, 5, 3, 7, 7, 7, 5])
h = ia.histogram(f)
fsi = np.argsort(f)
fs = f[fsi]
print('imagem original f :',f)
print('indices para ordenar fsi:',fsi)
print('f c/pixels ordenados fs :',fs)
print('histogram h: h :',h)
Pastilhas disponíveis para o mosaico: 2 pastilhas de cada tom de cinza:
In [19]:
gs = np.linspace(0,7,f.size).round(0).astype(np.int)
print('ladrilhos ordenados, gs :', gs)
Mapeando as pastilhas no mosaico final (gs
), utilizando o endereço dos pixels fsi
:
In [21]:
print('ladrilhos disponíveis gs:',gs)
print('endereço para colocar cada ladrilho fsi:',fsi)
g = np.empty( (f.size,), np.uint8)
g[fsi] = gs
print('mosaico montado g[fsi] = gs:',g)
Para entender como g[fsi]=gs
é calculado, veja as atribuições elemento a elemento:
In [22]:
print('g[fsi]= gs')
for i in np.arange(g.size):
print('g[%d] = %d' % (fsi[i],gs[i]))
Imagem original e com histograma equalizado (mosaico):
In [23]:
print('imagem usando os ladrilhos g:',g)
print('imagem original: f:',f)
Histograma da imagem equalizada (mosaico):
In [25]:
print('histograma de g:', ia.histogram(g))