# Copyright (C) 2009-2021, Ecole Polytechnique Federale de Lausanne (EPFL) and
# Hospital Center and University of Lausanne (UNIL-CHUV), Switzerland, and CMP3 contributors
# All rights reserved.
#
# This software is distributed under the open-source license Modified BSD.
"""Anatomical pipeline Class definition."""
import datetime
import os
import glob
import shutil
import nipype.pipeline.engine as pe
import nipype.interfaces.utility as util
import nipype.interfaces.io as nio
from nipype import config, logging
from traits.api import *
# Own import
import cmp.pipelines.common as cmp_common
from cmp.stages.segmentation.segmentation import SegmentationStage
from cmp.stages.parcellation.parcellation import ParcellationStage
[docs]class Global_Configuration(HasTraits):
"""Global pipeline configurations.
Attributes
----------
process_type: 'fMRI'
Processing pipeline type
subjects : traits.List
List of subjects ID (in the form ``sub-XX``)
subject : traits.Str
Subject to be processed (in the form ``sub-XX``)
subject_session : traits.Str
Subject session to be processed (in the form ``ses-YY``)
"""
process_type = Str('anatomical')
subjects = List(trait=Str)
subject = Str
subject_session = Str
[docs]class AnatomicalPipeline(cmp_common.Pipeline):
"""Class that extends a :class:`Pipeline` and represents the processing pipeline for structural MRI.
It is composed of the segmentation stage that performs FreeSurfer recon-all
and the parcellation stage that creates the Lausanne brain parcellations.
See Also
--------
cmp.stages.segmentation.segmentation.SegmentationStage
cmp.stages.parcellation.parcellation.ParcellationStage
"""
now = datetime.datetime.now().strftime("%Y%m%d_%H%M")
pipeline_name = Str("anatomical_pipeline")
# input_folders = ['DSI','DTI','HARDI','T1','T2']
input_folders = ['anat']
process_type = Str
diffusion_imaging_model = Str
parcellation_scheme = Str('Lausanne2008')
atlas_info = Dict()
# subject = Str
subject_directory = Directory
derivatives_directory = Directory
# ,'MRTrixConnectome']
ordered_stage_list = ['Segmentation', 'Parcellation']
custom_last_stage = Enum('Parcellation', ['Segmentation', 'Parcellation'])
global_conf = Global_Configuration()
config_file = Str
flow = Instance(pe.Workflow)
def __init__(self, project_info):
"""Constructor of an `AnatomicalPipeline` object.
Parameters
----------
project_info: cmp.project.CMP_Project_Info
Instance of `CMP_Project_Info` object.
See Also
--------
cmp.project.CMP_Project_Info
"""
# super(Pipeline, self).__init__(project_info)
# self.last_date_processed = project_info.anat_last_date_processed
self.global_conf.subjects = project_info.subjects
self.global_conf.subject = self.subject
if len(project_info.subject_sessions) > 0:
self.global_conf.subject_session = project_info.subject_session
self.subject_directory = os.path.join(project_info.base_directory,
project_info.subject,
project_info.subject_session)
else:
self.global_conf.subject_session = ''
self.subject_directory = os.path.join(project_info.base_directory, project_info.subject)
self.derivatives_directory = os.path.abspath(project_info.output_directory)
self.output_directory = os.path.abspath(project_info.output_directory)
self.stages = {'Segmentation': SegmentationStage(bids_dir=project_info.base_directory,
output_dir=self.output_directory),
'Parcellation': ParcellationStage(pipeline_mode="Diffusion",
bids_dir=project_info.base_directory,
output_dir=self.output_directory)}
cmp_common.Pipeline.__init__(self, project_info)
self.subject = project_info.subject
self.stages['Segmentation'].config.on_trait_change(self.update_parcellation, 'seg_tool')
self.stages['Parcellation'].config.on_trait_change(self.update_segmentation, 'parcellation_scheme')
self.stages['Parcellation'].config.on_trait_change(self.update_parcellation_scheme, 'parcellation_scheme')
[docs] def check_config(self):
"""Check if custom white matter mask and custom atlas files specified in the configuration exist.
Returns
-------
message : string
String empty if all the checks pass, otherwise it contains the error message
"""
message = ''
if self.stages['Segmentation'].config.seg_tool == 'Custom segmentation':
if not os.path.exists(self.stages['Segmentation'].config.white_matter_mask):
message = (
'\nCustom segmentation selected but no WM mask provided.\n'
'Please provide an existing WM mask file in the Segmentation configuration '
'window.\n')
if not os.path.exists(self.stages['Parcellation'].config.atlas_nifti_file):
message = (
'\n\tCustom segmentation selected but no atlas provided.\n'
'Please specify an existing atlas file in the '
'Parcellation configuration window.\t\n')
if not os.path.exists(self.stages['Parcellation'].config.graphml_file):
message = (
'\n\tCustom segmentation selected but no graphml info provided.\n'
'Please specify an existing graphml file in the '
'Parcellation configuration window.\t\n')
return message
[docs] def update_parcellation_scheme(self):
"""Updates ``parcellation_scheme`` and ``atlas_info`` when ``parcellation_scheme`` is updated."""
self.parcellation_scheme = self.stages['Parcellation'].config.parcellation_scheme
self.atlas_info = self.stages['Parcellation'].config.atlas_info
[docs] def update_parcellation(self):
"""Update self.stages['Parcellation'].config.parcellation_scheme when ``seg_tool`` is updated."""
if self.stages['Segmentation'].config.seg_tool == "Custom segmentation":
self.stages['Parcellation'].config.parcellation_scheme = 'Custom'
else:
self.stages['Parcellation'].config.parcellation_scheme = self.stages['Parcellation'].config.pre_custom
[docs] def update_segmentation(self):
"""Update self.stages['Segmentation'].config.seg_tool when ``parcellation_scheme`` is updated."""
if self.stages['Parcellation'].config.parcellation_scheme == 'Custom':
self.stages['Segmentation'].config.seg_tool = "Custom segmentation"
else:
self.stages['Segmentation'].config.seg_tool = 'Freesurfer'
[docs] def define_custom_mapping(self, custom_last_stage):
"""Define the pipeline to be executed until a specific stages.
Not used yet by CMP3.
Parameters
----------
custom_last_stage : string
Last stage to execute. Valid values are
"Segmentation" and "Parcellation"
"""
# start by disabling all stages
for stage in self.ordered_stage_list:
self.stages[stage].enabled = False
# enable until selected one
for stage in self.ordered_stage_list:
print('Enable stage : %s' % stage)
self.stages[stage].enabled = True
if stage == custom_last_stage:
break
[docs] def check_output(self):
"""Check if outputs of an :class:`AnatomicalPipeline` are available.
Returns
-------
valid_output <Bool>
True if all outputs are found
error_message <string>
Error message if an output is not found.
"""
t1_available = False
brain_available = False
brainmask_available = False
wm_available = False
roivs_available = False
valid_output = False
subject = self.subject
if self.global_conf.subject_session == '':
anat_deriv_subject_directory = os.path.join(
self.output_directory, "cmp", self.subject, 'anat')
else:
if self.global_conf.subject_session not in subject:
anat_deriv_subject_directory = os.path.join(self.output_directory, "cmp", subject,
self.global_conf.subject_session, 'anat')
subject = "_".join((subject, self.global_conf.subject_session))
else:
anat_deriv_subject_directory = os.path.join(self.output_directory, "cmp", subject.split("_")[0],
self.global_conf.subject_session, 'anat')
T1_file = os.path.join(anat_deriv_subject_directory,
subject + '_desc-head_T1w.nii.gz')
brain_file = os.path.join(
anat_deriv_subject_directory, subject + '_desc-brain_T1w.nii.gz')
brainmask_file = os.path.join(
anat_deriv_subject_directory, subject + '_desc-brain_mask.nii.gz')
wm_mask_file = os.path.join(
anat_deriv_subject_directory, subject + '_label-WM_dseg.nii.gz')
roiv_files = glob.glob(anat_deriv_subject_directory +
"/" + subject + "_label-L2018_desc-scale*_atlas.nii.gz")
error_message = ''
if os.path.isfile(T1_file):
t1_available = True
else:
error_message = "ERROR : Missing anatomical output file %s . Please re-run the anatomical pipeline" % T1_file
print(error_message)
if os.path.isfile(brain_file):
brain_available = True
else:
error_message = "ERROR : Missing anatomical output file %s . Please re-run the anatomical pipeline" % brain_file
print(error_message)
if os.path.isfile(brainmask_file):
brainmask_available = True
else:
error_message = "ERROR : Missing anatomical output file %s . Please re-run the anatomical pipeline" % brainmask_file
print(error_message)
if os.path.isfile(wm_mask_file):
wm_available = True
else:
error_message = "Missing anatomical output file %s . Please re-run the anatomical pipeline" % wm_mask_file
print(error_message)
cnt1 = 0
cnt2 = 0
for roiv_file in roiv_files:
cnt1 = cnt1 + 1
if os.path.isfile(roiv_file):
cnt2 = cnt2 + 1
if cnt1 == cnt2:
roivs_available = True
else:
error_message = "ERROR : Missing %g/%g anatomical parcellation output files. Please re-run the anatomical pipeline" % (
cnt1 - cnt2, cnt1)
print(error_message)
if t1_available is True and brain_available is True and brainmask_available is True and wm_available is True and roivs_available is True:
print("INFO : Valid derivatives for anatomical pipeline")
valid_output = True
return valid_output, error_message
[docs] def create_pipeline_flow(self, cmp_deriv_subject_directory, nipype_deriv_subject_directory):
"""Create the pipeline workflow.
Parameters
----------
cmp_deriv_subject_directory <Directory>
Main CMP output directory of a subject
e.g. ``/output_dir/cmp/sub-XX/(ses-YY)``
nipype_deriv_subject_directory <Directory>
Intermediate Nipype output directory of a subject
e.g. ``/output_dir/nipype/sub-XX/(ses-YY)``
Returns
-------
anat_flow <nipype.pipeline.engine.Workflow>
An instance of :class:`nipype.pipeline.engine.Workflow`
"""
# subject_directory = self.subject_directory
# Data import
datasource = pe.Node(interface=nio.DataGrabber(
outfields=['T1']), name='datasource')
datasource.inputs.base_directory = cmp_deriv_subject_directory
datasource.inputs.template = '*'
datasource.inputs.raise_on_empty = False
datasource.inputs.field_template = dict(
T1='anat/' + self.subject + '_desc-cmp_T1w.nii.gz')
datasource.inputs.sort_filelist = False
# Data sinker for output
sinker = pe.Node(nio.DataSink(), name="anatomical_sinker")
sinker.inputs.base_directory = os.path.abspath(
cmp_deriv_subject_directory)
# if self.parcellation_scheme == 'Lausanne2008':
# bids_atlas_label = 'L2008'
# elif self.parcellation_scheme == 'Lausanne2018':
# bids_atlas_label = 'L2018'
# elif self.parcellation_scheme == 'NativeFreesurfer':
# bids_atlas_label = 'Desikan'
# Dataname substitutions in order to comply with BIDS derivatives specifications
if self.parcellation_scheme == 'Lausanne2008':
sinker.inputs.substitutions = [('T1.nii.gz', self.subject + '_desc-head_T1w.nii.gz'),
('brain.nii.gz', self.subject +
'_desc-brain_T1w.nii.gz'),
('brain_mask.nii.gz', self.subject +
'_desc-brain_mask.nii.gz'),
('aseg.nii.gz', self.subject +
'_desc-aseg_dseg.nii.gz'),
('csf_mask.nii.gz', self.subject +
'_label-CSF_dseg.nii.gz'),
('fsmask_1mm.nii.gz', self.subject +
'_label-WM_dseg.nii.gz'),
('gmmask.nii.gz', self.subject +
'_label-GM_dseg.nii.gz'),
('T1w_class-GM.nii.gz', self.subject +
'_label-GM_dseg.nii.gz'),
('wm_eroded.nii.gz', self.subject +
'_label-WM_desc-eroded_dseg.nii.gz'),
('csf_eroded.nii.gz', self.subject +
'_label-CSF_desc-eroded_dseg.nii.gz'),
('brain_eroded.nii.gz',
self.subject + '_label-brain_desc-eroded_dseg.nii.gz'),
('aparc+aseg.native.nii.gz', self.subject +
'_desc-aparcaseg_dseg.nii.gz'),
('aparc+aseg.Lausanne2018.native.nii.gz',
self.subject + '_desc-aparcaseg_dseg.nii.gz'),
('ROIv_Lausanne2008_scale1.nii.gz',
self.subject + '_label-L2008_desc-scale1_atlas.nii.gz'),
('ROIv_Lausanne2008_scale2.nii.gz',
self.subject + '_label-L2008_desc-scale2_atlas.nii.gz'),
('ROIv_Lausanne2008_scale3.nii.gz',
self.subject + '_label-L2008_desc-scale3_atlas.nii.gz'),
('ROIv_Lausanne2008_scale4.nii.gz',
self.subject + '_label-L2008_desc-scale4_atlas.nii.gz'),
('ROIv_Lausanne2008_scale5.nii.gz',
self.subject + '_label-L2008_desc-scale5_atlas.nii.gz'),
('ROIv_Lausanne2008_scale1_final.nii.gz',
self.subject + '_label-L2008_desc-scale1_atlas.nii.gz'),
('ROIv_Lausanne2008_scale2_final.nii.gz',
self.subject + '_label-L2008_desc-scale2_atlas.nii.gz'),
('ROIv_Lausanne2008_scale3_final.nii.gz',
self.subject + '_label-L2008_desc-scale3_atlas.nii.gz'),
('ROIv_Lausanne2008_scale4_final.nii.gz',
self.subject + '_label-L2008_desc-scale4_atlas.nii.gz'),
('ROIv_Lausanne2008_scale5_final.nii.gz',
self.subject + '_label-L2008_desc-scale5_atlas.nii.gz'),
('resolution83.graphml',
self.subject + '_label-L2008_desc-scale1_atlas.graphml'),
('resolution150.graphml',
self.subject + '_label-L2008_desc-scale2_atlas.graphml'),
('resolution258.graphml',
self.subject + '_label-L2008_desc-scale3_atlas.graphml'),
('resolution500.graphml',
self.subject + '_label-L2008_desc-scale4_atlas.graphml'),
('resolution1015.graphml',
self.subject + '_label-L2008_desc-scale5_atlas.graphml'),
('resolution83_LUT.txt',
self.subject + '_label-L2008_desc-scale1_atlas_FreeSurferColorLUT.txt'),
('resolution150_LUT.txt',
self.subject + '_label-L2008_desc-scale2_atlas_FreeSurferColorLUT.txt'),
('resolution258_LUT.txt',
self.subject + '_label-L2008_desc-scale3_atlas_FreeSurferColorLUT.txt'),
('resolution500_LUT.txt',
self.subject + '_label-L2008_desc-scale4_atlas_FreeSurferColorLUT.txt'),
('resolution1015_LUT.txt',
self.subject + '_label-L2008_desc-scale5_atlas_FreeSurferColorLUT.txt'),
(
'roi_stats_scale1.tsv', self.subject + '_label-L2008_desc-scale1_stats.tsv'),
(
'roi_stats_scale2.tsv', self.subject + '_label-L2008_desc-scale2_stats.tsv'),
(
'roi_stats_scale3.tsv', self.subject + '_label-L2008_desc-scale3_stats.tsv'),
(
'roi_stats_scale4.tsv', self.subject + '_label-L2008_desc-scale4_stats.tsv'),
(
'roi_stats_scale5.tsv', self.subject + '_label-L2008_desc-scale5_stats.tsv'),
]
elif self.parcellation_scheme == 'Lausanne2018':
sinker.inputs.substitutions = [('T1.nii.gz', self.subject + '_desc-head_T1w.nii.gz'),
('brain.nii.gz', self.subject +
'_desc-brain_T1w.nii.gz'),
('brain_mask.nii.gz', self.subject +
'_desc-brain_mask.nii.gz'),
('aseg.nii.gz', self.subject +
'_desc-aseg_dseg.nii.gz'),
('csf_mask.nii.gz', self.subject +
'_label-CSF_dseg.nii.gz'),
('fsmask_1mm.nii.gz', self.subject +
'_label-WM_dseg.nii.gz'),
('gmmask.nii.gz', self.subject +
'_label-GM_dseg.nii.gz'),
('T1w_class-GM.nii.gz', self.subject +
'_label-GM_dseg.nii.gz'),
('wm_eroded.nii.gz', self.subject +
'_label-WM_desc-eroded_dseg.nii.gz'),
('csf_eroded.nii.gz', self.subject +
'_label-CSF_desc-eroded_dseg.nii.gz'),
('brain_eroded.nii.gz',
self.subject + '_label-brain_desc-eroded_dseg.nii.gz'),
('aparc+aseg.native.nii.gz', self.subject +
'_desc-aparcaseg_dseg.nii.gz'),
('aparc+aseg.Lausanne2018.native.nii.gz',
self.subject + '_desc-aparcaseg_dseg.nii.gz'),
('ROIv_Lausanne2018_scale1.nii.gz',
self.subject + '_label-L2018_desc-scale1_atlas.nii.gz'),
('ROIv_Lausanne2018_scale2.nii.gz',
self.subject + '_label-L2018_desc-scale2_atlas.nii.gz'),
('ROIv_Lausanne2018_scale3.nii.gz',
self.subject + '_label-L2018_desc-scale3_atlas.nii.gz'),
('ROIv_Lausanne2018_scale4.nii.gz',
self.subject + '_label-L2018_desc-scale4_atlas.nii.gz'),
('ROIv_Lausanne2018_scale5.nii.gz',
self.subject + '_label-L2018_desc-scale5_atlas.nii.gz'),
('ROIv_Lausanne2018_scale1_final.nii.gz',
self.subject + '_label-L2018_desc-scale1_atlas.nii.gz'),
('ROIv_Lausanne2018_scale2_final.nii.gz',
self.subject + '_label-L2018_desc-scale2_atlas.nii.gz'),
('ROIv_Lausanne2018_scale3_final.nii.gz',
self.subject + '_label-L2018_desc-scale3_atlas.nii.gz'),
('ROIv_Lausanne2018_scale4_final.nii.gz',
self.subject + '_label-L2018_desc-scale4_atlas.nii.gz'),
('ROIv_Lausanne2018_scale5_final.nii.gz',
self.subject + '_label-L2018_desc-scale5_atlas.nii.gz'),
('ROIv_Lausanne2018_scale1.graphml',
self.subject + '_label-L2018_desc-scale1_atlas.graphml'),
('ROIv_Lausanne2018_scale2.graphml',
self.subject + '_label-L2018_desc-scale2_atlas.graphml'),
('ROIv_Lausanne2018_scale3.graphml',
self.subject + '_label-L2018_desc-scale3_atlas.graphml'),
('ROIv_Lausanne2018_scale4.graphml',
self.subject + '_label-L2018_desc-scale4_atlas.graphml'),
('ROIv_Lausanne2018_scale5.graphml',
self.subject + '_label-L2018_desc-scale5_atlas.graphml'),
('ROIv_Lausanne2018_scale1_FreeSurferColorLUT.txt',
self.subject + '_label-L2018_desc-scale1_atlas_FreeSurferColorLUT.txt'),
('ROIv_Lausanne2018_scale2_FreeSurferColorLUT.txt',
self.subject + '_label-L2018_desc-scale2_atlas_FreeSurferColorLUT.txt'),
('ROIv_Lausanne2018_scale3_FreeSurferColorLUT.txt',
self.subject + '_label-L2018_desc-scale3_atlas_FreeSurferColorLUT.txt'),
('ROIv_Lausanne2018_scale4_FreeSurferColorLUT.txt',
self.subject + '_label-L2018_desc-scale4_atlas_FreeSurferColorLUT.txt'),
('ROIv_Lausanne2018_scale5_FreeSurferColorLUT.txt',
self.subject + '_label-L2018_desc-scale5_atlas_FreeSurferColorLUT.txt'),
('ROIv_HR_th_scale33.nii.gz',
self.subject + '_label-L2018_desc-scale1_atlas.nii.gz'),
('ROIv_HR_th_scale60.nii.gz',
self.subject + '_label-L2018_desc-scale2_atlas.nii.gz'),
('ROIv_HR_th_scale125.nii.gz',
self.subject + '_label-L2018_desc-scale3_atlas.nii.gz'),
('ROIv_HR_th_scale250.nii.gz',
self.subject + '_label-L2018_desc-scale4_atlas.nii.gz'),
('ROIv_HR_th_scale500.nii.gz',
self.subject + '_label-L2018_desc-scale5_atlas.nii.gz'),
(
'roi_stats_scale1.tsv', self.subject + '_label-L2018_desc-scale1_stats.tsv'),
(
'roi_stats_scale2.tsv', self.subject + '_label-L2018_desc-scale2_stats.tsv'),
(
'roi_stats_scale3.tsv', self.subject + '_label-L2018_desc-scale3_stats.tsv'),
(
'roi_stats_scale4.tsv', self.subject + '_label-L2018_desc-scale4_stats.tsv'),
(
'roi_stats_scale5.tsv', self.subject + '_label-L2018_desc-scale5_stats.tsv'),
]
elif self.parcellation_scheme == 'NativeFreesurfer':
sinker.inputs.substitutions = [('T1.nii.gz', self.subject + '_desc-head_T1w.nii.gz'),
('brain.nii.gz', self.subject +
'_desc-brain_T1w.nii.gz'),
('brain_mask.nii.gz', self.subject +
'_desc-brain_mask.nii.gz'),
('aseg.nii.gz', self.subject +
'_desc-aseg_dseg.nii.gz'),
('csf_mask.nii.gz', self.subject +
'_label-CSF_dseg.nii.gz'),
('fsmask_1mm.nii.gz', self.subject +
'_label-WM_dseg.nii.gz'),
('gmmask.nii.gz', self.subject +
'_label-GM_dseg.nii.gz'),
('T1w_class-GM.nii.gz', self.subject +
'_label-GM_dseg.nii.gz'),
('wm_eroded.nii.gz', self.subject +
'_label-WM_desc-eroded_dseg.nii.gz'),
('csf_eroded.nii.gz', self.subject +
'_label-CSF_desc-eroded_dseg.nii.gz'),
('brain_eroded.nii.gz',
self.subject + '_label-brain_desc-eroded_dseg.nii.gz'),
('aparc+aseg.native.nii.gz', self.subject +
'_desc-aparcaseg_dseg.nii.gz'),
('aparc+aseg.Lausanne2018.native.nii.gz',
self.subject + '_desc-aparcaseg_dseg.nii.gz'),
('ROIv_HR_th_freesurferaparc.nii.gz',
self.subject + '_label-Desikan_atlas.nii.gz'),
('freesurferaparc.graphml', self.subject +
'_label-Desikan_atlas.graphml'),
('FreeSurferColorLUT_adapted.txt',
self.subject + '_label-Desikan_FreeSurferColorLUT.txt'),
(
'roi_stats_freesurferaparc.tsv', self.subject + '_label-Desikan_stats.tsv'),
]
# else:
# sinker.inputs.substitutions = [ (self.subject+'_T1w.nii.gz', self.subject+'_T1w_head.nii.gz'),
# ('brain_mask.nii.gz', self.subject+'_T1w_brainmask.nii.gz'),
# ('brainmask_eroded.nii.gz', self.subject+'_T1w_brainmask_eroded.nii.gz'),
# ('brain.nii.gz', self.subject+'_T1w_brain.nii.gz'),
# ('fsmask_1mm.nii.gz',self.subject+'_T1w_class-WM.nii.gz'),
# ('fsmask_1mm_eroded.nii.gz',self.subject+'_T1w_class-WM_eroded.nii.gz'),
# ('csf_mask_eroded.nii.gz',self.subject+'_T1w_class-CSF_eroded.nii.gz'),
# #('gm_mask',self.subject+'_T1w_class-GM'),
# #('roivs', self.subject+'_T1w_parc'),#TODO substitute for list of files
# ('T1w_class-GM.nii.gz',self.subject+'_T1w_class-GM.nii.gz'),
# ('ROIv_HR_th_scale1.nii.gz',self.subject+'_T1w_parc_scale1.nii.gz'),
# ('ROIv_HR_th_scale2.nii.gz',self.subject+'_T1w_parc_scale2.nii.gz'),
# ('ROIv_HR_th_scale3.nii.gz',self.subject+'_T1w_parc_scale3.nii.gz'),
# ('ROIv_HR_th_scale4.nii.gz',self.subject+'_T1w_parc_scale4.nii.gz'),
# ('ROIv_HR_th_scale5.nii.gz',self.subject+'_T1w_parc_scale5.nii.gz'),
# ('ROIv_HR_th_scale33.nii.gz',self.subject+'_T1w_parc_scale1.nii.gz'),
# ('ROIv_HR_th_scale60.nii.gz',self.subject+'_T1w_parc_scale2.nii.gz'),
# ('ROIv_HR_th_scale125.nii.gz',self.subject+'_T1w_parc_scale3.nii.gz'),
# ('ROIv_HR_th_scale250.nii.gz',self.subject+'_T1w_parc_scale4.nii.gz'),
# ('ROIv_HR_th_scale500.nii.gz',self.subject+'_T1w_parc_scale5.nii.gz'),
# ]
# Clear previous outputs
self.clear_stages_outputs()
# Create common_flow
anat_flow = pe.Workflow(name='anatomical_pipeline', base_dir=os.path.abspath(
nipype_deriv_subject_directory))
anat_inputnode = pe.Node(interface=util.IdentityInterface(
fields=["T1"]), name="inputnode")
anat_outputnode = pe.Node(interface=util.IdentityInterface(
fields=["subjects_dir", "subject_id", "T1", "aseg", "aparc_aseg", "brain", "brain_mask", "csf_mask_file",
"wm_mask_file", "gm_mask_file", "wm_eroded", "brain_eroded", "csf_eroded",
"roi_volumes", "parcellation_scheme", "atlas_info", "roi_colorLUTs", "roi_graphMLs",
"roi_volumes_stats"]), name="outputnode")
anat_flow.add_nodes([anat_inputnode, anat_outputnode])
anat_flow.connect([
(datasource, anat_inputnode, [("T1", "T1")]),
])
if self.stages['Segmentation'].enabled:
if self.stages['Segmentation'].config.seg_tool == "Freesurfer":
if self.stages['Segmentation'].config.use_existing_freesurfer_data is False:
self.stages['Segmentation'].config.freesurfer_subjects_dir = os.path.join(self.output_directory,
'freesurfer')
print("Freesurfer_subjects_dir: %s" %
self.stages['Segmentation'].config.freesurfer_subjects_dir)
self.stages['Segmentation'].config.freesurfer_subject_id = os.path.join(self.output_directory,
'freesurfer', self.subject)
print("Freesurfer_subject_id: %s" %
self.stages['Segmentation'].config.freesurfer_subject_id)
seg_flow = self.create_stage_flow("Segmentation")
anat_flow.connect(
[(anat_inputnode, seg_flow, [('T1', 'inputnode.T1')])])
if self.stages['Segmentation'].config.seg_tool == "Custom segmentation":
anat_flow.connect([
(seg_flow, anat_outputnode, [("outputnode.brain_mask", "brain_mask"),
("outputnode.brain", "brain")]),
(anat_inputnode, anat_outputnode, [("T1", "T1")])
])
anat_flow.connect([
(seg_flow, anat_outputnode, [("outputnode.subjects_dir", "subjects_dir"),
("outputnode.subject_id", "subject_id")])
])
if self.stages['Parcellation'].enabled:
parc_flow = self.create_stage_flow("Parcellation")
if self.stages['Segmentation'].config.seg_tool == "Freesurfer":
anat_flow.connect([(seg_flow, parc_flow, [('outputnode.subjects_dir', 'inputnode.subjects_dir'),
('outputnode.subject_id', 'inputnode.subject_id')]),
])
else:
anat_flow.connect([
(seg_flow, parc_flow, [
("outputnode.custom_wm_mask", "inputnode.custom_wm_mask")])
])
if self.stages['Segmentation'].config.seg_tool == "Freesurfer":
anat_flow.connect([
(parc_flow, anat_outputnode, [("outputnode.wm_mask_file", "wm_mask_file"),
("outputnode.parcellation_scheme",
"parcellation_scheme"),
("outputnode.atlas_info",
"atlas_info"),
("outputnode.roi_volumes",
"roi_volumes"),
("outputnode.roi_colorLUTs",
"roi_colorLUTs"),
("outputnode.roi_graphMLs",
"roi_graphMLs"),
("outputnode.roi_volumes_stats",
"roi_volumes_stats"),
("outputnode.wm_eroded",
"wm_eroded"),
("outputnode.gm_mask_file",
"gm_mask_file"),
("outputnode.csf_mask_file",
"csf_mask_file"),
("outputnode.csf_eroded",
"csf_eroded"),
("outputnode.brain_eroded",
"brain_eroded"),
("outputnode.T1", "T1"),
("outputnode.aseg", "aseg"),
("outputnode.aparc_aseg",
"aparc_aseg"),
("outputnode.brain_mask",
"brain_mask"),
("outputnode.brain", "brain"),
])
])
else:
anat_flow.connect([
(parc_flow, anat_outputnode, [("outputnode.wm_mask_file", "wm_mask_file"),
("outputnode.parcellation_scheme",
"parcellation_scheme"),
("outputnode.atlas_info",
"atlas_info"),
("outputnode.roi_volumes",
"roi_volumes"),
("outputnode.wm_eroded",
"wm_eroded"),
("outputnode.gm_mask_file",
"gm_mask_file"),
("outputnode.csf_eroded",
"csf_eroded"),
("outputnode.brain_eroded",
"brain_eroded"),
]),
])
if not self.stages['Segmentation'].enabled:
anat_flow.connect([
(anat_inputnode, anat_outputnode, [("T1", "T1")])
])
anat_flow.connect([
(anat_outputnode, sinker, [("T1", "anat.@T1")]),
(anat_outputnode, sinker, [("aseg", "anat.@aseg")]),
(anat_outputnode, sinker, [("aparc_aseg", "anat.@aparc_aseg")]),
(anat_outputnode, sinker, [("brain", "anat.@brain")]),
(anat_outputnode, sinker, [("brain_mask", "anat.@brain_mask")]),
(anat_outputnode, sinker, [("wm_mask_file", "anat.@wm_mask")]),
(anat_outputnode, sinker, [("gm_mask_file", "anat.@gm_mask")]),
(anat_outputnode, sinker, [("csf_mask_file", "anat.@csf_mask")]),
(anat_outputnode, sinker, [("roi_volumes", "anat.@roivs")]),
(anat_outputnode, sinker, [("roi_colorLUTs", "anat.@luts")]),
(anat_outputnode, sinker, [("roi_graphMLs", "anat.@graphmls")]),
(anat_outputnode, sinker, [("roi_volumes_stats", "anat.@stats")]),
(anat_outputnode, sinker, [
("brain_eroded", "anat.@brainmask_eroded")]),
(anat_outputnode, sinker, [("wm_eroded", "anat.@wm_eroded")]),
(anat_outputnode, sinker, [("csf_eroded", "anat.@csf_eroded")])
])
self.flow = anat_flow
return anat_flow
[docs] def process(self):
"""Executes the pipeline workflow and returns True if successful."""
# Enable the use of the W3C PROV data model to capture and represent provenance in Nipype
# config.enable_provenance()
# Process time
self.now = datetime.datetime.now().strftime("%Y%m%d_%H%M")
if '_' in self.subject:
self.subject = self.subject.split('_')[0]
# old_subject = self.subject
if self.global_conf.subject_session == '':
cmp_deriv_subject_directory = os.path.join(
self.output_directory, "cmp", self.subject)
nipype_deriv_subject_directory = os.path.join(
self.output_directory, "nipype", self.subject)
else:
cmp_deriv_subject_directory = os.path.join(self.output_directory, "cmp", self.subject,
self.global_conf.subject_session)
nipype_deriv_subject_directory = os.path.join(self.output_directory, "nipype", self.subject,
self.global_conf.subject_session)
self.subject = "_".join(
(self.subject, self.global_conf.subject_session))
if not os.path.exists(os.path.join(nipype_deriv_subject_directory, "anatomical_pipeline")):
try:
os.makedirs(os.path.join(
nipype_deriv_subject_directory, "anatomical_pipeline"))
except os.error:
print("%s was already existing" % os.path.join(
nipype_deriv_subject_directory, "anatomical_pipeline"))
# Initialization
if os.path.isfile(os.path.join(nipype_deriv_subject_directory, "anatomical_pipeline", "pypeline.log")):
os.unlink(os.path.join(nipype_deriv_subject_directory,
"anatomical_pipeline", "pypeline.log"))
config.update_config(
{'logging': {'log_directory': os.path.join(nipype_deriv_subject_directory, "anatomical_pipeline"),
'log_to_file': True},
'execution': {'remove_unnecessary_outputs': False,
'stop_on_first_crash': True,
'stop_on_first_rerun': False,
'use_relative_paths': True,
'crashfile_format': "txt"}
})
logging.update_logging(config)
iflogger = logging.getLogger('nipype.interface')
iflogger.info("**** Processing ****")
anat_flow = self.create_pipeline_flow(cmp_deriv_subject_directory=cmp_deriv_subject_directory,
nipype_deriv_subject_directory=nipype_deriv_subject_directory)
anat_flow.write_graph(graph2use='colored',
format='svg', simple_form=True)
if self.number_of_cores != 1:
anat_flow.run(plugin='MultiProc', plugin_args={
'n_procs': self.number_of_cores})
else:
anat_flow.run()
# self.fill_stages_outputs()
# Clean undesired folders/files
# rm_file_list = ['rh.EC_average','lh.EC_average','fsaverage']
# for file_to_rm in rm_file_list:
# if os.path.exists(os.path.join(self.base_directory,file_to_rm)):
# os.remove(os.path.join(self.base_directory,file_to_rm))
# copy .ini and log file
# outdir = os.path.join(cmp_deriv_subject_directory,'config')
# if not os.path.exists(outdir):
# os.makedirs(outdir)
#
# try:
# shutil.copy(self.config_file,outdir)
# except shutil.Error:
# print("Skipped copy of config file")
# shutil.copy(os.path.join(self.base_directory,"derivatives","cmp",self.subject,'pypeline.log'),outdir)
iflogger.info("**** Processing finished ****")
return True, 'Processing successful'