Mir Rodriguez Lombardo

La frontera 32767: trabajando con imágenes enormes (I)

(english version here )


Si alguna vez tienes que exportar imágenes grandísimas a partir de QGIS, como lo necesitaba recientemente para un mapa tamaño mural, te espera una sorpresa. Cualquier cosa más grande que 32767 píxeles en una dimensión te causará problemas. Tratar de resolver este problema me enseñó mucho, llevándome por el camino de la composición de imágenes utilizando ImageMagick, así como el uso de la línea de comandos de Inkscape, los cuales resultaron ser muy interesantes, poderosos y probablemente útiles para alguien. Así que aquí está el tutorial.

Aquí mostraré como armar esta capa de fondo de México para un mapa muy grande:

Sentinel con uso de suelo con sombreado y estados

La capa es una combinación de una imagen de satélite Sentinel con color corregido, una capa de uso de suelo y de estados de México de INEGI, una de relieve sombreado basada en un modelo SRTM y una capa de batimetría con datos de GEBCO.

Este artículo cubre las siguientes aplicaciones: QGIS (una aplicación GIS para mapeo), Inkscape (edición de vectores), SVGO (para reducir el tamaño de archivo de imágenes vectoriales) e ImageMagick (procesamiento de imagen de línea de comando).

¿Se pueden hacer estas cosas con ArcGIS, Illustrator y/o Photoshop? No tengo idea, ya que no tengo/me subscribo/pirateo estas cosas. Mi flujo de trabajo para este proyecto y el tutorial a continuación consiste únicamente de herramientas libres y de código abierto.

Problema: tu bitmap es demasiado grande para QGIS

El mapa que estaba haciendo se tenía que imprimir a un tamaño de 4.4 x 3.3 metros a una resolución de 300 dpi. Necesitaba entonces exportar en formato PNG desde QGIS con un tamaño de 52438 x 39213 píxeles, lo que suma aproximadamente 2 gigapíxeles (2 mil millones de pixeles). Normalmente exportaría cada capa del mapa por separado y luego las armaría usando KritaKrita, a diferencia de GIMP, tiene capas de archivo que se actualizan automáticamente cuando sobrescribo el archivo en el cual está basado una capa., lo cual hallo mucho más rápido y flexible que hacerlo en QGIS si quiero hacer cambios sutiles en transparencias o balance de color. Entonces, en este caso, comencé exportando la capa de uso del suelo. Salió así:

Una imagen cortada al exportar

La exportación tardó una eternidad y luego el resultado estaba cortado en el eje x exactamente a 32767 píxeles (probablemente también se cortó horizontalmente). No me estaba quedando sin memoria durante el proceso, así que era otra cosa. Esta cifra sonaba familiar, y resulta que 32767 es el número más grande que se puede escribir como un valor entero con signo de 16 bits (binario) en la memoria de una computadora. Por lo tanto, debe ser algo complicado de arreglar (algo que debería reescribirse de 16 a 32 bits, tal vez). Resulta que hay un issue sobre esto en los repositorios de QGIS abierto desde 2014 y no parece que vaya a ningún lado. ¿Quién quiere exportar imágenes tan grandes? Pues yo. Mi mapa debe mirarse bien de cerca, por lo que a 300 dpi (en realidad preferiría usar 600 dpi…) es un número grande. Así que necesitaba resolver.

Solución: exportar como vectores y convertir a bitmap

Maíz apachito
Un ícono que representa el maíz apachito
(crédito: Kitzia Sámano)
En lugar de exportar como una imagen PNG, podríamos exportar nuestro mapa como un vector PDF o SVG. Pero mi mapa es realmente complejo, una de las capas de maíz tiene más de 22,000 símbolos, como el que aquí representa el maíz apachito, una de las muchas razas en México, con 230 granos de maíz individuales en cada símbolo. Si sumamos todo eso es un vector muy grande. QGIS se bloquea en medio del intento de exportar el mapa como vectores.

