¿Qué formato de imagen es más eficiente en memoria: PNG, JPEG o GIF?


Respuestas:


153

"Memoria" y "eficiencia" son términos comúnmente mal utilizados, así que te daré una respuesta para cuatro elementos diferentes que pueden afectar el rendimiento de tu juego.

Voy a simplificar demasiado demasiadas cosas para que sea breve y conciso, pero hay toneladas de imprecisiones en este texto a continuación, así que tómalo con una pizca de sal. Sin embargo, los conceptos principales deben ser entendibles.

Almacenamiento

Este es el tamaño que consumen sus imágenes en su distribución de software. Cuanto más espacio consuman sus recursos, mayor será la descarga (como desde su sitio web). Si está distribuyendo en medios físicos, como CD o DVD, probablemente tendrá que hacer algunas optimizaciones serias en este frente.

En general, JPEG comprime lo mejor para fotografías e imágenes sin bordes nítidos. Sin embargo, sus imágenes tendrán una calidad degradada porque JPEG usa compresión con pérdida (puede ajustar el nivel de compresión / degradación al exportar imágenes como JPEG. Consulte la documentación de su software de imágenes para obtener más información al respecto).

Sin embargo, por muy bueno que sea JPEG, no admite transparencia . Esto es crucial si desea tener imágenes que se muestren a través de otros, o si desea imágenes con formas irregulares. GIF es una buena opción, pero ha sido reemplazado en gran medida por PNG (solo hay unas pocas cosas que GIF admite que PNG no admite, pero son en gran medida irrelevantes en la programación de juegos).

PNG admite transparencia (y semitransparencia), comprime datos sin degradar la calidad (es decir, utiliza una compresión sin pérdidas) y comprime bastante bien, pero no tanto como JPG.

El problema surge cuando necesita una buena compresión, así como también transparencia. Si no le importan las imágenes ligeramente degradadas, puede usar programas de cuantificación PNG como pngquant , que puede probar en línea en TinyPNG . Tenga en cuenta que la degradación de la imagen realizada por la cuantización por sí sola es diferente a la de JPEG (que incluye la cuantificación y otras técnicas agresivas), así que asegúrese de probar ambas, con una amplia variedad de configuraciones.

Si desea minimizar agresivamente el tamaño de su distribución, puede procesar manualmente cada imagen de esta manera:

if the image has transparency then
    try pngquant on the image
    if the results are not satisfactory then
        revert to the non-quantized image
    end
    store as PNG
else
    try storing it as JPG with different quality settings
    if no single setting yields an image of an acceptable quality then
        try pngquant on the image
        if the results are not satisfactory then
            revert to the non-quantized image
        end
        store as PNG
    else
        store as JPG
    end
end

Consejo: está bien almacenar algunas imágenes en un formato y otras en otro formato.

Existen otros formatos especializados como DXT, ETC y PVRTC. Admiten compresión y también pueden cargarse comprimidos en la memoria, pero solo son compatibles con GPU específicas, y la mayoría de estas GPU solo admiten una de ellas, por lo que a menos que conozca las especificaciones exactas de hardware de su hardware de destino (un caso notable es iPhone / iPad, que admite texturas PVRTC), debe evitar estos formatos.

Memoria de programa

Lo incluí aquí, porque esto es lo que comúnmente se conoce como "memoria". Sin embargo, si su juego usa aceleración de gráficos (y si está haciendo un juego después de 1998, lo más probable es que lo haga), entonces lo único que consumirá memoria son sus descriptores de textura (solo unos pocos bytes por imagen), que es solo efectuado por la cantidad de imágenes, y no por su tamaño o formato (esto tiene algunas advertencias, pero en su mayoría son irrelevantes).

Si su plataforma no tiene memoria de video dedicada, no está acelerada por hardware u otros casos poco comunes, entonces la siguiente sección sobre VRAM sucederá total o parcialmente en la RAM, pero los principios principales serán los mismos.

Memoria de video

