Skip to content

Commit 0a5bc7a

Browse files
author
Shoshana Berleant
committed
move tsnr back into misc
1 parent ee726a1 commit 0a5bc7a

File tree

3 files changed

+103
-126
lines changed

3 files changed

+103
-126
lines changed

nipype/algorithms/compcor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
BaseInterface, traits, File)
33
from ..pipeline import engine as pe
44
from ..interfaces.utility import IdentityInterface
5-
from .tsnr import regress_poly
5+
from .misc import regress_poly
66

77
import nibabel as nb
88
import numpy as np

nipype/algorithms/misc.py

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,6 @@
4040
DynamicTraitedSpec, Undefined)
4141
from nipype.utils.filemanip import fname_presuffix, split_filename
4242

43-
# for backwards compatibility
44-
from nipype.algorithms.tsnr import TSNR
45-
4643
iflogger = logging.getLogger('interface')
4744

4845

@@ -263,6 +260,108 @@ def _list_outputs(self):
263260
outputs['nifti_file'] = self._gen_output_file_name()
264261
return outputs
265262

263+
class TSNRInputSpec(BaseInterfaceInputSpec):
264+
in_file = InputMultiPath(File(exists=True), mandatory=True,
265+
desc='realigned 4D file or a list of 3D files')
266+
regress_poly = traits.Range(low=1, desc='Remove polynomials')
267+
tsnr_file = File('tsnr.nii.gz', usedefault=True, hash_files=False,
268+
desc='output tSNR file')
269+
mean_file = File('mean.nii.gz', usedefault=True, hash_files=False,
270+
desc='output mean file')
271+
stddev_file = File('stdev.nii.gz', usedefault=True, hash_files=False,
272+
desc='output tSNR file')
273+
detrended_file = File('detrend.nii.gz', usedefault=True, hash_files=False,
274+
desc='input file after detrending')
275+
276+
277+
class TSNROutputSpec(TraitedSpec):
278+
tsnr_file = File(exists=True, desc='tsnr image file')
279+
mean_file = File(exists=True, desc='mean image file')
280+
stddev_file = File(exists=True, desc='std dev image file')
281+
detrended_file = File(desc='detrended input file')
282+
283+
284+
class TSNR(BaseInterface):
285+
"""Computes the time-course SNR for a time series
286+
287+
Typically you want to run this on a realigned time-series.
288+
289+
Example
290+
-------
291+
292+
>>> tsnr = TSNR()
293+
>>> tsnr.inputs.in_file = 'functional.nii'
294+
>>> res = tsnr.run() # doctest: +SKIP
295+
296+
"""
297+
input_spec = TSNRInputSpec
298+
output_spec = TSNROutputSpec
299+
300+
def _run_interface(self, runtime):
301+
img = nb.load(self.inputs.in_file[0])
302+
header = img.header.copy()
303+
vollist = [nb.load(filename) for filename in self.inputs.in_file]
304+
data = np.concatenate([vol.get_data().reshape(
305+
vol.get_shape()[:3] + (-1,)) for vol in vollist], axis=3)
306+
data = np.nan_to_num(data)
307+
308+
if data.dtype.kind == 'i':
309+
header.set_data_dtype(np.float32)
310+
data = data.astype(np.float32)
311+
312+
if isdefined(self.inputs.regress_poly):
313+
data = regress_poly(self.inputs.regress_poly, data)
314+
img = nb.Nifti1Image(data, img.get_affine(), header)
315+
nb.save(img, op.abspath(self.inputs.detrended_file))
316+
317+
meanimg = np.mean(data, axis=3)
318+
stddevimg = np.std(data, axis=3)
319+
tsnr = np.zeros_like(meanimg)
320+
tsnr[stddevimg > 1.e-3] = meanimg[stddevimg > 1.e-3] / stddevimg[stddevimg > 1.e-3]
321+
img = nb.Nifti1Image(tsnr, img.get_affine(), header)
322+
nb.save(img, op.abspath(self.inputs.tsnr_file))
323+
img = nb.Nifti1Image(meanimg, img.get_affine(), header)
324+
nb.save(img, op.abspath(self.inputs.mean_file))
325+
img = nb.Nifti1Image(stddevimg, img.get_affine(), header)
326+
nb.save(img, op.abspath(self.inputs.stddev_file))
327+
return runtime
328+
329+
def _list_outputs(self):
330+
outputs = self._outputs().get()
331+
for k in ['tsnr_file', 'mean_file', 'stddev_file']:
332+
outputs[k] = op.abspath(getattr(self.inputs, k))
333+
334+
if isdefined(self.inputs.regress_poly):
335+
outputs['detrended_file'] = op.abspath(self.inputs.detrended_file)
336+
return outputs
337+
338+
def regress_poly(degree, data):
339+
''' returns data with degree polynomial regressed out.
340+
The last dimension (i.e. data.shape[-1]) should be time.
341+
'''
342+
datashape = data.shape
343+
timepoints = datashape[-1]
344+
345+
# Rearrange all voxel-wise time-series in rows
346+
data = data.reshape((-1, timepoints))
347+
348+
# Generate design matrix
349+
X = np.ones((timepoints, 1))
350+
for i in range(degree):
351+
polynomial_func = legendre(i+1)
352+
value_array = np.linspace(-1, 1, timepoints)
353+
X = np.hstack((X, polynomial_func(value_array)[:, np.newaxis]))
354+
355+
# Calculate coefficients
356+
betas = np.linalg.pinv(X).dot(data.T)
357+
358+
# Estimation
359+
datahat = X[:, 1:].dot(betas[1:, ...]).T
360+
regressed_data = data - datahat
361+
362+
# Back to original shape
363+
return regressed_data.reshape(datashape)
364+
266365
class GunzipInputSpec(BaseInterfaceInputSpec):
267366
in_file = File(exists=True, mandatory=True)
268367

nipype/algorithms/tsnr.py

Lines changed: 0 additions & 122 deletions
This file was deleted.

0 commit comments

Comments
 (0)