Sobre esta página
O conteúdo desta disciplina é apresentado principalmente na lousa, durante as aulas. Esta página não substitui a aula: ela reúne material de apoio — sobretudo imagens muito representativas de algum conceito explicado em sala, e tabelas com dados que vocês vão precisar consultar.
Quando um exemplo visto na lousa tiver uma imagem que ajude a fixar o conceito, ela será postada aqui e citada durante a explicação, para consulta posterior.
Matriz de Pixels e Canais RGB
Formação e Digitalização da Imagem
Exemplos em Python: Amostragem e Quantização
Reamostragem espacial (redução de resolução) a partir de uma imagem carregada da web:
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
from io import BytesIO
import requests
# URL da imagem
url = "https://www.unip.br/assets/img/fotos/og-images/og-unip.webp"
# Carrega a imagem
resposta = requests.get(url)
img = np.array(Image.open(BytesIO(resposta.content)))
# Intervalo de amostragem
intervalo = 10
# Reamostragem
img_reduzida = img[::intervalo, ::intervalo]
# Exibe as imagens
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.imshow(img)
plt.title("Imagem original")
plt.axis("off")
plt.subplot(1, 2, 2)
plt.imshow(img_reduzida)
plt.title("Imagem reamostrada")
plt.axis("off")
plt.show()
Quantização das intensidades da imagem:
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
from io import BytesIO
import requests
# URL da imagem
url = "https://www.unip.br/assets/img/fotos/og-images/og-unip.webp"
# Carrega a imagem
resposta = requests.get(url)
img = np.array(Image.open(BytesIO(resposta.content)))
# Número de níveis desejados
niveis = 4
# Quantização
intervalo = 256 // niveis
img_quantizada = (img // intervalo) * intervalo
# Exibe as imagens
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.imshow(img)
plt.title("Imagem original")
plt.axis("off")
plt.subplot(1, 2, 2)
plt.imshow(img_quantizada)
plt.title(f"Imagem quantizada - {niveis} níveis")
plt.axis("off")
plt.show()
Por que usar o NumPy para representar imagens? Como visto nas figuras anteriores, uma imagem digital nada mais é do que uma matriz de valores de intensidade (ou, no caso de imagens coloridas, uma matriz tridimensional com os canais Red, Green e Blue). O NumPy é a biblioteca padrão do Python para trabalhar com esse tipo de estrutura: ele armazena a imagem como um array, o que permite acessar e transformar pixels e regiões inteiras com operações vetorizadas — como a fatia img[::intervalo, ::intervalo] usada na reamostragem, ou a operação img // intervalo aplicada a todos os pixels de uma vez na quantização. Essa vetorização é muito mais rápida e legível do que percorrer a imagem pixel a pixel com laços for, e é justamente por isso que bibliotecas de imagem como Pillow e OpenCV são compatíveis com o formato de array do NumPy.
Exemplo em Python: Conversão para Escala de Cinza
Conversão pixel a pixel de uma imagem colorida (RGB) para escala de cinza, usando a ponderação padrão dos canais Red, Green e Blue:
import matplotlib.pyplot as plt
from PIL import Image
imagem = Image.open("foto.jpg").convert("RGB")
largura, altura = imagem.size
cinza = Image.new("L", (largura, altura))
for y in range(altura):
for x in range(largura):
r, g, b = imagem.getpixel((x, y))
valor = int(0.299 * r + 0.587 * g + 0.114 * b)
cinza.putpixel((x, y), valor)
cinza.save("foto_cinza.jpg")
# Exibe as imagens
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.imshow(imagem)
plt.title("Imagem original")
plt.axis("off")
plt.subplot(1, 2, 2)
plt.imshow(cinza, cmap="gray")
plt.title("Imagem em escala de cinza")
plt.axis("off")
plt.show()
De onde vêm os pesos 0.299, 0.587 e 0.114? Eles refletem a sensibilidade do olho humano a cada cor: somos mais sensíveis ao verde, depois ao vermelho, e menos ao azul. Por isso o canal Green tem o maior peso na conversão para cinza, e o Blue o menor — essa é a fórmula de luminância utilizada pelo padrão ITU-R BT.601 (o mesmo usado por bibliotecas como Pillow e OpenCV em suas conversões automáticas para escala de cinza, como Image.open(...).convert("L")).
Exemplo em Python: Histograma
O histograma de uma imagem mostra a distribuição de frequência dos valores de intensidade (0 a 255) presentes nela. Contagem pixel a pixel a partir da imagem em escala de cinza:
import matplotlib.pyplot as plt
from PIL import Image
imagem = Image.open("foto.jpg").convert("RGB")
cinza = imagem.convert("L")
largura, altura = cinza.size
histograma = [0] * 256
for y in range(altura):
for x in range(largura):
valor = cinza.getpixel((x, y))
histograma[valor] += 1
# Exibe a imagem e o histograma
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.imshow(cinza, cmap="gray")
plt.title("Imagem em escala de cinza")
plt.axis("off")
plt.subplot(1, 2, 2)
plt.bar(range(256), histograma, width=1, color="black")
plt.title("Histograma")
plt.xlabel("Nível de cinza")
plt.ylabel("Número de pixels")
plt.show()
Para que serve o histograma? Ele revela características globais da imagem sem olhar diretamente para ela: uma concentração de barras à esquerda indica uma imagem escura, à direita indica uma imagem clara, e um histograma espalhado por toda a faixa indica bom contraste. É a base para técnicas de processamento como equalização e alargamento de contraste, que serão vistas mais adiante.
Filtragem e Redução de Ruído
Implementação do filtro da média pixel a pixel, percorrendo cada janela local da imagem:
import cv2
import numpy as np
def filtro_media(imagem, tamanho_kernel):
# Preenche as bordas da imagem com zeros (zero-padding)
# Isso garante que o filtro também possa ser aplicado aos pixels da borda
imagem_com_borda = cv2.copyMakeBorder(imagem,
tamanho_kernel//2,
tamanho_kernel//2,
tamanho_kernel//2,
tamanho_kernel//2,
cv2.BORDER_CONSTANT)
# Cria uma imagem vazia para armazenar o resultado filtrado
imagem_filtrada = np.zeros_like(imagem)
# Percorre cada pixel da imagem
for y in range(imagem.shape[0]):
for x in range(imagem.shape[1]):
# Extrai a janela local centrada no pixel atual
janela = imagem_com_borda[y:y+tamanho_kernel, x:x+tamanho_kernel]
# Calcula o valor médio da janela
valor_medio = np.mean(janela)
# Atribui o valor médio ao pixel correspondente na imagem filtrada
imagem_filtrada[y, x] = valor_medio
return imagem_filtrada
# Carrega a imagem em escala de cinza
imagem = cv2.imread('sua_imagem.jpeg', cv2.IMREAD_GRAYSCALE)
# Define o tamanho do kernel do filtro da média
tamanho_kernel = 3
# Aplica o filtro da média à imagem
imagem_filtrada = filtro_media(imagem, tamanho_kernel)
# Exibe a imagem original e a imagem filtrada
cv2.imshow('Imagem Original', imagem)
cv2.imshow('Imagem com Filtro da Média', imagem_filtrada)
cv2.waitKey(0)
cv2.destroyAllWindows()
Na prática, o OpenCV já traz o filtro da média pronto na função cv2.blur, dispensando a implementação manual:
import cv2
# Carrega a imagem
imagem = cv2.imread("foto.jpg")
# Aplica filtro de média 3x3
suavizada = cv2.blur(imagem, (3, 3))
# Exibe as imagens
cv2.imshow("Original", imagem)
cv2.imshow("Suavizada", suavizada)
cv2.waitKey(0)
cv2.destroyAllWindows()