Aquí es donde sus imágenes se almacenarán una vez que su programa se esté ejecutando. En general, el formato en el que los almacenó no hará ninguna diferencia aquí, ya que todas las imágenes se descomprimen antes de cargarlas en la memoria de video.

Ahora, la VRAM consumida por sus imágenes será aproximadamente width * height * bitdepth para cada imagen cargada en VRAM. Hay un par de cosas para notar aquí:

  1. El ancho y el alto en el que se almacenan sus imágenes en VRAM no coincidirán necesariamente con los de su imagen original. Algunas GPU solo pueden manejar texturas con tamaños en potencias de 2, por lo que su imagen de 320x240 en realidad puede almacenarse en un espacio de 512x256 en VRAM, con la memoria no utilizada efectivamente desperdiciada. Algunas veces ni siquiera se le permite cargar texturas con tamaños que no son potencias de 2 (como en GLES 1.1).

    Entonces, si desea minimizar el uso de VRAM, es posible que desee considerar reducir sus imágenes y dimensionarlas con potencias de 2, lo que también tendrá la ventaja de menos cambios de estado de renderizado al renderizar. Más sobre esto más tarde.

  2. La profundidad de bits es muy importante. Por lo general, las texturas se cargan en VRAM en ARGB de 32 bits o XRGB de 32 bits, pero si su hardware puede admitir profundidades de 16 bits, y no le importa tener una profundidad de bits menor, puede reducir la mitad de la cantidad de VRAM consumida por cada imagen , que puede ser algo interesante a considerar.

  3. Pero no importa lo que hagas, el factor más importante al considerar la cantidad de VRAM que usa tu juego es la cantidad de imágenes que tienes en VRAM en un momento dado. Este es el número que probablemente quieras mantener lo más bajo posible si quieres un buen juego. Cargar y descargar texturas en VRAM es costoso, por lo que no puede cargar cada imagen cada vez que la va a usar. Debe encontrar un equilibrio entre precargar las imágenes que probablemente usará y descargarlas cuando esté seguro de que ya no las va a usar. Hacer esto correctamente no es trivial, y debes pensar en tu propia estrategia para tu juego en particular.

Velocidad de ejecución

Aunque no es "memoria", está muy relacionado con el rendimiento en los juegos. Dibujar imágenes es costoso, y desea asegurarse de que su renderizado se ejecute lo más rápido posible. Por supuesto, aquí, el formato no importa, pero otras cosas sí:

  1. Tamaño de imagen (en realidad, sería "tamaño de muestreo"): cuanto más grande sea la región de una imagen que va a dibujar, más tiempo llevará dibujarla. Renderizar una imagen enorme en una pequeña sección de la pantalla no es muy eficaz, por lo que existe una técnica llamada mipmapping , que consiste en intercambiar VRAM por la velocidad de renderizado, almacenando sus imágenes varias veces a varias resoluciones y utilizando la más pequeña que pueda proporcionar Usted la calidad requerida en cualquier momento. Mipmapping se puede realizar cuando se cargan las imágenes, lo que afectará la velocidad de carga y el uso de VRAM, o en el preprocesamiento (almacenando manualmente diferentes versiones de la misma imagen o utilizando un formato que admita de forma nativa mipmapping como DDS), lo que afectará el almacenamiento y el uso de VRAM, pero tendrá poco impacto en la velocidad de carga.

  2. Renderizar cambios de estado. Lo más probable es que desee dibujar varias imágenes diferentes en la pantalla al mismo tiempo. Sin embargo, la GPU solo puede usar una única imagen de origen en un momento dado (esto no es cierto, pero tenga paciencia conmigo aquí). La imagen utilizada actualmente para la representación es uno de los muchos estados de representación , y es costosa. Entonces, si va a usar la misma imagen varias veces (¿recuerda cuando mencioné los atlas de textura ?), Notará una gran ganancia de rendimiento si reutiliza una imagen tanto como pueda antes de cambiar el estado de renderizado y comenzar a usar un imagen diferente (hay otros estados de render aparte de esto, y ajustar el orden en el que dibuja sus cosas para minimizar los cambios de estado de render es una actividad muy común cuando se mejora el rendimiento de un juego)

