Tales from the Evil Empire

Bertrand Le Roy's blog

News


Bertrand Le Roy


Add to Technorati Favorites Tales from the Evil Empire - Blogged

Blogs I read

My other stuff

Archives

What InterpolationMode and CompositingQuality to use when generating thumbnails via System.Drawing

For some reason I keep writing photo thumbnail generators lately. The last time was this week for an internal app building contest. One thing that I never seriously investigated before was how to optimize the quality of the generated thumbnails. So today I wrote a small benchmark that compares all combinations of InterpolationMode and CompositingQuality values and that measures the time each takes when generating small thumbnail versions of different images.

The benchmark project contains a few of my photos but the one where the differences are the  most obvious was the following:

Copenhagen

It is a contrasted image with sharp edges and a high potential for scaling artifacts. This is made clear by the thumbnail that gets rendered with the default settings:

CopenhagenDefault

There's plenty of artifacts here and pixels are very visible on edges. The scaling needs to be done with more subtlety. When comparing all combinations of mode and quality, it is very clear that CompositingQuality has very little effect if any on a thumbnail of this size. Even the differences between HighQuality and HighSpeed are not immediately obvious. The HighSpeed mode is not significantly faster. The documentation is not very explicit on what this is doing but I think it only really has an effect when composing different graphical elements on a same rendering surface, especially when transparency is involved, which is not the case here but could have an impact on my PhotoHandler folder thumbnails that combine different images.

The differences between InterpolationMode values, on the other hand, are pretty obvious. Here are the results for the main values:

CopenhagenNearestNeighbor CopenhagenBicubic CopenhagenBilinear
Nearest Neighbor
3.44 ms
Bicubic
6.41 ms
Bilinear
3.59 ms
CopenhagenHigh CopenhagenHighQualityBicubic CopenhagenHighQualityBilinear
High
10.78 ms
HQ Bicubic
10.62 ms
HQ Bilinear
12.50 ms

High Quality Bilinear is clearly too fuzzy: most of the details are gone. High and High Quality Bicubic are both of acceptable quality, the details are still there, no pixels are visible on the edges and there are no artifacts. Based only on quality, I'd choose High Quality Bicubic. Of course, it takes three times as long as Nearest Neighbor, which is the fastest but looks plain ugly.

Of course, you'd get a much better result by using Photoshop, but the point here is to have something fully automated. For the sake of comparison, here are thumbnails generated from Photoshop using the different available algorithms for image resizing:

CopenhagenNearest CopenhagenBicubic CopenhagenBilinear
Nearest neighbor Bicubic Bilinear
  CopenhagenBicubicSharper CopenhagenBicubicSmoother
  Bicubic sharper Bicubic smoother

The difference in quality is quite dramatic. The bicubic sharper version in particular looks very nice. I really wish we could have those great algorithms or something close to them packaged in a nice .NET assembly that scales in a server environment.

But wait, maybe that exists... If you heard of one, please comment here. Cheers!

UPDATE: Kjell-Ã…ke Andersson suggested in the comments that I check out Paint.NET. So I did and I have to say I'm quite impressed by how far the project has gone since the last time I checked it out. It really is a nice package, much more accessible than Photoshop (although of course nowhere near as powerful and complete). Anyway, here's how the resizing went with Paint.NET with the different available options:

Paint.NET nearest neighbor Paint.NET bicubic Paint.NET bilinear Paint.NET best quality
Nearest neighbor Bicubic Bilinear Best quality

This is clearly not quite as nice as Photoshop's bicubic sharper (there are some remaining artifacts and it's not as sharp) and I actually think the automatic thumbnails in high quality bicubic look better. I'm not sure if Paint.NET rolls out its own algorithms here or uses System.Drawing but whatever they use is not on par with Photoshop and definitely plays in the same league as System.Drawing.

Download the benchmarking code here:
ScalingExperiment.zip

Posted: Dec 05 2007, 06:06 PM by Bertrand Le Roy | with 16 comment(s) |
Filed under: ,

Comments

Jacques PHILIP said:

Hello Bertrand,

I wrote a little utility a while back to make thumbnails of transparent images, I don't know if the framework 3.5 supports this directly now, but it has been useful for me:

www.c-sharpcorner.com/.../MakingThumbnailsOfTransparentImages.aspx

If it is a any use, feel free to use it.

# December 5, 2007 10:17 PM

Kjell-Åke Andersson said:

Have you checked if Paint.Net generates thumbnails with similar quality as Photoshop? If it does, then you you probably could get the algotrithm from there since the code for it is open source. Otherwise you probably could get it from Gimp.

# December 6, 2007 2:20 AM

Wesley said:

