Microsoft .NET’s JPEG encoder makes crappy JPEGs

Microsoft .NET has been making quite a bit of headway in the developer community recently with both the open source efforts and the upcoming ASP.NET 5 modern web framework. With so much attention on making .NET a “hip” platform (and hopefully breaking into the startup ecosystem), I would like to draw attention to a very frustrating problem that I hope the .NET team can address, .NET produces pretty poor quality JPEGs.

Now if this were some obscure .NET function that hardly anyone uses I wouldn’t care as much, but JPEGs are an integral part of most desktop, mobile softwares and web services today. Granted PNGs and SVGs have become common for UI graphics, but JPEG is still the leading compressed image format used in uploaded avatars and photos (I look forward to the day WebP and BGP is the norm).

I like paying attention to detail, so it horrifies me just how much detail is lost through the Microsoft .NET JPEG encoder. There’s no better way to explain this than to show examples.

I first ran into this problem a few months ago when I was working with images in a podcast RSS feed. I was simply grabbing an online image, resizing it and then saving a JPEG file for local caching. A simple image optimization workflow that almost every modern app will do.

This was the image I was working with. (PNG lossless, 147KB)

82027738-d01b-11e3-9cbc-28a7386c60d7

And this is what happens after I save it as a JPEG in .NET. (JPEG 100% quality, 70.2KB)

783df736-d01b-11e3-9ab1-6d46dddafb4a

To the untrained eye, they might look not too different, but I invite you to look closer especially around the letters. Here’s the two side by side zoomed in.

compare1

Now I know what you’re thinking. Surely that’s just JPEG compression right? Well Photoshop’s JPEG encoder handles it fine. (JPEG 100% quality, 83.7KB)

photoshop

The thing about JPEG encoding is that it is all proprietary. You can encode JPEG many different ways and it will produce different files or varying fidelity and compression size. Yes they all have a “quality” parameter, but the same “quality” across two apps won’t produce the same result. (In my own testing, the Adobe Photoshop JPEG encoder seems to be the state-of-the-art.)

To cut a long story short, my workaround was to save the file as a PNG (which .NET is quite capable of) and then use the ImageMagick Windows command-line executable to convert it to a JPEG file. The result is worth the hassle. (JPEG 90%, 38.5KB).

imagemagick

Here’s the before and after comparison between .NET’s JPEG and ImageMagick’s JPEG with a 55% size saving (38.5KB vs. 70.2KB). I don’t think I need to tell you which one is which.

compare

My advice to any .NET developer is to avoid the built-in JPEG encoder – the quality is worse and the file size is larger than what it should be. Combined with the fact that almost all .NET best practices and image processing libraries (including the cool imageresizing.net) relies on the .NET JPEG encoder, I imagine this to be a pretty widespread issue.

I’m actually not the first person to notice this. A StackOverflow answer and blog post (images no longer work) by Chris Moschini also refers to this issue.

Perhaps with a bit of encouragement the Microsoft .NET team can fix this for .NET 2015 (it is not fixed in .NET Framework 4.6 Preview). Won’t somebody please think of the pixels?

11 insightful thoughts

  1. The quality differences could be due to chroma subsampling which unfortunately cannot be disabled with the JPEG encoders in .NET.

    I assume you are using the GDI+ image APIs in .NET (System.Drawing). Have you tried if the WIC encoder (JpegBitmapEncoder) performs better for you?

  2. Was going to say the same as Sammy, have you tried the wic encoder… Imageresizing.net has an option if you use iT with querystrings to specify the wic encoder and subsampling. This gives nice results at 90%, which is pretty much the standard.

  3. You’re REALLY grasping for straws here. PNGs are nice, but a terrible choice for photographic imagery if you care one lick about space. “To the untrained eye” doesn’t matter if you are shedding 50%+ filesize off an image. There are a bazillion sites out there that are getting fatter and fatter, sometimes swelling to multiple MBs per page load!

    If you want BAD jpg compression, just look at MS Paint’s save as jpg result. Now THAT is truly terrible (drops each image to like 40%-50% Save for Web Photoshop quality).

    ===============================================
    I look forward to the day WebP and BGP is the norm
    ===============================================
    Keep dreaming. There’s a reason why GIF is still around! Same w/ jpg!!!

    1. I’m confused Scott. Since the .NET encoder is compressing JPGs 55% larger than alternatives, don’t you agree this is an issue?

      1. If someone is truly concerned with image quality, they save files manually, individually, using Photoshop and its better-than-anything-else Save For Web feature.

        A JPG is a JPG is a JPG and yes, JPG compression isn’t perfect. You set the tone of the article by comparing it to a gigantic PNG version, which is totally unfair.

        But you know what? JPG output most of the time is GOOD ENOUGH. If you want pixel-perfect and best-use-of-filesize for JPG output, go the Photoshop route. Be grateful you even get .NET image tools – expecting something to be done about it is probably priority #1,203,302 on Redmond’s list of things to do.

        Yeah, the 39KB vs 70KB is annoying, but it’s not a dealbreaker in terms of speed. If filesizes are of crucial importance to you (say you NEED an ad to be a certain size for a media agency doing banner ad placement), again, go the Photoshop route. 147KB for the PNG version is just ridiculous though. Long story short, don’t rely on anything outside of Photoshop for perfect results.

        Also, you blowing up an image to illustrate the differences is misleading. Let people see the differences at 100% scale: http://i0.wp.com/www.istartedsomething.com/wp-content/uploads/2015/02/compare1.gif

        ^ When you do that, 99.9999999% of people won’t even care. And since so many people use AdBlock or are mentally trained to ignore 99.999999% of ads anyways, it doesn’t matter if there are slightly more artifacts on one over the other.

      2. @Scott I think you missed the part where it’s the highest quality possible (100%) and still looks beyond shocking. Colour data and sharpness are lost. This shouldn’t happen.

  4. I don’t think .NET encoder is just about quality, it probably seeks a balance of quality vs performance.

    I have in the past written .NET apps that have to resize / export 1000’s of images a second so if you are gonna get 100% better quality but it will be 2x slower or more. It just wont cut it for my app.

    (Just using an example here..)

  5. We can use only 4:2:0 sub-sampling in .NET Jpeg.
    MS doesnot open the way for 4:4:4.
    Quantize table is All 1, but chroma data is 1/4….

  6. The example images no longer illustrate the problem. In Firefox I get all the JPEG images “optimized” with subsampling, at about 89 quality, and in Opera I see them even worse as WebP inluding the reference.

    We can clearly see that the red mouth has lost sparkle, and the small “FM” text is unreadable. Small images, such as thumbnails, that are to be viewed up close on the screen should have full color resolution even if it doesn’t matter for large photos that are displayed at greater pixel density or zoomed out.

Leave a Reply