C# - How to change PNG quality or color depth
Asked Answered
A

3

13

I am supposed to write a program that gets some PNG images from the user, does some simple edits like rotation and saves them inside a JAR file so that it can use the images as resources. The problem is when I open, say an 80kb image and then save it with C#, I get an image with the same quality but for 130kb space. And because it has to go inside a J2ME jar file I really need lower sizes, at least the original size. I tried the code below but later found out that it only works for Jpeg images.

ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
                int j = 0;
                for (j = 0; j < codecs.Length; j++)
                {
                    if (codecs[j].MimeType == "image/png") break;
                }
                EncoderParameter ratio = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 10L);
                EncoderParameters CodecParams = new EncoderParameters(1);
                CodecParams.Param[0] = ratio;

                Image im;
                im = pictureBox1.Image;
                im.Save(address , codecs[j], CodecParams);

This is where the image is loaded to a picture box:

private void pictureBox1_DoubleClick(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                string address = openFileDialog1.FileName;
                address.Replace("\\", "\\\\");
                Image im = Image.FromFile(address);
                pictureBox1.Image = im;
            }
        }

And this is where it's being saved back with no edits:

private void generateToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
            {

                ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
                int j = 0;
                for (j = 0; j < codecs.Length; j++)
                {
                    if (codecs[j].MimeType == "image/png") break;
                }
                EncoderParameter ratio = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 10L);
                EncoderParameters CodecParams = new EncoderParameters(1);
                CodecParams.Param[0] = ratio;

                string address = folderBrowserDialog1.SelectedPath;
                address = address + "\\";
                address.Replace("\\", "\\\\");

                Image im;
                im = pictureBox1.Image;               
                im.Save(address + imageFileNames[1], codecs[j], CodecParams);

Note: imageFileNames[] is just a string array that has some of the the file names to which the images must be saved with.

Any ideas will be appreciated and thanks in advance.

Anatomical answered 10/3, 2010 at 17:49 Comment(3)
Do you get the file size changes when you re-save the image with no edits?Eskimo
Can we see the code where you load the image and the code where you save the image? I'm not seeing similar behavior in my (simple) test.Eskimo
Yeb, I updated the post and added the completer codes. Thanks for your attention.Anatomical
E
12

I have taken 4 different PNGs, ranging in sizes from 2KB to 2.6MB. Using your code, I load them up and save them out. I do not modify the image by rotating or flipping. All 4 PNGs, when re-saved, have exactly the same size.

In addition, I have taken the 2.6MB PNG, opened it in Photoshop and saved two copies of it, one interlaced (reduced to 2.06MB), one non-interlaced (reduced to 1.7MB.) I then took each of those, ran them through your code and saved them. The resulting sizes of both were back to the original 2.6MB.

So, my guess is that the original images were created with a piece of software (like photoshop) and that piece of software has an optimized compression algorithm it is using for the PNG spec that .NET does not employ.

I've messed around with the EncoderParameter for Compression, but it seems to have no impact.

Eskimo answered 10/3, 2010 at 19:13 Comment(4)
Yeb, your right. The images were exported by the 3dsMax jsr exporter. But do you know a way to compress the PNG or lower it's quality so I could acheive a similar result to the exporter?Anatomical
This looks promising: benhollis.net/software/pnggauntlet ... It wraps this PNGOUT from this page: advsys.net/ken/utils.htmEskimo
Thanks for your time, I think you gave me my answers.Anatomical
So, basically, the answer is, it's not C# that increases the size, it's other apps that seem to have optimization tricks to reduce it? Would sure be handy to have that in .Net too...Pamulapan
S
15

Sorry for bringing up an old question, but I were solving the same problem today and found this page on MSDN: "Listing Parameters and Values for All Encoders". I hope it might be useful to refer to it there.

The article contains a sample program which outputs EncoderParameters supported by stock GDI+ image encoders, and PNG encoder supports no parameters according to it.

I'm not sure thought if one can take this for granted, or in some future version of gdiplus.dll the PNG encoder might grow more support, and so one is supposed to check the encoder's capabilities at runtime.

In any case applying some explicit transformation on a source image appears to be more fruitful approach; I did not explore it yet though.

Sass answered 20/12, 2011 at 17:34 Comment(3)
Why would GDI+ not have a quality setting for PNG? Guess I'll have to use Jpeg then. :-/Loferski
@NickGotch, as PNG is a lossless format, it cannot have "quality setting" -- in a sense, its quality is always 100%. What the GDI+ encoder seems to be lacking is a knob controlling the encoding speed/compression ratio trade off.Sass
@Sass The "image/tiff" entry in that list has a "Compression" one, though. >edit< Oh nevermind, it's an enum of compression methods.Pamulapan
E
12

I have taken 4 different PNGs, ranging in sizes from 2KB to 2.6MB. Using your code, I load them up and save them out. I do not modify the image by rotating or flipping. All 4 PNGs, when re-saved, have exactly the same size.

In addition, I have taken the 2.6MB PNG, opened it in Photoshop and saved two copies of it, one interlaced (reduced to 2.06MB), one non-interlaced (reduced to 1.7MB.) I then took each of those, ran them through your code and saved them. The resulting sizes of both were back to the original 2.6MB.

So, my guess is that the original images were created with a piece of software (like photoshop) and that piece of software has an optimized compression algorithm it is using for the PNG spec that .NET does not employ.

I've messed around with the EncoderParameter for Compression, but it seems to have no impact.

Eskimo answered 10/3, 2010 at 19:13 Comment(4)
Yeb, your right. The images were exported by the 3dsMax jsr exporter. But do you know a way to compress the PNG or lower it's quality so I could acheive a similar result to the exporter?Anatomical
This looks promising: benhollis.net/software/pnggauntlet ... It wraps this PNGOUT from this page: advsys.net/ken/utils.htmEskimo
Thanks for your time, I think you gave me my answers.Anatomical
So, basically, the answer is, it's not C# that increases the size, it's other apps that seem to have optimization tricks to reduce it? Would sure be handy to have that in .Net too...Pamulapan
B
0

They're either saved using a different compression methods, or it's the same compression method but at different levels. LZW, for example, has 10 levels (0 for fast/no compression, to 9 for slow/max compression). I'd tell you how to set that when writing the image, but I found this thread while trying to figure that out lol.

Bribe answered 2/8, 2024 at 19:45 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.