Sin embargo , la optimización del uso de imágenes es un tema muy complejo, y lo que escribí aquí es una visión general muy amplia y simplificada de algunos de los factores que tendrá que tener en cuenta al escribir un juego, por lo que creo que definitivamente es mejor si lo mantiene simple, y solo optimiza cuando realmente necesites hacerlo. La mayoría de las veces, la optimización prematura es innecesaria (y a veces incluso perjudicial), así que tómalo con calma.


25
+1. Esta es una respuesta extremadamente completa que cubre mucho terreno. No estoy seguro de que el tamaño de los descriptores de textura requiera tanto espacio como sea posible, y la mención DXT / ETC / PVRTC realmente debería incluir que cada uno de ellos solo funciona en algunas plataformas: no están abiertas multiplataforma estándares que se pueden usar en todas partes, como JPEG, PNG y GIF. Pero en general, muy impresionado. ¡Esto es mucho más que una "anotación menor" además de mi respuesta! : D
Trevor Powell

9
@PandaPajama: Quizás no, pero es una respuesta sorprendentemente mejor de lo que merece esta pregunta.
Marca Thomas el

1
+1, y más si pudiera. Esta es una respuesta increíble, y debería ser un punto de referencia estándar para cualquier pregunta sobre la "eficiencia de la memoria", ya que desmiente el mito común de que usar menos memoria siempre es el mejor enfoque, y no solo para las imágenes. Un elemento que sugeriría agregar es que, en el caso no acelerado, si se necesita alguna descompresión sobre la marcha al dibujar, puede representar una sobrecarga de rendimiento inaceptable.
Maximus Minimus

2
Me gustaría sugerir PNGGauntlet . Puede realizar optimizaciones masivas sin pérdidas para los tamaños de archivo PNG, que pueden sumarse, especialmente cuando tiene muchos sprites. Lamentablemente es solo para Windows, pero las alternativas se enumeran en la página de inicio para otros sistemas operativos.
orlp

1
@DavidDimalanta No tengo experiencia con libgdx, pero como puedo ver en la documentación setEnforcePotImages, deshabilita la aplicación de potencia de texturas de 2 tamaños para OpenGLES 1.0. Esta no es una buena idea, ya que no todo el hardware admite texturas sin potencia de 2. OpenGLES 2.0 requiere soporte para texturas sin potencia de 2, por lo que si está apuntando a 2.0, puede usar texturas de cualquier tamaño. Para obtener más información, consulte la documentación de libgdx.
Panda Pyjama

26

Una vez que se carga una imagen del disco y se formatea para renderizarla, usará la misma cantidad de memoria independientemente de si esa imagen se guardó en el disco usando PNG, JPEG o GIF.

Regla general: JPEG es un formato con pérdida y degradará la calidad de la imagen para hacerla más pequeña en el disco. PNG, por otro lado, es un formato de imagen sin pérdidas, por lo que normalmente dará como resultado tamaños de archivo más grandes en el disco. GIF también es técnicamente un formato sin pérdidas, pero solo admite un máximo de 256 colores por imagen, por lo que una imagen de alto color a menudo incurrirá en una gran pérdida de calidad si se guarda como un GIF.

Sin embargo, eso es solo por su representación en el disco. En la memoria, ambos se expandirán en el mismo formato de textura, utilizando la misma cantidad de memoria, independientemente de si los guardó en el disco como PNG, JPEG o GIF.


Entonces, ¿no es la extensión del archivo lo que varía el tamaño de la memoria sino el tamaño del archivo?
Tredecies Nocturne

Ninguno. El tamaño en la memoria se basa en las dimensiones de la imagen y la profundidad de bits. Cualquier compresión en el archivo de imagen se elimina cuando la imagen se carga en la memoria para dibujarla en la pantalla. (Debido a que las GPU no pueden procesar PNG o JPEG o etc. Las imágenes se expanden en pixmaps genéricos para que las GPU puedan manejarlas).
Trevor Powell

