@@ -579,6 +579,79 @@ def _list_outputs(self):
579
579
outputs ['detrended_file' ] = op .abspath (self .inputs .detrended_file )
580
580
return outputs
581
581
582
+
583
+ class NonSteadyStateDetectorInputSpec (BaseInterfaceInputSpec ):
584
+ in_file = File (exists = True , mandatory = True , desc = '4D NIFTI EPI file' )
585
+
586
+
587
+ class NonSteadyStateDetectorOutputSpec (TraitedSpec ):
588
+ n_volumes_to_discard = traits .Int (desc = 'Number of non-steady state volumes'
589
+ 'detected in the beginning of the scan.' )
590
+
591
+
592
+ class NonSteadyStateDetector (BaseInterface ):
593
+ """
594
+ Returns the number of non-steady state volumes detected at the beginning
595
+ of the scan.
596
+ """
597
+
598
+ input_spec = NonSteadyStateDetectorInputSpec
599
+ output_spec = NonSteadyStateDetectorOutputSpec
600
+
601
+ def _run_interface (self , runtime ):
602
+ in_nii = nb .load (self .inputs .in_plots )
603
+ global_signal = in_nii .get_data ()[:,:,:,:50 ].mean (axis = 0 ).mean (axis = 0 ).mean (axis = 0 )
604
+
605
+ self ._results = {
606
+ 'out_file' : _is_outlier (global_signal )
607
+ }
608
+
609
+ return runtime
610
+
611
+ def _list_outputs (self ):
612
+ return self ._results
613
+
614
+ def _is_outlier (points , thresh = 3.5 ):
615
+ """
616
+ Returns a boolean array with True if points are outliers and False
617
+ otherwise.
618
+
619
+ Parameters:
620
+ -----------
621
+ points : An numobservations by numdimensions array of observations
622
+ thresh : The modified z-score to use as a threshold. Observations with
623
+ a modified z-score (based on the median absolute deviation) greater
624
+ than this value will be classified as outliers.
625
+
626
+ Returns:
627
+ --------
628
+ mask : A numobservations-length boolean array.
629
+
630
+ References:
631
+ ----------
632
+ Boris Iglewicz and David Hoaglin (1993), "Volume 16: How to Detect and
633
+ Handle Outliers", The ASQC Basic References in Quality Control:
634
+ Statistical Techniques, Edward F. Mykytka, Ph.D., Editor.
635
+ """
636
+ if len (points .shape ) == 1 :
637
+ points = points [:, None ]
638
+ median = np .median (points , axis = 0 )
639
+ diff = np .sum ((points - median ) ** 2 , axis = - 1 )
640
+ diff = np .sqrt (diff )
641
+ med_abs_deviation = np .median (diff )
642
+
643
+ modified_z_score = 0.6745 * diff / med_abs_deviation
644
+
645
+ timepoints_to_discard = 0
646
+ for i in range (len (modified_z_score )):
647
+ if modified_z_score [i ] <= thresh :
648
+ break
649
+ else :
650
+ timepoints_to_discard += 1
651
+
652
+ return timepoints_to_discard
653
+
654
+
582
655
def regress_poly (degree , data , remove_mean = True , axis = - 1 ):
583
656
''' returns data with degree polynomial regressed out.
584
657
Be default it is calculated along the last axis (usu. time).
0 commit comments