class T1Study(MRIStudy, metaclass=StudyMetaClass): add_data_specs = [ DatasetSpec('fs_recon_all', freesurfer_recon_all_format, 'freesurfer_pipeline'), DatasetSpec('brain', nifti_gz_format, 'brain_extraction_pipeline')] add_parameter_specs = [ ParameterSpec('bet_robust', True), ParameterSpec('bet_f_threshold', 0.57), ParameterSpec('bet_g_threshold', -0.1)] add_switch_specs = [ SwitchSpec('bet_method', 'optibet', choices=MRIStudy.switch_spec('bet_method').choices)] def freesurfer_pipeline(self, **kwargs): """ Segments grey matter, white matter and CSF from T1 images using SPM "NewSegment" function. NB: Default values come from the W2MHS toolbox """ pipeline = self.create_pipeline( name='segmentation', inputs=[DatasetSpec('primary', nifti_gz_format)], outputs=[DatasetSpec('fs_recon_all', freesurfer_recon_all_format)], desc="Segment white/grey matter and csf", version=1, citations=copy(freesurfer_cites), **kwargs) # FS ReconAll node recon_all = pipeline.create_node( interface=ReconAll(), name='recon_all', requirements=[freesurfer_req], wall_time=2000) recon_all.inputs.directive = 'all' recon_all.inputs.openmp = self.runner.num_processes # Wrapper around os.path.join join = pipeline.create_node(interface=JoinPath(), name='join') pipeline.connect(recon_all, 'subjects_dir', join, 'dirname') pipeline.connect(recon_all, 'subject_id', join, 'filename') # Connect inputs/outputs pipeline.connect_input('primary', recon_all, 'T1_files') pipeline.connect_output('fs_recon_all', join, 'path') return pipeline def segmentation_pipeline(self, **kwargs): pipeline = super(T1Study, self).segmentation_pipeline(img_type=1, **kwargs) return pipeline
class EPIStudy(MRIStudy, metaclass=StudyMetaClass): add_data_specs = [ DatasetSpec('coreg_ref_preproc', nifti_gz_format, optional=True), DatasetSpec('coreg_ref_wmseg', nifti_gz_format, optional=True), DatasetSpec('reverse_phase', nifti_gz_format, optional=True), DatasetSpec('field_map_mag', nifti_gz_format, optional=True), DatasetSpec('field_map_phase', nifti_gz_format, optional=True), DatasetSpec('moco', nifti_gz_format, 'intrascan_alignment_pipeline'), DatasetSpec('align_mats', directory_format, 'intrascan_alignment_pipeline'), DatasetSpec('moco_par', par_format, 'intrascan_alignment_pipeline'), FieldSpec('field_map_delta_te', float, 'field_map_time_info_pipeline') ] add_parameter_specs = [ ParameterSpec('bet_robust', True), ParameterSpec('bet_f_threshold', 0.2), ParameterSpec('bet_reduce_bias', False), ParameterSpec('fugue_echo_spacing', 0.000275) ] add_switch_specs = [ SwitchSpec('linear_reg_method', 'epireg', choices=('flirt', 'spm', 'ants', 'epireg')) ] def linear_coregistration_pipeline(self, **kwargs): if self.branch('linear_reg_method', 'epireg'): return self._epireg_linear_coregistration_pipeline(**kwargs) else: return super(EPIStudy, self).linear_coregistration_pipeline(**kwargs) def _epireg_linear_coregistration_pipeline(self, **kwargs): pipeline = self.create_pipeline( name='linear_coreg', inputs=[ DatasetSpec('brain', nifti_gz_format), DatasetSpec('coreg_ref_brain', nifti_gz_format), DatasetSpec('coreg_ref_preproc', nifti_gz_format), DatasetSpec('coreg_ref_wmseg', nifti_gz_format) ], outputs=[ DatasetSpec('coreg_brain', nifti_gz_format), DatasetSpec('coreg_matrix', text_matrix_format) ], desc=("Intra-subjects epi registration improved using white " "matter boundaries."), version=1, citations=[fsl_cite], **kwargs) epireg = pipeline.create_node(fsl.epi.EpiReg(), name='epireg', requirements=[fsl509_req]) epireg.inputs.out_base = 'epireg2ref' pipeline.connect_input('brain', epireg, 'epi') pipeline.connect_input('coreg_ref_brain', epireg, 't1_brain') pipeline.connect_input('coreg_ref_preproc', epireg, 't1_head') pipeline.connect_input('coreg_ref_wmseg', epireg, 'wmseg') pipeline.connect_output('coreg_brain', epireg, 'out_file') pipeline.connect_output('coreg_matrix', epireg, 'epi2str_mat') return pipeline def intrascan_alignment_pipeline(self, **kwargs): pipeline = self.create_pipeline( name='MCFLIRT_pipeline', inputs=[DatasetSpec('preproc', nifti_gz_format)], outputs=[ DatasetSpec('moco', nifti_gz_format), DatasetSpec('align_mats', directory_format), DatasetSpec('moco_par', par_format) ], desc=("Intra-epi volumes alignment."), version=1, citations=[fsl_cite], **kwargs) mcflirt = pipeline.create_node(fsl.MCFLIRT(), name='mcflirt', requirements=[fsl509_req]) mcflirt.inputs.ref_vol = 0 mcflirt.inputs.save_mats = True mcflirt.inputs.save_plots = True mcflirt.inputs.output_type = 'NIFTI_GZ' mcflirt.inputs.out_file = 'moco.nii.gz' pipeline.connect_input('preproc', mcflirt, 'in_file') pipeline.connect_output('moco', mcflirt, 'out_file') pipeline.connect_output('moco_par', mcflirt, 'par_file') merge = pipeline.create_node(MergeListMotionMat(), name='merge') pipeline.connect(mcflirt, 'mat_file', merge, 'file_list') pipeline.connect_output('align_mats', merge, 'out_dir') return pipeline def field_map_time_info_pipeline(self, **kwargs): pipeline = self.create_pipeline( name='field_map_time_info_pipeline', inputs=[DatasetSpec('field_map_mag', dicom_format)], outputs=[FieldSpec('field_map_delta_te', float)], desc=("Pipeline to extract delta TE from field map " "images, if provided"), version=1, citations=[fsl_cite], **kwargs) delta_te = pipeline.create_node(FieldMapTimeInfo(), name='extract_delta_te') pipeline.connect_input('field_map_mag', delta_te, 'fm_mag') pipeline.connect_output('field_map_delta_te', delta_te, 'delta_te') return pipeline def preproc_pipeline(self, **kwargs): if ('field_map_phase' in self.input_names and 'field_map_mag' in self.input_names): return self._fugue_pipeline(**kwargs) elif 'reverse_phase' in self.input_names: return self._topup_pipeline(**kwargs) else: return super(EPIStudy, self).preproc_pipeline(**kwargs) def _topup_pipeline(self, **kwargs): pipeline = self.create_pipeline( name='preproc_pipeline', inputs=[ DatasetSpec('primary', nifti_gz_format), DatasetSpec('reverse_phase', nifti_gz_format), FieldSpec('ped', str), FieldSpec('pe_angle', str) ], outputs=[DatasetSpec('preproc', nifti_gz_format)], desc=("Topup distortion correction pipeline"), version=1, citations=[fsl_cite], **kwargs) reorient_epi_in = pipeline.create_node(fsl.utils.Reorient2Std(), name='reorient_epi_in', requirements=[fsl509_req]) pipeline.connect_input('primary', reorient_epi_in, 'in_file') reorient_epi_opposite = pipeline.create_node( fsl.utils.Reorient2Std(), name='reorient_epi_opposite', requirements=[fsl509_req]) pipeline.connect_input('reverse_phase', reorient_epi_opposite, 'in_file') prep_dwi = pipeline.create_node(PrepareDWI(), name='prepare_dwi') prep_dwi.inputs.topup = True pipeline.connect_input('ped', prep_dwi, 'pe_dir') pipeline.connect_input('pe_angle', prep_dwi, 'ped_polarity') pipeline.connect(reorient_epi_in, 'out_file', prep_dwi, 'dwi') pipeline.connect(reorient_epi_opposite, 'out_file', prep_dwi, 'dwi1') ped = pipeline.create_node(GenTopupConfigFiles(), name='gen_config') pipeline.connect(prep_dwi, 'pe', ped, 'ped') merge_outputs = pipeline.create_node(merge_lists(2), name='merge_files') pipeline.connect(prep_dwi, 'main', merge_outputs, 'in1') pipeline.connect(prep_dwi, 'secondary', merge_outputs, 'in2') merge = pipeline.create_node(fsl_merge(), name='fsl_merge', requirements=[fsl509_req]) merge.inputs.dimension = 't' pipeline.connect(merge_outputs, 'out', merge, 'in_files') topup = pipeline.create_node(TOPUP(), name='topup', requirements=[fsl509_req]) pipeline.connect(merge, 'merged_file', topup, 'in_file') pipeline.connect(ped, 'config_file', topup, 'encoding_file') in_apply_tp = pipeline.create_node(merge_lists(1), name='in_apply_tp') pipeline.connect(reorient_epi_in, 'out_file', in_apply_tp, 'in1') apply_topup = pipeline.create_node(ApplyTOPUP(), name='applytopup', requirements=[fsl509_req]) apply_topup.inputs.method = 'jac' apply_topup.inputs.in_index = [1] pipeline.connect(in_apply_tp, 'out', apply_topup, 'in_files') pipeline.connect(ped, 'apply_topup_config', apply_topup, 'encoding_file') pipeline.connect(topup, 'out_movpar', apply_topup, 'in_topup_movpar') pipeline.connect(topup, 'out_fieldcoef', apply_topup, 'in_topup_fieldcoef') pipeline.connect_output('preproc', apply_topup, 'out_corrected') return pipeline def _fugue_pipeline(self, **kwargs): pipeline = self.create_pipeline( name='preproc_pipeline', inputs=[ DatasetSpec('primary', nifti_gz_format), DatasetSpec('field_map_mag', nifti_gz_format), DatasetSpec('field_map_phase', nifti_gz_format), FieldSpec('field_map_delta_te', float) ], outputs=[DatasetSpec('preproc', nifti_gz_format)], desc=("Fugue distortion correction pipeline"), version=1, citations=[fsl_cite], **kwargs) reorient_epi_in = pipeline.create_node(fsl.utils.Reorient2Std(), name='reorient_epi_in', requirements=[fsl509_req]) pipeline.connect_input('primary', reorient_epi_in, 'in_file') fm_mag_reorient = pipeline.create_node(fsl.utils.Reorient2Std(), name='reorient_fm_mag', requirements=[fsl509_req]) pipeline.connect_input('field_map_mag', fm_mag_reorient, 'in_file') fm_phase_reorient = pipeline.create_node(fsl.utils.Reorient2Std(), name='reorient_fm_phase', requirements=[fsl509_req]) pipeline.connect_input('field_map_phase', fm_phase_reorient, 'in_file') bet = pipeline.create_node(BET(), name="bet", wall_time=5, requirements=[fsl509_req]) bet.inputs.robust = True pipeline.connect(fm_mag_reorient, 'out_file', bet, 'in_file') create_fmap = pipeline.create_node(PrepareFieldmap(), name="prepfmap", wall_time=5, requirements=[fsl509_req]) # create_fmap.inputs.delta_TE = 2.46 pipeline.connect_input('field_map_delta_te', create_fmap, 'delta_TE') pipeline.connect(bet, "out_file", create_fmap, "in_magnitude") pipeline.connect(fm_phase_reorient, 'out_file', create_fmap, 'in_phase') fugue = pipeline.create_node(FUGUE(), name='fugue', wall_time=5, requirements=[fsl509_req]) fugue.inputs.unwarp_direction = 'x' fugue.inputs.dwell_time = self.parameter('fugue_echo_spacing') fugue.inputs.unwarped_file = 'example_func.nii.gz' pipeline.connect(create_fmap, 'out_fieldmap', fugue, 'fmap_in_file') pipeline.connect(reorient_epi_in, 'out_file', fugue, 'in_file') pipeline.connect_output('preproc', fugue, 'unwarped_file') return pipeline def motion_mat_pipeline(self, **kwargs): inputs = [ DatasetSpec('coreg_matrix', text_matrix_format), DatasetSpec('qform_mat', text_matrix_format) ] if 'reverse_phase' not in self.input_names: inputs.append(DatasetSpec('align_mats', directory_format)) pipeline = self.create_pipeline( name='motion_mat_calculation', inputs=inputs, outputs=[DatasetSpec('motion_mats', motion_mats_format)], desc=("Motion matrices calculation"), version=1, citations=[fsl_cite], **kwargs) mm = pipeline.create_node(MotionMatCalculation(), name='motion_mats') pipeline.connect_input('coreg_matrix', mm, 'reg_mat') pipeline.connect_input('qform_mat', mm, 'qform_mat') if 'reverse_phase' not in self.input_names: pipeline.connect_input('align_mats', mm, 'align_mats') pipeline.connect_output('motion_mats', mm, 'motion_mats') return pipeline
class FunctionalMRIStudy(EPIStudy, metaclass=StudyMetaClass): add_parameter_specs = [ ParameterSpec('component_threshold', 20), ParameterSpec('motion_reg', True), ParameterSpec('highpass', 0.01), ParameterSpec('brain_thresh_percent', 5), ParameterSpec('MNI_template', os.path.join(atlas_path, 'MNI152_T1_2mm.nii.gz')), ParameterSpec( 'MNI_template_mask', os.path.join(atlas_path, 'MNI152_T1_2mm_brain_mask.nii.gz')), ParameterSpec('group_ica_components', 15) ] add_data_specs = [ DatasetSpec('train_data', rfile_format, optional=True, frequency='per_project'), DatasetSpec('hand_label_noise', text_format, 'fix_preparation_pipeline'), DatasetSpec('labelled_components', text_format, 'fix_classification_pipeline'), DatasetSpec('cleaned_file', nifti_gz_format, 'fix_regression_pipeline'), DatasetSpec('filtered_data', nifti_gz_format, 'rsfMRI_filtering_pipeline'), DatasetSpec('mc_par', par_format, 'rsfMRI_filtering_pipeline'), DatasetSpec('melodic_ica', zip_format, 'single_subject_melodic_pipeline'), DatasetSpec('fix_dir', zip_format, 'fix_preparation_pipeline'), DatasetSpec('normalized_ts', nifti_gz_format, 'timeseries_normalization_to_atlas_pipeline'), DatasetSpec('smoothed_ts', nifti_gz_format, 'smoothing_pipeline') ] add_switch_specs = [ SwitchSpec('linear_reg_method', 'ants', choices=('flirt', 'spm', 'ants', 'epireg')) ] def rsfMRI_filtering_pipeline(self, **kwargs): pipeline = self.create_pipeline( name='rsfMRI_filtering', inputs=[ DatasetSpec('preproc', nifti_gz_format), DatasetSpec('brain_mask', nifti_gz_format), DatasetSpec('coreg_ref_brain', nifti_gz_format), FieldSpec('tr', float) ], outputs=[ DatasetSpec('filtered_data', nifti_gz_format), DatasetSpec('mc_par', par_format) ], desc=("Spatial and temporal rsfMRI filtering"), version=1, citations=[fsl_cite], **kwargs) afni_mc = pipeline.create_node(Volreg(), name='AFNI_MC', wall_time=5, requirements=[afni_req]) afni_mc.inputs.zpad = 1 afni_mc.inputs.out_file = 'rsfmri_mc.nii.gz' afni_mc.inputs.oned_file = 'prefiltered_func_data_mcf.par' pipeline.connect_input('preproc', afni_mc, 'in_file') filt = pipeline.create_node(Tproject(), name='Tproject', wall_time=5, requirements=[afni_req]) filt.inputs.stopband = (0, 0.01) filt.inputs.polort = 3 filt.inputs.blur = 3 filt.inputs.out_file = 'filtered_func_data.nii.gz' pipeline.connect_input('tr', filt, 'delta_t') pipeline.connect(afni_mc, 'out_file', filt, 'in_file') pipeline.connect_input('brain_mask', filt, 'mask') meanfunc = pipeline.create_node(ImageMaths(op_string='-Tmean', suffix='_mean'), name='meanfunc', wall_time=5, requirements=[fsl5_req]) pipeline.connect(afni_mc, 'out_file', meanfunc, 'in_file') add_mean = pipeline.create_node(ImageMaths(op_string='-add'), name='add_mean', wall_time=5, requirements=[fsl5_req]) pipeline.connect(filt, 'out_file', add_mean, 'in_file') pipeline.connect(meanfunc, 'out_file', add_mean, 'in_file2') pipeline.connect_output('filtered_data', add_mean, 'out_file') pipeline.connect_output('mc_par', afni_mc, 'oned_file') return pipeline def single_subject_melodic_pipeline(self, **kwargs): pipeline = self.create_pipeline( name='MelodicL1', inputs=[ DatasetSpec('filtered_data', nifti_gz_format), FieldSpec('tr', float), DatasetSpec('brain_mask', nifti_gz_format) ], outputs=[DatasetSpec('melodic_ica', directory_format)], desc=("Single subject ICA analysis using FSL MELODIC."), version=1, citations=[fsl_cite], **kwargs) mel = pipeline.create_node(MELODIC(), name='melodic_L1', wall_time=15, requirements=[fsl5_req]) mel.inputs.no_bet = True pipeline.connect_input('brain_mask', mel, 'mask') mel.inputs.bg_threshold = self.parameter('brain_thresh_percent') mel.inputs.report = True mel.inputs.out_stats = True mel.inputs.mm_thresh = 0.5 mel.inputs.out_dir = 'melodic_ica' pipeline.connect_input('tr', mel, 'tr_sec') pipeline.connect_input('filtered_data', mel, 'in_files') pipeline.connect_output('melodic_ica', mel, 'out_dir') return pipeline def fix_preparation_pipeline(self, **kwargs): pipeline = self.create_pipeline( name='prepare_fix', inputs=[ DatasetSpec('melodic_ica', directory_format), DatasetSpec('filtered_data', nifti_gz_format), DatasetSpec('coreg_to_atlas_mat', text_matrix_format), DatasetSpec('coreg_matrix', text_matrix_format), DatasetSpec('preproc', nifti_gz_format), DatasetSpec('brain', nifti_gz_format), DatasetSpec('coreg_ref_brain', nifti_gz_format), DatasetSpec('mc_par', par_format), DatasetSpec('brain_mask', nifti_gz_format) ], outputs=[ DatasetSpec('fix_dir', directory_format), DatasetSpec('hand_label_noise', text_format) ], desc=("Pipeline to create the right folder structure before " "running FIX"), version=1, citations=[fsl_cite], **kwargs) struct_ants2fsl = pipeline.create_node(ANTs2FSLMatrixConversion(), name='struct_ants2fsl', requirements=[c3d_req]) struct_ants2fsl.inputs.ras2fsl = True struct_ants2fsl.inputs.reference_file = self.parameter('MNI_template') pipeline.connect_input('coreg_to_atlas_mat', struct_ants2fsl, 'itk_file') pipeline.connect_input('coreg_ref_brain', struct_ants2fsl, 'source_file') epi_ants2fsl = pipeline.create_node(ANTs2FSLMatrixConversion(), name='epi_ants2fsl', requirements=[c3d_req]) epi_ants2fsl.inputs.ras2fsl = True pipeline.connect_input('brain', epi_ants2fsl, 'source_file') pipeline.connect_input('coreg_matrix', epi_ants2fsl, 'itk_file') pipeline.connect_input('coreg_ref_brain', epi_ants2fsl, 'reference_file') MNI2t1 = pipeline.create_node(ConvertXFM(), name='MNI2t1', wall_time=5, requirements=[fsl509_req]) MNI2t1.inputs.invert_xfm = True pipeline.connect(struct_ants2fsl, 'fsl_matrix', MNI2t1, 'in_file') struct2epi = pipeline.create_node(ConvertXFM(), name='struct2epi', wall_time=5, requirements=[fsl509_req]) struct2epi.inputs.invert_xfm = True pipeline.connect(epi_ants2fsl, 'fsl_matrix', struct2epi, 'in_file') meanfunc = pipeline.create_node(ImageMaths(op_string='-Tmean', suffix='_mean'), name='meanfunc', wall_time=5, requirements=[fsl509_req]) pipeline.connect_input('preproc', meanfunc, 'in_file') prep_fix = pipeline.create_node(PrepareFIX(), name='prep_fix') pipeline.connect_input('melodic_ica', prep_fix, 'melodic_dir') pipeline.connect_input('coreg_ref_brain', prep_fix, 't1_brain') pipeline.connect_input('mc_par', prep_fix, 'mc_par') pipeline.connect_input('brain_mask', prep_fix, 'epi_brain_mask') pipeline.connect_input('preproc', prep_fix, 'epi_preproc') pipeline.connect_input('filtered_data', prep_fix, 'filtered_epi') pipeline.connect(epi_ants2fsl, 'fsl_matrix', prep_fix, 'epi2t1_mat') pipeline.connect(struct_ants2fsl, 'fsl_matrix', prep_fix, 't12MNI_mat') pipeline.connect(MNI2t1, 'out_file', prep_fix, 'MNI2t1_mat') pipeline.connect(struct2epi, 'out_file', prep_fix, 't12epi_mat') pipeline.connect(meanfunc, 'out_file', prep_fix, 'epi_mean') pipeline.connect_output('fix_dir', prep_fix, 'fix_dir') pipeline.connect_output('hand_label_noise', prep_fix, 'hand_label_file') return pipeline def fix_classification_pipeline(self, **kwargs): pipeline = self.create_pipeline( name='fix_classification', inputs=[ DatasetSpec('train_data', rfile_format, frequency='per_project'), DatasetSpec('fix_dir', directory_format) ], outputs=[DatasetSpec('labelled_components', text_format)], desc=("Automatic classification of noisy components from the " "rsfMRI data using fsl FIX."), version=1, citations=[fsl_cite], **kwargs) fix = pipeline.create_node(FSLFIX(), name="fix", wall_time=30, requirements=[fsl509_req, fix_req]) pipeline.connect_input("fix_dir", fix, "feat_dir") pipeline.connect_input("train_data", fix, "train_data") fix.inputs.component_threshold = self.parameter('component_threshold') fix.inputs.motion_reg = self.parameter('motion_reg') fix.inputs.classification = True pipeline.connect_output('labelled_components', fix, 'label_file') return pipeline def fix_regression_pipeline(self, **kwargs): pipeline = self.create_pipeline( name='signal_regression', inputs=[ DatasetSpec('fix_dir', directory_format), DatasetSpec('labelled_components', text_format) ], outputs=[DatasetSpec('cleaned_file', nifti_gz_format)], desc=("Regression of the noisy components from the rsfMRI data " "using a python implementation equivalent to that in FIX."), version=1, citations=[fsl_cite], **kwargs) signal_reg = pipeline.create_node(SignalRegression(), name="signal_reg", wall_time=30, requirements=[fsl509_req, fix_req]) pipeline.connect_input("fix_dir", signal_reg, "fix_dir") pipeline.connect_input("labelled_components", signal_reg, "labelled_components") signal_reg.inputs.motion_regression = self.parameter('motion_reg') signal_reg.inputs.highpass = self.parameter('highpass') pipeline.connect_output('cleaned_file', signal_reg, 'output') return pipeline def timeseries_normalization_to_atlas_pipeline(self, **kwargs): pipeline = self.create_pipeline( name='timeseries_normalization_to_atlas_pipeline', inputs=[ DatasetSpec('cleaned_file', nifti_gz_format), DatasetSpec('coreg_to_atlas_warp', nifti_gz_format), DatasetSpec('coreg_to_atlas_mat', text_matrix_format), DatasetSpec('coreg_matrix', text_matrix_format) ], outputs=[DatasetSpec('normalized_ts', nifti_gz_format)], desc=("Apply ANTs transformation to the fmri filtered file to " "normalize it to MNI 2mm."), version=1, citations=[fsl_cite], **kwargs) merge_trans = pipeline.create_node(NiPypeMerge(3), name='merge_transforms', wall_time=1) pipeline.connect_input('coreg_to_atlas_warp', merge_trans, 'in1') pipeline.connect_input('coreg_to_atlas_mat', merge_trans, 'in2') pipeline.connect_input('coreg_matrix', merge_trans, 'in3') apply_trans = pipeline.create_node(ApplyTransforms(), name='ApplyTransform', wall_time=7, memory=24000, requirements=[ants2_req]) ref_brain = self.parameter('MNI_template') apply_trans.inputs.reference_image = ref_brain apply_trans.inputs.interpolation = 'Linear' apply_trans.inputs.input_image_type = 3 pipeline.connect(merge_trans, 'out', apply_trans, 'transforms') pipeline.connect_input('cleaned_file', apply_trans, 'input_image') pipeline.connect_output('normalized_ts', apply_trans, 'output_image') return pipeline def smoothing_pipeline(self, **kwargs): pipeline = self.create_pipeline( name='smoothing_pipeline', inputs=[DatasetSpec('normalized_ts', nifti_gz_format)], outputs=[DatasetSpec('smoothed_ts', nifti_gz_format)], desc=("Spatial smoothing of the normalized fmri file"), version=1, citations=[fsl_cite], **kwargs) smooth = pipeline.create_node(BlurToFWHM(), name='3dBlurToFWHM', wall_time=5, requirements=[afni_req]) smooth.inputs.fwhm = 5 smooth.inputs.out_file = 'smoothed_ts.nii.gz' smooth.inputs.mask = self.parameter('MNI_template_mask') pipeline.connect_input('normalized_ts', smooth, 'in_file') pipeline.connect_output('smoothed_ts', smooth, 'out_file') return pipeline
class NODDIStudy(DiffusionStudy, metaclass=StudyMetaClass): add_data_specs = [ DatasetSpec('low_b_dw_scan', mrtrix_format), DatasetSpec('high_b_dw_scan', mrtrix_format), DatasetSpec('forward_pe', mrtrix_format), DatasetSpec('reverse_pe', mrtrix_format), DatasetSpec('dwi_scan', mrtrix_format, 'concatenate_pipeline'), DatasetSpec('ficvf', nifti_format, 'noddi_fitting_pipeline'), DatasetSpec('odi', nifti_format, 'noddi_fitting_pipeline'), DatasetSpec('fiso', nifti_format, 'noddi_fitting_pipeline'), DatasetSpec('fibredirs_xvec', nifti_format, 'noddi_fitting_pipeline'), DatasetSpec('fibredirs_yvec', nifti_format, 'noddi_fitting_pipeline'), DatasetSpec('fibredirs_zvec', nifti_format, 'noddi_fitting_pipeline'), DatasetSpec('fmin', nifti_format, 'noddi_fitting_pipeline'), DatasetSpec('kappa', nifti_format, 'noddi_fitting_pipeline'), DatasetSpec('error_code', nifti_format, 'noddi_fitting_pipeline') ] add_parameter_specs = [ ParameterSpec('noddi_model', 'WatsonSHStickTortIsoV_B0') ] add_switch_specs = [SwitchSpec('single_slice', False)] def concatenate_pipeline(self, **kwargs): # @UnusedVariable """ Concatenates two dMRI datasets (with different b-values) along the DW encoding (4th) axis """ pipeline = self.create_pipeline( name='concatenation', inputs=[ DatasetSpec('low_b_dw_scan', mrtrix_format), DatasetSpec('high_b_dw_scan', mrtrix_format) ], outputs=[DatasetSpec('dwi_scan', mrtrix_format)], desc=("Concatenate low and high b-value dMRI datasets for NODDI " "processing"), version=1, citations=[mrtrix_cite], **kwargs) # Create concatenation node mrcat = pipeline.create_node(MRCat(), name='mrcat', requirements=[mrtrix3_req]) mrcat.inputs.quiet = True # Connect inputs pipeline.connect_input('low_b_dw_scan', mrcat, 'first_scan') pipeline.connect_input('high_b_dw_scan', mrcat, 'second_scan') # Connect outputs pipeline.connect_output('dwi_scan', mrcat, 'out_file') # Check inputs/outputs are connected return pipeline def noddi_fitting_pipeline(self, **kwargs): # @UnusedVariable """ Creates a ROI in which the NODDI processing will be performed Parameters ---------- single_slice: Int If provided the processing is only performed on a single slice (for testing) noddi_model: Str Name of the NODDI model to use for the fitting nthreads: Int Number of processes to use """ pipeline_name = 'noddi_fitting' inputs = [ DatasetSpec('bias_correct', nifti_gz_format), DatasetSpec('grad_dirs', fsl_bvecs_format), DatasetSpec('bvalues', fsl_bvals_format) ] if self.switch('single_slice'): inputs.append(DatasetSpec('eroded_mask', nifti_gz_format)) else: inputs.append(DatasetSpec('brain_mask', nifti_gz_format)) pipeline = self.create_pipeline( name=pipeline_name, inputs=inputs, outputs=[ DatasetSpec('ficvf', nifti_format), DatasetSpec('odi', nifti_format), DatasetSpec('fiso', nifti_format), DatasetSpec('fibredirs_xvec', nifti_format), DatasetSpec('fibredirs_yvec', nifti_format), DatasetSpec('fibredirs_zvec', nifti_format), DatasetSpec('fmin', nifti_format), DatasetSpec('kappa', nifti_format), DatasetSpec('error_code', nifti_format) ], desc=("Creates a ROI in which the NODDI processing will be " "performed"), citations=[noddi_cite], **kwargs) # Create node to unzip the nifti files unzip_bias_correct = pipeline.create_node(MRConvert(), name="unzip_bias_correct", requirements=[mrtrix3_req]) unzip_bias_correct.inputs.out_ext = 'nii' unzip_bias_correct.inputs.quiet = True unzip_mask = pipeline.create_node(MRConvert(), name="unzip_mask", requirements=[mrtrix3_req]) unzip_mask.inputs.out_ext = 'nii' unzip_mask.inputs.quiet = True # Create create-roi node create_roi = pipeline.create_node( CreateROI(), name='create_roi', requirements=[noddi_req, matlab2015_req], memory=4000) pipeline.connect(unzip_bias_correct, 'out_file', create_roi, 'in_file') pipeline.connect(unzip_mask, 'out_file', create_roi, 'brain_mask') # Create batch-fitting node batch_fit = pipeline.create_node( BatchNODDIFitting(), name="batch_fit", requirements=[noddi_req, matlab2015_req], wall_time=180, memory=8000) batch_fit.inputs.model = self.parameter('noddi_model') batch_fit.inputs.nthreads = self.runner.num_processes pipeline.connect(create_roi, 'out_file', batch_fit, 'roi_file') # Create output node save_params = pipeline.create_node( SaveParamsAsNIfTI(), name="save_params", requirements=[noddi_req, matlab2015_req], memory=4000) save_params.inputs.output_prefix = 'params' pipeline.connect(batch_fit, 'out_file', save_params, 'params_file') pipeline.connect(create_roi, 'out_file', save_params, 'roi_file') pipeline.connect(unzip_mask, 'out_file', save_params, 'brain_mask_file') # Connect inputs pipeline.connect_input('bias_correct', unzip_bias_correct, 'in_file') if not pipeline.switch('single_slice'): pipeline.connect_input('eroded_mask', unzip_mask, 'in_file') else: pipeline.connect_input('brain_mask', unzip_mask, 'in_file') pipeline.connect_input('grad_dirs', batch_fit, 'bvecs_file') pipeline.connect_input('bvalues', batch_fit, 'bvals_file') # Connect outputs pipeline.connect_output('ficvf', save_params, 'ficvf') pipeline.connect_output('odi', save_params, 'odi') pipeline.connect_output('fiso', save_params, 'fiso') pipeline.connect_output('fibredirs_xvec', save_params, 'fibredirs_xvec') pipeline.connect_output('fibredirs_yvec', save_params, 'fibredirs_yvec') pipeline.connect_output('fibredirs_zvec', save_params, 'fibredirs_zvec') pipeline.connect_output('fmin', save_params, 'fmin') pipeline.connect_output('kappa', save_params, 'kappa') pipeline.connect_output('error_code', save_params, 'error_code') # Check inputs/outputs are connected return pipeline
class DiffusionStudy(EPIStudy, metaclass=StudyMetaClass): add_data_specs = [ DatasetSpec('dwi_reference', nifti_gz_format, optional=True), DatasetSpec('forward_pe', dicom_format, optional=True), DatasetSpec('b0', nifti_gz_format, 'extract_b0_pipeline', desc="b0 image"), DatasetSpec('noise_residual', mrtrix_format, 'preproc_pipeline'), DatasetSpec('tensor', nifti_gz_format, 'tensor_pipeline'), DatasetSpec('fa', nifti_gz_format, 'tensor_pipeline'), DatasetSpec('adc', nifti_gz_format, 'tensor_pipeline'), DatasetSpec('wm_response', text_format, 'response_pipeline'), DatasetSpec('gm_response', text_format, 'response_pipeline'), DatasetSpec('csf_response', text_format, 'response_pipeline'), DatasetSpec('avg_response', text_format, 'average_response_pipeline'), DatasetSpec('wm_odf', mrtrix_format, 'fod_pipeline'), DatasetSpec('gm_odf', mrtrix_format, 'fod_pipeline'), DatasetSpec('csf_odf', mrtrix_format, 'fod_pipeline'), DatasetSpec('bias_correct', nifti_gz_format, 'bias_correct_pipeline'), DatasetSpec('grad_dirs', fsl_bvecs_format, 'preproc_pipeline'), DatasetSpec('bvalues', fsl_bvals_format, 'preproc_pipeline'), DatasetSpec('eddy_par', eddy_par_format, 'preproc_pipeline'), DatasetSpec('align_mats', directory_format, 'intrascan_alignment_pipeline'), DatasetSpec('tbss_mean_fa', nifti_gz_format, 'tbss_pipeline', frequency='per_project'), DatasetSpec('tbss_proj_fa', nifti_gz_format, 'tbss_pipeline', frequency='per_project'), DatasetSpec('tbss_skeleton', nifti_gz_format, 'tbss_pipeline', frequency='per_project'), DatasetSpec('tbss_skeleton_mask', nifti_gz_format, 'tbss_pipeline', frequency='per_project'), DatasetSpec('brain', nifti_gz_format, 'brain_extraction_pipeline'), DatasetSpec('brain_mask', nifti_gz_format, 'brain_extraction_pipeline'), DatasetSpec('norm_intensity', mrtrix_format, 'intensity_normalisation_pipeline'), DatasetSpec('norm_intens_fa_template', mrtrix_format, 'intensity_normalisation_pipeline', frequency='per_project'), DatasetSpec('norm_intens_wm_mask', mrtrix_format, 'intensity_normalisation_pipeline', frequency='per_project') ] add_parameter_specs = [ ParameterSpec('multi_tissue', True), ParameterSpec('preproc_pe_dir', None, dtype=str), ParameterSpec('tbss_skel_thresh', 0.2), ParameterSpec('fsl_mask_f', 0.25), ParameterSpec('bet_robust', True), ParameterSpec('bet_f_threshold', 0.2), ParameterSpec('bet_reduce_bias', False) ] add_switch_specs = [ SwitchSpec('preproc_denoise', False), SwitchSpec('response_algorithm', 'tax', ('tax', 'dhollander', 'msmt_5tt')), SwitchSpec('fod_algorithm', 'csd', ('csd', 'msmt_csd')), SwitchSpec('brain_extract_method', 'mrtrix', ('mrtrix', 'fsl')), SwitchSpec('bias_correct_method', 'ants', choices=('ants', 'fsl')) ] @property def multi_tissue(self): return self.branch('response_algorithm', ('msmt_5tt', 'dhollander')) def preproc_pipeline(self, **kwargs): # @UnusedVariable @IgnorePep8 """ Performs a series of FSL preprocessing steps, including Eddy and Topup Parameters ---------- phase_dir : str{AP|LR|IS} The phase encode direction """ outputs = [ DatasetSpec('preproc', nifti_gz_format), DatasetSpec('grad_dirs', fsl_bvecs_format), DatasetSpec('bvalues', fsl_bvals_format), DatasetSpec('eddy_par', eddy_par_format) ] citations = [fsl_cite, eddy_cite, topup_cite, distort_correct_cite] if self.switch('preproc_denoise'): outputs.append(DatasetSpec('noise_residual', mrtrix_format)) citations.extend(dwidenoise_cites) if 'dwi_reference' in self.input_names or 'reverse_phase' in self.input_names: inputs = [ DatasetSpec('primary', dicom_format), FieldSpec('ped', dtype=str), FieldSpec('pe_angle', dtype=str) ] if 'dwi_reference' in self.input_names: inputs.append(DatasetSpec('dwi_reference', nifti_gz_format)) if 'reverse_phase' in self.input_names: inputs.append(DatasetSpec('reverse_phase', nifti_gz_format)) distortion_correction = True else: inputs = [DatasetSpec('primary', dicom_format)] distortion_correction = False pipeline = self.create_pipeline( name='preprocess', inputs=inputs, outputs=outputs, desc=("Preprocess dMRI studies using distortion correction"), version=1, citations=citations, **kwargs) # Denoise the dwi-scan if self.switch('preproc_denoise'): # Run denoising denoise = pipeline.create_node(DWIDenoise(), name='denoise', requirements=[mrtrix3_req]) denoise.inputs.out_file_ext = '.mif' # Calculate residual noise subtract_operands = pipeline.create_node(Merge(2), name='subtract_operands') subtract = pipeline.create_node(MRCalc(), name='subtract', requirements=[mrtrix3_req]) subtract.inputs.out_ext = '.mif' subtract.inputs.operation = 'subtract' dwipreproc = pipeline.create_node( DWIPreproc(), name='dwipreproc', requirements=[mrtrix3_req, fsl509_req], wall_time=60) dwipreproc.inputs.eddy_parameters = '--data_is_shelled ' dwipreproc.inputs.no_clean_up = True dwipreproc.inputs.out_file_ext = '.nii.gz' dwipreproc.inputs.temp_dir = 'dwipreproc_tempdir' # Create node to reorient preproc out_file swap = pipeline.create_node(fsl.utils.Reorient2Std(), name='fslreorient2std', requirements=[fsl509_req]) if distortion_correction: # Extract b=0 volumes dwiextract = pipeline.create_node(ExtractDWIorB0(), name='dwiextract', requirements=[mrtrix3_req]) dwiextract.inputs.bzero = True dwiextract.inputs.out_ext = '.nii.gz' # Get first b=0 from dwi b=0 volumes mrconvert = pipeline.create_node(MRConvert(), name="mrconvert", requirements=[mrtrix3_req]) mrconvert.inputs.coord = (3, 0) # Concatenate extracted forward rpe with reverse rpe mrcat = pipeline.create_node(MRCat(), name='mrcat', requirements=[mrtrix3_req]) # Create node to assign the right PED to the diffusion prep_dwi = pipeline.create_node(PrepareDWI(), name='prepare_dwi') # Create preprocessing node dwipreproc.inputs.rpe_pair = True if self.parameter('preproc_pe_dir') is not None: dwipreproc.inputs.pe_dir = self.parameter('preproc_pe_dir') # Create nodes to gradients to FSL format extract_grad = pipeline.create_node(ExtractFSLGradients(), name="extract_grad", requirements=[mrtrix3_req]) # Connect inputs if 'dwi_reference' in self.input_names: pipeline.connect_input('dwi_reference', mrcat, 'second_scan') elif 'reverse_phase' in self.input_names: pipeline.connect_input('reverse_phase', mrcat, 'second_scan') else: assert False pipeline.connect_input('primary', dwiextract, 'in_file') # Connect inter-nodes if self.switch('preproc_denoise'): pipeline.connect_input('primary', denoise, 'in_file') pipeline.connect_input('primary', subtract_operands, 'in1') pipeline.connect(denoise, 'out_file', dwipreproc, 'in_file') pipeline.connect(denoise, 'noise', subtract_operands, 'in2') pipeline.connect(subtract_operands, 'out', subtract, 'operands') else: pipeline.connect_input('primary', dwipreproc, 'in_file') if distortion_correction: pipeline.connect_input('ped', prep_dwi, 'pe_dir') pipeline.connect_input('pe_angle', prep_dwi, 'ped_polarity') pipeline.connect(prep_dwi, 'pe', dwipreproc, 'pe_dir') pipeline.connect(mrcat, 'out_file', dwipreproc, 'se_epi') pipeline.connect(dwiextract, 'out_file', mrconvert, 'in_file') pipeline.connect(mrconvert, 'out_file', mrcat, 'first_scan') pipeline.connect_input('primary', extract_grad, 'in_file') pipeline.connect(dwipreproc, 'out_file', swap, 'in_file') # Connect outputs pipeline.connect_output('preproc', swap, 'out_file') pipeline.connect_output('grad_dirs', extract_grad, 'bvecs_file') pipeline.connect_output('bvalues', extract_grad, 'bvals_file') pipeline.connect_output('eddy_par', dwipreproc, 'eddy_parameters') if self.switch('preproc_denoise'): pipeline.connect_output('noise_residual', subtract, 'out_file') # Check inputs/outputs are connected return pipeline def brain_extraction_pipeline(self, **kwargs): # @UnusedVariable @IgnorePep8 """ Generates a whole brain mask using MRtrix's 'dwi2mask' command Parameters ---------- mask_tool: Str Can be either 'bet' or 'dwi2mask' depending on which mask tool you want to use """ if self.branch('brain_extract_method', 'mrtrix'): pipeline = self.create_pipeline( 'brain_extraction', inputs=[ DatasetSpec('preproc', nifti_gz_format), DatasetSpec('grad_dirs', fsl_bvecs_format), DatasetSpec('bvalues', fsl_bvals_format) ], outputs=[DatasetSpec('brain_mask', nifti_gz_format)], desc="Generate brain mask from b0 images", version=1, citations=[mrtrix_cite], **kwargs) # Create mask node dwi2mask = pipeline.create_node(BrainMask(), name='dwi2mask', requirements=[mrtrix3_req]) dwi2mask.inputs.out_file = 'brain_mask.nii.gz' # Gradient merge node grad_fsl = pipeline.create_node(MergeTuple(2), name="grad_fsl") # Connect nodes pipeline.connect(grad_fsl, 'out', dwi2mask, 'grad_fsl') # Connect inputs pipeline.connect_input('grad_dirs', grad_fsl, 'in1') pipeline.connect_input('bvalues', grad_fsl, 'in2') pipeline.connect_input('preproc', dwi2mask, 'in_file') # Connect outputs pipeline.connect_output('brain_mask', dwi2mask, 'out_file') # Check inputs/outputs are connected pipeline.assert_connected() else: pipeline = super(DiffusionStudy, self).brain_extraction_pipeline(**kwargs) return pipeline def bias_correct_pipeline(self, **kwargs): # @UnusedVariable @IgnorePep8 """ Corrects B1 field inhomogeneities """ bias_method = self.switch('bias_correct_method') pipeline = self.create_pipeline( name='bias_correct', inputs=[ DatasetSpec('preproc', nifti_gz_format), DatasetSpec('brain_mask', nifti_gz_format), DatasetSpec('grad_dirs', fsl_bvecs_format), DatasetSpec('bvalues', fsl_bvals_format) ], outputs=[DatasetSpec('bias_correct', nifti_gz_format)], desc="Corrects for B1 field inhomogeneity", version=1, citations=[ fast_cite, (n4_cite if bias_method == 'ants' else fsl_cite) ], **kwargs) # Create bias correct node bias_correct = pipeline.create_node( DWIBiasCorrect(), name="bias_correct", requirements=( [mrtrix3_req] + [ants2_req if bias_method == 'ants' else fsl509_req])) bias_correct.inputs.method = bias_method # Gradient merge node fsl_grads = pipeline.create_node(MergeTuple(2), name="fsl_grads") # Connect nodes pipeline.connect(fsl_grads, 'out', bias_correct, 'grad_fsl') # Connect to inputs pipeline.connect_input('grad_dirs', fsl_grads, 'in1') pipeline.connect_input('bvalues', fsl_grads, 'in2') pipeline.connect_input('preproc', bias_correct, 'in_file') pipeline.connect_input('brain_mask', bias_correct, 'mask') # Connect to outputs pipeline.connect_output('bias_correct', bias_correct, 'out_file') # Check inputs/output are connected return pipeline def intensity_normalisation_pipeline(self, **kwargs): pipeline = self.create_pipeline( name='intensity_normalization', inputs=[ DatasetSpec('bias_correct', nifti_gz_format), DatasetSpec('brain_mask', nifti_gz_format), DatasetSpec('grad_dirs', fsl_bvecs_format), DatasetSpec('bvalues', fsl_bvals_format) ], outputs=[ DatasetSpec('norm_intensity', mrtrix_format), DatasetSpec('norm_intens_fa_template', mrtrix_format, frequency='per_project'), DatasetSpec('norm_intens_wm_mask', mrtrix_format, frequency='per_project') ], desc="Corrects for B1 field inhomogeneity", version=1, citations=[mrtrix3_req], **kwargs) # Convert from nifti to mrtrix format grad_merge = pipeline.create_node(MergeTuple(2), name="grad_merge") mrconvert = pipeline.create_node(MRConvert(), name='mrconvert') mrconvert.inputs.out_ext = '.mif' # Set up join nodes fields = ['dwis', 'masks', 'subject_ids', 'visit_ids'] join_subjects = pipeline.create_join_subjects_node( IdentityInterface(fields), joinfield=fields, name='join_subjects') join_visits = pipeline.create_join_visits_node(Chain(fields), joinfield=fields, name='join_visits') # Set up expand nodes select = pipeline.create_node(SelectSession(), name='expand') # Intensity normalization intensity_norm = pipeline.create_node(DWIIntensityNorm(), name='dwiintensitynorm') # Connect inputs pipeline.connect_input('bias_correct', mrconvert, 'in_file') pipeline.connect_input('grad_dirs', grad_merge, 'in1') pipeline.connect_input('bvalues', grad_merge, 'in2') pipeline.connect_subject_id(join_subjects, 'subject_ids') pipeline.connect_visit_id(join_subjects, 'visit_ids') pipeline.connect_subject_id(select, 'subject_id') pipeline.connect_visit_id(select, 'visit_id') pipeline.connect_input('brain_mask', join_subjects, 'masks') # Internal connections pipeline.connect(grad_merge, 'out', mrconvert, 'grad_fsl') pipeline.connect(mrconvert, 'out_file', join_subjects, 'dwis') pipeline.connect(join_subjects, 'dwis', join_visits, 'dwis') pipeline.connect(join_subjects, 'masks', join_visits, 'masks') pipeline.connect(join_subjects, 'subject_ids', join_visits, 'subject_ids') pipeline.connect(join_subjects, 'visit_ids', join_visits, 'visit_ids') pipeline.connect(join_visits, 'dwis', intensity_norm, 'in_files') pipeline.connect(join_visits, 'masks', intensity_norm, 'masks') pipeline.connect(join_visits, 'subject_ids', select, 'subject_ids') pipeline.connect(join_visits, 'visit_ids', select, 'visit_ids') pipeline.connect(intensity_norm, 'out_files', select, 'items') # Connect outputs pipeline.connect_output('norm_intensity', select, 'item') pipeline.connect_output('norm_intens_fa_template', intensity_norm, 'fa_template') pipeline.connect_output('norm_intens_wm_mask', intensity_norm, 'wm_mask') return pipeline def tensor_pipeline(self, **kwargs): # @UnusedVariable """ Fits the apparrent diffusion tensor (DT) to each voxel of the image """ pipeline = self.create_pipeline( name='tensor', inputs=[ DatasetSpec('bias_correct', nifti_gz_format), DatasetSpec('grad_dirs', fsl_bvecs_format), DatasetSpec('bvalues', fsl_bvals_format), DatasetSpec('brain_mask', nifti_gz_format) ], outputs=[DatasetSpec('tensor', nifti_gz_format)], desc=("Estimates the apparent diffusion tensor in each " "voxel"), version=1, citations=[], **kwargs) # Create tensor fit node dwi2tensor = pipeline.create_node(FitTensor(), name='dwi2tensor') dwi2tensor.inputs.out_file = 'dti.nii.gz' # Gradient merge node fsl_grads = pipeline.create_node(MergeTuple(2), name="fsl_grads") # Connect nodes pipeline.connect(fsl_grads, 'out', dwi2tensor, 'grad_fsl') # Connect to inputs pipeline.connect_input('grad_dirs', fsl_grads, 'in1') pipeline.connect_input('bvalues', fsl_grads, 'in2') pipeline.connect_input('bias_correct', dwi2tensor, 'in_file') pipeline.connect_input('brain_mask', dwi2tensor, 'in_mask') # Connect to outputs pipeline.connect_output('tensor', dwi2tensor, 'out_file') # Check inputs/output are connected return pipeline def fa_pipeline(self, **kwargs): # @UnusedVariable """ Fits the apparrent diffusion tensor (DT) to each voxel of the image """ pipeline = self.create_pipeline( name='fa', inputs=[ DatasetSpec('tensor', nifti_gz_format), DatasetSpec('brain_mask', nifti_gz_format) ], outputs=[ DatasetSpec('fa', nifti_gz_format), DatasetSpec('adc', nifti_gz_format) ], desc=("Calculates the FA and ADC from a tensor image"), version=1, citations=[], **kwargs) # Create tensor fit node metrics = pipeline.create_node(TensorMetrics(), name='metrics', requirements=[mrtrix3_req]) metrics.inputs.out_fa = 'fa.nii.gz' metrics.inputs.out_adc = 'adc.nii.gz' # Connect to inputs pipeline.connect_input('tensor', metrics, 'in_file') pipeline.connect_input('brain_mask', metrics, 'in_mask') # Connect to outputs pipeline.connect_output('fa', metrics, 'out_fa') pipeline.connect_output('adc', metrics, 'out_adc') # Check inputs/output are connected return pipeline def response_pipeline(self, **kwargs): # @UnusedVariable """ Estimates the fibre orientation distribution (FOD) using constrained spherical deconvolution Parameters ---------- response_algorithm : str Algorithm used to estimate the response """ outputs = [DatasetSpec('wm_response', text_format)] if self.multi_tissue: outputs.append(DatasetSpec('gm_response', text_format)) outputs.append(DatasetSpec('csf_response', text_format)) pipeline = self.create_pipeline( name='response', inputs=[ DatasetSpec('bias_correct', nifti_gz_format), DatasetSpec('grad_dirs', fsl_bvecs_format), DatasetSpec('bvalues', fsl_bvals_format), DatasetSpec('brain_mask', nifti_gz_format) ], outputs=outputs, desc=("Estimates the fibre response function"), version=1, citations=[mrtrix_cite], **kwargs) # Create fod fit node response = pipeline.create_node(ResponseSD(), name='response', requirements=[mrtrix3_req]) response.inputs.algorithm = self.switch('response_algorithm') # Gradient merge node fsl_grads = pipeline.create_node(MergeTuple(2), name="fsl_grads") # Connect nodes pipeline.connect(fsl_grads, 'out', response, 'grad_fsl') # Connect to inputs pipeline.connect_input('grad_dirs', fsl_grads, 'in1') pipeline.connect_input('bvalues', fsl_grads, 'in2') pipeline.connect_input('bias_correct', response, 'in_file') pipeline.connect_input('brain_mask', response, 'in_mask') # Connect to outputs pipeline.connect_output('wm_response', response, 'wm_file') if self.multi_tissue: response.inputs.gm_file = 'gm.txt' response.inputs.csf_file = 'csf.txt' pipeline.connect_output('gm_response', response, 'gm_file') pipeline.connect_output('csf_response', response, 'csf_file') # Check inputs/output are connected return pipeline def average_response_pipeline(self, **kwargs): """ Averages the estimate response function over all subjects in the project """ pipeline = self.create_pipeline( name='average_response', inputs=[DatasetSpec('wm_response', text_format)], outputs=[ DatasetSpec('avg_response', text_format, frequency='per_project') ], desc=("Averages the fibre response function over the project"), version=1, citations=[mrtrix_cite], **kwargs) join_subjects = pipeline.create_join_subjects_node( IdentityInterface(['responses']), name='join_subjects', joinfield=['responses']) join_visits = pipeline.create_join_visits_node(Chain(['responses']), name='join_visits', joinfield=['responses']) avg_response = pipeline.create_node(AverageResponse(), name='avg_response') # Connect inputs pipeline.connect_input('wm_response', join_subjects, 'responses') # Connect inter-nodes pipeline.connect(join_subjects, 'responses', join_visits, 'responses') pipeline.connect(join_visits, 'responses', avg_response, 'in_files') # Connect outputs pipeline.connect_output('avg_response', avg_response, 'out_file') # Check inputs/output are connected return pipeline # # algorithm = traits.Enum( # 'csd', # 'msmt_csd', # argstr='%s', # position=-8, # mandatory=True, # desc='FOD algorithm') # in_file = File( # exists=True, # argstr='%s', # position=-7, # mandatory=True, # desc='input DWI image') # wm_txt = File( # argstr='%s', position=-6, mandatory=True, desc='WM response text file') # wm_odf = File( # 'wm.mif', # argstr='%s', # position=-5, # usedefault=True, # mandatory=True, # desc='output WM ODF') # gm_txt = File(argstr='%s', position=-4, desc='GM response text file') # gm_odf = File('gm.mif', usedefault=True, argstr='%s', # position=-3, desc='output GM ODF') # csf_txt = File(argstr='%s', position=-2, desc='CSF response text file') # csf_odf = File('csf.mif', usedefault=True, argstr='%s', # position=-1, desc='output CSF ODF') # mask_file = File(exists=True, argstr='-mask %s', desc='mask image') def fod_pipeline(self, **kwargs): # @UnusedVariable """ Estimates the fibre orientation distribution (FOD) using constrained spherical deconvolution Parameters ---------- """ inputs = [ DatasetSpec('bias_correct', nifti_gz_format), DatasetSpec('grad_dirs', fsl_bvecs_format), DatasetSpec('bvalues', fsl_bvals_format), DatasetSpec('wm_response', text_format), DatasetSpec('brain_mask', nifti_gz_format) ] outputs = [DatasetSpec('wm_odf', mrtrix_format)] if self.multi_tissue: inputs.append(DatasetSpec('gm_response', text_format)) inputs.append(DatasetSpec('csf_response', text_format)) outputs.append(DatasetSpec('gm_odf', mrtrix_format)) outputs.append(DatasetSpec('csf_odf', mrtrix_format)) algorithm = 'msmt_csd' else: algorithm = 'csd' pipeline = self.create_pipeline( name='fod', inputs=inputs, outputs=outputs, desc=("Estimates the fibre orientation distribution in each" " voxel"), version=1, citations=[mrtrix_cite], **kwargs) # Create fod fit node dwi2fod = pipeline.create_node(EstimateFOD(), name='dwi2fod', requirements=[mrtrix3_req]) dwi2fod.inputs.algorithm = algorithm # Gradient merge node fsl_grads = pipeline.create_node(MergeTuple(2), name="fsl_grads") # Connect nodes pipeline.connect(fsl_grads, 'out', dwi2fod, 'grad_fsl') # Connect to inputs pipeline.connect_input('grad_dirs', fsl_grads, 'in1') pipeline.connect_input('bvalues', fsl_grads, 'in2') pipeline.connect_input('bias_correct', dwi2fod, 'in_file') pipeline.connect_input('wm_response', dwi2fod, 'wm_txt') pipeline.connect_input('brain_mask', dwi2fod, 'mask_file') # Connect to outputs pipeline.connect_output('wm_odf', dwi2fod, 'wm_odf') # If multi-tissue if self.multi_tissue: pipeline.connect_input('gm_response', dwi2fod, 'gm_txt') pipeline.connect_input('csf_response', dwi2fod, 'csf_txt') dwi2fod.inputs.gm_odf = 'gm.mif' dwi2fod.inputs.csf_odf = 'csf.mif' pipeline.connect_output('gm_odf', dwi2fod, 'gm_odf') pipeline.connect_output('csf_odf', dwi2fod, 'csf_odf') # Check inputs/output are connected return pipeline def tbss_pipeline(self, **kwargs): # @UnusedVariable pipeline = self.create_pipeline( name='tbss', inputs=[DatasetSpec('fa', nifti_gz_format)], outputs=[ DatasetSpec('tbss_mean_fa', nifti_gz_format), DatasetSpec('tbss_proj_fa', nifti_gz_format, frequency='per_project'), DatasetSpec('tbss_skeleton', nifti_gz_format, frequency='per_project'), DatasetSpec('tbss_skeleton_mask', nifti_gz_format, frequency='per_project') ], version=1, citations=[tbss_cite, fsl_cite], **kwargs) # Create TBSS workflow tbss = create_tbss_all(name='tbss') # Connect inputs pipeline.connect_input('fa', tbss, 'inputnode.fa_list') # Connect outputs pipeline.connect_output('tbss_mean_fa', tbss, 'outputnode.meanfa_file') pipeline.connect_output('tbss_proj_fa', tbss, 'outputnode.projectedfa_file') pipeline.connect_output('tbss_skeleton', tbss, 'outputnode.skeleton_file') pipeline.connect_output('tbss_skeleton_mask', tbss, 'outputnode.skeleton_mask') # Check inputs/output are connected return pipeline def extract_b0_pipeline(self, **kwargs): # @UnusedVariable """ Extracts the b0 images from a DWI study and takes their mean """ pipeline = self.create_pipeline( name='extract_b0', inputs=[ DatasetSpec('bias_correct', nifti_gz_format), DatasetSpec('grad_dirs', fsl_bvecs_format), DatasetSpec('bvalues', fsl_bvals_format) ], outputs=[DatasetSpec('b0', nifti_gz_format)], desc="Extract b0 image from a DWI study", version=1, citations=[mrtrix_cite], **kwargs) # Gradient merge node fsl_grads = pipeline.create_node(MergeTuple(2), name="fsl_grads") # Extraction node extract_b0s = pipeline.create_node(ExtractDWIorB0(), name='extract_b0s', requirements=[mrtrix3_req]) extract_b0s.inputs.bzero = True extract_b0s.inputs.quiet = True # FIXME: Need a registration step before the mean # Mean calculation node mean = pipeline.create_node(MRMath(), name="mean", requirements=[mrtrix3_req]) mean.inputs.axis = 3 mean.inputs.operation = 'mean' mean.inputs.quiet = True # Convert to Nifti mrconvert = pipeline.create_node(MRConvert(), name="output_conversion", requirements=[mrtrix3_req]) mrconvert.inputs.out_ext = '.nii.gz' mrconvert.inputs.quiet = True # Connect inputs pipeline.connect_input('bias_correct', extract_b0s, 'in_file') pipeline.connect_input('grad_dirs', fsl_grads, 'in1') pipeline.connect_input('bvalues', fsl_grads, 'in2') # Connect between nodes pipeline.connect(extract_b0s, 'out_file', mean, 'in_files') pipeline.connect(fsl_grads, 'out', extract_b0s, 'grad_fsl') pipeline.connect(mean, 'out_file', mrconvert, 'in_file') # Connect outputs pipeline.connect_output('b0', mrconvert, 'out_file') pipeline.assert_connected() # Check inputs/outputs are connected return pipeline def track_gen_pipeline(self, **kwargs): pipeline = self.create_pipeline( name='extract_b0', inputs=[ DatasetSpec('bias_correct', nifti_gz_format), DatasetSpec('grad_dirs', fsl_bvecs_format), DatasetSpec('bvalues', fsl_bvals_format) ], outputs=[DatasetSpec('b0', nifti_gz_format)], desc="Extract b0 image from a DWI study", version=1, citations=[mrtrix_cite]) return pipeline def intrascan_alignment_pipeline(self, **kwargs): pipeline = self.create_pipeline( name='affine_mat_generation', inputs=[ DatasetSpec('preproc', nifti_gz_format), DatasetSpec('eddy_par', eddy_par_format) ], outputs=[DatasetSpec('align_mats', directory_format)], desc=("Generation of the affine matrices for the main dwi " "sequence starting from eddy motion parameters"), version=1, citations=[fsl_cite], **kwargs) aff_mat = pipeline.create_node(AffineMatrixGeneration(), name='gen_aff_mats') pipeline.connect_input('preproc', aff_mat, 'reference_image') pipeline.connect_input('eddy_par', aff_mat, 'motion_parameters') pipeline.connect_output('align_mats', aff_mat, 'affine_matrices') return pipeline
class MRIStudy(Study, metaclass=StudyMetaClass): add_data_specs = [ DatasetSpec('primary', dicom_format), DatasetSpec('coreg_ref_brain', nifti_gz_format, desc=("A reference scan to coregister the primary " "scan to. Should be brain extracted"), optional=True), DatasetSpec('coreg_matrix', text_matrix_format, 'linear_coregistration_pipeline'), DatasetSpec('preproc', nifti_gz_format, 'preproc_pipeline'), DatasetSpec('brain', nifti_gz_format, 'brain_extraction_pipeline', desc="The brain masked image"), DatasetSpec('brain_mask', nifti_gz_format, 'brain_extraction_pipeline', desc="Mask of the brain"), DatasetSpec('coreg_brain', nifti_gz_format, 'linear_coregistration_pipeline', desc=""), DatasetSpec('coreg_to_atlas', nifti_gz_format, 'coregister_to_atlas_pipeline'), DatasetSpec('coreg_to_atlas_coeff', nifti_gz_format, 'coregister_to_atlas_pipeline'), DatasetSpec('coreg_to_atlas_mat', text_matrix_format, 'coregister_to_atlas_pipeline'), DatasetSpec('coreg_to_atlas_warp', nifti_gz_format, 'coregister_to_atlas_pipeline'), DatasetSpec('coreg_to_atlas_report', gif_format, 'coregister_to_atlas_pipeline'), DatasetSpec('wm_seg', nifti_gz_format, 'segmentation_pipeline'), DatasetSpec('dcm_info', text_format, 'header_info_extraction_pipeline'), DatasetSpec('motion_mats', motion_mats_format, 'motion_mat_pipeline'), DatasetSpec('qformed', nifti_gz_format, 'qform_transform_pipeline'), DatasetSpec('qform_mat', text_matrix_format, 'qform_transform_pipeline'), FieldSpec('tr', float, 'header_info_extraction_pipeline'), FieldSpec('start_time', str, 'header_info_extraction_pipeline'), FieldSpec('real_duration', str, 'header_info_extraction_pipeline'), FieldSpec('tot_duration', str, 'header_info_extraction_pipeline'), FieldSpec('ped', str, 'header_info_extraction_pipeline'), FieldSpec('pe_angle', str, 'header_info_extraction_pipeline') ] add_parameter_specs = [ ParameterSpec('bet_robust', True), ParameterSpec('bet_f_threshold', 0.5), ParameterSpec('bet_reduce_bias', False), ParameterSpec('bet_g_threshold', 0.0), ParameterSpec('MNI_template', os.path.join(atlas_path, 'MNI152_T1_2mm.nii.gz')), ParameterSpec('MNI_template_brain', os.path.join(atlas_path, 'MNI152_T1_2mm_brain.nii.gz')), ParameterSpec( 'MNI_template_mask', os.path.join(atlas_path, 'MNI152_T1_2mm_brain_mask.nii.gz')), ParameterSpec('optibet_gen_report', False), ParameterSpec('fnirt_atlas', 'MNI152'), ParameterSpec('fnirt_resolution', '2mm'), ParameterSpec('fnirt_intensity_model', 'global_non_linear_with_bias'), ParameterSpec('fnirt_subsampling', [4, 4, 2, 2, 1, 1]), ParameterSpec('preproc_new_dims', ('RL', 'AP', 'IS')), ParameterSpec('preproc_resolution', None, dtype=list), ParameterSpec( 'flirt_degrees_of_freedom', 6, desc=("Number of degrees of freedom used in the registration. " "Default is 6 -> affine transformation.")), ParameterSpec( 'flirt_cost_func', 'normmi', desc=("Cost function used for the registration. Can be one of " "'mutualinfo', 'corratio', 'normcorr', 'normmi', 'leastsq'," " 'labeldiff', 'bbr'")), ParameterSpec( 'flirt_qsform', False, desc=("Whether to use the QS form supplied in the input image " "header (the image coordinates of the FOV supplied by the " "scanner")) ] add_switch_specs = [ SwitchSpec('linear_reg_method', 'flirt', choices=('flirt', 'spm', 'ants')), SwitchSpec('atlas_coreg_tool', 'ants', choices=('fnirt', 'ants')), SwitchSpec('bet_method', 'fsl_bet', choices=('fsl_bet', 'optibet')) ] @property def coreg_brain_spec(self): """ The name of the dataset after registration has been applied. If registration is not required, i.e. a reg_ref is not supplied then it is simply the 'brain' dataset. """ if 'coreg_ref_brain' in self.input_names: name = 'coreg_brain' else: name = 'brain' return DatasetSpec(name, nifti_gz_format) def linear_coregistration_pipeline(self, **kwargs): if self.branch('linear_reg_method', 'flirt'): pipeline = self._flirt_factory('linear_coreg', 'brain', 'coreg_ref_brain', 'coreg_brain', 'coreg_matrix', **kwargs) elif self.branch('linear_reg_method', 'ants'): pipeline = self._ants_linear_coreg_pipeline( 'linear_coreg', 'brain', 'coreg_ref_brain', 'coreg_brain', 'coreg_matrix', **kwargs) elif self.branch('linear_reg_method', 'spm'): raise NotImplementedError else: self.unhandled_branch('linear_reg_method') return pipeline def qform_transform_pipeline(self, **kwargs): return self._qform_transform_factory('qform_transform', 'brain', 'coreg_ref_brain', 'qformed', 'qform_mat', **kwargs) def _flirt_factory(self, name, to_reg, ref, reg, matrix, **kwargs): """ Registers a MR scan to a refernce MR scan using FSL's FLIRT command Parameters ---------- name : str Name for the generated pipeline to_reg : str Name of the DatasetSpec to register ref : str Name of the DatasetSpec to use as a reference reg : str Name of the DatasetSpec to output as registered image matrix : str Name of the DatasetSpec to output as registration matrix """ pipeline = self.create_pipeline( name=name, inputs=[ DatasetSpec(to_reg, nifti_gz_format), DatasetSpec(ref, nifti_gz_format) ], outputs=[ DatasetSpec(reg, nifti_gz_format), DatasetSpec(matrix, text_matrix_format) ], desc="Registers a MR scan against a reference image using FLIRT", version=1, citations=[fsl_cite], **kwargs) flirt = pipeline.create_node(interface=FLIRT(), name='flirt', requirements=[fsl5_req], wall_time=5) # Set registration parameters flirt.inputs.dof = self.parameter('flirt_degrees_of_freedom') flirt.inputs.cost = self.parameter('flirt_cost_func') flirt.inputs.cost_func = self.parameter('flirt_cost_func') flirt.inputs.output_type = 'NIFTI_GZ' # Connect inputs pipeline.connect_input(to_reg, flirt, 'in_file') pipeline.connect_input(ref, flirt, 'reference') # Connect outputs pipeline.connect_output(reg, flirt, 'out_file') pipeline.connect_output(matrix, flirt, 'out_matrix_file') return pipeline def _qform_transform_factory(self, name, to_reg, ref, qformed, qformed_mat, **kwargs): pipeline = self.create_pipeline( name=name, inputs=[ DatasetSpec(to_reg, nifti_gz_format), DatasetSpec(ref, nifti_gz_format) ], outputs=[ DatasetSpec(qformed, nifti_gz_format), DatasetSpec(qformed_mat, text_matrix_format) ], desc="Registers a MR scan against a reference image", version=1, citations=[fsl_cite], **kwargs) flirt = pipeline.create_node(interface=FLIRT(), name='flirt', requirements=[fsl5_req], wall_time=5) flirt.inputs.uses_qform = True flirt.inputs.apply_xfm = True # Connect inputs pipeline.connect_input(to_reg, flirt, 'in_file') pipeline.connect_input(ref, flirt, 'reference') # Connect outputs pipeline.connect_output(qformed, flirt, 'out_file') pipeline.connect_output(qformed_mat, flirt, 'out_matrix_file') return pipeline def _spm_coreg_pipeline(self, **kwargs): # @UnusedVariable """ Coregisters T2 image to T1 image using SPM's "Register" method. NB: Default values come from the W2MHS toolbox """ pipeline = self.create_pipeline( name='registration', inputs=[ DatasetSpec('t1', nifti_format), DatasetSpec('t2', nifti_format) ], outputs=[DatasetSpec('t2_coreg_t1', nifti_format)], desc="Coregister T2-weighted images to T1", version=1, citations=[spm_cite], **kwargs) coreg = pipeline.create_node(Coregister(), name='coreg', requirements=[spm12_req], wall_time=30) coreg.inputs.jobtype = 'estwrite' coreg.inputs.cost_function = 'nmi' coreg.inputs.separation = [4, 2] coreg.inputs.tolerance = [ 0.02, 0.02, 0.02, 0.001, 0.001, 0.001, 0.01, 0.01, 0.01, 0.001, 0.001, 0.001 ] coreg.inputs.fwhm = [7, 7] coreg.inputs.write_interp = 4 coreg.inputs.write_wrap = [0, 0, 0] coreg.inputs.write_mask = False coreg.inputs.out_prefix = 'r' # Connect inputs pipeline.connect_input('t1', coreg, 'target') pipeline.connect_input('t2', coreg, 'source') # Connect outputs pipeline.connect_output('t2_coreg_t1', coreg, 'coregistered_source') return pipeline def _ants_linear_coreg_pipeline(self, name, to_reg, ref, reg, matrix, **kwargs): pipeline = self.create_pipeline( name=name, inputs=[ DatasetSpec(to_reg, nifti_gz_format), DatasetSpec(ref, nifti_gz_format) ], outputs=[ DatasetSpec(reg, nifti_gz_format), DatasetSpec(matrix, text_matrix_format) ], desc="Registers a MR scan against a reference image using ANTs", version=1, citations=[], **kwargs) ants_linear = pipeline.create_node(AntsRegSyn(num_dimensions=3, transformation='r', out_prefix='reg2hires'), name='ANTs_linear_Reg', wall_time=10, requirements=[ants2_req]) pipeline.connect_input(ref, ants_linear, 'ref_file') pipeline.connect_input(to_reg, ants_linear, 'input_file') pipeline.connect_output(reg, ants_linear, 'reg_file') pipeline.connect_output(matrix, ants_linear, 'regmat') return pipeline def brain_extraction_pipeline(self, in_file='preproc', **kwargs): if self.branch('bet_method', 'fsl_bet'): pipeline = self._fsl_bet_brain_extraction_pipeline( in_file, **kwargs) elif self.branch('bet_method', 'optibet'): pipeline = self._optiBET_brain_extraction_pipeline( in_file, **kwargs) else: self.unhandled_branch('bet_method') return pipeline def _fsl_bet_brain_extraction_pipeline(self, in_file, **kwargs): """ Generates a whole brain mask using FSL's BET command. """ pipeline = self.create_pipeline( name='brain_extraction', inputs=[DatasetSpec(in_file, nifti_gz_format)], outputs=[ DatasetSpec('brain', nifti_gz_format), DatasetSpec('brain_mask', nifti_gz_format) ], desc="Generate brain mask from mr_scan", version=1, citations=[fsl_cite, bet_cite, bet2_cite], **kwargs) # Create mask node bet = pipeline.create_node(interface=fsl.BET(), name="bet", requirements=[fsl509_req]) bet.inputs.mask = True bet.inputs.output_type = 'NIFTI_GZ' if self.parameter('bet_robust'): bet.inputs.robust = True if self.parameter('bet_reduce_bias'): bet.inputs.reduce_bias = True bet.inputs.frac = self.parameter('bet_f_threshold') bet.inputs.vertical_gradient = self.parameter('bet_g_threshold') # Connect inputs/outputs pipeline.connect_input(in_file, bet, 'in_file') pipeline.connect_output('brain', bet, 'out_file') pipeline.connect_output('brain_mask', bet, 'mask_file') return pipeline def _optiBET_brain_extraction_pipeline(self, in_file, **kwargs): """ Generates a whole brain mask using a modified optiBET approach. """ outputs = [ DatasetSpec('brain', nifti_gz_format), DatasetSpec('brain_mask', nifti_gz_format) ] if self.parameter('optibet_gen_report'): outputs.append(DatasetSpec('optiBET_report', gif_format)) pipeline = self.create_pipeline( name='brain_extraction', inputs=[DatasetSpec(in_file, nifti_gz_format)], outputs=outputs, desc=("Modified implementation of optiBET.sh"), version=1, citations=[fsl_cite], **kwargs) mni_reg = pipeline.create_node(AntsRegSyn(num_dimensions=3, transformation='s', out_prefix='T12MNI', num_threads=4), name='T1_reg', wall_time=25, requirements=[ants2_req]) mni_reg.inputs.ref_file = self.parameter('MNI_template') pipeline.connect_input(in_file, mni_reg, 'input_file') merge_trans = pipeline.create_node(Merge(2), name='merge_transforms', wall_time=1) pipeline.connect(mni_reg, 'inv_warp', merge_trans, 'in1') pipeline.connect(mni_reg, 'regmat', merge_trans, 'in2') trans_flags = pipeline.create_node(Merge(2), name='trans_flags', wall_time=1) trans_flags.inputs.in1 = False trans_flags.inputs.in2 = True apply_trans = pipeline.create_node(ApplyTransforms(), name='ApplyTransform', wall_time=7, memory=24000, requirements=[ants2_req]) apply_trans.inputs.input_image = self.parameter('MNI_template_mask') apply_trans.inputs.interpolation = 'NearestNeighbor' apply_trans.inputs.input_image_type = 3 pipeline.connect(merge_trans, 'out', apply_trans, 'transforms') pipeline.connect(trans_flags, 'out', apply_trans, 'invert_transform_flags') pipeline.connect_input(in_file, apply_trans, 'reference_image') maths1 = pipeline.create_node(fsl.ImageMaths( suffix='_optiBET_brain_mask', op_string='-bin'), name='binarize', wall_time=5, requirements=[fsl5_req]) pipeline.connect(apply_trans, 'output_image', maths1, 'in_file') maths2 = pipeline.create_node(fsl.ImageMaths(suffix='_optiBET_brain', op_string='-mas'), name='mask', wall_time=5, requirements=[fsl5_req]) pipeline.connect_input(in_file, maths2, 'in_file') pipeline.connect(maths1, 'out_file', maths2, 'in_file2') if self.parameter('optibet_gen_report'): slices = pipeline.create_node(FSLSlices(), name='slices', wall_time=5, requirements=[fsl5_req]) slices.inputs.outname = 'optiBET_report' pipeline.connect_input(in_file, slices, 'im1') pipeline.connect(maths2, 'out_file', slices, 'im2') pipeline.connect_output('optiBET_report', slices, 'report') pipeline.connect_output('brain_mask', maths1, 'out_file') pipeline.connect_output('brain', maths2, 'out_file') return pipeline def coregister_to_atlas_pipeline(self, **kwargs): if self.branch('atlas_coreg_tool', 'fnirt'): pipeline = self._fsl_fnirt_to_atlas_pipeline(**kwargs) elif self.branch('atlas_coreg_tool', 'ants'): pipeline = self._ants_to_atlas_pipeline(**kwargs) else: self.unhandled_branch('atlas_coreg_tool') return pipeline # @UnusedVariable @IgnorePep8 def _fsl_fnirt_to_atlas_pipeline(self, **kwargs): """ Registers a MR scan to a refernce MR scan using FSL's nonlinear FNIRT command Parameters ---------- atlas : Which atlas to use, can be one of 'mni_nl6' """ pipeline = self.create_pipeline( name='coregister_to_atlas', inputs=[ DatasetSpec('preproc', nifti_gz_format), DatasetSpec('brain_mask', nifti_gz_format), DatasetSpec('brain', nifti_gz_format) ], outputs=[ DatasetSpec('coreg_to_atlas', nifti_gz_format), DatasetSpec('coreg_to_atlas_coeff', nifti_gz_format) ], desc=("Nonlinearly registers a MR scan to a standard space," "e.g. MNI-space"), version=1, citations=[fsl_cite], **kwargs) # Get the reference atlas from FSL directory ref_atlas = get_atlas_path(self.parameter('fnirt_atlas'), 'image', resolution=self.parameter('resolution')) ref_mask = get_atlas_path(self.parameter('fnirt_atlas'), 'mask_dilated', resolution=self.parameter('resolution')) ref_brain = get_atlas_path(self.parameter('fnirt_atlas'), 'brain', resolution=self.parameter('resolution')) # Basic reorientation to standard MNI space reorient = pipeline.create_node(Reorient2Std(), name='reorient', requirements=[fsl5_req]) reorient.inputs.output_type = 'NIFTI_GZ' reorient_mask = pipeline.create_node(Reorient2Std(), name='reorient_mask', requirements=[fsl5_req]) reorient_mask.inputs.output_type = 'NIFTI_GZ' reorient_brain = pipeline.create_node(Reorient2Std(), name='reorient_brain', requirements=[fsl5_req]) reorient_brain.inputs.output_type = 'NIFTI_GZ' # Affine transformation to MNI space flirt = pipeline.create_node(interface=FLIRT(), name='flirt', requirements=[fsl5_req], wall_time=5) flirt.inputs.reference = ref_brain flirt.inputs.dof = 12 flirt.inputs.output_type = 'NIFTI_GZ' # Nonlinear transformation to MNI space fnirt = pipeline.create_node(interface=FNIRT(), name='fnirt', requirements=[fsl5_req], wall_time=60) fnirt.inputs.ref_file = ref_atlas fnirt.inputs.refmask_file = ref_mask fnirt.inputs.output_type = 'NIFTI_GZ' intensity_model = self.parameter('fnirt_intensity_model') if intensity_model is None: intensity_model = 'none' fnirt.inputs.intensity_mapping_model = intensity_model fnirt.inputs.subsampling_scheme = self.parameter('fnirt_subsampling') fnirt.inputs.fieldcoeff_file = True fnirt.inputs.in_fwhm = [8, 6, 5, 4.5, 3, 2] fnirt.inputs.ref_fwhm = [8, 6, 5, 4, 2, 0] fnirt.inputs.regularization_lambda = [300, 150, 100, 50, 40, 30] fnirt.inputs.apply_intensity_mapping = [1, 1, 1, 1, 1, 0] fnirt.inputs.max_nonlin_iter = [5, 5, 5, 5, 5, 10] # Apply mask if corresponding subsampling scheme is 1 # (i.e. 1-to-1 resolution) otherwise don't. apply_mask = [int(s == 1) for s in self.parameter('fnirt_subsampling')] fnirt.inputs.apply_inmask = apply_mask fnirt.inputs.apply_refmask = apply_mask # Connect nodes pipeline.connect(reorient_brain, 'out_file', flirt, 'in_file') pipeline.connect(reorient, 'out_file', fnirt, 'in_file') pipeline.connect(reorient_mask, 'out_file', fnirt, 'inmask_file') pipeline.connect(flirt, 'out_matrix_file', fnirt, 'affine_file') # Set registration parameters # TOD: Need to work out which parameters to use # Connect inputs pipeline.connect_input('preproc', reorient, 'in_file') pipeline.connect_input('brain_mask', reorient_mask, 'in_file') pipeline.connect_input('brain', reorient_brain, 'in_file') # Connect outputs pipeline.connect_output('coreg_to_atlas', fnirt, 'warped_file') pipeline.connect_output('coreg_to_atlas_coeff', fnirt, 'fieldcoeff_file') return pipeline def _ants_to_atlas_pipeline(self, **kwargs): pipeline = self.create_pipeline( name='coregister_to_atlas', inputs=[DatasetSpec('coreg_ref_brain', nifti_gz_format)], outputs=[ DatasetSpec('coreg_to_atlas', nifti_gz_format), DatasetSpec('coreg_to_atlas_mat', text_matrix_format), DatasetSpec('coreg_to_atlas_warp', nifti_gz_format), DatasetSpec('coreg_to_atlas_report', gif_format) ], desc=("Nonlinearly registers a MR scan to a standard space," "e.g. MNI-space"), version=1, citations=[fsl_cite], **kwargs) ants_reg = pipeline.create_node(AntsRegSyn(num_dimensions=3, transformation='s', out_prefix='Struct2MNI', num_threads=4), name='Struct2MNI_reg', wall_time=25, requirements=[ants2_req]) ref_brain = self.parameter('MNI_template_brain') ants_reg.inputs.ref_file = ref_brain pipeline.connect_input('coreg_ref_brain', ants_reg, 'input_file') slices = pipeline.create_node(FSLSlices(), name='slices', wall_time=1, requirements=[fsl5_req]) slices.inputs.outname = 'coreg_to_atlas_report' slices.inputs.im1 = self.parameter('MNI_template') pipeline.connect(ants_reg, 'reg_file', slices, 'im2') pipeline.connect_output('coreg_to_atlas', ants_reg, 'reg_file') pipeline.connect_output('coreg_to_atlas_mat', ants_reg, 'regmat') pipeline.connect_output('coreg_to_atlas_warp', ants_reg, 'warp_file') pipeline.connect_output('coreg_to_atlas_report', slices, 'report') return pipeline def segmentation_pipeline(self, img_type=2, **kwargs): pipeline = self.create_pipeline( name='FAST_segmentation', inputs=[DatasetSpec('brain', nifti_gz_format)], outputs=[DatasetSpec('wm_seg', nifti_gz_format)], desc="White matter segmentation of the reference image", version=1, citations=[fsl_cite], **kwargs) fast = pipeline.create_node(fsl.FAST(), name='fast', requirements=[fsl509_req]) fast.inputs.img_type = img_type fast.inputs.segments = True fast.inputs.out_basename = 'Reference_segmentation' pipeline.connect_input('brain', fast, 'in_files') split = pipeline.create_node(Split(), name='split') split.inputs.splits = [1, 1, 1] split.inputs.squeeze = True pipeline.connect(fast, 'tissue_class_files', split, 'inlist') if img_type == 1: pipeline.connect_output('wm_seg', split, 'out3') elif img_type == 2: pipeline.connect_output('wm_seg', split, 'out2') else: raise ArcanaUsageError( "'img_type' parameter can either be 1 or 2 (not {})".format( img_type)) return pipeline def preproc_pipeline(self, in_file_name='primary', **kwargs): """ Performs basic preprocessing, such as swapping dimensions into standard orientation and resampling (if required) Parameters ------- new_dims : tuple(str)[3] A 3-tuple with the new orientation of the image (see FSL swap dim) resolution : list(float)[3] | None New resolution of the image. If None no resampling is performed """ pipeline = self.create_pipeline( name='preproc_pipeline', inputs=[DatasetSpec(in_file_name, nifti_gz_format)], outputs=[DatasetSpec('preproc', nifti_gz_format)], desc=("Dimensions swapping to ensure that all the images " "have the same orientations."), version=1, citations=[fsl_cite], **kwargs) swap = pipeline.create_node(fsl.utils.Reorient2Std(), name='fslreorient2std', requirements=[fsl509_req]) # swap.inputs.new_dims = self.parameter('preproc_new_dims') pipeline.connect_input(in_file_name, swap, 'in_file') if self.parameter('preproc_resolution') is not None: resample = pipeline.create_node(MRResize(), name="resample", requirements=[mrtrix3_req]) resample.inputs.voxel = self.parameter('preproc_resolution') pipeline.connect(swap, 'out_file', resample, 'in_file') pipeline.connect_output('preproc', resample, 'out_file') else: pipeline.connect_output('preproc', swap, 'out_file') return pipeline def header_info_extraction_pipeline(self, **kwargs): if self.input('primary').format != dicom_format: raise ArcanaUsageError( "Can only extract header info if 'primary' dataset " "is provided in DICOM format ({})".format( self.input('primary').format)) return self.header_info_extraction_pipeline_factory( 'header_info_extraction', 'primary', **kwargs) def header_info_extraction_pipeline_factory(self, name, dcm_in_name, multivol=False, output_prefix='', **kwargs): tr = output_prefix + 'tr' start_time = output_prefix + 'start_time' tot_duration = output_prefix + 'tot_duration' real_duration = output_prefix + 'real_duration' ped = output_prefix + 'ped' pe_angle = output_prefix + 'pe_angle' dcm_info = output_prefix + 'dcm_info' outputs = [ FieldSpec(tr, dtype=float), FieldSpec(start_time, dtype=str), FieldSpec(tot_duration, dtype=str), FieldSpec(real_duration, dtype=str), FieldSpec(ped, dtype=str), FieldSpec(pe_angle, dtype=str), DatasetSpec(dcm_info, text_format) ] pipeline = self.create_pipeline( name=name, inputs=[DatasetSpec(dcm_in_name, dicom_format)], outputs=outputs, desc=("Pipeline to extract the most important scan " "information from the image header"), version=1, citations=[], **kwargs) hd_extraction = pipeline.create_node(DicomHeaderInfoExtraction(), name='hd_info_extraction') hd_extraction.inputs.multivol = multivol pipeline.connect_input(dcm_in_name, hd_extraction, 'dicom_folder') pipeline.connect_output(tr, hd_extraction, 'tr') pipeline.connect_output(start_time, hd_extraction, 'start_time') pipeline.connect_output(tot_duration, hd_extraction, 'tot_duration') pipeline.connect_output(real_duration, hd_extraction, 'real_duration') pipeline.connect_output(ped, hd_extraction, 'ped') pipeline.connect_output(pe_angle, hd_extraction, 'pe_angle') pipeline.connect_output(dcm_info, hd_extraction, 'dcm_info') return pipeline def motion_mat_pipeline(self, **kwargs): if not self.spec('coreg_matrix').derivable: logger.info("Cannot derive 'coreg_matrix' for {} required for " "motion matrix calculation, assuming that it " "is the reference study".format(self)) inputs = [DatasetSpec('primary', dicom_format)] ref = True else: inputs = [ DatasetSpec('coreg_matrix', text_matrix_format), DatasetSpec('qform_mat', text_matrix_format) ] if 'align_mats' in self.data_spec_names(): inputs.append(DatasetSpec('align_mats', directory_format)) ref = False pipeline = self.create_pipeline( name='motion_mat_calculation', inputs=inputs, outputs=[DatasetSpec('motion_mats', motion_mats_format)], desc=("Motion matrices calculation"), version=1, citations=[fsl_cite], **kwargs) mm = pipeline.create_node(MotionMatCalculation(), name='motion_mats') if ref: mm.inputs.reference = True pipeline.connect_input('primary', mm, 'dummy_input') else: pipeline.connect_input('coreg_matrix', mm, 'reg_mat') pipeline.connect_input('qform_mat', mm, 'qform_mat') if 'align_mats' in self.data_spec_names(): pipeline.connect_input('align_mats', mm, 'align_mats') pipeline.connect_output('motion_mats', mm, 'motion_mats') return pipeline
class TestDerivableStudy(with_metaclass(StudyMetaClass, Study)): add_data_specs = [ AcquiredFilesetSpec('required', text_format), AcquiredFilesetSpec('optional', text_format, optional=True), FilesetSpec('derivable', text_format, 'pipeline1'), FilesetSpec('missing_input', text_format, 'pipeline2'), FilesetSpec('another_derivable', text_format, 'pipeline3'), FilesetSpec('requires_switch', text_format, 'pipeline3'), FilesetSpec('requires_switch2', text_format, 'pipeline4'), FilesetSpec('requires_foo', text_format, 'pipeline5'), FilesetSpec('requires_bar', text_format, 'pipeline5')] add_param_specs = [ SwitchSpec('switch', False), SwitchSpec('branch', 'foo', ('foo', 'bar', 'wee'))] def pipeline1(self, **name_maps): pipeline = self.pipeline( 'pipeline1', desc="", references=[], name_maps=name_maps) identity = pipeline.add('identity', IdentityInterface(['a'])) pipeline.connect_input('required', identity, 'a') pipeline.connect_output('derivable', identity, 'a') return pipeline def pipeline2(self, **name_maps): pipeline = self.pipeline( 'pipeline2', desc="", references=[], name_maps=name_maps) identity = pipeline.add('identity', IdentityInterface(['a', 'b'])) pipeline.connect_input('required', identity, 'a') pipeline.connect_input('optional', identity, 'b') pipeline.connect_output('missing_input', identity, 'a') return pipeline def pipeline3(self, **name_maps): pipeline = self.pipeline( 'pipeline3', desc="", references=[], name_maps=name_maps) identity = pipeline.add('identity', IdentityInterface(['a', 'b'])) pipeline.connect_input('required', identity, 'a') pipeline.connect_input('required', identity, 'b') pipeline.connect_output('another_derivable', identity, 'a') if self.branch('switch'): pipeline.connect_output('requires_switch', identity, 'b') return pipeline def pipeline4(self, **name_maps): pipeline = self.pipeline( 'pipeline4', desc="", references=[], name_maps=name_maps) identity = pipeline.add('identity', IdentityInterface(['a'])) pipeline.connect_input('requires_switch', identity, 'a') pipeline.connect_output('requires_switch2', identity, 'a') return pipeline def pipeline5(self, **name_maps): pipeline = self.pipeline( 'pipeline5', desc="", references=[], name_maps=name_maps) identity = pipeline.add('identity', IdentityInterface(['a'])) pipeline.connect_input('required', identity, 'a') if self.branch('branch', 'foo'): pipeline.connect_output('requires_foo', identity, 'a') elif self.branch('branch', 'bar'): pipeline.connect_output('requires_bar', identity, 'a') else: self.unhandled_branch('branch') return pipeline