forked from esteevens/MatlabFileDotNet
-
Notifications
You must be signed in to change notification settings - Fork 0
/
MatlabFileReader.cs
154 lines (133 loc) · 6.56 KB
/
MatlabFileReader.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Ionic.Zlib;
namespace MatlabFileIO
{
public class MatfileReader
{
private BinaryReader readStream;
public Dictionary<String, Variable> Variables { get; private set; }
public MatfileReader(string fileName)
{
//create stream from filename
FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
this.readStream = new BinaryReader(fileStream);
byte[] headerBytes = readStream.ReadBytes(128);
//Parse header (will throw if fail)
new Header(headerBytes);
ReadVariables();
}
private void ReadVariables()
{
Variables = new Dictionary<string, Variable>();
while(readStream.BaseStream.Position < readStream.BaseStream.Length)
{
Variable v = new Variable();
Tag t = MatfileHelper.ReadTag(readStream);
if(t.dataType == null)
throw new Exception("Not an array, don't know what to do with this stuff");
else if (t.dataType.Equals(typeof(Array))) //We use Array to indicate MiMatrix
{
ReadMatrix(ref v, t.length, readStream);
} else if (t.dataType.Equals(typeof(ZlibStream)))
{
byte[] compressed = readStream.ReadBytes((int)t.length);
byte[] decompressed = ZlibStream.UncompressBuffer(compressed);
MemoryStream m = new MemoryStream(decompressed);
BinaryReader br = new BinaryReader(m);
Tag ct = MatfileHelper.ReadTag(br);
ReadMatrix(ref v, ct.length, br);
}
else
throw new Exception("Not an array, don't know what to do with this stuff");
Variables.Add(v.name, v);
}
}
private static void ReadMatrix(ref Variable vi, UInt32 length, BinaryReader matrixStream)
{
Tag t;
long offset = matrixStream.BaseStream.Position;
//Array flags
//Will always be too large to be in small data format, so not checking t.data
//t = MatfileHelper.ReadTag(matrixStream);
//UInt32 flagsClass = matrixStream.ReadUInt32();
//byte flags = (byte)(flagsClass >> 8) ;
//if ((flags & 0x80) == 0x08)
// throw new IOException("Complex numbers not supported");
//vi.dataType = MatfileHelper.parseArrayType((byte)flagsClass);
//matrixStream.ReadUInt32();//unused flags
Flag Flag = matrixStream.ReadFlag();
vi.dataType = Flag.dataClass;
//Dimensions - There are always 2 dimensions, so this
//tag will never be of small data format, i.e. not checking for t.data
t = MatfileHelper.ReadTag(matrixStream);
int[] arrayDimensions = new int[t.length / MatfileHelper.MatlabBytesPerType(t.dataType)];
int elements = 1;
for (int i = 0; i < arrayDimensions.Length; i++)
{
int dimension = (int)matrixStream.ReadUInt32();// depends on sizeof(t.dataType) => MatfileHelper.MatlabBytesPerType(t.dataType)
arrayDimensions[arrayDimensions.Length - i - 1] = dimension;
elements *= dimension;
}
//Don't keep single dimensions
arrayDimensions = arrayDimensions.Where(x => x > 1).ToArray();
//If by doing this, we end up without dimensions, it means we had a 1x...x1 array.
//We need at least 1 dimension to instantiate the final array, so...
if (arrayDimensions.Length == 0)
arrayDimensions = new int[1] { 1 };
//Array name
t = MatfileHelper.ReadTag(matrixStream);
if (t.data != null)
{
sbyte[] varname = t.data as sbyte[];
vi.name = Encoding.UTF8.GetString(Array.ConvertAll(varname, x => (byte)x));
} else {
byte[] varname = matrixStream.ReadBytes((int)t.length);
vi.name = Encoding.UTF8.GetString(varname);
MatfileHelper.AdvanceTo8ByteBoundary(matrixStream);
}
//Read and reshape data
t = MatfileHelper.ReadTag(matrixStream);
reshape(ref vi.data, ref vi.dataType, matrixStream, t, arrayDimensions, elements);
// Read Imaginary data
if (Flag.Complex)
{
t = matrixStream.ReadTag();
reshape(ref vi.Image, ref vi.dataType, matrixStream, t, arrayDimensions, elements);
}
//Move on in case the data didn't end on a 64 byte boundary
matrixStream.BaseStream.Seek(offset + length, SeekOrigin.Begin);
}
private static void reshape(ref object data, ref Type dataType, BinaryReader matrixStream, Tag t, int[] arrayDimensions, int elements)
{
if (t.length / MatfileHelper.MatlabBytesPerType(t.dataType) != elements)
throw new IOException("Read dimensions didn't correspond to header dimensions");
Array readBytes;
if (t.data == null)
readBytes = MatfileHelper.CastToMatlabType(t.dataType, matrixStream.ReadBytes((int)t.length));
else
readBytes = (Array)t.data;
Array reshapedData = Array.CreateInstance(dataType, elements);//elements variable replaced with arrayDimensions to sure Linear data
if (t.dataType != dataType) //This happens when matlab choses to store the data in a smaller datatype when the values permit it
{
Array linearData = Array.CreateInstance(dataType, readBytes.Length);
Array.Copy(readBytes, linearData, readBytes.Length);
Buffer.BlockCopy(linearData, 0, reshapedData, 0, linearData.Length * MatfileHelper.MatlabBytesPerType(dataType));
// dataType = t.dataType;
}
else //Readbytes is already in the correct type
Buffer.BlockCopy(readBytes, 0, reshapedData, 0, readBytes.Length * MatfileHelper.MatlabBytesPerType(dataType));
if (reshapedData.Length == 1)
data = reshapedData.GetValue(0);
else
data = reshapedData;
}
public void Close()
{
readStream.Close();
}
}
}