Entonces, la solución es exportar cada capa vectorial por separado. Funciona bien casi siempre (ver abajo), y ahora tenemos un archivo SVG para cada capa. Luego podemos abrirlos en Inkscape, pulsar ctrl+shift+e y convertlos a PNG con una resolución de 300 dpi para obtener nuestra gran capa en bitmap. Bueno… resulta que Inkscape alcanza exactamente el mismo límite de tamaño de exportación de bitmaps que QGIS, como se puede ver en este issue de 2019.

Solución: convertir a DOS bitmaps

Para evitar el límite de exportación del tamaño de la imagen en Inkscape, podríamos intentar exportarla por separado en dos mitades más pequeñas y luego encontrar una forma de pegarlas de nuevo. Si juegas con las cifras de extensión de la imagen en el panel Exportar imagen, puedes exportar en dos mitades, lo cual funciona, ya que ambas son más pequeñas que el límite de 32767:

El panel de exportación de Inkscape

Pero entonces, ¿iba a comenzar a escribir números uno por uno para todas mis capas? ¡Qué va! Para eso tenemos la terminal.

La línea de comando de Inkscape

a terminal
La terminal no come

Si estás realizando algún tipo de procesamiento, conversión, animación o composición de imágenes masivas/complejas, es hora de que dejes el mouse y explores la terminal, ya que puede facilitarte la vida y liberarte de tareas realmente aburridas.

Cuando decimos “línea de comando” o “terminal” o “cli” o “shell” nos referimos al uso de una aplicación en le das órdenes a tu computadora escribiendo en lugar de haciendo clic. En Windows, OS X y Linux, esta aplicación se llama terminal, simplemente búscala y ábrela. De verdad, dedícale un poco de tiempo a darte cuenta de que realmente puedes usar la terminal y nada malo va a pasar.
Resulta que puede hacer cosas como convert un vector SVG en un bitmap PNG usando Inkscape desde la línea de comando. el formato es:

inkscape --export-area=x0:y0:x1:y1 --export-dpi=DPI --export-filename=nombre_de_archivo.png tu_vector.svg

Probemos con una de las capas de costa y mar que hice en QGIS con datos de batimetría GEBCO y exportadas como SVG (con algo de transparencia integrada), un archivo de 46 MB. Abro mi terminal y ejecuto el primer comando para exportar el lado izquierdo de mi vector,

inkscape --export-area=0:0:8390:12548 --export-dpi=300 --export-filename=out_left.png mar_color.svg

lo que da como resultado esta imagen PNG (de tamaño reducido y convertida a jpg para los fines de este sitio web):

lado izquierdo de la capa del mar

Esta operación usa alrededor de 10 GB de RAM y toma alrededor de 9 minutos en mi computadora. Vamos con el lado derecho:

inkscape --export-area=8391:0:16781:12548 --export-dpi=300 --export-filename=out_right.png mar_color.svg

lado derecho de la capa del mar

Ahora tengo una imagen exportada de 300 dpi para cada mitad de mi vector. Obtuve los números que ves arriba por ensayo y error. Pero en realidad funciona así: los números están en píxeles, lo que en Inkscape significa 1/96 de pulgada. La fórmula es:

ancho de imagen de píxeles de inkscape = ancho de imagen final en píxeles * 96 / dpi

Entonces, cuando hago eso para mi tamaño de imagen deseado de 52438 para 300 dpi, el ancho de la imagen en píxeles de Inkscape es 16780.8.

¿Por qué no obtengo el mismo error para la altura de la imagen, que es mayor que 32767 píxeles? ¡No lo sé y no me importa!

Seguimos en la línea de comando: unir imágenes

