Source code for cmp.cli.connectomemapper3

# 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.
"""This module defines the `connectomemapper3` script that is called by the BIDS App."""

import sys
import os
import argparse

import subprocess

# BIDS import
from bids import BIDSLayout

# CMP imports
import cmp.project
from cmp.info import __version__, __copyright__
from cmtklib.util import print_error, print_blue, print_warning

import warnings
warnings.filterwarnings("ignore",
                        message="""UserWarning: No valid root directory found for domain 'derivatives'.
                                Falling back on the Layout's root directory. If this isn't the intended behavior,
                                make sure the config file for this domain includes a 'root' key.""")


[docs]def info(): """Print version of copyright.""" print_blue(f'\nConnectome Mapper {__version__}') print_warning(f'{__copyright__}\n')
# Checks the needed dependencies. We call directly the functions instead # of just checking existence in $PATH in order to handl missing libraries. # Note that not all the commands give the awaited 1 exit code...
[docs]def dep_check(): """Check if dependencies are installed. This includes for the moment: * FSL * FreeSurfer """ nul = open(os.devnull, 'w') error = "" # Check for FSL if subprocess.call("fslorient", stdout=nul, stderr=nul, shell=True) != 255: error = """ .. ERROR: FSL not installed or not working correctly. Check that the FSL_DIR variable is exported and the fsl.sh setup script is sourced.""" # Check for Freesurfer if subprocess.call("mri_info", stdout=nul, stderr=nul, shell=True) != 1: error = """ .. ERROR: FREESURFER not installed or not working correctly. Check that the FREESURFER_HOME variable is exported and the SetUpFreeSurfer.sh setup script is sourced.""" # Check for MRtrix # if subprocess.call("mrconvert", stdout=nul, stderr=nul,shell=True) != 255: # error = """MRtrix3 not installed or not working correctly. Check that PATH variable is updated with MRtrix3 binary (bin) directory.""" # Check for DTK # if subprocess.call("dti_recon", stdout=nul, stderr=nul, shell=True) != 0 or "DSI_PATH" not in os.environ: # error = """Diffusion Toolkit not installed or not working correctly. Check that # the DSI_PATH variable is exported and that the dtk binaries (e.g. dti_recon) are in # your path.""" # Check for DTB # if subprocess.call("DTB_dtk2dir", stdout=nul, stderr=nul, shell=True) != 1: # error = """DTB binaries not installed or not working correctly. Check that the # DTB binaries (e.g. DTB_dtk2dir) are in your path and don't give any error.""" if error != "": print_error(error) sys.exit(2)
[docs]def create_parser(): """Create the parser of connectomemapper3 python script. Returns ------- p : argparse.ArgumentParser Parser """ p = argparse.ArgumentParser(description='Connectome Mapper 3 main script.') p.add_argument('--bids_dir', required=True, help='The directory with the input dataset ' 'formatted according to the BIDS standard.') p.add_argument('--output_dir', required=True, help='The directory where the output files ' 'should be stored. If you are running group level analysis ' 'this folder should be prepopulated with the results of the ' 'participant level analysis.') p.add_argument('--participant_label', required=True, help='The label of the participant' 'that should be analyzed. The label corresponds to' '<participant_label> from the BIDS spec ' '(so it DOES include "sub-"') p.add_argument('--anat_pipeline_config', required=True, help='Configuration .txt file for processing stages of ' 'the anatomical MRI processing pipeline') p.add_argument('--dwi_pipeline_config', help='Configuration .txt file for processing stages of ' 'the diffusion MRI processing pipeline') p.add_argument('--func_pipeline_config', help='Configuration .txt file for processing stages of ' 'the fMRI processing pipeline') p.add_argument('--session_label', help='The label of the participant session ' 'that should be analyzed. The label corresponds to ' '<session_label> from the BIDS spec ' '(so it DOES include "ses-"') p.add_argument('--number_of_threads', type=int, help='The number of OpenMP threads used for multi-threading by ' 'Freesurfer, FSL, MRtrix3, Dipy, AFNI ' '(Set to [Number of available CPUs -1] by default).') p.add_argument('-v', '--version', action='version', version=f'Connectome Mapper version {__version__}') return p
[docs]def main(): """Main function that runs the connectomemapper3 python script. Returns ------- exit_code : {0, 1} An exit code given to `sys.exit()` that can be: * '0' in case of successful completion * '1' in case of an error """ # Parse script arguments parser = create_parser() args = parser.parse_args() # Check dependencies dep_check() # Add current directory to the path, useful if DTB_ bins not installed os.environ["PATH"] += os.pathsep + os.path.dirname(sys.argv[0]) # Version and copyright message info() project = cmp.project.CMP_Project_Info() project.base_directory = os.path.abspath(args.bids_dir) project.output_directory = os.path.abspath(args.output_dir) project.subjects = ['{}'.format(args.participant_label)] project.subject = '{}'.format(args.participant_label) try: bids_layout = BIDSLayout(project.base_directory) except Exception: print_error(" .. EXCEPTION: Raised at BIDSLayout") exit_code = 1 return exit_code if args.session_label is not None: project.subject_sessions = ['{}'.format(args.session_label)] project.subject_session = '{}'.format(args.session_label) print(" .. INFO: Dataset has subject/session layout") else: print(" .. INFO: Dataset has basic subject layout") project.subject_sessions = [''] project.subject_session = '' project.anat_config_file = os.path.abspath(args.anat_pipeline_config) # Perform only the anatomical pipeline if args.dwi_pipeline_config is None and args.func_pipeline_config is None: anat_pipeline = cmp.project.init_anat_project(project, False) if anat_pipeline is not None: anat_valid_inputs = anat_pipeline.check_input(bids_layout, gui=False) if args.number_of_threads is not None: print(f' .. INFO: Set Freesurfer and ANTs to use {args.number_of_threads} threads by the means of OpenMP') anat_pipeline.stages['Segmentation'].config.number_of_threads = args.number_of_threads if anat_valid_inputs: anat_pipeline.process() else: print_error(" .. ERROR: Invalid inputs") exit_code = 1 return exit_code # Perform the anatomical and the diffusion pipelines elif args.dwi_pipeline_config is not None and args.func_pipeline_config is None: project.dmri_config_file = os.path.abspath(args.dwi_pipeline_config) anat_pipeline = cmp.project.init_anat_project(project, False) if anat_pipeline is not None: anat_valid_inputs = anat_pipeline.check_input(bids_layout, gui=False) if args.number_of_threads is not None: print(f' .. INFO: Set Freesurfer and ANTs to use {args.number_of_threads} threads by the means of OpenMP') anat_pipeline.stages['Segmentation'].config.number_of_threads = args.number_of_threads if anat_valid_inputs: print(">> Process anatomical pipeline") anat_pipeline.process() else: print_error(" .. ERROR: Invalid inputs") exit_code = 1 return exit_code anat_valid_outputs, msg = anat_pipeline.check_output() project.freesurfer_subjects_dir = anat_pipeline.stages['Segmentation'].config.freesurfer_subjects_dir project.freesurfer_subject_id = anat_pipeline.stages['Segmentation'].config.freesurfer_subject_id if anat_valid_outputs: dmri_valid_inputs, dmri_pipeline = cmp.project.init_dmri_project(project, bids_layout, False) if dmri_pipeline is not None: dmri_pipeline.parcellation_scheme = anat_pipeline.parcellation_scheme dmri_pipeline.atlas_info = anat_pipeline.atlas_info if dmri_valid_inputs: dmri_pipeline.process() else: print(" .. ERROR: Invalid inputs") exit_code = 1 return exit_code else: print_error(f' .. ERROR: Invalid anatomical outputs for diffusion pipeline') print_error(f'{msg}') exit_code = 1 return exit_code # Perform the anatomical and the fMRI pipelines elif args.dwi_pipeline_config is None and args.func_pipeline_config is not None: project.fmri_config_file = os.path.abspath(args.func_pipeline_config) anat_pipeline = cmp.project.init_anat_project(project, False) if anat_pipeline is not None: anat_valid_inputs = anat_pipeline.check_input(bids_layout, gui=False) if args.number_of_threads is not None: print(f' .. INFO: Set Freesurfer and ANTs to use {args.number_of_threads} threads by the means of OpenMP') anat_pipeline.stages['Segmentation'].config.number_of_threads = args.number_of_threads if anat_valid_inputs: print(">> Process anatomical pipeline") anat_pipeline.process() else: print_error(" .. ERROR: Invalid inputs") exit_code = 1 return exit_code anat_valid_outputs, msg = anat_pipeline.check_output() project.freesurfer_subjects_dir = anat_pipeline.stages['Segmentation'].config.freesurfer_subjects_dir project.freesurfer_subject_id = anat_pipeline.stages['Segmentation'].config.freesurfer_subject_id if anat_valid_outputs: fmri_valid_inputs, fmri_pipeline = cmp.project.init_fmri_project(project, bids_layout, False) if fmri_pipeline is not None: fmri_pipeline.parcellation_scheme = anat_pipeline.parcellation_scheme fmri_pipeline.atlas_info = anat_pipeline.atlas_info if fmri_valid_inputs: print(">> Process fmri pipeline") fmri_pipeline.process() else: print(" .. ERROR: Invalid inputs") exit_code = 1 return exit_code else: print_error(f' .. ERROR: Invalid anatomical outputs for fMRI pipeline') print_error(f'{msg}') exit_code = 1 return exit_code # Perform all pipelines (anatomical/diffusion/fMRI) elif args.dwi_pipeline_config is not None and args.func_pipeline_config is not None: project.dmri_config_file = os.path.abspath(args.dwi_pipeline_config) project.fmri_config_file = os.path.abspath(args.func_pipeline_config) anat_pipeline = cmp.project.init_anat_project(project, False) if anat_pipeline is not None: anat_valid_inputs = anat_pipeline.check_input(bids_layout, gui=False) if args.number_of_threads is not None: print(f' .. INFO: Set Freesurfer and ANTs to use {args.number_of_threads} threads by the means of OpenMP') anat_pipeline.stages['Segmentation'].config.number_of_threads = args.number_of_threads if anat_valid_inputs: print(">> Process anatomical pipeline") anat_pipeline.process() else: print_error(" .. ERROR: Invalid inputs") exit_code = 1 return exit_code anat_valid_outputs, msg = anat_pipeline.check_output() project.freesurfer_subjects_dir = anat_pipeline.stages['Segmentation'].config.freesurfer_subjects_dir project.freesurfer_subject_id = anat_pipeline.stages['Segmentation'].config.freesurfer_subject_id if anat_valid_outputs: dmri_valid_inputs, dmri_pipeline = cmp.project.init_dmri_project(project, bids_layout, False) if dmri_pipeline is not None: dmri_pipeline.parcellation_scheme = anat_pipeline.parcellation_scheme dmri_pipeline.atlas_info = anat_pipeline.atlas_info if dmri_valid_inputs: print(">> Process diffusion pipeline") dmri_pipeline.process() else: print_error(" .. ERROR: Invalid inputs") exit_code = 1 return exit_code fmri_valid_inputs, fmri_pipeline = cmp.project.init_fmri_project(project, bids_layout, False) if fmri_pipeline is not None: fmri_pipeline.parcellation_scheme = anat_pipeline.parcellation_scheme fmri_pipeline.atlas_info = anat_pipeline.atlas_info if fmri_valid_inputs: print(">> Process fmri pipeline") fmri_pipeline.process() else: print_error(" .. ERROR: Invalid inputs") exit_code = 1 return exit_code else: print_error(f' .. ERROR: Invalid anatomical outputs for diffusion and fMRI pipelines') print_error(f'{msg}') exit_code = 1 return exit_code exit_code = 0 return exit_code
if __name__ == "__main__": sys.exit(main())