15

I need to compress or at least drop the quality of some png images that users are uploading to my site. I already resized it but that doesn't do much for the image size.

Seeking a png/image compression or quality loss algorithm or library for .net 4.0 or under.


This is how I currently save/convert my images:

Image mainImg = ImageHelper.ResizeImage(bmp, 600, 500, false);
mainImg.Save(filePath, System.Drawing.Imaging.ImageFormat.Png);
Igor Brejc
  • 18,714
  • 13
  • 76
  • 95
Shawn Mclean
  • 56,733
  • 95
  • 279
  • 406

3 Answers3

23

Unfortunately, .NET (and GDI+, which the .NET graphics libraries are built on) does not support any encoding parameters for PNG. In fact, if you call GetEncoderParameterList on your image with the PNG encoder Clsid, you'll get a "Not implemented" exception.

Even more unfortunate is that both ImageFormat and ImageCodecInfo are sealed classes and you can't just add new codecs to what .NET can natively access. Microsoft dropped the ball on this one.

This means your alternatives are, in decreasing order of masochism: 1) Implement a save function on your own in .NET that implements RFC 2083, 2) Implement a save function based off porting libpng to .NET, 3) Call unmanaged code that was built from libpng directly

libpng has great documentation and is free, both in availability and license permissiveness.

Applekid
  • 390
  • 1
  • 5
7

PNG is generally a lossless compression scheme, which means image quality is never reduced unless you reduce the pixel count (size) or color depth. There are three ways to reduce the file size of PNG images:

  • Reduce the pixel count (by downscaling the image); there's an obvious loss of resolution here
  • Using a different precompression filters/DEFLATE compressor parameters (OptiPNG is my favorite tool to automatically choose the best combination of filters/compressor settings)
  • Reducing the color depth from true color to 256 (or less) colors will give you substantial byte savings, but usually at great visual cost (especially for photographic images)

It seems like .NET doesn't have a built-in way to change precompression filters or compressor parameters of PNGs, so you'll have to use an external library. OptiPNG or any of the other PNG optimization tools aren't native .NET libraries, so you'll have to deal with P/Invoking the libraries or running a separate process to.. process each image. However, you're often looking at a savings around 5% or less (although I've seen as much as 40%) since this is ultimately a lossless process.

I'd recommend against reducing color depth in the automated scenario you describe, because the results can look truly awful (think dithered GIFs of old). (However, If you're doing manual optimization, look at Color Quantizer on Windows and ImageAlpha on Mac.)

Realistically, the best way you can reduce file size at the expense of quality is to just convert to JPEG, which requires no external dependencies, and is designed to be a lossy compression algorithm:

mainImg.Save(filePath, System.Drawing.Imaging.ImageFormat.Jpeg); // make sure you change `filePath` to end with jpg instead of png.
Community
  • 1
  • 1
josh3736
  • 139,160
  • 33
  • 216
  • 263
  • 1
    This is not exactly true. PNG implements a concept of compression filters that are used to reduce the image size without losing the quality. It doesn't guarantee anything, but in some situations the improvement could be quite significant. You can read about that here: http://www.libpng.org/pub/png/pngintro.html – detunized Dec 11 '10 at 19:26
  • But I agree with josh3736, the easiest and the most reliable way would be to go with JPEG. – detunized Dec 11 '10 at 19:28
  • @detunized: Yes, but compression filters may not implement lossy compression, which is what @Shawn is really asking for. Anyway, any PNG uploaded by his users will necessarily already have had compression filters applied, so it's not possible to further reduce file size. – josh3736 Dec 11 '10 at 19:31
  • If filters are not applied it's possible to reencode the PNG. There are some tools out there that do that. Still have to agree that the result is not guaranteed. Though to be precise the OP is looking for `compression or quality loss algorithm`. I suggested the former. – detunized Dec 11 '10 at 19:34
  • When you decode then resize/save any optimization filters are lost on the new file output. You can still run optipng/pngcrush on the output file, and often gain a lot... especially if the color pallet is *really* less than 256 colors. sixteencolors.net when it was running on .Net code would optipng the rendered images to a 16-color pallet which reduced the image sizes considerably. – Tracker1 Feb 09 '12 at 22:05
  • @Tracker1: I've updated the answer to better address precompression filters and compressor parameters. – josh3736 Feb 09 '12 at 22:40
0

Unfortunately, .Net's image processor for png won't do any optimization heuristics on the output. Your best bet is to have an optipng/pngcrush binary available on the server, render the resized output to a temporary file, then use pngcrush on it via a System.Diagnostics.Process.

For the most part, if the uploads are photographs and the original format is JPEG, then you will be better served by using JPEG output.

Tracker1
  • 19,103
  • 12
  • 80
  • 106