# Copyright (C) 2012-2023 C-PAC Developers
# This file is part of C-PAC.
# C-PAC is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
# C-PAC is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with C-PAC. If not, see <https://www.gnu.org/licenses/>.
from CPAC.pipeline.nodeblock import nodeblock
import nipype.interfaces.utility as util
from nipype.interfaces import afni, fsl
from nipype.interfaces.utility import Function
from CPAC.connectome.connectivity_matrix import create_connectome_afni, \
create_connectome_nilearn, \
get_connectome_method
from CPAC.pipeline import nipype_pipeline_engine as pe
from CPAC.utils.datasource import create_roi_mask_dataflow, \
create_spatial_map_dataflow, \
resample_func_roi
[docs]def get_voxel_timeseries(wf_name='voxel_timeseries'):
"""
Workflow to extract time series for each voxel
in the data that is present in the input mask
Parameters
----------
wf_name : string
name of the workflow
Returns
-------
wflow : workflow object
workflow object
Notes
-----
`Source <https://github.com/FCP-INDI/C-PAC/blob/master/CPAC/timeseries/timeseries_analysis.py>`_
Workflow Inputs::
inputspec.rest : string (nifti file)
path to input functional data
inputspec.output_type : string (list of boolean)
list of boolean for csv and npz file formats
input_mask.masks : string (nifti file)
path to ROI mask
Workflow Outputs::
outputspec.mask_outputs: string (1D, csv and/or npz files)
list of time series matrices stored in csv and/or
npz files.By default it outputs mean of voxels
across each time point in a afni compatible 1D file.
High Level Workflow Graph:
Example
-------
>>> import CPAC.timeseries.timeseries_analysis as t
>>> wf = t.get_voxel_timeseries()
>>> wf.inputs.inputspec.rest = '/home/data/rest.nii.gz' # doctest: +SKIP
>>> wf.inputs.input_mask.mask = '/usr/local/fsl/data/standard/MNI152_T1_2mm_brain.nii.gz' # doctest: +SKIP
>>> wf.inputs.inputspec.output_type = [True,True]
>>> wf.base_dir = './'
>>> wf.run() # doctest: +SKIP
"""
wflow = pe.Workflow(name=wf_name)
inputNode = pe.Node(util.IdentityInterface(fields=['rest',
'output_type']),
name='inputspec')
inputNode_mask = pe.Node(util.IdentityInterface(fields=['mask']),
name='input_mask')
outputNode = pe.Node(util.IdentityInterface(fields=['mask_outputs']),
name='outputspec')
timeseries_voxel = pe.Node(util.Function(input_names=['data_file',
'template'],
output_names=['oneD_file'],
function=gen_voxel_timeseries),
name='timeseries_voxel')
wflow.connect(inputNode, 'rest',
timeseries_voxel, 'data_file')
#wflow.connect(inputNode, 'output_type',
# timeseries_voxel, 'output_type')
wflow.connect(inputNode_mask, 'mask',
timeseries_voxel, 'template')
wflow.connect(timeseries_voxel, 'oneD_file',
outputNode, 'mask_outputs')
return wflow
def clean_roi_csv(roi_csv):
"""Remove the file path comments from every other row of the output of
AFNI's 3dROIstats.
3dROIstats has a -nobriklab and a -quiet option, but neither remove the
file path comments while retaining the ROI label header, which is needed.
If there are no file path comments to remove, this function simply
passes the original file as output, instead of unnecessarily opening and
re-writing it.
Parameters
----------
roi_csv : str
path to CSV
Returns
-------
roi_array : numpy.ndarray
edited_roi_csv: str
path to CSV
"""
import os
import pandas as pd
import numpy as np
with open(roi_csv, 'r') as f:
csv_lines = f.readlines()
# flag whether to re-write
modified = False
edited_lines = []
for line in csv_lines:
line = line.replace('\t\t\t', '')
line = line.replace('\t\t', '')
line = line.replace('\t', ',')
line = line.replace('#,', '#')
if '#' in line:
if '/' in line and '.' in line:
modified = True
continue
if 'Sub-brick' in line:
modified = True
continue
edited_lines.append(line)
if modified:
edited_roi_csv = os.path.join(os.getcwd(), os.path.basename(roi_csv))
with open(edited_roi_csv, 'wt') as f:
for line in edited_lines:
f.write(line)
edited_roi_csv = edited_roi_csv
else:
edited_roi_csv = roi_csv
data = pd.read_csv(edited_roi_csv, sep=',', header=1)
data = data.dropna(axis=1)
roi_array = np.transpose(data.values)
return roi_array, edited_roi_csv
def write_roi_npz(roi_csv, out_type=None):
roi_npz = None
roi_outputs = [roi_csv[0]]
if not out_type:
return roi_outputs
elif out_type[1]:
np_roi_data = genfromtxt(roi_csv[0], delimiter=',')
roi_npz = os.path.join(os.getcwd(), 'roi_stats.npz')
with open(roi_npz, 'wb') as f:
np.savez(f, np_roi_data)
roi_outputs.append(roi_npz)
return roi_outputs
[docs]def get_roi_timeseries(wf_name='roi_timeseries'):
"""
Workflow to extract timeseries for each node in the ROI mask.
For each node, mean across all the timepoint is calculated and stored
in csv and npz format.
Parameters
----------
wf_name : string
name of the workflow
Returns
-------
wflow : workflow object
workflow object
Notes
-----
`Source <https://github.com/FCP-INDI/C-PAC/blob/master/CPAC/timeseries/timeseries_analysis.py>`_
Workflow Inputs::
inputspec.rest : string (nifti file)
path to input functional data
inputspec.output_type : string (list of boolean)
list of boolean for csv and npz file formats
input_roi.roi : string (nifti file)
path to ROI mask
Workflow Outputs::
outputspec.roi_ts : numpy array
Voxel time series stored in numpy array, which is used to create ndmg graphs.
outputspec.roi_outputs : string (list of files)
Voxel time series stored in 1D (column wise timeseries for each node),
csv and/or npz files. By default it outputs timeseries in a 1D file.
The 1D file is compatible with afni interfaces.
Example
-------
>>> import CPAC.timeseries.timeseries_analysis as t
>>> wf = t.get_roi_timeseries()
>>> wf.inputs.inputspec.rest = '/home/data/rest.nii.gz' # doctest: +SKIP
>>> wf.inputs.input_roi.roi = '/usr/local/fsl/data/atlases/HarvardOxford/HarvardOxford-cort-maxprob-thr0-2mm.nii.gz' # doctest: +SKIP
>>> wf.inputs.inputspec.output_type = [True,True] # doctest: +SKIP
>>> wf.base_dir = './'
>>> wf.run() # doctest: +SKIP
"""
wflow = pe.Workflow(name=wf_name)
inputNode = pe.Node(util.IdentityInterface(fields=['rest']),
name='inputspec')
inputnode_roi = pe.Node(util.IdentityInterface(fields=['roi']),
name='input_roi')
outputNode = pe.Node(util.IdentityInterface(fields=['roi_ts',
'roi_csv']),
name='outputspec')
timeseries_roi = pe.Node(interface=afni.ROIStats(),
name='3dROIstats',
mem_gb=0.4,
mem_x=(756789500459879 / 37778931862957161709568,
'in_file'))
timeseries_roi.inputs.quiet = False
timeseries_roi.inputs.args = "-1Dformat"
# TODO: add -mask_f2short for float parcellation mask
# if parcellation mask has float values
# timeseries_roi.inputs.mask_f2short = True
wflow.connect(inputNode, 'rest',
timeseries_roi, 'in_file')
wflow.connect(inputnode_roi, 'roi',
timeseries_roi, 'mask_file')
clean_csv_imports = ['import os']
clean_csv = pe.Node(util.Function(input_names=['roi_csv'],
output_names=['roi_array',
'edited_roi_csv'],
function=clean_roi_csv,
imports=clean_csv_imports),
name='clean_roi_csv')
wflow.connect(timeseries_roi, 'out_file', clean_csv, 'roi_csv')
wflow.connect(clean_csv, 'roi_array', outputNode, 'roi_ts')
wflow.connect(clean_csv, 'edited_roi_csv', outputNode, 'roi_csv')
#write_npz_imports = ['import os', 'import numpy as np',
# 'from numpy import genfromtxt']
#write_npz = pe.Node(util.Function(input_names=['roi_csv', 'out_type'],
# output_names=['roi_output_npz'],
# function=write_roi_npz,
# imports=write_npz_imports),
# name='write_roi_npz')
#wflow.connect(clean_csv, 'edited_roi_csv', write_npz, 'roi_csv')
#wflow.connect(inputNode, 'output_type', write_npz, 'out_type')
#wflow.connect(write_npz, 'roi_output_npz', outputNode, 'roi_outputs')
return wflow
[docs]def get_spatial_map_timeseries(wf_name='spatial_map_timeseries'):
"""
Workflow to regress each provided spatial
map to the subjects functional 4D file in order
to return a timeseries for each of the maps
Parameters
----------
wf_name : string
name of the workflow
Returns
-------
wflow : workflow object
workflow object
Notes
-----
`Source <https://github.com/FCP-INDI/C-PAC/blob/master/CPAC/timeseries/timeseries_analysis.py>`_
Workflow Inputs::
inputspec.subject_rest : string (nifti file)
path to input functional data
inputspec.subject_mask : string (nifti file)
path to subject functional mask
inputspec.spatial_map : string (nifti file)
path to Spatial Maps
inputspec.demean : Boolean
control whether to demean model and data
Workflow Outputs::
outputspec.subject_timeseries: string (txt file)
list of time series stored in a space separated
txt file
the columns are spatial maps, rows are timepoints
Example
-------
>>> import CPAC.timeseries.timeseries_analysis as t
>>> wf = t.get_spatial_map_timeseries()
>>> wf.inputs.inputspec.subject_rest = '/home/data/rest.nii.gz' # doctest: +SKIP
>>> wf.inputs.inputspec.subject_mask = '/home/data/rest_mask.nii.gz' # doctest: +SKIP
>>> wf.inputs.inputspec.ICA_map = '/home/data/spatialmaps/spatial_map.nii.gz' # doctest: +SKIP
>>> wf.inputs.inputspec.demean = True
>>> wf.base_dir = './'
>>> wf.run() # doctest: +SKIP
"""
wflow = pe.Workflow(name=wf_name)
inputNode = pe.Node(util.IdentityInterface
(fields=['subject_rest',
'subject_mask',
'spatial_map',
'demean']),
name='inputspec')
outputNode = pe.Node(util.IdentityInterface(
fields=['subject_timeseries']),
name='outputspec')
spatialReg = pe.Node(interface=fsl.GLM(),
name='spatial_regression',
mem_gb=0.2,
mem_x=(1708448960473801 / 302231454903657293676544,
'in_file'))
spatialReg.inputs.out_file = 'spatial_map_timeseries.txt'
wflow.connect(inputNode, 'subject_rest', spatialReg, 'in_file')
wflow.connect(inputNode, 'subject_mask', spatialReg, 'mask')
wflow.connect(inputNode, 'spatial_map', spatialReg, 'design')
wflow.connect(inputNode, 'demean', spatialReg, 'demean')
wflow.connect(spatialReg, 'out_file', outputNode, 'subject_timeseries')
return wflow
[docs]def get_vertices_timeseries(wf_name='vertices_timeseries'):
"""
Workflow to get vertices time series from a FreeSurfer surface file
Parameters
----------
wf_name : string
name of the workflow
Returns
-------
wflow : workflow object
workflow object
Notes
-----
`Source <https://github.com/FCP-INDI/C-PAC/blob/master/CPAC/timeseries/timeseries_analysis.py>`_
Workflow Inputs::
inputspec.lh_surface_file : string (nifti file)
left hemishpere surface file
inputspec.rh_surface_file : string (nifti file)
right hemisphere surface file
Workflow Outputs::
outputspec.surface_outputs: string (csv and/or npz files)
list of timeseries matrices stored in csv and/or
npz files
Example
-------
>>> import CPAC.timeseries.timeseries_analysis as t
>>> wf = t.get_vertices_timeseries()
>>> wf.inputs.inputspec.lh_surface_file = '/home/data/outputs/SurfaceRegistration/lh_surface_file.nii.gz' # doctest: +SKIP
>>> wf.inputs.inputspec.rh_surface_file = '/home/data/outputs/SurfaceRegistration/rh_surface_file.nii.gz' # doctest: +SKIP
>>> wf.base_dir = './'
>>> wf.run() # doctest: +SKIP
"""
wflow = pe.Workflow(name=wf_name)
inputNode = pe.Node(util.IdentityInterface(fields=['lh_surface_file',
'rh_surface_file']),
name='inputspec')
timeseries_surface = pe.Node(util.Function(input_names=['rh_surface_file',
'lh_surface_file'],
output_names=['out_file'],
function=gen_vertices_timeseries),
name='timeseries_surface')
outputNode = pe.Node(util.IdentityInterface(fields=['surface_outputs']),
name='outputspec')
wflow.connect(inputNode, 'rh_surface_file',
timeseries_surface, 'rh_surface_file')
wflow.connect(inputNode, 'lh_surface_file',
timeseries_surface, 'lh_surface_file')
wflow.connect(timeseries_surface, 'out_file',
outputNode, 'surface_outputs')
return wflow
def get_normalized_moments(wf_name='normalized_moments'):
"""
Workflow to calculate the normalized moments for skewedness calculations
Parameters
----------
wf_name : string
name of the workflow
Returns
-------
wflow : workflow object
workflow object
Notes
-----
`Source <https://github.com/FCP-INDI/C-PAC/blob/master/CPAC/timeseries/timeseries_analysis.py>`_
Workflow Inputs::
inputspec.spatial_timeseries : string (nifti file)
spatial map timeseries
Workflow Outputs::
outputspec.moments: list
list of moment values
Example
-------
>>> import CPAC.timeseries.timeseries_analysis as t
>>> wf = t.get_normalized_moments() # doctest: +SKIP
>>> wf.inputs.inputspec.spatial_timeseries = '/home/data/outputs/SurfaceRegistration/lh_surface_file.nii.gz' # doctest: +SKIP
>>> wf.base_dir = './' # doctest: +SKIP
>>> wf.run() # doctest: +SKIP
"""
wflow = pe.Workflow(name=wf_name)
inputNode = pe.Node(util.IdentityInterface(fields=['spatial_timeseries']),
name='inputspec')
# calculate normalized moments
# output of this node is a list, 'moments'
norm_moments = pe.Node(util.CalculateNormalizedMoments(moment='3'),
name='norm_moments')
outputNode = pe.Node(util.IdentityInterface(fields=['moments_outputs']),
name='outputspec')
wflow.connect(inputNode, 'spatial_timeseries',
norm_moments, 'timeseries_file')
wflow.connect(norm_moments, 'moments', outputNode, 'moments_outputs')
return wflow
[docs]def gen_roi_timeseries(data_file, template, output_type):
"""
Method to extract mean of voxel across
all timepoints for each node in roi mask
Parameters
----------
data_file : string
path to input functional data
template : string
path to input roi mask in functional native space
output_type : list
list of two boolean values suggesting
the output types - numpy npz file and csv
format
Returns
-------
out_list : list
list of 1D file, txt file, csv file and/or npz file containing
mean timeseries for each scan corresponding
to each node in roi mask
Raises
------
Exception
"""
import nibabel as nib
import csv
import numpy as np
import os
import shutil
unit_data = nib.load(template).get_fdata()
# Cast as rounded-up integer
unit_data = np.int64(np.ceil(unit_data))
datafile = nib.load(data_file)
img_data = datafile.get_fdata()
vol = img_data.shape[3]
if unit_data.shape != img_data.shape[:3]:
raise Exception('\n\n[!] CPAC says: Invalid Shape Error.'
'Please check the voxel dimensions. '
'Data and roi should have the same shape.\n\n')
nodes = np.unique(unit_data).tolist()
sorted_list = []
node_dict = {}
out_list = []
# extracting filename from input template
tmp_file = os.path.splitext(
os.path.basename(template))[0]
tmp_file = os.path.splitext(tmp_file)[0]
oneD_file = os.path.abspath('roi_' + tmp_file + '.1D')
txt_file = os.path.abspath('roi_' + tmp_file + '.txt')
csv_file = os.path.abspath('roi_' + tmp_file + '.csv')
numpy_file = os.path.abspath('roi_' + tmp_file + '.npz')
nodes.sort()
for n in nodes:
if n > 0:
node_array = img_data[unit_data == n]
node_str = 'node_{0}'.format(n)
avg = np.mean(node_array, axis=0)
avg = np.round(avg, 6)
list1 = [n] + avg.tolist()
sorted_list.append(list1)
node_dict[node_str] = avg.tolist()
# writing to 1Dfile
print("writing 1D file..")
f = open(oneD_file, 'w')
writer = csv.writer(f, delimiter=',')
value_list = []
new_keys = sorted([
int(float(key.split('node_')[1])) for key in node_dict
])
roi_number_list = [str(n) for n in new_keys]
roi_number_str = []
for number in roi_number_list:
roi_number_str.append("#" + number)
for key in new_keys:
value_list.append(str('{0}\n'.format(node_dict['node_{0}'.format(key)])))
column_list = list(zip(*value_list))
writer.writerow(roi_number_str)
for column in column_list:
writer.writerow(list(column))
f.close()
out_list.append(oneD_file)
# copy the 1D contents to txt file
shutil.copy(oneD_file, txt_file)
out_list.append(txt_file)
# if csv is required
'''
if output_type[0]:
print("writing csv file..")
f = open(csv_file, 'wt')
writer = csv.writer(f, delimiter=',', quoting=csv.QUOTE_MINIMAL)
headers = ['node/volume'] + np.arange(vol).tolist()
writer.writerow(headers)
writer.writerows(sorted_list)
f.close()
out_list.append(csv_file)
# if npz file is required
if output_type[1]:
print("writing npz file..")
np.savez(numpy_file, roi_data=value_list, roi_numbers=roi_number_list)
out_list.append(numpy_file)
return out_list
'''
return oneD_file
[docs]def gen_voxel_timeseries(data_file, template):
"""
Method to extract timeseries for each voxel
in the data that is present in the input mask
Parameters
----------
datafile : string (nifti file)
path to input functional data
template : string (nifti file)
path to input mask in functional native space
output_type : list
list of two boolean values suggesting
the output types - numpy npz file and csv
format
Returns
-------
out_list : list of files
Based on ouput_type options method returns a list containing
path to npz and csv file having timeseries of each voxel in
the data that is present in the input mask.The row header
corresponds to voxel's xyz cordinates and column headers corresponds
to the volume index in the csv. By default it outputs afni compatible
1D file with mean of timeseries of voxels across timepoints.
Raises
------
Exception
"""
import nibabel as nib
import numpy as np
import csv
import os
unit = nib.load(template)
unit_data = unit.get_fdata()
datafile = nib.load(data_file)
img_data = datafile.get_fdata()
header_data = datafile.header
qform = header_data.get_qform()
sorted_list = []
vol_dict = {}
out_list = []
tmp_file = os.path.splitext(
os.path.basename(template))[0]
tmp_file = os.path.splitext(tmp_file)[0]
oneD_file = os.path.abspath('mask_' + tmp_file + '.1D')
f = open(oneD_file, 'wt')
x, y, z = unit_data.shape
node_array = img_data[unit_data != 0]
node_array = node_array.T
time_points = node_array.shape[0]
for t in range(0, time_points):
string = 'vol {0}'.format(t)
vol_dict[string] = node_array[t]
f.write(str(np.round(np.mean(node_array[t]), 6)))
f.write('\n')
val = node_array[t].tolist()
val.insert(0, t)
sorted_list.append(val)
f.close()
csv_file = os.path.abspath('mask_' + tmp_file + '.csv')
f = open(csv_file, 'wt')
writer = csv.writer(f, delimiter=str(','),
quoting=csv.QUOTE_MINIMAL)
one = np.array([1])
headers = ['volume/xyz']
cordinates = np.argwhere(unit_data != 0)
for val in range(len(cordinates)):
ijk_mat = np.concatenate([cordinates[val], one])
ijk_mat = ijk_mat.T
product = np.dot(qform, ijk_mat)
val = tuple(product.tolist()[0:3])
headers.append(val)
writer.writerow(headers)
writer.writerows(sorted_list)
f.close()
#if output_type[1]:
# numpy_file = os.path.abspath('mask_' + tmp_file + '.npz')
# np.savez(numpy_file, **dict(vol_dict))
# out_list.append(numpy_file)
return oneD_file
[docs]def gen_vertices_timeseries(rh_surface_file,
lh_surface_file):
"""
Method to extract timeseries from vertices
of a freesurfer surface file
Parameters
----------
rh_surface_file : string (mgz/mgh file)
left hemisphere FreeSurfer surface file
lh_surface_file : string (mgz/mgh file)
right hemisphere FreeSurfer surface file
Returns
-------
out_list : string (list of file)
list of vertices timeseries csv files
"""
import gradunwarp
import numpy as np
import os
out_list = []
rh_file = os.path.splitext(
os.path.basename(rh_surface_file))[0] + '_rh.csv'
mghobj1 = gradunwarp.mgh.MGH()
mghobj1.load(rh_surface_file)
vol = mghobj1.vol
(x, y) = vol.shape
# print "rh shape", x, y
np.savetxt(rh_file, vol, delimiter='\t')
out_list.append(rh_file)
lh_file = os.path.splitext(os.path.basename(lh_surface_file))[0] + '_lh.csv'
mghobj2 = gradunwarp.mgh.MGH()
mghobj2.load(lh_surface_file)
vol = mghobj2.vol
(x, y) = vol.shape
# print "lh shape", x, y
np.savetxt(lh_file,
vol,
delimiter=',')
out_list.append(lh_file)
return out_list
def resample_function() -> 'Function':
"""
Returns a Function interface for
`CPAC.utils.datasource.resample_func_roi`
Returns
-------
Function
"""
return Function(input_names=['in_func', 'in_roi', 'realignment',
'identity_matrix'],
output_names=['out_func', 'out_roi'],
function=resample_func_roi, as_module=True)
@nodeblock(
name="timeseries_extraction_AVG",
config=["timeseries_extraction"],
switch=["run"],
inputs=["space-template_desc-preproc_bold",
"space-template_desc-bold_mask"],
outputs=[
"space-template_desc-Mean_timeseries",
"space-template_desc-ndmg_correlations",
"atlas_name",
"space-template_desc-PearsonAfni_correlations",
"space-template_desc-PartialAfni_correlations",
"space-template_desc-PearsonNilearn_correlations",
"space-template_desc-PartialNilearn_correlations",
],
)
def timeseries_extraction_AVG(wf, cfg, strat_pool, pipe_num, opt=None):
resample_functional_roi = pe.Node(resample_function(),
name='resample_functional_roi_'
f'{pipe_num}')
realignment = cfg.timeseries_extraction['realignment']
resample_functional_roi.inputs.realignment = realignment
resample_functional_roi.inputs.identity_matrix = \
cfg.registration_workflows['functional_registration'][
'func_registration_to_template']['FNIRT_pipelines']['identity_matrix']
roi_dataflow = create_roi_mask_dataflow(
cfg.timeseries_extraction['tse_atlases']['Avg'],
f'roi_dataflow_{pipe_num}')
roi_dataflow.inputs.inputspec.set(
creds_path=cfg.pipeline_setup['input_creds_path'],
dl_dir=cfg.pipeline_setup['working_directory']['path']
)
roi_timeseries = get_roi_timeseries(f'roi_timeseries_{pipe_num}')
#roi_timeseries.inputs.inputspec.output_type = cfg.timeseries_extraction[
# 'roi_tse_outputs']
node, out = strat_pool.get_data("space-template_desc-preproc_bold")
wf.connect(node, out, resample_functional_roi, 'in_func')
wf.connect(roi_dataflow, 'outputspec.out_file',
resample_functional_roi, 'in_roi')
# connect it to the roi_timeseries
# workflow.connect(roi_dataflow, 'outputspec.out_file',
# roi_timeseries, 'input_roi.roi')
wf.connect(resample_functional_roi, 'out_roi',
roi_timeseries, 'input_roi.roi')
wf.connect(resample_functional_roi, 'out_func',
roi_timeseries, 'inputspec.rest')
# create the graphs:
# - connectivity matrix
matrix_outputs = {}
for cm_measure in cfg['timeseries_extraction', 'connectivity_matrix',
'measure']:
for cm_tool in [tool for tool in cfg['timeseries_extraction',
'connectivity_matrix', 'using'] if tool != 'ndmg']:
implementation = get_connectome_method(cm_measure, cm_tool)
if implementation is NotImplemented:
continue
if cm_tool == 'Nilearn':
timeseries_correlation = create_connectome_nilearn(
name=f'connectomeNilearn{cm_measure}_{pipe_num}'
)
elif cm_tool == "AFNI":
timeseries_correlation = create_connectome_afni(
name=f'connectomeAfni{cm_measure}_{pipe_num}',
method=cm_measure,
pipe_num=pipe_num
)
brain_mask_node, brain_mask_out = strat_pool.get_data([
'space-template_desc-bold_mask'])
if 'func_to_ROI' in realignment:
resample_brain_mask_roi = pe.Node(
resample_function(),
name=f'resample_brain_mask_roi_{pipe_num}')
resample_brain_mask_roi.inputs.realignment = realignment
resample_brain_mask_roi.inputs.identity_matrix = (
cfg.registration_workflows['functional_registration'][
'func_registration_to_template'
]['FNIRT_pipelines']['identity_matrix'])
wf.connect([
(brain_mask_node, resample_brain_mask_roi, [
(brain_mask_out, 'in_func')]),
(roi_dataflow, resample_brain_mask_roi, [
('outputspec.out_file', 'in_roi')]),
(resample_brain_mask_roi, timeseries_correlation, [
('out_func', 'inputspec.mask')])])
else:
wf.connect(brain_mask_node, brain_mask_out,
timeseries_correlation, 'inputspec.mask')
timeseries_correlation.inputs.inputspec.method = cm_measure
wf.connect([
(roi_dataflow, timeseries_correlation, [
('outputspec.out_name', 'inputspec.atlas_name')]),
(resample_functional_roi, timeseries_correlation, [
('out_roi', 'inputspec.in_rois'),
('out_func', 'inputspec.in_file')])])
output_desc = ''.join(term.lower().capitalize() for term in [
cm_measure, cm_tool])
matrix_outputs[f'space-template_desc-{output_desc}_correlations'
] = (timeseries_correlation, 'outputspec.out_file')
outputs = {
'space-template_desc-Mean_timeseries': (
roi_timeseries, 'outputspec.roi_csv'),
'atlas_name': (roi_dataflow, 'outputspec.out_name'),
**matrix_outputs
}
# - NDMG
if 'ndmg' in cfg['timeseries_extraction', 'connectivity_matrix', 'using']:
# pylint: disable=import-outside-toplevel
from CPAC.utils.ndmg_utils import ndmg_create_graphs
ndmg_graph_imports = ['import os',
'from CPAC.utils.ndmg_utils import graph']
ndmg_graph = pe.Node(Function(
input_names=['ts', 'labels'],
output_names=['out_file'],
function=ndmg_create_graphs,
imports=ndmg_graph_imports,
as_module=True
), name=f'ndmg_graphs_{pipe_num}',
mem_gb=0.664,
mem_x=(1928411764134803 / 302231454903657293676544, 'ts'))
wf.connect(roi_timeseries, 'outputspec.roi_ts', ndmg_graph, 'ts')
wf.connect(roi_dataflow, 'outputspec.out_file', ndmg_graph, 'labels')
outputs['space-template_desc-ndmg_correlations'
] = (ndmg_graph, 'out_file')
return (wf, outputs)
@nodeblock(
name="timeseries_extraction_Voxel",
config=["timeseries_extraction"],
switch=["run"],
inputs=["space-template_desc-preproc_bold"],
outputs=["desc-Voxel_timeseries", "atlas_name"],
)
def timeseries_extraction_Voxel(wf, cfg, strat_pool, pipe_num, opt=None):
resample_functional_to_mask = pe.Node(resample_function(),
name='resample_functional_to_mask_'
f'{pipe_num}')
resample_functional_to_mask.inputs.realignment = cfg.timeseries_extraction[
'realignment']
resample_functional_to_mask.inputs.identity_matrix = \
cfg.registration_workflows['functional_registration'][
'func_registration_to_template']['FNIRT_pipelines']['identity_matrix']
mask_dataflow = create_roi_mask_dataflow(cfg.timeseries_extraction[
'tse_atlases']['Voxel'],
f'mask_dataflow_{pipe_num}')
voxel_timeseries = get_voxel_timeseries(
f'voxel_timeseries_{pipe_num}')
#voxel_timeseries.inputs.inputspec.output_type = cfg.timeseries_extraction[
# 'roi_tse_outputs']
node, out = strat_pool.get_data("space-template_desc-preproc_bold")
# resample the input functional file to mask
wf.connect(node, out,
resample_functional_to_mask, 'in_func')
wf.connect(mask_dataflow, 'outputspec.out_file',
resample_functional_to_mask, 'in_roi')
# connect it to the voxel_timeseries
wf.connect(resample_functional_to_mask, 'out_roi',
voxel_timeseries, 'input_mask.mask')
wf.connect(resample_functional_to_mask, 'out_func',
voxel_timeseries, 'inputspec.rest')
outputs = {
'desc-Voxel_timeseries':
(voxel_timeseries, 'outputspec.mask_outputs'),
'atlas_name': (mask_dataflow, 'outputspec.out_name')
}
return (wf, outputs)
@nodeblock(
name="spatial_regression",
config=["timeseries_extraction"],
switch=["run"],
inputs=["space-template_desc-preproc_bold",
["space-template_desc-bold_mask",
"space-template_desc-brain_mask"]],
outputs=["desc-SpatReg_timeseries", "atlas_name"],
)
def spatial_regression(wf, cfg, strat_pool, pipe_num, opt=None):
'''Performs spatial regression, extracting the spatial map timeseries of
the given atlases.
Note: this is a standalone function for when only spatial regression is
selected for the given atlases - if dual regression is selected,
that spatial regression is performed in the dual_regression function
'''
resample_spatial_map_to_native_space = pe.Node(
interface=fsl.FLIRT(),
name=f'resample_spatial_map_to_native_space_{pipe_num}',
mem_gb=3.4,
mem_x=(5381614225492473 / 1208925819614629174706176, 'in_file'))
resample_spatial_map_to_native_space.inputs.set(
interp='nearestneighbour',
apply_xfm=True,
in_matrix_file=cfg.registration_workflows['functional_registration'][
'func_registration_to_template']['FNIRT_pipelines'][
'identity_matrix'])
spatial_map_dataflow = create_spatial_map_dataflow(
cfg.timeseries_extraction['tse_atlases']['SpatialReg'],
f'spatial_map_dataflow_{pipe_num}')
spatial_map_dataflow.inputs.inputspec.set(
creds_path=cfg.pipeline_setup['input_creds_path'],
dl_dir=cfg.pipeline_setup['working_directory']['path'])
spatial_map_timeseries = get_spatial_map_timeseries(
f'spatial_map_timeseries_{pipe_num}')
spatial_map_timeseries.inputs.inputspec.demean = True
node, out = strat_pool.get_data("space-template_desc-preproc_bold")
# resample the input functional file and functional mask
# to spatial map
wf.connect(node, out, resample_spatial_map_to_native_space, 'reference')
wf.connect(spatial_map_dataflow, 'select_spatial_map.out_file',
resample_spatial_map_to_native_space, 'in_file')
wf.connect(node, out, spatial_map_timeseries, 'inputspec.subject_rest')
# connect it to the spatial_map_timeseries
wf.connect(resample_spatial_map_to_native_space, 'out_file',
spatial_map_timeseries, 'inputspec.spatial_map')
node, out = strat_pool.get_data(['space-template_desc-bold_mask', 'space-template_desc-brain_mask'])
wf.connect(node, out, spatial_map_timeseries, 'inputspec.subject_mask')
# 'atlas_name' will be an iterable and will carry through
outputs = {
'desc-SpatReg_timeseries':
(spatial_map_timeseries, 'outputspec.subject_timeseries'),
'atlas_name': (spatial_map_dataflow, 'select_spatial_map.out_name')
}
return (wf, outputs)