2
Esto no es 100% cierto. Las GPU más modernas (incluidas las integradas) admiten la carga y el almacenamiento de texturas comprimidas en la memoria de gráficos, con formatos como DXT, ETC y PVRTC que son los más comunes en el mundo integrado. Por supuesto, primero tendría que almacenar sus texturas en estos formatos en lugar de PNG / JPG / GIF.
Panda Pyjama

66
La pregunta era una elección explícita entre PNG / JPG / GIF, ninguno de los cuales es compatible de forma nativa en ninguna GPU que conozca. El autor de la pregunta original tenía suficientes problemas con la distinción entre el tamaño del archivo en el disco y el espacio ocupado en la RAM que sentí que agregar más formatos de archivo solo confundiría el problema fundamental. Sin embargo, siéntase libre de dar su propia respuesta.
Trevor Powell

2
Mi comentario es solo una anotación. Si tuviera que proporcionar una respuesta, probablemente habría escrito lo mismo que usted, más mi comentario como una declaración final de cierre. No tengo nada más que agregar, así que evitaré la repetición proporcionando mi propia respuesta.
Panda Pyjama

3

Depende.

Jpeg es más eficiente para fotografías. No es sin pérdida, pero los artefactos introducidos por la compresión son menos visibles en este caso de uso.

PNG es sin pérdidas y más eficiente para pixel art con líneas definidas y pocos colores. También es compatible con alfa-transparencia.

GIF no puede hacer nada PNG no puede hacerlo mejor, excepto por su capacidad de almacenar animaciones. Pero esto solo es relevante en el contexto de las aplicaciones web. En el desarrollo de juegos, generalmente creas animaciones usando una hoja de sprites.

Tenga en cuenta que cuando utiliza un motor gráfico como Libgdx, lo más probable es que descomprima las imágenes justo después de cargarlas y luego las guardará en la memoria como valores RGBA sin comprimir. Por lo tanto, el formato de imagen solo es importante para la velocidad de carga y tenía espacio en el disco (o uso de ancho de banda cuando los envía por la red).


2

No sé mucho sobre libgdx, pero sobre formatos de imagen y gráficos:

JPEG es muy bueno en casos de fotos del mundo real. Son con pérdida, pero no verá artefactos en las fotos a menos que tome imágenes de bordes afilados con espacios de color liso, como por ejemplo texto escrito o cómics. Úselos para grandes gráficos de fondo.

GIF es obsoleto, solo puede almacenar colores paletizados (hasta 8 bits por píxel) con un color dedicado para una transparencia completa. Permite pequeñas animaciones basadas en cuadros. Una vez hubo una patente en su algoritmo de empaque para que no pudiera usarse legalmente en todos lados Debido a esa patente, se desarrolló PNG.

PNG es más o menos un mapa de bits comprimido que puede almacenar RGB + alfa (hasta 32 bits) y otros formatos de píxeles. Está especializado para descomprimir rápidamente partes pequeñas de esa imagen, lo cual es conveniente para dispositivos muy pequeños y lentos (como un teléfono celular de 10 años), pero las bibliotecas de hoy simplemente los descomprimen en mapas de bits cuando están cargados.

PNG es mejor que GIF en tamaño, velocidad y características, pero si desea almacenar mapas de bits de manera eficiente, le sugiero: .PNM.BZ2 ([editar] Debido al diferente método de empaque, .PNM.BZ2 no siempre es más eficiente que .PNG. [/ edit])

PNM / PBM / PGM / PAM son formatos de mapa de bits sin formato con encabezados KISS en texto sin formato. El uso de gzip en esos dará como resultado un tamaño de archivo similar a PNG, por lo que bzip2 es la mejor solución para eso. Si va a utilizar mapas de bits internamente en su programa, es posible que desee utilizar mapas de bits comprimidos bzip2 en un contenedor .tar o .zip. Si no tiene bzip2, usar PNM en un contenedor zip (zip con compresión máxima) podría ser similar a usar PNG. - Por lo tanto, almacenar PNG en un archivo ZIP puede tener un beneficio pequeño o nulo, lo más probable es que aumente el tiempo de carga de la imagen.

