Skip to content

Commit 0cf16f1

Browse files
committed
ENH: Refactor NewSegmentInputSpec to support multichannel segmentation
1 parent 8fe05af commit 0cf16f1

File tree

1 file changed

+61
-39
lines changed

1 file changed

+61
-39
lines changed

nipype/interfaces/spm/preprocess.py

Lines changed: 61 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1628,22 +1628,34 @@ def _list_outputs(self):
16281628

16291629

16301630
class NewSegmentInputSpec(SPMCommandInputSpec):
1631-
channel_files = InputMultiPath(
1632-
ImageFileSPM(exists=True),
1633-
mandatory=True,
1634-
desc="A list of files to be segmented",
1635-
field="channel",
1636-
copyfile=False,
1637-
)
1638-
channel_info = traits.Tuple(
1639-
traits.Float(),
1640-
traits.Float(),
1641-
traits.Tuple(traits.Bool, traits.Bool),
1642-
desc="""A tuple with the following fields:
1643-
- bias reguralisation (0-10)
1644-
- FWHM of Gaussian smoothness of bias
1645-
- which maps to save (Field, Corrected) - a tuple of two boolean values""",
1646-
field="channel",
1631+
channels = traits.List(
1632+
traits.Tuple(
1633+
InputMultiPath(
1634+
ImageFileSPM(exists=True),
1635+
mandatory=True,
1636+
desc="A list of files to be segmented",
1637+
field="channel",
1638+
copyfile=False,
1639+
),
1640+
traits.Tuple(
1641+
traits.Float(),
1642+
traits.Float(),
1643+
traits.Tuple(traits.Bool, traits.Bool),
1644+
desc="""A tuple with the following fields:
1645+
- bias reguralisation (0-10)
1646+
- FWHM of Gaussian smoothness of bias
1647+
- which maps to save (Field, Corrected) - a tuple of two boolean values""",
1648+
field="channel",
1649+
)
1650+
),
1651+
desc="""A list of tuples (one per each channel)
1652+
with the following fields:
1653+
- a list of channel files (only 1rst channel files will be segmented)
1654+
- a tuple with the following channel-specific info fields:
1655+
- bias reguralisation (0-10)
1656+
- FWHM of Gaussian smoothness of bias
1657+
- which maps to save (Field, Corrected) - a tuple of two boolean values""",
1658+
field="channel",
16471659
)
16481660
tissues = traits.List(
16491661
traits.Tuple(
@@ -1719,23 +1731,24 @@ class NewSegment(SPMCommand):
17191731
"""Use spm_preproc8 (New Segment) to separate structural images into
17201732
different tissue classes. Supports multiple modalities.
17211733
1722-
NOTE: This interface currently supports single channel input only
1734+
NOTE: This interface supports multi-channel inputs
17231735
17241736
http://www.fil.ion.ucl.ac.uk/spm/doc/manual.pdf#page=43
17251737
17261738
Examples
17271739
--------
17281740
>>> import nipype.interfaces.spm as spm
17291741
>>> seg = spm.NewSegment()
1730-
>>> seg.inputs.channel_files = 'structural.nii'
1731-
>>> seg.inputs.channel_info = (0.0001, 60, (True, True))
1742+
>>> seg.inputs.channels = [('structural.nii',(0.0001, 60, (True, True)))]
17321743
>>> seg.run() # doctest: +SKIP
17331744
17341745
For VBM pre-processing [http://www.fil.ion.ucl.ac.uk/~john/misc/VBMclass10.pdf],
17351746
TPM.nii should be replaced by /path/to/spm8/toolbox/Seg/TPM.nii
17361747
17371748
>>> seg = NewSegment()
1738-
>>> seg.inputs.channel_files = 'structural.nii'
1749+
>>> channel1= ('t1w.nii',(0.0001, 60, (True, True)))
1750+
>>> channel2= ('t2w.nii',(0.0001, 60, (True, True)))
1751+
>>> seg.inputs.channels = [channel1, channel2]
17391752
>>> tissue1 = (('TPM.nii', 1), 2, (True,True), (False, False))
17401753
>>> tissue2 = (('TPM.nii', 2), 2, (True,True), (False, False))
17411754
>>> tissue3 = (('TPM.nii', 3), 2, (True,False), (False, False))
@@ -1764,16 +1777,20 @@ def _format_arg(self, opt, spec, val):
17641777
"""Convert input to appropriate format for spm
17651778
"""
17661779

1767-
if opt in ["channel_files", "channel_info"]:
1780+
#if opt in ["channel_files", "channel_info"]:
1781+
if opt == "channels":
17681782
# structure have to be recreated because of some weird traits error
1769-
new_channel = {}
1770-
new_channel["vols"] = scans_for_fnames(self.inputs.channel_files)
1771-
if isdefined(self.inputs.channel_info):
1772-
info = self.inputs.channel_info
1773-
new_channel["biasreg"] = info[0]
1774-
new_channel["biasfwhm"] = info[1]
1775-
new_channel["write"] = [int(info[2][0]), int(info[2][1])]
1776-
return [new_channel]
1783+
new_channels = []
1784+
for channel in val:
1785+
new_channel = {}
1786+
new_channel["vols"] = scans_for_fnames(channel[0])
1787+
if isdefined(channel[1]):
1788+
info = channel[1]
1789+
new_channel["biasreg"] = info[0]
1790+
new_channel["biasfwhm"] = info[1]
1791+
new_channel["write"] = [int(info[2][0]), int(info[2][1])]
1792+
new_channels.append(new_channel)
1793+
return new_channels
17771794
elif opt == "tissues":
17781795
new_tissues = []
17791796
for tissue in val:
@@ -1814,7 +1831,8 @@ def _list_outputs(self):
18141831
outputs["normalized_class_images"].append([])
18151832
outputs["modulated_class_images"].append([])
18161833

1817-
for filename in self.inputs.channel_files:
1834+
# main outputs are generated for the first channel images only
1835+
for filename in self.inputs.channels[0][0]:
18181836
pth, base, ext = split_filename(filename)
18191837
if isdefined(self.inputs.tissues):
18201838
for i, tissue in enumerate(self.inputs.tissues):
@@ -1853,15 +1871,19 @@ def _list_outputs(self):
18531871
os.path.join(pth, "y_%s.nii" % base)
18541872
)
18551873

1856-
if isdefined(self.inputs.channel_info):
1857-
if self.inputs.channel_info[2][0]:
1858-
outputs["bias_field_images"].append(
1859-
os.path.join(pth, "BiasField_%s.nii" % (base))
1860-
)
1861-
if self.inputs.channel_info[2][1]:
1862-
outputs["bias_corrected_images"].append(
1863-
os.path.join(pth, "m%s.nii" % (base))
1864-
)
1874+
# bias field related images are generated for images in all channels
1875+
for channel in self.inputs.channels:
1876+
for filename in channel[0]:
1877+
pth, base, ext = split_filename(filename)
1878+
if isdefined(channel[1]):
1879+
if channel[1][2][0]:
1880+
outputs["bias_field_images"].append(
1881+
os.path.join(pth, "BiasField_%s.nii" % (base))
1882+
)
1883+
if channel[1][2][1]:
1884+
outputs["bias_corrected_images"].append(
1885+
os.path.join(pth, "m%s.nii" % (base))
1886+
)
18651887
return outputs
18661888

18671889

0 commit comments

Comments
 (0)