goGud
goGud

Reputation: 4341

How to create bitmap from byte array?

I searched all question about byte array but i always failed. I have never coded c# i am new in this side. Could you help me how to make image file from byte array.

Here is my function which stores byte in array named imageData

public void imageReady( byte[] imageData, int fWidth, int fHeight))

Upvotes: 103

Views: 236451

Answers (6)

goGud
goGud

Reputation: 4341

Thank you for your help. I think all of the answers provided could work. However, my ByteArray contains raw bytes. That's those solutions didn't work for my code.

However, I found a solution. Maybe it will help others who have the same problem I had.

static byte[] PadLines(byte[] bytes, int rows, int columns) {
   int currentStride = columns; // 3
   int newStride = columns;  // 4
   byte[] newBytes = new byte[newStride * rows];
   for (int i = 0; i < rows; i++)
       Buffer.BlockCopy(bytes, currentStride * i, newBytes, newStride * i, currentStride);
   return newBytes;
 }

 int columns = imageWidth;
 int rows = imageHeight;
 int stride = columns;
 byte[] newbytes = PadLines(imageData, rows, columns);

 Bitmap im = new Bitmap(columns, rows, stride, 
          PixelFormat.Format8bppIndexed, 
          Marshal.UnsafeAddrOfPinnedArrayElement(newbytes, 0));

 im.Save("C:\\Users\\musa\\Documents\\Hobby\\image21.bmp");

This works for 8bit 256 bpp (Format8bppIndexed). If your image has a different format you should change PixelFormat .

I still have a problem with colors right now. As soon as I solve that I will edit my answer for other users.

*PS = I am not sure about stride value but for 8bit it should be equal to columns.

Also this function works for me... it copies 8 bit greyscale image into a 32bit layout.

public void SaveBitmap(string fileName, int width, int height, byte[] imageData)
        {
            
            byte[] data = new byte[width * height * 4];

            int o = 0;

            for (int i = 0; i < width * height; i++)
            {
                byte value = imageData[i];

                
                data[o++] = value;
                data[o++] = value;
                data[o++] = value;
                data[o++] = 0; 
            }

            unsafe
            {
                fixed (byte* ptr = data)
                {
                    
                    using (Bitmap image = new Bitmap(width, height, width * 4,
                                PixelFormat.Format32bppRgb, new IntPtr(ptr)))
                    {
                        
                        image.Save(Path.ChangeExtension(fileName, ".jpg"));
                    }
                }
            }
        }

Upvotes: 37

Gy&#246;rgy Kőszeg
Gy&#246;rgy Kőszeg

Reputation: 18033

Based on the accepted answer the OP wanted to interpret imageData byte array as the pixel buffer, rather than an already encoded bitmap stream as the most upvoted answer suggests. And though it works, it contains a lot of copies, as well as palette issues ("And there is a problem with colors right now").

I actually happen to have a drawing library exactly for this purpose (among others). The platform-independent core library allows you to interpret any array of primitive types as a bitmap data:

// Unlike in the accepted answer, no extra buffer allocation or
// array copy happens in the background. Note that we can specify
// a palette for the indexed format so the colors will be interpreted correctly
using var myBitmap = BitmapDataFactory.CreateBitmapData(imageData, new Size(fWidth, fHeight),
    stride: fWidth, // stride is same as width because of the 8bpp pixel format
    pixelFormat: KnownPixelFormat.Format8bppIndexed,
    palette: Palette.Grayscale256());

myBitmap is now an IReadWriteBitmapData instance, allowing a lot of operations (just see the available extension methods). It also offers a pretty fast SetPixel method, which respects the palette so in this particular case it turns any color to grayscale. But if you know the actual pixel format you can also can use the WriteRaw<T> method to access the pixels directly.

And if you use the technology-specific packages such as the one for GDI+ or WPF, then you can simply convert your buffer into known bitmap types such as System.Drawing.Bitmap or System.Windows.Media.WriteableBitmap:

// the accepted answer creates two bitmaps due to the color problems where
// the 2nd one is a 32 bpp image. This solution is much faster, simpler, it avoids
// unnecessary allocations and uses parallel processing internally if possible
var systemBitmap = myBitmap.ToBitmap(); // or ToBitmapAsync, ToWriteableBitmap, etc.

Upvotes: 0

anhoppe
anhoppe

Reputation: 4507

This was helpful to me: https://www.tek-tips.com/viewthread.cfm?qid=1264492 (Reference answer)

I understand the question as follows:

  • I have a byte array that contains pixel data e.g. in RGB format (24bit/pixel)
  • From this raw pixel data I want to create a Bitmap

This code worked for me:

int width = ...;
int height = ...;
byte[] pixelArray = new byte[] {
  // Creation of the actual data is not in the scope of this answer
};

Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppRgb);

// Create a BitmapData and lock all pixels to be written 
BitmapData bmpData = bmp.LockBits(
                    new Rectangle(0, 0, bmp.Width, bmp.Height),
                    ImageLockMode.WriteOnly, bmp.PixelFormat);
// Copy the data from the byte array into BitmapData.Scan0
Marshal.Copy(pixelArray, 0, bmpData.Scan0, pixelArray.Length);

// Unlock the pixels
bmp.UnlockBits(bmpData);

// Do something with your image, e.g. save it to disc
bmp.Save("c:\\temp\\mybmp.bmp", ImageFormat.Bmp);

Upvotes: 3

Majedur
Majedur

Reputation: 3242

In addition, you can simply convert byte array to Bitmap.

var bmp = new Bitmap(new MemoryStream(imgByte));

You can also get Bitmap from file Path directly.

Bitmap bmp = new Bitmap(Image.FromFile(filePath));

Upvotes: 7

Mike Perrenoud
Mike Perrenoud

Reputation: 67928

You'll need to get those bytes into a MemoryStream:

Bitmap bmp;
using (var ms = new MemoryStream(imageData))
{
    bmp = new Bitmap(ms);
}

That uses the Bitmap(Stream stream) constructor overload.

UPDATE: keep in mind that according to the documentation, and the source code I've been reading through, an ArgumentException will be thrown on these conditions:

stream does not contain image data or is null.
-or-
stream contains a PNG image file with a single dimension greater than 65,535 pixels.

Upvotes: 142

Mario S
Mario S

Reputation: 11955

Can be as easy as:

var ms = new MemoryStream(imageData);
System.Drawing.Image image = Image.FromStream(ms);

image.Save("c:\\image.jpg");

Testing it out:

byte[] imageData;

// Create the byte array.
var originalImage = Image.FromFile(@"C:\original.jpg");
using (var ms = new MemoryStream())
{
    originalImage.Save(ms, ImageFormat.Jpeg);
    imageData = ms.ToArray();
}

// Convert back to image.
using (var ms = new MemoryStream(imageData))
{
    Image image = Image.FromStream(ms);
    image.Save(@"C:\newImage.jpg");
}

Upvotes: 21

Related Questions