73

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))
Soner Gönül
  • 91,172
  • 101
  • 184
  • 324
goGud
  • 3,647
  • 7
  • 31
  • 58

4 Answers4

120

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.
Mike Perrenoud
  • 63,395
  • 23
  • 143
  • 222
  • And then is it enough to write bmp.Save(c:\\image.jpg); ?? – goGud Feb 04 '14 at 14:57
  • 1
    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 Feb 04 '14 at 15:02
  • @goGud, on what line? – Mike Perrenoud Feb 04 '14 at 15:03
  • '((System.IO.Stream)(ms)).ReadTimeout' threw an exception of type 'System.InvalidOperationException' '((System.IO.Stream)(ms)).WriteTimeout' threw an exception of type 'System.InvalidOperationException' These are from Debug – goGud Feb 04 '14 at 15:07
  • @goGud, **what line** is throwing the `ArgumentException`? – Mike Perrenoud Feb 04 '14 at 15:09
  • @goGud Maybe you should show us the code for creating the byte array from an image, as well. – Mario S Feb 04 '14 at 15:12
  • bmp = new Bitmap(ms); this line throws exception – goGud Feb 04 '14 at 15:14
  • @MichaelPerrenoud i think i have raw bytes – goGud Feb 04 '14 at 15:19
  • @goGud, based on the .NET source the only way you'd get this exception is if the `Stream` is `null`. Is the `byte[]` actually `null`? – Mike Perrenoud Feb 04 '14 at 15:20
  • no i also checked this it has value.. And also value of ms is same as byte[]. – goGud Feb 04 '14 at 15:26
  • @goGud: then at this point you fall into one of the other two categories I've documented in my answer. It's not a valid image or it's a PNG that's got a single dimension that's too large. – Mike Perrenoud Feb 04 '14 at 15:30
  • @wondra I'm not sure how it doesn't answer the question, can you elaborate on your position? – Mike Perrenoud Mar 15 '15 at 23:23
  • Second try: according to comments [here](http://stackoverflow.com/a/6782534/3096657) the stream should contain BMP, not byte array. Can you provide example how did you convert `byte[] imageData` to in-memory BMP data? – wondra Mar 21 '15 at 16:59
  • So just how is the height and width of that Bitmap specified? – Mike C Jan 29 '16 at 18:15
  • @MikeC the actual height and width of the image should be defined in the metadata of the file format. The display height and width would be handled by the user interface. – Mike Perrenoud Jan 29 '16 at 19:43
  • 9
    No! Don't dispose the stream! From [`Bitmap(Stream)`](https://msdn.microsoft.com/en-us/library/z7ha67kw%28v=vs.110%29.aspx): "You must keep the stream open for the lifetime of the Bitmap." – piedar May 17 '16 at 14:07
  • 2
    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`](http://referencesource.microsoft.com/#q=ImageConverter) 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. – Steven Doggart Feb 01 '18 at 14:34
  • problem with this is that it wont convert an array to a bitmap, because there is no dimensional image (how width height should the image be). If you start with a bitmap and put it in a stream, you'll provide such info and if you have such a stream you can do the rverse as well, but such a stream is not a normal array its more close to an an object. – Peter Jun 22 '18 at 08:34
30

Guys thank you for your help. I think all of this answers works. However i think my byte array contains raw bytes. That's why all of those solutions didnt work for my code.

However i found a solution. Maybe this solution helps other coders who have problem like mine.

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 solutions works for 8bit 256 bpp (Format8bppIndexed). If your image has another format you should change PixelFormat .

And there is a problem with colors right now. As soon as i solved this one 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.

And also this function Works for me.. This function 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"));
                    }
                }
            }
        }
Facebamm
  • 147
  • 2
  • 12
goGud
  • 3,647
  • 7
  • 31
  • 58
  • 4
    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: http://stackoverflow.com/questions/6782489/create-bitmap-from-a-byte-array-of-pixel-data – chrispepper1989 Oct 07 '14 at 09:59
19

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");
}
Mario S
  • 10,931
  • 24
  • 34
  • 45
  • 1
    +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`. – Mike Perrenoud Feb 04 '14 at 14:45
  • 1
    @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. – Mario S Feb 04 '14 at 14:49
  • 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. – goGud Feb 04 '14 at 14:51
  • @goGud It sounds like the byte array you're sending in to the `Image.FromStream` method is not a valid image. – Mario S Feb 04 '14 at 14:58
  • @Mario it has an byte array which has 0-99999 and i think i need to use 8bit, 256 bppi. – goGud Feb 04 '14 at 15:00
  • No! Don't dispose the stream! From [`Image.FromStream(Stream)`](https://msdn.microsoft.com/en-us/library/93z9ee4x\(v=vs.110\).aspx): "You must keep the stream open for the lifetime of the Image." – piedar May 17 '16 at 14:08
  • @piedar, you're right in large. Just in this test case though the code is saving the image to disk so there's no use to keep the stream. – Mario S May 23 '16 at 12:12
  • 1
    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). – Peter Jun 08 '18 at 07:09
1

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));
Majedur Rahaman
  • 1,582
  • 17
  • 28