Server-side resizing with WPF: now with JPG
I’ve shown before how to generate thumbnails from ASP.NET server code using the WPF media APIs instead of GDI+ (which is unsupported when used in server code).
In the previous article, I’ve been generating the thumbnails as PNG files. The reason for that is that PNG is a lossless format and I wanted to isolate as few variables as possible that impacted output quality and performance. Adding JPEG artifacts and the variable of the quality setting would have just muddied the water.
One commenter (Victor) pointed out that the PNG API in WPF does not compress the output though. That is clearly a problem for a web application.
This is why I decided to write this follow-up post. The previous one is still useful for establishing that the decision to use WPF rather than GDI+ makes sense but I want to complement that with some JPEG comparison.
The algorithm that I’m going to use here is what I consider the best compromise between quality and speed for each technology. I chose HQ bicubic for GDI and “Fast WPF” for WPF. PNG generation will be our baseline, and I’ll generate JPG files for quality settings in increments of 5 from 50% (lower than 50% is just horribly bad). The set of images that I will resize is the same as in the previous post.
The code almost doesn’t change, except that we use a JpgBitmapEncoder that needs to be configured with the quality setting instead of a PngBitmapEncoder:
var targetEncoder = new JpegBitmapEncoder { QualityLevel = quality };
Let’s look at the qualities for my two favorite, moiré-prone images:
50% | 55% | 60% |
65% | 70% | 75% |
80% | 85% | 90% |
95% | 100% | PNG |
50% | 55% | 60% | 65% |
70% | 75% | 80% | 85% |
90% | 95% | 100% | PNG |
Even at 100%, JPEG doesn’t look as sharp, deep and saturated as PNG but none of those look tragically bad. This is because thumbnails are too small for the defects to jump to the eye. If you use magnification ([Windows] + [+] on Windows), you’ll see them.
The compression defects we are looking for are waves near high-contrast edges:
And zones, usually square, where the picture lacks definition and tends to be too flat to show the details of the original picture:
The defects stop being too noticeable around 75% compression on these pictures. Of course, with this kind of lossless compression, it depends a lot on the pictures, so do your own tests and choose the compression that is the best compromise between quality and size for your images. Speaking of size, here’s a graph of size and time against compression level over resizing the same thirty 12 megapixels photos I used for the previous post:
The first thing to notice is that the time to compress almost does not depend on the compression level and is a little faster than PNG. Second, we also see that once more, WPF is more than twice as fast as GDI+. Third, the size of the resulting files grows reasonably with compression level up to around 85%, after which it rises faster.
There is a weird peak at 75% that I’m not sure how to explain. I did make some additional measurements around that value and it’s really just for 75%, not 76, not 74, and none of the other values. There seems to be something magical about 75% for some images so it might be a good idea to stay clear of that particular value.
Of course, even at 100%, thumbnails are still more than twice as small as uncompressed PNG but the sweet spot here seems to be between 76% and 90%, where most of the artifacts of compression stop being too visible on most images and yet the size doesn’t grow too fast. I’ll be using 85% myself.
Here are some of the sample images I used, at 85% so that you can compare with the previous PNG thumbnails:
UPDATE: I contacted the WPF team to have the final word on whether this is supported. Unfortunately, it's not, and the documentation is being updated accordingly. I apologize about any confusion this may have caused. We're looking at ways to make that story more acceptable in the future.
UPDATE 2: for an approach that scales better, try this.
My benchmark code:
http://weblogs.asp.net/blogs/bleroy/Samples/ImageResizeBenchmarkJpg.zip
The code for the JPG thumbnail handlers:
http://weblogs.asp.net/blogs/bleroy/Samples/SupportedResizeJpg.zip
The previous article:
http://weblogs.asp.net/bleroy/archive/2009/12/10/resizing-images-from-the-server-using-wpf-wic-instead-of-gdi.aspx