Home > front end >  File.ReadAllBytes doesn't read the PNG image pixels properly
File.ReadAllBytes doesn't read the PNG image pixels properly

Time:05-12

I am trying to read the bytes of a .png image using File.ReadAllBytes(string) method without success.

My images are of size 2464x2056x3 (15.197.952 bytes), but this method returns an array of about 12.000.000 bytes.

I tried with a white image of the same size, and I am getting a byte array of 25.549, and checking the byte array I can see all kind of values, that obviously is not correct because is a white image.

The code I am using is:

var frame = File.ReadAllBytes("C:\\workspace\\white.png");

I've also tried to open the image first as an Image object then get the byte array with the following:

using (var ms = new MemoryStream())
{
  var imageIn = Image.FromFile("C:\\workspace\\white.png");
  imageIn.Save(ms, imageIn.RawFormat);
  var array = ms.ToArray();
}

But the result is the same as before...

Any idea of what's happening?

How can I read the byte array?

CodePudding user response:

PNG is a compressed format.
See some info about it: Portable Network Graphics - Wikipedia.

This means that the binary representation is not the actual pixel values that you expect.

You need some kind of a PNG decoder to get the pixel values from the compressed data.

This post might steer you in the right direction: Reading a PNG image file in .Net 2.0. Note that it's quite old, maybe there are newer methods for doing it.

On a side note: even a non compressed format like BMP has a header, so you can't simply read the binary file and get the pixel values in a trivial way.


Update: One way to get the pixel values from a PNG file is demonstrated below:

using System.Drawing.Imaging;

byte[] GetPngPixels(string filename)
{
    byte[] rgbValues = null;

    // Load the png and convert to Bitmap. This will use a .NET PNG decoder:
    using (var imageIn = Image.FromFile(filename))
    using (var bmp = new Bitmap(imageIn))
    {
        // Lock the pixel data to gain low level access:
        BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);

        // Get the address of the first line.
        IntPtr ptr = bmpData.Scan0;

        // Declare an array to hold the bytes of the bitmap.
        int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
        rgbValues = new byte[bytes];

        // Copy the RGB values into the array.
        System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);

        // Unlock the pixel data:
        bmp.UnlockBits(bmpData);
    }

    // Here rgbValues is an array of the pixel values.
    return rgbValues;
}

This method will return a byte array with the size that you expect.
In order to use the data with opencv (or any similar usage), I advise you to enhance my code example and return also the image metadata (width, height, stride, pixel-format). You will need this metadata to construct a cv::Mat.

  • Related