5.7. Composición de imágenes

Las primitivas de dibujo de la página anterior pintan marcas geométricas sobre una imagen: una línea, un rectángulo, un fragmento de texto. Eso cubre la mayoría de las anotaciones que un algoritmo necesita hacer visibles, pero no todas. A veces la anotación es en sí misma una imagen: un fotograma de referencia capturado para mostrarlo junto al actual, una miniatura de una captura anterior mostrada en una esquina de la vista previa, una plantilla almacenada previamente y visualizada sobre un fotograma en vivo para calibración. El mecanismo para dibujar una imagen sobre otra es un único método, draw_image(), con suficientes parámetros para gestionar la posición, el escalado, la paleta de colores y la transparencia que requiere una composición real.

5.7.1. La llamada básica

En su forma más simple, draw_image toma otra Image y una posición en la que dibujarla:

reference = image.Image("/sdcard/reference.bmp")
img.draw_image(reference, x=10, y=10)

El destino es img; el origen es reference; el píxel superior izquierdo del origen aterriza en (10, 10) de img, y el resto de los píxeles del origen siguen hacia la derecha y hacia abajo desde ahí. Los píxeles del destino que el origen cubre se sobrescriben con los píxeles correspondientes del origen; los píxeles fuera de la huella del origen se dejan intactos.

Si el origen se extiende más allá del borde del destino, las partes que quedan fuera se recortan silenciosamente, el mismo comportamiento indulgente que set_pixel muestra para posiciones fuera de rango. El código de la aplicación no tiene que ajustar la posición a las dimensiones de la imagen de antemano; puede pasar la posición que quiera y dejar que el método se encargue del recorte.

5.7.2. Cargar un archivo en línea

draw_image acepta una ruta de archivo en lugar del argumento Image y carga el archivo antes de componerlo:

img.draw_image("/sdcard/reference.bmp", x=10, y=10)

Eso parece una comodidad (una línea en lugar de dos) y lo es, pero la diferencia es algo más que sintaxis. Construir una Image a partir de un archivo reserva un búfer para contener los píxeles decodificados, y ese búfer perdura hasta que el recolector de basura lo libera. Pasar la ruta directamente a draw_image permite que el módulo decodifique el archivo en un búfer temporal, componga a partir de él y libere el búfer cuando la llamada retorna, sin que el código de la aplicación tenga que mantener una referencia a una Image aparte entre fotogramas.

5.7.3. Escalado

Cuando el origen y el destino tienen tamaños diferentes (una captura de baja resolución que se compone sobre un lienzo de mayor resolución, o una miniatura que necesita dimensionarse a una fracción concreta del fotograma) dos parámetros de escala se encargan de redimensionar el origen a medida que se dibuja:

img.draw_image(reference, x=10, y=10, x_scale=2.0, y_scale=2.0)

x_scale y y_scale son números de coma flotante independientes; pasar ambos con el mismo valor escala de forma uniforme, y pasar valores distintos estira o encoge el origen a lo largo de un eje. El escalado ocurre en el momento del dibujo; el origen reference no se modifica.

Una máscara de bits de banderas de sugerencia (hint) decide cómo interpola realmente el escalado entre píxeles. image.BILINEAR produce resultados más suaves a costa de mayor cómputo; image.BICUBIC produce resultados aún más suaves y cuesta todavía más; el valor predeterminado usa vecino más cercano, que es el más barato y la elección correcta cuando el origen ya está a la resolución de píxeles del destino. Las banderas de gestión de la relación de aspecto (SCALE_ASPECT_KEEP, SCALE_ASPECT_EXPAND, SCALE_ASPECT_IGNORE) deciden qué hacer cuando la relación de aspecto del origen no coincide con el rectángulo en el que se dibuja.

5.7.4. Mezcla alfa

De forma predeterminada, draw_image reemplaza los píxeles del destino con los píxeles del origen. Cuando el objetivo es una superposición translúcida (de modo que el destino se transparente a través del origen) el parámetro alpha controla cómo se mezclan los dos. alpha=0 muestra solo el destino (sin origen); alpha=255 es el valor predeterminado y muestra solo el origen (reemplazo completo); los valores intermedios mezclan los dos proporcionalmente:

img.draw_image(overlay, x=0, y=0, alpha=128)

Un argumento alpha_palette aparte es el único mecanismo de alfa por píxel del módulo. Toma una imagen GRAYSCALE cuyos valores se usan como alfa en la posición coincidente del origen: un mapa de calor cuyo alfa varía con su intensidad, por ejemplo. El alfa debe suministrarse como ese argumento aparte en escala de grises; una imagen de origen que lleva su propio canal alfa (un PNG con transparencia, por ejemplo) no lo transmite automáticamente.

5.7.5. ROI del origen y paleta

Otros dos parámetros completan el mecanismo de composición:

  • roi=(x, y, w, h) restringe el origen a un subrectángulo de sí mismo, de modo que solo ese rectángulo se compone sobre el destino. Útil para recortar dentro de la misma llamada, sin preparar un intermedio recortado.

  • color_palette sustituye el valor de cada píxel del origen a través de una tabla de búsqueda antes de dibujar, el mismo mecanismo que usan to_rainbow() y to_ironbow(), expuesto aquí para que una superposición pueda paletizarse en su camino hacia el destino sin una pasada de conversión aparte.

Ambos se combinan con todo lo demás de la llamada: el escalado, el alfa, el argumento mask del lado del destino y el parámetro roi del lado del destino que limita la escritura a un rectángulo del destino.