Mudanças entre as edições de "Computação Gráfica: Transformações em Imagens Matriciais"
(16 revisões intermediárias pelo mesmo usuário não estão sendo mostradas) | |||
Linha 1: | Linha 1: | ||
+ | |||
Afluentes: [[Computação Gráfica]] | Afluentes: [[Computação Gráfica]] | ||
Linha 26: | Linha 27: | ||
Abaixo temos um programa em python que faz um ''scaling'' em uma imagem alterando a largura e altura para manter o aspecto original. | Abaixo temos um programa em python que faz um ''scaling'' em uma imagem alterando a largura e altura para manter o aspecto original. | ||
− | <syntaxhighlight lang=python | + | <syntaxhighlight lang="python"> |
import sys | import sys | ||
import pygame | import pygame | ||
Linha 110: | Linha 111: | ||
O programa em python abaixo faz o espelhamento horizontal da imagem. | O programa em python abaixo faz o espelhamento horizontal da imagem. | ||
− | <syntaxhighlight lang=python | + | <syntaxhighlight lang="python"> |
import sys | import sys | ||
import pygame | import pygame | ||
Linha 166: | Linha 167: | ||
# Altere o programa para fazer o espelhamento vertical da imagem. | # Altere o programa para fazer o espelhamento vertical da imagem. | ||
+ | |||
+ | = Rotação = | ||
+ | |||
+ | Uma imagem pode ser rotacionada de um ângulo arbitrário, tanto no sentido horário quanto no anti-horário. Rotações com ângulos múltiplos de 90º são mais simples de implementar, pois consistem na cópia de pixels que estão organizados em linhas, reordenando-os em colunas na direção em que se deseja rotacionar a imagem. A figura 25 ilustra o processo de rotação de 90º no sentido horário. A área tracejada destaca as primeiras linhas da imagem original, que são reposicionadas em formas de colunas, da direita para a esquerda, na imagem rotacionada. | ||
+ | |||
+ | Rotações diferentes requerem operações mais complexas, como no caso do exemplo a seguir em que a imagem foi rotacionada em 15º. | ||
+ | |||
+ | <center>[[Image:Cg_mat_rotation_00.jpg|400px]]</center> | ||
+ | |||
+ | Observe que quando rotacionamos, tanto a largura quanto a altura são aumentadas para comportar a imagem em sua totalidade. Logo, a imagem resultante será maior que a original. | ||
+ | |||
+ | <center>[[Image:Cg_mat_rotation_03.jpg|300px]]</center> | ||
+ | |||
+ | Para calcular o tamanho da nova imagem, precisamos rotacionar os vértices da imagem original pelo ângulo requerido. Depois pegamos o x mínimo de todos os vértices, o x máximo, assim como o y mínimo e o y máximo. Então teremos o tamanho da imagem como: | ||
+ | |||
+ | <math> | ||
+ | w = x\_max - x\_min | ||
+ | </math> | ||
+ | |||
+ | <math> | ||
+ | h = y\_max - y\_min | ||
+ | </math> | ||
+ | |||
+ | Ok, com isso temos a largura e altura da nova imagem. Contudo, ainda não vimos a fórmula da rotação. | ||
+ | |||
+ | A operação de rotação ocorre com a multiplicação da matriz de Rotação com todos os píxeis da imagem. Porém, é importante observar que a matriz de rotação, no caso de uma imagem matricial, é apenas nos eixos x e y. No espaço tridimensional, temos a rotação em todos os eixos (x, y e z). Dessa forma, as matrizes de rotação são apresentadas a seguir: | ||
+ | |||
+ | <math> | ||
+ | \begin{pmatrix} | ||
+ | \cos(\theta) & \sin(\theta) \\ | ||
+ | -\sin(\theta) & \cos(\theta) \\ | ||
+ | \end{pmatrix} | ||
+ | </math> | ||
+ | |||
+ | tal que: | ||
+ | |||
+ | ''θ'' é o ângulo, '''em radianos''', que se quer rotacionar o objeto, | ||
+ | ''sin'' é a operação do seno sobre o ângulo e | ||
+ | ''cos'' é a operação do cosseno sobre o ângulo. | ||
+ | |||
+ | Logo, a fórmula matemática da operação de translação fica da seguinte forma: | ||
+ | |||
+ | <math> | ||
+ | \begin{pmatrix} | ||
+ | x & y | ||
+ | \end{pmatrix} | ||
+ | \cdot | ||
+ | \begin{pmatrix} | ||
+ | \cos(\theta) & \sin(\theta) \\ | ||
+ | -\sin(\theta) & \cos(\theta) \\ | ||
+ | \end{pmatrix} | ||
+ | </math> | ||
+ | |||
+ | tal que: | ||
+ | |||
+ | <math> | ||
+ | x' = (x \cdot \cos(\theta)) + (y \cdot -\sin(\theta)) | ||
+ | </math> | ||
+ | |||
+ | <math> | ||
+ | y' = (x \cdot \sin(\theta)) + (y \cdot \cos(\theta)) | ||
+ | </math> | ||
+ | |||
+ | Um '''problema''' é que se simplesmente rotacionarmos a imagem, ela irá se basear no seu ponto de origem (0, 0). Então vários pontos acabam saindo da área onde a imagem deve ser representada. | ||
+ | |||
+ | <center>[[Image:Cg_mat_rotation_02.jpg|250px]]</center> | ||
+ | |||
+ | Dessa forma, precisamos mover nosso ponto de origem para o meio da imagem. Ou melhor, movemos a imagem para que seu ponto de origem coincida com o ponto (0, 0). Essa é uma operação de translação. | ||
+ | |||
+ | A operação da translação ocorre com a soma da matriz de translação com todos os pontos do objeto. A matriz de translação é a seguinte: | ||
+ | |||
+ | <math> | ||
+ | \begin{pmatrix} | ||
+ | Tx & Ty | ||
+ | \end{pmatrix} | ||
+ | </math> | ||
+ | |||
+ | tal que: | ||
+ | |||
+ | Tx é a translação na horizontal e | ||
+ | Ty é a translação na vertical | ||
+ | |||
+ | Logo, a fórmula matemática da operação de translação fica da seguinte forma: | ||
+ | |||
+ | <math> | ||
+ | \begin{pmatrix} | ||
+ | x & y | ||
+ | \end{pmatrix} | ||
+ | + | ||
+ | \begin{pmatrix} | ||
+ | Tx & Ty | ||
+ | \end{pmatrix} | ||
+ | </math> | ||
+ | |||
+ | tal que: | ||
+ | |||
+ | <math> | ||
+ | x = x + Tx | ||
+ | </math> | ||
+ | |||
+ | <math> | ||
+ | y = y + Ty | ||
+ | </math> | ||
+ | |||
+ | Com a fórmula da translação, então devemos fazer o seguinte: | ||
+ | |||
+ | # Para cada ponto da imagem | ||
+ | ## Pego o meio da imagem original | ||
+ | ## translado para o negativo do meio da imagem original | ||
+ | ## rotaciono o ponto | ||
+ | ## translado considerando o meio da imagem de destino | ||
+ | |||
+ | <center>[[Image:Cg_mat_rotation_08.jpg|800px]]</center> | ||
+ | |||
+ | A seguir temos um código em python para rotação da imagem. Veja que existem píxeis na imagem que não estão pintados. Esse algoritmo é simplificado e poderia ser melhorado. | ||
+ | |||
+ | <syntaxhighlight lang="python"> | ||
+ | import sys | ||
+ | import pygame | ||
+ | import math | ||
+ | |||
+ | |||
+ | # Rotaciona um ponto x, y pelo angulo ang. | ||
+ | def rot_xy(x, y, ang): | ||
+ | x2 = (x * math.cos(ang)) + (y * -math.sin(ang)) | ||
+ | y2 = (x * math.sin(ang)) + (y * math.cos(ang)) | ||
+ | return int(x2), int(y2) | ||
+ | |||
+ | |||
+ | # Pego o novo tamanho da imagem | ||
+ | def get_new_image_size(w, h, ang): | ||
+ | # pega todos os vertices do retangulo | ||
+ | vertex = [[0, 0], [w, 0], [w, h], [0, h]] | ||
+ | print(vertex) | ||
+ | # rotaciona todos os pontos | ||
+ | for i in range(4): | ||
+ | x, y = rot_xy(vertex[i][0], vertex[i][1], ang) | ||
+ | vertex[i] = [x, y] | ||
+ | print(vertex) | ||
+ | # pega os mínimos e máximos | ||
+ | x_min = 0 | ||
+ | y_min = 0 | ||
+ | x_max = 0 | ||
+ | y_max = 0 | ||
+ | for i in range(4): | ||
+ | if vertex[i][0] < x_min: | ||
+ | x_min = vertex[i][0] | ||
+ | if vertex[i][0] > x_max: | ||
+ | x_max = vertex[i][0] | ||
+ | if vertex[i][1] < y_min: | ||
+ | y_min = vertex[i][1] | ||
+ | if vertex[i][1] > y_max: | ||
+ | y_max = vertex[i][1] | ||
+ | return (x_max - x_min), (y_max - y_min) | ||
+ | |||
+ | |||
+ | # Função de espelhamento. O resultado é colocado em surface | ||
+ | def rotation(image, surface, ang): | ||
+ | w_orig = image.get_width() # largura da imagem de origem | ||
+ | h_orig = image.get_height() # altura da imagem de origem | ||
+ | |||
+ | w_mid_orig = int(w_orig / 2) # metade da largura da imagem de origem | ||
+ | h_mid_orig = int(h_orig / 2) # metade da altura da imagem de origem | ||
+ | |||
+ | w_dest = surface.get_width() # largura da imagem de destino | ||
+ | h_dest = surface.get_height() # altura da imagem de destino | ||
+ | |||
+ | w_mid_dest = int(w_dest / 2) # metade da largura da imagem de estino | ||
+ | h_mid_dest = int(h_dest / 2) # metade da altura da imagem de destino | ||
+ | |||
+ | for y in range(h - 1): # para cada linha da imagem de origem | ||
+ | for x in range(w - 1): # para cada coluna da imagem de origem | ||
+ | # translada o ponto para o meio negativo da imagem de origem | ||
+ | xaux = x - w_mid_orig | ||
+ | yaux = y - h_mid_orig | ||
+ | # rotaciona o ponto | ||
+ | xaux, yaux = rot_xy(xaux, yaux, ang) | ||
+ | # translada o ponto pegando o meio da imagem de destino | ||
+ | xaux = xaux + w_mid_dest | ||
+ | yaux = yaux + h_mid_dest | ||
+ | # imprime o pixel na posição calculada da imagem de destino | ||
+ | surface.set_at((xaux, yaux), image.get_at((x, y))) | ||
+ | |||
+ | |||
+ | # PROGRAMA PRINCIPAL | ||
+ | pygame.init() | ||
+ | |||
+ | file_in = sys.argv[1] | ||
+ | file_out = sys.argv[2] | ||
+ | # o angulo é horário, vamos trabalhar no anti-horário | ||
+ | angle = -math.radians(int(sys.argv[3])) | ||
+ | |||
+ | image = pygame.image.load(file_in) | ||
+ | w = image.get_width() | ||
+ | h = image.get_height() | ||
+ | |||
+ | # cria a surface com a proporção a ser alterada | ||
+ | ws, hs = get_new_image_size(w, h, angle) | ||
+ | surface = pygame.display.set_mode((ws, hs)) | ||
+ | |||
+ | # chama a função fazer o espelhamento | ||
+ | rotation(image, surface, angle) | ||
+ | |||
+ | # salva a imagem | ||
+ | pygame.image.save(surface, file_out) | ||
+ | |||
+ | pygame.display.set_caption(file_in) | ||
+ | |||
+ | finish = False | ||
+ | while not finish: | ||
+ | for event in pygame.event.get(): | ||
+ | if event.type == pygame.QUIT: | ||
+ | finish = True | ||
+ | elif event.type == pygame.KEYDOWN: | ||
+ | if event.key == pygame.K_ESCAPE: | ||
+ | finish = True | ||
+ | pygame.display.update() | ||
+ | pygame.quit() | ||
+ | quit() | ||
+ | |||
+ | </syntaxhighlight> |
Edição atual tal como às 19h38min de 7 de junho de 2024
Afluentes: Computação Gráfica
Transformações Geométricas
As transformações geométricas são operações de processamento de imagem para alterar a posição inicial dos seus píxels. Dentre algumas operações, temos ampliação, diminuição, espelhamento, rotação, distorção, etc.. Veremos alguns deles aqui.
Para nossos testes, iremos utilizar a imagem abaixo:
Alteração de Dimensões
Na alteração de dimensões de uma imagem, alteramos as proporções da saída gravando em arquivo contendo a imagem alterada, seja ela ampliada ou reduzida. Na literatura técnica de processamento de imagens existem dois tipos de alterações de dimensões de uma imagem, embora tecnicamente idênticos:
- scaling a imagem é ampliada ou reduzida por um fator (que pode ser igual para as dimensões horizontal e vertical / preservando a relação de aspecto original / ou não);
- sizing (ou resizing) é utilizado nos casos em que, ao invés de especificar o fator de ampliação / redução, o usuário especifica o novo tamanho que a imagem deve possuir.
Se quisermos ampliar a imagem, a nova imagem resultante deve possuir o tamanho necessário para comportá-la. A nova imagem irá conter uma quantidade de pixeis maior do que a original. Então, pelo método mais simples, replicamos os pixeis na nova imagem.
Veja que conforme o tamanho da imagem podemos ter um resultado visual onde podemos observar grandes quadriculados referente aos pixeis de origem. Existem algoritmos para suavizar essa percepção.
Abaixo temos um programa em python que faz um scaling em uma imagem alterando a largura e altura para manter o aspecto original.
import sys
import pygame
# Retorna x e y equivalente da imagem original na destino
def get_dest_xy(x_orig, y_orig, proportion):
x = int(proportion * x_orig / 100) # calcula a regra de tres de x
y = int(proportion * y_orig / 100) # calcula a regra de tres de y
return x, y
# Retorna x e y equivalente da imagem destino na original
def get_orig_xy(x_dest, y_dest, proportion):
p = 100 / (proportion / 100) # pega o percentual invertido
x = int(p * x_dest / 100) # calcula a regra de tres de x
y = int(p * y_dest / 100) # calcula a regra de tres de y
return x, y
# Redimensiona a imagem
def resize(image, surface, proportion):
# Para cada coluna da imagem de destino
for y in range(surface.get_height()):
# Para cada linha da imagem de destino
for x in range(surface.get_width()):
# pega o x e y relativo da imagem original
x2, y2 = get_orig_xy(x, y, proportion)
# e pinta na imagem de destino
surface.set_at((x, y), image.get_at((x2, y2)))
# PROGRAMA PRINCIPAL
pygame.init()
file_in = sys.argv[1]
file_out = sys.argv[2]
proportion = int(sys.argv[3])
image = pygame.image.load(file_in)
w = image.get_width()
h = image.get_height()
sw, sh = get_dest_xy(w, h, proportion)
# cria a surface com a proporção a ser alterada
surface = pygame.display.set_mode((sw, sh))
# chama a função pra redimensionar
resize(image, surface, proportion)
# salva a surface como nova imagem
pygame.image.save(surface, file_out)
pygame.display.set_caption(file_in)
finish = False
while not finish:
for event in pygame.event.get():
if event.type == pygame.QUIT:
finish = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
finish = True
pygame.display.update()
pygame.quit()
quit()
- Exemplo de execução
python3.9 resize.py imagem.jpg out.jpg 120
O exemplo aumenta a imagem em 20% da original (imagem.jpg), criando uma imagem resultado chamada out.jpg.
- Exercício
- Altere o algoritmo para redimensionar apenas a largura e depois apenas a altura. Observe que a imagem irá ficar espichada.
Espelhamento
A operação de espelhamento consiste em inverter a imagem. No caso do espelhamento horizontal, é como se estivéssemos vendo por um espelho. Quando usamos um espelhamento vertical, a imagem fica de ponta cabeça.
A operação consiste, no caso do horizontal. Para cada píxel de uma linha, ele pega o primeiro e coloca na última posição da imagem de destino. Depois pega o segundo e coloca na penúltima posição. Esse procedimento ocorre até que todos os pixeis da linha tiverem sido colocado na sua respectiva posição da imagem de destino. Depois segue para a próxima linha até que toda a imagem tenha sido rasterizada.
O programa em python abaixo faz o espelhamento horizontal da imagem.
import sys
import pygame
# Função de espelhamento. O resultado é colocado em surface
def flip(image, surface):
w = image.get_width()
h = image.get_height()
for y in range(h):
for x in range(w):
# coloca o último pixel na primeira posição,
# o penúltimo na segunda, e assim por diante.
surface.set_at((w - 1 - x, y), image.get_at((x, y)))
# PROGRAMA PRINCIPAL
pygame.init()
file_in = sys.argv[1]
file_out = sys.argv[2]
image = pygame.image.load(file_in)
w = image.get_width()
h = image.get_height()
# cria a surface com a proporção a ser alterada
surface = pygame.display.set_mode((w, h))
# chama a função fazer o espelhamento
flip(image, surface)
# salva a imagem
pygame.image.save(surface, file_out)
pygame.display.set_caption(file_in)
finish = False
while not finish:
for event in pygame.event.get():
if event.type == pygame.QUIT:
finish = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
finish = True
pygame.display.update()
pygame.quit()
quit()
- Execução
python3.9 flip.py image.jpg flip.jpg
- Exercícios
- Altere o programa para fazer o espelhamento vertical da imagem.
Rotação
Uma imagem pode ser rotacionada de um ângulo arbitrário, tanto no sentido horário quanto no anti-horário. Rotações com ângulos múltiplos de 90º são mais simples de implementar, pois consistem na cópia de pixels que estão organizados em linhas, reordenando-os em colunas na direção em que se deseja rotacionar a imagem. A figura 25 ilustra o processo de rotação de 90º no sentido horário. A área tracejada destaca as primeiras linhas da imagem original, que são reposicionadas em formas de colunas, da direita para a esquerda, na imagem rotacionada.
Rotações diferentes requerem operações mais complexas, como no caso do exemplo a seguir em que a imagem foi rotacionada em 15º.
Observe que quando rotacionamos, tanto a largura quanto a altura são aumentadas para comportar a imagem em sua totalidade. Logo, a imagem resultante será maior que a original.
Para calcular o tamanho da nova imagem, precisamos rotacionar os vértices da imagem original pelo ângulo requerido. Depois pegamos o x mínimo de todos os vértices, o x máximo, assim como o y mínimo e o y máximo. Então teremos o tamanho da imagem como:
Ok, com isso temos a largura e altura da nova imagem. Contudo, ainda não vimos a fórmula da rotação.
A operação de rotação ocorre com a multiplicação da matriz de Rotação com todos os píxeis da imagem. Porém, é importante observar que a matriz de rotação, no caso de uma imagem matricial, é apenas nos eixos x e y. No espaço tridimensional, temos a rotação em todos os eixos (x, y e z). Dessa forma, as matrizes de rotação são apresentadas a seguir:
tal que:
θ é o ângulo, em radianos, que se quer rotacionar o objeto, sin é a operação do seno sobre o ângulo e cos é a operação do cosseno sobre o ângulo.
Logo, a fórmula matemática da operação de translação fica da seguinte forma:
tal que:
Um problema é que se simplesmente rotacionarmos a imagem, ela irá se basear no seu ponto de origem (0, 0). Então vários pontos acabam saindo da área onde a imagem deve ser representada.
Dessa forma, precisamos mover nosso ponto de origem para o meio da imagem. Ou melhor, movemos a imagem para que seu ponto de origem coincida com o ponto (0, 0). Essa é uma operação de translação.
A operação da translação ocorre com a soma da matriz de translação com todos os pontos do objeto. A matriz de translação é a seguinte:
tal que:
Tx é a translação na horizontal e Ty é a translação na vertical
Logo, a fórmula matemática da operação de translação fica da seguinte forma:
tal que:
Com a fórmula da translação, então devemos fazer o seguinte:
- Para cada ponto da imagem
- Pego o meio da imagem original
- translado para o negativo do meio da imagem original
- rotaciono o ponto
- translado considerando o meio da imagem de destino
A seguir temos um código em python para rotação da imagem. Veja que existem píxeis na imagem que não estão pintados. Esse algoritmo é simplificado e poderia ser melhorado.
import sys
import pygame
import math
# Rotaciona um ponto x, y pelo angulo ang.
def rot_xy(x, y, ang):
x2 = (x * math.cos(ang)) + (y * -math.sin(ang))
y2 = (x * math.sin(ang)) + (y * math.cos(ang))
return int(x2), int(y2)
# Pego o novo tamanho da imagem
def get_new_image_size(w, h, ang):
# pega todos os vertices do retangulo
vertex = [[0, 0], [w, 0], [w, h], [0, h]]
print(vertex)
# rotaciona todos os pontos
for i in range(4):
x, y = rot_xy(vertex[i][0], vertex[i][1], ang)
vertex[i] = [x, y]
print(vertex)
# pega os mínimos e máximos
x_min = 0
y_min = 0
x_max = 0
y_max = 0
for i in range(4):
if vertex[i][0] < x_min:
x_min = vertex[i][0]
if vertex[i][0] > x_max:
x_max = vertex[i][0]
if vertex[i][1] < y_min:
y_min = vertex[i][1]
if vertex[i][1] > y_max:
y_max = vertex[i][1]
return (x_max - x_min), (y_max - y_min)
# Função de espelhamento. O resultado é colocado em surface
def rotation(image, surface, ang):
w_orig = image.get_width() # largura da imagem de origem
h_orig = image.get_height() # altura da imagem de origem
w_mid_orig = int(w_orig / 2) # metade da largura da imagem de origem
h_mid_orig = int(h_orig / 2) # metade da altura da imagem de origem
w_dest = surface.get_width() # largura da imagem de destino
h_dest = surface.get_height() # altura da imagem de destino
w_mid_dest = int(w_dest / 2) # metade da largura da imagem de estino
h_mid_dest = int(h_dest / 2) # metade da altura da imagem de destino
for y in range(h - 1): # para cada linha da imagem de origem
for x in range(w - 1): # para cada coluna da imagem de origem
# translada o ponto para o meio negativo da imagem de origem
xaux = x - w_mid_orig
yaux = y - h_mid_orig
# rotaciona o ponto
xaux, yaux = rot_xy(xaux, yaux, ang)
# translada o ponto pegando o meio da imagem de destino
xaux = xaux + w_mid_dest
yaux = yaux + h_mid_dest
# imprime o pixel na posição calculada da imagem de destino
surface.set_at((xaux, yaux), image.get_at((x, y)))
# PROGRAMA PRINCIPAL
pygame.init()
file_in = sys.argv[1]
file_out = sys.argv[2]
# o angulo é horário, vamos trabalhar no anti-horário
angle = -math.radians(int(sys.argv[3]))
image = pygame.image.load(file_in)
w = image.get_width()
h = image.get_height()
# cria a surface com a proporção a ser alterada
ws, hs = get_new_image_size(w, h, angle)
surface = pygame.display.set_mode((ws, hs))
# chama a função fazer o espelhamento
rotation(image, surface, angle)
# salva a imagem
pygame.image.save(surface, file_out)
pygame.display.set_caption(file_in)
finish = False
while not finish:
for event in pygame.event.get():
if event.type == pygame.QUIT:
finish = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
finish = True
pygame.display.update()
pygame.quit()
quit()