Cómo funciona JPG

Cómo funciona JPG

El formato de archivo JPG fue uno de los avances tecnológicamente más impresionantes en la compresión de imágenes que apareció en escena en 1992. Desde entonces, ha sido una fuerza dominante en la representación de imágenes de calidad fotográfica en Internet. Y por una buena razón. Gran parte de la tecnología detrás del funcionamiento de JPG es excepcionalmente compleja y requiere una comprensión firme de cómo el ojo humano se ajusta a la percepción de colores y bordes.

Y como estoy en ese tipo de cosas (y a usted también, si está leyendo esto), quería analizar cómo funciona la codificación JPG, para que podamos comprender mejor cómo hacer archivos JPG más pequeños.

LA ESENCIA

El esquema de compresión JPG se divide en varias fases. La imagen a continuación los describe a un alto nivel, y analizaremos cada fase a continuación.

Conversión de espacio de color

Uno de los principios clave de la compresión de datos con pérdida es que los sensores humanos no son tan precisos como los sistemas informáticos. Científicamente, el ojo humano solo tiene la capacidad física de distinguir alrededor de 10 millones de colores diferentes. Sin embargo, hay muchas cosas que pueden influir en cómo el ojo humano percibe un color; perfectamente resaltado con ilusiones de color, o el hecho de que este vestido rompió internet. La esencia es que el ojo humano se puede manipular muy bien con respecto a los colores que percibe.

La cuantificación es una forma de este efecto en la compresión de imágenes con pérdida, sin embargo, JPG adopta un enfoque diferente: modelos de color . Un espacio de color es una organización específica de colores, y su modelo de color representa la fórmula matemática de cómo se representan esos colores (por ejemplo, se triplica en RGB o se cuadruplica en CMYK).

Lo poderoso de este proceso es que puede convertir de un modelo de color a otro , lo que significa que puede cambiar la representación matemática de un color determinado, con un conjunto de valores numéricos completamente diferente.

Por ejemplo, a continuación se muestra un color específico, y su representación en modelos de color RGB y CMYK, son del mismo color para el ojo humano, pero se pueden representar con un conjunto diferente de valores numéricos.

JPG convierte de modelo de color RGB a Y, Cb, Cr; Que se compone de Luminance (Y), Chroma Blue (Cb) y Chroma Red (Cr). La razón de esto es que los experimentos psico-visuales (también conocidos como cómo funciona el cerebro con la información que ve el ojo) demuestran que el ojo humano es más sensible a la luminancia que a la crominancia, lo que significa que podemos descuidar cambios más grandes en la crominancia sin afectar nuestra percepción de la imagen. Como tal, podemos realizar cambios agresivos en los canales CbCr antes de que el ojo humano se dé cuenta.

Submuestreo

Uno de los resultados interesantes del espacio de color YCbCr es que los canales Cb / Cr resultantes tienen detalles menos detallados; contienen menos información que el canal Y.

Como resultado, el algoritmo JPG cambia el tamaño de los canales Cb y Cr para que sean aproximadamente ¼ de su tamaño original (nota, hay algunos matices en cómo se hace esto que no estoy cubriendo aquí…), lo que se llama reducción de resolución .

Lo que es importante tener en cuenta aquí es que la reducción de resolución es un proceso de compresión con pérdida (no podrá recuperar los colores de origen exactos, pero solo una aproximación cercana), pero su impacto general en los componentes visuales de la corteza visual humana es mínimo. Luma (Y) es donde están las cosas interesantes y, dado que solo estamos reduciendo el muestreo de los canales CbCr, el impacto en el sistema visual es bajo.

Imagen dividida en bloques de píxeles de 8x8

A partir de ahora, JPG realiza todas las operaciones en bloques de píxeles de 8x8. Esto se hace porque generalmente esperamos que no haya mucha variación sobre los bloques de 8x8, incluso en fotos muy complejas, tiende a haber algo de auto-similitud en áreas locales; esta similitud es lo que aprovecharemos durante nuestra compresión más adelante.

Vale la pena señalar que en este punto, estamos presentando uno de los primeros "artefactos" comunes de la codificación JPG. El "sangrado de color" es donde los colores a lo largo de los bordes afilados pueden "sangrar" hacia el otro lado. Esto se debe a que los canales de crominancia, que expresan el color de los píxeles, tienen cada bloque de 4 píxeles promediado en un solo color, y algunos de estos bloques cruzan el borde afilado.

Transformada discreta del coseno

