views:

86

answers:

4

I just got a real surprise when I loaded a jpg file and turned around and saved it with a quality of 100 and the size was almost 4x the original. To further investigate I open and saved without explicitly setting the quality and the file size was exactly the same. I figured this was because nothing changed so it's just writing the exact same bits back to a file. To test this assumption I drew a big fat line diagonally across the image and saved again without setting quality (this time I expected the file to jump up because it would be "dirty") but it decreased ~10Kb!

At this point I really don't understand what is happening when I simply call Image.Save() w/out specifying a compression quality. How is the file size so close (after the image is modified) to the original size when no quality is set yet when I set quality to 100 (basically no compression) the file size is several times larger than the original?

I've read the documentation on Image.Save() and it's lacking any detail about what is happening behind the scenes. I've googled every which way I can think of but I can't find any additional information that would explain what I'm seeing. I have been working for 31 hours straight so maybe I'm missing something obvious ;0)

All of this has come about while I implement some library methods to save images to a database. I've overloaded our "SaveImage" method to allow explicitly setting a quality and during my testing I came across the odd (to me) results explained above. Any light you can shed will be appreciated.

Here is some code that will illustrate what I'm experiencing:

string filename = @"C:\temp\image testing\hh.jpg";
string destPath = @"C:\temp\image testing\";

using(Image image = Image.FromFile(filename))
{
    ImageCodecInfo codecInfo = ImageUtils.GetEncoderInfo(ImageFormat.Jpeg);

    //  Set the quality
    EncoderParameters parameters = new EncoderParameters(1);

    // Quality: 10
    parameters.Param[0] = new EncoderParameter(
        System.Drawing.Imaging.Encoder.Quality, 10L);
    image.Save(destPath + "10.jpg", codecInfo, parameters);

    // Quality: 75
    parameters.Param[0] = new EncoderParameter(
        System.Drawing.Imaging.Encoder.Quality, 75L);
    image.Save(destPath + "75.jpg", codecInfo, parameters);

    // Quality: 100
    parameters.Param[0] = new EncoderParameter(
        System.Drawing.Imaging.Encoder.Quality, 100L);
    image.Save(destPath + "100.jpg", codecInfo, parameters);

    //  default
    image.Save(destPath + "default.jpg", ImageFormat.Jpeg);

    //  Big line across image
    using (Graphics g = Graphics.FromImage(image))
    {
        using(Pen pen = new Pen(Color.Red, 50F))
        {
            g.DrawLine(pen, 0, 0, image.Width, image.Height);
        }
    }

    image.Save(destPath + "big red line.jpg", ImageFormat.Jpeg);
}

public static ImageCodecInfo GetEncoderInfo(ImageFormat format)
{
    return ImageCodecInfo.GetImageEncoders().ToList().Find(delegate(ImageCodecInfo codec)
    {
        return codec.FormatID == format.Guid;
    });
}
A: 

IIRC, it is 75%, but I dont recall where I read this.

leppie
75% is the accepted compression for displaying on screen.
Stefanvds
A: 

I don't know much about the Image.Save method, but I can tell you that adding that fat line would logicly reduce the size of the jpg image. This is due to the way a jpg is saved (and encoded).

The thick black line makes for a very simple and smaller encoding (If I remember correctly this is relevent mostly after the Discrete cosine transform), so the modified image can be stored using less data (bytes).

jpg encoding steps

Regarding the changes in size (without the added line), I'm not sure which image you reopened and resaved

To further investigate I open and saved without explicitly setting the quality and the file size was exactly the same

If you opened the old (original normal size) image and resaved it, then maybe the default compression and the original image compression are the same. If you opened the new (4X larger) image and resaved it, then maybe the default compression for the save method is derived from the image (as it was when loaded).

Again, I don't know the save method, so I'm just throwing ideas (maybe they'll give you a lead).

Neowizard
@Neowizard: You're right, it makes perfect sense that a large, solid region would compress efficiently. What I was trying to accomplish and should have explained better is to force a re-encoding of the data. I was trying to determine if I was seeing the same file size because Save() was doing a straight dump of bits to disk. I figured if I stomped across the image and then saved it would trigger the file to be re-encoded. Thanks for the links and additional information.
Steve K
+1  A: 

Using reflector, it turns out Image.Save() boils down to the GDI+ function GdipSaveImageToFile, with the encoderParams NULL. So I think the question is what the JPEG encoder does when it gets a null encoderParams. 75% has been suggested here, but I can't find any solid reference.

EDIT You could probably find out for yourself by running your program above for quality values of 1..100 and comparing them with the jpg saved with the default quality (using, say, fc.exe /B)

ohadsc
@ohadsc: Ah, I forgot about reflector! ;0) Good to know there is no mysterious magic happening in that method. I think I will do a test as you suggested just to set my mind at ease. I will report the results here on this thread.
Steve K
I did a quick test and opened a *.bmp file, then saved to jpg with default settings and also with a quality explicitly set to 75. The files are EXACTLY the same size. So it must be that the encoder is internally defaulting to 75. I think this should be in the documentation somewhere, don't you guys?
Steve K
Definitely, but I'm not sure *which* documentation. As far as I understand, it is a feature of the default windows JPEG encoder, whose documentation resides in a place unknown to me...
ohadsc
A: 

When you save an image as a JPEG file with a quality level of <100%, you are introducing artefacts into the saved-off image, which are a side-effect of the compression process. This is why re-saving the image at 100% is actually increasing the size of your file beyond the original - ironically there's more information present in the bitmap.

This is also why you should always attempt to save in a non-lossy format (such as PNG) if you intend to do any edits to your file afterwards, otherwise you'll be affecting the quality of the output through multiple lossy transformations.

Dave R.
As a tangent, it is also important to save at a high color depth. When doing certain operations on images, especially images with gradients, it is possible to induce posterization. Using high color depth source images helps smooth color gradients to stay smooth.
emddudley
@Dave: Thanks for the info. I did a quick test in photoshop to see what it would do with this image when saved at 100 quality and indeed it increased the size to about the same as my test I did at 100.
Steve K