Byte array to image conversion

401,819

Solution 1

You are writing to your memory stream twice, also you are not disposing the stream after use. You are also asking the image decoder to apply embedded color correction.

Try this instead:

using (var ms = new MemoryStream(byteArrayIn))
{
    return Image.FromStream(ms);
}

Solution 2

Maybe I'm missing something, but for me this one-liner works fine with a byte array that contains an image of a JPEG file.

Image x = (Bitmap)((new ImageConverter()).ConvertFrom(jpegByteArray));

EDIT:

See here for an updated version of this answer: How to convert image in byte array

Solution 3

public Image byteArrayToImage(byte[] bytesArr)
{
    using (MemoryStream memstr = new MemoryStream(bytesArr))
    {
        Image img = Image.FromStream(memstr);
        return img;
    }
}

Solution 4

I'd like to note there is a bug in solution provided by @isaias-b.

That solution assume that stride is equal to row length. But it is not always true. Due to memory alignments performed by GDI, stride can be greater then row length. This must be taken into account. Otherwise invalid shifted image will be generated. Padding bytes in each row will be ignored.

The stride is the width of a single row of pixels (a scan line), rounded up to a four-byte boundary.

Fixed code:

using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

public static class ImageExtensions
{
    public static Image ImageFromRawBgraArray(this byte[] arr, int width, int height, PixelFormat pixelFormat)
    {
        var output = new Bitmap(width, height, pixelFormat);
        var rect = new Rectangle(0, 0, width, height);
        var bmpData = output.LockBits(rect, ImageLockMode.ReadWrite, output.PixelFormat);

        // Row-by-row copy
        var arrRowLength = width * Image.GetPixelFormatSize(output.PixelFormat) / 8;
        var ptr = bmpData.Scan0;
        for (var i = 0; i < height; i++)
        {
            Marshal.Copy(arr, i * arrRowLength, ptr, arrRowLength);
            ptr += bmpData.Stride;
        }

        output.UnlockBits(bmpData);
        return output;
    }
}

To illustrate what it can lead to, let's generate PixelFormat.Format24bppRgb gradient image 101x101:

var width = 101;
var height = 101;
var gradient = new byte[width * height * 3 /* bytes per pixel */];
for (int i = 0, pixel = 0; i < gradient.Length; i++, pixel = i / 3)
{
    var x = pixel % height;
    var y = (pixel - x) / width;
    gradient[i] = (byte)((x / (double)(width - 1) + y / (double)(height - 1)) / 2d * 255);
}

If we will copy entire array as-is to address pointed by bmpData.Scan0, we will get following image. Image shifting because part of image was written to padding bytes, that was ignored. Also that is why last row is incomplete:

stride ignored

But if we will copy row-by-row shifting destination pointer by bmpData.Stride value, valid imaged will be generated:

stride taken into account

Stride also can be negative:

If the stride is positive, the bitmap is top-down. If the stride is negative, the bitmap is bottom-up.

But I didn't worked with such images and this is beyond my note.

Related answer: C# - RGB Buffer from Bitmap different from C++

Solution 5

All presented answers assume that the byte array contains data in a known file format representation, like: gif, png or jpg. But i recently had a problem trying to convert byte[]s, containing linearized BGRA information, efficiently into Image objects. The following code solves it using a Bitmap object.

using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
public static class Extensions
{
    public static Image ImageFromRawBgraArray(
        this byte[] arr, int width, int height)
    {
        var output = new Bitmap(width, height);
        var rect = new Rectangle(0, 0, width, height);
        var bmpData = output.LockBits(rect, 
            ImageLockMode.ReadWrite, output.PixelFormat);
        var ptr = bmpData.Scan0;
        Marshal.Copy(arr, 0, ptr, arr.Length);
        output.UnlockBits(bmpData);
        return output;
    }
}

This is a slightly variation of a solution which was posted on this site.

Share:
401,819
Tanzeel ur Rahman
Author by

Tanzeel ur Rahman

Updated on April 01, 2021

