Performance Comparison of Image Libraries

For the past couple years, I’ve been using the Python CoreGraphics bindings to do some of my image manipulation. While a bit more complex than I would have liked for setup (dealing w/ context rotations and other scaling math was a bit of the pain), it otherwise worked great (and more importantly, right out of the box) on Leopard. Unfortunately, with Snow Leopard, the CoreGraphics library was unceremoniously (as far as I know, without any sort of announcement or acknowledgement) deprecated. It’d only work in 32-bit mode and more troubling, certain Context calls that used to take floats now required CGFloats. Not so much of a problem… besides the fact that even after much research and poking, I found no way to instantiate a CGFloat (there’s an undocumented CGFloatArray call, but that just gives you uninitialized CGFloats w/o a good way to assign them).

As has been the trend, I’ve been isolating/switching more and more of my code from anything that touches Apple libraries. I’ve come to the conclusion that they just don’t give a shit about breaking your code (much less care about fixing or even responding what they’ve broken). It’s incredibly off-putting. In this case, it’s unfortunate, as the CoreGraphics code performs much better than both PIL and ImageMagick (I would have tested GraphicsMagick as well, but it doesn’t support the chaining features I needed for my particular resizing/layout operations).

CoreGraphics Python w/ kCGInterpolationHigh
real     0m1.885s
user     0m1.456s
sys      0m0.400s

PIL w/ Bilinear Filter Resize
real     0m3.380s
user     0m2.981s
sys      0m0.365s

32-bit Static ImageMagick
real     0m7.125s
user     0m9.730s
sys      0m0.652s

32-bit Static ImageMagick w/ Box Filter Resize
real     0m4.237s
user     0m4.438s
sys      0m0.636s

64-bit Shared ImageMagick
real     0m6.080s
user     0m8.495s
sys      0m0.366s

64-bit Shared ImageMagick w/ Box Filter Resize
real     0m3.268s
user     0m3.599s
sys      0m0.331s

A few things worth noting:

  • I try to use the system Python. After all the problems w/ 10.5->10.6 though, I am reconsidering.
  • PIL seemed to easy_install well (w/ a binary egg no less) on my 10.6 – I’ll have to test on a clean system to make sure I hadn’t made my life easier w/ MacPorts or something, but this is a huge improvement over the problems surrounding installing PIL on 10.5, which was what actually drove me to use the CoreGraphics Python library in the first place. UPDATE: on a clean 10.6 install, it compiles, but doesn’t have JPEG or FreeType support. waah wahhh
  • ImageMagick defaults to Lanczos filtering by default, which is quite slow. Testing out various filters, Box filtering was about twice as fast and for my test images had neglible-to-nonexistent image quality differences even under the loupe. Definitely worth poking around a bit if you’re trying to get better performance.
  • The 64-bit shared lib version of ImageMagick is fair bit faster than the 32-bit static version. Until I’m all on 64-bit hardware, is a bit of a moot point to do further testing though. I’m assuming the extra sys time is due to the staticness and the remainder is due to 64-bitness.
  • Given roughly equivalent performance between PIL and ImageMagick, I’ll be going with ImageMagick for the additional flexibility/features it provides.
  • Although it doesn’t work for this particular set of operations, I wanted to mention that Marc Lyniage’s CoreImageTool is just a wicked, wicked piece of software. It of course has all the CoreImage caveats though, especially if you have to deal with Intel’s crappy GPUs (*fist shaking*)