Primero intenté unir estas dos mitades de mi capa con GIMP y Krita, pero fue espantosamente lento y doloroso. Y dado que ya estamos aquí, también podríamos permanecer en la línea de comando e intentar automatizar todo lo más posible para todas mis capas. Presentamos a ImageMagick.Logotipo de ImageMagick
ImageMagick es una herramienta multiplataforma (Linux, Windows, OS X y Android) para mostrar, modificar, convertir y combinar imágenes desde la línea de comandos. Es bastante antiguo, fue creado en 1987, por lo que hay muchas guías de la comunidad sobre cómo hacer la mayoría de las cosas, pero no demasiadas para lo que explico en este tutorial (composición de imágenes). Es libre y de código abierto, por lo que otras aplicaciones lo utilizan para tareas básicas de procesamiento de imágenes. Un fork del proyecto original, llamado GraphicsMagick se hizo en 2002, no lo he usado aunque dicen que es más rápido y eficiente. La última versión de ImageMagick es 7.x, pero aquí usé la versión 6.9.11, que tiene algunas diferencias significativas. Consulta las páginas de legacy.ImageMagick.org para ver la documentación de la versión 6.x.

Esta maravillosa herramienta de línea de comandos es conocida sobre todo por sus comandos convert y mogrify, que a menudo se ven en los foros de Internet como la solución para convertir o cambiar el tamaño de imágenes desde y hacia muchos formatos y tamaños, ya sea individualmente o en masa. Uno de los usos de una herramienta de línea de comandos a diferencia de una con la que interactúas usando el mouse, es que puedes usarla como parte de un script o programa simple para realizar tareas repetitivas. En el caso más simple de automatización, puedes simplemente copiar y pegar un comando que estás utilizando como parte de un flujo de trabajo de procesamiento de imágenes.

Pegando imágenes con ImageMagick

La primera tarea de ImageMagick es unir las dos mitades PNG que producimos a partir de nuestro SVG. Esto se llama “appending” en ImageMagick. El formato es este:

convert [archivo 1] [archivo 2] [archivo 3] ... [+append (horizontalmente) | -append (verticalmente)] [archivo de salida]

En nuestro caso, queremos pegar imágenes una al lado de la otra horizontalmente, así que enumeramos los archivos de izquierda a derecha y usamos +append. Entonces el comando va así:

convert out_left.png out_right.png +append mar_color_300.png

capa completa del mar

Dado que estamos utilizando PNG, todo el proceso es sin pérdidas (en inglés, lossless, es decir, no se pierde calidad en ninguna parte del proceso). Ten cuidado si usas JPG con cualquier cantidad de compresión, ya que las imágenes pueden perder calidad con cada paso.

Optimización de SVG

Cuando trabajes con archivos SVG grandes y complejos, deberías considerar la posiblidad de optimizar tu archivo. Hay muchas maneras en las que los SVG producidos por QGIS e Inkscape se pueden hacer más pequeños, lo que presumiblemente reduciría el tiempo de procesamiento cuando estés haciendo cosas con ellos. Hay una utilidad llamada SVGO que hace esto. Una forma popular y fácil de usarlo es a través de una aplicación web, SVOMG donde puedes cargar tu archivo, seleccionar tu configuración de optimización y descargar tu nuevo archivo. Esto funciona bien para íconos individuales, donde en mi experiencia obtuve una reducción de tamaño del 30-50%. Esto probablemente puede hacer que QGIS sea significativamente más rápido si tienes muchas instancias de cada ícono en tu mapa.

En mi caso, quería reducir el tamaño de todas mis capas exportadas, algunas de las cuales eran muy grandes. Por ejemplo, la de maíz por municipio es de 104 MB. Para archivos extra grandes, la versión web de SVGO no funcionará, por lo que deberás instalarla en tu compu, lo que significa que debes primero instalar Node.js (consulta la documentación de SVGO). Aquí hay un detalle de mi capa:

Detalle capa municipio maíz

Hay 8 símbolos SVG diferentes que se usan miles de veces en esta capa. La especificación del formato SVG permite “clonar” símbolos repetidos, lo que significa definirlos una vez en el archivo SVG y luego referirse al original cada vez que se usa el símbolo. En cambio, QGIS dibuja los símbolos cada vez. Esto significa que mi capa se beneficiaría un montón si puedo optimizar esto. El comando SVGO que utilicé es así:

