Transformações de intensidade modificam o valor do pixel de acordo com uma equação ou mapeamento. Estas transformações são ditas pontuais para contrastar com operações ditas de vizinhança. Um exemplo de transformação de intensidade é uma operação que divide o valor dos pixels por 2. O resultado será uma nova imagem onde todos os pixels serão mais escuros.
A transformação de intensidade tem a forma $s = T(v)$, onde $v$ é um valor de nível de cinza de entrada e s é o valor de nível de cinza na saída. Este tipo de mapeamento pode apresentar muitas denominações: transformação de contraste, lookup table, tabela ou mapa de cores, etc. A transformação T pode ser implementada por uma função ou através de uma simples tabela de mapeamento. O NumPy possui um forma elegante e eficiente de se aplicar um mapeamento de intensidade a uma imagem.
O ndarray
pode ser indexado por outros ndarrays
. O uso de arrays como índice
podem ser simples mas também bastante complexos e difíceis de entender. O uso de
arrays indexados retornam sempre uma cópia dos dados originais e não uma visão ou
cópia rasa normalmente obtida com o slicing. Assim, o uso de arrays indexados devem
ser utilizados com precaução pois podem gerar códigos não tão eficientes.
Veja um exemplo numérico unidimensional simples. Um vetor row
de 10 elementos de 0 a 90
é criado e um outro vetor de indices i
com valores [3,5,0,8] irá indexar row
na forma row[i]
. O resultado será [30,50,0,80] que são os elementos de row
indexados por i
:
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
O indexador i
precisa ser inteiro. Entretanto o array que será indexado pode ser qualquer tipo.
f = row[i]
In [2]:
row = np.arange(0.,100,10)
print('row:', row)
i = np.array([[3,5,0,8],[4,2,7,1]])
f = row[i]
print('i:', i)
print('f=row[i]\n',f)
print(id(i),id(row),id(f))
Vejamos agora o caso bidimensional, apropriado para imagens e a transformação de intensidade.
Seja uma imagem f
de dimensões (2,3)
com os valores de pixels variando de 0 a 2:
In [3]:
f = np.array([[0, 1, 2],
[2, 0, 1]])
print('f=\n',f)
Seja agora a transformação de intensidade T
, especificada por um vetor de 3 elementos, onde
T[0] = 5; T[1] = 6 e T[2] = 7:
In [4]:
T = np.array([5, 6, 7])
print('T:', T)
for i in np.arange(T.size):
print('%d:%d'% (i,T[i]))
A aplicação da transformação de intensidade é feita utilizando-se a imagem f
como índice da
transformação T, como se escreve na equação matemática:
In [5]:
g = T[f]
print('g=T[f]= \n', g)
print('g.shape:', g.shape)
Note que T[f]
tem as mesmas dimensões de f
, entretanto, seus pixels passaram pelo
mapeamento da tabela T
.
Existem muitas funções úteis que podem ser feitas com o mapeamento T: realce de contraste, equalização de histograma, thresholding, redução de níveis de cinza, negativo da imagem, entre várias outras.
É comum representar a tabela de transformação de intensidade em um gráfico. A seguir várias funções de transformações são calculadas:
In [6]:
T1 = np.arange(256).astype('uint8') # função identidade
T2 = ia.normalize(np.log(T1+1.)) # logaritmica - realce partes escuras
T3 = 255 - T1 # negativo
T4 = ia.normalize(T1 > 128) # threshold 128
T5 = ia.normalize(T1//30) # reduz o número de níveis de cinza
plt.plot(T1)
plt.plot(T2)
plt.plot(T3)
plt.plot(T4)
plt.plot(T5)
plt.legend(['T1', 'T2', 'T3', 'T4','T5'], loc='right')
plt.xlabel('valores de entrada')
plt.ylabel('valores de saída')
plt.show()
In [7]:
nb = ia.nbshow(2)
f = mpimg.imread('../data/cameraman.tif')
f1 = T1[f]
nb.nbshow(f,'original')
plt.plot(T1)
plt.title('T1: identidade')
nb.nbshow(f1,'T1[f]')
nb.nbshow()
In [8]:
f2 = T2[f]
nb.nbshow(f,'original')
plt.plot(T2)
plt.title('T2: logaritmica')
nb.nbshow(f2,'T2[f]')
nb.nbshow()
In [9]:
f3 = T3[f]
nb.nbshow(f,'original')
plt.plot(T3)
plt.title('T3: negativo')
nb.nbshow(f3,'T3[f]')
nb.nbshow()
In [10]:
f4 = T4[f]
nb.nbshow(f,'original')
plt.plot(T4)
plt.title('T4: threshold 128')
nb.nbshow(f4,'T4[f]')
nb.nbshow()
In [11]:
f5 = T5[f]
nb.nbshow(f,'original')
plt.plot(T5)
plt.title('T5: quantização')
nb.nbshow(f5,'T5[f]')
nb.nbshow()
Observando o histograma de cada imagem após o mapaemento:
In [12]:
h = ia.histogram(f)
h2 = ia.histogram(f2) #logaritmica
h3 = ia.histogram(f3) # negativo
h4 = ia.histogram(f4) # threshold
h5 = ia.histogram(f5) # quantização
plt.plot(h)
#plt.plot(h2)
#plt.plot(h3)
#plt.plot(h4)
plt.plot(h5)
plt
Out[12]:
Do ponto de vista de eficiência, qual é o melhor, utilizar o mapeamento pela tabela, ou processar a imagem diretamente?
In [13]:
f = ia.normalize(np.arange(1000000).reshape(1000,1000))
In [14]:
%timeit g2t = T2[f]
%timeit g2 = ia.normalize(np.log(f+1.))
In [15]:
%timeit g3t = T3[f]
%timeit g3 = 255 - f
T pode ser denominado como função de transferência de intensidade. Quando a derivada de T for maior que 1, o contraste é aumentado naqueles valores de T, se for menor que 1, o contraste é diminuído. Caso a derivada for negativa, existe uma quebra de ordenação dos valores.
Se T for uma função crescente, isto é:
$$ T(i) >= T(j) \ \text{ se }\ i > j $$Então aplicando-se g = T[f]
, a propriedade