5.6. Dibujar formas y texto

Un algoritmo que decide algo sobre una imagen a menudo necesita que esa decisión sea visible. Un detector de manchas (blobs) encuentra una región que le interesa a la aplicación; la aplicación quiere que la región se dibuje en el fotograma para que un operador (o el desarrollador que ejecuta el script) pueda ver qué se encontró. Una transformación de coordenadas mapea una posición de entrada a una de salida; depurarla normalmente significa marcar las dos posiciones en la misma imagen. La vista previa del IDE lee lo que sea que haya en el búfer de fotogramas (frame buffer) en el momento en que lo consulta, así que la forma más sencilla de hacer visible la salida de un algoritmo es escribir anotaciones en el propio búfer de fotogramas. La familia de dibujo de la clase Image es el conjunto de herramientas para exactamente ese trabajo.

5.6.1. Las primitivas

Cada método de dibujo coloca un tipo específico de marca sobre la imagen. El catálogo es pequeño y se mantiene cerca de las primitivas geométricas que realmente necesita una anotación:

  • draw_line(): un segmento de línea recta entre dos extremos.

  • draw_rectangle(): un rectángulo alineado con los ejes, hueco o relleno.

  • draw_circle(): un círculo alrededor de un centro, hueco o relleno.

  • draw_ellipse(): una elipse con rotación arbitraria.

  • draw_cross(): un signo más en un punto, la marca habitual para un centroide o un objetivo de clic.

  • draw_arrow(): una flecha desde un punto de inicio hasta un punto final.

  • draw_edges(): los cuatro lados de un cuadrilátero arbitrario, dados los cuatro puntos de las esquinas; la forma natural de delinear una etiqueta detectada o una región deformada en perspectiva.

  • draw_string(): texto a partir de una fuente de mapa de bits integrada.

Cada uno de estos modifica la imagen de origen en el sitio y devuelve la misma imagen para encadenar, siguiendo la convención de métodos operativos establecida anteriormente.

Una cuadrícula de paneles pequeños que muestran cada una de las ocho primitivas de dibujo aplicadas una vez. Cada panel contiene una línea, un rectángulo, un círculo, una elipse, una cruz, una flecha, un cuadrilátero o una cadena de texto corta, con el nombre del método que la produjo etiquetado debajo.

Las ocho primitivas de dibujo, una por panel. Cada método hace un tipo de marca.

5.6.2. Color

Cada método de dibujo toma un argumento color que decide qué valor escribir en cada píxel pintado. La forma que adopta ese argumento depende del formato de la imagen. Para una imagen RGB565, la forma natural es una tupla (r, g, b) con cada canal en 0255; el módulo lo empaqueta en la palabra RGB565 de 16 bits antes de escribirlo. Para una imagen en escala de grises la forma natural es un único entero de brillo de 0 (negro) a 255 (blanco). Los métodos también aceptan el valor crudo almacenado del formato (una palabra empaquetada de 16 bits para RGB565, un entero de 8 bits para escala de grises), que es la forma eficiente cuando el color se calculó en otro lugar y ya está en la forma almacenada.

Omitir el argumento color pinta de blanco. Ese valor predeterminado es cómodo para el trabajo en escala de grises, donde el blanco es el valor máximo y se lee con claridad contra la mayoría de los fondos. Para superposiciones de depuración en RGB565 casi siempre es incorrecto: el verde o el rojo normalmente se leen mejor contra el tipo de escena que realmente ve una cámara, y un color explícito comunica la intención.

5.6.3. Grosor y relleno

La mayoría de los métodos geométricos toman dos banderas que deciden cómo se dibuja la marca:

  • thickness=N establece el ancho de línea en píxeles. El valor predeterminado es 1, que está bien para la mayoría de las superposiciones; un valor mayor es útil cuando una anotación tiene que seguir siendo visible contra una escena cargada o después de que una etapa posterior de la canalización modifique más la imagen.

  • fill=True cambia la marca de un contorno a una sólida, pintando cada píxel interior con el color elegido. El valor predeterminado es False.

Estas banderas no se aplican a las primitivas que no tienen interior que rellenar (la línea, la cruz, la flecha, el cuadrilátero) donde solo thickness tiene sentido.

5.6.4. Dibujar texto

draw_string() escribe caracteres a partir de una fuente de mapa de bits integrada de 8 por 10 píxeles. x e y son la esquina superior izquierda de la celda del primer carácter, text es la cadena que se va a dibujar y color sigue la misma convención que los métodos geométricos. La fuente abarca todo el rango ASCII imprimible y no tiene interletraje (kerning); cada carácter ocupa la misma celda de 8 píxeles de ancho, lo que facilita posicionar la salida.

img.draw_string(10, 10, "blobs: 3", color=(0, 255, 0))

La cadena puede incluir saltos de línea (\n); cada salto de línea mueve el siguiente carácter al inicio de una nueva línea diez píxeles por debajo de la anterior. El argumento scale dibuja cada carácter a un tamaño mayor mediante un factor de coma flotante, y x_spacing e y_spacing añaden relleno alrededor de cada carácter. Un pequeño conjunto de banderas de rotar / espejar / voltear se aplica ya sea a toda la cadena o a cada carácter por separado, suficiente control para disponer el texto a lo largo de un ángulo o contra un borde no horizontal cuando el diseño lo requiera.

5.6.5. Limpiar el lienzo

Un método de la familia no dibuja ninguna marca específica. Simplemente restablece a cero cada píxel de la imagen:

  • clear(): pone a cero cada píxel, opcionalmente restringido a una ROI o limitado a través de una máscara.

clear() es la respuesta correcta cuando una aplicación compone una anotación desde cero en cada fotograma (empezar con un lienzo negro, dibujar las nuevas anotaciones, entregar el resultado a la pantalla) en lugar de superponer sobre el fotograma capturado. Es también la forma más barata de preparar una imagen temporal para usarla como búfer de máscara.

Una imagen recién reservada ya está a cero desde el constructor, así que clear() importa específicamente para los búferes reutilizados entre fotogramas.