[RESOLVED] How to read a binary file into 2D array
Hi guys,
I was wondering if you could help me on reading a binary file into 2D array. I have an image that has dimension of (100,100). I am using FileStream class. When I run the code I get EndOfStreamException error at line 44. I have pasted the for loop section of the code below.
Code:
For x = 0 To 99
For y = 0 To 99
byteRead1(x, y) = br1.ReadByte ' line 44 (exception thrown here)
Next y
Next x
for what reason would you want to read a binary file into a 2d array ? There is no good reason for doing that. Explain what you are trying to do and i'm sure someone will be able to help you
May be to 'quickload' an image by loading the bytes directly? Not sure
The only reason this doesn't work must be in the image file itself. If you are loading a .png or .jpg image for example, it contains compression. The same counts for the .bmp image, since it contains an header. Loading image files like this is probably not possible, unless the image is stored in raw byte data which I doubt.
The image I am using has raw bytes and does not include header or something else. I want to load the image pixel values into 2D array so that I could be able to access a pixel's values using (x,y).
Then you would not know the dimensions any ways. I recommend building in a simple header to your custom image format:
<short value indicating width>
<short value indicating height>
For x As Integer = 0 To Width - 1
For y As Integer = 0 To Height - 1
Write byte color (x/y)
Next
Next
Make the y-loop on the outside to save in horizontal lines, instead of vertical as it is now.
I don't think this has anything to do with the image. Are you are reusing an existing filestream? If so, you should either declare it as a New FileStream just before the loop, or you can reset it like this:
Code:
br1.Seek(0, SeekOrigin.Begin)
Then you should check you are not mistaken in the stream length:
I don't think this has anything to do with the image. Are you are reusing an existing filestream? If so, you should either declare it as a New FileStream just before the loop, or you can reset it like this:
Code:
br1.Seek(0, SeekOrigin.Begin)
Then you should check you are not mistaken in the stream length:
I'm lost here. Your read loop reads raw bytes. The image is a GIF file and a single byte does not correspond to a pixel because GIF uses LZW compression, that's why instead of the image taking up 100x100 = 10000 bytes of space it only uses 437 bytes.
I feel pretty certain that this isn't the issue here and I've misunderstood the problem.
But the image reminds me of the image pathfinding thread, so I believe he posted the "visual representation" of the image, not the actual file containing the bytes. If this is the case, please upload the real image file (not the visual representation) you try to load.
It's the test image cs_tx_usa is using. He has turned that into a custom image file format which he can't even read himself yet, so there is not much point in posting that as an image to the forum. The error tells us the filestream isn't long enough for the loop (given that it is being read from the beginning). So all we need to know for the moment is how long the file is in bytes (my guess: 1,250).
If the file were readable but giving garbage, there might be some point in looking at it. But then it would be more useful to see the encoding routine as well as the decoding.
BB
Last edited by boops boops; Apr 5th, 2011 at 03:51 PM.
Ok guys, the image I have attached on my previous post has only two color index values (0 and 1):
black pixels have a value of 0
white pixels have a value of 1
What I would like to do is that I want to read those pixel values (0s and 1s) into 2-dimensional array. The image has 100x100 (10,000 pixels) and I would like to load those values into 2D array of 100 rows and 100 columns.
I would appreciate it if you could show me the way to accomplish this problem.
Hmm I am confused here too because you are right and I get 437 as file length. Ok then what I should use to read the values that are stored in each pixel in a GIF file?
Originally Posted by ntg
I'm lost here. Your read loop reads raw bytes. The image is a GIF file and a single byte does not correspond to a pixel because GIF uses LZW compression, that's why instead of the image taking up 100x100 = 10000 bytes of space it only uses 437 bytes.
I feel pretty certain that this isn't the issue here and I've misunderstood the problem.
A CUSTOM format. You can't use the Image class at all.
The first step is converting your current Image format to a data byte array:
Code:
Dim img As Bitmap = New Bitmap(32, 32) 'load it here
Dim data(img.Width - 1, img.Height - 1) As Byte
For x = 0 To img.Width - 1
For y = 0 To img.Height - 1
With img.GetPixel(x, y)
If .R < 128 And .G < 128 And .B < 128 Then
'black value
data(x, y) = 0
Else
'white value
data(x, y) = 1
End If
End With
Next
Next
'===================================
This method is a bit slow (uses GetPixel), but since it only has to run one time it is no issue. Next you save the byte data to a file.
Code:
Dim f As New IO.FileStream("Filetosaveto.cif", IO.FileMode.OpenOrCreate, IO.FileAccess.Write)
Dim b As New IO.BinaryWriter(f)
'header
b.Write(img.Width)
b.Write(img.Height)
'data, horizontal rows
For y As Integer = 0 To img.Height - 1
For x As Integer = 0 To img.Width - 1
b.Write(data(x, y))
Next
Next
b.Close()
f.Close()
'===================================
"cif" stands for "custom image format".
Now you have a file. If your image is 100x100, it will contain 100^2 + 4*2 bytes = 10008 bytes. (an integer is a length of 4 bytes)
Now time to load it back into a multidimensional array:
Code:
Dim f As New IO.FileStream("Filetosaveto.cif", IO.FileMode.Open, IO.FileAccess.Read)
Dim b As New IO.BinaryReader(f)
Dim width As Integer = b.ReadInt32()
Dim height As Integer = b.ReadInt32()
Dim data(width - 1, height - 1) As Byte
For y As Integer = 0 To height - 1
For x As Integer = 0 To width - 1
data(x, y) = b.ReadByte
Next
Next
b.Close()
f.Close()
'====================================
Additionally, for a final test, you could do the reverse of GetPixel again (use SetPixel) and generate a new Bitmap.
Please note that you only have to store a single bit per pixel, so basically you could set 8 bits in a single byte. This is done using bit shifting.
The format of the image will be 8x as small, but it will be a bit harder to load and save images. Plus your image must be a size with 8 as base number:
8, 16, 24, 32, 40, 48, 56, etc.
I left it out to keep this all simple.
Last edited by bergerkiller; Apr 5th, 2011 at 05:07 PM.
Do you want to go into "compression"? You could make the size of the image 8x as small if you were to store 8 pixels in a single byte.
Made a simple "BPixel" structure that can hold 8 boolean values and converts them into a byte, and can get boolean values back.
Code:
Public Structure BPixel
Sub New(ByVal eigthvalues As Byte)
Me._value = eigthvalues
End Sub
Shared Widening Operator CType(ByVal b As BPixel) As Byte
Return b._value
End Operator
Private _value As Byte
Public Property Pixels() As Boolean()
Get
Dim rval(7) As Boolean
For i As Integer = 0 To 7
rval(i) = Me.Pixels(i)
Next
Return rval
End Get
Set(ByVal value() As Boolean)
Me._value = 0
For i As Integer = 0 To 7
If value(i) Then Me._value += 2 ^ i
Next
End Set
End Property
Public Property Pixels(ByVal index As Integer) As Boolean
Get
Return (Me._value And 2 ^ index) = 2 ^ index
End Get
Set(ByVal value As Boolean)
If Pixels(index) <> value Then
If value = True Then Me._value += 2 ^ index Else Me._value -= 2 ^ index
End If
End Set
End Property
End Structure
Hi Bergerkiller,
the TIFF image I am trying to read has 1170lines and 675rows and in total of 1,968,821 bytes... But the code below reads only 789,750 bytes of it...
Code:
Dim data(width - 1, height - 1) As Byte
Dim output(width - 1, height - 1) As Byte
Dim x As Integer
Dim y As Integer
For y = 0 To height - 1
For x = 0 To width - 1
data(x, y) = br1.ReadByte
output(x, y) = data(x, y)
bw.Write(output(x, y))
Next
Next
s1.Close()
s3.Close()
I think for next loop does not work for 2D arrays during reading and writing to files. Do you have any idea what to do?
That is pretty obvious, 1170 * 675 = 789750. As I said before, you can not load regular images, only raw data. The images must be loaded using Image.load() and loading is done by the system (codec).
If you REALLY want to do this directly, load the file using Image.load and save it in raw data to a new file.
Just to make sure you understand:
1. Tiff, Bmp, Jpeg, Png, etc. image format contain compression. You can not load these images by looping the bytes
2. Only RAW image data (every pixel in a single byte) can be looped
3. To convert Image to RAW data, Load the image and use GetPixel on a multidimensional byte array. Then save it in raw form.
Thank you, Bergerkiller.. You made your point very clear )
Originally Posted by bergerkiller
That is pretty obvious, 1170 * 675 = 789750. As I said before, you can not load regular images, only raw data. The images must be loaded using Image.load() and loading is done by the system (codec).
If you REALLY want to do this directly, load the file using Image.load and save it in raw data to a new file.
Hi bergerkiller. Thanks again for all your help. Now that bytes are loaded into 2D array how can I access it from another method. Bytes are loaded into 2D array in form1_load subroutine but I would like to access the elements of this array from another method.
Originally Posted by bergerkiller
That is pretty obvious, 1170 * 675 = 789750. As I said before, you can not load regular images, only raw data. The images must be loaded using Image.load() and loading is done by the system (codec).
If you REALLY want to do this directly, load the file using Image.load and save it in raw data to a new file.
Re: [RESOLVED] How to read a binary file into 2D array
I guess its best to keep it empty:
Code:
Public Data()() As Byte
If you define bounds it will only overwrite it later any ways. You can not (easily) change the dimensions of a multi dimensional array, so best is to set the Data member directly, like so:
Code:
Dim NewData(width - 1, height - 1) As Byte
For ---
For ---
NewData(x,y) = r.ReadByte()
Next
Next
Data = NewData
Another minor thing: you may want to read all bytes of a single horizontal row in one go.
Code:
For y As Integer = 0 To Height - 1
r.Read(NewData(y), 0, Width)
Next
Or something like that. Not sure if it is faster, but it would reduce the amount of traffic going to the processor.
Last edited by bergerkiller; Apr 12th, 2011 at 08:22 AM.