segunda-feira, 2 de dezembro de 2013

Uma implementação de uma técnica de quantização de imagens

Autores: Isaac V. M., Tulio M. M.
Disciplina - Semestre: Processamento Digital de Imagens - 2013/2
Prof.: Msc. Thiago Meireles Paixão
Instituição: Universidade Federal do Espírito Santo

Olá pessoal!

Na disciplina de Processamento Digital de Imagens(PDI) nós fizemos um trabalho sobre quantização de imagens. Resolvemos compartilhar o resultado deste trabalho aqui, para quem se interessar pelo o assunto e deseja ver uma solução alternativa para o problema.



O procedimento de quantização nada mais é que uma forma de discretizar valores contínuos em intervalos discretos. Ok, mas o que é isso na prática? No mundo real, existe um número infinito de variação de intensidade de pixels. Na computação, normalmente temos uma variação de 256 cores para cada canal da nossa imagem, o que implica que se tomarmos como exemplo imagens de 8 bits, conhecidas como imagens em escala de cinza, as infinitas intensidades de cor que existem do preto para o branco são discretizadas em 256 variações de cor. Da mesma forma que temos a imagem discretizada para 256 cores, podemos ter a mesma imagem discretizada para 2, 16, 32 ou para a quantidades de cores que acharmos interessante para nosso contexto de pesquisa. Por isso a quantização é um processamento interessante no estudo de PDI.

Agora, vamos ao que interessa. A implementação!

Nós usamos da biblioteca sys o argv para passar os argumentos do programa via shell, também usamos o numpy para manipularmos os arrays, importamos também o PyLab para gerarmos os gráficos, do matplotlib importamos cm para plotarmos a imagem mapeando-a com greys_r e assim em segmentos lineares.

O algoritmo começa com a atribuição do atributo nroCores que é responsável por definir a quantidade de cores que será quantizada na imagem. Após isso, importamos a imagem da Lena, foto disponível na biblioteca do Scipy, para o atributo orig. O atributo quantizada recebe um array, este array tem as dimensões da imagem da lena e é definido como um array com intervalo de valores de 0 a 255. Logo em seguida atribuimos o valor 0 para cada posição do nosso array definido como quantizada.

from sys import argv
import numpy as np
import scipy.misc as msc
from PIL import Image
from pylab import *
import matplotlib.cm as cm
""" imagem e quantidade de cores para quantização que são argumentos passados pelo shell """
script, nroCores = argv
""" numero de cores que será usada para a quantização """
ncores = int(nroCores)
""" imagem original da famosa lena """
orig = msc.lena()
""" ndarray com as dimensões da imagem original da nossa amiga lena, onde o 3º argumento do método
zeros define o valor do ndarray como zero(0), uint8, para cada posicao """
quantizada = np.zeros([orig.shape[0], orig.shape[1]], dtype='uint8')

Trabalhando agora com os intervalos que usaremos na quantização, temos que definir o tamanho do passo do nosso algoritmo, ou seja, de quantos a quantos será o valor que vamos gerar. Para isso primeiro temos que definir o tamanho do passo, para isso, dividimos a quantidade de cores que temos originalmente na nossa imagem pela quantidade de cores que queremos no resultado do processo de quantização.

""" define de quanto em quanto será os intervalos para a quantização da imagem """
passo = 256/ncores
""" define os intervalos de pixels que usaremos na analise """
intervalos = range(0,255+passo,passo)
""" define a média aritmetica de cada um dos intervalos que definimos """
mediaIntervalos = range(passo/2, 255+passo, passo)
""" cria um ndarray de mascaras para a imagem, onde o argumento ncores indica quantas camadas esta mascara ira ter
e o tipo do ndarray é definido como booleano """
mascaras = np.empty([orig.shape[0], orig.shape[1], ncores], dtype=bool)
""" define todas as posições do ndarray como false """
mascaras[:,:,:] = False
""" pega o tempo atual(inicial) para calcular o tempo de execução da heurística """
startTime = datetime.now()
"""para cada uma das camadas do nosso ndarray de mascaras fazemos um "E lógico" que retorna se o pixel em questão
está no intervalo analisado """
for x in range(ncores):
    mascaras[:,:,x] = np.logical_and((orig >= intervalos[x]), (orig < intervalos[x+1]))
