5.7. Compondo imagens¶
As primitivas de desenho da página anterior pintam marcas geométricas sobre uma imagem – uma linha, um retângulo, um trecho de texto. Isso cobre a maioria das anotações que um algoritmo precisa tornar visíveis, mas não todas. Às vezes a anotação é, ela mesma, uma imagem: um quadro de referência capturado para exibir lado a lado com o atual, uma miniatura de uma captura anterior mostrada em um canto da pré-visualização, um modelo previamente armazenado visualizado sobre um quadro ao vivo para calibração. O mecanismo para desenhar uma imagem sobre outra é um único método – draw_image() – com parâmetros suficientes para lidar com a posição, o escalonamento, a paleta de cores e a transparência que uma composição real exige.
5.7.1. A chamada básica¶
Em sua forma mais simples, draw_image recebe outra Image e uma posição para desenhá-la:
reference = image.Image("/sdcard/reference.bmp")
img.draw_image(reference, x=10, y=10)
O destino é img; a origem é reference; o pixel superior esquerdo da origem cai em (10, 10) de img, e o restante dos pixels da origem segue para a direita e para baixo a partir daí. Os pixels do destino que a origem cobre são sobrescritos pelos pixels correspondentes da origem; os pixels fora da área coberta pela origem permanecem intactos.
Se a origem se estender além da borda do destino, as partes que ficam de fora são silenciosamente recortadas – o mesmo comportamento tolerante que set_pixel apresenta para posições fora do intervalo. O código da aplicação não precisa limitar a posição às dimensões da imagem com antecedência; ele pode passar a posição desejada e deixar que o método cuide do recorte.
5.7.2. Carregando um arquivo diretamente¶
draw_image aceita um caminho de arquivo no lugar do argumento Image e carrega o arquivo antes de compô-lo:
img.draw_image("/sdcard/reference.bmp", x=10, y=10)
Isso parece uma conveniência – uma linha em vez de duas – e é, mas a diferença vai além da sintaxe. Construir uma Image a partir de um arquivo aloca um buffer para armazenar os pixels decodificados, e esse buffer permanece até que a coleta de lixo o libere. Passar o caminho diretamente para draw_image permite que o módulo decodifique o arquivo em um buffer temporário, faça a composição a partir dele e libere o buffer quando a chamada retorna, sem que o código da aplicação tenha que manter uma referência a uma Image separada entre os quadros.
5.7.3. Escalonamento¶
Quando a origem e o destino têm tamanhos diferentes – uma captura de baixa resolução sendo composta sobre uma tela de resolução mais alta, ou uma miniatura que precisa ser dimensionada para uma fração específica do quadro – dois parâmetros de escala cuidam de redimensionar a origem conforme ela é desenhada:
img.draw_image(reference, x=10, y=10, x_scale=2.0, y_scale=2.0)
x_scale e y_scale são floats independentes; passar ambos com o mesmo valor escalona uniformemente, e passar valores diferentes estica ou encolhe a origem ao longo de um eixo. O escalonamento acontece no momento do desenho; a origem reference não é modificada.
Uma máscara de bits de flags de dica (hint) decide como o escalonamento realmente interpola entre os pixels. image.BILINEAR produz resultados mais suaves ao custo de mais processamento; image.BICUBIC produz resultados ainda mais suaves e custa mais ainda; o padrão usa o vizinho mais próximo, que é o mais barato e a escolha certa quando a origem já está na resolução de pixels do destino. As flags de tratamento de proporção – SCALE_ASPECT_KEEP, SCALE_ASPECT_EXPAND, SCALE_ASPECT_IGNORE – decidem o que fazer quando a proporção da origem não corresponde ao retângulo em que ela está sendo desenhada.
5.7.4. Mistura alfa (alpha blending)¶
Por padrão, draw_image substitui os pixels do destino pelos pixels da origem. Quando o objetivo é uma sobreposição translúcida – de modo que o destino apareça através da origem – o parâmetro alpha controla como os dois são misturados. alpha=0 mostra apenas o destino (nenhuma origem); alpha=255 é o padrão e mostra apenas a origem (substituição total); valores intermediários misturam os dois proporcionalmente:
img.draw_image(overlay, x=0, y=0, alpha=128)
Um argumento alpha_palette separado é o único mecanismo de alfa por pixel do módulo. Ele recebe uma imagem GRAYSCALE cujos valores são usados como alfa na posição correspondente da origem – um mapa de calor cujo alfa varia com sua intensidade, por exemplo. O alfa precisa ser fornecido como esse argumento separado em escala de cinza; uma imagem de origem que carrega seu próprio canal alfa (um PNG com transparência, digamos) não o transfere automaticamente.
5.7.5. ROI e paleta da origem¶
Dois parâmetros adicionais completam o mecanismo de composição:
roi=(x, y, w, h)restringe a origem a um sub-retângulo dela mesma, de modo que apenas esse retângulo seja composto sobre o destino. Útil para recortar dentro da mesma chamada, sem preparar um intermediário recortado.color_palettesubstitui o valor de cada pixel da origem por uma consulta em uma tabela de lookup antes de desenhar – o mesmo mecanismo queto_rainbow()eto_ironbow()usam, exposto aqui para que uma sobreposição possa ser paletizada a caminho do destino sem um passo de conversão separado.
Ambos se combinam com tudo o mais na chamada: o escalonamento, o alfa, o argumento mask do lado do destino e o parâmetro roi do lado do destino que limita a escrita a um retângulo do destino.