Support numerical arrays
This commit is contained in:
parent
93be86d526
commit
b66e380414
@ -50,6 +50,66 @@ namespace MatFileHandler.Tests
|
|||||||
Assert.That(arrayUnicodeWide.String, Is.EqualTo("🍆"));
|
Assert.That(arrayUnicodeWide.String, Is.EqualTo("🍆"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test reading a two-dimensional double array.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestMatrix()
|
||||||
|
{
|
||||||
|
var matFile = ReadHdfTestFile("matrix");
|
||||||
|
var matrix = matFile["matrix"].Value as IArrayOf<double>;
|
||||||
|
Assert.That(matrix.Dimensions, Is.EqualTo(new[] { 3, 2 }));
|
||||||
|
Assert.That(matrix.ConvertToDoubleArray(), Is.EqualTo(new[] { 1.0, 3.0, 5.0, 2.0, 4.0, 6.0 }));
|
||||||
|
Assert.That(matrix[0, 0], Is.EqualTo(1.0));
|
||||||
|
Assert.That(matrix[0, 1], Is.EqualTo(2.0));
|
||||||
|
Assert.That(matrix[1, 0], Is.EqualTo(3.0));
|
||||||
|
Assert.That(matrix[1, 1], Is.EqualTo(4.0));
|
||||||
|
Assert.That(matrix[2, 0], Is.EqualTo(5.0));
|
||||||
|
Assert.That(matrix[2, 1], Is.EqualTo(6.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test reading lower and upper limits of integer data types.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestLimits()
|
||||||
|
{
|
||||||
|
var matFile = ReadHdfTestFile("limits");
|
||||||
|
IArray array;
|
||||||
|
array = matFile["int8_"].Value;
|
||||||
|
CheckLimits(array as IArrayOf<sbyte>, CommonData.Int8Limits);
|
||||||
|
Assert.That(array.ConvertToDoubleArray(), Is.EqualTo(new[] { -128.0, 127.0 }));
|
||||||
|
|
||||||
|
array = matFile["uint8_"].Value;
|
||||||
|
CheckLimits(array as IArrayOf<byte>, CommonData.UInt8Limits);
|
||||||
|
|
||||||
|
array = matFile["int16_"].Value;
|
||||||
|
CheckLimits(array as IArrayOf<short>, CommonData.Int16Limits);
|
||||||
|
|
||||||
|
array = matFile["uint16_"].Value;
|
||||||
|
CheckLimits(array as IArrayOf<ushort>, CommonData.UInt16Limits);
|
||||||
|
|
||||||
|
array = matFile["int32_"].Value;
|
||||||
|
CheckLimits(array as IArrayOf<int>, CommonData.Int32Limits);
|
||||||
|
|
||||||
|
array = matFile["uint32_"].Value;
|
||||||
|
CheckLimits(array as IArrayOf<uint>, CommonData.UInt32Limits);
|
||||||
|
|
||||||
|
array = matFile["int64_"].Value;
|
||||||
|
CheckLimits(array as IArrayOf<long>, CommonData.Int64Limits);
|
||||||
|
|
||||||
|
array = matFile["uint64_"].Value;
|
||||||
|
CheckLimits(array as IArrayOf<ulong>, CommonData.UInt64Limits);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CheckLimits<T>(IArrayOf<T> array, T[] limits)
|
||||||
|
where T : struct
|
||||||
|
{
|
||||||
|
Assert.That(array, Is.Not.Null);
|
||||||
|
Assert.That(array.Dimensions, Is.EqualTo(new[] { 1, 2 }));
|
||||||
|
Assert.That(array.Data, Is.EqualTo(limits));
|
||||||
|
}
|
||||||
|
|
||||||
private static AbstractTestDataFactory<IMatFile> GetTests(string factoryName) =>
|
private static AbstractTestDataFactory<IMatFile> GetTests(string factoryName) =>
|
||||||
new MatTestDataFactory(Path.Combine(TestDirectory, factoryName));
|
new MatTestDataFactory(Path.Combine(TestDirectory, factoryName));
|
||||||
|
|
||||||
|
BIN
MatFileHandler.Tests/test-data/hdf/limits.mat
Normal file
BIN
MatFileHandler.Tests/test-data/hdf/limits.mat
Normal file
Binary file not shown.
BIN
MatFileHandler.Tests/test-data/hdf/matrix.mat
Normal file
BIN
MatFileHandler.Tests/test-data/hdf/matrix.mat
Normal file
Binary file not shown.
@ -8,26 +8,151 @@ using HDF.PInvoke;
|
|||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
{
|
{
|
||||||
public class HdfCharArray : ICharArray
|
internal class HdfArray : IArray
|
||||||
{
|
{
|
||||||
public HdfCharArray(int[] dimensions, string data)
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="HdfArray"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dimensions">Dimensions of the array.</param>
|
||||||
|
protected HdfArray(
|
||||||
|
int[] dimensions)
|
||||||
{
|
{
|
||||||
Dimensions = dimensions;
|
Dimensions = dimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int[] Dimensions { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int Count => Dimensions.NumberOfElements();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a new empty array.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Empty array.</returns>
|
||||||
|
public static HdfArray Empty()
|
||||||
|
{
|
||||||
|
return new HdfArray(Array.Empty<int>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual double[] ConvertToDoubleArray()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Complex[] ConvertToComplexArray()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsEmpty => Dimensions.Length == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A numerical array.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Element type.</typeparam>
|
||||||
|
internal class HdfNumericalArrayOf<T> : HdfArray, IArrayOf<T>
|
||||||
|
where T : struct
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="HdfNumericalArrayOf{T}"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dimensions">Dimensions of the array.</param>
|
||||||
|
/// <param name="name">Array name.</param>
|
||||||
|
/// <param name="data">Array contents.</param>
|
||||||
|
public HdfNumericalArrayOf(int[] dimensions, T[] data)
|
||||||
|
: base(dimensions)
|
||||||
|
{
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public T[] Data { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public T this[params int[] list]
|
||||||
|
{
|
||||||
|
get => Data[Dimensions.DimFlatten(list)];
|
||||||
|
set => Data[Dimensions.DimFlatten(list)] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to convert the array to an array of Double values.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Array of values of the array, converted to Double, or null if the conversion is not possible.</returns>
|
||||||
|
public override double[] ConvertToDoubleArray()
|
||||||
|
{
|
||||||
|
return Data as double[] ?? Data.Select(x => Convert.ToDouble(x)).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to convert the array to an array of Complex values.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Array of values of the array, converted to Complex, or null if the conversion is not possible.</returns>
|
||||||
|
public override Complex[] ConvertToComplexArray()
|
||||||
|
{
|
||||||
|
if (Data is Complex[])
|
||||||
|
{
|
||||||
|
return Data as Complex[];
|
||||||
|
}
|
||||||
|
if (Data is ComplexOf<sbyte>[])
|
||||||
|
{
|
||||||
|
return ConvertToComplex(Data as ComplexOf<sbyte>[]);
|
||||||
|
}
|
||||||
|
if (Data is ComplexOf<byte>[])
|
||||||
|
{
|
||||||
|
return ConvertToComplex(Data as ComplexOf<byte>[]);
|
||||||
|
}
|
||||||
|
if (Data is ComplexOf<short>[])
|
||||||
|
{
|
||||||
|
return ConvertToComplex(Data as ComplexOf<short>[]);
|
||||||
|
}
|
||||||
|
if (Data is ComplexOf<ushort>[])
|
||||||
|
{
|
||||||
|
return ConvertToComplex(Data as ComplexOf<ushort>[]);
|
||||||
|
}
|
||||||
|
if (Data is ComplexOf<int>[])
|
||||||
|
{
|
||||||
|
return ConvertToComplex(Data as ComplexOf<int>[]);
|
||||||
|
}
|
||||||
|
if (Data is ComplexOf<uint>[])
|
||||||
|
{
|
||||||
|
return ConvertToComplex(Data as ComplexOf<uint>[]);
|
||||||
|
}
|
||||||
|
if (Data is ComplexOf<long>[])
|
||||||
|
{
|
||||||
|
return ConvertToComplex(Data as ComplexOf<long>[]);
|
||||||
|
}
|
||||||
|
if (Data is ComplexOf<ulong>[])
|
||||||
|
{
|
||||||
|
return ConvertToComplex(Data as ComplexOf<ulong>[]);
|
||||||
|
}
|
||||||
|
return ConvertToDoubleArray().Select(x => new Complex(x, 0.0)).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Complex[] ConvertToComplex<TS>(IEnumerable<ComplexOf<TS>> array)
|
||||||
|
where TS : struct
|
||||||
|
{
|
||||||
|
return array.Select(x => new Complex(Convert.ToDouble(x.Real), Convert.ToDouble(x.Imaginary))).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class HdfCharArray : HdfArray, ICharArray
|
||||||
|
{
|
||||||
|
public HdfCharArray(int[] dimensions, string data)
|
||||||
|
: base(dimensions)
|
||||||
|
{
|
||||||
StringData = data;
|
StringData = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsEmpty => Dimensions.Length == 0;
|
public override double[] ConvertToDoubleArray()
|
||||||
|
|
||||||
public int[] Dimensions { get; }
|
|
||||||
|
|
||||||
public int Count => Dimensions.NumberOfElements();
|
|
||||||
|
|
||||||
public double[] ConvertToDoubleArray()
|
|
||||||
{
|
{
|
||||||
return Data.Select(Convert.ToDouble).ToArray();
|
return Data.Select(Convert.ToDouble).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Complex[] ConvertToComplexArray()
|
public override Complex[] ConvertToComplexArray()
|
||||||
{
|
{
|
||||||
return ConvertToDoubleArray().Select(x => new Complex(x, 0.0)).ToArray();
|
return ConvertToDoubleArray().Select(x => new Complex(x, 0.0)).ToArray();
|
||||||
}
|
}
|
||||||
@ -129,26 +254,158 @@ namespace MatFileHandler
|
|||||||
return dims.Select(x => (int)x).ToArray();
|
return dims.Select(x => (int)x).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ArrayType ArrayTypeFromMatlabClassName(string matlabClassName)
|
||||||
|
{
|
||||||
|
switch (matlabClassName)
|
||||||
|
{
|
||||||
|
case "char":
|
||||||
|
return ArrayType.MxChar;
|
||||||
|
case "int8":
|
||||||
|
return ArrayType.MxInt8;
|
||||||
|
case "uint8":
|
||||||
|
return ArrayType.MxUInt8;
|
||||||
|
case "int16":
|
||||||
|
return ArrayType.MxInt16;
|
||||||
|
case "uint16":
|
||||||
|
return ArrayType.MxUInt16;
|
||||||
|
case "int32":
|
||||||
|
return ArrayType.MxInt32;
|
||||||
|
case "uint32":
|
||||||
|
return ArrayType.MxUInt32;
|
||||||
|
case "int64":
|
||||||
|
return ArrayType.MxInt64;
|
||||||
|
case "uint64":
|
||||||
|
return ArrayType.MxUInt64;
|
||||||
|
case "double":
|
||||||
|
return ArrayType.MxDouble;
|
||||||
|
}
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
private static IArray ReadDataset(long datasetId)
|
private static IArray ReadDataset(long datasetId)
|
||||||
{
|
{
|
||||||
var dims = GetDimensionsOfDataset(datasetId);
|
var dims = GetDimensionsOfDataset(datasetId);
|
||||||
|
|
||||||
var matlabClass = GetMatlabClassOfDataset(datasetId);
|
var matlabClass = GetMatlabClassOfDataset(datasetId);
|
||||||
|
var arrayType = ArrayTypeFromMatlabClassName(matlabClass);
|
||||||
|
|
||||||
if (matlabClass == "char")
|
switch (arrayType)
|
||||||
{
|
{
|
||||||
return ReadCharArray(datasetId, dims);
|
case ArrayType.MxChar:
|
||||||
|
return ReadCharArray(datasetId, dims);
|
||||||
|
case ArrayType.MxInt8:
|
||||||
|
return ReadNumericalArray<sbyte>(datasetId, dims, arrayType);
|
||||||
|
case ArrayType.MxUInt8:
|
||||||
|
return ReadNumericalArray<byte>(datasetId, dims, arrayType);
|
||||||
|
case ArrayType.MxInt16:
|
||||||
|
return ReadNumericalArray<short>(datasetId, dims, arrayType);
|
||||||
|
case ArrayType.MxUInt16:
|
||||||
|
return ReadNumericalArray<ushort>(datasetId, dims, arrayType);
|
||||||
|
case ArrayType.MxInt32:
|
||||||
|
return ReadNumericalArray<int>(datasetId, dims, arrayType);
|
||||||
|
case ArrayType.MxUInt32:
|
||||||
|
return ReadNumericalArray<uint>(datasetId, dims, arrayType);
|
||||||
|
case ArrayType.MxInt64:
|
||||||
|
return ReadNumericalArray<long>(datasetId, dims, arrayType);
|
||||||
|
case ArrayType.MxUInt64:
|
||||||
|
return ReadNumericalArray<ulong>(datasetId, dims, arrayType);
|
||||||
|
case ArrayType.MxSingle:
|
||||||
|
return ReadNumericalArray<float>(datasetId, dims, arrayType);
|
||||||
|
case ArrayType.MxDouble:
|
||||||
|
return ReadNumericalArray<double>(datasetId, dims, arrayType);
|
||||||
|
}
|
||||||
|
throw new NotImplementedException($"Unknown array type: {arrayType}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int SizeOfArrayElement(ArrayType arrayType)
|
||||||
|
{
|
||||||
|
switch (arrayType)
|
||||||
|
{
|
||||||
|
case ArrayType.MxInt8:
|
||||||
|
case ArrayType.MxUInt8:
|
||||||
|
return 1;
|
||||||
|
case ArrayType.MxInt16:
|
||||||
|
case ArrayType.MxUInt16:
|
||||||
|
return 2;
|
||||||
|
case ArrayType.MxInt32:
|
||||||
|
case ArrayType.MxUInt32:
|
||||||
|
case ArrayType.MxSingle:
|
||||||
|
return 4;
|
||||||
|
case ArrayType.MxInt64:
|
||||||
|
case ArrayType.MxUInt64:
|
||||||
|
case ArrayType.MxDouble:
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long H5tTypeFromArrayType(ArrayType arrayType)
|
||||||
|
{
|
||||||
|
switch (arrayType)
|
||||||
|
{
|
||||||
|
case ArrayType.MxInt8:
|
||||||
|
return H5T.NATIVE_INT8;
|
||||||
|
case ArrayType.MxUInt8:
|
||||||
|
return H5T.NATIVE_UINT8;
|
||||||
|
case ArrayType.MxInt16:
|
||||||
|
return H5T.NATIVE_INT16;
|
||||||
|
case ArrayType.MxUInt16:
|
||||||
|
return H5T.NATIVE_UINT16;
|
||||||
|
case ArrayType.MxInt32:
|
||||||
|
return H5T.NATIVE_INT32;
|
||||||
|
case ArrayType.MxUInt32:
|
||||||
|
return H5T.NATIVE_UINT32;
|
||||||
|
case ArrayType.MxInt64:
|
||||||
|
return H5T.NATIVE_INT64;
|
||||||
|
case ArrayType.MxUInt64:
|
||||||
|
return H5T.NATIVE_UINT64;
|
||||||
|
case ArrayType.MxSingle:
|
||||||
|
return H5T.NATIVE_FLOAT;
|
||||||
|
case ArrayType.MxDouble:
|
||||||
|
return H5T.NATIVE_DOUBLE;
|
||||||
}
|
}
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static T[] ConvertDataToProperType<T>(byte[] bytes, ArrayType arrayType)
|
||||||
|
where T : struct
|
||||||
|
{
|
||||||
|
var length = bytes.Length;
|
||||||
|
var arrayElementSize = SizeOfArrayElement(arrayType);
|
||||||
|
var data = new T[length / arrayElementSize];
|
||||||
|
Buffer.BlockCopy(bytes, 0, data, 0, length);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] ReadDataset(long datasetId, long elementType, int dataSize)
|
||||||
|
{
|
||||||
|
var dataBuffer = Marshal.AllocHGlobal(dataSize);
|
||||||
|
H5D.read(datasetId, elementType, H5S.ALL, H5S.ALL, H5P.DEFAULT, dataBuffer);
|
||||||
|
var data = new byte[dataSize];
|
||||||
|
Marshal.Copy(dataBuffer, data, 0, dataSize);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IArray ReadNumericalArray<T>(long datasetId, int[] dims, ArrayType arrayType)
|
||||||
|
where T : struct
|
||||||
|
{
|
||||||
|
var numberOfElements = dims.NumberOfElements();
|
||||||
|
var dataSize = numberOfElements * SizeOfArrayElement(arrayType);
|
||||||
|
var storageSize = (int)H5D.get_storage_size(datasetId);
|
||||||
|
if (dataSize != storageSize)
|
||||||
|
{
|
||||||
|
throw new Exception("Data size mismatch.");
|
||||||
|
}
|
||||||
|
var data = ReadDataset(datasetId, H5tTypeFromArrayType(arrayType), dataSize);
|
||||||
|
var convertedData = ConvertDataToProperType<T>(data, arrayType);
|
||||||
|
return new HdfNumericalArrayOf<T>(dims, convertedData);
|
||||||
|
}
|
||||||
|
|
||||||
private static IArray ReadCharArray(long datasetId, int[] dims)
|
private static IArray ReadCharArray(long datasetId, int[] dims)
|
||||||
{
|
{
|
||||||
var storageSize = (int)H5D.get_storage_size(datasetId);
|
var storageSize = (int)H5D.get_storage_size(datasetId);
|
||||||
var data = new byte[storageSize];
|
var data = ReadDataset(datasetId, H5T.NATIVE_UINT16, storageSize);
|
||||||
var dataBuffer = Marshal.AllocHGlobal(storageSize);
|
|
||||||
H5D.read(datasetId, H5T.NATIVE_UINT16, H5S.ALL, H5S.ALL, H5P.DEFAULT, dataBuffer);
|
|
||||||
Marshal.Copy(dataBuffer, data, 0, storageSize);
|
|
||||||
var str = Encoding.Unicode.GetString(data);
|
var str = Encoding.Unicode.GetString(data);
|
||||||
return new HdfCharArray(dims, str);
|
return new HdfCharArray(dims, str);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
using System;
|
using HDF.PInvoke;
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
|
||||||
using HDF.PInvoke;
|
|
||||||
|
|
||||||
namespace MatFileHandler
|
namespace MatFileHandler
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user