Skip to content

Commit 6d253a2

Browse files
committed
Merge pull request #349 from cindeem/nonbatch-spm-coreg
Nonbatch spm coreg
2 parents bc203b9 + 10bf980 commit 6d253a2

File tree

3 files changed

+329
-8
lines changed

3 files changed

+329
-8
lines changed

doc/devel/matlab_interface_devel.rst

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ How to wrap a MATLAB script
77
This is minimal script for wrapping MATLAB code. You should replace the MATLAB
88
code template, and define approriate inputs and outputs.
99

10+
11+
Example 1
12+
+++++++++
13+
1014
.. testcode::
1115

1216
from nipype.interfaces.matlab import MatlabCommand
@@ -35,17 +39,95 @@ code template, and define approriate inputs and outputs.
3539
exit;
3640
""").substitute(d)
3741

38-
# mfile = True will create an .m file with your script and executed. Alternatively
39-
# mfile can be set to False which will cause the matlab code to be passed
40-
# as a commandline argument to the matlab executable (without creating any files).
41-
# This, however, is less reliable and harder to debug (code will be reduced to
42+
# mfile = True will create an .m file with your script and executed.
43+
# Alternatively
44+
# mfile can be set to False which will cause the matlab code to be
45+
# passed
46+
# as a commandline argument to the matlab executable
47+
# (without creating any files).
48+
# This, however, is less reliable and harder to debug
49+
# (code will be reduced to
4250
# a single line and stripped of any comments).
51+
4352
mlab = MatlabCommand(script=script, mfile=True)
44-
result = mlab.run()
53+
result = mlab.run()
4554
return result.runtime
4655

4756
def _list_outputs(self):
4857
outputs = self._outputs().get()
4958
outputs['out_file'] = os.path.abspath(self.inputs.out_file)
5059
return outputs
5160

61+
62+
Example 2
63+
+++++++++
64+
65+
By subclassing **MatlabCommand** for your main class, and **MatlabInputSpec** for your input and output spec, you gain access to some useful MATLAB hooks
66+
67+
.. testcode::
68+
69+
import os
70+
from nipype.interfaces.base import File, traits
71+
from nipype.interfaces.matlab import MatlabCommand, MatlabInputSpec
72+
73+
74+
class HelloWorldInputSpec( MatlabInputSpec):
75+
name = traits.Str( mandatory = True,
76+
desc = 'Name of person to say hello to')
77+
78+
class HelloWorldOutputSpec( MatlabInputSpec):
79+
matlab_output = traits.Str( )
80+
81+
class HelloWorld( MatlabCommand):
82+
""" Basic Hello World that displays Hello <name> in MATLAB
83+
84+
Returns
85+
-------
86+
87+
matlab_output : capture of matlab output which may be
88+
parsed by user to get computation results
89+
90+
Examples
91+
--------
92+
93+
>>> hello = HelloWorld(matlab_cmd = 'mymatlab')
94+
>>> hello.inputs.name = 'Monty'
95+
>>> hello.inputs.mfile = True #creates mfile
96+
>>> hello.inputs.paths = '/path/to/matlab/toolbox'
97+
>>> hello.inputs.script_file = 'helloworld_pyscript.m'
98+
>>> out = hello.run()
99+
>>> out.outputs['matlab_output']
100+
"""
101+
input_spec = HelloWorldInputSpec
102+
output_spec = HelloWorldOutputSpec
103+
104+
def _my_script(self):
105+
"""This is where you implement your script"""
106+
script = """
107+
disp('Hello %s Python')
108+
two = 1 + 1
109+
"""%(self.inputs.name)
110+
return script
111+
112+
113+
def run(self, **inputs):
114+
## inject your script
115+
self.inputs.script = self._my_script()
116+
results = super(MatlabCommand, self).run( **inputs)
117+
stdout = results.runtime.stdout
118+
# attach stdout to outputs to access matlab results
119+
results.outputs.matlab_output = stdout
120+
return results
121+
122+
123+
def _list_outputs(self):
124+
outputs = self._outputs().get()
125+
return outputs
126+
127+
128+
129+
130+
131+
132+
133+
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
2+
# vi: set ft=python sts=4 ts=4 sw=4 et:
3+
import os
4+
from nipype.testing import (assert_equal, assert_false,assert_raises,
5+
assert_true, skipif, example_data)
6+
from nipype.interfaces.spm import no_spm
7+
import nipype.interfaces.spm.utils as spmu
8+
from nipype.interfaces.base import isdefined
9+
from nipype.utils.filemanip import split_filename, fname_presuffix
10+
from nipype.interfaces.base import TraitError
11+
12+
def test_coreg():
13+
moving = example_data(infile = 'functional.nii')
14+
target = example_data(infile = 'T1.nii')
15+
mat = example_data(infile = 'trans.mat')
16+
coreg = spmu.CalcCoregAffine(matlab_cmd = 'mymatlab')
17+
coreg.inputs.target = target
18+
assert_equal(coreg.inputs.matlab_cmd, 'mymatlab')
19+
coreg.inputs.moving = moving
20+
assert_equal( isdefined(coreg.inputs.mat),False)
21+
pth, mov, _ = split_filename(moving)
22+
_, tgt, _ = split_filename(target)
23+
mat = os.path.join(pth, '%s_to_%s.mat'%(mov,tgt))
24+
invmat = fname_presuffix(mat, prefix = 'inverse_')
25+
scrpt = coreg._make_matlab_command(None)
26+
assert_equal(coreg.inputs.mat, mat)
27+
assert_equal( coreg.inputs.invmat, invmat)
28+
29+
30+
def test_apply_transform():
31+
moving = example_data(infile = 'functional.nii')
32+
mat = example_data(infile = 'trans.mat')
33+
applymat = spmu.ApplyTransform(matlab_cmd = 'mymatlab')
34+
assert_equal( applymat.inputs.matlab_cmd, 'mymatlab' )
35+
applymat.inputs.in_file = moving
36+
applymat.inputs.mat = mat
37+
scrpt = applymat._make_matlab_command(None)
38+
expected = 'img_space = spm_get_space(infile);'
39+
assert_equal( expected in scrpt, True)
40+
expected = 'spm_get_space(infile, transform.M * img_space);'
41+
assert_equal(expected in scrpt, True)
42+
43+
def test_reslice():
44+
moving = example_data(infile = 'functional.nii')
45+
space_defining = example_data(infile = 'T1.nii')
46+
reslice = spmu.Reslice(matlab_cmd = 'mymatlab_version')
47+
assert_equal( reslice.inputs.matlab_cmd, 'mymatlab_version')
48+
reslice.inputs.in_file = moving
49+
reslice.inputs.space_defining = space_defining
50+
assert_equal( reslice.inputs.interp, 0)
51+
assert_raises(TraitError,reslice.inputs.trait_set,interp = 'nearest')
52+
assert_raises(TraitError, reslice.inputs.trait_set, interp = 10)
53+
reslice.inputs.interp = 1
54+
script = reslice._make_matlab_command(None)
55+
outfile = fname_presuffix(moving, prefix='r')
56+
assert_equal(reslice.inputs.out_file, outfile)
57+
expected = '\nflags.mean=0;\nflags.which=1;\nflags.mask=0;'
58+
assert_equal(expected in script.replace(' ',''), True)
59+
expected_interp = 'flags.interp = 1;\n'
60+
assert_equal(expected_interp in script, True)
61+
assert_equal('spm_reslice(invols, flags);' in script, True)

nipype/interfaces/spm/utils.py

Lines changed: 181 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
from nipype.interfaces.spm.base import SPMCommandInputSpec, SPMCommand
2-
from nipype.interfaces.base import File
3-
from nipype.utils.filemanip import split_filename
1+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
2+
# vi: set ft=python sts=4 ts=4 sw=4 et:
3+
from nipype.interfaces.spm.base import SPMCommandInputSpec, SPMCommand, Info
4+
from nipype.interfaces.matlab import MatlabCommand
5+
from nipype.interfaces.base import (TraitedSpec, BaseInterface,
6+
BaseInterfaceInputSpec, isdefined)
7+
from nipype.interfaces.base import File, traits
8+
from nipype.utils.filemanip import split_filename, fname_presuffix
49
import os
510

611
class Analyze2niiInputSpec(SPMCommandInputSpec):
@@ -28,3 +33,176 @@ def _list_outputs(self):
2833
outputs = self._outputs().get()
2934
outputs['nifti_file'] = self.output_name
3035
return outputs
36+
37+
class CalcCoregAffineInputSpec(SPMCommandInputSpec):
38+
target = File( exists = True, mandatory = True,
39+
desc = 'target for generating affine transform')
40+
moving = File( exists = True, mandatory = True,
41+
desc = 'volume transform can be applied to register with target')
42+
mat = File( desc = 'Filename used to store affine matrix')
43+
invmat = File( desc = 'Filename used to store inverse affine matrix')
44+
45+
46+
class CalcCoregAffineOutputSpec(SPMCommandInputSpec):
47+
mat = File(exists = True, desc = 'Matlab file holding transform')
48+
invmat = File( desc = 'Matlab file holding inverse transform')
49+
50+
51+
class CalcCoregAffine(SPMCommand):
52+
""" Uses SPM (spm_coreg) to calculate the transform mapping
53+
moving to target. Saves Transform in mat (matlab binary file)
54+
Also saves inverse transform
55+
56+
Examples
57+
--------
58+
59+
>>> import nipype.interfaces.spm.utils as spmu
60+
>>> coreg = spmu.CalcCoregAffine(matlab_cmd='matlab-spm8')
61+
>>> coreg.inputs.target = 'structural.nii'
62+
>>> coreg.inputs.moving = 'functional.nii'
63+
>>> coreg.inputs.mat = 'func_to_struct.mat'
64+
>>> coreg.run() # doctest: +SKIP
65+
66+
Notes
67+
-----
68+
69+
* the output file mat is saves as a matlab binary file
70+
* calculating the transforms does NOT change either input image
71+
it does not **move** the moving image, only calculates the transform
72+
that can be used to move it
73+
"""
74+
75+
input_spec = CalcCoregAffineInputSpec
76+
output_spec = CalcCoregAffineOutputSpec
77+
78+
def _make_inv_file(self):
79+
""" makes filename to hold inverse transform if not specified"""
80+
invmat = fname_presuffix(self.inputs.mat, prefix = 'inverse_')
81+
return invmat
82+
83+
def _make_mat_file(self):
84+
""" makes name for matfile if doesn exist"""
85+
pth, mv, _ = split_filename(self.inputs.moving)
86+
_, tgt, _ = split_filename(self.inputs.target)
87+
mat = os.path.join(pth, '%s_to_%s.mat'%(mv,tgt))
88+
return mat
89+
90+
def _make_matlab_command(self, _):
91+
"""checks for SPM, generates script"""
92+
if not isdefined(self.inputs.mat):
93+
self.inputs.mat = self._make_mat_file()
94+
if not isdefined(self.inputs.invmat):
95+
self.inputs.invmat = self._make_inv_file()
96+
script = """
97+
target = '%s';
98+
moving = '%s';
99+
targetv = spm_vol(target);
100+
movingv = spm_vol(moving);
101+
x = spm_coreg(movingv, targetv);
102+
M = spm_matrix(x(:)');
103+
save('%s' , 'M' );
104+
M = inv(spm_matrix(x(:)'));
105+
save('%s','M')
106+
"""%(self.inputs.target,
107+
self.inputs.moving,
108+
self.inputs.mat,
109+
self.inputs.invmat)
110+
return script
111+
112+
def _list_outputs(self):
113+
outputs = self._outputs().get()
114+
outputs['mat'] = os.path.abspath(self.inputs.mat)
115+
outputs['invmat'] = os.path.abspath(self.inputs.invmat)
116+
return outputs
117+
118+
class ApplyTransformInputSpec(SPMCommandInputSpec):
119+
in_file = File( exists = True, mandatory = True,
120+
desc='file to apply transform to, (only updates header)')
121+
mat = File( exists = True, mandatory = True,
122+
desc='file holding transform to apply')
123+
124+
125+
class ApplyTransformOutputSpec(SPMCommandInputSpec):
126+
out_file = File(exists = True, desc = 'File with updated header')
127+
128+
129+
class ApplyTransform(SPMCommand):
130+
""" Uses spm to apply transform stored in a .mat file to given file
131+
132+
Examples
133+
--------
134+
135+
>>> import nipype.interfaces.spm.utils as spmu
136+
>>> applymat = spmu.ApplyTransform(matlab_cmd='matlab-spm8')
137+
>>> applymat.inputs.in_file = 'functional.nii'
138+
>>> applymat.inputs.mat = 'func_to_struct.mat'
139+
>>> applymat.run() # doctest: +SKIP
140+
141+
Notes
142+
-----
143+
CHANGES YOUR INPUT FILE (applies transform by updating the header)
144+
"""
145+
input_spec = ApplyTransformInputSpec
146+
output_spec = ApplyTransformOutputSpec
147+
148+
def _make_matlab_command(self, _):
149+
"""checks for SPM, generates script"""
150+
script = """
151+
infile = '%s';
152+
transform = load('%s');
153+
img_space = spm_get_space(infile);
154+
spm_get_space(infile, transform.M * img_space);
155+
"""%(self.inputs.in_file,
156+
self.inputs.mat)
157+
return script
158+
159+
def _list_outputs(self):
160+
outputs = self._outputs().get()
161+
outputs['out_file'] = os.path.abspath(self.inputs.mat)
162+
return outputs
163+
164+
class ResliceInputSpec(SPMCommandInputSpec):
165+
in_file = File( exists = True, mandatory=True,
166+
desc='file to apply transform to, (only updates header)')
167+
space_defining = File ( exists = True, mandatory = True,
168+
desc = 'Volume defining space to slice in_file into')
169+
170+
interp = traits.Range(low = 0, high = 7, usedefault = True,
171+
desc='degree of b-spline used for interpolation'\
172+
'0 is nearest neighbor (default)')
173+
174+
175+
out_file = File(desc = 'Optional file to save resliced volume')
176+
177+
class ResliceOutputSpec(SPMCommandInputSpec):
178+
out_file = File( exists = True, desc = 'resliced volume')
179+
180+
class Reslice(SPMCommand):
181+
""" uses spm_reslice to resample in_file into space of space_defining"""
182+
183+
input_spec = ResliceInputSpec
184+
output_spec = ResliceOutputSpec
185+
186+
def _make_matlab_command(self, _):
187+
""" generates script"""
188+
if not isdefined(self.inputs.out_file):
189+
self.inputs.out_file = fname_presuffix(self.inputs.in_file,
190+
prefix = 'r')
191+
script = """
192+
flags.mean = 0;
193+
flags.which = 1;
194+
flags.mask = 0;
195+
flags.interp = %d;
196+
infiles = strvcat(\'%s\', \'%s\');
197+
invols = spm_vol(infiles);
198+
spm_reslice(invols, flags);
199+
"""%(self.inputs.interp,
200+
self.inputs.space_defining,
201+
self.inputs.in_file)
202+
return script
203+
204+
def _list_outputs(self):
205+
outputs = self._outputs().get()
206+
outputs['out_file'] = os.path.abspath(self.inputs.out_file)
207+
return outputs
208+

0 commit comments

Comments
 (0)