Reputation: 3802
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 ;
Upvotes: 4
Views: 16130
Reputation: 4900
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
Upvotes: 2
Reputation: 307
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.
Upvotes: 1