Comments

  • Tanzeel ur Rahman
    Tanzeel ur Rahman over 3 years

    I want to convert a byte array to an image.

    This is my database code from where I get the byte array:

    public void Get_Finger_print()
    {
        try
        {
            using (SqlConnection thisConnection = new SqlConnection(@"Data Source=" + System.Environment.MachineName + "\\SQLEXPRESS;Initial Catalog=Image_Scanning;Integrated Security=SSPI "))
            {
                thisConnection.Open();
                string query = "select pic from Image_tbl";// where Name='" + name + "'";
                SqlCommand cmd = new SqlCommand(query, thisConnection);
                byte[] image =(byte[]) cmd.ExecuteScalar();
                Image newImage = byteArrayToImage(image);
                Picture.Image = newImage;
                //return image;
            }
        }
        catch (Exception) { }
        //return null;
    }
    

    My conversion code:

    public Image byteArrayToImage(byte[] byteArrayIn)
    {
        try
        {
            MemoryStream ms = new MemoryStream(byteArrayIn,0,byteArrayIn.Length);
            ms.Write(byteArrayIn, 0, byteArrayIn.Length);
            returnImage = Image.FromStream(ms,true);//Exception occurs here
        }
        catch { }
        return returnImage;
    }
    

    When I reach the line with a comment, the following exception occurs: Parameter is not valid.

    How can I fix whatever is causing this exception?

    • Holstebroe
      Holstebroe over 12 years
      Have you checked that the image bytes in your query are valid? You could do a File.WriteAllBytes("myimage.jpg", byteArrayIn) to verify.
    • Andrew Morton
      Andrew Morton almost 3 years
      Would you like to accept one of the answers? What should I do when someone answers my question?
  • Holstebroe
    Holstebroe over 12 years
    It makes little sense to write the byte array to the memory stream after it has been initialized with the same byte array. Actually I am not sure MemoryStream allows writing beyond the length specified in the constructor.
  • Holstebroe
    Holstebroe over 12 years
    You may also explicitly make the memory stream non-writable after initialization: new MemoryStream(byteArrayIn, false)
  • RenniePet
    RenniePet over 11 years
    This violates a specification in MSDN for Image.FromStream(), where it says "You must keep the stream open for the lifetime of the Image." See also stackoverflow.com/questions/3290060/…
  • Julian50
    Julian50 about 9 years
    AAAh thanks, Finally a good answer. Why so many answers with memory stream, it cause me so much problem. thanks a lot !
  • A_L
    A_L almost 8 years
    Good answer! this can be used in a separate function whereas all other proposals using MemoryStream cannot (Stream must be kept open for lifetime of Image)
  • Reid
    Reid about 7 years
    Code useful as an idea, but of course won't work as written. returnImage has to be declared outside of try/catch section. Also returnImage has to be instantiated in 'catch' - I create a single pixel bitmap: var image = new Bitmap(1, 1); MemoryStream stream = new MemoryStream(); image.Save(stream, ImageFormat.Jpeg); stream.Position = 0;
  • Kayot
    Kayot over 6 years
    While it's not normally a good idea, I put a GC.Collect() before the Memory stream. I was running out of memory when I preloaded a whole lot of large graphic files as bytearrays into memory and then turned them into images during viewing.
  • kbridge4096
    kbridge4096 over 6 years
    as a reference MSDN: Bitmap(Int32, Int32): "This constructor creates a Bitmap with a PixelFormat enumeration value of Format32bppArgb.", which means byte[0] is blue, byte[1] is green, byte[2] is red, byte[3] is alpha, byte[4] is blue, and so on.
  • Casey Crookston
    Casey Crookston about 6 years
    THANK YOU for adding all of the needed "using"s. Most people forget that and it an be a pain to find them all.
  • Thulasiram
    Thulasiram about 3 years
    Working Fine :-).
  • aelveborn
    aelveborn almost 3 years
    This adds nothing to the existing answer posted 3 years prior, and suffers from the same problem of disposing the stream prematurely.