Search code examples
c#imagebitmapjpeg

Loaded bitmap differs from saved one in jpeg (c#)


I'm using following methods to load bitmap from jpeg file (JpegToBitmap):

private static Bitmap JpegToBitmap(string fileName)
    {

        FileStream jpg = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);

        JpegBitmapDecoder ldDecoder = new JpegBitmapDecoder(jpg, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
        BitmapFrame lfFrame = ldDecoder.Frames[0];
        Bitmap lbmpBitmap = new Bitmap(lfFrame.PixelWidth, lfFrame.PixelHeight);
        Rectangle lrRect = new Rectangle(0, 0, lbmpBitmap.Width, lbmpBitmap.Height);
        BitmapData lbdData = lbmpBitmap.LockBits(lrRect, ImageLockMode.WriteOnly, (lfFrame.Format.BitsPerPixel == 24 ? PixelFormat.Format24bppRgb : PixelFormat.Format32bppArgb));
        lfFrame.CopyPixels(System.Windows.Int32Rect.Empty, lbdData.Scan0, lbdData.Height * lbdData.Stride, lbdData.Stride);
        lbmpBitmap.UnlockBits(lbdData);

        return lbmpBitmap;
    }

And to save bitmap to jpeg file:

 private static void SaveToJpeg(Bitmap bitmap, string filePath)
    {
        EncoderParameters encoderParameters = new EncoderParameters(1);
        encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
        bitmap.Save(filePath, GetEncoder(ImageFormat.Jpeg), encoderParameters);
    }

    public static ImageCodecInfo GetEncoder(ImageFormat format)
    {
        ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();

        foreach (ImageCodecInfo codec in codecs)
        {
            if (codec.FormatID == format.Guid)
            {
                return codec;
            }
        }

        return null;
    }

Bitmap pixels differs just a little bit, but this difference is significant for me:

Screenshot of values in matrix of colors of bitmap before save

Screenshot of values in same matrix of colors of bitmap after load

This matrixes are just 2-d arrays of Color got by standart bitmap.GetPixel(x,y) in a loop.

public ColorMatrix(Bitmap bitmap, int currHeight, int currWidth)
    {
        matrix = new Color[8, 8];
        for (int y = 0; y < 8; y++)
        {
            for (int x = 0; x < 8; x++)
            {
                matrix[y, x] = bitmap.GetPixel(x + currWidth, y + currHeight);
            }
        }
    }

So, the question is: How can i load saved bitmap (in jpeg/png or whatever format) correctly?


Solution

  • There are three reasons:

    1. JPEG encoding and decoding processes use floating point arithmetic on integer input.

    2. The Cb and Cr components are often subsampled relative to the Y component during compression.

    3. During the quantization (fancy word for integer division) phase, DCT coefficients are usually discarded.