Raging Sloth Tech

PNG Optimizer

Please note that the original script for this tutorial has changed. It did not originally check for symlinks and could end up in infinite recursion on Unix like systems (the fix is in the last code block and in the downloadable file)

Pages: Page 1 Page 2 Clean up

Alright so here we are all of a sudden in the first ever Raging Sloth article. As promised (though I doubt anyone read the promise other than possibly the good people at Google scanning my sight for an adsense ok) this is a Software Engineering tutorial written in python. Also please bear in mind that things like code coloring and other cosmetic issues are in the works...

Notice that I said written in python and not a python tutorial. A python tutorial would center around explaining how to accomplish various tasks (usually simple ones) in python. This tutorial is meant to help you learn how to be a good engineer or developer or a better programmer but not a programmer. I'm not going to take the time to explain every little bit about running python scripts and how python syntax works, there are plenty of tutorials about those things already. As this is my first such tutorial the bar is very low. Things like test driven development, agile methods, teams and such will be addressed in later articles. this is for a raw beginner, or alternatively a seasoned pro who wants to critique my ideas and poke fun at my grammer 'n spelling :) .

There are a lot of schools of thought on what exactly makes a good developer and I'll get to those to a greater extent later as this article is just a quickie before I head of for my Christmas Vacation from work (a very long one at that :) ) The time line for a follow up isn't yet clear but I'll see what I can do and how much free time I have (Geoff will hopefully have some stuff in the coming weeks as well.)

OK so as with any good tutorial this is going to center around a task that I want performed. In this case we're going to be building a script to optimize multiple png images. There exist a few applications out there for optimizing png images, two that spring to mind (because I use them) are optipng and pngout

So the plan at this point is to write a script that will run optipng and pngout on all of the images within a directory. I don't think it's a reach to say that we should obviously look at how these programs work. A quick look at the output of optipng -help tells me that we can get decent optimization using the command line -o7 without delving too deep into the more advanced options. pngout defaults to some reasonable options (going for min file size and not compression time that is) so we can just use it as is. Now this is a moment to take note of, do we just go ahead and start writing a script? jumping into things here would rob us of an important discovery. pngout does not reduce bitdepths and optipng does. This actually means something to us, as if you actually run the two on an image where the bit depth can be reduced, you'll find that running optipng and then pngout on the same image you will get better results than running them in the reverse order. Here are the results I got running the two programs on the raging sloth logo (a 640x480 version.)

Action Size (image 1) Action Size (image 2)
Save File using Gimp 4459 Save File using Gimp 4459
run pngout 3735 run optipng 2547
run optipng 2526 run pngout 2149
run pngout 2149 run either 2149
run either 2149

So you can see that there is an advantage to running optipng first. Also note that the Raging Sloth logo isn't really a typical image and pngout can tend to make a profound difference even after optipng.

The most important thing to take from that chart is that things aren't always as you expect them to be and in order to be a good engineer of any sorts you need to test things, and research things while keeping your goals in mind. If the point of this script was to make the program execute quickly we would probably just use one of the two programs but our goal is to get our pngs as small as possible to conserve our cheap shared bandwidth :) . We also have enough time to reasonably do this kind of testing and research now, as we have no set schedule and the script itself, even in it's final form, should be pretty simple.

Alright our next step is going to be setting a reasonable short term goal for ourselves. As we aren't necessarily very familiar with python (when I wrote this script it was actually my second script, the first being a related Gimp script that will appear in later tutorials.) we'll set our goal to simply be "write a script that runs optipng on a single file with the command line parameter -o7".

Hopefully you already have Python installed at this point or at least can figure out how to get that done on your own (it's not really the point of this tutorial) now start up your favorite IDE that supports python or any text editor (I think I used Kate when I first wrote this.) You might also find the interactive python interpreter useful for this exercise.

Now a quick Internet search or two reveals that the python os module has a little function called spawnlp which has the following doc string

spawnlp(mode, file, *args) -> integer Execute file (which is looked for along $PATH) with arguments from args in a subprocess with the supplied environment. If mode == P_NOWAIT return the pid of the process. If mode == P_WAIT return the process's exit code if it exits normally; otherwise return -SIG, where SIG is the signalimport os,sys that killed it.

This should work great for us, however google tells me that this function is not available on windows so for those unfortunates out there who aren't running a unix like operating system you'll have to use spawnl instead and while spawnlp will be happy to take something like "optipng" and search for such a program somewhere in the $PATH, spawnl will demand you provide an absolute path (ouch.)

Now remember, we set the goal to run optipng on a single file but we didn't say which one. Well it's going to be a specific one so grab yourself a png file and throw it in the same directory where you'll be saving the script. Call it something like test.png.

Here's what our script looks like now:

import os os.spawnlp(os.P_WAIT, 'optipng','optipng','-o7', 'test.png')

wow how absolutely mind blowing eh? Well go ahead and save this as a script in the same folder as the test.png file and run it (or alternatively type this into an interactive python window as it is relatively short after all.) you should see all kinds of crazy optipng console output when you run it (assuming you ran it from a terminal or command line which you should so you can tell if it's working.)

Now that we have a working product (of sorts) lets clean it up a little bit. First of all when we run the eventual script on a tonne of files we don't want to waste time writing all of that optipng output stuff to the screen, so we'll add in a -q option to quiet optipng down. Secondly we will want some sort of feedback from the script so we'll write our own quick little output.

Script the second

import os print "optipnging " + "test.png" os.spawnlp(os.P_WAIT, 'optipng','optipng','-o7','-q','test.png')

We're now about half way so here's a little next page action.