svgo cultivos_maiz_municipios.svg --config svgo.config.js -o cultivos_maiz_municipios_opt.svg

En este caso, utilicé un archivo de configuración que pedía específicamente reusePaths, que por defecto está desactivado. Mi archivo svgo.config.js era este:

module.exports = {
    plugins: [
        {
        name: "preset-default",
        params: {
            cleanupAttrs: false,
        },
        name: "reusePaths",
        },
    ],
};

Después de optimizar con SVGO, mi archivo pasó de 104 MB a 9,5 MB, ¡una reducción de tamaño del 91%! Es posible que no obtengas una reducción tan drástica en el tamaño de tus archivos y que en realidad no te haga falta optimizar tus SVG, pero ten en cuenta que esto existe y que SVGO puede ser un componente de tu flujo de trabajo.

Problema: tu vector todavía es demasiado complejo para QGIS

Tenía una capa aún más compleja, la de maíz por núcleo agrícola. Esta es la capa con los 22.000 símbolos, de entre uno de 8 posibles marcadores SVG (8 razas de maíz) que tienen entre 24 a 67 KB de tamaño. QGIS no pudo exportar este archivo vectorial: simplemente falla silenciosamente (sin llegar a quedarse sin memoria), al igual que cuando intenta exportar todo el mapa. Esta capa única está llegando a algún límite en QGIS, pero ni siquiera he buscado un informe de error al respecto, en ese momento ya sabía que lo que estaba tratando de hacer estaba fuera de los límites del trabajo de la gran mayoría de los usuarios de QGIS así que fui directamente a busar una solución (prometo informar o agregar un comentario sobre este bug pronto). Entonces, ¿cómo exportar vectores extra complejos desde QGIS?

Solución: usa clipping en QGIS para hacer exportaciones parciales

La solución es hacer exportaciones parciales de tu lienzo del compositor, lo suficientemente pequeñas como para que QGIS pueda manejarlas. Hay una opción algo oculta en el compositor de impresión de QGIS para “recortar” las exportaciones de tu lienzo. Primero debe hacer varios rectángulos en tu composición que cubran todo tu lienzo. Asegúrate de usar el snapping/ajustes o verifica la posición y el tamaño de cada rectángulo para estar seguro de que cubren absolutamente toda la superficie de tu mapa.

Recortando rectángulos en QGIS composer

Selecciona tu mapa y luego ve a la pestaña Propiedades del elemento. El icono Clipping settings (en mi versión de QGIS no está traducido) está oculto en la parte superior (aquí uso screenshots en inglés, pero están en el mismo lugar en la versión en español):

El icono de clipping de QGIS

En el cuadro de diálogo Clipping Settings, asegúrate de que la casilla Clip to item esté activada y elige el nombre de uno de tus rectángulos.

Configuración de recorte del compositor de QGIS

Cuando exportas tu mapa, se exporta un SVG de tamaño completo, pero solo se incluye el contenido debajo de la forma seleccionada.

Una de las imágenes exportadas

Hice un pequeño zoom para que se vea más que un borrón (recuerda que este mapa se va a imprimir a 4 metros de ancho). Repite los pasos anteriores una vez para cada una de tus formas de clip. Terminé con 5 archivos con un tamaño total de 821 MB (aproximadamente el tamaño esperado dada la cantidad de símbolos y el tamaño de los marcadores SVG). Después de enviarlos a través de SVGO (cada uno por separado), obtuve un tamaño total de 72 MB, nuevamente una reducción de tamaño del 91%.

¿Podría esta solución de clipping haber funcionado como una forma de exportar todo el mapa, aunque seguramente con muchas más de 5 piezas? Quién sabe, lo acabo de pensar. Hazme saber si esto funciona para ti.

