5.6. Desenhar formas e texto#
Um algoritmo que decide algo sobre uma imagem muitas vezes precisa que essa decisão seja visível. Um detetor de manchas encontra uma região que a aplicação interessa; a aplicação quer que a região seja desenhada no fotograma para que um operador – ou o programador 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 da IDE lê o que estiver no buffer de fotograma no momento em que faz a consulta, por isso a forma mais simples de tornar a saída do algoritmo visível é escrever anotações no próprio buffer de fotograma. A família de desenho na 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 fica próximo das primitivas geométricas que uma anotação realmente necessita:
draw_line()– um segmento de linha reta entre dois pontos extremos.draw_rectangle()– um retângulo alinhado com os eixos, oco ou preenchido.draw_circle()– um círculo em torno de um centro, oco ou preenchido.draw_ellipse()– uma elipse com rotação arbitrária.draw_cross()– um sinal de adição num ponto, a marca habitual para um centróide ou um alvo de clique.draw_arrow()– uma seta de um ponto inicial para um ponto final.draw_edges()– os quatro lados de um quadrilátero arbitrário, dados os quatro pontos de canto; a forma natural de delinear uma etiqueta detetada ou uma região com perspetiva distorcida.draw_string()– texto de uma fonte de mapa de bits embutida.
Cada um destes modifica a imagem fonte no local e devolve a mesma imagem para encadeamento, seguindo a convenção de método operacional estabelecida anteriormente.
As oito primitivas de desenho, uma por painel. Cada método produz um tipo de marca.#
5.6.2. Cor#
Cada método de desenho recebe um argumento color que decide que valor escrever em cada pixel pintado. A forma que esse argumento assume depende do formato da imagem. Para uma imagem RGB565, a forma natural é um tuplo (r, g, b) com cada canal em 0 – 255; o módulo compacta isso na palavra RGB565 de 16 bits antes de escrever. Para uma imagem em escala de cinzentos, a forma natural é um número inteiro de brilho de 0 (preto) a 255 (branco). Os métodos também aceitam o valor armazenado bruto do formato – uma palavra compactada de 16 bits para RGB565, um inteiro de 8 bits para escala de cinzentos – que é a forma eficiente quando a cor foi calculada noutro lado e já está na forma armazenada.
Omitir o argumento color pinta a branco. Esse padrão é conveniente para trabalho em escala de cinzentos, onde o branco é o valor máximo e lê-se claramente contra a maioria dos fundos. Para sobreposições de depuração RGB565 está quase sempre errado: o verde ou o vermelho geralmente lê-se melhor contra o tipo de cena que uma câmara realmente vê, e uma cor explícita comunica 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 é adequado para a maioria das sobreposições; um valor maior é útil quando uma anotação tem de permanecer visível contra uma cena complexa ou após uma etapa subsequente do pipeline modificar a imagem.fill=Truemuda a marca de um contorno para uma sólida, pintando cada pixel interior com a cor escolhida. O padrão éFalse.
Estas flags não se aplicam às primitivas que não têm interior para preencher – a linha, a cruz, a seta, o quadrilátero – onde apenas thickness é relevante.
5.6.4. Desenhar texto#
draw_string() escreve caracteres de uma fonte de mapa de bits embutida de 8 por 10 pixels. x e y são o canto superior esquerdo da célula do primeiro caractere, text é a cadeia de caracteres a desenhar, e color segue a mesma convenção que os métodos geométricos. A fonte cobre todo o intervalo ASCII imprimível e não tem kerning – cada caractere ocupa a mesma célula de 8 pixels de largura – o que facilita o posicionamento da saída.
img.draw_string(10, 10, "blobs: 3", color=(0, 255, 0))
A cadeia de caracteres pode incluir newlines (\n); cada newline move o próximo caractere para o início de uma nova linha dez pixels abaixo da anterior. O argumento scale desenha cada caractere num tamanho maior por um fator de ponto flutuante, e x_spacing e y_spacing adicionam preenchimento em torno de cada caractere. Um pequeno conjunto de flags de rotação / espelhamento / inversão aplica-se a toda a cadeia ou a cada caractere individualmente – controlo suficiente para dispor texto ao longo de um ângulo ou contra uma aresta não horizontal quando o layout o exige.
5.6.5. Limpar a tela#
Um método na família não desenha nenhuma marca específica. Simplesmente repõe todos os pixels da imagem a zero:
clear()– zera todos os pixels, opcionalmente restringido a um ROI ou com âmbito através de uma máscara.
clear() é a resposta certa quando uma aplicação está a compor uma anotação do zero em cada fotograma – começar com uma tela preta, desenhar as novas anotações, entregar o resultado ao ecrã – em vez de sobrepor sobre o fotograma capturado. É também a forma mais económica de preparar uma imagem de rascunho para uso como buffer de máscara.
Uma imagem recém-alocada já é zero a partir do construtor, pelo que clear() importa especificamente para buffers reutilizados entre fotogramas.