# 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."""importnumpyasnpimportnibabelasnbfromnipypeimportloggingfromnipype.interfaces.baseimport(traits,TraitedSpec,BaseInterfaceInputSpec,SimpleInterface,File,)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