36
36
from .. import logging , config
37
37
iflogger = logging .getLogger ('interface' )
38
38
39
+
39
40
def _get_affine_matrix (params , source ):
40
41
"""Return affine matrix given a set of translation and rotation parameters
41
42
@@ -117,7 +118,7 @@ def _calc_norm(mc, use_differences, source, brain_pts=None):
117
118
if brain_pts is not None :
118
119
displacement [i , :] = \
119
120
np .sqrt (np .sum (np .power (np .reshape (newpos [i , :],
120
- (3 , all_pts .shape [1 ])) - \
121
+ (3 , all_pts .shape [1 ])) -
121
122
all_pts [0 :3 , :],
122
123
2 ),
123
124
axis = 0 ))
@@ -135,6 +136,7 @@ def _calc_norm(mc, use_differences, source, brain_pts=None):
135
136
normdata = np .sqrt (np .mean (np .power (newpos , 2 ), axis = 1 ))
136
137
return normdata , displacement
137
138
139
+
138
140
def _nanmean (a , axis = None ):
139
141
"""Return the mean excluding items that are nan
140
142
@@ -151,42 +153,56 @@ def _nanmean(a, axis=None):
151
153
152
154
class ArtifactDetectInputSpec (BaseInterfaceInputSpec ):
153
155
realigned_files = InputMultiPath (File (exists = True ),
154
- desc = "Names of realigned functional data files" ,
156
+ desc = "Names of realigned functional data files" ,
155
157
mandatory = True )
156
158
realignment_parameters = InputMultiPath (File (exists = True ), mandatory = True ,
157
- desc = ("Names of realignment parameters"
158
- "corresponding to the functional data files" ))
159
+ desc = ("Names of realignment parameters"
160
+ "corresponding to the functional data files" ))
159
161
parameter_source = traits .Enum ("SPM" , "FSL" , "AFNI" , "NiPy" ,
160
- desc = "Source of movement parameters" , mandatory = True )
161
- use_differences = traits .ListBool ([True , False ], minlen = 2 , maxlen = 2 , usedefault = True ,
162
- desc = "Use differences between successive motion (first element)" \
163
- "and intensity paramter (second element) estimates in order" \
164
- "to determine outliers. (default is [True, False])" )
165
- use_norm = traits .Bool (True , desc = "Uses a composite of the motion parameters in order to determine" \
166
- "outliers. Requires ``norm_threshold`` to be set. (default is" \
167
- "True) " , usedefault = True )
168
- norm_threshold = traits .Float (desc = "Threshold to use to detect motion-related outliers when" \
169
- "composite motion is being used (see ``use_norm``)" , mandatory = True ,
170
- xor = ['rotation_threshold' , 'translation_threshold' ])
171
- rotation_threshold = traits .Float (desc = "Threshold (in radians) to use to detect rotation-related outliers" ,
172
- mandatory = True , xor = ['norm_threshold' ])
173
- translation_threshold = traits .Float (desc = "Threshold (in mm) to use to detect translation-related outliers" ,
174
- mandatory = True , xor = ['norm_threshold' ])
175
- zintensity_threshold = traits .Float (desc = "Intensity Z-threshold use to detection images that deviate from the" \
176
- "mean" , mandatory = True )
177
- mask_type = traits .Enum ('spm_global' , 'file' , 'thresh' , desc = "Type of mask that should be used to mask the functional data." \
178
- "*spm_global* uses an spm_global like calculation to determine the" \
179
- "brain mask. *file* specifies a brain mask file (should be an image" \
180
- "file consisting of 0s and 1s). *thresh* specifies a threshold to" \
181
- "use. By default all voxels are used, unless one of these mask" \
182
- "types are defined." , mandatory = True )
183
- mask_file = File (exists = True , desc = "Mask file to be used if mask_type is 'file'." )
184
- mask_threshold = traits .Float (desc = "Mask threshold to be used if mask_type is 'thresh'." )
185
- intersect_mask = traits .Bool (True , desc = "Intersect the masks when computed from spm_global. (default is" \
186
- "True)" )
162
+ desc = "Source of movement parameters" ,
163
+ mandatory = True )
164
+ use_differences = traits .ListBool ([True , False ], minlen = 2 , maxlen = 2 ,
165
+ usedefault = True ,
166
+ desc = ("Use differences between successive motion (first element)"
167
+ "and intensity paramter (second element) estimates in order"
168
+ "to determine outliers. (default is [True, False])" ))
169
+ use_norm = traits .Bool (True , requires = ['norm_threshold' ],
170
+ desc = ("Uses a composite of the motion parameters in "
171
+ "order to determine outliers." ),
172
+ usedefault = True )
173
+ norm_threshold = traits .Float (desc = ("Threshold to use to detect motion-rela"
174
+ "ted outliers when composite motion is "
175
+ "being used" ), mandatory = True ,
176
+ xor = ['rotation_threshold' ,
177
+ 'translation_threshold' ])
178
+ rotation_threshold = traits .Float (mandatory = True , xor = ['norm_threshold' ],
179
+ desc = ("Threshold (in radians) to use to detect rotation-related "
180
+ "outliers" ))
181
+ translation_threshold = traits .Float (mandatory = True , xor = ['norm_threshold' ],
182
+ desc = ("Threshold (in mm) to use to detect translation-related "
183
+ "outliers" ))
184
+ zintensity_threshold = traits .Float (mandatory = True ,
185
+ desc = ("Intensity Z-threshold use to detection images that deviate "
186
+ "from the mean" ))
187
+ mask_type = traits .Enum ('spm_global' , 'file' , 'thresh' ,
188
+ desc = ("Type of mask that should be used to mask the functional "
189
+ "data. *spm_global* uses an spm_global like calculation to "
190
+ "determine the brain mask. *file* specifies a brain mask "
191
+ "file (should be an image file consisting of 0s and 1s). "
192
+ "*thresh* specifies a threshold to use. By default all voxels"
193
+ "are used, unless one of these mask types are defined." ),
194
+ mandatory = True )
195
+ mask_file = File (exists = True ,
196
+ desc = "Mask file to be used if mask_type is 'file'." )
197
+ mask_threshold = traits .Float (desc = ("Mask threshold to be used if mask_type"
198
+ " is 'thresh'." ))
199
+ intersect_mask = traits .Bool (True ,
200
+ desc = ("Intersect the masks when computed from "
201
+ "spm_global." ))
187
202
save_plot = traits .Bool (True , desc = "save plots containing outliers" ,
188
203
usedefault = True )
189
- plot_type = traits .Enum ('png' , 'svg' , 'eps' , 'pdf' , desc = "file type of the outlier plot" ,
204
+ plot_type = traits .Enum ('png' , 'svg' , 'eps' , 'pdf' ,
205
+ desc = "file type of the outlier plot" ,
190
206
usedefault = True )
191
207
bound_by_brainmask = traits .Bool (False , desc = ("use the brain mask to "
192
208
"determine bounding box"
@@ -198,16 +214,23 @@ class ArtifactDetectInputSpec(BaseInterfaceInputSpec):
198
214
199
215
200
216
class ArtifactDetectOutputSpec (TraitedSpec ):
201
- outlier_files = OutputMultiPath (File (exists = True ), desc = "One file for each functional run containing a list of 0-based" \
202
- "indices corresponding to outlier volumes" )
203
- intensity_files = OutputMultiPath (File (exists = True ), desc = "One file for each functional run containing the global intensity" \
204
- "values determined from the brainmask" )
205
- norm_files = OutputMultiPath (File , desc = "One file for each functional run containing the composite norm" )
206
- statistic_files = OutputMultiPath (File (exists = True ), desc = "One file for each functional run containing information about the" \
207
- "different types of artifacts and if design info is provided then" \
208
- "details of stimulus correlated motion and a listing or artifacts by" \
209
- "event type." )
210
- plot_files = OutputMultiPath (File , desc = "One image file for each functional run containing the detected outliers" )
217
+ outlier_files = OutputMultiPath (File (exists = True ),
218
+ desc = ("One file for each functional run containing a list of "
219
+ "0-based indices corresponding to outlier volumes" ))
220
+ intensity_files = OutputMultiPath (File (exists = True ),
221
+ desc = ("One file for each functional run containing the global "
222
+ "intensity values determined from the brainmask" ))
223
+ norm_files = OutputMultiPath (File ,
224
+ desc = ("One file for each functional run containing the composite "
225
+ "norm" ))
226
+ statistic_files = OutputMultiPath (File (exists = True ),
227
+ desc = ("One file for each functional run containing information "
228
+ "about the different types of artifacts and if design info is"
229
+ " provided then details of stimulus correlated motion and a "
230
+ "listing or artifacts by event type." ))
231
+ plot_files = OutputMultiPath (File ,
232
+ desc = ("One image file for each functional run containing the "
233
+ "detected outliers" ))
211
234
mask_files = OutputMultiPath (File ,
212
235
desc = ("One image file for each functional run containing the mask"
213
236
"used for global signal calculation" ))
@@ -245,7 +268,6 @@ def __init__(self, **inputs):
245
268
'in release 0.8' ))
246
269
super (ArtifactDetect , self ).__init__ (** inputs )
247
270
248
-
249
271
def _get_output_filenames (self , motionfile , output_dir ):
250
272
"""Generate output files based on motion filenames
251
273
@@ -344,7 +366,7 @@ def _detect_outliers_core(self, imgfile, motionfile, runidx, cwd=None):
344
366
affine = nim .get_affine ()
345
367
g = np .zeros ((timepoints , 1 ))
346
368
masktype = self .inputs .mask_type
347
- if masktype == 'spm_global' : # spm_global like calculation
369
+ if masktype == 'spm_global' : # spm_global like calculation
348
370
iflogger .debug ('art: using spm global' )
349
371
intersect_mask = self .inputs .intersect_mask
350
372
if intersect_mask :
@@ -420,7 +442,9 @@ def _detect_outliers_core(self, imgfile, motionfile, runidx, cwd=None):
420
442
if displacement is not None :
421
443
dmap = np .zeros ((x , y , z , timepoints ), dtype = np .float )
422
444
for i in range (timepoints ):
423
- dmap [voxel_coords [0 ], voxel_coords [1 ], voxel_coords [2 ], i ] = displacement [i , :]
445
+ dmap [voxel_coords [0 ],
446
+ voxel_coords [1 ],
447
+ voxel_coords [2 ], i ] = displacement [i , :]
424
448
dimg = Nifti1Image (dmap , affine )
425
449
dimg .to_filename (displacementfile )
426
450
else :
@@ -430,8 +454,11 @@ def _detect_outliers_core(self, imgfile, motionfile, runidx, cwd=None):
430
454
axis = 0 )
431
455
traval = mc [:, 0 :3 ] # translation parameters (mm)
432
456
rotval = mc [:, 3 :6 ] # rotation parameters (rad)
433
- tidx = find_indices (np .sum (abs (traval ) > self .inputs .translation_threshold , 1 ) > 0 )
434
- ridx = find_indices (np .sum (abs (rotval ) > self .inputs .rotation_threshold , 1 ) > 0 )
457
+ tidx = find_indices (np .sum (abs (traval ) >
458
+ self .inputs .translation_threshold , 1 )
459
+ > 0 )
460
+ ridx = find_indices (np .sum (abs (rotval ) >
461
+ self .inputs .rotation_threshold , 1 ) > 0 )
435
462
436
463
outliers = np .unique (np .union1d (iidx , np .union1d (tidx , ridx )))
437
464
@@ -476,25 +503,26 @@ def _detect_outliers_core(self, imgfile, motionfile, runidx, cwd=None):
476
503
motion_outliers )),
477
504
'motion_outliers' : len (np .setdiff1d (motion_outliers , iidx )),
478
505
},
479
- {'motion' : [{'using differences' : self .inputs .use_differences [0 ]},
480
- {'mean' : np .mean (mc_in , axis = 0 ).tolist (),
481
- 'min' : np .min (mc_in , axis = 0 ).tolist (),
482
- 'max' : np .max (mc_in , axis = 0 ).tolist (),
483
- 'std' : np .std (mc_in , axis = 0 ).tolist ()},
484
- ]},
485
- {'intensity' : [{'using differences' : self .inputs .use_differences [1 ]},
486
- {'mean' : np .mean (gz , axis = 0 ).tolist (),
487
- 'min' : np .min (gz , axis = 0 ).tolist (),
488
- 'max' : np .max (gz , axis = 0 ).tolist (),
489
- 'std' : np .std (gz , axis = 0 ).tolist ()},
490
- ]},
506
+ {'motion' : [{'using differences' : self .inputs .use_differences [0 ]},
507
+ {'mean' : np .mean (mc_in , axis = 0 ).tolist (),
508
+ 'min' : np .min (mc_in , axis = 0 ).tolist (),
509
+ 'max' : np .max (mc_in , axis = 0 ).tolist (),
510
+ 'std' : np .std (mc_in , axis = 0 ).tolist ()},
511
+ ]},
512
+ {'intensity' : [{'using differences' : self .inputs .use_differences [1 ]},
513
+ {'mean' : np .mean (gz , axis = 0 ).tolist (),
514
+ 'min' : np .min (gz , axis = 0 ).tolist (),
515
+ 'max' : np .max (gz , axis = 0 ).tolist (),
516
+ 'std' : np .std (gz , axis = 0 ).tolist ()},
517
+ ]},
491
518
]
492
519
if self .inputs .use_norm :
493
- stats .insert (3 , {'motion_norm' : {'mean' : np .mean (normval , axis = 0 ).tolist (),
494
- 'min' : np .min (normval , axis = 0 ).tolist (),
495
- 'max' : np .max (normval , axis = 0 ).tolist (),
496
- 'std' : np .std (normval , axis = 0 ).tolist (),
497
- }})
520
+ stats .insert (3 , {'motion_norm' :
521
+ {'mean' : np .mean (normval , axis = 0 ).tolist (),
522
+ 'min' : np .min (normval , axis = 0 ).tolist (),
523
+ 'max' : np .max (normval , axis = 0 ).tolist (),
524
+ 'std' : np .std (normval , axis = 0 ).tolist (),
525
+ }})
498
526
save_json (statsfile , stats )
499
527
500
528
def _run_interface (self , runtime ):
@@ -510,7 +538,8 @@ def _run_interface(self, runtime):
510
538
511
539
class StimCorrInputSpec (BaseInterfaceInputSpec ):
512
540
realignment_parameters = InputMultiPath (File (exists = True ), mandatory = True ,
513
- desc = 'Names of realignment parameters corresponding to the functional data files' )
541
+ desc = ('Names of realignment parameters corresponding to the functional '
542
+ 'data files' ))
514
543
intensity_values = InputMultiPath (File (exists = True ), mandatory = True ,
515
544
desc = 'Name of file containing intensity values' )
516
545
spm_mat_file = File (exists = True , mandatory = True ,
@@ -561,7 +590,8 @@ def _get_output_filenames(self, motionfile, output_dir):
561
590
"""
562
591
(filepath , filename ) = os .path .split (motionfile )
563
592
(filename , ext ) = os .path .splitext (filename )
564
- corrfile = os .path .join (output_dir , '' .join (('qa.' , filename , '_stimcorr.txt' )))
593
+ corrfile = os .path .join (output_dir , '' .join (('qa.' , filename ,
594
+ '_stimcorr.txt' )))
565
595
return corrfile
566
596
567
597
def _stimcorr_core (self , motionfile , intensityfile , designmatrix , cwd = None ):
@@ -608,7 +638,8 @@ def _get_spm_submatrix(self, spmmat, sessidx, rows=None):
608
638
if rows is None :
609
639
rows = spmmat ['SPM' ][0 ][0 ].Sess [0 ][sessidx ].row [0 ] - 1
610
640
cols = spmmat ['SPM' ][0 ][0 ].Sess [0 ][sessidx ].col [0 ][range (len (U ))] - 1
611
- outmatrix = designmatrix .take (rows .tolist (), axis = 0 ).take (cols .tolist (), axis = 1 )
641
+ outmatrix = designmatrix .take (rows .tolist (), axis = 0 ).take (cols .tolist (),
642
+ axis = 1 )
612
643
return outmatrix
613
644
614
645
def _run_interface (self , runtime ):
0 commit comments