Hasta este momento, las cosas han sido bastante moderadas. Los espacios de colores, la reducción de resolución y el bloqueo son cosas simples en el mundo de la compresión de imágenes. Pero ahora ... ahora aparecen las verdaderas matemáticas.

El componente clave de la transformada DCT es que asume que cualquier señal numérica se puede recrear usando una combinación de funciones coseno.

Por ejemplo, si tenemos este gráfico a continuación:

Puede ver que en realidad es una suma de cos (x) + cos (2x) + cos (4x)

Quizás una mejor visualización de esto es la decodificación real de una imagen, dada una serie de funciones de coseno en un espacio 2D. Para mostrar esto, presento uno de los GIF más asombrosos de Internet: la codificación de un bloque de píxeles de 8x8 usando cosenos en un espacio 2D:

Lo que estás viendo aquí es la reconstrucción de una imagen (panel de la izquierda). En cada cuadro, tomamos un nuevo valor base (panel derecho) y lo multiplicamos por un valor de peso (texto del panel derecho) para producir la contribución a la imagen (panel central).

Como puede ver, al sumar varios valores de coseno contra un peso, podemos reconstruir nuestra imagen original (bastante bien ...)

Este es el trasfondo fundamental de cómo funciona la Transformada de coseno discreta. La idea es que cualquier bloque de 8x8 se puede representar como una suma de transformadas de coseno ponderadas, a varias frecuencias. El truco con todo esto es averiguar qué entradas de coseno usar y cómo deben ponderarse juntas.

Resulta que el problema de " qué cosenos usar" es bastante fácil; Después de muchas pruebas, se eligió un conjunto de valores de coseno para producir los mejores resultados, son nuestras funciones básicas y se visualizan en la imagen a continuación.

En cuanto al problema de “cómo deben ponderarse juntos”, simplemente (¡JA!) Aplique esta fórmula.

Te ahorraré lo que significan todos esos valores, puedes buscarlos en la página de wikipedia.

El resultado básico es que para un bloque de píxeles de 8x8 en cada canal de color, la aplicación de la fórmula anterior y las funciones básicas generará una nueva matriz de 8x8, que representa los pesos que se utilizarán durante la reconstrucción. Aquí hay un gráfico del proceso:

Esta matriz, G, representa los pesos base que se utilizarán para reconstruir la imagen (el valor decimal pequeño en la parte inferior derecha de la animación de arriba). Básicamente, para cada base, lo multiplicamos por el peso en esta matriz, sumamos todo y obtenemos la imagen resultante.

En este punto, ya no estamos trabajando en espacios de color, sino directamente con la Matriz G (pesos base), toda la compresión adicional se realiza directamente en esta matriz.

Sin embargo, el problema aquí es que ahora hemos convertido valores enteros alineados por bytes en números reales. Lo que efectivamente hincha nuestra información (pasando de 1 byte a 1 flotante (4 bytes)). Para resolver esto y comenzar a producir una compresión más significativa, pasamos a la fase de cuantificación.

Cuantificación

Entonces, no queremos comprimir los datos de punto flotante. Esto inflaría nuestra corriente y no sería efectivo. Con ese fin, nos gustaría encontrar una manera de convertir la matriz de pesos de nuevo a valores en el espacio de [0,255]. Directamente, podríamos hacer esto encontrando el valor mínimo / máximo para la matriz (-415.38 y 77.13, respectivamente) y dividiendo cada número en este rango para darnos un valor entre [0,1] al que multiplicamos por 255 para obtener nuestro valor final.

Por ejemplo: [34.12- -415.38] / [77.13 - -415.38] * 255 = 232

Esto funciona, pero la compensación es una reducción de precisión significativa. Esta escala producirá una distribución desigual de valores, cuyo resultado es una pérdida visual significativa de la imagen.

En cambio, el JPG toma una ruta diferente. En lugar de utilizar el rango de valores de la matriz como valor de escala, utiliza una matriz precalculada de factores de cuantificación. Estos QF no necesitan ser parte de la transmisión, sino que pueden ser parte del códec en sí.

Este ejemplo muestra una matriz de factores de cuantificación de uso común, uno para cada imagen base,

Ahora usamos las matrices Q y G para calcular nuestra matriz de coeficientes DCT cuantificados:

Por ejemplo, usando los valores G [0,0] = - 415.37 y Q [0,0] = 16:

Dando como resultado una matriz final de:

Observe cuánto más simple se vuelve la matriz: ahora contiene una gran cantidad de entradas pequeñas o nulas, lo que hace que sea mucho más fácil de comprimir.

