Skip to content

Commit 1a52ca0

Browse files
committed
enh: more tests on io.afni and io.fsl modules
1 parent 62a83aa commit 1a52ca0

File tree

3 files changed

+63
-3
lines changed

3 files changed

+63
-3
lines changed

nitransforms/io/afni.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,13 @@ def from_string(cls, string):
5959
tf = cls()
6060
sa = tf.structarr
6161
lines = [l for l in string.splitlines()
62-
if l.strip()]
62+
if l.strip() and not l.startswith('#')]
6363

6464
if '3dvolreg matrices' in lines[0]:
6565
lines = lines[1:] # Drop header
6666

67+
if not lines:
68+
raise ValueError('String "%s"' % string)
6769
parameters = np.vstack((
6870
np.genfromtxt([lines[0].encode()],
6971
dtype='f8').reshape((3, 4)),
@@ -86,7 +88,11 @@ def to_string(self):
8688
"""Convert to a string directly writeable to file."""
8789
strings = []
8890
for i, xfm in enumerate(self.xforms):
89-
strings.append(xfm.to_string(banner=(i == 0)))
91+
lines = [
92+
l.strip()
93+
for l in xfm.to_string(banner=(i == 0)).splitlines()
94+
if l.strip()]
95+
strings += lines
9096
return '\n'.join(strings)
9197

9298
@classmethod
@@ -103,7 +109,8 @@ def from_string(cls, string):
103109
"""Read the struct from string."""
104110
_self = cls()
105111
_self.xforms = [cls._inner_type.from_string(l.strip())
106-
for l in string.splitlines() if l.strip()]
112+
for l in string.splitlines()
113+
if l.strip() and not l.startswith('#')]
107114
return _self
108115

109116

nitransforms/io/fsl.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Read/write FSL's transforms."""
2+
import os
23
import numpy as np
34
from nibabel.affines import voxel_sizes
45

@@ -95,6 +96,26 @@ def from_string(cls, string):
9596
for l in string.splitlines() if l.strip()]
9697
return _self
9798

99+
@classmethod
100+
def from_filename(cls, filename):
101+
"""Read the struct from a file given its path."""
102+
if os.path.exists(str(filename)):
103+
return super().from_filename(filename)
104+
105+
_xforms = []
106+
index = 0
107+
while os.path.exists('%s.%03d' % (filename, index)):
108+
with open('%s.%03d' % (filename, index)) as f:
109+
string = f.read()
110+
_xforms.append(cls._inner_type.from_string(string))
111+
index += 1
112+
113+
if index == 0:
114+
raise FileNotFoundError(str(filename))
115+
_self = cls()
116+
_self.xforms = _xforms
117+
return _self
118+
98119

99120
def _fsl_aff_adapt(space):
100121
"""

nitransforms/tests/test_io.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,38 @@ def test_Linear_common(tmpdir, data_path, sw, image_orientation,
137137
assert np.allclose(xfm.to_ras(reference=reference, moving=moving), RAS)
138138

139139

140+
@pytest.mark.parametrize('image_orientation', [
141+
'RAS', 'LAS', 'LPS', 'oblique',
142+
])
143+
@pytest.mark.parametrize('sw', ['afni', 'fsl', 'itk'])
144+
def test_LinearList_common(tmpdir, data_path, sw, image_orientation,
145+
get_testdata):
146+
tmpdir.chdir()
147+
148+
angles = np.random.uniform(low=-3.14, high=3.14, size=(5, 3))
149+
translation = np.random.uniform(low=-5., high=5., size=(5, 3))
150+
mats = [from_matvec(euler2mat(*a), t)
151+
for a, t in zip(angles, translation)]
152+
153+
ext = ''
154+
if sw == 'afni':
155+
factory = afni.AFNILinearTransformArray
156+
elif sw == 'fsl':
157+
factory = fsl.FSLLinearTransformArray
158+
elif sw == 'itk':
159+
ext = '.tfm'
160+
factory = itk.ITKLinearTransformArray
161+
162+
tflist1 = factory(mats)
163+
164+
fname = 'affine-%s.%s%s' % (image_orientation, sw, ext)
165+
tflist1.to_filename(fname)
166+
tflist2 = factory.from_filename(fname)
167+
168+
assert tflist1['nxforms'] == tflist2['nxforms']
169+
assert all([np.allclose(x1['parameters'], x2['parameters'])
170+
for x1, x2 in zip(tflist1.xforms, tflist2.xforms)])
171+
140172
def test_ITKLinearTransform(tmpdir, data_path):
141173
tmpdir.chdir()
142174

0 commit comments

Comments
 (0)