Pixels e imagens
Uma imagem digital, por vezes chamada de uma imagem bitmap ou raster, nada mais é do que uma sequência de números, frequentemente indicando variações de vermelho, verde e azul (mistura das primárias de cor-luz) numa localização particular de uma grade de pixels, um neologismo cunhado na década de 60 juntado pix, abreviação de picture, e el de element, é o menor elemento de uma imagem.
A maior parte do tempo nós visualizamos esses pixels como mosaicos miniatura justapostos na tela do computador. No entanto, com um pouco de pensamento criativo e com a manipulação dos pixels com código, podemos mostrar esta informação de inúmeras maneiras. Apesar disso, de tempos em tempos, podemos querer quebrar nossa rotina de desenho corriqueira e manipular os pixels da tela diretamente. O Processing proporciona isso através de um array (uma estrutura de dados que lembra uma lista mas que tem todos os elementos do mesmo tipo e um tamanho predefinido) para descrever os pixels. A biblioteca py5 permite acessar esse array (em que os pixels são representados como inteiros de 32 bits) e permite também manipular os pixels como um array NumPy com uma estrutura um pouco mais complexa.
Começando com a ideia geral dos pixels na memória
Estamos acostumados com a ideia de cada pixel na tela ter uma posição X e Y numa janela. No entanto, um array de pixels tem apenas uma dimensão na memória, armazenado os valores de cor numa sequência linear.
Como os pixels aparecem:
0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|
5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 |
Como os pixels são armazenados na memória:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | … | 24 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Um exemplo de acesso aos pixels com .get_pixels
def setup():
global img
size(400, 400)
no_stroke()
rect_mode(CENTER)
img = load_image("ale.jpg") # carregando uma imagem da pasta /data/
def draw():
# desenha a imagem (Py5Image, x, y, [largura, altura]*) *opcionais
image(img, 0, 0)
cor = img.get_pixels(mouse_x, mouse_y) # pega a cor do pixel sob o mouse
r = red(cor) # componente vermelho do pixel
g = green(cor) # componente verde do pixel
b = blue(cor) # componente azul do pixel
fill(cor)
circle(mouse_x, mouse_y, 60) # desenha um círculo com a cor do pixel
fill(255, 0, 0) # vermelho
rect(mouse_x, mouse_y + 60, r, 20)
fill(0, 255, 0) # verde
rect(mouse_x, mouse_y + 80, g, 20)
fill(0, 0, 255) # azul
rect(mouse_x, mouse_y + 100, b, 20)
Manipulando individualmente os pixels de uma imagem
O método load_pixels()
dá acesso a um array contendo os pixels da imagem e update_pixels()
atualiza na imagem modificações que tenham sido feitas no array.
Use create_image()
para criar um novo objeto Py5Image
(tipo de dados para armazenar imagens) vazio, fornecendo assim um buffer de pixels que pode ser manipulado.
# w (largura em pixels), h (altura em pixels),
# formato (RGB, ARGB ou ALPHA: canal alpha em escala de cinzas)
create_image(w, h, formato)
Um exemplo de inversão de uma lista de pixels
- Copie na pasta
data
: desenho.gif - Aperte a tecla i
def setup():
global img # define um objeto PImage chamado imagem
global img_aux # define um objeto PImage chamado imagem auxiliar
img = load_image("desenho.gif") # carrega uma imagem
img_aux = img # carrega a imagem auxiliar
# define o tamanho da tela
this.surface.set_size(img.width * 2, img.height)
def draw():
background(255)
image(img, 0, 0)
image(img_aux, img.width, 0)
def key_pressed():
global foto, img_aux
img.load_pixels()
img_aux.load_pixels()
foto = create_image(img.width, img.height, RGB)
foto.load_pixels()
if key == 'i':
origem = img.width * img.height
# multiplicar a largura pela altura para encontrar o último pixel
destino = 0
for temp in range(origem-1, -1, -1):
# origem -1 pq começamos contar do 0
foto.pixels[destino] = img.pixels[temp]
destino += 1
img_aux = foto
img_aux.update_pixels()```
Filtros de imagem
Processing oferece uma série de filtros prontos que podem ser aplicados em qualquer imagem. O comando filtro() aplica um filtro em uma imagem usando a sintaxe filter(MODE)
ou filter(MODE, level)
Modos disponíveis como parâmetros de apply_filter()
THRESHOLD: Converte a imagem em pixels pretos ou brancos, dependendo se eles estão acima ou abaixo do limite definido pelo parâmetro de nível. O nível deve estar entre 0, 0 (preto) e 1, 0 (branco). Se nenhum nível for especificado, 0, 5 será usado.
img = load_image("exemplo.jpg")
image(img, 0, 0)
apply_filter(THRESHOLD)
GRAY
: Converte as cores na imagem em equivalentes de escala de cinza. Nenhum parâmetro é usado.
INVERT
: Define cada pixel para o seu valor inverso. Nenhum parâmetro é usado.
POSTERIZE
: Limita cada canal da imagem ao número de cores especificado como parâmetro. O parâmetro pode ser configurado para valores entre 2 e 255, mas os resultados são mais visíveis nos intervalos inferiores.
BLUR
: executa um borramento Gaussiano(n.t. Guassian blur), sendo que o parâmetro level especifica a extensão do borramento. Nos casos em que o parâmetro level não é utilizado, o borramento equivalente a um borramento gaussiano de raio 1.
OPAQUE
: Define o canal alfa de forma totalmente opaca. Nenhum parâmetro é usado.
ERODE
: Reduz as áreas de luz. Nenhum parâmetro é usado.
DILATE
: Aumenta as áreas de luz. Nenhum parâmetro é usado.
Manipulação de bits em Pixels
O valor de um pixel é representado no Processing Java como um número inteiro. Nesse sentido, uma imagem digital é um array de números inteiros, como vimos acima. Um inteiro é composto de 32 bits ou 4 bytes para armazenar a informação sobre a cor dos pixels. Especificamente, o primeiro byte(ou seja, 8 bits - um número entre 0 and 255) armazena o grau de transparência (canal alpha), o segundo byte para vermelho, terceiro byte para verde e o quarto byte para azul. Esquematicamente, os bits de inteiros, representando um pixel, aparecem assim:
Alpha | Vermelho | Verde | Azul |
---|---|---|---|
00000000 | 00000000 | 00000000 | 00000000 |
Esses valores podem ser manipulados com “bit shifting”. Isso significa que para acessar uma cor, nós precisamos mexer no nível dos bits para extrair os 8 bits específicos que desejamos.
Outro arranjo de pixels numpy.arrays
A biblioteca py5 permite uma interface NumPy para os pixels das imagens.