Skip to content

Commit a47bf1c

Browse files
committed
enh: read ITK's composite transforms with only affines
1 parent 6a5142f commit a47bf1c

File tree

1 file changed

+45
-2
lines changed

1 file changed

+45
-2
lines changed

nitransforms/io/itk.py

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import warnings
33
import numpy as np
44
from scipy.io import loadmat as _read_mat, savemat as _save_mat
5+
from h5py import File as H5File
56
from nibabel import Nifti1Header, Nifti1Image
67
from nibabel.affines import from_matvec
78
from .base import (
@@ -112,6 +113,9 @@ def from_filename(cls, filename):
112113
if str(filename).endswith(".mat"):
113114
with open(str(filename), "rb") as fileobj:
114115
return cls.from_binary(fileobj)
116+
elif str(filename).endswith(".h5"):
117+
with H5File(str(filename)) as f:
118+
return cls.from_h5obj(f)
115119

116120
with open(str(filename)) as fileobj:
117121
return cls.from_string(fileobj.read())
@@ -121,6 +125,10 @@ def from_fileobj(cls, fileobj, check=True):
121125
"""Read the struct from a file object."""
122126
if fileobj.name.endswith(".mat"):
123127
return cls.from_binary(fileobj)
128+
elif fileobj.name.endswith(".h5"):
129+
with H5File(fileobj) as f:
130+
return cls.from_h5obj(f)
131+
124132
return cls.from_string(fileobj.read())
125133

126134
@classmethod
@@ -145,6 +153,11 @@ def from_matlab_dict(cls, mdict, index=0):
145153
sa["offset"] = mdict["fixed"].flatten()
146154
return tf
147155

156+
@classmethod
157+
def from_h5obj(cls, fileobj, check=True):
158+
"""Read the struct from a file object."""
159+
raise NotImplementedError
160+
148161
@classmethod
149162
def from_ras(cls, ras, index=0, moving=None, reference=None):
150163
"""Create an ITK affine from a nitransform's RAS+ matrix."""
@@ -225,6 +238,9 @@ def from_filename(cls, filename):
225238
if str(filename).endswith(".mat"):
226239
with open(str(filename), "rb") as f:
227240
return cls.from_binary(f)
241+
elif str(filename).endswith(".h5"):
242+
with H5File(str(filename)) as f:
243+
return cls.from_h5obj(f)
228244

229245
with open(str(filename)) as f:
230246
string = f.read()
@@ -235,6 +251,10 @@ def from_fileobj(cls, fileobj, check=True):
235251
"""Read the struct from a file object."""
236252
if fileobj.name.endswith(".mat"):
237253
return cls.from_binary(fileobj)
254+
255+
elif fileobj.name.endswith(".h5"):
256+
with H5File(fileobj) as f:
257+
return cls.from_h5obj(f)
238258
return cls.from_string(fileobj.read())
239259

240260
@classmethod
@@ -272,6 +292,31 @@ def from_string(cls, string):
272292
_self.xforms.append(cls._inner_type.from_string("#%s" % xfm))
273293
return _self
274294

295+
@classmethod
296+
def from_h5obj(cls, fileobj, check=True):
297+
"""Read the struct from a file object."""
298+
h5group = fileobj["TransformGroup"]
299+
typo_fallback = "Transform"
300+
try:
301+
h5group["1"][f"{typo_fallback}Parameters"]
302+
except KeyError:
303+
typo_fallback = "Tranform"
304+
305+
_self = cls()
306+
_self.xforms = []
307+
for xfm in list(h5group.values())[1:]:
308+
if xfm["TransformType"][0].startswith(b"AffineTransform"):
309+
_params = np.asanyarray(xfm[f"{typo_fallback}Parameters"])
310+
_self.xforms.append(
311+
ITKLinearTransform(
312+
parameters=from_matvec(
313+
_params[:-3].reshape(3, 3), _params[-3:]
314+
),
315+
offset=np.asanyarray(xfm[f"{typo_fallback}FixedParameters"]),
316+
)
317+
)
318+
return _self
319+
275320

276321
class ITKDisplacementsField(DisplacementsField):
277322
"""A data structure representing displacements fields."""
@@ -304,8 +349,6 @@ class ITKCompositeH5:
304349
@classmethod
305350
def from_filename(cls, filename):
306351
"""Read the struct from a file given its path."""
307-
from h5py import File as H5File
308-
309352
if not str(filename).endswith(".h5"):
310353
raise TransformFileError("Extension is not .h5")
311354

0 commit comments

Comments
 (0)