5.6. Desenhando formas e texto#
Um algoritmo que decide algo sobre uma imagem frequentemente precisa que essa decisão seja visível. Um detector de blobs encontra uma região de interesse da aplicação; a aplicação quer a região desenhada no quadro para que um operador – ou o desenvolvedor que executa o script – possa ver o que foi encontrado. Uma transformação de coordenadas mapeia uma posição de entrada para uma de saída; depurá-la geralmente significa marcar as duas posições na mesma imagem. A pré-visualização do IDE lê o que estiver no frame buffer no momento em que faz a consulta, então a maneira mais simples de tornar a saída do algoritmo visível é escrever anotações no próprio frame buffer. A família de métodos de desenho da classe Image é o conjunto de ferramentas exatamente para esse trabalho.
5.6.1. As primitivas#
Cada método de desenho coloca um tipo específico de marca na imagem. O catálogo é pequeno e se mantém próximo das primitivas geométricas que uma anotação realmente precisa:
draw_line()– um segmento de linha reta entre dois pontos extremos.draw_rectangle()– um retângulo alinhado aos eixos, vazado ou preenchido.draw_circle()– um círculo em torno de um centro, vazado ou preenchido.draw_ellipse()– uma elipse com rotação arbitrária.draw_cross()– um sinal de mais em um ponto, a marca usual para um centroide ou um alvo de clique.draw_arrow()– uma seta de um ponto inicial a um ponto final.draw_edges()– os quatro lados de um quadrilátero arbitrário, dados os quatro pontos dos cantos; a maneira natural de contornar uma tag detectada ou uma região distorcida em perspectiva.draw_string()– texto a partir de uma fonte bitmap embutida.
Cada um desses métodos modifica a imagem de origem in-place e retorna a mesma imagem para encadeamento, seguindo a convenção dos métodos operadores estabelecida anteriormente.
As oito primitivas de desenho, uma por painel. Cada método faz um tipo de marca.#
5.6.2. Cor#
Cada método de desenho recebe um argumento color que decide qual valor escrever em cada pixel pintado. A forma desse argumento depende do formato da imagem. Para uma imagem RGB565, a forma natural é uma tupla (r, g, b) com cada canal de 0 a 255; o módulo compacta isso na palavra RGB565 de 16 bits antes de escrevê-la. Para uma imagem em escala de cinza, a forma natural é um único inteiro de brilho, de 0 (preto) a 255 (branco). Os métodos também aceitam o valor bruto armazenado do formato – uma palavra empacotada de 16 bits para RGB565, um inteiro de 8 bits para escala de cinza – que é a forma eficiente quando a cor foi calculada em outro lugar e já está na forma armazenada.
Omitir o argumento color pinta de branco. Esse padrão é conveniente para trabalho em escala de cinza, onde o branco é o valor máximo e se destaca claramente contra a maioria dos fundos. Para sobreposições de depuração em RGB565, ele quase sempre está errado: verde ou vermelho geralmente se destacam melhor contra o tipo de cena que uma câmera realmente vê, e uma cor explícita comunica a intenção.
5.6.3. Espessura e preenchimento#
A maioria dos métodos geométricos recebe duas flags que decidem como a marca é desenhada:
thickness=Ndefine a largura da linha em pixels. O padrão é1, que serve para a maioria das sobreposições; um valor maior é útil quando uma anotação precisa permanecer visível contra uma cena carregada ou após uma etapa posterior do pipeline modificar ainda mais a imagem.fill=Truemuda a marca de um contorno para uma forma sólida, pintando cada pixel interno com a cor escolhida. O padrão éFalse.
Essas flags não se aplicam às primitivas que não têm interior a preencher – a linha, a cruz, a seta, o quadrilátero – onde apenas thickness tem significado.
5.6.4. Desenhando texto#
draw_string() escreve caracteres usando uma fonte bitmap embutida de 8 por 10 pixels. x e y são o canto superior esquerdo da célula do primeiro caractere, text é a string a desenhar, e color segue a mesma convenção dos métodos geométricos. A fonte abrange toda a faixa ASCII imprimível e não tem kerning – cada caractere ocupa a mesma célula de 8 pixels de largura – o que torna a saída fácil de posicionar.
img.draw_string(10, 10, "blobs: 3", color=(0, 255, 0))
A string pode incluir quebras de linha (\n); cada quebra de linha move o próximo caractere para o início de uma nova linha dez pixels abaixo da anterior. O argumento scale desenha cada caractere em um tamanho maior por um fator de ponto flutuante, e x_spacing e y_spacing adicionam espaçamento em torno de cada caractere. Um pequeno conjunto de flags de rotacionar / espelhar / inverter se aplica tanto à string inteira quanto a cada caractere individualmente – controle suficiente para dispor o texto ao longo de um ângulo ou contra uma borda não horizontal quando o layout exigir.
5.6.5. Limpando a tela#
Um método da família não desenha nenhuma marca específica. Ele apenas redefine cada pixel da imagem para zero:
clear()– zera cada pixel, opcionalmente restrito a uma ROI ou delimitado por uma máscara.
clear() é a resposta certa quando uma aplicação está compondo uma anotação do zero a cada quadro – começar com uma tela preta, desenhar as novas anotações, entregar o resultado ao display – em vez de sobrepor sobre o quadro capturado. É também a maneira mais barata de preparar uma imagem temporária para uso como buffer de máscara.
Uma imagem recém-alocada já vem zerada do construtor, então clear() importa especificamente para buffers reutilizados entre quadros.