Skip to content

Commit 1ab91f0

Browse files
committed
Merge pull request #1 from matthew-brett/setting-mgh-affines
Setting mgh affines
2 parents 6724c78 + 13e87c0 commit 1ab91f0

File tree

2 files changed

+70
-14
lines changed

2 files changed

+70
-14
lines changed

nibabel/freesurfer/mghformat.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,9 @@ def get_affine(self):
229229
M[0:3, 3] = pxyz_0.T
230230
return M
231231

232+
# For compatibility with nifti (multiple affines)
233+
get_best_affine = get_affine
234+
232235
def get_vox2ras(self):
233236
'''return the get_affine()
234237
'''
@@ -554,20 +557,25 @@ def update_header(self):
554557
hdr = self._header
555558
if not self._data is None:
556559
hdr.set_data_shape(self._data.shape)
560+
# If the affine is not None, and it is different from the main affine in
561+
# the header, update the heaader
562+
if self._affine is None:
563+
return
564+
if np.allclose(self._affine, hdr.get_best_affine()):
565+
return
566+
# for more information, go through save_mgh.m in FreeSurfer dist
567+
MdcD = self._affine[:3, :3]
568+
delta = np.sqrt(np.sum(MdcD * MdcD, axis=0))
569+
Mdc = MdcD / np.tile(delta, (3, 1))
570+
Pcrs_c = np.array([0, 0, 0, 1], dtype=np.float)
571+
Pcrs_c[:3] = np.array([self._data.shape[0], self._data.shape[1],
572+
self._data.shape[2]], dtype=np.float) / 2.0
573+
Pxyz_c = np.dot(self._affine, Pcrs_c)
574+
575+
hdr['delta'][:] = delta
576+
hdr['Mdc'][:, :] = Mdc.T
577+
hdr['Pxyz_c'][:] = Pxyz_c[:3]
557578

558-
if not self._affine is None:
559-
# for more information, go through save_mgh.m in FreeSurfer dist
560-
MdcD = self._affine[:3, :3]
561-
delta = np.sqrt(np.sum(MdcD * MdcD, axis=0))
562-
Mdc = MdcD / np.tile(delta, (3, 1))
563-
Pcrs_c = np.array([0, 0, 0, 1], dtype=np.float)
564-
Pcrs_c[:3] = np.array([self._data.shape[0], self._data.shape[1],
565-
self._data.shape[2]], dtype=np.float) / 2.0
566-
Pxyz_c = np.dot(self._affine, Pcrs_c)
567-
568-
hdr['delta'][:] = delta
569-
hdr['Mdc'][:, :] = Mdc.T
570-
hdr['Pxyz_c'][:] = Pxyz_c[:3]
571579

572580
load = MGHImage.load
573581
save = MGHImage.instance_to_filename

nibabel/freesurfer/tests/test_mghformat.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,17 @@
88
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
99
'''Tests for mghformat reading writing'''
1010
from __future__ import with_statement
11+
1112
import os
13+
from io import StringIO, BytesIO
14+
1215
import numpy as np
1316
from .. import load, save, MGHImage
1417
from ..mghformat import MGHError
1518
from ...tmpdirs import InTemporaryDirectory
19+
from ...py3k import unicode
20+
from ...fileholders import FileHolder
21+
1622
from ...testing import data_path
1723
from numpy.testing import assert_equal, assert_array_equal, \
1824
assert_array_almost_equal, assert_almost_equal, assert_raises
@@ -47,7 +53,7 @@ def test_read_mgh():
4753
assert_equal(h['goodRASFlag'], 1)
4854
assert_array_equal(h['dims'], [3, 4, 5, 2])
4955
assert_array_almost_equal(h['mrparms'], [2.0, 0.0, 0.0, 0.0])
50-
assert_array_almost_equal(h.get_zooms(), zooms)
56+
assert_array_almost_equal(h.get_zooms(), 1)
5157
assert_array_almost_equal(h.get_vox2ras(), v2r)
5258
assert_array_almost_equal(h.get_vox2ras_tkr(), v2rtkr)
5359

@@ -150,3 +156,45 @@ def test_filename_exts():
150156
img_back = load(fname)
151157
assert_array_equal(img_back.get_data(), v)
152158
del img_back
159+
160+
161+
def _mgh_rt(img, fobj):
162+
file_map = {'image': FileHolder(fileobj=fobj)}
163+
img.to_file_map(file_map)
164+
return MGHImage.from_file_map(file_map)
165+
166+
167+
def test_header_updating():
168+
# Don't update the header information if the affine doesn't change.
169+
# Luckily the test.mgz dataset had a bad set of cosine vectors, so these
170+
# will be changed if the affine gets updated
171+
mgz_path = os.path.join(data_path, 'test.mgz')
172+
mgz = load(mgz_path)
173+
hdr = mgz.get_header()
174+
# Test against mri_info output
175+
exp_aff = np.loadtxt(StringIO(unicode("""
176+
1.0000 2.0000 3.0000 -13.0000
177+
2.0000 3.0000 1.0000 -11.5000
178+
3.0000 1.0000 2.0000 -11.5000
179+
0.0000 0.0000 0.0000 1.0000""")))
180+
assert_almost_equal(mgz.get_affine(), exp_aff, 6)
181+
assert_almost_equal(hdr.get_affine(), exp_aff, 6)
182+
# Test that initial wonky header elements have not changed
183+
assert_equal(hdr['delta'], 1)
184+
assert_almost_equal(hdr['Mdc'], exp_aff[:3, :3].T)
185+
# Save, reload, same thing
186+
img_fobj = BytesIO()
187+
mgz2 = _mgh_rt(mgz, img_fobj)
188+
hdr2 = mgz2.get_header()
189+
assert_almost_equal(hdr2.get_affine(), exp_aff, 6)
190+
assert_equal(hdr2['delta'], 1)
191+
# Change affine, change underlying header info
192+
exp_aff_d = exp_aff.copy()
193+
exp_aff_d[0, -1] = -14
194+
# This will (probably) become part of the official API
195+
mgz2._affine[:] = exp_aff_d
196+
mgz2.update_header()
197+
assert_almost_equal(hdr2.get_affine(), exp_aff_d, 6)
198+
RZS = exp_aff_d[:3, :3]
199+
assert_almost_equal(hdr2['delta'], np.sqrt(np.sum(RZS ** 2, axis=0)))
200+
assert_almost_equal(hdr2['Mdc'], (RZS / hdr2['delta']).T)

0 commit comments

Comments
 (0)