Automated Image Transformation
Comparing Java Libraries
Our websites contain images in all variations. The users mostly create and edit them on their desktops using the usual image manipulation applications. After that the final version of the image, i.e. the version that is going to be used on the website, is uploaded in Mojo and placed wherever needed. In the future the CMS should be able to automatically transform those images to suit various devices.
For the impatient reader:
Of course we need some piece of code to do that, either self developed or a library with an appropriate license model. This article is about evaluating a set of libraries to help us with the task.
Challenges: Quality and Speed
Our primary constraint is that the library has to be usable from within the Java Virtual Machine (JVM). On top of that we need something that manages to keep a decent image quality after transformations. And we don’t want to sacrifice speed for quality so that has to be balanced.
When evaluating quality and speed it is important to think about specific image transformations. It makes a difference if you downscale an image or if you distort and blur it. Since what we are going to need most is downscaling that is our test case for quality and speed.
The first contestant is not a library but the built in Java Graphics2D package. It has the advantage of being built-in so there is no need for additional jars. The main disadvantage is its rather intransparent API which often leads to unsatisfying implementations. If you want details on that topic I strongly encourage you to read „The Perils of Image.getScaledInstance()“ .
The short story is: You need to use Graphics2D in the right way and luckily there is already a library that provides us with an easy-to-use API for that: ImageScalr. For our tests we use the QUALITY setting because that is most comparable with the other contestants default behavior.
Second comes a tiny library named java-image-scaling. It is specifically built for scaling images and provides a nice Java API.
Third we will take a look at the classic swiss army knife of opensource image transformations: ImageMagick. But be aware that ImageMagick is not a Java library but a set of C coded command line utilities. It is available for Windows and Linux with standard packages in most Linux distributions.
How does that fit with our primary constraint (JVM based)? Well with ImageMagick we have 2 options. Either call the command line utilities from Java via shell execution provided by im4java. Or use the Java-to-C API coded in Java/C using JNI named JMagick. We will stick to the first option (im4java) because the later is still stuck with an rather old version of ImageMagick and I found some people having stability issues.
Fourth comes GraphicsMagick which is a successor to ImageMagick. It advertises itself as being faster and smaller but as feature-rich as ImageMagick. With gm4java its command line utilities are accessible from the JVM in the same manner.
The test case for our contestants is image downscaling. For the sake of diversity 3 different images are used and they are scaled from original size to half and to a quarter. I took images from our productive system so the original image width is always 472 pixels.
Every contestant executes the same set of code (reading the file, getting parameters) before the code-specific scale starts. Keep in mind that accessing the scaling code is different between ImageMagick/GraphicsMagick and the other 2. But what we want is an overall performance/quality comparison so in our case that does not matter.
Benchmarking the tests is done with junit-benchmarks on a debian squeeze (x64) server with an intel xeon cpu x5450 @ 3.00GHz , 8 cores and 8gb of ram. Options used are
- callgc = false
- benchmarkRounds = 200
- warmupRounds = 0)
That means the garbage collecting of memory is disabled, we do the benchmark 200 times and average the results and we do not want any warmup runs.
To a certain degree image quality lies in the eye of the beholder. But I am confident you will agree that the native Java solution (Graphics2d called via ImageScalr) provides the lesser quality. To my mind java-image-scaling is pretty good but ImageMagick and GraphicsMagick do a better job regarding artifact removal and image softening.
Looking at the time table for the benchmarks ImageMagick finishes last with 0.15 seconds for downscaling 3 images to 2 sizes (in sum 6 operations). ImageScalr is slightly faster than java-image-scaling, 0.6 vs. 0.7 seconds. And GraphicsMagick really shines here with 0.03 seconds.
So all in all I will put it this way: If restricted to a pure Java solution I would prefer java-image-scaling over Graphics2D/ImageScalr regarding quality and speed. Looking at quality ImageMagick does not outperform java-image-scaling that much but is way slower, so even between the 2 I would consider java-image-scaling for downscaling purposes.
But in the end GraphicsMagick seems to have it both: quality and speed. Plus a ton of features more than just downscaling (which java-image-scaling is restricted to). So we are going to settle with GraphicsMagick for now.