5.21. Escalar, voltear y recortar

Todas las subsecciones anteriores trabajaban sobre los píxeles en las mismas posiciones en las que empezaban. La familia de transformaciones cambia eso. El escalado envía cada píxel de entrada a una posición de salida distinta, posiblemente a varias posiciones de salida a la vez (al ampliar) o a una posición compartida con otros varios píxeles de entrada (al reducir). Voltear y rotar hacen lo mismo a través de un mapeo diferente. El recorte conserva un subconjunto rectangular de los píxeles de entrada y descarta el resto.

El módulo image expone esa familia mediante tres métodos que comparten la mayor parte de sus argumentos y de su comportamiento:

  • copy() – produce una copia de la imagen, posiblemente escalada, recortada o reorientada.

  • crop() – la misma operación que copy, pero con la expectativa de que la aplicación va a extraer un subrectángulo de la fuente.

  • scale() – lo mismo de nuevo, con la expectativa de que la aplicación va a redimensionar el resultado.

Los tres comparten los mismos argumentos y la misma maquinaria de transformación; la diferencia está en dónde acaba el resultado por defecto. copy() produce una imagen nueva, mientras que crop() y scale() modifican la fuente en su sitio.

5.21.1. Los argumentos compartidos

Una sola llamada combina cualquier combinación de escalado, recorte, orientación y extracción de canal que la aplicación solicite:

x_scale e y_scale escalan la entrada a lo largo de los ejes horizontal y vertical de forma independiente. Ambos tienen como valor por defecto 1.0 (sin escalado). Valores distintos para cada uno producen un escalado no uniforme – un fotograma estirado al doble de ancho que de alto, por ejemplo.

roi restringe la entrada a un rectángulo de la imagen fuente, llevando solo esos píxeles a través del resto de la transformación. Esta es la parte de «recorte» de la operación: pasa un roi para extraer una subregión.

hint es un campo de bits con banderas que selecciona el método de interpolación y cualquier volteo de orientación. Varias banderas se combinan mediante un OR a nivel de bits (hint=image.BILINEAR | image.HMIRROR). Las banderas se dividen en dos grupos – la familia de interpolación y la familia de orientación – que no tienen nada que ver entre sí pero comparten el mismo campo de bits.

rgb_channel selecciona un único canal de una fuente RGB565. 0 significa rojo, 1 significa verde, 2 significa azul; el resultado sale como una imagen en escala de grises que contiene solo ese canal. Útil para aplicar un umbral únicamente sobre el canal rojo, por ejemplo.

color_palette y alpha_palette reasignan los valores de los píxeles a través de una tabla de búsqueda a la salida, del mismo modo que lo hacen los métodos de conversión to_rainbow() y to_ironbow().

copy=True y copy_to_fb=True siguen la misma convención que todos los demás métodos que producen un resultado – en su sitio por defecto, copy=True reserva un resultado aparte, copy_to_fb=True coloca el resultado en el búfer de fotogramas (frame buffer) para la vista previa del IDE.

5.21.2. Interpolación: AREA, BILINEAR, BICUBIC

Cuando el escalado envía cada píxel de salida a una posición que no se alinea con ningún píxel de entrada concreto, el método tiene que decidir qué valor escribir. Tres banderas controlan cómo:

image.BILINEAR interpola entre los cuatro píxeles de entrada más cercanos ponderados por su distancia a la posición de salida. El resultado es más suave que el del vecino más cercano, sin dientes de sierra visibles en las líneas diagonales, pero la aritmética adicional cuesta unas cuatro veces lo que una pasada de vecino más cercano. Es la elección adecuada para la mayoría del trabajo de ampliación y para cualquier factor de escala no entero.

image.BICUBIC interpola entre los dieciséis píxeles de entrada más cercanos usando una curva cúbica, lo que produce resultados aún más suaves a costa de nuevamente más aritmética. La mejor calidad para las aplicaciones sensibles al coste que la necesitan; rara vez vale la pena el cómputo adicional para los fotogramas en vivo que el IDE solo va a mostrar.

