# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-# vi: set ft=python sts=4 ts=4 sw=4 et:## Copyright 2021 The NiPreps Developers <nipreps@gmail.com>## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at## http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.## We support and encourage derived works from this project, please read# about our expectations at## https://www.nipreps.org/community/licensing/#"""Utilities for BOLD fMRI imaging."""importnibabelasnbimportnumpyasnpfromnipypeimportloggingfromnipype.interfaces.baseimport(BaseInterfaceInputSpec,File,SimpleInterface,TraitedSpec,traits,)LOGGER=logging.getLogger('nipype.interface')class_NonsteadyStatesDetectorInputSpec(BaseInterfaceInputSpec):in_file=File(exists=True,mandatory=True,desc='BOLD fMRI timeseries')nonnegative=traits.Bool(True,usedefault=True,desc='whether image voxels must be nonnegative')n_volumes=traits.Range(value=40,low=10,high=200,usedefault=True,desc='drop volumes in 4D image beyond this timepoint',)zero_dummy_masked=traits.Range(value=20,low=2,high=40,usedefault=True,desc='number of timepoints to average when the number of dummies is zero',)class_NonsteadyStatesDetectorOutputSpec(TraitedSpec):t_mask=traits.List(traits.Bool,desc='list of nonsteady-states (True) and stable (False) volumes')n_dummy=traits.Int(desc='number of volumes identified as nonsteady states')
[docs]classNonsteadyStatesDetector(SimpleInterface):"""Detect initial non-steady states in BOLD fMRI timeseries."""input_spec=_NonsteadyStatesDetectorInputSpecoutput_spec=_NonsteadyStatesDetectorOutputSpecdef_run_interface(self,runtime):img=nb.load(self.inputs.in_file)ntotal=img.shape[-1]ifimg.dataobj.ndim==4else1t_mask=np.zeros((ntotal,),dtype=bool)ifntotal==1:self._results['t_mask']=[True]self._results['n_dummy']=1returnruntimefromnipype.algorithms.confoundsimportis_outlierdata=img.get_fdata(dtype='float32')[...,:self.inputs.n_volumes]# Data can come with outliers showing very high numbers - preemptively prunedata=np.clip(data,a_min=0.0ifself.inputs.nonnegativeelsenp.percentile(data,0.2),a_max=np.percentile(data,99.8),)self._results['n_dummy']=is_outlier(np.mean(data,axis=(0,1,2)))start=0stop=self._results['n_dummy']ifstop<2:stop=min(ntotal,self.inputs.n_volumes)start=max(0,stop-self.inputs.zero_dummy_masked)t_mask[start:stop]=Trueself._results['t_mask']=t_mask.tolist()returnruntime