Nice article. Definately clears some things. I'll use High(or photoshop) for my thumbnails.

# December 6, 2007 4:16 AM

Greg said:

I've suggested that better imaging codecs be put into .NET as well as adding support for mainstream image formats (jbig2 especially as an improvement over tiff G4).

Microsoft should have some of these already implemented in its professional photo editor package.

Consider using photoshop, gimp or imagemagick to compare the thumbnails to each other.  Also consider using an image optimizer to see how well the thumbnails compress (optipng for png images and jpegtran for jpegs).

I'd also like to see a JAI like set of operators in .NET (JAI - Java Advanced Imaging).

# December 6, 2007 11:03 AM

Lionel said:

Wikipedia suggests that Lanczos resampling (en.wikipedia.org/.../Lanczos_resampling) gives very good results.

@Kjell-Åke Andersson:

I believe that Paint.NET uses the methods provided by the .Net framework, so it would be HQ Bicubic or supersampling by default.

# December 6, 2007 1:51 PM

Morten said:

"Another thing I want to test is how much jpeg compression levels affected my automatic thumbnails"

For these sort of tests, you shouldn't be doing any kind of lossy compression. That will just give you too many variables.

# December 6, 2007 6:12 PM

Bertrand Le Roy said:

My point exactly, Morten, although the differences between the compressed versions are still very visible. But there are only 24 hours a day and those will have to wait a bit.

# December 6, 2007 6:23 PM

Lionel said:

Actually, you can get nearly the same result as Photoshop's "Bicubic sharper" using Paint.NET.  Here is the method:

1. Apply a Gaussian blur (with radius 2)

2. Resize using Best Quality/Supersampling (to 134x100 pixels)

3. Apply a "Sharpen" effect (with radius 2).

The Gaussian blur avoids moiré patterns, and the final sharpening increases the _apparent_ sharpness of the image (see en.wikipedia.org/.../Unsharp_masking for how it works).  Both would be rather easy to implement in a standalone assembly.

The visual effect is nice, but the is some cheating involved: even if it looks nicer, the blur+sharpening actually further degrades the image.

As for Lanczos resampling and other high order methods, it looks like they are much more suited to encreasing the resolution of the image.  For making small thumbnails, it looks like very little is gained.

# December 10, 2007 12:04 PM

Bertrand Le Roy said:

Lionel, that is very good info. Thanks a lot.

# December 10, 2007 2:27 PM

Rick Brewster said:

Paint.NET uses its own code for resampling. There are issues with the underlying GDI+ library when trying to use some of System.Drawing's interpolation modes. It quite often makes the border pixels slightly transparent. Or it makes things blurry like you documented here.

# December 11, 2007 8:27 PM

Bertrand Le Roy said:

Rick: thanks for the info! Cheers!

# December 11, 2007 8:59 PM

Thomas said:

can someone please post a code sample on how to do this resizing using the Paint.net api, I downloaded the source code and it's an abyss of functions that are not documented and don't even bother posting on their forum so far they've been less than helpful.

I'd truly appreciate it.

thanks

# December 26, 2007 7:09 PM

Mike Ormond's Blog said:

Having talked about the pain of capturing image snapshots , someone mentioned Cropper and its plug-in

# June 6, 2008 6:46 AM

nathanaeljones said:

You might be interested in this image resizing module, then.

It integrates with the pipeline, so you can just slap a query string onto any image URL to get it resized or converted:

image.jpg?thumbnail=jpg&maxwidth=30

It also permanently caches resized versions to disk... otherwise you run into serious scalability issues. With disk caching it's practical to use this on high-load production servers.

Take a look:

nathanaeljones.com/.../asp-net-image-resizer

# October 9, 2008 4:49 PM

Bertrand Le Roy said:

@Nathanael: well, yes, I also wrote this, which explains how to do the same thing:

weblogs.asp.net/.../generating-thumbnails-in-asp-net-dotnetslackers.aspx

# October 9, 2008 5:08 PM

nathanaeljones said:

Cool! I recently read the article, but hadn't realized you wrote it.

I usually have my photos in the same directory with the .aspx file that contain them, so putting everything in a photos folder would be a severe disadvantage... I like relative paths :).

MapPath completely ignores the security and access settings in the URL authorization portion of web.config. Since that's the easiest way to protect static files, it's nice to support it. Integrating as HttpHandler makes that compatibility automatic.

I also use a *wide* variety of thumbnail sizes, so having them all the same wouldn't benefit me much. I often need several versions of the same file, so the caching system needs to be that flexible also.

I also implemented automatic cache management to prevent denial of service attacks... you can read my comment at nathanaeljones.com/.../asp-net-image-resizer

# October 9, 2008 5:57 PM