Why Bitmap.Save changes image's size?

1.4k views Asked by At

I succeeded to change image's DateTaken property. But, after resaving the image, its size changed. I checked with Matlab and both images' bytes are identical.

To check whether changing property changes the size I decided just to open the file and save it without changing any properties. The code is below:

using (var image = new Bitmap(@"C:\Temp\1.jpg"))
{  
    image.Save(@"C:\Temp\2.jpg");
}

But, the size still changed. The size of the original jpeg image 1.jpg is 1.88 MB (1,975,162 bytes). After resaving it to 2.jpg, the size of the image changes to 1.86 MB (1,960,824 bytes).

What could be the problem? Though Matlab ensured me that the images' bytes were not changed I want to hear from you that Bitmap.Save shouldn't change the image's bytes.

2

There are 2 answers

0
user3344003 On BEST ANSWER

JPEG is capable of variable compression that trades compression off against fidelity to the original. The factors that can effect size include:

  1. The specific JPEG format: Sequential or Progressive. Sequential encodes all the data for a component in a single scan. Progressive encodes a component in multiple scans. Progressive can often produce greater compression. However, there is a myriad of settings that apply to progressive.

  2. Sampling. Jpeg allows the Cb and Cb components to be sampled at a lower rate than the Y component. If you take one Cb and Cr sample for every two Ys (vertical and horizontal), you get 1 Cb and CR sample for every 4 Y samples. That reduces the amount of data to compress from in half from 12 to 6.

  3. Quantization tables. Quantization table selection is the main form of JPEG compression setting. Many encoders hide this behind a "quality" setting.

  4. Huffman tables. Some encoders use predefined Huffman tables. You can get better compression if the encoder generates a Huffman table optimized for the specific image (slower and more work).

Unless you recompress using the same settings at the original, you will get a different output size. Even if you use the identical settings, you usually come up with different values because of rounding: JPEG uses floating point calculations that get rounded to integers.

5
Icemanind On

Jpeg is a compression format. It is capable of saving in different qualities and different compression formats. My guess is, the compression level is set differently on the two images. Because the bytes get decompressed on read (regardless of the compression format), the bytes will be identical, even though they are compressed differently.

Think about if you take a file and compress it with ZIP format and you take the same file and compress it with RAR format. The two files will have different sizes, but when they are decompressed, the files are identical inside.

You can read about how to set compression levels for images here

I am copying the code from the link for easier reference:

private void VaryQualityLevel()
{
    // Get a bitmap.
    Bitmap bmp1 = new Bitmap(@"c:\TestPhoto.jpg");
    ImageCodecInfo jpgEncoder = GetEncoder(ImageFormat.Jpeg);

    // Create an Encoder object based on the GUID 
    // for the Quality parameter category.
    System.Drawing.Imaging.Encoder myEncoder =
        System.Drawing.Imaging.Encoder.Quality;

    // Create an EncoderParameters object. 
    // An EncoderParameters object has an array of EncoderParameter 
    // objects. In this case, there is only one 
    // EncoderParameter object in the array.
    EncoderParameters myEncoderParameters = new EncoderParameters(1);

    EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, 50L);
    myEncoderParameters.Param[0] = myEncoderParameter;
    bmp1.Save(@"c:\TestPhotoQualityFifty.jpg", jpgEncoder, myEncoderParameters);

    myEncoderParameter = new EncoderParameter(myEncoder, 100L);
    myEncoderParameters.Param[0] = myEncoderParameter;
    bmp1.Save(@"c:\TestPhotoQualityHundred.jpg", jpgEncoder, myEncoderParameters);

    // Save the bitmap as a JPG file with zero quality level compression.
    myEncoderParameter = new EncoderParameter(myEncoder, 0L);
    myEncoderParameters.Param[0] = myEncoderParameter;
    bmp1.Save(@"c:\TestPhotoQualityZero.jpg", jpgEncoder, myEncoderParameters);

}