Además de eso, es una buena opción almacenar varios sprites / imágenes pequeños en un mapa de bits, especialmente cuando los necesita todos juntos en la misma situación.


¿Está el archivo PNM disponible y legible para todos los SO de teléfonos inteligentes y SO de computadoras de escritorio?
David Dimalanta

1
@David Dimalanta: No sé dónde se admite y dónde no. Si desea programar con PNM, entonces rara vez quiere usar una biblioteca, porque ese formato es demasiado simple para elegir cualquier API. Debido a eso, es legible y escribible con todos los lenguajes de programación existentes que admiten la lectura / escritura de archivos binarios. Por supuesto, puede usar Netpbm en cualquier sistema operativo, no solo en Unix. Por otro lado, no sé qué bibliotecas de imágenes generales no admiten esto. No es un formato comprimido, por lo que rara vez se utiliza para almacenar imágenes en el disco de todos modos. ver en.wikipedia.org/wiki/Netpbm_format
comonad

1

Como el formato de almacenamiento JPEG es probablemente la mejor opción para algunas texturas como césped o paredes, donde la pérdida de información es probablemente indetectable. Seguido por PNG cuando necesita transparencia o cuando no puede pagar con la pérdida de información, por ejemplo, sprites en un juego 2D (el jugador, los enemigos, un cofre del tesoro), probablemente quiera usar PNG para esas imágenes.

Cuando se habla del costo de la memoria, el formato utilizado para almacenar gráficos de juegos en el sistema de archivos no es relevante en absoluto. Si almacena almacenamientos intermedios de píxeles en VRAM o RAM (procesador de software), probablemente los tenga almacenados sin comprimir, porque los juegos favorecen la lectura rápida de píxeles frente a la memoria utilizada por cada almacenamiento intermedio de píxeles.

Tener datos comprimidos almacenados en la memoria no tiene sentido, excepto que mantiene algún tipo de caché para guardar las lecturas del disco, pero probablemente tenga que leer desde ese caché a un estado sin comprimir para las imágenes en uso en un momento dado de su juego.

Los datos de imagen comprimidos tienen un poco más de sentido si fuera posible una rápida decodificación de hardware. Al menos para el mapeo normal, recuerdo esto http://en.wikipedia.org/wiki/3Dc . Con eso puedes guardar algo de VRAM. Todavía no conozco otros ejemplos de decodificación de hardware.

En resumen: cualquiera que sea el formato utilizado para que su juego almacene gráficos en un almacenamiento persistente, tendrá que decodificarlo y mantener una versión sin comprimir en la memoria dinámica, la memoria de video o ambas, para poder renderizarlas rápidamente cuando sea necesario.

Finalmente: soy un chico de escritorio. Cuando digo "memoria" siempre me refiero a la memoria dinámica. Cuando digo "disco", "sistema de archivos" o "almacenamiento persistente", siempre me refiero a lo que sea que su dispositivo use como almacenamiento persistente, generalmente pienso en discos duros. Cuando dijiste "eficiencia de memoria", lo tomé por "memoria dinámica", no por "almacenamiento persistente". Últimamente, veo a muchas personas que usan la palabra "memoria" para referirse a "almacenamiento persistente" (¿tal vez esa es la terminología de los dispositivos móviles?).


¿Es posible también que no sea que el JPEG ni PNG afecten la memoria sino el tamaño del archivo?
Tredecies Nocturne

Si. Elegirás cualquiera de los formatos discutidos pensando en ahorrar espacio en disco o ancho de banda. Al cargar imágenes en el juego, cualquier biblioteca / motor que conozca los decodificará en búferes de píxeles sin comprimir (piense en los búferes de píxeles como una matriz de valores RGB o RGBA, donde cada canal generalmente ocupa 1 Byte en RAM / VRAM si usa 32 bits por píxeles, eso es probablemente lo que todos están haciendo).
Hatoru Hansou
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.