"""para cada intervalo de cor do nosso ndarray, ele "pinta" os pixels que fazem correspondência com a máscara em questão"""
for i in range(ncores):
    quantizada[mascaras[:,:,i]] = mediaIntervalos[i]
"""
# pega o tempo atual(final) para calcular o tempo de execução da heurística
finalTime = datetime.now()
# calcula mse
mse = np.mean((quantizada - orig)**2)
print "MSE: " + str(mse)
# calcula o tempo de execucao
print "Tempo de Execucao: " + str(finalTime-startTime)
"""

Para plotar os resultados na tela do computador, dividimos a área da imagem em uma região superior e uma região inferior. Na região superior mostramos a imagem resultante do processo de quantização e na região inferior mostramos o histograma da imagem original e da imagem quantizada para efeito de comparação. A partir da análise do histograma podemos ver o nível de contraste das imagens e ainda realizar algumas mensurações estatísticas, tais como valores médios, máximos, ou desvio padrão nos níveis de cinza da imagem.

""" inicializa a figura"""
figure(1)
"""divide a figura em 2 linhas e 1 coluna, alem disso, seleciona a parte superior
2(linhas)1(coluna)1(parte superior)"""
subplot(211)
""" plota a imagem quantizada na parte selecionada """
imshow(quantizada, cmap = cm.Greys_r)
""" divide a figura em 2 linhas e 1 coluna, alem disso, seleciona a parte inferior
2(linhas)1(coluna)2(parte inferior) """
subplot(212)
""" cria os histogramas das imagens quantizada e original, respectivamente em cada linha.
Os argumentos são a imagem comprimida (pela função flatten(), e a quantidade de posições respectivamente """
hist(quantizada.flatten(), 256)
hist(orig.flatten(), 256)
""" exibe as imagens e o histograma destas imagens """
show()

Resultados obtidos

Abaixo temos o resultado da aplicação deste algoritmo de quantização aplicado para resultados com 2, 4, 8, 16, 32, 64, e 128 cores e seus respectivos histogramas comparando-os com a imagem original.













Realizando uma inspeção visual, pode-se perceber que, pelo menos a olho nu, a diferença entre as imagens de 32 e 64 cores é pequena, já a diferença entre as imagens de 64 cores para 128 cores são praticamente imperceptíveis. Isso acontece devido ao fato de que o olho humano consegue distinguir muito mais variações de cores (milhares) do que as variações de cinza (aproximadamente 30).

Para uma análise numérica foi calculado o erro quadrático médio (EQM). Analisando o gráfico percebemos que a discrepância da imagem quantizada em relação à imagem original é maior quando a quantidade de cores é menor, o que é um comportamento natural, ao passo que, estamos substituindo um intervalo de cores por uma única cor que as representam. Quando aumentamos a quantidade de cores para realização do algoritmo de quantização, a substituição das cores se torna menos incorretas, pois se tem mais cores que são correspondentes à imagem original.



Por fim, com o intuito de analisar o desempenho do algoritmo de acordo com a quantidade de cores processadas na quantização, geramos um gráfico do tempo de execução.



Referências

http://astro.if.ufrgs.br/med/imagens/node27.htm
http://www.dpi.inpe.br/~carlos/Academicos/Cursos/Pdi/pdi_cores.html
PEDRINI, Hélio; SCHWARTZ, William Robson. Análise de imagens digitais: princípios, algoritmos e aplicações. São Paulo: Thomson Learning, 2008. xvi, 508 p. ISBN 9788522105953

Nenhum comentário:

Postar um comentário