104

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))

6 Answers 6

144

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.
Sign up to request clarification or add additional context in comments.

18 Comments

And then is it enough to write bmp.Save(c:\\image.jpg); ??
I get same error message like @mario 's answer : An unhandled exception of type 'System.ArgumentException' occurred in System.Drawing.dll Additional information: Parameter is not valid
@goGud Maybe you should show us the code for creating the byte array from an image, as well.
No! Don't dispose the stream! From Bitmap(Stream): "You must keep the stream open for the lifetime of the Bitmap."
As @piedar pointed out, the stream should NOT be closed/disposed until after the bitmap is disposed. However, if you are using a MemoryStream, then you don't have to worry about ever closing it, since the MemoryStream doesn't actually do anything when it's disposed anyway. The System.Drawing.ImageConverter.ConvertFrom method actually takes advantage of that fact, so it seems safe to make that assumption. Do, simply var bmp = new Bitmap(new MemoryStream(imageData)); will suffice.
|
36

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"));
                    }
                }
            }
        }

3 Comments

Hi goGud. You don't need to pad the data, "Format32bppRgb" uses 4 bytes per pixel (or 32 bits per pixel as the name states, 8 bits == 1 byte). "Format32bppRgb" is actually RGBX, whereas your data seems to be stored "RGB" hence the 3 bytes per pixel Try Format24bppRGB, this will be 3 bytes per pixel. and your stride will be width * 3 finally its worth mentioning the IntPtr part, you will have to keep a separate reference to that... if you want the bitmap to persist, I recommend looking at this post: stackoverflow.com/questions/6782489/…
internally a bitmap width is evenly dividable by four. Stride is normally bit depth * width but if that's not divisible by four, it's padded.
when I try to draw it I get this error : A Graphics object cannot be created from an image that has an indexed pixel format. (Parameter 'image') at System.Drawing.Graphics.FromImage(Image image)
21

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");
}

8 Comments

+1 for showing another way of skinning the cat with the static accessor, but yes you'll want to get that MemoryStream wrapped in a using.
@goGud I have updated the answer. System.Windows.Controls.Image is a control to show an image. Here, it's the class System.Drawing.Image that is used.
Ty i have last question and i think it will be end. When i used your code I get this error while debug. An unhandled exception of type 'System.ArgumentException' occurred in System.Drawing.dll Additional information: Parameter is not valid.
No! Don't dispose the stream! From Image.FromStream(Stream): "You must keep the stream open for the lifetime of the Image."
The problem here is that its not a stream of raw RGB as in question. To retrieve data no dimensions are given, it saves images just like any object can saved where the object itself allready was a image with makeup (ea dimensions pixelformat etc).
|
8

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));

2 Comments

error ArgumentException: Parameter is not valid.
I got the same error message ArgumentException: Parameter is not valid
3

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);

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.