Skip to content

Commit 7a39f7c

Browse files
committed
BF+TST - SPM analyze had 111 voxel offset in mats
The .mat files for SPM analyze images were not being interpreted right because the affines therein have 111 voxel origins not 000 as for nifti.
1 parent 582b5ec commit 7a39f7c

File tree

2 files changed

+62
-1
lines changed

2 files changed

+62
-1
lines changed

nibabel/spm99analyze.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,6 @@ def from_file_map(klass, file_map):
261261
'using first')
262262
mat = mat[:, :, 0]
263263
ret._affine = mat
264-
return ret
265264
elif 'M' in mats: # the 'M' matrix does not include flips
266265
hdr = ret._header
267266
if hdr.default_x_flip:
@@ -270,6 +269,10 @@ def from_file_map(klass, file_map):
270269
ret._affine = mats['M']
271270
else:
272271
raise ValueError('mat file found but no "mat" or "M" in it')
272+
# Adjust for matlab 1,1,1 voxel origin
273+
to_111 = np.eye(4)
274+
to_111[:3,3] = 1
275+
ret._affine = np.dot(ret._affine, to_111)
273276
return ret
274277

275278
def to_file_map(self, file_map=None):
@@ -295,6 +298,11 @@ def to_file_map(self, file_map=None):
295298
M = np.dot(np.diag([-1, 1, 1, 1]), mat)
296299
else:
297300
M = mat
301+
# Adjust for matlab 1,1,1 voxel origin
302+
from_111 = np.eye(4)
303+
from_111[:3,3] = -1
304+
M = np.dot(M, from_111)
305+
mat = np.dot(mat, from_111)
298306
# use matlab 4 format to allow gzipped write without error
299307
mfobj = file_map['mat'].get_prepare_fileobj(mode='wb')
300308
sio.savemat(mfobj, {'M': M, 'mat': mat}, format='4')

nibabel/tests/test_spm99analyze.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,59 @@ class TestSpm99AnalyzeImage(test_analyze.TestAnalyzeImage):
105105
test_analyze.TestAnalyzeImage.test_data_hdr_cache
106106
))
107107

108+
@scipy_skip
109+
def test_mat_read(self):
110+
# Test mat file reading and writing for the SPM analyze types
111+
img_klass = self.image_class
112+
arr = np.arange(24).reshape((2,3,4))
113+
aff = np.diag([2,3,4,1]) # no LR flip in affine
114+
img = img_klass(arr, aff)
115+
fm = img.file_map
116+
for key, value in fm.items():
117+
value.fileobj = BytesIO()
118+
# Test round trip
119+
img.to_file_map()
120+
r_img = img_klass.from_file_map(fm)
121+
assert_array_equal(r_img.get_data(), arr)
122+
assert_array_equal(r_img.get_affine(), aff)
123+
# mat files are for matlab and have 111 voxel origins. We need to
124+
# adjust for that, when loading and saving. Check for signs of that in
125+
# the saved mat file
126+
mat_fileobj = img.file_map['mat'].fileobj
127+
from scipy.io import loadmat, savemat
128+
mat_fileobj.seek(0)
129+
mats = loadmat(mat_fileobj)
130+
assert_true('M' in mats and 'mat' in mats)
131+
from_111 = np.eye(4)
132+
from_111[:3,3] = -1
133+
to_111 = np.eye(4)
134+
to_111[:3,3] = 1
135+
assert_array_equal(mats['mat'], np.dot(aff, from_111))
136+
# The M matrix does not include flips, so if we only
137+
# have the M matrix in the mat file, and we have default flipping, the
138+
# mat resulting should have a flip. The 'mat' matrix does include flips
139+
# and so should be unaffected by the flipping. If both are present we
140+
# prefer the the 'mat' matrix.
141+
assert_true(img.get_header().default_x_flip) # check the default
142+
flipper = np.diag([-1,1,1,1])
143+
assert_array_equal(mats['M'], np.dot(aff, np.dot(flipper, from_111)))
144+
mat_fileobj.seek(0)
145+
savemat(mat_fileobj, dict(M=np.diag([3,4,5,1]), mat=np.diag([6,7,8,1])))
146+
# Check we are preferring the 'mat' matrix
147+
r_img = img_klass.from_file_map(fm)
148+
assert_array_equal(r_img.get_data(), arr)
149+
assert_array_equal(r_img.get_affine(),
150+
np.dot(np.diag([6,7,8,1]), to_111))
151+
# But will use M if present
152+
mat_fileobj.seek(0)
153+
mat_fileobj.truncate(0)
154+
savemat(mat_fileobj, dict(M=np.diag([3,4,5,1])))
155+
r_img = img_klass.from_file_map(fm)
156+
assert_array_equal(r_img.get_data(), arr)
157+
assert_array_equal(r_img.get_affine(),
158+
np.dot(np.diag([3,4,5,1]), np.dot(flipper, to_111)))
159+
160+
108161

109162
def test_origin_affine():
110163
hdr = Spm99AnalyzeHeader()

0 commit comments

Comments
 (0)