Display a byte array in a pictureBox in C#

15,415

Solution 1

Every Image needs a description of what the content is of the byte array. This description is called Header. If you now want to exchange the bytes, you need to avoid changing the header.

http://en.wikipedia.org/wiki/BMP_file_format

That is a sample of my sourcecode while working with such an ByteArray

''' <summary>
''' Copies an Bytearray into an image and return it.
''' </summary>
''' <param name="ArrSrc">Source byte array of image which can be anything</param>
''' <param name="ImageSize">the image size of the image</param>
''' <param name="SourceArrayPixelFormat">Pixel format, like 24Bit or 32Bit</param>
''' <returns>System.Drawing.Image</returns>
''' <remarks>copyright, http://software.goldengel.ch, 2012</remarks>
Public Function ArrayToBitmapData(ByVal ArrSrc() As Byte, ByVal ImageSize As System.Drawing.Size, ByVal SourceArrayPixelFormat As System.Drawing.Imaging.PixelFormat) As System.Drawing.Bitmap
    'Kopiert ein ByteArray in ein Bitmap

    Dim m As Integer
    Dim bmTemp As System.Drawing.Bitmap = Nothing
    Dim S As System.Drawing.Size
    Dim MemorySize As Integer
    Dim ScanLine As Integer

    'Bild prüfen
    If ArrSrc Is Nothing Then Return bmTemp

    'Bildgrösse definieren
    'Bei unterschiedlichen Grössen, wird muss die kleinere Grösse verwendet werden
    S = ImageSize


    'Helfer für die Bildverarbeitung erzeugen
    Dim bts As System.Drawing.Imaging.BitmapData

    'Bitmap erzeugen um damit zu arbeiten
    bmTemp = New System.Drawing.Bitmap(S.Width, S.Height, SourceArrayPixelFormat)

    'Farbtiefe berechnen
    '24Bit und 32Bit Bilder werden unterstützt
    'Kann beliebig erweitert werden
    m = BytesInPixelFormat(SourceArrayPixelFormat)



    '*** Hauptroutine - Array --> Bitmap ***

    'Bilddaten in die Picturebox laden
    bts = bmTemp.LockBits(New System.Drawing.Rectangle(0, 0, S.Width, _
        S.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, SourceArrayPixelFormat)


    'Speicherplatz reservieren
    'MemorySize = S.Height * S.Width * m
    'Nur zur Kontrolle
    ScanLine = GetScanline(S, SourceArrayPixelFormat)

    MemorySize = S.Height * bts.Stride
    If ArrSrc.Length >= MemorySize Then
        'Bilddaten aus dem Array laden
        Global.System.Runtime.InteropServices.Marshal.Copy(ArrSrc, 0, bts.Scan0, MemorySize)
    End If
    bmTemp.UnlockBits(bts)

    'Erzeugtes Bitmap zurückgeben
    Return bmTemp

End Function



convert Bitmap image into byte array

'Neue Funktion 27.2.2008
'Mit korrekter Dimensionierung mittels bts.Stride und Umrechnung auf das Pixelformat
''' <summary>
''' Get an Array of the data of any image. 
''' Bitmap header execluded.
''' </summary>
''' <param name="bmSrc">Source image</param>
''' <param name="NeededDestinationPixelFormat">Pixelformat, like 24Bit or 32Bit</param>
''' <returns>Image content</returns>
''' <remarks>copyright http://software.goldengel.ch, 2012</remarks>
Public Function BitmapDataToArray(ByVal bmSrc As System.Drawing.Bitmap, ByVal NeededDestinationPixelFormat As System.Drawing.Imaging.PixelFormat, ByRef DstStride As Integer) As Byte()
    'Kopiert ein Bild in ein Bytearray

    Dim m As Integer
    Dim A() As Byte = Nothing
    Dim S As System.Drawing.Size
    Dim MemorySize As Integer
    Dim bmTemp As System.Drawing.Bitmap = Nothing

    'Bild prüfen
    If bmSrc Is Nothing Then Return A

    'Bildgrösse definieren
    'Bei unterschiedlichen Grössen, wird muss die kleinere Grösse verwendet werden
    S = bmSrc.Size


    'Helfer für die Bildverarbeitung erzeugen
    Dim bts As System.Drawing.Imaging.BitmapData

    'Farbtiefe berechnen
    '24Bit und 32Bit Bilder werden unterstützt
    'Kann beliebig erweitert werden
    m = BytesInPixelFormat(NeededDestinationPixelFormat)


    '*** Hauptroutine - Bitmap --> Array ***
    'Bilddaten aus der Picturebox laden
    If NeededDestinationPixelFormat <> bmSrc.PixelFormat Then
        'Bitmap erzeugen um damit zu arbeiten
        bmTemp = New System.Drawing.Bitmap(S.Width, S.Height, NeededDestinationPixelFormat)

        Using gr As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmTemp)
            gr.DrawImage(bmSrc, 0, 0)
        End Using
        'ImgSrc.Dispose()'Achtung, würde das Original mit zerstören
        bmSrc = bmTemp
    End If

    bts = bmSrc.LockBits(New System.Drawing.Rectangle(0, 0, S.Width, _
        S.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, NeededDestinationPixelFormat)

    'Speicherplatz reservieren
    MemorySize = S.Height * bts.Stride
    ReDim A(MemorySize - 1) '28.2.2010. wichtige Änderung. 1 Byte zuviel wurde reserviert. Das konnte bei Wiederholung von Graphics.Drawing zu einem Fehler kommen

    'Bitmapdaten in das Array kopieren
    Global.System.Runtime.InteropServices.Marshal.Copy(bts.Scan0, A, 0, A.Length)
    bmSrc.UnlockBits(bts)

    DstStride = bts.Stride

    If bmTemp IsNot Nothing Then bmTemp = Nothing

    Return A

End Function

Solution 2

Following your original suggestion at the top of your post, I didn't have any problem with this working.

pictureBox.Image = GetImage();

public Image GetImage()
{
    Image image;
    using (FileStream fs = File.OpenRead(@"C:\picture.jpg"))
    {
        long length = fs.Length;

        byte[] bytes = new byte[length];

        for (int pos = 0; pos < length; )
            pos += fs.Read(bytes, pos, (int)length - pos);

        fs.Position = 0;

        using (MemoryStream ms = new MemoryStream(bytes))
            image = Image.FromStream(ms);
    }

    return image;
}

Now, whether or not this is safe/correct I don't know, but it seems to work.

Share:
15,415
Smash
Author by

Smash

Updated on June 04, 2022

Comments

  • Smash
    Smash almost 2 years

    I read a lot of questions and answers, most suggest:

    byte[] byteArray; //(contains image data)
    MemoryStream stream = new MemoryStream(byteArray);
    Bitmap image = new Bitmap(stream);
    pictureBox.Image = image;
    

    or more directly:

    pictureBox.Image = Image.FromStream(stream);
    

    I always get :"An unhandled exception of type 'System.ArgumentException' occurred in System.Drawing.dll

    Additional information: Parameter is not valid."

    in regards to the stream parameter.

    Even in the case where:

    byte[] byteArray = new byte[1];
    byteArray[0] = 255;
    

    I can't figure out why.

    EDIT:

    I get the data from a file like this:

    //byteArray is defined as List<byte> byteArray = new List<byte>();
    TextReader tr = new StreamReader(file);
    string File = tr.ReadToEnd();
    string[] bits = File.Split('\t');
    List<string> image = new List<string>(bits);
    height = int.Parse(bits[0]);
    width  = int.Parse(bits[1]);
    image.RemoveRange(0, 2);
    image.RemoveAt(image.Count - 1);
    foreach (string s in image)
    {
      byteArray.Add(byte.Parse(s));
    }
    return byteArray //(i do .ToArray() in the MemoryStream call);
    

    In the debugger, I see i.e. that the byteArray is fine, count = 2244, values everywhere, etc.

    EDIT #2: Sample data file (first byte[0] is height, second byte[1] is width, rest is RGB data)

    47 15 12 55 25 52 55 25 52 55 25 52 55 25 52 55
    25 52 55 25 52 55 25 52 55 25 52 55 25 52 55 25
    52 55 25 52 55 25 52 55 25 52 55 25 52 55 25 52
    55 25 52 55 25 52 55 25 52 55 25 52 55 25 52 55
    25 52 51 24 82 49 24 82 49 24 92 50 25 12 50 24
    92 48 24 92 50 24 82 50 25 02 50 24 92 50 25 02
    51 25 12 50 24 92 49 25 02 50 25 02 49 25 12 49
    25 02 49 25 02 47 25 12 47 25 22 50 24 82 47 24
    82 50 24 72 50 24 82 49 24 82 50 24 72 50 24 82
    50 24 72 49 24 82 49 25 22 52 24 92 50 24 82 50
    24 72 47 25 00 etc.
    

    EDIT #3: SOLUTION

    Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
    BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
    IntPtr ptr = bmpData.Scan0;
    Marshal.Copy(byteArray, 0, ptr, height * width * 3);
    bmp.UnlockBits(bmpData);
    pictureBox.Image = bmp;
    

    Need to check for 4 byte alignment, so loading function is now:

    TextReader tr = new StreamReader(file);
    string File = tr.ReadToEnd();
    string[] bits = File.Split('\t');
    List<string> image = new List<string>(bits);
    height = int.Parse(bits[0]);
    width  = int.Parse(bits[1]);
    int falseBits = 0;
    int oldWidth = width;
    while (width % 4 != 0)
    {
        width++;
        falseBits++;
    }
    int size = height * width * 3;
    byte[] byteArray = new byte[size];
    Parallel.For(0, size - 1, i => byteArray[i] = 255);
    int index  = 0;
    int lineIndex = 0;
    image.RemoveRange(0, 2);
    image.RemoveAt(image.Count - 1);
    foreach (string s in image)
    {
        byteArray [index]   = byte.Parse(s);
        byteArray [index + 1] = byteArray [index];
        byteArray [index + 2] = byteArray [index];
        index +=3;
        lineIndex++;
        if (lineIndex == oldWidth)
        {
            lineIndex = 0;
            index += 3*falseBits;
        }
    }
    return byteArray ;
    
    • scartag
      scartag about 12 years
      what is the format of the image in the bytearray? and the last example will never work ... can't think of a one byte image :)
    • leppie
      leppie about 12 years
      The byte[] data is not well-formed then.
    • Smash
      Smash about 12 years
      the last example is just to say that the stream doesn't work even when I know the data is well-formed as leppie put it, i'll edit and write an example of the data I have
    • Hans
      Hans about 12 years
      Could you show us a sample data file?
    • Hans
      Hans about 12 years
      Does data mean the actual RGB color values for each pixel? How many bytes are used for one pixel? Or does the data also include a header (e.g. a bitmap header)?
    • Smash
      Smash about 12 years
      The data is a grayscale value for 8bpp (0-255)
    • user1703401
      user1703401 about 12 years
      This cannot work as is, you didn't create the proper bitmap file format. Use Bitmap.LockBits instead so you can write the pixels directly from the data. It looks like a greyscale image so create a 24bpp Bitmap and set the R, G and B byte values to the same value.
    • Smash
      Smash about 12 years
      Ok, got it to work, thanks for the help, you should post an answer so I can accept it, Nasenbaer's answer is just too poorly formated
  • Smash
    Smash about 12 years
    yea, that is pretty much what I want to do, except that my data is from a text file and the stream does not work...
  • Jesse
    Jesse about 12 years
    Maybe ReadToEnd isn't returning what you think it returns? Have you tried reading it in to a buffer?