diff options
Diffstat (limited to 'LibOVR/Src/Util/Util_MatFile.cpp')
-rw-r--r-- | LibOVR/Src/Util/Util_MatFile.cpp | 496 |
1 files changed, 496 insertions, 0 deletions
diff --git a/LibOVR/Src/Util/Util_MatFile.cpp b/LibOVR/Src/Util/Util_MatFile.cpp new file mode 100644 index 0000000..f17e269 --- /dev/null +++ b/LibOVR/Src/Util/Util_MatFile.cpp @@ -0,0 +1,496 @@ +/************************************************************************************ + +Filename : Util_MatFile.cpp +Content : Matlab .MAT file access functions +Created : June 1, 2014 +Authors : Neil Konzen + +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.2 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#include "Util_MatFile.h" + +#include "Kernel/OVR_Types.h" +#include "Kernel/OVR_Alg.h" +#include "Kernel/OVR_Std.h" + +OVR_DISABLE_MSVC_WARNING(4996) // 'fopen': This function or variable may be unsafe. Consider using fopen_s inst + + +namespace OVR { namespace Util { + +using namespace OVR::Alg; + + +// Data structures relating to MATLAB .MAT binary files +#define FX_FORM_IEEE_LE 0000u +#define FX_FORM_IEEE_BE 1000u +#define FX_FORM_VAX_D_FLOAT 2000u +#define FX_FORM_VAX_G_FLOAT 3000u +#define FX_FORM_CRAY 4000u +#define FX_FORM(type) ((((type) / 1000u) % 10u) * 1000u) + +#define FX_PREC_UINT8 50u +#define FX_PREC_INTU 40u +#define FX_PREC_INTS 30u +#define FX_PREC_LONG 20u +#define FX_PREC_SINGLE 10u +#define FX_PREC_DOUBLE 00u +#define FX_PREC(type) ((((type) / 10u) % 10u) * 10u) + +// Note that the elements of a text matrix are stored as floating-point numbers +// between 0 and 255 representing ASCII-encoded characters. +#define FX_MAT_NUMERIC 0u +#define FX_MAT_TEXT 1u +#define FX_MAT_SPARSE 2u +#define FX_MAT(type) ((type) % 10u) + +struct Fmatrix +{ + uint32_t type; // Type - see #defines + uint32_t mrows; // Row dimension - NOTE: Column dimension for C Arrays! + uint32_t ncols; // Column dimension - NOTE: Row dimension for C Arrays! + uint32_t imagf; // 1=complex, 0=real + uint32_t namelen; // length including zero terminator +}; + + +uint32_t MatFile::GetMatlabType(ValueType type, size_t& valueSize) +{ + switch (type) + { + case ByteValue: valueSize = sizeof(uint8_t); return FX_PREC_UINT8; + case UInt16Value: valueSize = sizeof(uint16_t); return FX_PREC_INTU; + case Int16Value: valueSize = sizeof(int16_t); return FX_PREC_INTS; + case UInt32Value: valueSize = sizeof(uint32_t); return FX_PREC_LONG; // Not directly supported by matlab! + case Int32Value: valueSize = sizeof(int32_t); return FX_PREC_LONG; + case FloatValue: valueSize = sizeof(float); return FX_PREC_SINGLE; + case DoubleValue: valueSize = sizeof(double); return FX_PREC_DOUBLE; + case StringValue: valueSize = sizeof(char); return FX_MAT_TEXT; // special case for string arrays + default: + OVR_ASSERT(false); + valueSize = 0; + return 0; + } +} + +MatFile::ValueType MatFile::GetValueType(uint32_t matlabType, size_t& valueSize) +{ + switch (matlabType) + { + case FX_PREC_UINT8: valueSize = sizeof(uint8_t); return ByteValue; + case FX_PREC_INTU: valueSize = sizeof(uint16_t); return UInt16Value; + case FX_PREC_INTS: valueSize = sizeof(int16_t); return Int16Value; + case FX_PREC_LONG: valueSize = sizeof(int32_t); return Int32Value; + case FX_PREC_SINGLE: valueSize = sizeof(float); return FloatValue; + case FX_PREC_DOUBLE: valueSize = sizeof(double); return DoubleValue; + case FX_MAT_TEXT: valueSize = sizeof(char); return StringValue; + default: + OVR_ASSERT(false); + valueSize = 0; + return UnknownValue; + } +} + +MatFile::MatFile(void) +{ + m_f = NULL; +} + +MatFile::~MatFile(void) +{ + if (m_f) + fclose(m_f); + m_f = NULL; +} + +// Matlab arrays are stored column-major, while C/C++ arrays are stored row-major. +// This means that a C array appears to Matlab transposed, and vice versa. +// To deal with this we swap the row and column values stored in the Matlab matrix header. + +bool MatFile::Open(const char* pszFile, bool write) +{ + OVR_ASSERT(!m_f); + m_f = fopen(pszFile, write ? "wb" : "rb"); + return (m_f != nullptr); +} + +void MatFile::Close() +{ + if (m_f) + { + fclose(m_f); + m_f = NULL; + } +} + +int MatFile::ReadString(const char* name, char* text, size_t maxTextSize) +{ + int rows, cols; + ValueType valueType; + + maxTextSize = Alg::Min(maxTextSize, INT_MAX/sizeof(double)/2); + + if (!GetMatrixInfo(name, valueType, rows, cols)) + return 0; + + if (valueType != StringValue) + return 0; + + int count = rows * cols; // character count, not including zero terminator + + double* doubles = new double[count]; + ReadMatrixValues(doubles, StringValue, count, 1); + + if (maxTextSize > 0 && count > 0) + { + count = (int)Alg::Min(count, (int)(maxTextSize-1)); + for (int i = 0; i < count; i++) + text[i] = (char)doubles[i]; + text[count] = 0; // Always zero terminate + } + + delete[] doubles; + + return count; +} + +bool MatFile::WriteString(const char* name, const char* string) +{ + int length = (int)Alg::Min(strlen(string), INT_MAX/sizeof(double)/2); + + double* doubles = new double[length]; + for (int i = 0; i < length; i++) + doubles[i] = (double)((unsigned char)string[i]); + + bool ok = WriteMatrix(name, doubles, StringValue, (int)length, 1); + + delete[] doubles; + + return ok; +} + +void* MatFile::ReadMatrix(const char* name, ValueType valueType, int& rows, int& cols) +{ + ValueType fileValueType; + if (!GetMatrixInfo(name, fileValueType, rows, cols)) + return NULL; + + int valueCount = rows * cols; + + void* values = NULL; + switch (fileValueType) + { + case StringValue: // Text matrices are stored as doubles + case DoubleValue: + values = new double[valueCount]; + break; + case FloatValue: + values = new float[valueCount]; + break; + case ByteValue: + values = new uint8_t[valueCount]; + break; + case Int16Value: + values = new int16_t[valueCount]; + break; + case UInt16Value: + values = new uint16_t[valueCount]; + break; + case Int32Value: + /*case UInt32Value: -- not directly supported by matlab -v4 files */ + values = new int32_t[valueCount]; + break; + default: + OVR_ASSERT(false); + return NULL; + } + + bool ok = ReadMatrixValues(values, fileValueType, rows, cols); + + if (ok) + values = ConvertVector(values, valueCount, fileValueType, valueType); + + if (!ok) + { + delete[] (char*)values; + values = NULL; + } + + OVR_ASSERT(values); + return values; +} + +void* MatFile::ConvertVector(void* fromValues, int valueCount, ValueType fromType, ValueType toType) +{ + // Special case: Always convert characters stored as doubles to a char array + if (fromType == StringValue) + fromType = DoubleValue; + + if (fromType == toType) + return fromValues; + + // UInt32 values are stored as Int32 values by Matlab + if (fromType == Int32Value && toType == UInt32Value) + return fromValues; + + // When a .mat file is saved by Matlab, many datatypes are converted to double. + // We support conversion of doubles to some other types: float, long, byte, char + // and strings and floats to doubles. + // convert singles to doubles + bool ok = true; + if (fromType == DoubleValue) + { + const double* fromDoubles = (const double*)fromValues; + if (toType == FloatValue) + { + float* newValues = new float[valueCount]; + for (int i = 0; i < valueCount; i++) + newValues[i] = (float)fromDoubles[i]; + delete[] (char*)fromValues; + fromValues = newValues; + } + else if (toType == Int32Value) + { + int32_t* newValues = new int32_t[valueCount]; + for (int i = 0; i < valueCount; i++) + newValues[i] = (int32_t)fromDoubles[i]; + delete[] (char*)fromValues; + fromValues = newValues; + } + else if (toType == UInt32Value) + { + uint32_t* newValues = new uint32_t[valueCount]; + for (int i = 0; i < valueCount; i++) + newValues[i] = (uint32_t)fromDoubles[i]; + delete[] (char*)fromValues; + fromValues = newValues; + } + else if (toType == Int16Value) + { + int16_t* newValues = new int16_t[valueCount]; + for (int i = 0; i < valueCount; i++) + newValues[i] = (int16_t)fromDoubles[i]; + delete[] (char*)fromValues; + fromValues = newValues; + } + else if (toType == UInt16Value) + { + uint16_t* newValues = new uint16_t[valueCount]; + for (int i = 0; i < valueCount; i++) + newValues[i] = (uint16_t)fromDoubles[i]; + delete[] (char*)fromValues; + fromValues = newValues; + } + else if (toType == ByteValue) + { + uint8_t* newValues = new uint8_t[valueCount]; + for (int i = 0; i < valueCount; i++) + newValues[i] = (uint8_t)fromDoubles[i]; + delete[] (char*)fromValues; + fromValues = newValues; + } + else if (toType == StringValue) + { + char* newValues = new char[valueCount]; + for (int i = 0; i < valueCount; i++) + newValues[i] = (char)fromDoubles[i]; + delete[] (char*)fromValues; + fromValues = newValues; + } + else + { + // unsupported type conversion + ok = false; + } + } + else + { + ok = false; // only conversions from doubles supported + } + + if (!ok) + { + OVR_ASSERT(false); + delete[] (char*)fromValues; + fromValues = NULL; + } + + return fromValues; +} + +bool MatFile::GetMatrixInfo(const char* name, ValueType& valueType, int& rows, int& cols) +{ + OVR_ASSERT(m_f); + fseek(m_f, 0, SEEK_SET); // rewind to start of file + + static const int maxVarNameLen = 255; + char varName[maxVarNameLen+1]; + + while (ReadMatrixInfo(varName, maxVarNameLen, valueType, rows, cols)) + { + if (OVR_stricmp(name, varName) == 0) + return true; + // skip over data to next one + ReadMatrixValues(NULL, valueType, rows, cols); + } + + return false; +} + +bool MatFile::ReadMatrixInfo(char name[], size_t maxNameSize, ValueType& valueType, int& rows, int& cols) +{ + if (name && maxNameSize > 0) + name[0] = 0; + + valueType = UnknownValue; + rows = 0; + cols = 0; + + OVR_ASSERT(m_f); + if (!m_f) + return false; + + Fmatrix header; + if (fread(&header, sizeof(header), 1, m_f) != 1) + return false; + + // Read transpose of row and column values stored in the file + cols = header.mrows; + rows = header.ncols; + + if (FX_FORM(header.type) != FX_FORM_IEEE_LE) + { + OVR_ASSERT(false); + return false; + } + + // Imaginary not supported + if (header.imagf != 0) + { + OVR_ASSERT(false); + return false; + } + + // sparse matrices not supported + if (FX_MAT(header.type) == FX_MAT_SPARSE) + { + OVR_ASSERT(false); + return false; + } + + // Special case for strings as text matrixes: they are stored as doubles(!) + if (FX_MAT(header.type) == FX_MAT_TEXT) + { + valueType = StringValue; + } + else + { + // only numeric types supported + if (FX_MAT(header.type) != FX_MAT_NUMERIC) + { + OVR_ASSERT(false); + return false; + } + size_t valueSize; + valueType = GetValueType(FX_PREC(header.type), valueSize); + } + + // Read in name + OVR_ASSERT(maxNameSize >= header.namelen); + if (maxNameSize < header.namelen) + return false; + + if (fread(name, sizeof(char), header.namelen, m_f) != header.namelen) + return false; + + return true; +} + +bool MatFile::ReadMatrixValues(void* values, ValueType valueType, int rows, int cols) +{ + OVR_ASSERT(m_f); + if (!m_f) + return false; + + OVR_ASSERT(rows*cols > 0); + + size_t valueCount = (size_t)(rows * cols); + size_t valueSize = 0; + GetMatlabType(valueType, valueSize); + if (valueSize == 0) + return false; + + // If no values pointer specified, skip over data without reading + if (!values) + { + if (fseek(m_f, (long)(valueSize * valueCount), SEEK_CUR) != 0) + return false; + } + else + { + if (fread(values, valueSize, valueCount, m_f) != valueCount) + return false; + } + + return true; +} + +bool MatFile::WriteMatrix(const char* name, const void* values, ValueType valueType, int rows, int cols) +{ + if (!m_f) + return false; + + OVR_ASSERT(rows*cols > 0); + + size_t valueCount = (size_t)(rows * cols); + size_t valueSize = 0; + uint32_t matlabType = GetMatlabType(valueType, valueSize); + if (valueSize == 0) + return false; + + Fmatrix header; + if (valueType == StringValue) + { + header.type = (FX_FORM_IEEE_LE + FX_MAT_TEXT); + } + else + { + header.type = (FX_FORM_IEEE_LE + FX_MAT_NUMERIC) + matlabType; + } + + // NOTE: We store transposed dimensions! + header.mrows = cols; + header.ncols = rows; + header.imagf = 0; + header.namelen = (uint32_t)(strlen(name) + 1); + OVR_ASSERT(header.namelen > 1); + + if (fwrite(&header, sizeof(header), 1, m_f) != 1) + return false; + if (fwrite(name, sizeof(char), header.namelen, m_f) != header.namelen) + return false; + if (fwrite(values, valueSize, valueCount, m_f) != valueCount) + return false; + + return true; +} + + +}} // namespace OVR::Util |