Luego rasterizamos estos cinco vectores (cultivos_maiz_1.svg, cultivos_maiz_2.svg, etc.) usando los comandos explicados anteriormente: dos llamadas a inkscape para convertir la mitad de cada imagen en bitmap, luego una llamada a convert para añadir las imágenes. Podemos comenzar a simplificar un poco las cosas aquí, usando && para enviar varios comandos separados de una sola vez sin tener que sentarnos y esperar a que se complete cada uno. Se ejecutarán en secuencia:

inkscape --export-area=0:0:8390:12548 --export-dpi=300 --export-filename=out_left.png cultivos_maiz_1.svg &&     inkscape --export-area=8391:0:16781:12548 --export-dpi=300 --export-filename=out_right.png cultivos_maiz_1.svg &&     convert -monitor +append out_left.png out_right.png cultivos_maiz_1.png

Quedamos al final con 5 bitmaps, todos de tamaño completo, cada uno con una sección de la capa vectorial.

Superponiendo capas con ImageMagick

Así que ahora tengo un montón de capas de mi mapa en formato PNG. Necesito volver a armarlos con diferentes niveles y modos de transparencia, todo tipo de máscaras y cosas. Ya sé que no puedo hacer esto en GIMP o Krita, solo abrir una de estas capas de 52438 x 39213 requiere mucho tiempo y memoria, ni hablar de 15 capas. Y tengo mejores cosas que hacer con mi tiempo.

En el mundo de ImageMagick, la superposición de imágenes se denomina composición. Técnicamente, implica juntar exactamente dos imágenes, configurando transparenciaTen en cuenta que los términos “transparencia”, “opacidad” y “alfa” pueden o no ser intercambiables, pueden significar cosas diferentes en ImageMagick y otras aplicaciones y eso generalmente podría volverte loco., posición, tamaño, etc. Estamos trabajando con imágenes sin pérdida (PNG), casi todas ellas a resolución nativa, que no cambiará (a excepción de la capa de relieve sombreado, que es de mucha menor resolución dado que se origina de datos SRTM). Esto significa que no debería tener ningún problema de calidad mientras compongo mis capas una por una, solo necesito planificar el proceso.

La documentación de ImageMagick no es la más fácil de entender, la sintaxis de los comando es un poco compleja y no hay muchos ejemplos en línea de lo que necesitaba hacer. Así que solo enumeraré aquí algunos de los comandos que usé con una explicación de lo que hace cada sección. Con suerte, algunos de estos son lo que necesitas para tu caso de uso específico.

Puntos generales

Preparando capas de fondo y jugando con la transparencia

El primer paso fue armar las capas inferiores: uso del suelo, imágenes de satélite Sentinel, relieve sombreado, costa y mar. Empecé con solo el uso del suelo y el relieve sombreado:

convert -monitor \( land_use.png -channel a -evaluate set 25% \) \
\( \( -size 52438x39213 xc:white \( -resize 52438x39213 shades_140.png \) -composite \) -channel a -evaluate set 75% +channel \) \
-compose multiply \
-composite \
\( land_use.png -channel a -separate +channel \) -alpha off \
-compose copy_opacity \
-composite 1.png

(estoy usando el backslash \ para mostrar aquí el comando en varias líneas y que sea más legible, normalmente lo omitirías al poner esto en la terminal y se vería así:

convert -monitor \( land_use.png -channel a -evaluate set 25% \) \( \( -size 52438x39213 xc:white \( -resize 52438x39213 shades_140.png \) -composite \) -channel a -evaluate set 75% +channel \) -compose multiply -composite \( land_use.png -channel a -separate +channel \) -alpha off -compose copy_opacity -composite 1.png

)

