282 lines
8.0 KiB
Python
282 lines
8.0 KiB
Python
''' Constants and classes for matlab 5 read and write
|
|
|
|
See also mio5_utils.pyx where these same constants arise as c enums.
|
|
|
|
If you make changes in this file, don't forget to change mio5_utils.pyx
|
|
'''
|
|
import numpy as np
|
|
|
|
from ._miobase import convert_dtypes
|
|
|
|
|
|
__all__ = [
|
|
'MDTYPES', 'MatlabFunction', 'MatlabObject', 'MatlabOpaque',
|
|
'NP_TO_MTYPES', 'NP_TO_MXTYPES', 'OPAQUE_DTYPE', 'codecs_template',
|
|
'mat_struct', 'mclass_dtypes_template', 'mclass_info', 'mdtypes_template',
|
|
'miCOMPRESSED', 'miDOUBLE', 'miINT16', 'miINT32', 'miINT64', 'miINT8',
|
|
'miMATRIX', 'miSINGLE', 'miUINT16', 'miUINT32', 'miUINT64', 'miUINT8',
|
|
'miUTF16', 'miUTF32', 'miUTF8', 'mxCELL_CLASS', 'mxCHAR_CLASS',
|
|
'mxDOUBLE_CLASS', 'mxFUNCTION_CLASS', 'mxINT16_CLASS', 'mxINT32_CLASS',
|
|
'mxINT64_CLASS', 'mxINT8_CLASS', 'mxOBJECT_CLASS',
|
|
'mxOBJECT_CLASS_FROM_MATRIX_H', 'mxOPAQUE_CLASS', 'mxSINGLE_CLASS',
|
|
'mxSPARSE_CLASS', 'mxSTRUCT_CLASS', 'mxUINT16_CLASS', 'mxUINT32_CLASS',
|
|
'mxUINT64_CLASS', 'mxUINT8_CLASS'
|
|
]
|
|
miINT8 = 1
|
|
miUINT8 = 2
|
|
miINT16 = 3
|
|
miUINT16 = 4
|
|
miINT32 = 5
|
|
miUINT32 = 6
|
|
miSINGLE = 7
|
|
miDOUBLE = 9
|
|
miINT64 = 12
|
|
miUINT64 = 13
|
|
miMATRIX = 14
|
|
miCOMPRESSED = 15
|
|
miUTF8 = 16
|
|
miUTF16 = 17
|
|
miUTF32 = 18
|
|
|
|
mxCELL_CLASS = 1
|
|
mxSTRUCT_CLASS = 2
|
|
# The March 2008 edition of "Matlab 7 MAT-File Format" says that
|
|
# mxOBJECT_CLASS = 3, whereas matrix.h says that mxLOGICAL = 3.
|
|
# Matlab 2008a appears to save logicals as type 9, so we assume that
|
|
# the document is correct. See type 18, below.
|
|
mxOBJECT_CLASS = 3
|
|
mxCHAR_CLASS = 4
|
|
mxSPARSE_CLASS = 5
|
|
mxDOUBLE_CLASS = 6
|
|
mxSINGLE_CLASS = 7
|
|
mxINT8_CLASS = 8
|
|
mxUINT8_CLASS = 9
|
|
mxINT16_CLASS = 10
|
|
mxUINT16_CLASS = 11
|
|
mxINT32_CLASS = 12
|
|
mxUINT32_CLASS = 13
|
|
# The following are not in the March 2008 edition of "Matlab 7
|
|
# MAT-File Format," but were guessed from matrix.h.
|
|
mxINT64_CLASS = 14
|
|
mxUINT64_CLASS = 15
|
|
mxFUNCTION_CLASS = 16
|
|
# Not doing anything with these at the moment.
|
|
mxOPAQUE_CLASS = 17 # This appears to be a function workspace
|
|
# Thread 'saving/loading symbol table of annymous functions',
|
|
# octave-maintainers, April-May 2007
|
|
# https://lists.gnu.org/archive/html/octave-maintainers/2007-04/msg00031.html
|
|
# https://lists.gnu.org/archive/html/octave-maintainers/2007-05/msg00032.html
|
|
# (Was/Deprecated: https://www-old.cae.wisc.edu/pipermail/octave-maintainers/2007-May/002824.html)
|
|
mxOBJECT_CLASS_FROM_MATRIX_H = 18
|
|
|
|
mdtypes_template = {
|
|
miINT8: 'i1',
|
|
miUINT8: 'u1',
|
|
miINT16: 'i2',
|
|
miUINT16: 'u2',
|
|
miINT32: 'i4',
|
|
miUINT32: 'u4',
|
|
miSINGLE: 'f4',
|
|
miDOUBLE: 'f8',
|
|
miINT64: 'i8',
|
|
miUINT64: 'u8',
|
|
miUTF8: 'u1',
|
|
miUTF16: 'u2',
|
|
miUTF32: 'u4',
|
|
'file_header': [('description', 'S116'),
|
|
('subsystem_offset', 'i8'),
|
|
('version', 'u2'),
|
|
('endian_test', 'S2')],
|
|
'tag_full': [('mdtype', 'u4'), ('byte_count', 'u4')],
|
|
'tag_smalldata':[('byte_count_mdtype', 'u4'), ('data', 'S4')],
|
|
'array_flags': [('data_type', 'u4'),
|
|
('byte_count', 'u4'),
|
|
('flags_class','u4'),
|
|
('nzmax', 'u4')],
|
|
'U1': 'U1',
|
|
}
|
|
|
|
mclass_dtypes_template = {
|
|
mxINT8_CLASS: 'i1',
|
|
mxUINT8_CLASS: 'u1',
|
|
mxINT16_CLASS: 'i2',
|
|
mxUINT16_CLASS: 'u2',
|
|
mxINT32_CLASS: 'i4',
|
|
mxUINT32_CLASS: 'u4',
|
|
mxINT64_CLASS: 'i8',
|
|
mxUINT64_CLASS: 'u8',
|
|
mxSINGLE_CLASS: 'f4',
|
|
mxDOUBLE_CLASS: 'f8',
|
|
}
|
|
|
|
mclass_info = {
|
|
mxINT8_CLASS: 'int8',
|
|
mxUINT8_CLASS: 'uint8',
|
|
mxINT16_CLASS: 'int16',
|
|
mxUINT16_CLASS: 'uint16',
|
|
mxINT32_CLASS: 'int32',
|
|
mxUINT32_CLASS: 'uint32',
|
|
mxINT64_CLASS: 'int64',
|
|
mxUINT64_CLASS: 'uint64',
|
|
mxSINGLE_CLASS: 'single',
|
|
mxDOUBLE_CLASS: 'double',
|
|
mxCELL_CLASS: 'cell',
|
|
mxSTRUCT_CLASS: 'struct',
|
|
mxOBJECT_CLASS: 'object',
|
|
mxCHAR_CLASS: 'char',
|
|
mxSPARSE_CLASS: 'sparse',
|
|
mxFUNCTION_CLASS: 'function',
|
|
mxOPAQUE_CLASS: 'opaque',
|
|
}
|
|
|
|
NP_TO_MTYPES = {
|
|
'f8': miDOUBLE,
|
|
'c32': miDOUBLE,
|
|
'c24': miDOUBLE,
|
|
'c16': miDOUBLE,
|
|
'f4': miSINGLE,
|
|
'c8': miSINGLE,
|
|
'i8': miINT64,
|
|
'i4': miINT32,
|
|
'i2': miINT16,
|
|
'i1': miINT8,
|
|
'u8': miUINT64,
|
|
'u4': miUINT32,
|
|
'u2': miUINT16,
|
|
'u1': miUINT8,
|
|
'S1': miUINT8,
|
|
'U1': miUTF16,
|
|
'b1': miUINT8, # not standard but seems MATLAB uses this (gh-4022)
|
|
}
|
|
|
|
|
|
NP_TO_MXTYPES = {
|
|
'f8': mxDOUBLE_CLASS,
|
|
'c32': mxDOUBLE_CLASS,
|
|
'c24': mxDOUBLE_CLASS,
|
|
'c16': mxDOUBLE_CLASS,
|
|
'f4': mxSINGLE_CLASS,
|
|
'c8': mxSINGLE_CLASS,
|
|
'i8': mxINT64_CLASS,
|
|
'i4': mxINT32_CLASS,
|
|
'i2': mxINT16_CLASS,
|
|
'i1': mxINT8_CLASS,
|
|
'u8': mxUINT64_CLASS,
|
|
'u4': mxUINT32_CLASS,
|
|
'u2': mxUINT16_CLASS,
|
|
'u1': mxUINT8_CLASS,
|
|
'S1': mxUINT8_CLASS,
|
|
'b1': mxUINT8_CLASS, # not standard but seems MATLAB uses this
|
|
}
|
|
|
|
''' Before release v7.1 (release 14) matlab (TM) used the system
|
|
default character encoding scheme padded out to 16-bits. Release 14
|
|
and later use Unicode. When saving character data, R14 checks if it
|
|
can be encoded in 7-bit ascii, and saves in that format if so.'''
|
|
|
|
codecs_template = {
|
|
miUTF8: {'codec': 'utf_8', 'width': 1},
|
|
miUTF16: {'codec': 'utf_16', 'width': 2},
|
|
miUTF32: {'codec': 'utf_32','width': 4},
|
|
}
|
|
|
|
|
|
def _convert_codecs(template, byte_order):
|
|
''' Convert codec template mapping to byte order
|
|
|
|
Set codecs not on this system to None
|
|
|
|
Parameters
|
|
----------
|
|
template : mapping
|
|
key, value are respectively codec name, and root name for codec
|
|
(without byte order suffix)
|
|
byte_order : {'<', '>'}
|
|
code for little or big endian
|
|
|
|
Returns
|
|
-------
|
|
codecs : dict
|
|
key, value are name, codec (as in .encode(codec))
|
|
'''
|
|
codecs = {}
|
|
postfix = byte_order == '<' and '_le' or '_be'
|
|
for k, v in template.items():
|
|
codec = v['codec']
|
|
try:
|
|
" ".encode(codec)
|
|
except LookupError:
|
|
codecs[k] = None
|
|
continue
|
|
if v['width'] > 1:
|
|
codec += postfix
|
|
codecs[k] = codec
|
|
return codecs.copy()
|
|
|
|
|
|
MDTYPES = {}
|
|
for _bytecode in '<>':
|
|
_def = {'dtypes': convert_dtypes(mdtypes_template, _bytecode),
|
|
'classes': convert_dtypes(mclass_dtypes_template, _bytecode),
|
|
'codecs': _convert_codecs(codecs_template, _bytecode)}
|
|
MDTYPES[_bytecode] = _def
|
|
|
|
|
|
class mat_struct:
|
|
"""Placeholder for holding read data from structs.
|
|
|
|
We use instances of this class when the user passes False as a value to the
|
|
``struct_as_record`` parameter of the :func:`scipy.io.loadmat` function.
|
|
"""
|
|
pass
|
|
|
|
|
|
class MatlabObject(np.ndarray):
|
|
"""Subclass of ndarray to signal this is a matlab object.
|
|
|
|
This is a simple subclass of :class:`numpy.ndarray` meant to be used
|
|
by :func:`scipy.io.loadmat` and should not be instantiated directly.
|
|
"""
|
|
|
|
def __new__(cls, input_array, classname=None):
|
|
# Input array is an already formed ndarray instance
|
|
# We first cast to be our class type
|
|
obj = np.asarray(input_array).view(cls)
|
|
# add the new attribute to the created instance
|
|
obj.classname = classname
|
|
# Finally, we must return the newly created object:
|
|
return obj
|
|
|
|
def __array_finalize__(self,obj):
|
|
# reset the attribute from passed original object
|
|
self.classname = getattr(obj, 'classname', None)
|
|
# We do not need to return anything
|
|
|
|
|
|
class MatlabFunction(np.ndarray):
|
|
"""Subclass for a MATLAB function.
|
|
|
|
This is a simple subclass of :class:`numpy.ndarray` meant to be used
|
|
by :func:`scipy.io.loadmat` and should not be directly instantiated.
|
|
"""
|
|
|
|
def __new__(cls, input_array):
|
|
obj = np.asarray(input_array).view(cls)
|
|
return obj
|
|
|
|
|
|
class MatlabOpaque(np.ndarray):
|
|
"""Subclass for a MATLAB opaque matrix.
|
|
|
|
This is a simple subclass of :class:`numpy.ndarray` meant to be used
|
|
by :func:`scipy.io.loadmat` and should not be directly instantiated.
|
|
"""
|
|
|
|
def __new__(cls, input_array):
|
|
obj = np.asarray(input_array).view(cls)
|
|
return obj
|
|
|
|
|
|
OPAQUE_DTYPE = np.dtype(
|
|
[('s0', 'O'), ('s1', 'O'), ('s2', 'O'), ('arr', 'O')])
|