Image resize quality (Java)

The two most popular open source libs specializing in image resizing in java currently are:

  • Thumbnailator
  • imgscalr

Additonal there is the JDK way with Java's Graphics2D (see this question on how to do it) which is notorious to create bad results especially with downscaling. There is also a Java interface to ImageMagick which will be omitted here because it requires an external tool.

Visual Quality

Here is a comparison of the results of resizing/downscaling a 580x852 png to 145x213. As reference Photoshop CS5 "save for web" resizing is used. Note: the results are 1:1 what the libs created just copied together. The zoom does not use any filtering, just a simple nearest neighbor algorithm. Here you can find the original image.

comparison

  1. Thumbnailator 0.4.8 with default settings, no dimension adjustments
  2. Photoshop CS5 with bicubic algorithm
  3. imgscalr 4.2 with ULTRA_QUALITY setting, no dimension adjustments
  4. Graphics2D (Java 8) with render hints VALUE_INTERPOLATION_BICUBIC, VALUE_RENDER_QUALITY, VALUE_ANTIALIAS_ON

I leave it to the reader to select the best result as this is subjective. Generally, all have good output except Graphics2D. Thumbnailator generates sharper images very similar to Photoshop output, whereas imgscalr's output is considerably softer. For icons/text etc. you want a sharper output, for pictures you may want softer output.

Computational Time

Here is non-scientific benchmark using this tool and 114 images with dimension from about 96x96 up to 2560x1440 treating it as 425% images creating: 100%, 150%, 200%, 300% and 400% scaled versions of it (so 114 * 5 scaling operations). All libs use the same settings as in the quality comparison (so highest quality possible). Times are only scaling not the whole process. Done on a i5-2520M with 8GB Ram and 5 runs.

  • Thumbnailator: 7003.0ms | 6581.3ms | 6019.1ms | 6375.3ms | 8700.3ms
  • imgscalr: 25218.5ms | 25786.6ms | 25095.7ms | 25790.4ms | 29296.3ms
  • Graphics2D: 7387.6ms | 7177.0ms | 7048.2ms | 7132.3ms | 7510.3ms

Here is the code used in this benchmark.

Interestingly Thumbnailator is also the fastest with an average time of 6.9 sec followed by Java2D with 7.2 sec leaving imgscalr behind with a poor 26.2 sec. This is probably not fair since imgscalr is set to ULTRA_QUALITY which seems to be extremely expensive; with the QUALITY setting it averages at a more competitive 11.1 sec.


I've tried it all - including the tricks here, and all I can say is that you're better of using ImageMagick with whatever interface. Javas imaging libraries are just not up to snuff when it comes to this. You need to support so many formats and algorithms to get it right.


Phil, I don't know which solution you eventually went with, but scaling images in Java can look pretty good if you:

  • Avoid BufferedImage types that aren't well supported by the JDK.
  • Use incremental scaling
  • Stick to bicubic when using incremental scaling

I've done a fair share of testing with these methods and the incremental scaling along with sticking to well supported image types is the key -- I see Alexander mentioned he still didn't get good luck with it which is a bummer.

I released the imgscalr library (Apache 2) about 6 months ago to address the issue of "I want good-looking scaled copies of this image, DO IT NOW!" after reading something like 10 questions like this on SO.

Standard usage looks like:

BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, 640);

The 2nd argument is the bounding width and height imgscalr will use to scale the image -- keeping its proportions correct even if you passed in invalid dimensions -- there are many more detailed methods, but that is the simplest usage.

The use-case you would want, for example if Facebook limited images to 800x600 pixels, would look like this:

BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, Method.QUALITY, 800, 600);

That will ensure the image stays in the best supported image type and scaled with the highest quality method that Java can muster.

In my own high-resolution testing I have not noticed any gaping discrepancies with scaled images using this library/these methods EXCEPT when your image gets put into a poorly supported image type by the ImageIO loader -- for example, this happens a lot with GIFs. If you leave them like that and don't get them out of those poorly supported types, it ends up looking really dithered and terrible.

The reason for this is that the Java2D team actually has different hardware accelerated pipelines for all the different types of BufferedImages that the JDK can process - a subset of those image types that are less common all fall back to using the same software rendering pipeline under the covers in Java2D, resulting in poor and sometimes totally incorrect looking images. This was such a PIA to explain and try and figure out that I just wrote that logic directly into the library.

The two best supported types are BufferedImage.TYPE_INT_RGB and _ARGB if you are curious.