Entendiendo la compresión de imágenes

Julius Uy
Julius Uy

Sigue

19 abr, 2019 – 7 min read

Atrás en la década de 1980, Microsoft desarrolló una solución de representación de imágenes agnóstica para dispositivos para mapas de bits: el formato de archivo BMP.¹ Aquellos que utilizaban Microsoft Paint antes, disfrutarían del gigantesco tamaño de los archivos producidos a partir de simples trazos y rellenos de color.

La idea detrás del formato de archivo BMP es que a cada píxel se le asigna un valor de color. Así que si tengo un mapa de bits de 480×360 que admite 16 millones de colores (24 bits), el mapa de bits acabaría teniendo un tamaño de más de 4MB.

Figura 1 – La estructura del archivo de imagen de mapa de bits (https://en.wikipedia.org/wiki/BMP_file_format)

Esto, por supuesto, no es lo ideal si uno desea renderizar múltiples imágenes de alta calidad. De ahí que haya que preguntarse. «¿Existe una forma de optimizar la representación del mapa de bits de forma que se pueda seguir conservando la integridad visual de la imagen pero con menos recursos a utilizar?»

La respuesta es sí. Resulta que, en su mayor parte, los usuarios están más interesados en las imágenes como ayuda visual que en la exhaustividad. Por ejemplo, supongamos que me dan una imagen del puente Golden Gate como la que se muestra a continuación:

Figura 2 – Puente Golden Gate

Aunque sé que el puente real, cuando se ve en persona, tiene muchos más detalles, lo que veo aquí es suficiente para sus fines. Así que la mayoría de la gente está dispuesta a cambiar la integridad visual por la velocidad, siempre y cuando la compensación sea aceptable. Por ejemplo, si la degradación de la calidad visual subjetiva es del 1%, pero el usuario disfruta de un ahorro de espacio del 90%, es, en su mayor parte, un intercambio bienvenido.

Por lo tanto, una de las consideraciones clave en la compresión de mapas de bits es optimizar la indistinguibilidad visual. Es decir, eliminar ciertos elementos de una imagen en los que el ojo desnudo es incapaz de identificar la diferencia. Esto se llama compresión con pérdidas, que constituye la mayoría de los tipos de compresión que se ven en la transmisión de vídeo en imágenes en línea. Así es como funcionan los códecs H.264, HEVC, HEIF y JPEG.

Figura 3 – La compresión de imágenes optimiza la indistinguibilidad visual

Otros tipos de imágenes, como PNG, se comprimen sin pérdidas. La idea es que se conserva toda la integridad visual de la imagen original pero consumiendo menos bytes para representar lo mismo. Sin embargo, hay otros formatos de archivo, como WebP, que admiten tanto la compresión con y sin pérdidas. ¿Por qué queremos comprimir sin pérdidas? Como en el caso anterior, se basa en lo que queremos conseguir para la imagen. Por ejemplo, los iconos en las aplicaciones están generalmente en PNG (y recientemente, en formato de gráficos vectoriales).

La compresión de imágenes sin pérdida consiste en un mayor tamaño de archivo a diferencia de la compresión con pérdida. La razón principal es que hay menos posibilidades de comprimir la primera que la segunda. A efectos de este blog, hablaremos de la compresión con pérdidas.

Figura 4 – Una visión general de la compresión JPEG

Los pasos generales que se siguen en la compresión JPEG se hacen hasta hoy en la compresión de vídeo. A lo largo de los años, el algoritmo ha mejorado, pero los conceptos generales siguen siendo los mismos.

Paso 1. Conversión del espacio de color

La conversión de la imagen comienza convirtiendo el formato de la imagen RGB en bruto a sus valores de Croma (r y b) y Luminancia (Y). La idea es que nuestros ojos son más sensibles a los cambios de luminosidad que al color. Por lo tanto, podemos reducir la gama de colores de la imagen sin que se vea afectada la calidad visual de la misma. Esto se hace a través de Chroma Subsampling como se explica a continuación.

PASO 2. Submuestreo de croma

Muchos jugadores recordarán que el submuestreo está entre los posibles mandos que pueden ajustar para optimizar su experiencia de juego. La idea principal es que cuanto más submuestreo se haga, más rápido será el rendimiento del juego. Esto se debe a que el juego requiere menos gama de colores para renderizar.

El submuestreo de croma se denota por J:a:b siendo J el número de píxeles a submuestrear, a representa los píxeles de la fila superior y b los de la fila inferior.

Figura 5 – Submuestreo de croma

En el caso de 4:4:4, significa que en un píxel de 4×2, la primera fila (a) debe tener 4 colores y también la segunda. En el caso de 4:2:2, significa que en un píxel 4×2, la primera fila debe tener dos colores y la segunda también. En el caso de 4:2:0, significa que en un 4×2 píxeles, la primera fila debe estar representada por dos colores y la segunda fila copia lo que hay en la primera.

Como se puede ver, a través del submuestreo de croma, se puede reducir la gama de colores hasta en un 75%.

PASO 3. Transformada discreta del coseno

La compresión JPEG se realiza cortando la imagen original en trozos de 8×8 píxeles. En este paso, asignamos los coeficientes para el trozo de 8×8 basándonos en las señales que se muestran a continuación.

Figura 6 – Transformada discreta del coseno (DCT). La imagen de la izquierda es una señal de referencia de 8×8 utilizada para ponderar la imagen original. La imagen de la derecha es el trozo resultante después de pasar por la DCT.

La idea aquí es que a medida que el ojo humano se desplaza desde la parte superior izquierda de la referencia de la DCT hasta la parte inferior derecha, más difícil es su percepción. Así que normalmente, lo que ocurre es que al asignar los coeficientes, la parte superior izquierda recibe un valor muy alto y va bajando a medida que uno se mueve hacia abajo en diagonal.

Así es como podrían verse las cosas en formato numérico:

Figura 7 – Trozo original (izquierda) Nuevo trozo tras aplicar la DCT (derecha)

Paso 4. Cuantización

Después de aplicar la DCT, el siguiente paso se llama cuantificación. Aquí se aplica una tabla de cuantificación a los valores resultantes de la DCT. La tabla difiere entre los algoritmos de compresión y algunos programas permiten al usuario establecer la cantidad de cuantificación que desea utilizar. A continuación se muestra una tabla estándar:

Figura 8 – Una tabla de cuantificación estándar

Nótese que los dígitos son más altos a medida que uno se desplaza de la parte superior izquierda a la inferior derecha. Esto es intencionado. La idea de la cuantificación es que los datos resultantes de la DCT se dividen con la tabla de cuantificación. Aquí es donde la imagen comprimida pierde muchos de sus datos. Debido a que los números inferiores de la derecha son altos, la mayoría de sus valores se convertirán en cero después de la división. Así es como podría verse:

Figura 9 – Tabla de cuantificación (izquierda) Valores resultantes (derecha)

Paso 5. Codificación de entropía mediante codificación Huffman

La codificación Huffman es el último paso en la compresión. Así es como funciona.

Supongamos que quiero representar un rango de números usando bits. Además, quiero representarlos de forma que consuma la menor cantidad de bits para la representación. La forma de hacerlo es que a los números muy repetidos se les den bits más bajos. Por ejemplo, si el cero se representa mucho, generalmente le asignaría bits más bajos. Una explicación más completa de la codificación Huffman se puede encontrar aquí.

La idea aquí es que usted utiliza menos bits para representar un conjunto más largo de valores. La codificación Huffman es un algoritmo de compresión sin pérdidas que también se utiliza para comprimir archivos de texto. Haciendo esto, es posible ahorrar hasta un 50% del tamaño original.

¿Qué hay después?

La compresión es simplemente una parte de la ecuación. Cuando uno tiene que renderizar la imagen, tiene que invertir el proceso de compresión antes de poder renderizar la imagen en la pantalla.

Los JPEG son aproximadamente un 90% más pequeños que su homólogo de mapa de bits. A día de hoy sigue siendo el formato de compresión de imágenes más popular. Algoritmos más nuevos como HEIF (2013) y AVIF (2018) aumentan el rango de píxeles que uno puede utilizar para el algoritmo de compresión.²

A pesar de la popularidad de JPEG, los formatos más nuevos proporcionan una mejor compresión. WebP, por ejemplo, es en general alrededor del 70% del tamaño de JPEG y aún así es capaz de mantener la integridad visual de la imagen. Por ello, Google (la empresa que ha desarrollado WebP) ha estado presionando a los desarrolladores para que recodifiquen sus imágenes de JPEG a WebP. Sin embargo, la compatibilidad con WebP sigue siendo menor que la de JPEG. De ahí que sea necesario dar soporte a ambos formatos.

¹ «El formato de archivo BMP». Prepressure. Consultado el 19 de abril de 2019. https://www.prepressure.com/library/file-formats/bmp.

² Netflix publicó el primer conjunto de imágenes AVIF en 2018. A partir de este post, las imágenes siguen siendo accesibles aquí. Empresas como Firefox y Microsoft soportarán pronto esta imagen en sus ofertas de software.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.