image.AREA promedia todos los píxeles de entrada que caen dentro de la huella del píxel de salida – el algoritmo adecuado para reducir. Bilinear y bicubic son interpoladores: estiman un valor entre los píxeles de la fuente, que es lo que la ampliación necesita, pero al reducir cada píxel de salida cubre muchos píxeles de la fuente y un interpolador lee solo los pocos más cercanos – el detalle que se salta vuelve en forma de aliasing. image.AREA en cambio incorpora todos los píxeles cubiertos al promedio.

El algoritmo de escalado por defecto sin ninguna pista (hint) es el del vecino más cercano, que es el más barato y la respuesta correcta cuando la fuente ya está a la resolución de píxel del destino.

5.21.3. Orientación: volteos y rotaciones

Las banderas de orientación son un pequeño conjunto de transformaciones booleanas que se componen libremente entre sí y con las banderas de interpolación:

  • image.VFLIP voltea la imagen verticalmente (la parte superior pasa a ser la inferior).

  • image.HMIRROR la refleja horizontalmente (la izquierda pasa a ser la derecha).

  • image.TRANSPOSE intercambia los ejes x e y (las filas pasan a ser columnas).

La mayoría de las rotaciones surgen de componer esas tres. El módulo también expone atajos con nombre:

  • image.ROTATE_90 (= VFLIP | TRANSPOSE)

  • image.ROTATE_180 (= HMIRROR | VFLIP)

  • image.ROTATE_270 (= HMIRROR | TRANSPOSE)

En código:

img.copy(hint=image.ROTATE_90, copy_to_fb=True)

5.21.4. Manejo de la relación de aspecto

Cuando la relación de aspecto de la fuente no coincide con el rectángulo en el que se está dibujando, tres banderas deciden qué hacer con el desajuste:

image.SCALE_ASPECT_KEEP preserva la relación de aspecto de la fuente y aplica letterboxing al resultado – la fuente se escala hasta que cabe dentro del destino, rellenando el resto del destino con píxeles vacíos (en cero). Es la elección adecuada cuando mantener la fuente sin distorsionar importa más que llenar toda la salida.

image.SCALE_ASPECT_EXPAND preserva la relación de aspecto de la fuente y la recorta – la fuente se escala hasta que llena el destino, cortando las partes que sobresalen más allá del destino. Es la elección adecuada cuando llenar toda la salida importa más que ver cada parte de la fuente.

image.SCALE_ASPECT_IGNORE ignora la relación de aspecto y estira la fuente para llenar el destino, aceptando cualquier distorsión que esto introduzca. Es la elección adecuada cuando la aplicación ya ha tenido en cuenta la distorsión – cuando las dimensiones del destino no son en realidad un rectángulo de la misma escena, por ejemplo.

El valor por defecto (sin ninguna bandera de aspecto establecida) es el mismo que SCALE_ASPECT_IGNORE: estirar para llenar. Las aplicaciones a las que les importa la relación de aspecto especifican una de las tres de forma explícita.

5.21.5. Cuándo recurrir a cada cual

La mayoría de los redimensionamientos usan scale() con un par x_scale / y_scale y una pista de interpolación:

img.scale(x_scale=0.5, y_scale=0.5, hint=image.AREA)

La mayoría de las rotaciones usan la misma llamada con hint=image.ROTATE_90 o similar.

El recorte usa crop() con un roi distinto del valor por defecto:

img.crop(roi=(40, 30, 200, 150))

Cuando la fuente tiene que sobrevivir a la operación – al capturar un fotograma de referencia, al tomar una miniatura de un fotograma que está a punto de procesarse de forma destructiva – copy() produce el resultado como una imagen nueva y deja la fuente intacta:

thumbnail = img.copy(x_scale=0.25, y_scale=0.25, hint=image.AREA)

Ese valor por defecto es la verdadera diferencia detrás de los tres nombres: scale y crop transforman en su sitio, copy reserva memoria. Las palabras clave de colocación del resultado salvan la distancia: copy=True en scale o crop reserva el resultado como un búfer separado en el heap en lugar de sobrescribir la fuente, y copy_to_fb=True en cualquiera de los tres lo deposita en el búfer de fotogramas (frame buffer) para la vista previa del IDE.