@@ -1865,6 +1865,262 @@ def _list_outputs(self):
18651865 return outputs
18661866
18671867
1868+ class MultiChannelNewSegmentInputSpec (SPMCommandInputSpec ):
1869+ channels = traits .List (
1870+ traits .Tuple (
1871+ InputMultiPath (
1872+ ImageFileSPM (exists = True ),
1873+ mandatory = True ,
1874+ desc = "A list of files to be segmented" ,
1875+ field = "channel" ,
1876+ copyfile = False ,
1877+ ),
1878+ traits .Tuple (
1879+ traits .Float (),
1880+ traits .Float (),
1881+ traits .Tuple (traits .Bool , traits .Bool ),
1882+ desc = """A tuple with the following fields:
1883+ - bias reguralisation (0-10)
1884+ - FWHM of Gaussian smoothness of bias
1885+ - which maps to save (Field, Corrected) - a tuple of two boolean values""" ,
1886+ field = "channel" ,
1887+ )
1888+ ),
1889+ desc = """A list of tuples (one per each channel) with the following fields:
1890+ - a list of channel files (only 1rst channel files will be segmented)
1891+ - a tuple with the following channel-specific info fields:
1892+ - bias reguralisation (0-10)
1893+ - FWHM of Gaussian smoothness of bias
1894+ - which maps to save (Field, Corrected) - a tuple of two boolean values""" ,
1895+ field = "channel" ,
1896+ )
1897+ tissues = traits .List (
1898+ traits .Tuple (
1899+ traits .Tuple (ImageFileSPM (exists = True ), traits .Int ()),
1900+ traits .Int (),
1901+ traits .Tuple (traits .Bool , traits .Bool ),
1902+ traits .Tuple (traits .Bool , traits .Bool ),
1903+ ),
1904+ desc = """A list of tuples (one per tissue) with the following fields:
1905+ - tissue probability map (4D), 1-based index to frame
1906+ - number of gaussians
1907+ - which maps to save [Native, DARTEL] - a tuple of two boolean values
1908+ - which maps to save [Unmodulated, Modulated] - a tuple of two boolean values""" ,
1909+ field = "tissue" ,
1910+ )
1911+ affine_regularization = traits .Enum (
1912+ "mni" ,
1913+ "eastern" ,
1914+ "subj" ,
1915+ "none" ,
1916+ field = "warp.affreg" ,
1917+ desc = "mni, eastern, subj, none " ,
1918+ )
1919+ warping_regularization = traits .Either (
1920+ traits .List (traits .Float (), minlen = 5 , maxlen = 5 ),
1921+ traits .Float (),
1922+ field = "warp.reg" ,
1923+ desc = (
1924+ "Warping regularization "
1925+ "parameter(s). Accepts float "
1926+ "or list of floats (the "
1927+ "latter is required by "
1928+ "SPM12)"
1929+ ),
1930+ )
1931+ sampling_distance = traits .Float (
1932+ field = "warp.samp" , desc = ("Sampling distance on data for parameter estimation" ),
1933+ )
1934+ write_deformation_fields = traits .List (
1935+ traits .Bool (),
1936+ minlen = 2 ,
1937+ maxlen = 2 ,
1938+ field = "warp.write" ,
1939+ desc = ("Which deformation fields to write:[Inverse, Forward]" ),
1940+ )
1941+
1942+
1943+ class MultiChannelNewSegmentOutputSpec (TraitedSpec ):
1944+ native_class_images = traits .List (
1945+ traits .List (File (exists = True )), desc = "native space probability maps"
1946+ )
1947+ dartel_input_images = traits .List (
1948+ traits .List (File (exists = True )), desc = "dartel imported class images"
1949+ )
1950+ normalized_class_images = traits .List (
1951+ traits .List (File (exists = True )), desc = "normalized class images"
1952+ )
1953+ modulated_class_images = traits .List (
1954+ traits .List (File (exists = True )), desc = ("modulated+normalized class images" )
1955+ )
1956+ transformation_mat = OutputMultiPath (
1957+ File (exists = True ), desc = "Normalization transformation"
1958+ )
1959+ bias_corrected_images = OutputMultiPath (
1960+ File (exists = True ), desc = "bias corrected images"
1961+ )
1962+ bias_field_images = OutputMultiPath (File (exists = True ), desc = "bias field images" )
1963+ forward_deformation_field = OutputMultiPath (File (exists = True ))
1964+ inverse_deformation_field = OutputMultiPath (File (exists = True ))
1965+
1966+
1967+ class MultiChannelNewSegment (SPMCommand ):
1968+ """Use spm_preproc8 (New Segment) to separate structural images into
1969+ different tissue classes. Supports multiple modalities and multichannel inputs.
1970+
1971+ http://www.fil.ion.ucl.ac.uk/spm/doc/manual.pdf#page=45
1972+
1973+ Examples
1974+ --------
1975+ >>> import nipype.interfaces.spm as spm
1976+ >>> seg = spm.MultiChannelNewSegment()
1977+ >>> seg.inputs.channels = [('structural.nii',(0.0001, 60, (True, True)))]
1978+ >>> seg.run() # doctest: +SKIP
1979+
1980+ For VBM pre-processing [http://www.fil.ion.ucl.ac.uk/~john/misc/VBMclass10.pdf],
1981+ TPM.nii should be replaced by /path/to/spm8/toolbox/Seg/TPM.nii
1982+
1983+ >>> seg = MultiChannelNewSegment()
1984+ >>> channel1= ('T1.nii',(0.0001, 60, (True, True)))
1985+ >>> channel2= ('T2.nii',(0.0001, 60, (True, True)))
1986+ >>> seg.inputs.channels = [channel1, channel2]
1987+ >>> tissue1 = (('TPM.nii', 1), 2, (True,True), (False, False))
1988+ >>> tissue2 = (('TPM.nii', 2), 2, (True,True), (False, False))
1989+ >>> tissue3 = (('TPM.nii', 3), 2, (True,False), (False, False))
1990+ >>> tissue4 = (('TPM.nii', 4), 2, (False,False), (False, False))
1991+ >>> tissue5 = (('TPM.nii', 5), 2, (False,False), (False, False))
1992+ >>> seg.inputs.tissues = [tissue1, tissue2, tissue3, tissue4, tissue5]
1993+ >>> seg.run() # doctest: +SKIP
1994+
1995+ """
1996+
1997+ input_spec = MultiChannelNewSegmentInputSpec
1998+ output_spec = MultiChannelNewSegmentOutputSpec
1999+
2000+ def __init__ (self , ** inputs ):
2001+ _local_version = SPMCommand ().version
2002+ if _local_version and "12." in _local_version :
2003+ self ._jobtype = "spatial"
2004+ self ._jobname = "preproc"
2005+ else :
2006+ self ._jobtype = "tools"
2007+ self ._jobname = "preproc8"
2008+
2009+ SPMCommand .__init__ (self , ** inputs )
2010+
2011+ def _format_arg (self , opt , spec , val ):
2012+ """Convert input to appropriate format for spm
2013+ """
2014+
2015+ if opt == "channels" :
2016+ # structure have to be recreated because of some weird traits error
2017+ new_channels = []
2018+ for channel in val :
2019+ new_channel = {}
2020+ new_channel ["vols" ] = scans_for_fnames (channel [0 ])
2021+ if isdefined (channel [1 ]):
2022+ info = channel [1 ]
2023+ new_channel ["biasreg" ] = info [0 ]
2024+ new_channel ["biasfwhm" ] = info [1 ]
2025+ new_channel ["write" ] = [int (info [2 ][0 ]), int (info [2 ][1 ])]
2026+ new_channels .append (new_channel )
2027+ return new_channels
2028+ elif opt == "tissues" :
2029+ new_tissues = []
2030+ for tissue in val :
2031+ new_tissue = {}
2032+ new_tissue ["tpm" ] = np .array (
2033+ ["," .join ([tissue [0 ][0 ], str (tissue [0 ][1 ])])], dtype = object
2034+ )
2035+ new_tissue ["ngaus" ] = tissue [1 ]
2036+ new_tissue ["native" ] = [int (tissue [2 ][0 ]), int (tissue [2 ][1 ])]
2037+ new_tissue ["warped" ] = [int (tissue [3 ][0 ]), int (tissue [3 ][1 ])]
2038+ new_tissues .append (new_tissue )
2039+ return new_tissues
2040+ elif opt == "write_deformation_fields" :
2041+ return super (MultiChannelNewSegment , self )._format_arg (
2042+ opt , spec , [int (val [0 ]), int (val [1 ])]
2043+ )
2044+ else :
2045+ return super (MultiChannelNewSegment , self )._format_arg (opt , spec , val )
2046+
2047+ def _list_outputs (self ):
2048+ outputs = self ._outputs ().get ()
2049+ outputs ["native_class_images" ] = []
2050+ outputs ["dartel_input_images" ] = []
2051+ outputs ["normalized_class_images" ] = []
2052+ outputs ["modulated_class_images" ] = []
2053+ outputs ["transformation_mat" ] = []
2054+ outputs ["bias_corrected_images" ] = []
2055+ outputs ["bias_field_images" ] = []
2056+ outputs ["inverse_deformation_field" ] = []
2057+ outputs ["forward_deformation_field" ] = []
2058+
2059+ n_classes = 5
2060+ if isdefined (self .inputs .tissues ):
2061+ n_classes = len (self .inputs .tissues )
2062+ for i in range (n_classes ):
2063+ outputs ["native_class_images" ].append ([])
2064+ outputs ["dartel_input_images" ].append ([])
2065+ outputs ["normalized_class_images" ].append ([])
2066+ outputs ["modulated_class_images" ].append ([])
2067+
2068+ # main outputs are generated for the first channel images only
2069+ for filename in self .inputs .channels [0 ][0 ]:
2070+ pth , base , ext = split_filename (filename )
2071+ if isdefined (self .inputs .tissues ):
2072+ for i , tissue in enumerate (self .inputs .tissues ):
2073+ if tissue [2 ][0 ]:
2074+ outputs ["native_class_images" ][i ].append (
2075+ os .path .join (pth , "c%d%s.nii" % (i + 1 , base ))
2076+ )
2077+ if tissue [2 ][1 ]:
2078+ outputs ["dartel_input_images" ][i ].append (
2079+ os .path .join (pth , "rc%d%s.nii" % (i + 1 , base ))
2080+ )
2081+ if tissue [3 ][0 ]:
2082+ outputs ["normalized_class_images" ][i ].append (
2083+ os .path .join (pth , "wc%d%s.nii" % (i + 1 , base ))
2084+ )
2085+ if tissue [3 ][1 ]:
2086+ outputs ["modulated_class_images" ][i ].append (
2087+ os .path .join (pth , "mwc%d%s.nii" % (i + 1 , base ))
2088+ )
2089+ else :
2090+ for i in range (n_classes ):
2091+ outputs ["native_class_images" ][i ].append (
2092+ os .path .join (pth , "c%d%s.nii" % (i + 1 , base ))
2093+ )
2094+ outputs ["transformation_mat" ].append (
2095+ os .path .join (pth , "%s_seg8.mat" % base )
2096+ )
2097+
2098+ if isdefined (self .inputs .write_deformation_fields ):
2099+ if self .inputs .write_deformation_fields [0 ]:
2100+ outputs ["inverse_deformation_field" ].append (
2101+ os .path .join (pth , "iy_%s.nii" % base )
2102+ )
2103+ if self .inputs .write_deformation_fields [1 ]:
2104+ outputs ["forward_deformation_field" ].append (
2105+ os .path .join (pth , "y_%s.nii" % base )
2106+ )
2107+
2108+ # bias field related images are generated for images in all channels
2109+ for channel in self .inputs .channels :
2110+ for filename in channel [0 ]:
2111+ pth , base , ext = split_filename (filename )
2112+ if isdefined (channel [1 ]):
2113+ if channel [1 ][2 ][0 ]:
2114+ outputs ["bias_field_images" ].append (
2115+ os .path .join (pth , "BiasField_%s.nii" % (base ))
2116+ )
2117+ if channel [1 ][2 ][1 ]:
2118+ outputs ["bias_corrected_images" ].append (
2119+ os .path .join (pth , "m%s.nii" % (base ))
2120+ )
2121+ return outputs
2122+
2123+
18682124class SmoothInputSpec (SPMCommandInputSpec ):
18692125 in_files = InputMultiPath (
18702126 ImageFileSPM (exists = True ),
0 commit comments