Skip to content

Commit 3ce081a

Browse files
authored
Merge pull request #2173 from mgxd/fix/art
fix: allow translation thresholding
2 parents a86c1bb + d0dd706 commit 3ce081a

File tree

1 file changed

+106
-68
lines changed

1 file changed

+106
-68
lines changed

nipype/algorithms/rapidart.py

Lines changed: 106 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
>>> datadir = os.path.realpath(os.path.join(filepath, '../testing/data'))
1919
>>> os.chdir(datadir)
2020
"""
21-
from __future__ import print_function, division, unicode_literals, absolute_import
21+
from __future__ import (print_function, division,
22+
unicode_literals, absolute_import)
2223
from builtins import open, range, str, bytes
2324

2425
import os
@@ -160,45 +161,61 @@ def _calc_norm_affine(affines, use_differences, brain_pts=None):
160161

161162
class ArtifactDetectInputSpec(BaseInterfaceInputSpec):
162163
realigned_files = InputMultiPath(File(exists=True),
163-
desc="Names of realigned functional data files",
164+
desc=("Names of realigned functional data "
165+
"files"),
164166
mandatory=True)
165-
realignment_parameters = InputMultiPath(File(exists=True), mandatory=True,
166-
desc=("Names of realignment parameters"
167-
"corresponding to the functional data files"))
167+
realignment_parameters = InputMultiPath(File(exists=True),
168+
mandatory=True,
169+
desc=("Names of realignment "
170+
"parameters corresponding to "
171+
"the functional data files"))
168172
parameter_source = traits.Enum("SPM", "FSL", "AFNI", "NiPy", "FSFAST",
169173
desc="Source of movement parameters",
170174
mandatory=True)
171-
use_differences = traits.ListBool([True, False], minlen=2, maxlen=2,
175+
use_differences = traits.ListBool([True, False],
176+
minlen=2,
177+
maxlen=2,
172178
usedefault=True,
173-
desc=("Use differences between successive motion (first element)"
174-
"and intensity paramter (second element) estimates in order"
175-
"to determine outliers. (default is [True, False])"))
176-
use_norm = traits.Bool(True, requires=['norm_threshold'],
179+
desc=("Use differences between successive"
180+
" motion (first element) and "
181+
"intensity parameter (second "
182+
"element) estimates in order to "
183+
"determine outliers. "
184+
"(default is [True, False])"))
185+
use_norm = traits.Bool(True,
186+
usedefault=True,
187+
requires=['norm_threshold'],
177188
desc=("Uses a composite of the motion parameters in "
178-
"order to determine outliers."),
179-
usedefault=True)
180-
norm_threshold = traits.Float(desc=("Threshold to use to detect motion-rela"
189+
"order to determine outliers."))
190+
norm_threshold = traits.Float(xor=['rotation_threshold',
191+
'translation_threshold'],
192+
mandatory=True,
193+
desc=("Threshold to use to detect motion-rela"
181194
"ted outliers when composite motion is "
182-
"being used"), mandatory=True,
183-
xor=['rotation_threshold',
184-
'translation_threshold'])
185-
rotation_threshold = traits.Float(mandatory=True, xor=['norm_threshold'],
186-
desc=("Threshold (in radians) to use to detect rotation-related "
187-
"outliers"))
188-
translation_threshold = traits.Float(mandatory=True, xor=['norm_threshold'],
189-
desc=("Threshold (in mm) to use to detect translation-related "
195+
"being used"))
196+
rotation_threshold = traits.Float(mandatory=True,
197+
xor=['norm_threshold'],
198+
desc=("Threshold (in radians) to use to "
199+
"detect rotation-related outliers"))
200+
translation_threshold = traits.Float(mandatory=True,
201+
xor=['norm_threshold'],
202+
desc=("Threshold (in mm) to use to "
203+
"detect translation-related "
190204
"outliers"))
191205
zintensity_threshold = traits.Float(mandatory=True,
192-
desc=("Intensity Z-threshold use to detection images that deviate "
206+
desc=("Intensity Z-threshold use to "
207+
"detection images that deviate "
193208
"from the mean"))
194209
mask_type = traits.Enum('spm_global', 'file', 'thresh',
195-
desc=("Type of mask that should be used to mask the functional "
196-
"data. *spm_global* uses an spm_global like calculation to "
197-
"determine the brain mask. *file* specifies a brain mask "
198-
"file (should be an image file consisting of 0s and 1s). "
199-
"*thresh* specifies a threshold to use. By default all voxels"
200-
"are used, unless one of these mask types are defined."),
201-
mandatory=True)
210+
mandatory=True,
211+
desc=("Type of mask that should be used to mask the"
212+
" functional data. *spm_global* uses an "
213+
"spm_global like calculation to determine the"
214+
" brain mask. *file* specifies a brain mask "
215+
"file (should be an image file consisting of "
216+
"0s and 1s). *thresh* specifies a threshold "
217+
"to use. By default all voxels are used,"
218+
"unless one of these mask types are defined"))
202219
mask_file = File(exists=True,
203220
desc="Mask file to be used if mask_type is 'file'.")
204221
mask_threshold = traits.Float(desc=("Mask threshold to be used if mask_type"
@@ -224,36 +241,46 @@ class ArtifactDetectInputSpec(BaseInterfaceInputSpec):
224241

225242
class ArtifactDetectOutputSpec(TraitedSpec):
226243
outlier_files = OutputMultiPath(File(exists=True),
227-
desc=("One file for each functional run containing a list of "
228-
"0-based indices corresponding to outlier volumes"))
244+
desc=("One file for each functional run "
245+
"containing a list of 0-based indices"
246+
" corresponding to outlier volumes"))
229247
intensity_files = OutputMultiPath(File(exists=True),
230-
desc=("One file for each functional run containing the global "
231-
"intensity values determined from the brainmask"))
248+
desc=("One file for each functional run "
249+
"containing the global intensity "
250+
"values determined from the "
251+
"brainmask"))
232252
norm_files = OutputMultiPath(File,
233-
desc=("One file for each functional run containing the composite "
234-
"norm"))
253+
desc=("One file for each functional run "
254+
"containing the composite norm"))
235255
statistic_files = OutputMultiPath(File(exists=True),
236-
desc=("One file for each functional run containing information "
237-
"about the different types of artifacts and if design info is"
238-
" provided then details of stimulus correlated motion and a "
239-
"listing or artifacts by event type."))
256+
desc=("One file for each functional run "
257+
"containing information about the "
258+
"different types of artifacts and "
259+
"if design info is provided then "
260+
"details of stimulus correlated "
261+
"motion and a listing or artifacts "
262+
"by event type."))
240263
plot_files = OutputMultiPath(File,
241-
desc=("One image file for each functional run containing the "
242-
"detected outliers"))
264+
desc=("One image file for each functional run "
265+
"containing the detected outliers"))
243266
mask_files = OutputMultiPath(File,
244-
desc=("One image file for each functional run containing the mask"
245-
"used for global signal calculation"))
267+
desc=("One image file for each functional run "
268+
"containing the mask used for global "
269+
"signal calculation"))
246270
displacement_files = OutputMultiPath(File,
247-
desc=("One image file for each functional run containing the voxel"
248-
"displacement timeseries"))
271+
desc=("One image file for each "
272+
"functional run containing the "
273+
"voxel displacement timeseries"))
249274

250275

251276
class ArtifactDetect(BaseInterface):
252277
"""Detects outliers in a functional imaging series
253278
254279
Uses intensity and motion parameters to infer outliers. If `use_norm` is
255280
True, it computes the movement of the center of each face a cuboid centered
256-
around the head and returns the maximal movement across the centers.
281+
around the head and returns the maximal movement across the centers. If you
282+
wish to use individual thresholds instead, import `Undefined` from
283+
`nipype.interfaces.base` and set `....inputs.use_norm = Undefined`
257284
258285
259286
Examples
@@ -516,17 +543,19 @@ def _detect_outliers_core(self, imgfile, motionfile, runidx, cwd=None):
516543
motion_outliers)),
517544
'motion_outliers': len(np.setdiff1d(motion_outliers, iidx)),
518545
},
519-
{'motion': [{'using differences': self.inputs.use_differences[0]},
520-
{'mean': np.mean(mc_in, axis=0).tolist(),
521-
'min': np.min(mc_in, axis=0).tolist(),
522-
'max': np.max(mc_in, axis=0).tolist(),
523-
'std': np.std(mc_in, axis=0).tolist()},
546+
{'motion': [
547+
{'using differences': self.inputs.use_differences[0]},
548+
{'mean': np.mean(mc_in, axis=0).tolist(),
549+
'min': np.min(mc_in, axis=0).tolist(),
550+
'max': np.max(mc_in, axis=0).tolist(),
551+
'std': np.std(mc_in, axis=0).tolist()},
524552
]},
525-
{'intensity': [{'using differences': self.inputs.use_differences[1]},
526-
{'mean': np.mean(gz, axis=0).tolist(),
527-
'min': np.min(gz, axis=0).tolist(),
528-
'max': np.max(gz, axis=0).tolist(),
529-
'std': np.std(gz, axis=0).tolist()},
553+
{'intensity': [
554+
{'using differences': self.inputs.use_differences[1]},
555+
{'mean': np.mean(gz, axis=0).tolist(),
556+
'min': np.min(gz, axis=0).tolist(),
557+
'max': np.max(gz, axis=0).tolist(),
558+
'std': np.std(gz, axis=0).tolist()},
530559
]},
531560
]
532561
if self.inputs.use_norm:
@@ -550,20 +579,27 @@ def _run_interface(self, runtime):
550579

551580

552581
class StimCorrInputSpec(BaseInterfaceInputSpec):
553-
realignment_parameters = InputMultiPath(File(exists=True), mandatory=True,
554-
desc=('Names of realignment parameters corresponding to the functional '
555-
'data files'))
556-
intensity_values = InputMultiPath(File(exists=True), mandatory=True,
557-
desc='Name of file containing intensity values')
558-
spm_mat_file = File(exists=True, mandatory=True,
559-
desc='SPM mat file (use pre-estimate SPM.mat file)')
582+
realignment_parameters = InputMultiPath(File(exists=True),
583+
mandatory=True,
584+
desc=("Names of realignment "
585+
"parameters corresponding to "
586+
"the functional data files"))
587+
intensity_values = InputMultiPath(File(exists=True),
588+
mandatory=True,
589+
desc=("Name of file containing intensity "
590+
"values"))
591+
spm_mat_file = File(exists=True,
592+
mandatory=True,
593+
desc="SPM mat file (use pre-estimate SPM.mat file)")
560594
concatenated_design = traits.Bool(mandatory=True,
561-
desc='state if the design matrix contains concatenated sessions')
595+
desc=("state if the design matrix "
596+
"contains concatenated sessions"))
562597

563598

564599
class StimCorrOutputSpec(TraitedSpec):
565600
stimcorr_files = OutputMultiPath(File(exists=True),
566-
desc='List of files containing correlation values')
601+
desc=("List of files containing "
602+
"correlation values"))
567603

568604

569605
class StimulusCorrelation(BaseInterface):
@@ -573,8 +609,9 @@ class StimulusCorrelation(BaseInterface):
573609
Currently this class supports an SPM generated design matrix and requires
574610
intensity parameters. This implies that one must run
575611
:ref:`ArtifactDetect <nipype.algorithms.rapidart.ArtifactDetect>`
576-
and :ref:`Level1Design <nipype.interfaces.spm.model.Level1Design>` prior to running this or
577-
provide an SPM.mat file and intensity parameters through some other means.
612+
and :ref:`Level1Design <nipype.interfaces.spm.model.Level1Design>` prior to
613+
running this or provide an SPM.mat file and intensity parameters through
614+
some other means.
578615
579616
Examples
580617
--------
@@ -650,7 +687,8 @@ def _get_spm_submatrix(self, spmmat, sessidx, rows=None):
650687
U = spmmat['SPM'][0][0].Sess[0][sessidx].U[0]
651688
if rows is None:
652689
rows = spmmat['SPM'][0][0].Sess[0][sessidx].row[0] - 1
653-
cols = spmmat['SPM'][0][0].Sess[0][sessidx].col[0][list(range(len(U)))] - 1
690+
cols = (
691+
spmmat['SPM'][0][0].Sess[0][sessidx].col[0][list(range(len(U)))]-1)
654692
outmatrix = designmatrix.take(rows.tolist(), axis=0).take(cols.tolist(),
655693
axis=1)
656694
return outmatrix

0 commit comments

Comments
 (0)