Pixels e Cores
Nesta aula, usaremos o conceito de bases para alterar cores em imagens.
Você vai perceber que nesta aula específica não há "certo" ou "errado" -- o que há é um processo de aprendizado. Sendo o processo tão particular de cada um, é preciso que cada um seja responsável por si. O que gostaríamos nessa atividade é praticar o seguinte fluxo de pensamento:
Por esse motivo, essa atividade não tem uma "rubrica" ou um "gabarito". Existem muitas soluções para cada um dos exercícios propostos. Porém, é importante que, para cada um deles, você explicitamente passe por todas as etapas do fluxo.
A quarta etapa é especialmente importante: como este é um trabalho aberto, isto é, que envolve o pensamento divergente, é esperado do aluno (e dos grupos de alunos) que o entendimento de cada etapa, e do que significa ter "resolvido" cada etapa, seja discutido pelo grupo de trabalho. Isso significa que fazer essa atividade pensando em "cumprir rapidamente etapas para entregar um resultado que será avaliado" não é produtivo. Ao invés disso, trata-se um trabalho importante pelo vivenciar o processo, e não pela entrega em si.
Exercício 1
Objetivo: entender pixels como vetores em 3 dimensões
Uma imagem digital é composta de pequenos pontinhos chamados pixels. Os pixels se organizam numa grade semelhante a uma matriz. Um pixel tem uma representação da cor que deve ser "pintada" naquele pontinho específico.
Uma das possíveis representações de cores é a RGB. Nessa representação, cores são representadas por três números, cada um deles correspondente à intensidade das componentes R (red - vermelho), G (green - verde) e B (blue - azul).
Como já vimos, a função imread abre uma imagem na forma de um np.array com as dimensões altura, largura e cores:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
image = mpimg.imread("rio_de_janeiro.jpg")
plt.figure()
plt.imshow(image)
plt.show()
altura, largura, cores = image.shape
print(altura, largura, cores)
Podemos enfileirar nossos pixels em uma matriz com um pixel por coluna e uma cor por linha usando a instrução reshape e a transposição:
X = image.reshape(altura*largura, cores).T
# Agora, tenho uma matrix 3 x n, onde cada linha é uma componente!
print(X.shape)
# print("Pixel na posição (0,10) na imagem: ", image[0,10,:])
# print("Pixel na coluna 10 na lista: ", X[:,10])
# print("Primeiras 11 colunas da lista:")
# print(X[:, :11])
Depois, podemos voltar nossa matriz X para o formato de uma imagem, de forma que conseguimos desenhá-la na tela:
Y = X.T.reshape(altura, largura, cores)
plt.figure()
plt.imshow(Y)
plt.show()
O que acontece se multiplicarmos todos os pixels por \(2\)? E se dividimos todos os pixels por \(2\)?
Dica
provavelmente você precisará usar o método astype(int) antes de mostrar sua imagem na tela! O método imshow aceita ou tipo float, escalado entre 0 e 1, ou tipo int, que deve estar entre 0 e 255.
Exercício 2
Objetivo: transformar as cores de uma imagem
Usando uma multiplicação matricial, altere as cores da imagem para que ela fique mais avermelhada. Para isso, faça uma transformação que aplica uma escala no vetor de cores relacionado a cada pixel.
Exercício 3
Objetivo: transformar uma imagem para o espaço YIQ
O formato RGB nem sempre é o mais eficiente para todas as aplicações. Na época da TV analógica, o padrão NTSC usava o formato YIQ. Nesse formato, a componente Y é chamada de luma, e representa o brilho do pixel; as componentes I e Q representam a crominância, isto é, a cor do pixel, distribuída num espaço 2D.
Para mapear um pixel \(x\) no formato RGB para um pixel \(y\) no formato YIQ, é preciso fazer uma multiplicação matricial:
onde a matriz \(A\) é pré-definida como abaixo:
A = np.array( [[0.299, 0.587, 0.144], [0.5959, -0.2746, -0.3213], [0.2115, -0.5227, 0.3112]] )
Sabendo disso:
- Faça um programa que converte uma imagem em RGB para uma imagem em YIQ.
- Em seu programa, acrescente uma multiplicação matricial que remove (multiplica por zero) as componentes I e Q
- Após, faça seu programa retornar a imagem para RGB
- Por fim, seu programa deve mostrar a imagem resultante na tela. Se precisar, use o método
.astype(int)para que sua matriz seja convertida para números inteiros!
# Somente redefina a matriz R abaixo! Explique cada parte da sua alteração!
R = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
# O código abaixo aplica a matrix R sobre cada pixel da imagem.
X = image.reshape(altura*largura, cores).T
X_ = R @ X
image_ = X_.T.reshape(altura, largura, cores).astype(int)
plt.figure()
plt.imshow(image_)
plt.show()
Exercício 4
Objetivo: formular matematicamente o procedimento de conversão
No exercício anterior, executamos três multiplicações matriciais:
- A primeira, para converter a representação de RGB para YIQ;
- A segunda, para retirar as componentes I e Q;
-
A terceira, para voltar a representação de YIQ para RGB.
-
Essas operações podem ser escritas como: \(X' = A^{-1} R A X\). Associe cada uma dessas matrizes às suas respectivas operações.
- Encontre uma única matriz \(M\) que realiza as três operações simultaneamente, isto é, \(X' = M X\).
- Substitua as operações no código do exercício anterior para que somente a matriz \(M\) seja usada.
Exercício 5
Aplicar um procedimento de distorção de cores
Faça um código que, usando uma única multiplicação matricial:
- Lê uma imagem em RGB,
- Reduz sua componente luma (Y) para 20% do valor inicial,
- Troca os valores das compontentes I e Q
- Mostra a imagem resultante na tela.
# Faça seu código aqui