Este tutorial explica cómo optimizar el tamaño de archivos de imagen PNG para sitios web, usando la línea de comandos en un sistema linux. Hay maneras de hacerlo usando programas con interfaz gráfica, pero aquí usaremos la línea de comandos para poder trabajar con un lote de imágenes de una vez, hacer una edición masiva usando un loop en bash, y para tener opción de hacerlo en una máquina sin enterno de ventanas, como un servidor al que nos conectemos remotamente.
Vamos a probar distintas combinaciones para la optimización y comprobar cuál es la que produce archivos menos pesados.
Para empezar necesitamos tener tres paquetes instalados en nuestro sistema linux, en mi caso Debian Squeeze: un optimizador (pngcrush, optipng o pngnq), convert e identify:
- pngcrush, optipng y pngnq son programas que optimizan archivos PNG sin disminuir su calidad. Con uno de ellos vale, no hacen falta todos. Yo me quedo con pngcrush, pero todos optimizan de manera casi idéntica.
- convert e identify son dos programas del paquete ImageMagick, un conjunto de herramientas muy potente para editar imágenes y vectores desde la línea de comandos.
A la hora de preparar archivos PNG para la web tenemos que tener en cuenta los siguientes parámetros de la imagen: dimensión en píxeles, transparencia, calidad y compresión, número de colores y si el archivo está entrelazado.
Primero editaremos la imagen usando convert, para reducir sus dimensiones y darle la calidad y compresión adecuadas, y ajustar el número de colores.
Posteriormente optimizaremos la información del archivo con pngcrush.
Iremos monitorizando los resultados con identify, que nos devuelve, entre otros datos, el tamaño del archivo.
Por último, tras encontrar la combinación de parámetros que nos interese, construiremos un pequeño loop para aplicar el comando a un grupo de imágenes.
Tomamos una imagen para experimentar, test.png
, de 1024×768 píxeles. Según identify:
$ identify test.png test.png PNG 1024x768 1024x768+0+0 8-bit DirectClass 1.632MB 0.060u 0:00.070
Ahora usamos convert para reducir sus dimensiones y darle una calidad adecuada para su uso en inernet:
$ convert -resize 600x600 -antialias -quality 00 -verbose test.png 600-00-test.png test.png PNG 1024x768 1024x768+0+0 8-bit DirectClass 1.632MB 0.070u 0:00.070 test.png=>600-test.png PNG 1024x768=>600x450 600x450+0+0 8-bit DirectClass 541KB 0.630u 0:00.399
Vamos a ver qué ha hecho cada parámetro de convert:
-resize 600x600
. Redimensiona la imagen. El valor 600×600 indica la cantidad de píxeles que quremos que tenga su lado mayor; el otro lado, lo redimensiona proporcionalmente, a 450 píxeles en nuestro caso.-antialias
. Trata convenientemente las transparencias de la imagen.-quality 00
. Asigna el nivel de compresión a una calidad determinada de la imagen. El valor se compone de dos dígitos independientes. El primero es el nivel de compresión, 0 es el máximo, 9 es el mínimo. El valor 0 además utiliza un tipo de compresión llamado Huffman que es sustancialmente más eficaz: hay un gran salto entre los valores 0 y 1. El segundo dígito es el tipo de calidad de la imagen, la codificación de los datos del archivo. Los valores van de 0 a 5. Para imágenes con poca información, planas, elegiremos valores próximos a 0; para imágenes con mucho detalle (por ejemplo un paisaje), elegiremos valores próximos a 5. Más información sobre los valores de quality para PNG.-verbose
. Devuelve información del proceso en la línea de comandos
Además de la combinación anterior vamos a probar otras, fijando como constante la dimensión, para ver cómo influye el parámetro quality:
$ convert -resize 600x600 -antialias -quality 05 -verbose test.png 600-05-test.png $ convert -resize 600x600 -antialias -quality 10 -verbose test.png 600-10-test.png $ convert -resize 600x600 -antialias -quality 15 -verbose test.png 600-15-test.png $ convert -resize 600x600 -antialias -quality 95 -verbose test.png 600-95-test.png
Ahora vamos a ver qué dice identify de cada una de las imágenes creadas:
$ identify * 600-00-test.png PNG 600x450 600x450+0+0 8-bit DirectClass 542KB 0.030u 0:00.030 600-05-test.png[1] PNG 600x450 600x450+0+0 8-bit DirectClass 749KB 0.020u 0:00.019 600-10-test.png[2] PNG 600x450 600x450+0+0 8-bit DirectClass 705KB 0.020u 0:00.019 600-15-test.png[3] PNG 600x450 600x450+0+0 8-bit DirectClass 705KB 0.030u 0:00.019 600-90-test.png[4] PNG 600x450 600x450+0+0 8-bit DirectClass 726KB 0.020u 0:00.019 600-95-test.png[4] PNG 600x450 600x450+0+0 8-bit DirectClass 542KB 0.020u 0:00.019 test.png[5] PNG 1024x768 1024x768+0+0 8-bit DirectClass 1.632MB 0.060u 0:00.059
Podemos ver que las imágenes más ligeras son en las que hemos aplicado la opción 00 y la 95. Esto quiere decir que no debemos tomar los valores como una escala, sino elegirlos en función del detalle de la imagen de origen.
Hay un parámetro de convert que entrelaza el archivo PNG para que se cargue progresivamente en una web, y no línea a línea. Se puede usar si es necesario, pero generalmente aumenta el peso de los archivos. El parámetro es interlace
y puede recibir dos valores line
y plane
, que definen el tipo de entrelazado.
$ convert -resize 600x600 -antialias -quality 00 -interlace plane -verbose test.png 600-00-plane-test.png $ convert -resize 600x600 -antialias -quality 00 -verbose test.png 600-00-line-test.png
Veamos cómo afecta al tamaño:
$ identify 600-00-* 600-00-line-test.png PNG 600x450 600x450+0+0 8-bit DirectClass 603KB 0.060u 0:00.059 600-00-plane-test.png[1] PNG 600x450 600x450+0+0 8-bit DirectClass 603KB 0.070u 0:00.080 600-00-test.png[2] PNG 600x450 600x450+0+0 8-bit DirectClass 542KB 0.020u 0:00.019
Por último podemos jugar con la cantidad de colores de la imagen. Podemos reducirla a 256, a ver qué pasa:
$ convert -resize 600x600 -quality 00 -antialias -colors 256 -verbose test.png 600-00-c256-test.png
Ahora identify:
$ identify 600-00-c256-test.png 600-00-c256-test.png PNG 600x450 600x450+0+0 8-bit DirectClass 552KB 0.020u 0:00.029 600-00-test.png[1] PNG 600x450 600x450+0+0 8-bit DirectClass 542KB 0.020u 0:00.019
¡Maldición! Podemos comprobar que no solo no ha disminuido, sino que ha aumentado de tamaño. Un poco de paciencia, todo tiene su explicación.
Llegó el momento de pasar la imagen por el programa de optimización de archivos PNG, pngcrush. Este tipo de programas no siempre pueden optimizar los PNG. Lo que hacen es eliminar información innecesaria sin disminuir la calidad; si el archivo tiene lo justo, lo deja igual. En cualquier caso, no le va a hacer pesar más.
Pasamos por pngcrush dos de las versiones que hemos generado con convert: en la que hemos usado el parámtro -quality 00, que es la que menos pesaba, y a la que hemos reducido la cantidad de colores:
$ pngcrush -rem allb -brute -reduce 600-00-test.png pngcrush-600-00-test.png $ pngcrush -rem allb -brute -reduce 600-00-c256-test.png pngcrush-600-00-c256-test.png
Para una explicación de los parámetros de cada uno de estos dos programas se puede consultar el tutorial de 5-Minute PNG Image Optimization. Hay que tener en cuenta que optipng sustituye el archivo de origen, no crea otro optimizado.
Si comprobamos con identify veremos que pngcrush no ha reducido el peso de la versión a la que aplicamos quality 00, lo que quiere decir que estaba ya optimizada. En cambio, la versión en la que redujimos el número de colores ha conseguido reducirla casi en un 50%. Ahora se explica que cuando la generamos pesase más que la original, porque convert reduce los colores, pero no la cantidad de información del archivo, no optimiza.
$ identify * 600-00-test.png PNG 600x450 600x450+0+0 8-bit DirectClass 542KB 0.020u 0:00.030 pngcrush-600-00-c256-test.png[9] PNG 600x450 600x450+0+0 8-bit DirectClass 297KB 0.010u 0:00.010 pngcrush-600-00-test.png[6] PNG 600x450 600x450+0+0 8-bit DirectClass 542KB 0.020u 0:00.030
Aplicando convert y pngcrush hemos reducido la imagen original, de 1.632MB, a 297KB. Los dos comandos juntos:
$ convert -resize 600x600 -quality 00 -antialias -colors 256 -verbose test.png 600-00-c256-test.png $ pngcrush -rem allb -brute -reduce 600-00-c256-test.png pngcrush-600-00-c256-test.png
Para aplicar estas acciones a un grupo de imágenes podemos usar un loop de bash:
$ for i in *.png; do convert -resize 600x600 -antialias -quality 00 -colors 256 -verbose $i tmp-$i; pngcrush -rem allb -brute -reduce tmp-$i optim-$i; rm tmp-$i; done
for
monta el loop. Para cada archivo que encuentra que coincide con la expresión regular *.png
–todos los archivos con extensión png– ejecuta una serie de acciones. El inicio de las acciones se marca con do
y acaba con done
. Cada acción debe estar separada por un punto y coma.
Dentro del loop hemos ejecutado las dos acciones que necesitamos, mediante convert
y pngcrush
, y posteriormente borramos el archivo temporal que necesitamos crear, mediante rm
.