Lo cual significa:

  1. convert: El comando de ImageMagick
  2. -monitor: dame un porcentaje del progreso a medida que haces las cosas
  3. land_use.png: mi capa inferior, una capa de uso de suelo mexicano del INEGI que he coloreado para que se mezcle con la imagen de Sentinel utilizada para los países vecinos: Capa de uso del suelo
  4. -channel a -evaluate set 25%: Dale a esa capa un 25% de transparencia
  5. ( ( -size 52438x39213 xc:white ( -resize 52438x39213 shaded_relief.png ) -composite ): toma la capa de relieve sombreado, Capa de relieve sombreado cámbiale el tamaño a las dimensiones de mi mapa y colócalo en un fondo blanco (el fondo blanco va primero, el orden es de abajo hacia arriba). Entonces hay dos capas: un fondo blanco y el relieve sombreado, ambos del mismo tamaño, juntados con el -composite al final Relieve sombreado sobre fondo blanco
  6. -channel a -evaluate set 75% +channel ): toma el resultado del paso anterior y dale una transparencia del 75% Relieve sombreado sobre fondo blanco al 75%
  7. -compose multiply -composite: Superpón esas dos imágenes (land_use.png y la que acabamos de hacer con el fondo blanco y el relieve sombreado al 75%) usando el modo de fusión de multiplicación Uso del suelo y multiplicación del relieve sombreado
  8. \( land_use.png -channel a -separate +channel \) -alpha off -compose copy_opacity -composite 1.png: Toma la parte transparente (el canal alfa) de land_use.png y cópialo en la imagen anterior (uso de la tierra y relieve sombreado) y aplánalo, obteniendo así una imagen final (1.png) con la misma transparencia que el uso del suelo. Esto es necesario porque el relieve sombreado no tiene un mar transparente y necesitamos que el mar sea completamente transparente para el siguiente paso. Imagen de fondo final

El uso de memoria de esta operación alcanza un máximo de aproximadamente 51 GB de RAM, ya que ImageMagick realiza un montón de procesamiento en la memoria.

Agregar imágenes de Sentinel y mar

Ahora tomamos nuestra imagen satelital Sentinel, que usamos para el color natural de los EE.UU. y Guatemala, a la cual le hemos ajustado el color con GIMP para que coincida con la capa de uso del suelo utilizada como fondo de México.

Imagen de Sentinel

(crédito de la imagen: Agencia Espacial Europea - ESA) y la apilamos con un 20 % de transparencia sobre un fondo blanco,

20% imagen de Sentinel

luego superponemos el resultado de nuestra composición de uso de suelo que hicimos con anterioridad,

Sentinel más uso del suelo más sombreado

Luego usamos una capa de máscara de costa creada con QGIS,

Capa de máscara de costa

y lo ponemos encima de nuestra composición Sentinel/uso del suelo/relieve sombreado para deshacernos de la imagen del mar Sentinel,

Sentinel más uso del suelo más sombreado

Finalmente tenemos nuestra capa de mar,

capa completa del mar

y la ponemos encima de todo:

Sentinel más uso del suelo más sombreado.

Este es el comando completo para ese paso:

convert -monitor -size 52438x39213 xc:white \
\( sentinel.png -channel a -evaluate set 20% \) -composite \
1.png -composite \
costa_mask.png -composite \
mar_color_300.png -composite \
2.png

Te dejaré deconstruirlo en sus partes. Cada -composite dice:

La operación final para completar nuestro fondo es bastante simple: simplemente agrego la línea de costa, las fronteras estatales y los ríos en la parte superior, en ese orden:

convert -monitor 2.png costa.png -composite estados.png -composite rios.png -composite 01_FONDO.png

Terminamos esta sección con un nombre de archivo para la primera de nuestras piezas principales, 01_FONDO.png. Así vamos avanzando de manera modular y si hay que hacer algún cambio lo hacemos en cada una de nuestras piezas.

Este es el final de la Parte I de este tutorial. La Parte II se ocupará de la automatización de todo mediante secuencias de comandos, el posicionamiento de imágenes que son más pequeñas que el lienzo y la producción del resultado final. Me encantaría leer tus comentarios.

Comentarios

Los comentarios son moderados antes de publicarse.


Debes poner tu nombre

Necesito tu dirección de email

Por favor escribe un comentario

También puedes comentar vía Twitter