Como comentario rápido, aplicamos este proceso a los canales Y, CbCr de forma independiente y, como tal, necesitamos dos matrices diferentes: una para Y y la otra para los canales C:

La cuantificación comprime la imagen de dos formas importantes: una, limita el rango efectivo de los pesos, disminuyendo el número de bits necesarios para representarlos. Dos, muchos de los pesos se vuelven idénticos o cero, mejorando la compresión en el tercer paso, la codificación de entropía.

Como tal, la cuantificación es la fuente principal de artefactos JPEG. Debido a que las imágenes en la parte inferior derecha tienden a tener los divisores de cuantificación más grandes, los artefactos JPEG tenderán a parecerse a combinaciones de estas imágenes. La matriz de factores de cuantificación se puede controlar directamente alterando el "nivel de calidad" del JPEG, que escala sus valores hacia arriba o hacia abajo (lo cubriremos en un minuto)

Compresión

A estas alturas, estamos de vuelta en el mundo de los valores enteros y podemos seguir adelante aplicando una etapa de compresión sin pérdidas a nuestros bloques. Sin embargo, al mirar nuestros datos transformados, debería notar algo interesante:

A medida que se desplaza de la parte superior izquierda a la inferior derecha, aumenta la frecuencia de los ceros. Esto parece un sospechoso principal para Run Length Encoding. Pero los pedidos de fila principal y columna principal no son ideales aquí, ya que eso intercalaría estas series de ceros, en lugar de empaquetarlos todos juntos.

En su lugar, comenzamos con la esquina superior izquierda y zig-zag en un patrón diagonal a través de la matriz, yendo y viniendo hasta llegar a la esquina inferior derecha.

El resultado de nuestra matriz luma, en este orden, se convierte en:

−26, −3,0, −3, −2, −6,2, −4,1, −3,1,1,5,1,2, −1,1, −1,2,0,0 , 0,0,0, -1, -1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 , 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

Una vez que los datos están en este formato, los siguientes pasos son sencillos: ejecute RLE en la secuencia y luego aplique algún codificador estadístico (Huffman / Arithmetic / ANS) en los resultados.

Y bum. Su bloque ahora está codificado en JPG.

Entender el parámetro de calidad

Ahora que comprende cómo se crean realmente los archivos JPG, vale la pena revisar el concepto del parámetro de calidad que normalmente ve al exportar imágenes JPG desde Photoshop (o cualquier otra cosa).

Este parámetro, al que llamaremos q, es un número entero de 1 a 100. Debe pensar en q como una medida de la calidad de la imagen: los valores más altos de q corresponden a imágenes de mayor calidad y tamaños de archivo más grandes.

Este valor de calidad se utiliza durante la fase de cuantificación, para escalar los factores de cuantificación de forma adecuada. De modo que por peso base, el paso de cuantificación ahora parece redondo (Gi, k / alfa * Qi, k)

Donde el símbolo alfa se crea como resultado del parámetro de calidad.

Cuando se aumenta alfa o Q [x, y] (recuerde que los valores grandes de alfa corresponden a valores más pequeños del parámetro de calidad q), se pierde más información y el tamaño del archivo disminuye .

Como tal, si desea un archivo más pequeño, a costa de más artefactos visuales, puede establecer un valor de calidad más bajo durante la fase de exportación.

Observe arriba, en la imagen de menor calidad, cómo vemos signos claros de la etapa de bloqueo, así como de la etapa de cuantificación.

Probablemente lo más importante es que el parámetro de calidad varía según la imagen . Dado que cada imagen es única y presenta diferentes tipos de artefactos visuales, el valor Q también será único.

Conclusión

Una vez que comprenda cómo funciona el algoritmo JPG, algunas cosas se vuelven evidentes:

  1. Obtener el valor de calidad correcto, por imagen, es importante para encontrar el equilibrio entre la calidad visual y el tamaño del archivo.
  2. Dado que este proceso se basa en bloques, los artefactos tenderán a ocurrir en bloques o "sonando"
  3. Dado que los bloques procesados ​​no se entremezclan entre sí, JPG generalmente ignora la oportunidad de comprimir grandes franjas de bloques similares juntos. Abordar esa preocupación es algo que el formato WebP hace bien.

Y si quieres jugar con todo esto tú mismo, toda esta locura se puede reducir a un archivo de ~ 1000 líneas.

¡OYE!

¿Quiere saber cómo reducir el tamaño de sus archivos JPG?

¿Quiere saber cómo funcionan los archivos PNG o cómo hacerlos más pequeños?

¿Quiere más bondad de compresión de datos? ¡Compra mi libro!