Thursday, 17 September 2015 03:19

How-To: Quickly Compress All PNG Images In A Directory using Linux

Written by 
Rate this item
(1 Vote)

Reference to using the command line in linux to compress and optimize all of your png, tiff, bmp, and gif image files

I needed a lossless compression tool, one that I can run in the command line and easily optimize all images in an entire directory.

I found Optipng which is a small command line program that can easily be installed and compress full directories quickly and efficiently without any noticeable loss in image quality.

Here's what you'll need

  • Computer running linux (My example uses Ubuntu, but other variations may apply)
  • Images to compress

Here are a few examples utilizing the optipng,  that I have used to compress and optimize png images for my website...

 

Installing Optipng (Ubuntu/Debian)

sudo apt-get update
sudo apt-get install optipng

 

Using Optipng

It's extremely easy to use, and can compress and optimize your image files without any noticeable loss.  For developers dealing with many large images, this can be crucial in saving bandwidth and speeding up load times.  Optipng will also work with BMP, GIF, PNM, or TIFF image formats as well.  I use PNG in my examples as the image formats below.

I like to use the -o7 option as shown in the examples below, but keep in mind, although description of quickly referred to in the title of this article, is meant more in the terms of a "set it and forget it' task.  Large high resolution and complex images can take a long time to compress.  As an example, I had some images shot with my Panasonic DMC-ZS7 ( a common middle of the road point-n-shoot) camera which were 4320x2432px and over 10MB in size took about an hour for optipng to optimise and compress each image.  If that high of a resolution is not needed, you can resize them and compress the smaller image much more efficiently.

On the same machine, I ran 27 images that were scaled down to approximately 1280x720px took less than an hour.

 

Compress and overwrite an image

This will overwrite your existing image, if you want to preserve the existing image, without modification, see the example "Compress and write a new image" below

optipng -o7 <path-and-directory><image.png>
  • -o7 - highest optimization

example:

optipng -o7 ~/images/Image_0034.png

 

Optimize all of the files in a single directory, writing over the existing images

optipng -o7 ~/images/*.png

 

Recursively optimize all of the files in a directory and it's children directories, writing over the existing images

cd <path-and-directory>
find -name '*.png' -print0 | xargs -0 optipng -o7

 

Compress and write a new Image

This will write new images (with a different name) into the same directory (preserving your existing files)

optipng -o7 <path-and-directory><image.png> -out <path-and-directory><new-image.png>
  • -o7 - highest optimization

example:

optipng -o7 ~/images/Image_0034.png -out ~/images/Image_0034_Compressed.png

 

Optimize all of the files in a single directory and write to a new directory with the same file names (preserving your existing files).  This is the command that I tend to use the most.

optipng -o7 ~/images/*.png -dir ~/images/compressed/

 

 

Something a little extra...get your geek on!

This is completely optional and will not provide any further benefit in compressing your images.  I like to easily view and compare the amount of reduction optipng was able to perform.  Utilizing the power of the command line and tools such as awk, we can quickly do this.  Optipng will output the percentage of compression as its completed each file, but rather then to scroll through all of the files, I decided to find another way.

 

Print all the file names and sizes that are located in a directory

This is optional but you can use this for a later time to do a side-by-side comparison of the size reduction to each individual image you compress.  In the example shown below I use the command ls to retrieve the file names and file sizes from a particular directory.  Typically it may not be the best idea to rely on the output of ls to retrieve the information needed.  This is partially due to being able to use non-printable characters in the use of file names in linux, where ls maybe substituting non-printable characters in its output and possibly could differ on different distributions.  For my everyday needs this is not an issue. 

Below examples are assuming you only have image files in your directory that you are compressing.  It does not take into account directories that may have other types of files.

ls -lp ~/images/| grep -v / | awk '{ if ($5 > 0) printf ("%i ", $5); else next;  for (i=9; i<=NF; i++)printf("%s ", $i); printf("\n")}'

or better yet, print both directory listings into a temporary file for later analysis

ls -lp ~/images/| grep -v / | awk '{ if ($5 > 0) printf ("%i ", $5); else next;  for (i=9; i<=NF; i++)printf("%s ", $i); printf("\n")}' > /tmp/before.txt
ls -lp ~/images/compressed | grep -v / | awk '{ if ($5 > 0) printf ("%i ", $5); else next;  for (i=9; i<=NF; i++)printf("%s ", $i); printf("\n")}' > /tmp/after.txt

This example uses the directories "~/images/" and "~/images/compressed" for the image directories used to compare the file sizes, you can modify that as needed.  The command ls lists out the contents of the directories "~/images", -lp lists out in long file names and indicates directories with "/".  Grep with "-v /" states to grab any line except with a "/", and finally awk prints columns 5 and 9 and anything after 9 (in case of spaces in a file name) to the temporary file "/tmp/before.txt".

 

Compare on-screen the file size and percentage reduction on each image

This does work, but Ok, this gets a little silly.  There are better ways of achieving this, and much less cryptic to read, but just as a fun challenge I wanted to see if I could mange to do this with awk with a single line.  It worked for me, but YMMV.  If you saved two separate txt files as described above (before.txt and after.txt), you can run this to see the results

awk 'BEGIN { OFS="\t"} FNR==NR { a[(FNR"")] = $1; next } { if((a[(FNR"")]) > 0)  printf ("%d%%\t"), ((((((a[(FNR"")]) - $1)/ (a[(FNR"")])) * 100))) ; else next; printf (a[(FNR"")]) ; printf ("\t%i\t", $1); for (i=2; i<=NF; i++)printf("%s ",$i); printf ("\n") }' /tmp/before.txt /tmp/after.txt

or if you completed multiple image optimizations while saving them into a new directory, combine the actions (saving a temporary file of both directory listings, and compare the size of each file) into a single line.

ls -lp ~/images/| grep -v / | awk '{ if ($5 > 0) printf ("%i ", $5); else next;  for (i=9; i<=NF; i++)printf("%s ", $i); printf("\n")}' > /tmp/before.txt && ls -lp ~/images/compressed | grep -v / | awk '{ if ($5 > 0) printf ("%i ", $5); else next;  for (i=9; i<=NF; i++)printf("%s ", $i); printf("\n")}' > /tmp/after.txt && awk 'BEGIN { OFS="\t"} FNR==NR { a[(FNR"")] = $1; next } { if((a[(FNR"")]) > 0)  printf ("%d%%\t"), ((((((a[(FNR"")]) - $1)/ (a[(FNR"")])) * 100))) ; else next; printf (a[(FNR"")]) ; printf ("\t%i\t", $1); for (i=2; i<=NF; i++)printf("%s ",$i); printf ("\n") }' /tmp/before.txt /tmp/after.txt

 

Below is my screen output showing the results of some misc images that I had compressed, starting with percentage (rounded to a whole number), file size before compression, file size after compression, and file name.

 

References

http://mywiki.wooledge.org/ParsingLs

http://www.math.utah.edu/docs/info/gawk_7.html

http://optipng.sourceforge.net/pngtech/optipng.html

http://stackoverflow.com/questions/14984340/using-awk-to-process-input-from-multiple-files

http://stackoverflow.com/questions/1602035/print-third-column-to-last-column

 

Read 15158 times Last modified on Sunday, 04 October 2015 13:35

Creator and owner of algissalys.com.  Linux enthusiast, electronics tinkerer, and likes to spend time in the workshop building and creating new projects.