Skip to content

Commit 54ad1ea

Browse files
authored
Merge pull request #179 from nipy/fix/load-itk-mat
ENH: Load ITK's ``.mat`` files with ``Affine``'s loaders
2 parents 8b31bf2 + 9b8c363 commit 54ad1ea

File tree

4 files changed

+84
-16
lines changed

4 files changed

+84
-16
lines changed

nitransforms/io/fsl.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,7 @@ def to_filename(self, filename):
109109
output_dir = Path(filename).parent
110110
output_dir.mkdir(exist_ok=True, parents=True)
111111
for i, xfm in enumerate(self.xforms):
112-
(output_dir / ".".join((str(filename), "%03d" % i))).write_text(
113-
xfm.to_string()
114-
)
112+
(output_dir / f"{filename}.{i:03d}").write_text(str(xfm))
115113

116114
def to_ras(self, moving=None, reference=None):
117115
"""Return a nitransforms' internal RAS matrix."""

nitransforms/io/itk.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from h5py import File as H5File
66
from nibabel import Nifti1Header, Nifti1Image
77
from nibabel.affines import from_matvec
8-
from .base import (
8+
from nitransforms.io.base import (
99
BaseLinearTransformList,
1010
DisplacementsField,
1111
LinearParameters,
@@ -204,7 +204,14 @@ def from_string(cls, string):
204204
parameters[:3, :3] = vals[:-3].reshape((3, 3))
205205
parameters[:3, 3] = vals[-3:]
206206
sa["parameters"] = parameters
207-
return tf
207+
208+
# Try to double-dip and see if there are more transforms
209+
try:
210+
cls.from_string("\n".join(lines[4:8]))
211+
except TransformFileError:
212+
return tf
213+
else:
214+
raise TransformFileError("More than one linear transform found.")
208215

209216

210217
class ITKLinearTransformArray(BaseLinearTransformList):

nitransforms/linear.py

+29-10
Original file line numberDiff line numberDiff line change
@@ -203,18 +203,35 @@ def from_filename(cls, filename, fmt=None, reference=None, moving=None):
203203
"""Create an affine from a transform file."""
204204
fmtlist = [fmt] if fmt is not None else ("itk", "lta", "afni", "fsl")
205205

206+
if fmt is not None and not Path(filename).exists():
207+
if fmt != "fsl":
208+
raise FileNotFoundError(
209+
f"[Errno 2] No such file or directory: '{filename}'"
210+
)
211+
elif not Path(f"{filename}.000").exists():
212+
raise FileNotFoundError(
213+
f"[Errno 2] No such file or directory: '{filename}[.000]'"
214+
)
215+
216+
is_array = cls != Affine
217+
errors = []
206218
for potential_fmt in fmtlist:
219+
if (potential_fmt == "itk" and Path(filename).suffix == ".mat"):
220+
is_array = False
221+
cls = Affine
222+
207223
try:
208-
struct = get_linear_factory(potential_fmt).from_filename(filename)
209-
matrix = struct.to_ras(reference=reference, moving=moving)
210-
if cls == Affine:
211-
if np.shape(matrix)[0] != 1:
212-
raise TypeError("Cannot load transform array '%s'" % filename)
213-
matrix = matrix[0]
214-
return cls(matrix, reference=reference)
215-
except (TransformFileError, FileNotFoundError):
224+
struct = get_linear_factory(
225+
potential_fmt,
226+
is_array=is_array
227+
).from_filename(filename)
228+
except (TransformFileError, FileNotFoundError) as err:
229+
errors.append((potential_fmt, err))
216230
continue
217231

232+
matrix = struct.to_ras(reference=reference, moving=moving)
233+
return cls(matrix, reference=reference)
234+
218235
raise TransformFileError(
219236
f"Could not open <{filename}> (formats tried: {', '.join(fmtlist)})."
220237
)
@@ -499,6 +516,8 @@ def load(filename, fmt=None, reference=None, moving=None):
499516
xfm = LinearTransformsMapping.from_filename(
500517
filename, fmt=fmt, reference=reference, moving=moving
501518
)
502-
if len(xfm) == 1:
503-
return xfm[0]
519+
520+
if isinstance(xfm, LinearTransformsMapping) and len(xfm) == 1:
521+
xfm = xfm[0]
522+
504523
return xfm

nitransforms/tests/test_linear.py

+45-1
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,22 @@ def test_linear_typeerrors1(matrix):
4444

4545
def test_linear_typeerrors2(data_path):
4646
"""Exercise errors in Affine creation."""
47-
with pytest.raises(TypeError):
47+
with pytest.raises(io.TransformFileError):
4848
nitl.Affine.from_filename(data_path / "itktflist.tfm", fmt="itk")
4949

5050

51+
def test_linear_filenotfound(data_path):
52+
"""Exercise errors in Affine creation."""
53+
with pytest.raises(FileNotFoundError):
54+
nitl.Affine.from_filename("doesnotexist.tfm", fmt="itk")
55+
56+
with pytest.raises(FileNotFoundError):
57+
nitl.LinearTransformsMapping.from_filename("doesnotexist.tfm", fmt="itk")
58+
59+
with pytest.raises(FileNotFoundError):
60+
nitl.LinearTransformsMapping.from_filename("doesnotexist.mat", fmt="fsl")
61+
62+
5163
def test_linear_valueerror():
5264
"""Exercise errors in Affine creation."""
5365
with pytest.raises(ValueError):
@@ -85,6 +97,38 @@ def test_loadsave_itk(tmp_path, data_path, testdata_path):
8597
)
8698

8799

100+
@pytest.mark.parametrize(
101+
"image_orientation",
102+
[
103+
"RAS",
104+
"LAS",
105+
"LPS",
106+
"oblique",
107+
],
108+
)
109+
def test_itkmat_loadsave(tmpdir, data_path, image_orientation):
110+
tmpdir.chdir()
111+
112+
io.itk.ITKLinearTransform.from_filename(
113+
data_path / f"affine-{image_orientation}.itk.tfm"
114+
).to_filename(f"affine-{image_orientation}.itk.mat")
115+
116+
xfm = nitl.load(data_path / f"affine-{image_orientation}.itk.tfm", fmt="itk")
117+
mat1 = nitl.load(f"affine-{image_orientation}.itk.mat", fmt="itk")
118+
119+
assert xfm == mat1
120+
121+
mat2 = nitl.Affine.from_filename(f"affine-{image_orientation}.itk.mat", fmt="itk")
122+
123+
assert xfm == mat2
124+
125+
mat3 = nitl.LinearTransformsMapping.from_filename(
126+
f"affine-{image_orientation}.itk.mat", fmt="itk"
127+
)
128+
129+
assert xfm == mat3
130+
131+
88132
@pytest.mark.parametrize("autofmt", (False, True))
89133
@pytest.mark.parametrize("fmt", ["itk", "fsl", "afni", "lta"])
90134
def test_loadsave(tmp_path, data_path, testdata_path, autofmt, fmt):

0 commit comments

Comments
 (0)