|
1 | 1 | """Read/write linear transforms."""
|
| 2 | +import numpy as np |
2 | 3 | from scipy.io.matlab.miobase import get_matfile_version
|
3 | 4 | from scipy.io.matlab.mio4 import MatFile4Reader
|
4 | 5 | from scipy.io.matlab.mio5 import MatFile5Reader
|
@@ -29,12 +30,141 @@ def __array__(self):
|
29 | 30 | return self._structarr
|
30 | 31 |
|
31 | 32 |
|
| 33 | +class LinearParameters(StringBasedStruct): |
| 34 | + """ |
| 35 | + A string-based structure for linear transforms. |
| 36 | +
|
| 37 | + Examples |
| 38 | + -------- |
| 39 | + >>> lp = LinearParameters() |
| 40 | + >>> np.all(lp.structarr['parameters'] == np.eye(4)) |
| 41 | + True |
| 42 | +
|
| 43 | + >>> p = np.diag([2., 2., 2., 1.]) |
| 44 | + >>> lp = LinearParameters(p) |
| 45 | + >>> np.all(lp.structarr['parameters'] == p) |
| 46 | + True |
| 47 | +
|
| 48 | + """ |
| 49 | + |
| 50 | + template_dtype = np.dtype([ |
| 51 | + ('parameters', 'f8', (4, 4)), |
| 52 | + ]) |
| 53 | + dtype = template_dtype |
| 54 | + |
| 55 | + def __init__(self, parameters=None): |
| 56 | + """Initialize with default parameters.""" |
| 57 | + super().__init__() |
| 58 | + self.structarr['parameters'] = np.eye(4) |
| 59 | + if parameters is not None: |
| 60 | + self.structarr['parameters'] = parameters |
| 61 | + |
| 62 | + def to_filename(self, filename): |
| 63 | + """Store this transform to a file with the appropriate format.""" |
| 64 | + with open(str(filename), 'w') as f: |
| 65 | + f.write(self.to_string()) |
| 66 | + |
| 67 | + def to_ras(self, moving=None, reference=None): |
| 68 | + """Return a nitransforms internal RAS+ matrix.""" |
| 69 | + raise NotImplementedError |
| 70 | + |
| 71 | + @classmethod |
| 72 | + def from_filename(cls, filename): |
| 73 | + """Read the struct from a file given its path.""" |
| 74 | + with open(str(filename)) as f: |
| 75 | + string = f.read() |
| 76 | + return cls.from_string(string) |
| 77 | + |
| 78 | + @classmethod |
| 79 | + def from_fileobj(cls, fileobj, check=True): |
| 80 | + """Read the struct from a file object.""" |
| 81 | + return cls.from_string(fileobj.read()) |
| 82 | + |
| 83 | + @classmethod |
| 84 | + def from_string(cls, string): |
| 85 | + """Read the struct from string.""" |
| 86 | + raise NotImplementedError |
| 87 | + |
| 88 | + |
| 89 | +class BaseLinearTransformList(StringBasedStruct): |
| 90 | + """A string-based structure for series of linear transforms.""" |
| 91 | + |
| 92 | + template_dtype = np.dtype([('nxforms', 'i4')]) |
| 93 | + dtype = template_dtype |
| 94 | + _xforms = None |
| 95 | + _inner_type = LinearParameters |
| 96 | + |
| 97 | + def __init__(self, |
| 98 | + xforms=None, |
| 99 | + binaryblock=None, |
| 100 | + endianness=None, |
| 101 | + check=True): |
| 102 | + """Initialize with (optionally) a list of transforms.""" |
| 103 | + super().__init__(binaryblock, endianness, check) |
| 104 | + self.xforms = [self._inner_type(parameters=mat) |
| 105 | + for mat in xforms or []] |
| 106 | + |
| 107 | + @property |
| 108 | + def xforms(self): |
| 109 | + """Get the list of internal transforms.""" |
| 110 | + return self._xforms |
| 111 | + |
| 112 | + @xforms.setter |
| 113 | + def xforms(self, value): |
| 114 | + self._xforms = list(value) |
| 115 | + |
| 116 | + def __getitem__(self, idx): |
| 117 | + """Allow dictionary access to the transforms.""" |
| 118 | + if idx == 'xforms': |
| 119 | + return self._xforms |
| 120 | + if idx == 'nxforms': |
| 121 | + return len(self._xforms) |
| 122 | + raise KeyError(idx) |
| 123 | + |
| 124 | + def to_filename(self, filename): |
| 125 | + """Store this transform to a file with the appropriate format.""" |
| 126 | + with open(str(filename), 'w') as f: |
| 127 | + f.write(self.to_string()) |
| 128 | + |
| 129 | + def to_ras(self, moving=None, reference=None): |
| 130 | + """Return a nitransforms' internal RAS matrix.""" |
| 131 | + raise NotImplementedError |
| 132 | + |
| 133 | + def to_string(self): |
| 134 | + """Convert to a string directly writeable to file.""" |
| 135 | + raise NotImplementedError |
| 136 | + |
| 137 | + @classmethod |
| 138 | + def from_filename(cls, filename): |
| 139 | + """Read the struct from a file given its path.""" |
| 140 | + with open(str(filename)) as f: |
| 141 | + string = f.read() |
| 142 | + return cls.from_string(string) |
| 143 | + |
| 144 | + @classmethod |
| 145 | + def from_fileobj(cls, fileobj, check=True): |
| 146 | + """Read the struct from a file object.""" |
| 147 | + return cls.from_string(fileobj.read()) |
| 148 | + |
| 149 | + @classmethod |
| 150 | + def from_ras(cls, ras, moving=None, reference=None): |
| 151 | + """Create an ITK affine from a nitransform's RAS+ matrix.""" |
| 152 | + raise NotImplementedError |
| 153 | + |
| 154 | + @classmethod |
| 155 | + def from_string(cls, string): |
| 156 | + """Read the struct from string.""" |
| 157 | + raise NotImplementedError |
| 158 | + |
| 159 | + |
32 | 160 | def _read_mat(byte_stream):
|
33 | 161 | mjv, _ = get_matfile_version(byte_stream)
|
34 | 162 | if mjv == 0:
|
35 | 163 | reader = MatFile4Reader(byte_stream)
|
36 | 164 | elif mjv == 1:
|
37 | 165 | reader = MatFile5Reader(byte_stream)
|
38 | 166 | elif mjv == 2:
|
39 |
| - raise TransformFileError('Please use HDF reader for matlab v7.3 files') |
| 167 | + raise TransformFileError('Please use HDF reader for Matlab v7.3 files') |
| 168 | + else: |
| 169 | + raise TransformFileError('Not a Matlab file.') |
40 | 170 | return reader.get_variables()
|
0 commit comments