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:

flowchart TD A[Entender a situação proposta: qual é o problema, e como sabemos que ele foi resolvido, isto é, quais são os critérios de aceitação] --> B[Ligar a situação a um modelo matemático e suas variáveis] --> C[Resolver o modelo matemático, possivelmente implementando um programa para isso] --> D[Identificar, dentro do modelo resolvido e/ou dos resultados mostrados pelo programa, como a situação inicial foi resolvida]

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()
No description has been provided for this image
altura, largura, cores = image.shape
print(altura, largura, cores)
427 640 3

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])
(3, 273280)

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()
No description has been provided for this image

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:

\[ \begin{bmatrix} Y \\ I \\ Q \end{bmatrix} = A \begin{bmatrix} R \\ G \\ B \end{bmatrix} \]

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:

  1. Faça um programa que converte uma imagem em RGB para uma imagem em YIQ.
  2. Em seu programa, acrescente uma multiplicação matricial que remove (multiplica por zero) as componentes I e Q
  3. Após, faça seu programa retornar a imagem para RGB
  4. 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()
No description has been provided for this image

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:

  1. Lê uma imagem em RGB,
  2. Reduz sua componente luma (Y) para 20% do valor inicial,
  3. Troca os valores das compontentes I e Q
  4. Mostra a imagem resultante na tela.
# Faça seu código aqui