Remote cortical degeneration related to structural connectivity following recent small subcortical infarcts.
The 7 matches
- [1] § Methods › Neuroimaging data processing ↔ cvdproc/pipelines/smri/fsl_anat_pipeline.py, lines 9 to 76 · score 0.68 · fsl_anat, Structural MRI, MNI space, tissue, pipeline, preprocessing · Report a mismatch
- [2] § Methods › Estimation of lesion-connected cortical region › Comparison between direct and indirect approaches ↔ cvdproc/pipelines/dmri/fdt/fdt_nipype.py, lines 1079 to 1160 · score 0.67 · medium connectivity, low connectivity, high connectivity, sulc, curv, thickness · Report a mismatch
- [3] § Methods › Estimation of lesion-connected cortical region › Direct estimation ↔ cvdproc/pipelines/dmri/dwi_pipeline.py, lines 1564 to 1630 · score 0.63 · Probabilistic tractography, seed mask, medium, native, threshold, space · Report a mismatch
- [4] § Methods › Neuroimaging data processing ↔ cvdproc/pipelines/smri/scn_pipeline.py, lines 10 to 70 · score 0.62 · structural covariance network, FreeSurfer, recon, pipeline, field · Report a mismatch
- [5] § Methods › cSVD score and vascular risk factor (VRF) score ↔ cvdproc/pipelines/smri/csvd_quantification/wmh/wmh_location_nipype.py, lines 40 to 114 · score 0.58 · periventricular WMH, deep WMH, Fazekas, cSVD · Report a mismatch
- [6] § Methods › Neuroimaging data processing ↔ cvdproc/pipelines/dmri/dwi_pipeline.py, lines 676 to 704 · score 0.57 · denoising, Synb0, MRtrix3, TOPUP, eddy, EPI · Report a mismatch
- [7] § Methods › Neuroimaging data processing ↔ cvdproc/pipelines/bash/synb0/run_synb0.sh, lines 64 to 143 · score 0.56 · Synb0 DISCO, EPI, FSL, undistorted, field, MRI · Report a mismatch
Paper
Loaded from Europe PMC by your browser; OSCR does not store it: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
Python · 2,919 lines · 195 KB · MIT · 2 matches
- import os
- import pandas as pd
- import json
- import nibabel as nib
- import numpy as np
- import glob
- from cvdproc.config.paths import get_package_path
- from nipype import Node, Workflow, MapNode, Function
- from nipype.interfaces.utility import IdentityInterface, Merge, Select
- from cvdproc.bids_data.rename_bids_file import rename_bids_file
- # nipype built-in interfaces
- from nipype.interfaces.fsl import FLIRT, ExtractROI, ConvertXFM, MultiImageMaths
- from nipype.interfaces.freesurfer import MRIConvert
- from nipype.interfaces.mrtrix3 import Tractography, MRConvert, DWIPreproc, BuildConnectome, LabelConvert
- # custom interfaces
- from cvdproc.pipelines.common.files import FilterExisting, MergeFilename
- from cvdproc.pipelines.common.pad_dwi import PadDWI
- from cvdproc.pipelines.common.flip_b_table import FlipBTable
- from cvdproc.pipelines.common.register import Tkregister2fs2t1w, MRIConvertApplyWarp, MRIVol2Vol
- from cvdproc.pipelines.common.dwi_resample import ANTsResampleDWI
- from cvdproc.pipelines.common.image_calc import RemoveMaskRegion, CalculateScalarMaps
- from cvdproc.pipelines.dmri.fdt.fdt_nipype import B0AllAndAcqparam, IndexTxt, Topup, EddyCuda, OrderEddyOutputs, B0RefAndBrainMask, DTIFitBIDS, BedpostxGPUCustom, Probtrackx, ReadSingleValue, ExtractSurfaceParameters, ApplyGiiMaskToMgh, ApplyIndexTxtToMgh, DefineConnectionLevel
- from cvdproc.pipelines.dmri.synb0.synb0_nipype import Synb0
- from cvdproc.pipelines.dmri.psmd.psmd_nipype import PSMDCommandLine
- from cvdproc.pipelines.dmri.alps.alps_nipype import ALPS
- from cvdproc.pipelines.dmri.dsistudio.create_src_nipype import CreateSRC
- from cvdproc.pipelines.dmri.dsistudio.reconstruction_nipype import DSIstudioReconstruction, DSIstudioReconstructionOldVer, DSIstudioReconstruction2, DSIStudioBvecToFSL
- from cvdproc.pipelines.dmri.dsistudio.tracking_nipype import DSIstudioTracking
- from cvdproc.pipelines.dmri.dsistudio.tract_analysis import TractStatsInterface
- from cvdproc.pipelines.dmri.dsistudio.fixing_nipype import FixDSIStudioDWIAndBvec
- from cvdproc.pipelines.dmri.pved.pved_nipype import PVeD, PVeDAtropos, PVeDPrepareMNI
- from cvdproc.pipelines.dmri.mrtrix3.tcksample_nipype import TckSampleCommand, CalculateMeanTckSample
- from cvdproc.pipelines.dmri.mrtrix3.denoise_degibbs_nipype import MrtrixDenoise, MrtrixDegibbs
- from cvdproc.pipelines.dmri.mrtrix3.connectome_nipype import RemoveWMH, ConnectomePrepare
- from cvdproc.pipelines.dmri.mrtrix3.utils import RemoveTractRegion
- from cvdproc.pipelines.dmri.stats.dti_scalar_maps import GenerateNAWMMask, CalculateTDIWeightedScalars, TckSampleMultiScalarProfile, TckSampleMultiScalarBundle
- from cvdproc.pipelines.dmri.dipy.dipy_freewater_dti import FreeWaterTensor
- from cvdproc.pipelines.dmri.dipy.dipy_dki import DKIFit
- from cvdproc.pipelines.dmri.dipy.dipy_degibbs import DipyDegibbs
- from cvdproc.pipelines.dmri.freewater.single_shell_freewater import SingleShellFW
- from cvdproc.pipelines.dmri.freewater.markvcid_freewater import MarkVCIDFreeWater
- from cvdproc.pipelines.dmri.qsiprep.register import QSIPrepOrigToACPC
- from cvdproc.pipelines.dmri.visualization.vis import PlotTckOnSliceCmd
- from cvdproc.pipelines.smri.mirror.mirror_nipype import MirrorMask
- from cvdproc.pipelines.smri.fsl.fsl_anat_nipype import FSLANAT
- from cvdproc.pipelines.smri.freesurfer.synthstrip import SynthStrip
- from cvdproc.pipelines.smri.freesurfer.utils import MRIBinarize, MRIvol2surf, MergeRibbon
- class DWIPipeline:
- def __init__(self,
- subject: object,
- session: object,
- output_path: str,
- use_which_dwi: str = None,
- use_which_t1w: str = None,
- use_which_flair: str = None,
- use_freesurfer_longitudinal: bool = False,
- preprocess: bool = False,
- output_resolution: float = 2.0,
- degibbs: bool = True,
- flip_b_table_axis: list = [],
- preprocess_method: str = 'fdt',
- synb0: bool = False,
- use_which_reverse_b0: str = None,
- dti_fit: bool = False,
- dti_low_b: bool = True,
- dwi_t1w_register: bool = False,
- dki_fit: bool = False,
- dsistudio_dti: bool = False,
- dsistudio_gqi: bool = False,
- dsistudio_qsdr: bool = False,
- amico_noddi: bool = False,
- connectome: list = [],
- tractography: list = [],
- seed_mask: str = 'lesion_mask',
- use_which_mask: str = None,
- dtialps: bool = False,
- dtialps_register_method: int = 1,
- pved: bool = False,
- pved_method: str = 'v2',
- freewater: list = [],
- psmd: bool = False,
- psmd_exclude_seed_mask: bool = False,
- visual_pathway_analysis: bool = False,
- visual_pathway_analysis_flip_y: bool = False,
- calculate_dwi_metrics: bool = False,
- use_synthseg_wm_mask: bool = False,
- exclude_seed_mask: bool = True,
- exclude_wmh_mask: bool = False,
- keep_preproc_intermediate: bool = True,
- extract_from: str = None,
- **kwargs):
- """
- DWI pipeline
- Args:
- subject (object): Subject object
- session (object): Session object
- output_path (str): Output path
- use_which_dwi (str, optional): Which DWI file to use. Defaults to None.
- use_which_t1w (str, optional): Which T1w file to use. Defaults to None.
- use_which_flair (str, optional): Which FLAIR file to use. Defaults to None.
- use_freesurfer_longitudinal (bool, optional): Whether to use FreeSurfer longitudinal processing outpus. Defaults to False.
- preprocess (bool, optional): Whether to preprocess the DWI data. Defaults to False.
- output_resolution (float, optional): Output resolution in mm. Defaults to 2.0.
- degibbs (bool, optional): Whether to perform degibbsing. Defaults to True.
- flip_b_table_axis (list, optional): List of axes to flip the b-table. Defaults to []. [0], [1], [2] for x, y, z axes.
- preprocess_method (str, optional): Preprocessing method. 'fdt', 'mrtrix3', 'dsistudio_mc' or 'post_qsiprep'. Defaults to 'fdt'.
- synb0 (bool, optional): Whether to use Synb0 for TOPUP. Defaults to False.
- use_which_reverse_b0 (str, optional): Which reverse coded b0 image to use. Defaults to None.
- dti_fit (bool, optional): Whether to fit DTI model using FSL dtifit. Defaults to False.
- dti_low_b (bool, optional): Whether to only use b<=1500 volumes for DTI fitting. Defaults to True.
- dwi_t1w_register (bool, optional): Whether to register DWI to T1w space. Defaults to False.
- dki_fit (bool, optional): Whether to fit DKI model using Dipy. Defaults to False.
- dsistudio_dti (bool, optional): Whether to fit DTI model using DSI Studio. Defaults to False.
- dsistudio_gqi (bool, optional): Whether to fit GQI model using DSI Studio. Defaults to False.
- dsistudio_qsdr (bool, optional): Whether to fit QSDR model using DSI Studio. Defaults to False.
- amico_noddi (bool, optional): Whether to fit NODDI model using AMICO. Defaults to False.
- connectome (list, optional): Whether to create a connectome (Global). Defaults to []. Subsets of ['fdt', 'mrtrix3', 'dsistudio'].
- tractography (list, optional): Tractography method (ROI-based). Defaults to []. Subsets of ['fdt', 'mrtrix3', 'dsistudio'].
- seed_mask (str, optional): Folder name for seed mask. Expected to find <bids_dir>/derivatives/<seed_mask>/sub-<id>/ses-<id>/*<use_which_mask>*.nii.gz. Defaults to 'lesion_mask'. Must be in the T1w space.
- use_which_mask (str, optional): Which mask file to use (in the processed DWI space). Defaults to None.
- dtialps (bool, optional): Whether to perform DTI-ALPS analysis. Defaults to False.
- dtialps_register_method (int, optional): Registration method for DTI-ALPS. 1 for FA + FLIRT, 2 for FA + synthmorph.
- pved (bool, optional): Whether to perform PVeD analysis. Defaults to False.
- pved_method (str, optional): PVeD implementation. ``v1`` uses the legacy MATLAB/QSDR workflow; ``v2`` uses the faster Atropos workflow with MNI-space DTI maps. Defaults to ``v2``.
- freewater (list, optional): List of freewater estimation methods. Defaults to []. Subsets of ['single_shell_freewater', 'markvcid_freewater', 'dti_freewater'].
- psmd (bool, optional): Whether to perform PSMD analysis. Defaults to False.
- psmd_exclude_seed_mask (bool, optional): Whether to exclude seed mask in PSMD calculation. Defaults to False.
- visual_pathway_analysis (bool, optional): Whether to perform visual pathway analysis. Defaults to False. (An ongoing project! Experimental feature.)
- visual_pathway_analysis_flip_y (bool, optional): Whether to fix header (flip y) in visual pathway analysis. Defaults to False. (An ongoing project! Experimental feature.)
- calculate_dwi_metrics (bool, optional): Whether to calculate DWI scalar maps. Defaults to False.
- use_synthseg_wm_mask (bool, optional): Whether to use SynthSeg WM mask (instead of Freesurfer results). Defaults to False.
- exclude_seed_mask (bool, optional): Whether to exclude seed mask in DWI metrics calculation (For NAWM). Defaults to True.
- exclude_wmh_mask (bool, optional): Whether to exclude WMH mask in DWI metrics calculation (For NAWM). Defaults to False.
- keep_preproc_intermediate (bool, optional): Whether to keep the preproc_intermediate directory after preprocessing. Defaults to True. Set False to clean up after the final DWI, gradients, and brain mask are ready; workflow caches and QSIPrep outputs are not removed.
- extract_from (str, optional): Folder name to extract results from.
- """
- self.subject = subject
- self.session = session
- self.output_path = os.path.abspath(output_path)
- self.use_which_dwi = use_which_dwi # which dwi file to use
- self.use_which_t1w = use_which_t1w # which t1w file to use
- self.use_which_flair = use_which_flair # which flair file to use
- self.use_freesurfer_longitudinal = use_freesurfer_longitudinal # whether to use FreeSurfer longitudinal processing outputs
- # Preprocessing
- self.preprocess = preprocess # whether to preprocess
- self.output_resolution = output_resolution # output resolution in mm
- self.degibbs = degibbs # whether to perform degibbsing
- self.flip_b_table_axis = flip_b_table_axis # axes to flip the b-table
- self.preprocess_method = preprocess_method # preprocessing method
- self.synb0 = synb0 # whether to use synthetic b0 image for TOPUP
- self.use_which_reverse_b0 = use_which_reverse_b0 # whether to use reverse b0 image for TOPUP. if synb0 is True, this will be ignored
- # Renconstruction
- self.dti_fit = dti_fit # whether to fit DTI model using FSL FDT
- self.dti_low_b = dti_low_b # whether to only use b<=1500 volumes for DTI fitting
- self.dwi_t1w_register = dwi_t1w_register # whether to register DWI to T1w space
- self.dki_fit = dki_fit # whether to fit DKI model using Dipy
- self.dsistudio_dti = dsistudio_dti # whether to fit DTI model using DSI Studio
- self.dsistudio_gqi = dsistudio_gqi # whether to fit GQI model using DSI Studio
- self.dsistudio_qsdr = dsistudio_qsdr # whether to fit QSDR model using DSI Studio
- self.amico_noddi = amico_noddi # whether to fit NODDI model using AMICO
- # Global connectome and connectivity analysis
- self.connectome = connectome # whether to create a connectome
- # ROI-based tractography
- self.tractography = tractography
- self.seed_mask = seed_mask # seed mask for probabilistic tractography
- self.use_which_mask = use_which_mask # which mask to use (in T1w space)
- # DTI-ALPS
- self.dtialps = dtialps
- self.dtialps_register_method = dtialps_register_method # 1 for FA + FLIRT, 2 for T1 + FNIRT, 3 for T1 + Synthmorph
- # PVeD
- self.pved = pved
- self.pved_method = str(pved_method).lower()
- if self.pved_method not in {'v1', 'v2'}:
- raise ValueError(f"Unsupported PVeD method: {pved_method}. Expected 'v1' or 'v2'.")
- # Freewater
- self.freewater = freewater
- # PSMD
- self.psmd = psmd
- self.psmd_exclude_seed_mask = psmd_exclude_seed_mask
- # Visual Pathway Analysis (an ongoing project!)
- self.visual_pathway_analysis = visual_pathway_analysis
- self.visual_pathway_analysis_flip_y = visual_pathway_analysis_flip_y
- # Calculate scalar maps
- self.calculate_dwi_metrics = calculate_dwi_metrics # whether to calculate DWI metrics
- self.use_synthseg_wm_mask = use_synthseg_wm_mask # whether to use SynthSeg WM mask
- self.exclude_seed_mask = exclude_seed_mask # whether to exclude seed mask in DWI metrics calculation
- self.exclude_wmh_mask = exclude_wmh_mask # whether to exclude WMH mask in DWI metrics calculation
- self.keep_preproc_intermediate = keep_preproc_intermediate # whether to keep preproc_intermediate directory
- # extract results
- self.extract_from = extract_from
- #### Not user configurable ####
- base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
- self.script_path2 = os.path.join(base_dir, 'bash', 'fdt_fs_processing.sh') # FreeSurfer processing
- self.psmd_skeleton_mask = os.path.join(base_dir, 'external', 'psmd', 'skeleton_mask_2019.nii.gz')
- def check_data_requirements(self):
- """
- check if the required data is available
- :return: bool
- """
- return self.session.get_dwi_files() is not None and self.session.get_t1w_files() is not None
- def create_workflow(self):
- # Find Image Files
- os.makedirs(self.output_path, exist_ok=True)
- dwi_files = self.session.get_dwi_files()
- phase_encoding_direction_dict = {
- "i": "1 0 0",
- "i-": "-1 0 0",
- "j": "0 1 0",
- "j-": "0 -1 0",
- "k": "0 0 1",
- "k-": "0 0 -1"
- }
- if self.use_which_dwi:
- dwi_files = [file for file in dwi_files if self.use_which_dwi in file['path']]
- if len(dwi_files) != 4:
- raise FileNotFoundError(f"No specific DWI file found for {self.use_which_dwi} or more than one found or not in standard BIDS format.")
- else:
- print("No specific DWI file selected. Using the first complete set.")
- grouped_files = {}
- for file in dwi_files:
- base_name = file['path'].split('_acq-')[0]
- if base_name not in grouped_files:
- grouped_files[base_name] = []
- grouped_files[base_name].append(file)
- first_complete_group = None
- for group in grouped_files.values():
- file_types = {file['type'] for file in group}
- if {'dwi', 'bval', 'bvec', 'json'}.issubset(file_types):
- first_complete_group = group
- break
- if not first_complete_group:
- raise FileNotFoundError("No complete set of DWI files found.")
- dwi_files = first_complete_group
- dwi_files_dict = {file['type']: file['path'] for file in dwi_files}
- print(f"[DWI Pipeline] Using DWI files: {dwi_files_dict}")
- dwi_image = dwi_files_dict['dwi']
- dwi_json = dwi_files_dict['json']
- dwi_bval = dwi_files_dict['bval']
- dwi_bvec = dwi_files_dict['bvec']
- # Get the total readout time and phase encoding direction from the json file
- with open(dwi_json, 'r') as f:
- dwi_json_data = json.load(f)
- try:
- total_readout_time = dwi_json_data['TotalReadoutTime']
- # as a str
- total_readout_time = str(total_readout_time)
- phase_encoding_direction = dwi_json_data['PhaseEncodingDirection']
- except KeyError:
- print("[Warn] Seems TotalReadoutTime or PhaseEncodingDirection not found in the json file.")
- total_readout_time = '0.05'
- phase_encoding_direction = 'j-' # default phase encoding direction
- print("[Warn] Set TotalReadoutTime to 0.05. But make sure the value is the same in all acquisitions.")
- print("[Warn] Set PhaseEncodingDirection to j-. It might not be correct")
- # Find Reverse b0 Image
- if not self.synb0 and self.use_which_reverse_b0 is not None:
- # TODO: Use the 'IntendedFor' field in the json file to find the reverse b0 image
- fmap_files = self.session.get_fmap_files()
- reverse_b0_files = [file for file in fmap_files if self.use_which_reverse_b0 in file['path']]
- if len(reverse_b0_files) != 4:
- raise FileNotFoundError(f"No specific reverse b0 file found for {self.use_which_reverse_b0} or more than one found.")
- reverse_b0_files_dict = {file['type']: file['path'] for file in reverse_b0_files}
- print(f"Using reverse b0 files: {reverse_b0_files_dict}")
- reverse_b0_image = reverse_b0_files_dict['dwi']
- reverse_b0_json = reverse_b0_files_dict['json']
- reverse_b0_bval = reverse_b0_files_dict['bval']
- reverse_b0_bvec = reverse_b0_files_dict['bvec']
- with open(reverse_b0_json, 'r') as f:
- reverse_b0_json_data = json.load(f)
- try:
- reverse_b0_total_readout_time = str(reverse_b0_json_data['TotalReadoutTime'])
- reverse_b0_phase_encoding_direction = reverse_b0_json_data['PhaseEncodingDirection']
- except KeyError:
- print("Seems TotalReadoutTime or PhaseEncodingDirection not found in the json file.")
- if not reverse_b0_total_readout_time:
- reverse_b0_total_readout_time = '0.05'
- print("Set TotalReadoutTime to 0.05. But make sure the value is the same in all acquisitions.")
- else:
- reverse_b0_image = None
- # get T1w image
- t1w_files = self.session.get_t1w_files()
- if self.use_which_t1w:
- t1w_files = [f for f in t1w_files if self.use_which_t1w in f]
- if len(t1w_files) != 1:
- raise FileNotFoundError(f"No specific T1w file found for {self.use_which_t1w} or more than one found.")
- t1w_file = t1w_files[0]
- print(f"[DWI Pipeline] Using T1w: {t1w_file}")
- else:
- t1w_files = [t1w_files[0]]
- t1w_file = t1w_files[0]
- print(f"No specific T1w file selected. Using the first one: {t1w_file}.")
- fs_output = self.session.freesurfer_dir
- # if fs_output exists
- if fs_output is None:
- fs_subjects_dir = ''
- fs_subject_id = ''
- fs_output_process = False
- print("[DWI Pipeline] No FreeSurfer output found. Skipping related processing.")
- else:
- fs_subjects_dir = os.path.dirname(fs_output)
- fs_subject_id = os.path.basename(fs_output)
- # automatically do related anat preprocessing
- fs_output_process = True
- print(f"[DWI Pipeline] FreeSurfer output found: {fs_output}. Will do related processing.")
- # if use longitudinal outputs, check if the longitudinal output exists (fs_subjects_dir/ses-<session_id>.long.sub-<subject_id>)
- if self.use_freesurfer_longitudinal:
- session_ids = self.subject.sessions_id
- fs_long_output_dir = os.path.join(fs_subjects_dir, f'ses-{self.session.session_id}.long.sub-{self.subject.subject_id}')
- if not os.path.exists(fs_long_output_dir):
- raise FileNotFoundError(f"FreeSurfer longitudinal output not found: {fs_long_output_dir}. Please check the directory or set use_freesurfer_longitudinal to False.")
- else:
- print(f"[DWI Pipeline] FreeSurfer longitudinal output found: {fs_long_output_dir}. Related to surface metrics extraction in tractography.")
- # for session_id in session_ids:
- # fs_long_output_dir = os.path.join(fs_subjects_dir, f'ses-{session_id}.long.sub-{self.subject.subject_id}')
- # if not os.path.exists(fs_long_output_dir):
- # raise FileNotFoundError(f"FreeSurfer longitudinal output not found: {fs_long_output_dir}. Please check the directory or set use_freesurfer_longitudinal to False.")
- # else:
- # print(f"[DWI Pipeline] FreeSurfer longitudinal output found: {fs_long_output_dir}. Related to surface metrics extraction in tractography.")
- if self.pved and self.pved_method == 'v2':
- pved_t1w_to_mni_warp = os.path.join(self.subject.bids_dir, 'derivatives', 'xfm', f'sub-{self.subject.subject_id}', f'ses-{self.session.session_id}', f'sub-{self.subject.subject_id}_ses-{self.session.session_id}_from-T1w_to-MNI152NLin6ASym_warp.nii.gz')
- if os.path.isfile(pved_t1w_to_mni_warp):
- self.dti_fit = True
- self.dwi_t1w_register = True
- else:
- print(f"[DWI Pipeline] PVeD method='v2' requires an existing T1w-to-MNI152NLin6Asym warp, but it was not found: {pved_t1w_to_mni_warp}")
- print("[DWI Pipeline] Falling back to PVeD method='v1'.")
- self.pved_method = 'v1'
- if self.pved and self.pved_method == 'v1':
- # must run QSDR reconstruction first
- self.dsistudio_qsdr = True
- if self.visual_pathway_analysis and self.preprocess_method != 'post_qsiprep':
- self.dsistudio_gqi = True
- if self.dwi_t1w_register or self.dtialps or self.psmd:
- # must run DTI fitting first
- self.dti_fit = True
- # Decide one seed mask to use
- lesion_mask_dir = self.session._find_output(self.seed_mask)
- seed_mask = [file for file in os.listdir(lesion_mask_dir) if f'{self.use_which_mask}' in file]
- if seed_mask is not None and len(seed_mask) == 1:
- print(f"[DWI Pipeline] Using seed mask: {seed_mask[0]}.")
- seed_mask = os.path.join(lesion_mask_dir, seed_mask[0])
- elif seed_mask is not None and len(seed_mask) > 1:
- #print(f"Using the first mask found: {seed_mask[0]}.")
- seed_mask = os.path.join(lesion_mask_dir, seed_mask[0])
- print(f"[DWI Pipeline] Using seed mask: {seed_mask}.")
- else:
- seed_mask = ''
- print("[DWI Pipeline] No seed mask found.")
- #raise FileNotFoundError(f"No seed mask found in {lesion_mask_dir}. Please check the directory.")
- # Whether need mrtrix3 preproc dwi (nifti to mif)
- if 'mrtrix3' in self.connectome or 'mrtrix3' in self.tractography:
- mrtrix3_preproc = True
- else:
- mrtrix3_preproc = False
- # Whether need fdt bedpostx
- if 'fdt' in self.connectome or 'fdt' in self.tractography:
- fdt_bedpostx = True
- else:
- fdt_bedpostx = False
- t1w_to_mni_warp = os.path.join(self.subject.bids_dir, 'derivatives', 'xfm', f'sub-{self.subject.subject_id}', f'ses-{self.session.session_id}', f'sub-{self.subject.subject_id}_ses-{self.session.session_id}_from-T1w_to-MNI152NLin6ASym_warp.nii.gz')
- if not os.path.exists(t1w_to_mni_warp):
- t1w_to_mni_warp = ""
- mni_to_t1w_warp = os.path.join(self.subject.bids_dir, 'derivatives', 'xfm', f'sub-{self.subject.subject_id}', f'ses-{self.session.session_id}', f'sub-{self.subject.subject_id}_ses-{self.session.session_id}_from-MNI152NLin6ASym_to-T1w_warp.nii.gz')
- if not os.path.exists(mni_to_t1w_warp):
- mni_to_t1w_warp = ""
- # Whether need to exclude WMH mask in DWI metrics calculation
- wmh_mask_file = None
- wmh_probmap_file = None
- if self.exclude_wmh_mask or self.visual_pathway_analysis:
- wmh_output_dir = self.session._find_output('wmh_quantification')
- if wmh_output_dir is None:
- raise FileNotFoundError("WMH quantification output not found. Cannot exclude WMH mask.")
- # sub-{self.subject.subject_id}_ses-{self.session.session_id}_space-T1w_label-WMH_desc-*_mask.nii.gz
- wmh_mask_files = [file for file in os.listdir(wmh_output_dir) if f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_space-T1w_label-WMH" in file and file.endswith('_mask.nii.gz')]
- if len(wmh_mask_files) == 1:
- print(f"[DWI Pipeline] Exclude WMH mask: {wmh_mask_files[0]}.")
- wmh_mask_file = os.path.join(wmh_output_dir, wmh_mask_files[0])
- # sub-XXX_ses-XXX_space-T1w_label-WMH_desc-XXX_probmap.nii.gz
- wmh_probmap_files = [file for file in os.listdir(wmh_output_dir) if f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_space-T1w_label-WMH_desc-" in file and file.endswith('_probmap.nii.gz')]
- if len(wmh_probmap_files) == 1:
- print(f"[DWI Pipeline] WMH probmap: {wmh_probmap_files[0]}.")
- wmh_probmap_file = os.path.join(wmh_output_dir, wmh_probmap_files[0])
- else:
- raise FileNotFoundError("[DWI Pipeline] WMH probmap in T1w space not found or multiple files found. Cannot exclude WMH probmap.")
- elif len(wmh_mask_files) == 2:
- mask_excluded_files = [file for file in wmh_mask_files if "MaskExcluded" in file]
- if len(mask_excluded_files) == 1:
- print(f"[DWI Pipeline] Exclude WMH mask: {mask_excluded_files[0]}.")
- wmh_mask_file = os.path.join(wmh_output_dir, mask_excluded_files[0])
- else:
- raise FileNotFoundError("[DWI Pipeline] WMH MaskExcluded file not found or multiple files found. Cannot exclude WMH mask.")
- wmh_probmap_files = [
- file for file in os.listdir(wmh_output_dir)
- if f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_space-T1w_label-WMH_desc-" in file
- and file.endswith("_probmap.nii.gz")
- ]
- if len(wmh_probmap_files) == 1:
- print(f"[DWI Pipeline] WMH probmap: {wmh_probmap_files[0]}.")
- wmh_probmap_file = os.path.join(wmh_output_dir, wmh_probmap_files[0])
- else:
- raise FileNotFoundError("[DWI Pipeline] WMH probmap in T1w space not found or multiple files found. Cannot exclude WMH probmap.")
- else:
- raise FileNotFoundError("[DWI Pipeline] WMH mask in T1w space not found. Cannot exclude WMH mask.")
- # Check chpseg segmentation output
- if self.session.anat_seg_dir is not None:
- # ./chpseg/T1w_chp_mask.nii.gz
- chpseg_mask_file = os.path.join(self.session.anat_seg_dir, 'chpseg', 'T1w', 'T1w_chp_mask.nii.gz')
- if os.path.exists(chpseg_mask_file) and self.dwi_t1w_register:
- print(f"[DWI Pipeline] CHP segmentation mask found: {chpseg_mask_file}. Will do related processing.")
- chpseg_process = True
- else:
- chpseg_process = False
- else:
- chpseg_process = False
- # Space entity ('preprocdwi' for our custom pipeline, 'ACPC' for QSIPrep)
- if self.preprocess_method == 'post_qsiprep':
- space_entity = 'ACPC'
- else:
- space_entity = 'preprocdwi'
- # ============================================
- # Main Workflow
- # ============================================
- dwi_workflow = Workflow(name='dwi_workflow')
- inputnode = Node(IdentityInterface(fields=['bids_dir', 't1w_file',
- 'dwi_file', 'bval_file',
- 'bvec_file', 'json_file',
- 'output_path',
- 'phase_encoding_number', 'total_readout_time',
- 'fs_output', 'seed_mask_dtispace', 'seed_mask_t1wspace',
- 'fs_subjects_dir', 'fs_subject_id',
- 'fs_processing_dir', 'qsiprep_out_bvec',
- 'mni_to_t1w_warp', 't1w_to_mni_warp']), name="inputnode")
- inputnode.inputs.bids_dir = self.subject.bids_dir
- inputnode.inputs.t1w_file = t1w_file
- inputnode.inputs.dwi_file = dwi_image
- inputnode.inputs.bval_file = dwi_bval
- inputnode.inputs.bvec_file = dwi_bvec
- inputnode.inputs.json_file = dwi_json
- inputnode.inputs.output_path = self.output_path
- inputnode.inputs.phase_encoding_number = phase_encoding_direction_dict[phase_encoding_direction]
- inputnode.inputs.total_readout_time = total_readout_time
- inputnode.inputs.fs_output = fs_output
- inputnode.inputs.fs_subjects_dir = fs_subjects_dir
- inputnode.inputs.fs_subject_id = fs_subject_id
- inputnode.inputs.seed_mask_t1wspace = seed_mask
- inputnode.inputs.mni_to_t1w_warp = mni_to_t1w_warp
- inputnode.inputs.t1w_to_mni_warp = t1w_to_mni_warp
- # ============================================
- # Preprocessing (DWI)
- # ============================================
- # Node to store the b0_all and acqparam for TOPUP
- b0_all_node = Node(IdentityInterface(fields=['b0_all', 'acqparam']), name='b0_all')
- # Node to store preprocessed DWI data
- preproc_dwi_node = Node(IdentityInterface(fields=['preproc_dwi', 'bvec', 'bval', 'dwi_mask',
- 'b0', 'preproc_t1w']) # These two used for QSIPrep output
- , name='preproc_dwi')
- if self.preprocess:
- # Directories for intermediate results
- preproc_intermediate_dir = os.path.join(self.output_path, 'preproc_intermediate')
- os.makedirs(preproc_intermediate_dir, exist_ok=True)
- print("[DWI Pipeline] Output resolution set to {} mm.".format(self.output_resolution))
- print("[DWI Pipeline] Flip b-table axis: {}.".format(self.flip_b_table_axis))
- # Padding: make sure the slices in the z direction are even (TOPUP requirement)
- # If no need to pad, the output will be the same as input
- padding_node = Node(PadDWI(), name='padding')
- dwi_workflow.connect(inputnode, 'dwi_file', padding_node, 'in_dwi')
- dwi_workflow.connect(inputnode, 'bvec_file', padding_node, 'in_bvec')
- dwi_workflow.connect(inputnode, 'bval_file', padding_node, 'in_bval')
- dwi_workflow.connect(inputnode, 'json_file', padding_node, 'in_json')
- padding_node.inputs.out_file = os.path.join(preproc_intermediate_dir, rename_bids_file(dwi_image, {"desc": "cropped"}, 'dwi', '.nii.gz'))
- # Denoise using MRtrix3
- denoise_node = Node(MrtrixDenoise(), name='denoise')
- dwi_workflow.connect(padding_node, 'out_file', denoise_node, 'dwi_img')
- denoise_node.inputs.output_dwi = os.path.join(preproc_intermediate_dir, 'dwi_denoised.nii.gz')
- # # Degibbs using MRtrix3
- # degibbs_node = Node(MrtrixDegibbs(), name='degibbs')
- # dwi_workflow.connect(denoise_node, 'output_dwi_img', degibbs_node, 'dwi_img')
- # dwi_workflow.connect(denoise_node, 'output_dwi_bvec', degibbs_node, 'dwi_bvec')
- # dwi_workflow.connect(denoise_node, 'output_dwi_bval', degibbs_node, 'dwi_bval')
- # degibbs_node.inputs.output_dir = preproc_intermediate_dir
- # Degibbs using DIPY
- if self.degibbs:
- degibbs_node = Node(DipyDegibbs(), name='degibbs')
- dwi_workflow.connect(denoise_node, 'output_dwi_img', degibbs_node, 'dwi_file')
- degibbs_node.inputs.output_dwi = os.path.join(preproc_intermediate_dir, 'dwi_degibbs.nii.gz')
- else:
- print("[DWI Pipeline] Skip degibbs.")
- degibbs_node = Node(IdentityInterface(fields=['output_dwi_img', 'output_dwi_bvec', 'output_dwi_bval']), name='pesudo_degibbs')
- dwi_workflow.connect(denoise_node, 'output_dwi_img', degibbs_node, 'output_dwi_img')
- dwi_workflow.connect(inputnode, 'bvec_file', degibbs_node, 'output_dwi_bvec')
- dwi_workflow.connect(inputnode, 'bval_file', degibbs_node, 'output_dwi_bval')
- # Get b0 image for TOPUP
- # Need a b0_all image and acqparam.txt file for FSL TOPUP
- if self.synb0:
- print("[DWI Pipeline] Use Synb0 for TOPUP.")
- synb0_node = Node(Synb0(), name='synb0')
- dwi_workflow.connect([
- (inputnode, synb0_node, [('t1w_file', 't1w_img'),
- ('json_file', 'dwi_json')]),
- (degibbs_node, synb0_node, [('output_dwi_img', 'dwi_img')])
- ])
- synb0_node.inputs.fmap_output_dir = os.path.join(self.subject.bids_dir, f"sub-{self.subject.subject_id}", f"ses-{self.session.session_id}", 'fmap')
- synb0_node.inputs.output_path_synb0 = os.path.join(self.subject.bids_dir, 'derivatives', 'synb0', f'sub-{self.subject.subject_id}', f"ses-{self.session.session_id}")
- dwi_workflow.connect(synb0_node, 'acqparam', b0_all_node, 'acqparam')
- dwi_workflow.connect(synb0_node, 'b0_all', b0_all_node, 'b0_all')
- elif reverse_b0_image is not None:
- print("[DWI Pipeline] Use reverse b0 image for TOPUP.")
- create_b0_acqparam_node = Node(B0AllAndAcqparam(), name='create_b0_acqparam')
- dwi_workflow.connect([
- (inputnode, create_b0_acqparam_node, [('phase_encoding_number', 'phase_encoding_number'),
- ('total_readout_time', 'total_readout_time')]),
- (degibbs_node, create_b0_acqparam_node, [('output_dwi_img', 'dwi_img'),
- ('output_dwi_bval', 'dwi_bval')])
- ])
- create_b0_acqparam_node.inputs.output_path = preproc_intermediate_dir
- create_b0_acqparam_node.inputs.reverse_dwi_img = reverse_b0_image
- create_b0_acqparam_node.inputs.reverse_dwi_bval = reverse_b0_bval
- create_b0_acqparam_node.inputs.reverse_phase_encoding_number = phase_encoding_direction_dict[reverse_b0_phase_encoding_direction]
- create_b0_acqparam_node.inputs.reverse_total_readout_time = reverse_b0_total_readout_time
- dwi_workflow.connect(create_b0_acqparam_node, 'b0_all', b0_all_node, 'b0_all')
- dwi_workflow.connect(create_b0_acqparam_node, 'acqparam', b0_all_node, 'acqparam')
- else:
- if self.preprocess_method != "dsistudio_mc":
- raise FileNotFoundError("No reverse b0 image found for TOPUP. Please check the directory or set synb0 to True to use synthetic b0 image.")
- print("[DWI Pipeline] No reverse b0 image available.")
- # Main preprocessing workflow, according to the preprocess method
- if self.preprocess_method == 'fdt':
- print("[DWI Pipeline] Use FSL FDT for DWI preprocessing.")
- topup_node = Node(Topup(), name='topup')
- dwi_workflow.connect([
- (b0_all_node, topup_node, [('b0_all', 'b0_all_file'),
- ('acqparam', 'acqparam_file')])
- ])
- topup_node.inputs.output_basename = os.path.join(preproc_intermediate_dir, 'topup_results')
- topup_node.inputs.output_b0_basename = os.path.join(preproc_intermediate_dir, 'hifi_b0')
- topup_node.inputs.config_file = 'b02b0.cnf'
- extract_b0_node = Node(ExtractROI(), name='extract_b0')
- dwi_workflow.connect(topup_node, 'b0_image', extract_b0_node, 'in_file')
- extract_b0_node.inputs.roi_file = os.path.join(preproc_intermediate_dir, 'dwi_b0.nii.gz')
- extract_b0_node.inputs.t_min = 0
- extract_b0_node.inputs.t_size = 1
- create_dwi_mask_node = Node(SynthStrip(), name='create_dwi_mask')
- dwi_workflow.connect([
- (extract_b0_node, create_dwi_mask_node, [('roi_file', 'image')])
- ])
- dwi_mask_filepath = os.path.join(preproc_intermediate_dir, 'dwi_brain_mask.nii.gz')
- create_dwi_mask_node.inputs.mask_file = dwi_mask_filepath
- create_index_node = Node(IndexTxt(), name='create_index')
- dwi_workflow.connect(inputnode, 'bval_file', create_index_node, 'bval_file')
- create_index_node.inputs.output_dir = preproc_intermediate_dir
- eddy_node = Node(EddyCuda(), name='eddy')
- dwi_workflow.connect([
- (degibbs_node, eddy_node, [('output_dwi_img', 'dwi_file')]),
- (inputnode, eddy_node, [('bval_file', 'bval_file'), ('bvec_file', 'bvec_file')]),
- (b0_all_node, eddy_node, [('acqparam', 'acqparam_file')]),
- (create_index_node, eddy_node, [('index_file', 'index_file')]),
- (create_dwi_mask_node, eddy_node, [('mask_file', 'mask_file')]),
- (topup_node, eddy_node, [('topup_basename', 'topup_basename')]),
- ])
- eddy_node.inputs.output_basename = os.path.join(preproc_intermediate_dir, 'eddy_corrected_data')
- order_eddy_outputs_node = Node(OrderEddyOutputs(), name='order_eddy_outputs')
- dwi_workflow.connect(eddy_node, 'output_filename', order_eddy_outputs_node, 'eddy_output_filename')
- dwi_workflow.connect(eddy_node, 'bvals', order_eddy_outputs_node, 'bval')
- order_eddy_outputs_node.inputs.output_resolution = self.output_resolution
- dwi_workflow.connect(eddy_node, 'eddy_output_dir', order_eddy_outputs_node, 'eddy_output_dir')
- dwi_workflow.connect(inputnode, 'output_path', order_eddy_outputs_node, 'new_output_dir')
- order_eddy_outputs_node.inputs.new_output_filename = rename_bids_file(dwi_image, {"space": space_entity, "desc": "preproc"}, 'dwi', '')
- flip_b_table_node = Node(FlipBTable(), name='flip_b_table')
- dwi_workflow.connect(order_eddy_outputs_node, 'ordered_bvec', flip_b_table_node, 'in_bvec')
- flip_b_table_node.inputs.flip_axis = self.flip_b_table_axis
- flip_b_table_node.inputs.out_bvec = os.path.join(self.output_path, rename_bids_file(dwi_image, {"space": space_entity, "desc": "preproc"}, 'dwi', '.bvec'))
- create_b0ref_and_brainmask_node = Node(B0RefAndBrainMask(), name='create_b0ref_and_brainmask')
- dwi_workflow.connect(order_eddy_outputs_node, 'ordered_dwi', create_b0ref_and_brainmask_node, 'input_dwi')
- dwi_workflow.connect(order_eddy_outputs_node, 'ordered_bval', create_b0ref_and_brainmask_node, 'input_bval')
- create_b0ref_and_brainmask_node.inputs.output_dir = self.output_path
- create_b0ref_and_brainmask_node.inputs.output_b0_filename = rename_bids_file(dwi_image, {"space": space_entity}, 'dwiref', '.nii.gz')
- create_b0ref_and_brainmask_node.inputs.output_b0_mask_filename = rename_bids_file(dwi_image, {"space": space_entity, "desc": "brain"}, 'mask', '.nii.gz')
- dwi_workflow.connect(order_eddy_outputs_node, 'ordered_dwi', preproc_dwi_node, 'preproc_dwi')
- dwi_workflow.connect(flip_b_table_node, 'out_bvec', preproc_dwi_node, 'bvec')
- dwi_workflow.connect(order_eddy_outputs_node, 'ordered_bval', preproc_dwi_node, 'bval')
- dwi_workflow.connect(create_b0ref_and_brainmask_node, 'b0_brain_mask', preproc_dwi_node, 'dwi_mask')
- dwi_workflow.connect(create_b0ref_and_brainmask_node, 'b0_image', preproc_dwi_node, 'b0')
- preproc_dwi_filename = rename_bids_file(dwi_image, {"space": space_entity, "desc": "preproc"}, 'dwi', '.nii.gz')
- elif self.preprocess_method == 'mrtrix3':
- print("[DWI Pipeline] Use MRtrix3 for DWI preprocessing.")
- # Get b0_all.mif
- convert_b0_all_mif_node = Node(MRConvert(), name='convert_b0_all_mif')
- dwi_workflow.connect(b0_all_node, 'b0_all', convert_b0_all_mif_node, 'in_file')
- convert_b0_all_mif_node.inputs.out_file = os.path.join(preproc_intermediate_dir, 'b0_all.mif')
- convert_b0_all_mif_node.inputs.args = '-force'
- #convert_raw_dwi_mif_node.inputs.args = f'-import_pe_table {os.path.join(self.output_path, "acqparam.txt")}'
- # Convert denoised and degibbsed DWI to mif
- convert_denoised_degibbs_mif_node = Node(MRConvert(), name='convert_denoised_degibbs_mif')
- dwi_workflow.connect(degibbs_node, 'output_dwi_img', convert_denoised_degibbs_mif_node, 'in_file')
- dwi_workflow.connect(inputnode, 'bval_file', convert_denoised_degibbs_mif_node, 'in_bval')
- dwi_workflow.connect(inputnode, 'bvec_file', convert_denoised_degibbs_mif_node, 'in_bvec')
- convert_denoised_degibbs_mif_node.inputs.out_file = os.path.join(preproc_intermediate_dir, 'dwi_denoise_degibbs.mif')
- convert_denoised_degibbs_mif_node.inputs.args = '-force'
- # TOPUP and Eddy
- # I don't know how to integrate TOPUP and Eddy in mrtrix3 if use synb0 :(
- # preprocess
- dwi_preprocess_node = Node(DWIPreproc(), name='dwi_preprocess')
- dwi_workflow.connect(convert_denoised_degibbs_mif_node, 'out_file', dwi_preprocess_node, 'in_file')
- dwi_workflow.connect(convert_b0_all_mif_node, 'out_file', dwi_preprocess_node, 'in_epi')
- dwi_preprocess_node.inputs.out_file = os.path.join(preproc_intermediate_dir, 'dwi_preproc.mif')
- dwi_preprocess_node.inputs.rpe_options = 'pair'
- dwi_preprocess_node.inputs.pe_dir = phase_encoding_direction
- # alternative of preprocess
- # https://community.mrtrix.org/t/synb0-for-dwifslpreproc-how/6386/2
- # TODO
- # convert to nifti
- convert_preproc_dwi_node = Node(MRConvert(), name='convert_preproc_dwi')
- dwi_workflow.connect(dwi_preprocess_node, 'out_file', convert_preproc_dwi_node, 'in_file')
- convert_preproc_dwi_node.inputs.out_file = os.path.join(self.output_path, rename_bids_file(dwi_image, {"space": space_entity, "desc": "preproc"}, 'dwi', '.nii.gz'))
- convert_preproc_dwi_node.inputs.out_bvec = os.path.join(self.output_path, rename_bids_file(dwi_image, {"space": space_entity, "desc": "preproc"}, 'dwi', '.bvec'))
- convert_preproc_dwi_node.inputs.out_bval = os.path.join(self.output_path, rename_bids_file(dwi_image, {"space": space_entity, "desc": "preproc"}, 'dwi', '.bval'))
- convert_preproc_dwi_node.inputs.args = '-force'
- # get the mask
- extract_b0_node = Node(ExtractROI(), name='extract_b0')
- dwi_workflow.connect(convert_preproc_dwi_node, 'out_file', extract_b0_node, 'in_file')
- extract_b0_node.inputs.roi_file = os.path.join(preproc_intermediate_dir, 'dwi_b0.nii.gz')
- extract_b0_node.inputs.t_min = 0
- extract_b0_node.inputs.t_size = 1
- create_dwi_mask_node = Node(SynthStrip(), name='create_dwi_mask')
- dwi_workflow.connect(extract_b0_node, 'roi_file', create_dwi_mask_node, 'image')
- create_dwi_mask_node.inputs.mask_file = os.path.join(self.output_path, rename_bids_file(dwi_image, {"space": space_entity, "desc": "brain"}, 'mask', '.nii.gz'))
- dwi_workflow.connect(convert_preproc_dwi_node, 'out_file', preproc_dwi_node, 'preproc_dwi')
- dwi_workflow.connect(convert_preproc_dwi_node, 'out_bvec', preproc_dwi_node, 'bvec')
- dwi_workflow.connect(convert_preproc_dwi_node, 'out_bval', preproc_dwi_node, 'bval')
- dwi_workflow.connect(create_dwi_mask_node, 'mask_file', preproc_dwi_node, 'dwi_mask')
- preproc_dwi_filename = rename_bids_file(dwi_image, {"space": space_entity, "desc": "preproc"}, 'dwi', '.nii.gz')
- elif self.preprocess_method == 'dsistudio_mc':
- print("[DWI Pipeline] Use DSI Studio for motion correction. This is designed for DSI acquisitions without reverse phase encoding.")
- create_src_preprocess = Node(CreateSRC(), name='create_src_preprocess')
- dwi_workflow.connect(degibbs_node, 'output_dwi_img', create_src_preprocess, 'source')
- dwi_workflow.connect(inputnode, 'bval_file', create_src_preprocess, 'bval')
- dwi_workflow.connect(inputnode, 'bvec_file', create_src_preprocess, 'bvec')
- create_src_preprocess.inputs.output = os.path.join(preproc_intermediate_dir, 'dwi_temp.src.gz')
- motion_correction_node = Node(DSIstudioReconstruction2(), name='dsistudio_hmc')
- dwi_workflow.connect(create_src_preprocess, 'out_file', motion_correction_node, 'source')
- motion_correction_node.inputs.thread_count = 12
- motion_correction_node.inputs.motion_correction = 1
- motion_correction_node.inputs.check_btable = 0
- motion_correction_node.inputs.make_isotropic = self.output_resolution
- motion_correction_node.inputs.save_nii = os.path.join(self.output_path, rename_bids_file(dwi_image, {"space": space_entity, "desc": "preproc"}, 'dwi', '.nii.gz'))
- fix_bvec_node = Node(DSIStudioBvecToFSL(), name='bvec_to_fsl')
- dwi_workflow.connect(motion_correction_node, 'out_bvec', fix_bvec_node, 'in_bvec')
- fix_bvec_node.inputs.out_bvec = os.path.join(self.output_path, rename_bids_file(dwi_image, {"space": space_entity, "desc": "preproc"}, 'dwi', '.bvec'))
- dwi_bvec_fix_node = Node(FixDSIStudioDWIAndBvec(), name='dwi_bvec_fix')
- dwi_workflow.connect(motion_correction_node, 'out_nii', dwi_bvec_fix_node, 'in_file')
- dwi_workflow.connect(fix_bvec_node, 'out_bvec', dwi_bvec_fix_node, 'in_bvec')
- dwi_bvec_fix_node.inputs.out_file = os.path.join(self.output_path, rename_bids_file(dwi_image, {"space": space_entity, "desc": "preproc"}, 'dwi', '.nii.gz'))
- dwi_bvec_fix_node.inputs.out_bvec = os.path.join(self.output_path, rename_bids_file(dwi_image, {"space": space_entity, "desc": "preproc"}, 'dwi', '.bvec'))
- dwi_workflow.connect(inputnode, 'dwi_file', dwi_bvec_fix_node, 'reference_file')
- dwi_resample_node = Node(ANTsResampleDWI(), name='dwi_resample_iso')
- dwi_workflow.connect(dwi_bvec_fix_node, 'out_file', dwi_resample_node, 'in_file')
- dwi_resample_node.inputs.out_file = os.path.join(self.output_path, rename_bids_file(dwi_image, {"space": space_entity, "desc": "preproc"}, 'dwi', '.nii.gz'))
- dwi_resample_node.inputs.voxel_size = self.output_resolution
- dwi_resample_node.inputs.interpolation = 4
- dwi_resample_node.inputs.bspline_order = 3
- dwi_resample_node.inputs.pixeltype = 6
- create_b0ref_and_brainmask_node = Node(B0RefAndBrainMask(), name='create_b0ref_and_brainmask')
- dwi_workflow.connect(dwi_resample_node, 'out_file', create_b0ref_and_brainmask_node, 'input_dwi')
- dwi_workflow.connect(motion_correction_node, 'out_bval', create_b0ref_and_brainmask_node, 'input_bval')
- create_b0ref_and_brainmask_node.inputs.output_dir = self.output_path
- create_b0ref_and_brainmask_node.inputs.output_b0_filename = rename_bids_file(dwi_image, {"space": space_entity}, 'dwiref', '.nii.gz')
- create_b0ref_and_brainmask_node.inputs.output_b0_mask_filename = rename_bids_file(dwi_image, {"space": space_entity, "desc": "brain"}, 'mask', '.nii.gz')
- dwi_workflow.connect(dwi_resample_node, 'out_file', preproc_dwi_node, 'preproc_dwi')
- dwi_workflow.connect(dwi_bvec_fix_node, 'out_bvec', preproc_dwi_node, 'bvec')
- dwi_workflow.connect(motion_correction_node, 'out_bval', preproc_dwi_node, 'bval')
- dwi_workflow.connect(create_b0ref_and_brainmask_node, 'b0_brain_mask', preproc_dwi_node, 'dwi_mask')
- dwi_workflow.connect(create_b0ref_and_brainmask_node, 'b0_image', preproc_dwi_node, 'b0')
- preproc_dwi_filename = rename_bids_file(dwi_image, {"space": space_entity, "desc": "preproc"}, 'dwi', '.nii.gz')
- elif not self.preprocess and self.preprocess_method == 'post_qsiprep':
- # search for preprocessed dwi in qsiprep output
- qsiprep_dwi_path = os.path.join(self.subject.bids_dir, 'derivatives', 'qsiprep', f"sub-{self.subject.subject_id}", f"ses-{self.session.session_id}", 'dwi')
- # *space-ACPC_desc-preproc_dwi.nii.gz *space-ACPC_desc-preproc_dwi.bvec *space-ACPC_desc-preproc_dwi.bval *space-ACPC_desc-brain_mask.nii.gz
- target_dwi = None
- for file in os.listdir(qsiprep_dwi_path):
- if file.endswith('_desc-preproc_dwi.nii.gz'):
- target_dwi = os.path.join(qsiprep_dwi_path, file)
- break
- if target_dwi is None:
- raise FileNotFoundError(f"No preprocessed DWI file found in qsiprep output: {qsiprep_dwi_path}")
- target_bvec = target_dwi.replace('_dwi.nii.gz', '_dwi.bvec')
- target_bval = target_dwi.replace('_dwi.nii.gz', '_dwi.bval')
- target_mask = target_dwi.replace('_desc-preproc_dwi.nii.gz', '_desc-brain_mask.nii.gz')
- if not os.path.exists(target_bvec) or not os.path.exists(target_bval) or not os.path.exists(target_mask):
- raise FileNotFoundError(f"bvec, bval or brain mask file not found in qsiprep output: {qsiprep_dwi_path}")
- # search for preprocessed t1w in qsiprep output
- # situation 1: qsiprep/sub-XXX/anat/sub-XXX_space-ACPC_desc-preproc_T1w.nii.gz (subject-wise)
- # situation 2: qsiprep/sub-XXX/ses-YYY/anat/sub-XXX_ses-YYY_space-ACPC_desc-preproc_T1w.nii.gz (session-wise)
- qsiprep_t1w_path1 = os.path.join(self.subject.bids_dir, 'derivatives', 'qsiprep', f"sub-{self.subject.subject_id}", 'anat', f"sub-{self.subject.subject_id}_space-ACPC_desc-preproc_T1w.nii.gz")
- qsiprep_t1w_path2 = os.path.join(self.subject.bids_dir, 'derivatives', 'qsiprep', f"sub-{self.subject.subject_id}", f"ses-{self.session.session_id}", 'anat', f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_space-ACPC_desc-preproc_T1w.nii.gz")
- if os.path.exists(qsiprep_t1w_path1):
- target_t1w = qsiprep_t1w_path1
- elif os.path.exists(qsiprep_t1w_path2):
- target_t1w = qsiprep_t1w_path2
- else:
- raise FileNotFoundError(f"No preprocessed T1w file found in qsiprep output: {qsiprep_t1w_path1} or {qsiprep_t1w_path2}")
- # check QSIPrep outputs
- print(f"[DWI Pipeline] You selected to use QSIPrep preprocessed DWI and T1w images.")
- print(f"[DWI Pipeline] Found preprocessed DWI: {target_dwi}")
- print(f"[DWI Pipeline] Found preprocessed T1w: {target_t1w}")
- # Flip bvec
- print("[DWI Pipeline] Flip b-table axis: {}.".format(self.flip_b_table_axis))
- inputnode.inputs.qsiprep_out_bvec = target_bvec
- flip_b_table_node = Node(FlipBTable(), name='flip_b_table_qsiprep')
- dwi_workflow.connect([(inputnode, flip_b_table_node, [('qsiprep_out_bvec', 'in_bvec')])])
- flip_b_table_node.inputs.flip_axis = self.flip_b_table_axis
- flip_b_table_node.inputs.out_bvec = target_bvec
- preproc_dwi_node.inputs.preproc_dwi = target_dwi
- #preproc_dwi_node.inputs.bvec = target_bvec
- dwi_workflow.connect([(flip_b_table_node, preproc_dwi_node, [('out_bvec', 'bvec')])])
- preproc_dwi_node.inputs.bval = target_bval
- preproc_dwi_node.inputs.dwi_mask = target_mask
- preproc_dwi_node.inputs.b0 = target_dwi.replace('_desc-preproc_dwi.nii.gz', '_dwiref.nii.gz')
- preproc_dwi_node.inputs.preproc_t1w = target_t1w
- preproc_dwi_filename = rename_bids_file(dwi_image, {"space": "ACPC", "desc": "preproc"}, 'dwi', '.nii.gz')
- else:
- print("[DWI Pipeline] Assuming DWI has been preprocessed and arching to output directory.")
- preproc_dwi_node.inputs.preproc_dwi = os.path.join(self.output_path, rename_bids_file(dwi_image, {"space": space_entity, "desc": "preproc"}, 'dwi', '.nii.gz'))
- preproc_dwi_node.inputs.bvec = os.path.join(self.output_path, rename_bids_file(dwi_image, {"space": space_entity, "desc": "preproc"}, 'dwi', '.bvec'))
- preproc_dwi_node.inputs.bval = os.path.join(self.output_path, rename_bids_file(dwi_image, {"space": space_entity, "desc": "preproc"}, 'dwi', '.bval'))
- preproc_dwi_node.inputs.dwi_mask = os.path.join(self.output_path, rename_bids_file(dwi_image, {"space": space_entity, "desc": "brain"}, 'mask', '.nii.gz'))
- preproc_dwi_node.inputs.b0 = os.path.join(self.output_path, rename_bids_file(dwi_image, {"space": space_entity}, 'dwiref', '.nii.gz'))
- preproc_dwi_filename = rename_bids_file(dwi_image, {"space": space_entity, "desc": "preproc"}, 'dwi', '.nii.gz')
- # Wait for all final outputs, including the mask whose creation can still
- # read intermediate files after the final DWI has been written.
- if not self.keep_preproc_intermediate and self.preprocess_method != 'post_qsiprep':
- def _cleanup_intermediate(preproc_dwi, bvec, bval, dwi_mask, intermediate_dir):
- import os
- import shutil
- if os.path.isdir(intermediate_dir):
- target = os.path.realpath(intermediate_dir)
- for path in (preproc_dwi, bvec, bval, dwi_mask):
- if not os.path.isfile(path):
- raise FileNotFoundError(f"Cannot clean preprocessing intermediates: missing final output {path}")
- if os.path.commonpath([target, os.path.realpath(path)]) == target:
- raise ValueError(f"Cannot remove intermediate directory containing final output {path}")
- shutil.rmtree(intermediate_dir)
- print(f"[DWI Pipeline] Removed preprocessing intermediates: {intermediate_dir}")
- return preproc_dwi
- cleanup_fields = ['preproc_dwi', 'bvec', 'bval', 'dwi_mask']
- cleanup_node = Node(Function(input_names=cleanup_fields + ['intermediate_dir'],
- output_names=['dummy'],
- function=_cleanup_intermediate),
- name='cleanup_preproc_intermediate', always_run=True)
- cleanup_node.inputs.intermediate_dir = os.path.join(self.output_path, 'preproc_intermediate')
- for field in cleanup_fields:
- dwi_workflow.connect(preproc_dwi_node, field, cleanup_node, field)
- # MRtrix3 conversion (nifti to mif): preprocessed DWI and brain mask
- if mrtrix3_preproc:
- mrtrix3_output_dir = os.path.join(self.output_path, 'mrtrix3')
- os.makedirs(mrtrix3_output_dir, exist_ok=True)
- preproc_dwi_mif_node = Node(IdentityInterface(fields=['preproc_dwi', 'dwi_mask']), name='preproc_dwi_mif_node')
- preproc_dwi_mif = os.path.join(mrtrix3_output_dir, rename_bids_file(preproc_dwi_filename, {}, 'dwi', '.mif'))
- if not os.path.exists(preproc_dwi_mif):
- mri_convert_node = Node(MRConvert(), name='dwi_mif_convert')
- mri_convert_node.inputs.out_file = preproc_dwi_mif
- mri_convert_node.inputs.args = '-force'
- dwi_workflow.connect(preproc_dwi_node, 'preproc_dwi', mri_convert_node, 'in_file')
- dwi_workflow.connect(preproc_dwi_node, 'bvec', mri_convert_node, 'in_bvec')
- dwi_workflow.connect(preproc_dwi_node, 'bval', mri_convert_node, 'in_bval')
- dwi_workflow.connect(mri_convert_node, 'out_file', preproc_dwi_mif_node, 'preproc_dwi')
- else:
- preproc_dwi_mif_node.inputs.preproc_dwi = preproc_dwi_mif
- brain_mask_mif = os.path.join(mrtrix3_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': 'brain'}, 'mask', '.mif'))
- if not os.path.exists(brain_mask_mif):
- mask_mif_convert = Node(MRConvert(), name='mask_mif_convert')
- mask_mif_convert.inputs.out_file = brain_mask_mif
- mask_mif_convert.inputs.args = '-force'
- dwi_workflow.connect(preproc_dwi_node, 'dwi_mask', mask_mif_convert, 'in_file')
- dwi_workflow.connect(mask_mif_convert, 'out_file', preproc_dwi_mif_node, 'dwi_mask')
- else:
- preproc_dwi_mif_node.inputs.dwi_mask = brain_mask_mif
- # ===========================================
- # DWI scalar maps storage node
- dwi_scalarmaps_output_node = Node(IdentityInterface(fields=[
- 'fa_img', 'md_img', 'markvcid2_fw_img', 'ad', 'rd', # Tensor model
- 'odi_img', 'icvf_img', 'isovf_img', # NODDI model
- 'gqi_gfa_img', 'gqi_iso_img', 'gqi_qa_img', # GQI model
- 'chidia_img', # QSM!
- 'mk_img', 'ak_img', 'rk_img', # DKI model
- ]), name='dwi_scalarmaps_output')
- # default all to ''
- dwi_scalarmaps_output_node.inputs.fa_img = ''
- dwi_scalarmaps_output_node.inputs.md_img = ''
- dwi_scalarmaps_output_node.inputs.markvcid2_fw_img = ''
- dwi_scalarmaps_output_node.inputs.ad = ''
- dwi_scalarmaps_output_node.inputs.rd = ''
- dwi_scalarmaps_output_node.inputs.odi_img = ''
- dwi_scalarmaps_output_node.inputs.icvf_img = ''
- dwi_scalarmaps_output_node.inputs.isovf_img = ''
- dwi_scalarmaps_output_node.inputs.gqi_gfa_img = ''
- dwi_scalarmaps_output_node.inputs.gqi_iso_img = ''
- dwi_scalarmaps_output_node.inputs.gqi_qa_img = ''
- dwi_scalarmaps_output_node.inputs.chidia_img = ''
- dwi_scalarmaps_output_node.inputs.mk_img = ''
- dwi_scalarmaps_output_node.inputs.ak_img = ''
- dwi_scalarmaps_output_node.inputs.rk_img = ''
- dwi_scalarmaps_output_node.inputs.chidia_img = ''
- # ===========================================
- # ===========================================
- # Reconstruction (FSL FDT dtifit)
- # ===========================================
- dti_fit_output_node = Node(
- IdentityInterface(fields=['fa_img', 'md_img', 'tensor_img']),
- name='dti_fit_output'
- )
- if self.dti_fit:
- print("[DWI Pipeline] DTI fitting using FSL dtifit: True")
- dtifit_output_dir = os.path.join(self.output_path, 'dtifit')
- os.makedirs(dtifit_output_dir, exist_ok=True)
- target_fa_output = os.path.join(dtifit_output_dir, rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "tensor", "param": "fa"}, "dwimap", ".nii.gz"))
- target_ad_output = os.path.join(dtifit_output_dir, rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "tensor", "param": "ad"}, "dwimap", ".nii.gz"))
- if os.path.exists(target_ad_output):
- print(f"[DWI Pipeline] DTI AD image already exists: {target_ad_output}. Skipping DTI fit.")
- dti_fit_output_node.inputs.fa_img = target_fa_output
- dti_fit_output_node.inputs.md_img = os.path.join(dtifit_output_dir,rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "tensor", "param": "md"}, "dwimap", ".nii.gz"))
- dti_fit_output_node.inputs.ad_img = os.path.join(dtifit_output_dir, rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "tensor", "param": "ad"}, "dwimap", ".nii.gz"))
- dti_fit_output_node.inputs.rd_img = os.path.join(dtifit_output_dir, rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "tensor", "param": "rd"}, "dwimap", ".nii.gz"))
- dti_fit_output_node.inputs.tensor_img = os.path.join(dtifit_output_dir, rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "tensor", "param": "tensor"}, "dwimap", ".nii.gz"))
- else:
- dti_fit_node = Node(DTIFitBIDS(), name='dti_fit')
- dwi_workflow.connect(preproc_dwi_node, 'preproc_dwi', dti_fit_node, 'dwi_file')
- dwi_workflow.connect(preproc_dwi_node, 'bvec', dti_fit_node, 'bvec_file')
- dwi_workflow.connect(preproc_dwi_node, 'bval', dti_fit_node, 'bval_file')
- dwi_workflow.connect(preproc_dwi_node, 'dwi_mask', dti_fit_node, 'mask_file')
- dti_fit_node.inputs.output_basename = os.path.join(dtifit_output_dir, 'dti')
- dti_fit_node.inputs.bids_rename = True
- dti_fit_node.inputs.overwrite = True
- dti_fit_node.inputs.dti_low_b = self.dti_low_b
- dwi_workflow.connect(dti_fit_node, 'dti_fa', dti_fit_output_node, 'fa_img')
- dwi_workflow.connect(dti_fit_node, 'dti_md', dti_fit_output_node, 'md_img')
- dwi_workflow.connect(dti_fit_node, 'dti_ad', dti_fit_output_node, 'ad_img')
- dwi_workflow.connect(dti_fit_node, 'dti_rd', dti_fit_output_node, 'rd_img')
- dwi_workflow.connect(dti_fit_node, 'dti_tensor', dti_fit_output_node, 'tensor_img')
- # Connect DTI outputs to dwi_scalarmaps_output_node
- dwi_workflow.connect(dti_fit_output_node, 'fa_img', dwi_scalarmaps_output_node, 'fa_img')
- dwi_workflow.connect(dti_fit_output_node, 'md_img', dwi_scalarmaps_output_node, 'md_img')
- dwi_workflow.connect(dti_fit_output_node, 'ad_img', dwi_scalarmaps_output_node, 'ad_img')
- dwi_workflow.connect(dti_fit_output_node, 'rd_img', dwi_scalarmaps_output_node, 'rd_img')
- # ===========================================
- # Reconstruction (Dipy DKI fit)
- # ===========================================
- dki_fit_output_node = Node(
- IdentityInterface(fields=['mk_img', 'ak_img', 'rk_img']),
- name='dki_fit_output'
- )
- if self.dki_fit:
- print("[DWI Pipeline] DKI fitting using Dipy: True")
- dki_output_dir = os.path.join(self.output_path, 'dkifit')
- os.makedirs(dki_output_dir, exist_ok=True)
- target_mk_output = os.path.join(dki_output_dir, rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "dki", "param": "mk"}, "dwimap", ".nii.gz"))
- if os.path.exists(target_mk_output):
- print(f"[DWI Pipeline] DKI MK image already exists: {target_mk_output}. Skipping DKI fit.")
- dki_fit_output_node.inputs.mk_img = target_mk_output
- dki_fit_output_node.inputs.ak_img = os.path.join(dki_output_dir, rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "dki", "param": "ak"}, "dwimap", ".nii.gz"))
- dki_fit_output_node.inputs.rk_img = os.path.join(dki_output_dir, rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "dki", "param": "rk"}, "dwimap", ".nii.gz"))
- else:
- dki_fit_node = Node(DKIFit(), name='dki_fit')
- dwi_workflow.connect(preproc_dwi_node, 'preproc_dwi', dki_fit_node, 'dwi_file')
- dwi_workflow.connect(preproc_dwi_node, 'bvec', dki_fit_node, 'bvec_file')
- dwi_workflow.connect(preproc_dwi_node, 'bval', dki_fit_node, 'bval_file')
- dwi_workflow.connect(preproc_dwi_node, 'dwi_mask', dki_fit_node, 'mask_file')
- dki_fit_node.inputs.output_dir = dki_output_dir
- dki_fit_node.inputs.bids_rename = True
- dki_fit_node.inputs.overwrite = True
- dki_fit_node.inputs.num_threads = 8
- dwi_workflow.connect(dki_fit_node, 'mk_file', dki_fit_output_node, 'mk_img')
- dwi_workflow.connect(dki_fit_node, 'ak_file', dki_fit_output_node, 'ak_img')
- dwi_workflow.connect(dki_fit_node, 'rk_file', dki_fit_output_node, 'rk_img')
- # Connect DKI outputs to dwi_scalarmaps_output_node
- dwi_workflow.connect(dki_fit_output_node, 'mk_img', dwi_scalarmaps_output_node, 'mk_img')
- dwi_workflow.connect(dki_fit_output_node, 'ak_img', dwi_scalarmaps_output_node, 'ak_img')
- dwi_workflow.connect(dki_fit_output_node, 'rk_img', dwi_scalarmaps_output_node, 'rk_img')
- # ============================================
- # DWI and T1w registration (And other anat preprocessing)
- # ============================================
- # We put it here because we prefer to use FA image for registration (FLIRT), while QSIPrep uses b0 image !
- nawm_dseg = None
- if self.dwi_t1w_register:
- print("[DWI Pipeline] Register DWI to T1w image: True")
- anat_output_dir = os.path.join(self.output_path, 'anat')
- os.makedirs(anat_output_dir, exist_ok=True)
- xfm_dir = os.path.join(self.subject.bids_dir, 'derivatives', 'xfm', f"sub-{self.subject.subject_id}", f"ses-{self.session.session_id}")
- if self.session.xfm_dir is None:
- os.makedirs(os.path.join(self.subject.bids_dir, 'derivatives', 'xfm', f"sub-{self.subject.subject_id}", f"ses-{self.session.session_id}"), exist_ok=True)
- if self.preprocess_method == 'post_qsiprep':
- qsiprep_out_matrix = os.path.join(xfm_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_from-ACPC_to-T1w_xfm.mat")
- if os.path.exists(qsiprep_out_matrix):
- print(f"[DWI Pipeline] QSIPrep transform matrix already exists: {qsiprep_out_matrix}. Skipping.")
- dwi_to_t1w_reg_node = Node(IdentityInterface(fields=['out_matrix_file', 'out_file']), name='dwi_to_t1w_reg_qsiprep')
- dwi_to_t1w_reg_node.inputs.out_matrix_file = qsiprep_out_matrix
- dwi_to_t1w_reg_node.inputs.out_file = ''
- else:
- dwi_to_t1w_reg_node = Node(QSIPrepOrigToACPC(), name='dwi_to_t1w_reg_qsiprep')
- dwi_workflow.connect(inputnode, 't1w_file', dwi_to_t1w_reg_node, 'orig_t1w_file')
- dwi_workflow.connect(dti_fit_output_node, 'fa_img', dwi_to_t1w_reg_node, 'dwimap_file')
- dwi_to_t1w_reg_node.inputs.subject_id = self.subject.subject_id
- dwi_to_t1w_reg_node.inputs.session_id = self.session.session_id
- dwi_to_t1w_reg_node.inputs.output_dir = xfm_dir
- dwi_workflow.connect(preproc_dwi_node, 'preproc_t1w', dwi_to_t1w_reg_node, 'preprocess_t1w_file')
- else:
- dwi_to_t1w_reg_node = Node(FLIRT(), name='dwi_to_t1w_reg')
- t1w_stripped = os.path.join(xfm_dir, rename_bids_file(t1w_file, {'desc': 'brain', 'space': None}, 'T1w', '.nii.gz'))
- dwi_workflow.connect(dti_fit_output_node, 'fa_img', dwi_to_t1w_reg_node, 'in_file')
- if os.path.exists(t1w_stripped):
- dwi_to_t1w_reg_node.inputs.reference = t1w_stripped
- else:
- t1w_strip_node = Node(SynthStrip(), name='t1w_strip_for_dwi_reg')
- dwi_workflow.connect(inputnode, 't1w_file', t1w_strip_node, 'image')
- t1w_strip_node.inputs.out_file = t1w_stripped
- dwi_workflow.connect(t1w_strip_node, 'out_file', dwi_to_t1w_reg_node, 'reference')
- dwi_to_t1w_reg_node.inputs.out_file = os.path.join(xfm_dir, rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "tensor", "param": "fa", 'space': 'T1w'}, 'dwimap', '.nii.gz'))
- dwi_to_t1w_reg_node.inputs.out_matrix_file = os.path.join(xfm_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_from-{space_entity}_to-T1w_xfm.mat")
- # need to invert the matrix for later use
- invert_dwi_to_t1w_reg_node = Node(ConvertXFM(), name='invert_dwi_to_t1w_reg')
- dwi_workflow.connect(dwi_to_t1w_reg_node, 'out_matrix_file', invert_dwi_to_t1w_reg_node, 'in_file')
- invert_dwi_to_t1w_reg_node.inputs.out_file = os.path.join(xfm_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_from-T1w_to-{space_entity}_xfm.mat")
- invert_dwi_to_t1w_reg_node.inputs.invert_xfm = True
- # If seed_mask_t1wspace is provided, we need to transform it to DWI space
- if seed_mask != '':
- seed_mask_to_dwi_node = Node(FLIRT(), name='seed_mask_to_dwi')
- dwi_workflow.connect(inputnode, 'seed_mask_t1wspace', seed_mask_to_dwi_node, 'in_file')
- dwi_workflow.connect(invert_dwi_to_t1w_reg_node, 'out_file', seed_mask_to_dwi_node, 'in_matrix_file')
- dwi_workflow.connect(preproc_dwi_node, 'b0', seed_mask_to_dwi_node, 'reference')
- seed_mask_to_dwi_node.inputs.interp = 'nearestneighbour'
- seed_mask_to_dwi_node.inputs.apply_xfm = True
- seed_mask_to_dwi_node.inputs.out_file = os.path.join(os.path.dirname(seed_mask), rename_bids_file(seed_mask, {'space': space_entity}, 'mask', '.nii.gz'))
- if chpseg_process:
- chpseg_mask_to_dwi_node = Node(FLIRT(), name='chpseg_mask_to_dwi')
- chpseg_mask_to_dwi_node.inputs.in_file = chpseg_mask_file
- dwi_workflow.connect(invert_dwi_to_t1w_reg_node, 'out_file', chpseg_mask_to_dwi_node, 'in_matrix_file')
- dwi_workflow.connect(preproc_dwi_node, 'b0', chpseg_mask_to_dwi_node, 'reference')
- chpseg_mask_to_dwi_node.inputs.interp = 'nearestneighbour'
- chpseg_mask_to_dwi_node.inputs.apply_xfm = True
- chpseg_mask_to_dwi_node.inputs.out_file = os.path.join(anat_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_space-{space_entity}_desc-chpseg_dseg.nii.gz")
- if fs_output_process:
- # Freesurfer to T1w registration
- fs_to_t1w_xfm_node = Node(Tkregister2fs2t1w(), name='fs_to_t1w_xfm')
- dwi_workflow.connect(inputnode, 'fs_subjects_dir', fs_to_t1w_xfm_node, 'fs_subjects_dir')
- dwi_workflow.connect(inputnode, 'fs_subject_id', fs_to_t1w_xfm_node, 'fs_subject_id')
- fs_to_t1w_xfm_node.inputs.output_matrix = os.path.join(xfm_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_from-fs_to-T1w_xfm.mat")
- fs_to_t1w_xfm_node.inputs.output_inverse_matrix = os.path.join(xfm_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_from-T1w_to-fs_xfm.mat")
- concat_fs_to_dwi_xfm_node = Node(ConvertXFM(), name='concat_fs_to_dwi_xfm')
- dwi_workflow.connect(fs_to_t1w_xfm_node, 'output_matrix', concat_fs_to_dwi_xfm_node, 'in_file')
- concat_fs_to_dwi_xfm_node.inputs.concat_xfm = True
- dwi_workflow.connect(invert_dwi_to_t1w_reg_node, 'out_file', concat_fs_to_dwi_xfm_node, 'in_file2')
- concat_fs_to_dwi_xfm_node.inputs.out_file = os.path.join(xfm_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_from-fs_to-{space_entity}_xfm.mat")
- # Seed mask to fs space registration
- if seed_mask != '':
- seed_mask_to_fs_node = Node(MRIVol2Vol(), name='seed_mask_to_fs')
- seed_mask_to_fs_node.inputs.moving_image = seed_mask # in T1w space
- dwi_workflow.connect(fs_to_t1w_xfm_node, 'output_inverse_matrix', seed_mask_to_fs_node, 'fsl_matrix')
- seed_mask_to_fs_node.inputs.target_image = os.path.join(fs_output, 'mri', 'orig.mgz') # fs native space
- seed_mask_to_fs_node.inputs.interp = 'nearestneighbour'
- seed_mask_to_fs_node.inputs.output_image = os.path.join(os.path.dirname(seed_mask), rename_bids_file(seed_mask, {'space': 'fs'}, 'mask', '.nii.gz'))
- # Refine freesurfer aparc+aseg parcellation
- def get_aseg_file(fs_subjects_dir, fs_subject_id):
- import os
- return os.path.join(fs_subjects_dir, fs_subject_id, 'mri', 'aparc+aseg.mgz')
- get_aseg_node = Node(Function(input_names=['fs_subjects_dir', 'fs_subject_id'],
- output_names=['aseg_file'],
- function=get_aseg_file), name='get_aseg_file')
- dwi_workflow.connect(inputnode, 'fs_subjects_dir', get_aseg_node, 'fs_subjects_dir')
- dwi_workflow.connect(inputnode, 'fs_subject_id', get_aseg_node, 'fs_subject_id')
- aseg_mgz_to_nifti_node = Node(MRIConvert(), name='aseg_mgz_to_nifti')
- dwi_workflow.connect(get_aseg_node, 'aseg_file', aseg_mgz_to_nifti_node, 'in_file')
- aseg_mgz_to_nifti_node.inputs.out_file = os.path.join(anat_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_space-fs_aparcaseg.nii.gz")
- # convert to DWI space
- fs_aparcaseg_to_dwi_node = Node(FLIRT(), name='fs_aparcaseg_to_dwi')
- dwi_workflow.connect(aseg_mgz_to_nifti_node, 'out_file', fs_aparcaseg_to_dwi_node, 'in_file')
- dwi_workflow.connect(concat_fs_to_dwi_xfm_node, 'out_file', fs_aparcaseg_to_dwi_node, 'in_matrix_file')
- dwi_workflow.connect(preproc_dwi_node, 'b0', fs_aparcaseg_to_dwi_node, 'reference')
- fs_aparcaseg_to_dwi_node.inputs.interp = 'nearestneighbour'
- fs_aparcaseg_to_dwi_node.inputs.apply_xfm = True
- fs_aparcaseg_to_dwi_node.inputs.out_file = os.path.join(anat_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_space-{space_entity}_aparcaseg.nii.gz")
- # Remove WMH from aseg
- refine_aseg_node = Node(RemoveWMH(), name='refine_aseg')
- dwi_workflow.connect(fs_aparcaseg_to_dwi_node, 'out_file', refine_aseg_node, 'in_aseg')
- refine_aseg_node.inputs.out_aseg = os.path.join(anat_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_space-{space_entity}_desc-WMHremoved_aparcaseg.nii.gz")
- create_cortical_GM_mask_node = Node(MergeRibbon(), name='create_cortical_GM_mask')
- dwi_workflow.connect(inputnode, 'fs_subjects_dir', create_cortical_GM_mask_node, 'subjects_dir')
- dwi_workflow.connect(inputnode, 'fs_subject_id', create_cortical_GM_mask_node, 'subject_id')
- create_cortical_GM_mask_node.inputs.output_gm_mask = os.path.join(anat_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_space-fs_label-corticalGM_mask.nii.gz")
- cortical_GM_to_dwi_node = Node(FLIRT(), name='cortical_GM_to_dwi')
- dwi_workflow.connect(create_cortical_GM_mask_node, 'output_gm_mask', cortical_GM_to_dwi_node, 'in_file')
- dwi_workflow.connect(concat_fs_to_dwi_xfm_node, 'out_file', cortical_GM_to_dwi_node, 'in_matrix_file')
- dwi_workflow.connect(preproc_dwi_node, 'b0', cortical_GM_to_dwi_node, 'reference')
- cortical_GM_to_dwi_node.inputs.interp = "nearestneighbour"
- cortical_GM_to_dwi_node.inputs.apply_xfm = True
- cortical_GM_to_dwi_node.inputs.out_file = os.path.join(
- anat_output_dir, rename_bids_file(create_cortical_GM_mask_node.inputs.output_gm_mask, {"space": space_entity}, "mask", ".nii.gz")
- )
- # convert wmparc.mgz to DWI space
- def get_wmparc_file(fs_subjects_dir, fs_subject_id):
- import os
- return os.path.join(fs_subjects_dir, fs_subject_id, 'mri', 'wmparc.mgz')
- get_wmparc_node = Node(Function(input_names=['fs_subjects_dir', 'fs_subject_id'],
- output_names=['wmparc_file'],
- function=get_wmparc_file), name='get_wmparc_file')
- dwi_workflow.connect(inputnode, 'fs_subjects_dir', get_wmparc_node, 'fs_subjects_dir')
- dwi_workflow.connect(inputnode, 'fs_subject_id', get_wmparc_node, 'fs_subject_id')
- wmparc_mgz_to_nifti_node = Node(MRIConvert(), name='wmparc_mgz_to_nifti')
- dwi_workflow.connect(get_wmparc_node, 'wmparc_file', wmparc_mgz_to_nifti_node, 'in_file')
- wmparc_mgz_to_nifti_node.inputs.out_file = os.path.join(anat_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_space-fs_wmparc.nii.gz")
- # convert to DWI space
- fs_wmparc_to_dwi_node = Node(FLIRT(), name='fs_wmparc_to_dwi')
- dwi_workflow.connect(wmparc_mgz_to_nifti_node, 'out_file', fs_wmparc_to_dwi_node, 'in_file')
- dwi_workflow.connect(concat_fs_to_dwi_xfm_node, 'out_file', fs_wmparc_to_dwi_node, 'in_matrix_file')
- dwi_workflow.connect(preproc_dwi_node, 'b0', fs_wmparc_to_dwi_node, 'reference')
- fs_wmparc_to_dwi_node.inputs.interp = 'nearestneighbour'
- fs_wmparc_to_dwi_node.inputs.apply_xfm = True
- fs_wmparc_to_dwi_node.inputs.out_file = os.path.join(anat_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_space-{space_entity}_wmparc.nii.gz")
- # exclude seed mask from wmparc (DWI space) if exists
- final_wmparc_node = Node(IdentityInterface(fields=['final_wmparc']), name='final_wmparc')
- if seed_mask != '':
- remove_seed_from_wmparc_node = Node(RemoveMaskRegion(), name='remove_seed_from_wmparc')
- remove_seed_from_wmparc_node.inputs.output_image = os.path.join(
- anat_output_dir,
- f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_space-{space_entity}_desc-seedRemoved_wmparc.nii.gz"
- )
- dwi_workflow.connect(fs_wmparc_to_dwi_node, 'out_file', remove_seed_from_wmparc_node, 'input_image')
- dwi_workflow.connect(seed_mask_to_dwi_node, 'out_file', remove_seed_from_wmparc_node, 'mask_image')
- dwi_workflow.connect(remove_seed_from_wmparc_node, 'output_image', final_wmparc_node, 'final_wmparc')
- else:
- dwi_workflow.connect(fs_wmparc_to_dwi_node, 'out_file', final_wmparc_node, 'final_wmparc')
- # Load CHIDIA (QSM) image if available, independent of visual_pathway_analysis
- chidia_in_t1w_file = os.path.join(
- self.subject.bids_dir, 'derivatives', 'qsm_pipeline', f"sub-{self.subject.subject_id}", f"ses-{self.session.session_id}", "QSM_registered",
- f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_space-T1w_Chidia.nii.gz",
- )
- if os.path.exists(chidia_in_t1w_file):
- chidia_to_dwi_node = Node(FLIRT(), name='chidia_to_dwi')
- chidia_to_dwi_node.inputs.in_file = chidia_in_t1w_file
- dwi_workflow.connect(invert_dwi_to_t1w_reg_node, 'out_file', chidia_to_dwi_node, 'in_matrix_file')
- dwi_workflow.connect(preproc_dwi_node, 'b0', chidia_to_dwi_node, 'reference')
- chidia_to_dwi_node.inputs.interp = 'trilinear'
- chidia_to_dwi_node.inputs.apply_xfm = True
- chidia_to_dwi_node.inputs.out_file = os.path.join(anat_output_dir, rename_bids_file(chidia_in_t1w_file, {'space': space_entity}, "Chidia", ".nii.gz"))
- dwi_workflow.connect(chidia_to_dwi_node, 'out_file', dwi_scalarmaps_output_node, 'chidia_img')
- if self.visual_pathway_analysis:
- if wmh_probmap_file is not None:
- wmhprobmap_to_dwi_node = Node(FLIRT(), name='wmhprobmap_to_dwi')
- wmhprobmap_to_dwi_node.inputs.in_file = wmh_probmap_file
- dwi_workflow.connect(invert_dwi_to_t1w_reg_node, 'out_file', wmhprobmap_to_dwi_node, 'in_matrix_file')
- dwi_workflow.connect(preproc_dwi_node, 'b0', wmhprobmap_to_dwi_node, 'reference')
- wmhprobmap_to_dwi_node.inputs.interp = 'trilinear'
- wmhprobmap_to_dwi_node.inputs.apply_xfm = True
- wmhprobmap_to_dwi_node.inputs.out_file = os.path.join(anat_output_dir, rename_bids_file(wmh_probmap_file, {'space': space_entity}, "probmap", ".nii.gz"))
- # NAWM mask creation
- base_name = f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_space-{space_entity}"
- wm_source_available = False
- wm_source_node = None
- wm_source_output = None
- if self.use_synthseg_wm_mask:
- synthseg_dir = os.path.join(
- self.subject.bids_dir, "derivatives", "anat_seg",
- f"sub-{self.subject.subject_id}", f"ses-{self.session.session_id}", "synthseg"
- )
- matches = glob.glob(os.path.join(synthseg_dir, "*synthseg.nii.gz"))
- if len(matches) != 1:
- raise RuntimeError(f"Expected exactly 1 SynthSeg output, found {len(matches)}: {matches}")
- synthseg_target_output = matches[0]
- synthseg_to_dwi_node = Node(FLIRT(), name="synthseg_to_dwi")
- synthseg_to_dwi_node.inputs.in_file = synthseg_target_output
- synthseg_to_dwi_node.inputs.interp = "nearestneighbour"
- synthseg_to_dwi_node.inputs.apply_xfm = True
- synthseg_to_dwi_node.inputs.out_file = os.path.join(
- anat_output_dir,
- rename_bids_file(synthseg_target_output, {"space": space_entity}, "synthseg", ".nii.gz")
- )
- dwi_workflow.connect(invert_dwi_to_t1w_reg_node, "out_file", synthseg_to_dwi_node, "in_matrix_file")
- dwi_workflow.connect(preproc_dwi_node, "b0", synthseg_to_dwi_node, "reference")
- wm_source_available = True
- wm_source_type = "synthseg"
- wm_source_node = synthseg_to_dwi_node
- wm_source_output = "out_file"
- elif fs_output_process:
- wm_source_available = True
- wm_source_type = "freesurfer"
- wm_source_node = refine_aseg_node
- wm_source_output = "out_aseg"
- else:
- wm_source_type = None
- print("[DWI Pipeline] No valid WM source found. Skip NAWM mask creation.")
- if wm_source_available:
- create_WM_masks_node = MapNode(
- MRIBinarize(), name="create_WM_masks", iterfield=["output_volume", "match"]
- )
- dwi_workflow.connect(wm_source_node, wm_source_output, create_WM_masks_node, "input_volume")
- if wm_source_type == "synthseg":
- create_WM_masks_node.inputs.match = [
- [2, 41],
- [2, 41, 7, 46],
- [2], # Left-Cerebral-WM only
- [41], # Right-Cerebral-WM only
- ]
- else:
- create_WM_masks_node.inputs.match = [
- [2, 41, 77, 251, 252, 253, 254, 255],
- [2, 41, 77, 251, 252, 253, 254, 255, 7, 46],
- [2], # Left-Cerebral-WM only
- [41], # Right-Cerebral-WM only
- ]
- create_WM_masks_node.inputs.output_volume = [
- os.path.join(anat_output_dir, f"{base_name}_label-cerebralWM_mask.nii.gz"),
- os.path.join(anat_output_dir, f"{base_name}_label-WM_mask.nii.gz"),
- os.path.join(anat_output_dir, f"{base_name}_label-rawNAWMLeft_mask.nii.gz"),
- os.path.join(anat_output_dir, f"{base_name}_label-rawNAWMRight_mask.nii.gz"),
- ]
- def collect_exclusion_masks(seed_mask, wmh_mask, use_seed, use_wmh):
- import os
- masks = []
- if use_seed and seed_mask and os.path.exists(seed_mask):
- masks.append(seed_mask)
- if use_wmh and wmh_mask and os.path.exists(wmh_mask):
- masks.append(wmh_mask)
- return masks
- collect_excl_node = Node(Function(
- input_names=["seed_mask", "wmh_mask", "use_seed", "use_wmh"],
- output_names=["masks"],
- function=collect_exclusion_masks
- ), name="collect_exclusion_masks")
- collect_excl_node.inputs.use_seed = bool(self.exclude_seed_mask and seed_mask != "")
- collect_excl_node.inputs.use_wmh = bool(self.exclude_wmh_mask)
- if self.exclude_seed_mask and seed_mask != "":
- dwi_workflow.connect(seed_mask_to_dwi_node, "out_file", collect_excl_node, "seed_mask")
- else:
- collect_excl_node.inputs.seed_mask = ""
- if self.exclude_wmh_mask or self.visual_pathway_analysis:
- wmh_mask_to_dwi_node = Node(FLIRT(), name="wmh_mask_to_dwi")
- wmh_mask_to_dwi_node.inputs.in_file = wmh_mask_file
- wmh_mask_to_dwi_node.inputs.interp = "nearestneighbour"
- wmh_mask_to_dwi_node.inputs.apply_xfm = True
- wmh_mask_to_dwi_node.inputs.out_file = os.path.join(
- anat_output_dir,
- rename_bids_file(wmh_mask_file, {"space": space_entity}, "mask", ".nii.gz")
- )
- dwi_workflow.connect(invert_dwi_to_t1w_reg_node, "out_file", wmh_mask_to_dwi_node, "in_matrix_file")
- dwi_workflow.connect(preproc_dwi_node, "b0", wmh_mask_to_dwi_node, "reference")
- dwi_workflow.connect(wmh_mask_to_dwi_node, "out_file", collect_excl_node, "wmh_mask")
- else:
- collect_excl_node.inputs.wmh_mask = ""
- def merge_exclusion_masks(mask_files, ref_file, out_file):
- import os
- import subprocess
- mask_files = [] if mask_files is None else [m for m in mask_files if m]
- os.makedirs(os.path.dirname(out_file), exist_ok=True)
- if len(mask_files) == 0:
- subprocess.check_call(["fslmaths", ref_file, "-mul", "0", out_file])
- return out_file
- cmd = ["fslmaths", mask_files[0]]
- for mask_file in mask_files[1:]:
- cmd += ["-add", mask_file]
- cmd += ["-thr", "0.5", "-bin", out_file]
- subprocess.check_call(cmd)
- return out_file
- merge_masks_to_exclude_node = Node(Function(
- input_names=["mask_files", "ref_file", "out_file"],
- output_names=["out_file"],
- function=merge_exclusion_masks
- ), name="merge_masks_to_exclude")
- merge_masks_to_exclude_node.inputs.out_file = os.path.join(anat_output_dir, f"{base_name}_desc-exclusionMerged_mask.nii.gz")
- dwi_workflow.connect(collect_excl_node, "masks", merge_masks_to_exclude_node, "mask_files")
- dwi_workflow.connect(preproc_dwi_node, "b0", merge_masks_to_exclude_node, "ref_file")
- # Left NAWM
- get_raw_nawm_left_mask_node = Node(Select(index=2), name="select_raw_nawm_left_mask")
- dwi_workflow.connect(create_WM_masks_node, "output_volume", get_raw_nawm_left_mask_node, "inlist")
- get_final_nawm_left_node = Node(GenerateNAWMMask(), name="get_final_nawm_left_mask")
- get_final_nawm_left_node.inputs.erode_mm = 2
- get_final_nawm_left_node.inputs.output_mask = os.path.join(anat_output_dir, f"{base_name}_label-NAWMLeft_mask.nii.gz")
- dwi_workflow.connect(get_raw_nawm_left_mask_node, "out", get_final_nawm_left_node, "wm_mask")
- dwi_workflow.connect(merge_masks_to_exclude_node, "out_file", get_final_nawm_left_node, "exclude_mask")
- # Right NAWM
- get_raw_nawm_right_mask_node = Node(Select(index=3), name="select_raw_nawm_right_mask")
- dwi_workflow.connect(create_WM_masks_node, "output_volume", get_raw_nawm_right_mask_node, "inlist")
- get_final_nawm_right_node = Node(GenerateNAWMMask(), name="get_final_nawm_right_mask")
- get_final_nawm_right_node.inputs.erode_mm = 2
- get_final_nawm_right_node.inputs.output_mask = os.path.join(anat_output_dir, f"{base_name}_label-NAWMRight_mask.nii.gz")
- dwi_workflow.connect(get_raw_nawm_right_mask_node, "out", get_final_nawm_right_node, "wm_mask")
- dwi_workflow.connect(merge_masks_to_exclude_node, "out_file", get_final_nawm_right_node, "exclude_mask")
- # Merge left and right into a single NAWM dseg (1=left, 2=right)
- def merge_nawm_dseg(left_mask, right_mask, out_file):
- import os, nibabel, numpy as np
- os.makedirs(os.path.dirname(out_file), exist_ok=True)
- left_img = nibabel.load(left_mask)
- left_data = left_img.get_fdata().astype(bool)
- right_data = nibabel.load(right_mask).get_fdata().astype(bool)
- dseg_data = np.zeros(left_data.shape, dtype=np.uint8)
- dseg_data[left_data] = 1
- dseg_data[right_data] = 2
- dseg_img = nibabel.Nifti1Image(dseg_data, left_img.affine, left_img.header)
- dseg_img.header.set_data_dtype(np.uint8)
- nibabel.save(dseg_img, out_file)
- return out_file
- merge_nawm_dseg_node = Node(Function(
- input_names=["left_mask", "right_mask", "out_file"],
- output_names=["out_file"],
- function=merge_nawm_dseg
- ), name="merge_nawm_dseg")
- nawm_dseg = os.path.join(anat_output_dir, f"{base_name}_label-NAWM_dseg.nii.gz")
- merge_nawm_dseg_node.inputs.out_file = nawm_dseg
- dwi_workflow.connect(get_final_nawm_left_node, "nawm_mask", merge_nawm_dseg_node, "left_mask")
- dwi_workflow.connect(get_final_nawm_right_node, "nawm_mask", merge_nawm_dseg_node, "right_mask")
- else:
- nawm_dseg = None
- # ============================================
- # DSI Studio Reconstruction
- # ============================================
- if self.dsistudio_gqi or self.dsistudio_qsdr:
- # first need to convert to .src.gz format
- os.makedirs(os.path.join(self.output_path, 'dsistudio'), exist_ok=True)
- create_src_node = Node(CreateSRC(), name='create_src')
- dwi_workflow.connect(preproc_dwi_node, 'preproc_dwi', create_src_node, 'source')
- create_src_node.inputs.output = os.path.join(self.output_path, 'dsistudio', rename_bids_file(preproc_dwi_filename, {"desc": None, "desc": "preproc"}, 'dwi', '.src.gz'))
- dti_out = os.path.join(self.output_path, 'dsistudio', rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "dti"}, 'dwimap', '.fib.gz'))
- gqi_out = os.path.join(self.output_path, 'dsistudio', rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "gqi"}, 'dwimap', '.fib.gz'))
- qsdr_out = os.path.join(self.output_path, 'dsistudio', rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "qsdr"}, 'dwimap', '.fib.gz'))
- if self.dsistudio_dti:
- dti_reconstruction_node = Node(DSIstudioReconstruction(), name='dti_reconstruction')
- dwi_workflow.connect(create_src_node, 'out_file', dti_reconstruction_node, 'source')
- dti_reconstruction_node.inputs.method = 1
- dti_reconstruction_node.inputs.output = dti_out
- dti_reconstruction_node.inputs.other_output = 'fa,ad,rd,md,tensor'
- #dti_reconstruction_node.inputs.check_btable = 1
- #dwi_workflow.connect(preproc_dwi_node, 'dwi_mask', dti_reconstruction_node, 'mask')
- if self.dsistudio_gqi:
- gqi_reconstruction_node = Node(DSIstudioReconstruction(), name='gqi_reconstruction')
- dwi_workflow.connect(create_src_node, 'out_file', gqi_reconstruction_node, 'source')
- gqi_reconstruction_node.inputs.method = 4
- gqi_reconstruction_node.inputs.other_output = 'fa,ad,rd,md,iso,rdi,nrdi,tensor'
- gqi_reconstruction_node.inputs.output = gqi_out
- gqi_reconstruction_node.inputs.check_btable = 1
- #dwi_workflow.connect(preproc_dwi_node, 'dwi_mask', gqi_reconstruction_node, 'mask')
- if self.dsistudio_qsdr:
- qsdr_reconstruction_node = Node(DSIstudioReconstructionOldVer(), name='qsdr_reconstruction')
- dwi_workflow.connect(create_src_node, 'out_file', qsdr_reconstruction_node, 'source')
- qsdr_reconstruction_node.inputs.method = 7
- qsdr_reconstruction_node.inputs.qsdr_reso = 2.0
- qsdr_reconstruction_node.inputs.other_output = 'fa,ad,rd,md,iso,rdi,nrdi,tensor'
- qsdr_reconstruction_node.inputs.output = qsdr_out
- qsdr_reconstruction_node.inputs.thread_count = 8
- qsdr_reconstruction_node.inputs.check_btable = 1
- #dwi_workflow.connect(preproc_dwi_node, 'dwi_mask', qsdr_reconstruction_node, 'mask')
- if self.preprocess_method == 'post_qsiprep':
- qsirecon_output_dir = self.session._find_output('qsirecon-DSIStudio')
- gqi_fib = os.path.join(qsirecon_output_dir, 'dwi', rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "gqi"}, 'dwimap', '.fib.gz'))
- if not os.path.exists(gqi_fib):
- raise FileNotFoundError(f"GQI .fib.gz file not found in {qsirecon_output_dir}. Please run QSIRecon with DSIStudio reconstruction first.")
- dwi_scalarmaps_output_node.inputs.gqi_gfa_img = os.path.join(qsirecon_output_dir, 'dwi', rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "gqi", "param": "gfa"}, 'dwimap', '.nii.gz'))
- dwi_scalarmaps_output_node.inputs.gqi_iso_img = os.path.join(qsirecon_output_dir, 'dwi', rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "gqi", "param": "iso"}, 'dwimap', '.nii.gz'))
- dwi_scalarmaps_output_node.inputs.gqi_qa_img = os.path.join(qsirecon_output_dir, 'dwi', rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "gqi", "param": "qa"}, 'dwimap', '.nii.gz'))
- # ===========================================
- # AMICO NODDI Reconstruction
- # ===========================================
- if self.amico_noddi:
- from cvdproc.pipelines.dmri.amico.amico_nipype import AmicoNoddi
- amico_output_dir = os.path.join(self.output_path, 'NODDI')
- amico_noddi_node = Node(AmicoNoddi(), name='amico_noddi')
- dwi_workflow.connect(preproc_dwi_node, 'preproc_dwi', amico_noddi_node, 'dwi')
- dwi_workflow.connect(preproc_dwi_node, 'bval', amico_noddi_node, 'bval')
- dwi_workflow.connect(preproc_dwi_node, 'bvec', amico_noddi_node, 'bvec')
- dwi_workflow.connect(preproc_dwi_node, 'dwi_mask', amico_noddi_node, 'mask')
- amico_noddi_node.inputs.output_dir = amico_output_dir
- amico_noddi_node.inputs.direction_filename = rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "noddi", 'param': 'direction'}, 'dwimap', '.nii.gz')
- amico_noddi_node.inputs.icvf_filename = rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "noddi", 'param': 'icvf'}, 'dwimap', '.nii.gz')
- amico_noddi_node.inputs.isovf_filename = rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "noddi", 'param': 'isovf'}, 'dwimap', '.nii.gz')
- amico_noddi_node.inputs.od_filename = rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "noddi", 'param': 'odi'}, 'dwimap', '.nii.gz')
- amico_noddi_node.inputs.modulated_icvf_filename = rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "noddi", 'param': 'ModulatedICVF'}, 'dwimap', '.nii.gz')
- amico_noddi_node.inputs.modulated_od_filename = rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "noddi", 'param': 'ModulatedODI'}, 'dwimap', '.nii.gz')
- amico_noddi_node.inputs.config_filename = rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "noddi"}, 'config', '.pickle')
- # Connect outputs to dwi_scalarmaps_output_node
- dwi_workflow.connect(amico_noddi_node, 'icvf', dwi_scalarmaps_output_node, 'icvf_img')
- dwi_workflow.connect(amico_noddi_node, 'isovf', dwi_scalarmaps_output_node, 'isovf_img')
- dwi_workflow.connect(amico_noddi_node, 'od', dwi_scalarmaps_output_node, 'odi_img')
- else:
- # if these files already exist, also set in dwi_scalarmaps_output_node
- amico_output_dir = os.path.join(self.output_path, 'NODDI')
- icvf_img = os.path.join(amico_output_dir, rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "noddi", 'param': 'icvf'}, 'dwimap', '.nii.gz'))
- isovf_img = os.path.join(amico_output_dir, rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "noddi", 'param': 'isovf'}, 'dwimap', '.nii.gz'))
- od_img = os.path.join(amico_output_dir, rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "noddi", 'param': 'odi'}, 'dwimap', '.nii.gz'))
- if os.path.exists(icvf_img) and os.path.exists(isovf_img) and os.path.exists(od_img):
- dwi_scalarmaps_output_node.inputs.icvf_img = icvf_img
- dwi_scalarmaps_output_node.inputs.isovf_img = isovf_img
- dwi_scalarmaps_output_node.inputs.odi_img = od_img
- print('[DWI Pipeline] NODDI scalar maps already exist, using them.')
- # ===========================================
- # FSL FDT bedpostx
- # ===========================================
- if fdt_bedpostx:
- bedpostx_output_dir = os.path.join(self.output_path, 'bedpostx')
- bedpostx_node = Node(BedpostxGPUCustom(), name="bedpostx")
- dwi_workflow.connect(preproc_dwi_node, "preproc_dwi", bedpostx_node, "dwi_img")
- dwi_workflow.connect(preproc_dwi_node, "bvec", bedpostx_node, "bvec")
- dwi_workflow.connect(preproc_dwi_node, "bval", bedpostx_node, "bval")
- dwi_workflow.connect(preproc_dwi_node, "dwi_mask", bedpostx_node, "mask")
- bedpostx_node.inputs.out_dir = bedpostx_output_dir
- # ===========================================
- # Connectome
- # ===========================================
- if self.connectome:
- connectome_output_dir = os.path.join(self.output_path, 'connectome')
- os.makedirs(connectome_output_dir, exist_ok=True)
- # Prepare connectome node
- prepare_connectome_node = Node(ConnectomePrepare(), name='prepare_connectome')
- dwi_workflow.connect(preproc_dwi_mif_node, 'preproc_dwi', prepare_connectome_node, 'preproc_dwi_mif')
- dwi_workflow.connect(preproc_dwi_mif_node, 'dwi_mask', prepare_connectome_node, 'dwi_mask_mif')
- prepare_connectome_node.inputs.output_dir = connectome_output_dir
- dwi_workflow.connect(refine_aseg_node, 'out_aseg', prepare_connectome_node, 'aseg')
- prepare_connectome_node.inputs.wm_response = rename_bids_file(preproc_dwi_filename, {'param': 'fod', 'label': 'WM', 'desc': None}, 'dwimap', '.txt')
- prepare_connectome_node.inputs.wm_fod = rename_bids_file(preproc_dwi_filename, {'param': 'fod', 'label': 'WM', 'desc': None}, 'dwimap', '.mif')
- prepare_connectome_node.inputs.wm_fod_norm = rename_bids_file(preproc_dwi_filename, {'param': 'fod', 'label': 'WM', 'desc': 'norm'}, 'dwimap', '.mif')
- prepare_connectome_node.inputs.gm_response = rename_bids_file(preproc_dwi_filename, {'param': 'fod', 'label': 'GM', 'desc': None}, 'dwimap', '.txt')
- prepare_connectome_node.inputs.gm_fod = rename_bids_file(preproc_dwi_filename, {'param': 'fod', 'label': 'GM', 'desc': None}, 'dwimap', '.mif')
- prepare_connectome_node.inputs.gm_fod_norm = rename_bids_file(preproc_dwi_filename, {'param': 'fod', 'label': 'GM', 'desc': 'norm'}, 'dwimap', '.mif')
- prepare_connectome_node.inputs.csf_response = rename_bids_file(preproc_dwi_filename, {'param': 'fod', 'label': 'CSF', 'desc': None}, 'dwimap', '.txt')
- prepare_connectome_node.inputs.csf_fod = rename_bids_file(preproc_dwi_filename, {'param': 'fod', 'label': 'CSF', 'desc': None}, 'dwimap', '.mif')
- prepare_connectome_node.inputs.csf_fod_norm = rename_bids_file(preproc_dwi_filename, {'param': 'fod', 'label': 'CSF', 'desc': 'norm'}, 'dwimap', '.mif')
- prepare_connectome_node.inputs.sift_mu = rename_bids_file(preproc_dwi_filename, {'model': 'sift2', 'desc': None}, 'mu', '.txt')
- prepare_connectome_node.inputs.five_tt_dwi = rename_bids_file(preproc_dwi_filename, {'desc': None}, '5tt', '.mif')
- prepare_connectome_node.inputs.gmwmSeed_dwi = rename_bids_file(preproc_dwi_filename, {'desc': None}, 'gmwmSeed', '.mif')
- prepare_connectome_node.inputs.streamlines = rename_bids_file(preproc_dwi_filename, {'desc': None}, 'streamlines', '.tck')
- prepare_connectome_node.inputs.sift_weights = rename_bids_file(preproc_dwi_filename, {'model': 'sift2', 'desc': None}, 'streamlineweights', '.txt')
- # Prepare parcellation
- fs_parcellation_config = Node(Merge(2), name='merge_fs_parcellation_config')
- fs_parcellation_config.inputs.in1 = get_package_path('data', 'labelconvert', 'fs_default.txt')
- fs_parcellation_config.inputs.in2 = get_package_path('data', 'labelconvert', 'custom', 'fs_aparc.txt')
- prepare_fs_parcellation_node = MapNode(LabelConvert(), name='prepare_parcellation', iterfield=['in_config', 'out_file'])
- dwi_workflow.connect(refine_aseg_node, 'out_aseg', prepare_fs_parcellation_node, 'in_file')
- prepare_fs_parcellation_node.inputs.in_lut = get_package_path('data', 'labelconvert_in', 'FreeSurferColorLUT.txt')
- prepare_fs_parcellation_node.inputs.args = '-force'
- dwi_workflow.connect(fs_parcellation_config, 'out', prepare_fs_parcellation_node, 'in_config')
- prepare_fs_parcellation_node.inputs.out_file = [
- os.path.join(connectome_output_dir, rename_bids_file(preproc_dwi_filename, {'seg': 'aparcaseg', 'desc': None}, 'parcellation', '.mif')),
- os.path.join(connectome_output_dir, rename_bids_file(preproc_dwi_filename, {'seg': 'aparc', 'desc': None}, 'parcellation', '.mif'))
- ]
- # Connectome construction
- construct_connectome_node = MapNode(BuildConnectome(), name='construct_connectome', iterfield=['in_parc', 'out_file'])
- dwi_workflow.connect(prepare_connectome_node, 'global_streamlines', construct_connectome_node, 'in_file')
- construct_connectome_node.inputs.out_file = [
- os.path.join(connectome_output_dir, rename_bids_file(preproc_dwi_filename, {'seg': 'aparcaseg', 'desc': None}, 'connectivity', '.csv')),
- os.path.join(connectome_output_dir, rename_bids_file(preproc_dwi_filename, {'seg': 'aparc', 'desc': None}, 'connectivity', '.csv'))
- ]
- dwi_workflow.connect(prepare_fs_parcellation_node, 'out_file', construct_connectome_node, 'in_parc')
- dwi_workflow.connect(prepare_connectome_node, 'sift_weights', construct_connectome_node, 'in_weights')
- construct_connectome_node.inputs.args = '-zero_diagonal -symmetric -scale_invnodevol'
- # ===========================================
- # Tractography: Designed for lesion-based probabilistic tractography
- # ===========================================
- tractography_output_dir = os.path.join(self.output_path, 'tractography')
- if 'fdt' in self.tractography:
- fdt_tractography_output_dir = os.path.join(tractography_output_dir, 'fdt')
- os.makedirs(fdt_tractography_output_dir, exist_ok=True)
- uncorrected_tractography_output_dir = os.path.join(fdt_tractography_output_dir, 'PathLengthUncorrected')
- seed_mask_tractography = Node(Probtrackx(), name='seed_mask_tractography')
- dwi_workflow.connect(bedpostx_node, 'source_for_probtrackx', seed_mask_tractography, 'source')
- dwi_workflow.connect(bedpostx_node, 'mask_for_probtrackx', seed_mask_tractography, 'dwi_mask')
- dwi_workflow.connect(seed_mask_to_fs_node, 'output_image', seed_mask_tractography, 'seed_mask') # in fs space
- dwi_workflow.connect(concat_fs_to_dwi_xfm_node, 'out_file', seed_mask_tractography, 'seed_to_dwi_xfm')
- seed_mask_tractography.inputs.seed_ref = os.path.join(fs_output, 'mri', 'orig.mgz') # fs native space
- dwi_workflow.connect(create_cortical_GM_mask_node, 'output_gm_mask', seed_mask_tractography, 'waypoints') # in fs space
- seed_mask_tractography.inputs.path_length_correction = False
- seed_mask_tractography.inputs.output_dir = uncorrected_tractography_output_dir
- seed_mask_tractography.inputs.nsamples = 10000
- seed_mask_tractography.inputs.args = '-l --forcedir --opd --ompl'
- waytotal_node = Node(ReadSingleValue(), name='waytotal_node')
- dwi_workflow.connect(seed_mask_tractography, 'waytotal', waytotal_node, 'input_file')
- lh_fdtpaths_to_surf = Node(MRIvol2surf(), name='lh_fdtpaths_to_surf')
- dwi_workflow.connect(seed_mask_tractography, 'fdt_paths', lh_fdtpaths_to_surf, 'volume')
- lh_fdtpaths_to_surf.inputs.subjects_dir = fs_subjects_dir
- lh_fdtpaths_to_surf.inputs.regheader = fs_subject_id
- lh_fdtpaths_to_surf.inputs.hemi = 'lh'
- lh_fdtpaths_to_surf.inputs.output_surf = os.path.join(uncorrected_tractography_output_dir, f'sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-L_space-fsaverage_TDI.mgh')
- lh_fdtpaths_to_surf.inputs.proj_frac = 0.5
- lh_fdtpaths_to_surf.inputs.target = 'fsaverage'
- mask_lh_fdtpaths_surf = Node(ApplyIndexTxtToMgh(), name='mask_lh_fdtpaths_surf')
- dwi_workflow.connect(lh_fdtpaths_to_surf, 'output_surf', mask_lh_fdtpaths_surf, 'measure_mgh')
- mask_lh_fdtpaths_surf.inputs.index_txt = get_package_path('data', 'standard', 'fsaverage', 'lh.aparc.label_medial_wall.txt')
- mask_lh_fdtpaths_surf.inputs.output_mgh = os.path.join(uncorrected_tractography_output_dir, f'sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-L_space-fsaverage_desc-NoMedialWall_TDI.mgh')
- rh_fdtpaths_to_surf = Node(MRIvol2surf(), name='rh_fdtpaths_to_surf')
- dwi_workflow.connect(seed_mask_tractography, 'fdt_paths', rh_fdtpaths_to_surf, 'volume')
- rh_fdtpaths_to_surf.inputs.subjects_dir = fs_subjects_dir
- rh_fdtpaths_to_surf.inputs.regheader = fs_subject_id
- rh_fdtpaths_to_surf.inputs.hemi = 'rh'
- rh_fdtpaths_to_surf.inputs.output_surf = os.path.join(uncorrected_tractography_output_dir, f'sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-R_space-fsaverage_TDI.mgh')
- rh_fdtpaths_to_surf.inputs.proj_frac = 0.5
- rh_fdtpaths_to_surf.inputs.target = 'fsaverage'
- mask_rh_fdtpaths_surf = Node(ApplyIndexTxtToMgh(), name='mask_rh_fdtpaths_surf')
- dwi_workflow.connect(rh_fdtpaths_to_surf, 'output_surf', mask_rh_fdtpaths_surf, 'measure_mgh')
- mask_rh_fdtpaths_surf.inputs.index_txt = get_package_path('data', 'standard', 'fsaverage', 'rh.aparc.label_medial_wall.txt')
- mask_rh_fdtpaths_surf.inputs.output_mgh = os.path.join(uncorrected_tractography_output_dir, f'sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-R_space-fsaverage_desc-NoMedialWall_TDI.mgh')
- define_connection_level_node = Node(DefineConnectionLevel(), name='define_connection_levels')
- dwi_workflow.connect(mask_lh_fdtpaths_surf, 'output_mgh', define_connection_level_node, 'lh_mgh')
- dwi_workflow.connect(mask_rh_fdtpaths_surf, 'output_mgh', define_connection_level_node, 'rh_mgh')
- define_connection_level_node.inputs.low_threshold = 3.8e-5
- dwi_workflow.connect(waytotal_node, 'value', define_connection_level_node, 'divisor')
- define_connection_level_node.inputs.output_files = {
- 'lh_unconn': os.path.join(uncorrected_tractography_output_dir, f'sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-L_space-fsaverage_desc-UnConn_mask.mgh'),
- 'rh_unconn': os.path.join(uncorrected_tractography_output_dir, f'sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-R_space-fsaverage_desc-UnConn_mask.mgh'),
- 'lh_lowconn': os.path.join(uncorrected_tractography_output_dir, f'sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-L_space-fsaverage_desc-LowConn_mask.mgh'),
- 'rh_lowconn': os.path.join(uncorrected_tractography_output_dir, f'sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-R_space-fsaverage_desc-LowConn_mask.mgh'),
- 'lh_medconn': os.path.join(uncorrected_tractography_output_dir, f'sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-L_space-fsaverage_desc-MediumConn_mask.mgh'),
- 'rh_medconn': os.path.join(uncorrected_tractography_output_dir, f'sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-R_space-fsaverage_desc-MediumConn_mask.mgh'),
- 'lh_highconn': os.path.join(uncorrected_tractography_output_dir, f'sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-L_space-fsaverage_desc-HighConn_mask.mgh'),
- 'rh_highconn': os.path.join(uncorrected_tractography_output_dir, f'sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-R_space-fsaverage_desc-HighConn_mask.mgh'),
- }
- # remask unconn regions
- mask_lh_unconn_node = Node(ApplyGiiMaskToMgh(), name='mask_lh_unconn_regions')
- dwi_workflow.connect(define_connection_level_node, 'lh_unconn_mask', mask_lh_unconn_node, 'measure_mgh')
- mask_lh_unconn_node.inputs.mask_gii = get_package_path('data', 'standard', 'fsaverage', 'lh.thickness.shape.gii')
- mask_lh_unconn_node.inputs.output_mgh = os.path.join(uncorrected_tractography_output_dir, f'sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-L_space-fsaverage_desc-UnConn_mask.mgh')
- mask_rh_unconn_node = Node(ApplyGiiMaskToMgh(), name='mask_rh_unconn_regions')
- dwi_workflow.connect(define_connection_level_node, 'rh_unconn_mask', mask_rh_unconn_node, 'measure_mgh')
- mask_rh_unconn_node.inputs.mask_gii = get_package_path('data', 'standard', 'fsaverage', 'rh.thickness.shape.gii')
- mask_rh_unconn_node.inputs.output_mgh = os.path.join(uncorrected_tractography_output_dir, f'sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-R_space-fsaverage_desc-UnConn_mask.mgh')
- # extract surface parameters
- uncorr_extract_surf_params = Node(ExtractSurfaceParameters(), name='uncorr_extract_surf_params')
- uncorr_extract_surf_params.inputs.fs_subjects_dir = fs_subjects_dir
- uncorr_extract_surf_params.inputs.sessions = self.subject.sessions_id
- if self.use_freesurfer_longitudinal:
- uncorr_extract_surf_params.inputs.sessions = self.subject.sessions_id + [f'long.sub-{self.subject.subject_id}']
- dwi_workflow.connect(mask_lh_unconn_node, 'output_mgh', uncorr_extract_surf_params, 'lh_unconn_mask')
- dwi_workflow.connect(mask_rh_unconn_node, 'output_mgh', uncorr_extract_surf_params, 'rh_unconn_mask')
- dwi_workflow.connect(define_connection_level_node, 'lh_lowconn_mask', uncorr_extract_surf_params, 'lh_low_conn_mask')
- dwi_workflow.connect(define_connection_level_node, 'rh_lowconn_mask', uncorr_extract_surf_params, 'rh_low_conn_mask')
- dwi_workflow.connect(define_connection_level_node, 'lh_medconn_mask', uncorr_extract_surf_params, 'lh_medium_conn_mask')
- dwi_workflow.connect(define_connection_level_node, 'rh_medconn_mask', uncorr_extract_surf_params, 'rh_medium_conn_mask')
- dwi_workflow.connect(define_connection_level_node, 'lh_highconn_mask', uncorr_extract_surf_params, 'lh_high_conn_mask')
- dwi_workflow.connect(define_connection_level_node, 'rh_highconn_mask', uncorr_extract_surf_params, 'rh_high_conn_mask')
- uncorr_extract_surf_params.inputs.output_dir = uncorrected_tractography_output_dir
- uncorr_extract_surf_params.inputs.csv_file_name = f'sub-{self.subject.subject_id}_ses-{self.session.session_id}_desc-ConnectivityLevels_surfpars.csv'
- if 'mrtrix3' in self.tractography:
- seed_based_track_node = Node(IdentityInterface(fields=['seed_based_track']), name='seed_based_track')
- tckgen_output_dir = os.path.join(tractography_output_dir, 'mrtrix3')
- tckgen_node = Node(Tractography(), name='tckgen')
- os.makedirs(tckgen_output_dir, exist_ok=True)
- dwi_workflow.connect(preproc_dwi_mif_node, 'preproc_dwi', tckgen_node, 'in_file')
- dwi_workflow.connect(preproc_dwi_node, 'dwi_mask', tckgen_node, 'roi_mask')
- dwi_workflow.connect(seed_mask_to_dwi_node, 'out_file', tckgen_node, 'seed_image')
- tckgen_node.inputs.out_file = os.path.join(tckgen_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': 'ROIseed'}, 'streamlines', '.tck'))
- tckgen_node.inputs.algorithm = 'Tensor_Prob'
- tckgen_node.inputs.select = 10000
- tckgen_node.inputs.nthreads = 4
- tckgen_node.inputs.args = '-force'
- dwi_workflow.connect(tckgen_node, 'out_file', seed_based_track_node, 'seed_based_track')
- if nawm_dseg is not None:
- nawm_without_tract = os.path.join(tckgen_output_dir, rename_bids_file(nawm_dseg, {'desc': 'ConnTractRemoved'}, 'dseg', '.nii.gz'))
- remove_tract_region_node = Node(RemoveTractRegion(), name='remove_tract_region')
- dwi_workflow.connect(seed_based_track_node, 'seed_based_track', remove_tract_region_node, 'tck_file')
- dwi_workflow.connect(merge_nawm_dseg_node, 'out_file', remove_tract_region_node, 'wm_mask')
- remove_tract_region_node.inputs.out_tract_mask = os.path.join(tckgen_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': 'ROIseedTract'}, 'mask', '.nii.gz'))
- remove_tract_region_node.inputs.out_tdi_norm = os.path.join(tckgen_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': 'ROIseedTract'}, 'tdi', '.nii.gz'))
- remove_tract_region_node.inputs.out_wm_mask = nawm_without_tract
- # ===========================================
- # DTI-ALPS
- # ===========================================
- #if self.dtialps and self.preprocess_method != 'post_qsiprep':
- if self.dtialps:
- print("[DWI Pipeline] DTI-ALPS analysis: True")
- dti_alps_node = Node(ALPS(), name='dti_alps')
- dwi_workflow.connect(dti_fit_output_node, 'fa_img', dti_alps_node, 'fa_img')
- dti_alps_node.inputs.output_dir = os.path.join(self.output_path, 'DTI-ALPS')
- dti_alps_node.inputs.alps_dir = get_package_path("pipelines", "external", "alps")
- # self.dtialps_register_method: 1 for 'flirt', 2 for 'synthmorph'
- dti_alps_node.inputs.register_method = 'flirt' if self.dtialps_register_method == 1 else 'synthmorph' if self.dtialps_register_method == 2 else 'ants'
- dwi_workflow.connect(dti_fit_output_node, 'tensor_img', dti_alps_node, 'tensor_img')
- # BIDS output filenames in MNI space
- mni_entities = {"space": "MNI152NLin6ASym", "desc": None, "model": "tensor"}
- dti_alps_node.inputs.fa_out = os.path.join(dti_alps_node.inputs.output_dir, rename_bids_file(preproc_dwi_filename, {**mni_entities, "param": "fa"}, "dwimap", ".nii.gz"))
- dti_alps_node.inputs.xx_out = os.path.join(dti_alps_node.inputs.output_dir, rename_bids_file(preproc_dwi_filename, {**mni_entities, "param": "xx"}, "dwimap", ".nii.gz"))
- dti_alps_node.inputs.yy_out = os.path.join(dti_alps_node.inputs.output_dir, rename_bids_file(preproc_dwi_filename, {**mni_entities, "param": "yy"}, "dwimap", ".nii.gz"))
- dti_alps_node.inputs.zz_out = os.path.join(dti_alps_node.inputs.output_dir, rename_bids_file(preproc_dwi_filename, {**mni_entities, "param": "zz"}, "dwimap", ".nii.gz"))
- # If warp already exists
- target_warp = os.path.join(self.subject.bids_dir, 'derivatives', 'xfm', f'sub-{self.subject.subject_id}', f'ses-{self.session.session_id}', f'sub-{self.subject.subject_id}_ses-{self.session.session_id}_from-T1w_to-MNI152NLin6ASym_warp.nii.gz')
- if self.dtialps_register_method == 2 and os.path.exists(target_warp) and self.dwi_t1w_register:
- dwi_workflow.connect(inputnode, 't1w_file', dti_alps_node, 't1_img')
- dwi_workflow.connect(dwi_to_t1w_reg_node, 'out_matrix_file', dti_alps_node, 'fa_to_t1w_affine')
- dti_alps_node.inputs.t1_to_mni_warp = target_warp
- # ===========================================
- # PVeD
- # ===========================================
- if self.pved:
- print(f"[DWI Pipeline] PVeD analysis: True (method={self.pved_method})")
- if self.pved_method == 'v1':
- pved_node = Node(PVeD(), name='pved_v1')
- dwi_workflow.connect(qsdr_reconstruction_node, 'out_file', pved_node, 'qsdr_fib_file')
- pved_node.inputs.output_dir = os.path.join(self.output_path, 'PVeD')
- pved_node.inputs.script_path = get_package_path("pipelines", "matlab", "pved", "pved_single.m")
- pved_node.inputs.spm_path = get_package_path("data", "matlab_toolbox", "spm12")
- pved_node.inputs.pved_path = get_package_path("data", "matlab_toolbox", "EstPVeD", 'MATLAB', 'pkg_pved_est')
- else:
- pved_prepare_node = Node(PVeDPrepareMNI(), name='pved_prepare_mni')
- dwi_workflow.connect(dti_fit_output_node, 'fa_img', pved_prepare_node, 'fa_file')
- dwi_workflow.connect(dti_fit_output_node, 'md_img', pved_prepare_node, 'md_file')
- dwi_workflow.connect(dti_fit_output_node, 'tensor_img', pved_prepare_node, 'tensor_file')
- dwi_workflow.connect(dwi_to_t1w_reg_node, 'out_matrix_file', pved_prepare_node, 'dwi_to_t1w_affine')
- dwi_workflow.connect(inputnode, 't1w_file', pved_prepare_node, 't1w_reference')
- pved_prepare_node.inputs.t1w_to_mni_warp = pved_t1w_to_mni_warp
- pved_prepare_node.inputs.output_dir = os.path.join(self.output_path, 'PVeD')
- pved_prepare_node.inputs.subject_id = f"sub-{self.subject.subject_id}"
- pved_prepare_node.inputs.session_id = f"ses-{self.session.session_id}"
- pved_node = Node(PVeDAtropos(), name='pved_v2')
- dwi_workflow.connect(pved_prepare_node, 'fa_file', pved_node, 'fa_file')
- dwi_workflow.connect(pved_prepare_node, 'md_file', pved_node, 'md_file')
- dwi_workflow.connect(pved_prepare_node, 'tensor_file', pved_node, 'tensor_file')
- pved_node.inputs.output_dir = os.path.join(self.output_path, 'PVeD')
- pved_node.inputs.subject_id = f"sub-{self.subject.subject_id}"
- pved_node.inputs.session_id = f"ses-{self.session.session_id}"
- # ===========================================
- # Free Water
- # ===========================================
- freewater_node = Node(IdentityInterface(fields=['single_shell_fw_img', 'markvcid_fw_img', 'dti_fw_img']), name='freewater_node')
- if 'single_shell_freewater' in self.freewater:
- single_shell_freewater_node = Node(SingleShellFW(), name='single_shell_freewater')
- dwi_workflow.connect(inputnode, 'bval_file', single_shell_freewater_node, 'fbval')
- dwi_workflow.connect(preproc_dwi_node, 'preproc_dwi', single_shell_freewater_node, 'fdwi')
- dwi_workflow.connect(preproc_dwi_node, 'bvec', single_shell_freewater_node, 'fbvec')
- dwi_workflow.connect(preproc_dwi_node, 'dwi_mask', single_shell_freewater_node, 'mask_file')
- fw_output_path = os.path.join(self.output_path, 'freewater', 'single_shell_freewater')
- single_shell_freewater_node.inputs.working_directory = fw_output_path
- single_shell_freewater_node.inputs.output_directory = fw_output_path
- single_shell_freewater_node.inputs.crop_shells = False
- dwi_workflow.connect(single_shell_freewater_node, 'output_fw', freewater_node, 'fw_img')
- else:
- freewater_node.inputs.fw_img = os.path.join(self.output_path, 'single_shell_freewater', 'freewater.nii.gz')
- if 'markvcid_freewater' in self.freewater:
- markvcid_freewater_node = Node(MarkVCIDFreeWater(), name='markvcid_freewater')
- dwi_workflow.connect(preproc_dwi_node, 'preproc_dwi', markvcid_freewater_node, 'in_dwi')
- dwi_workflow.connect(preproc_dwi_node, 'bvec', markvcid_freewater_node, 'in_dwi_bvec')
- dwi_workflow.connect(preproc_dwi_node, 'bval', markvcid_freewater_node, 'in_dwi_bval')
- dwi_workflow.connect(preproc_dwi_node, 'dwi_mask', markvcid_freewater_node, 'in_dwi_mask')
- fw_output_path = os.path.join(self.output_path, 'freewater', 'markvcid_freewater')
- markvcid_freewater_node.inputs.output_dir = fw_output_path
- markvcid_freewater_node.inputs.script_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'external', 'MarkVCID2', 'scripts_FW_CONSORTIUM'))
- markvcid_freewater_node.inputs.output_fw_img = os.path.join(os.path.dirname(fw_output_path), rename_bids_file(preproc_dwi_filename, {'model': 'tensor', 'param': 'freewater', 'desc': 'MarkVCID2'}, 'dwimap', '.nii.gz'))
- dwi_workflow.connect(markvcid_freewater_node, 'out_fw', freewater_node, 'markvcid_fw_img')
- dwi_workflow.connect(markvcid_freewater_node, 'out_fw', dwi_scalarmaps_output_node, 'markvcid2_fw_img')
- elif os.path.exists(os.path.join(self.output_path, 'freewater', rename_bids_file(preproc_dwi_filename, {'model': 'tensor', 'param': 'freewater', 'desc': 'MarkVCID2'}, 'dwimap', '.nii.gz'))):
- dwi_scalarmaps_output_node.inputs.markvcid2_fw_img = os.path.join(self.output_path, 'freewater', rename_bids_file(preproc_dwi_filename, {'model': 'tensor', 'param': 'freewater', 'desc': 'MarkVCID2'}, 'dwimap', '.nii.gz'))
- if 'dti_freewater' in self.freewater:
- dti_freewater_node = Node(FreeWaterTensor(), name='dti_freewater')
- dwi_workflow.connect(preproc_dwi_node, 'preproc_dwi', dti_freewater_node, 'dwi_file')
- dwi_workflow.connect(preproc_dwi_node, 'bvec', dti_freewater_node, 'bvec_file')
- dwi_workflow.connect(preproc_dwi_node, 'bval', dti_freewater_node, 'bval_file')
- dwi_workflow.connect(preproc_dwi_node, 'dwi_mask', dti_freewater_node, 'mask_file')
- fw_output_path = os.path.join(self.output_path, 'freewater', 'tensor_model_freewater')
- dti_freewater_node.inputs.output_dir = fw_output_path
- dwi_workflow.connect(dti_freewater_node, 'freewater_file', freewater_node, 'dti_fw_img')
- else:
- freewater_node.inputs.dti_fw_img = os.path.join(self.output_path, 'tensor_model_freewater', 'dti_freewater.nii.gz')
- # ===========================================
- # PSMD
- # ===========================================
- if self.psmd:
- print("[DWI Pipeline] PSMD analysis: True (using pipeline dtifit FA/MD)")
- psmd_output_dir = os.path.join(self.output_path, 'psmd')
- psmd_node = Node(PSMDCommandLine(), name="psmd_node")
- dwi_workflow.connect(dti_fit_output_node, 'fa_img', psmd_node, 'fa_image')
- dwi_workflow.connect(dti_fit_output_node, 'md_img', psmd_node, 'md_image')
- psmd_node.inputs.mask_file = self.psmd_skeleton_mask
- if self.psmd_exclude_seed_mask:
- dwi_workflow.connect(seed_mask_to_dwi_node, 'out_file', psmd_node, 'lesion_mask')
- psmd_node.inputs.output_dir = psmd_output_dir
- ###########################
- # Visual Pathway Analysis #
- ###########################
- if self.visual_pathway_analysis:
- vpa_output_dir = os.path.join(self.output_path, 'visual_pathway_analysis')
- os.makedirs(vpa_output_dir, exist_ok=True)
- from cvdproc.pipelines.dmri.vp_project.tt_process_nipype import TTGZToTCK, TTGZToTDI, FlipTT
- gather_gqi_fib_node = Node(IdentityInterface(fields=['gqi_fib']), name='gqi_fib_gather')
- if self.preprocess_method == 'post_qsiprep':
- qsirecon_output_dir = self.session._find_output('qsirecon-DSIStudio')
- gqi_fib = os.path.join(qsirecon_output_dir, 'dwi', rename_bids_file(preproc_dwi_filename, {"desc": None, "model": "gqi"}, 'dwimap', '.fib.gz'))
- if not os.path.exists(gqi_fib):
- raise FileNotFoundError(f"GQI .fib.gz file not found in {qsirecon_output_dir}. Please run QSIRecon with DSIStudio reconstruction first.")
- gather_gqi_fib_node.inputs.gqi_fib = gqi_fib
- fix_header = False
- else:
- fix_header = True
- print("[DWI Pipeline] Fix header in VP analysis: True")
- if self.dsistudio_dti:
- dwi_workflow.connect(dti_reconstruction_node, 'out_file', gather_gqi_fib_node, 'gqi_fib')
- else:
- dwi_workflow.connect(gqi_reconstruction_node, 'out_file', gather_gqi_fib_node, 'gqi_fib')
- vp_flip_y = self.visual_pathway_analysis_flip_y
- # 1. Prepare ROIs
- from cvdproc.pipelines.dmri.vp_project.prepare_roi_nipype import PrepareVPROI
- prepare_vp_roi_node = Node(PrepareVPROI(), name='prepare_vp_roi')
- dwi_workflow.connect(inputnode, 'fs_output', prepare_vp_roi_node, 'fs_output_dir')
- prepare_vp_roi_node.inputs.subject_id = self.subject.subject_id
- prepare_vp_roi_node.inputs.session_id = self.session.session_id
- dwi_workflow.connect(concat_fs_to_dwi_xfm_node, 'out_file', prepare_vp_roi_node, 'fs_to_dwi_xfm')
- dwi_workflow.connect(preproc_dwi_node, 'b0', prepare_vp_roi_node, 'dwi_ref')
- prepare_vp_roi_node.inputs.output_dir = anat_output_dir
- prepare_vp_roi_node.inputs.space_entity = space_entity
- # ROI for Meyer's Loop
- roi4meyersloop_to_t1w_node = MapNode(MRIConvertApplyWarp(), name='roi4meyersloop_to_t1w', iterfield=['input_image', 'output_image'])
- dwi_workflow.connect(inputnode, 'mni_to_t1w_warp', roi4meyersloop_to_t1w_node, 'warp_image')
- roi4meyersloop_to_t1w_node.inputs.input_image = [
- get_package_path('data', 'standard', 'MNI152', 'meyers_loop_roi', 'meyers_loop_roi_L.nii.gz'),
- get_package_path('data', 'standard', 'MNI152', 'meyers_loop_roi', 'meyers_loop_roi_R.nii.gz')
- ]
- roi4meyersloop_to_t1w_node.inputs.output_image = [
- os.path.join(anat_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-L_space-T1w_desc-roi4meyersloop_mask.nii.gz"),
- os.path.join(anat_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-R_space-T1w_desc-roi4meyersloop_mask.nii.gz")
- ]
- roi4meyersloop_to_t1w_node.inputs.interp = 'nearest'
- roi4meyersloop_to_dwi_node = MapNode(FLIRT(), name='roi4meyersloop_to_dwi', iterfield=['in_file', 'out_file'])
- dwi_workflow.connect(invert_dwi_to_t1w_reg_node, 'out_file', roi4meyersloop_to_dwi_node, 'in_matrix_file')
- dwi_workflow.connect(roi4meyersloop_to_t1w_node, 'output_image', roi4meyersloop_to_dwi_node, 'in_file')
- roi4meyersloop_to_dwi_node.inputs.out_file = [
- os.path.join(anat_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-L_space-{space_entity}_desc-roi4meyersloop_mask.nii.gz"),
- os.path.join(anat_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-R_space-{space_entity}_desc-roi4meyersloop_mask.nii.gz")
- ]
- dwi_workflow.connect(preproc_dwi_node, 'b0', roi4meyersloop_to_dwi_node, 'reference')
- roi4meyersloop_to_dwi_node.inputs.interp = "nearestneighbour"
- roi4meyersloop_to_dwi_node.inputs.apply_xfm = True
- # select left and right roi separately
- fetch_roi4meyersloop_L = Node(Select(index=0), name="fetch_roi4meyersloop_L")
- fetch_roi4meyersloop_R = Node(Select(index=1), name="fetch_roi4meyersloop_R")
- dwi_workflow.connect(roi4meyersloop_to_dwi_node, "out_file", fetch_roi4meyersloop_L, "inlist")
- dwi_workflow.connect(roi4meyersloop_to_dwi_node, "out_file", fetch_roi4meyersloop_R, "inlist")
- # 2. ROI Tractography
- raw_track_output_dir = os.path.join(vpa_output_dir, 'raw_tracts')
- os.makedirs(raw_track_output_dir, exist_ok=True)
- lh_or_track_node = Node(DSIstudioTracking(), name='lh_or_track')
- dwi_workflow.connect(gather_gqi_fib_node, 'gqi_fib', lh_or_track_node, 'source')
- lh_or_track_node.inputs.output = os.path.join(raw_track_output_dir, 'lh_OR.tt.gz')
- lh_or_track_node.inputs.thread_count = 8
- lh_or_track_node.inputs.tract_count = 2500
- #lh_or_track_node.inputs.turning_angle = 45
- lh_or_track_node.inputs.max_length = 200
- lh_or_track_node.inputs.args = '--tip_iteration=0 --method=0'
- dwi_workflow.connect(prepare_vp_roi_node, 'lh_lgn_dilx_roi', lh_or_track_node, 'seed')
- dwi_workflow.connect(prepare_vp_roi_node, 'lh_v1_ext2_roi', lh_or_track_node, 'end')
- #dwi_workflow.connect(fetch_roi4meyersloop_L, 'out', lh_or_track_node, 'ter')
- rh_or_track_node = Node(DSIstudioTracking(), name='rh_or_track')
- dwi_workflow.connect(gather_gqi_fib_node, 'gqi_fib', rh_or_track_node, 'source')
- rh_or_track_node.inputs.output = os.path.join(raw_track_output_dir, 'rh_OR.tt.gz')
- rh_or_track_node.inputs.thread_count = 8
- rh_or_track_node.inputs.tract_count = 2500
- #rh_or_track_node.inputs.turning_angle = 45
- rh_or_track_node.inputs.max_length = 200
- rh_or_track_node.inputs.args = '--tip_iteration=0 --method=0'
- dwi_workflow.connect(prepare_vp_roi_node, 'rh_lgn_dilx_roi', rh_or_track_node, 'seed')
- dwi_workflow.connect(prepare_vp_roi_node, 'rh_v1_ext2_roi', rh_or_track_node, 'end')
- #dwi_workflow.connect(fetch_roi4meyersloop_R, 'out', rh_or_track_node, 'ter')
- lh_ot_track_node = Node(DSIstudioTracking(), name='lh_ot_track')
- dwi_workflow.connect(gather_gqi_fib_node, 'gqi_fib', lh_ot_track_node, 'source')
- lh_ot_track_node.inputs.output = os.path.join(raw_track_output_dir, 'lh_OT.tt.gz')
- lh_ot_track_node.inputs.thread_count = 8
- lh_ot_track_node.inputs.tract_count = 2500
- lh_ot_track_node.inputs.max_length = 60
- lh_ot_track_node.inputs.args = '--tip_iteration=2 --method=1'
- dwi_workflow.connect(prepare_vp_roi_node, 'optic_chiasm_dil1_roi', lh_ot_track_node, 'seed')
- dwi_workflow.connect(prepare_vp_roi_node, 'lh_lgn_dil1_roi', lh_ot_track_node, 'end')
- rh_ot_track_node = Node(DSIstudioTracking(), name='rh_ot_track')
- dwi_workflow.connect(gather_gqi_fib_node, 'gqi_fib', rh_ot_track_node, 'source')
- rh_ot_track_node.inputs.output = os.path.join(raw_track_output_dir, 'rh_OT.tt.gz')
- rh_ot_track_node.inputs.thread_count = 8
- rh_ot_track_node.inputs.tract_count = 2500
- rh_ot_track_node.inputs.max_length = 60
- rh_ot_track_node.inputs.args = '--tip_iteration=2 --method=1'
- dwi_workflow.connect(prepare_vp_roi_node, 'optic_chiasm_dil1_roi', rh_ot_track_node, 'seed')
- dwi_workflow.connect(prepare_vp_roi_node, 'rh_lgn_dil1_roi', rh_ot_track_node, 'end')
- # Experimental: Meyer's Loop
- lr_ml_track_node = Node(DSIstudioTracking(), name='lr_meyersloop_track')
- dwi_workflow.connect(gather_gqi_fib_node, 'gqi_fib', lr_ml_track_node, 'source')
- lr_ml_track_node.inputs.output = os.path.join(raw_track_output_dir, 'lr_MeyersLoop.tt.gz')
- lr_ml_track_node.inputs.thread_count = 8
- lr_ml_track_node.inputs.tract_count = 2500
- lr_ml_track_node.inputs.turning_angle = 45
- lr_ml_track_node.inputs.args = '--tip_iteration=2 --method=1'
- dwi_workflow.connect(fetch_roi4meyersloop_L, 'out', lr_ml_track_node, 'seed')
- dwi_workflow.connect(prepare_vp_roi_node, 'lh_v1_ext2_roi', lr_ml_track_node, 'end')
- rh_ml_track_node = Node(DSIstudioTracking(), name='rh_meyersloop_track')
- dwi_workflow.connect(gather_gqi_fib_node, 'gqi_fib', rh_ml_track_node, 'source')
- rh_ml_track_node.inputs.output = os.path.join(raw_track_output_dir, 'rh_MeyersLoop.tt.gz')
- rh_ml_track_node.inputs.thread_count = 8
- rh_ml_track_node.inputs.tract_count = 2500
- rh_ml_track_node.inputs.turning_angle = 45
- rh_ml_track_node.inputs.args = '--tip_iteration=2 --method=1'
- dwi_workflow.connect(fetch_roi4meyersloop_R, 'out', rh_ml_track_node, 'seed')
- dwi_workflow.connect(prepare_vp_roi_node, 'rh_v1_ext2_roi', rh_ml_track_node, 'end')
- # 3. Refine tracts with ROIs
- from cvdproc.pipelines.dmri.vp_project.refine_vp_nipype import RefineVP
- vp_refine_node = Node(RefineVP(), name='vp_refine')
- # LPS -> RAS if fix_header
- if fix_header:
- gather_raw_track_node = Node(Merge(6), name='gather_raw_track')
- dwi_workflow.connect(lh_or_track_node, 'output', gather_raw_track_node, 'in1')
- dwi_workflow.connect(lh_ot_track_node, 'output', gather_raw_track_node, 'in2')
- dwi_workflow.connect(rh_or_track_node, 'output', gather_raw_track_node, 'in3')
- dwi_workflow.connect(rh_ot_track_node, 'output', gather_raw_track_node, 'in4')
- dwi_workflow.connect(lr_ml_track_node, 'output', gather_raw_track_node, 'in5')
- dwi_workflow.connect(rh_ml_track_node, 'output', gather_raw_track_node, 'in6')
- transform_tt_space_node = MapNode(FlipTT(), name='transform_tt_space', iterfield=['in_tt', 'out_tt'])
- dwi_workflow.connect(gather_raw_track_node, 'out', transform_tt_space_node, 'in_tt')
- transform_tt_space_node.inputs.out_tt = [
- os.path.join(raw_track_output_dir, 'lh_OR_flipy.tt.gz'),
- os.path.join(raw_track_output_dir, 'lh_OT_flipy.tt.gz'),
- os.path.join(raw_track_output_dir, 'rh_OR_flipy.tt.gz'),
- os.path.join(raw_track_output_dir, 'rh_OT_flipy.tt.gz'),
- os.path.join(raw_track_output_dir, 'lr_ML_flipy.tt.gz'),
- os.path.join(raw_track_output_dir, 'rh_ML_flipy.tt.gz'),
- ]
- transform_tt_space_node.inputs.flip_x_axis = False
- transform_tt_space_node.inputs.flip_y_axis = True
- transform_tt_space_node.inputs.flip_z_axis = False
- select_lh_or_node = Node(Select(index=[0]), name="select_lh_or_ras")
- select_lh_ot_node = Node(Select(index=[1]), name="select_lh_ot_ras")
- select_rh_or_node = Node(Select(index=[2]), name="select_rh_or_ras")
- select_rh_ot_node = Node(Select(index=[3]), name="select_rh_ot_ras")
- select_lh_ml_node = Node(Select(index=[4]), name="select_lh_ml_ras")
- select_rh_ml_node = Node(Select(index=[5]), name="select_rh_ml_ras")
- dwi_workflow.connect(transform_tt_space_node, "out_tt", select_lh_or_node, "inlist")
- dwi_workflow.connect(transform_tt_space_node, "out_tt", select_lh_ot_node, "inlist")
- dwi_workflow.connect(transform_tt_space_node, "out_tt", select_rh_or_node, "inlist")
- dwi_workflow.connect(transform_tt_space_node, "out_tt", select_rh_ot_node, "inlist")
- dwi_workflow.connect(transform_tt_space_node, "out_tt", select_lh_ml_node, "inlist")
- dwi_workflow.connect(transform_tt_space_node, "out_tt", select_rh_ml_node, "inlist")
- dwi_workflow.connect(select_lh_or_node, "out", vp_refine_node, "lh_or")
- dwi_workflow.connect(select_lh_ot_node, "out", vp_refine_node, "lh_ot")
- dwi_workflow.connect(select_rh_or_node, "out", vp_refine_node, "rh_or")
- dwi_workflow.connect(select_rh_ot_node, "out", vp_refine_node, "rh_ot")
- dwi_workflow.connect(select_lh_ml_node, "out", vp_refine_node, "lh_ml")
- dwi_workflow.connect(select_rh_ml_node, "out", vp_refine_node, "rh_ml")
- else:
- dwi_workflow.connect(lh_or_track_node, 'output', vp_refine_node, 'lh_or')
- dwi_workflow.connect(lh_ot_track_node, 'output', vp_refine_node, 'lh_ot')
- dwi_workflow.connect(rh_or_track_node, 'output', vp_refine_node, 'rh_or')
- dwi_workflow.connect(rh_ot_track_node, 'output', vp_refine_node, 'rh_ot')
- dwi_workflow.connect(lr_ml_track_node, 'output', vp_refine_node, 'lh_ml')
- dwi_workflow.connect(rh_ml_track_node, 'output', vp_refine_node, 'rh_ml')
- dwi_workflow.connect(prepare_vp_roi_node, 'optic_chiasm_dil1_roi', vp_refine_node, 'cho_roi')
- dwi_workflow.connect(prepare_vp_roi_node, 'lh_lgn_dil1_roi', vp_refine_node, 'lh_lgn_roi')
- dwi_workflow.connect(prepare_vp_roi_node, 'lh_lgn_dilx_roi', vp_refine_node, 'lh_lgn_dia_x_roi')
- dwi_workflow.connect(prepare_vp_roi_node, 'lh_lgn_extendpart_roi', vp_refine_node, 'lh_lgn_extendpart_roi')
- dwi_workflow.connect(prepare_vp_roi_node, 'rh_lgn_dil1_roi', vp_refine_node, 'rh_lgn_roi')
- dwi_workflow.connect(prepare_vp_roi_node, 'rh_lgn_dilx_roi', vp_refine_node, 'rh_lgn_dia_x_roi')
- dwi_workflow.connect(prepare_vp_roi_node, 'rh_lgn_extendpart_roi', vp_refine_node, 'rh_lgn_extendpart_roi')
- dwi_workflow.connect(prepare_vp_roi_node, 'lh_v1_ext2_roi', vp_refine_node, 'lh_v1_roi')
- dwi_workflow.connect(prepare_vp_roi_node, 'rh_v1_ext2_roi', vp_refine_node, 'rh_v1_roi')
- dwi_workflow.connect(fetch_roi4meyersloop_L, 'out', vp_refine_node, 'lh_meyersloop_roi')
- dwi_workflow.connect(fetch_roi4meyersloop_R, 'out', vp_refine_node, 'rh_meyersloop_roi')
- vp_refine_node.inputs.output_dir = vpa_output_dir
- vp_refine_node.inputs.output_lh_ot = rename_bids_file(preproc_dwi_filename, {'bundle': 'OT', 'hemi': 'L', 'desc': 'voxelspace'}, 'streamlines', '.tt.gz')
- vp_refine_node.inputs.output_rh_ot = rename_bids_file(preproc_dwi_filename, {'bundle': 'OT', 'hemi': 'R', 'desc': 'voxelspace'}, 'streamlines', '.tt.gz')
- vp_refine_node.inputs.output_lh_or = rename_bids_file(preproc_dwi_filename, {'bundle': 'OR', 'hemi': 'L', 'desc': 'voxelspace'}, 'streamlines', '.tt.gz')
- vp_refine_node.inputs.output_rh_or = rename_bids_file(preproc_dwi_filename, {'bundle': 'OR', 'hemi': 'R', 'desc': 'voxelspace'}, 'streamlines', '.tt.gz')
- vp_refine_node.inputs.output_lh_ml = rename_bids_file(preproc_dwi_filename, {'bundle': 'ML', 'hemi': 'L', 'desc': 'voxelspace'}, 'streamlines', '.tt.gz')
- vp_refine_node.inputs.output_rh_ml = rename_bids_file(preproc_dwi_filename, {'bundle': 'ML', 'hemi': 'R', 'desc': 'voxelspace'}, 'streamlines', '.tt.gz')
- if fix_header:
- gather_fliped_refined_tracts = Node(Merge(6), name='gather_fliped_refined_tracts')
- dwi_workflow.connect(vp_refine_node, 'refined_lh_ot', gather_fliped_refined_tracts, 'in1')
- dwi_workflow.connect(vp_refine_node, 'refined_rh_ot', gather_fliped_refined_tracts, 'in2')
- dwi_workflow.connect(vp_refine_node, 'refined_lh_or', gather_fliped_refined_tracts, 'in3')
- dwi_workflow.connect(vp_refine_node, 'refined_rh_or', gather_fliped_refined_tracts, 'in4')
- dwi_workflow.connect(vp_refine_node, 'refined_lh_ml', gather_fliped_refined_tracts, 'in5')
- dwi_workflow.connect(vp_refine_node, 'refined_rh_ml', gather_fliped_refined_tracts, 'in6')
- re_flip_node = MapNode(FlipTT(), name='re_flip_tt', iterfield=['in_tt', 'out_tt'])
- dwi_workflow.connect(gather_fliped_refined_tracts, 'out', re_flip_node, 'in_tt')
- re_flip_node.inputs.out_tt = [
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'bundle': 'OT', 'hemi': 'L', 'desc': 'voxelspace'}, 'streamlines', '.tt.gz')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'bundle': 'OT', 'hemi': 'R', 'desc': 'voxelspace'}, 'streamlines', '.tt.gz')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'bundle': 'OR', 'hemi': 'L', 'desc': 'voxelspace'}, 'streamlines', '.tt.gz')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'bundle': 'OR', 'hemi': 'R', 'desc': 'voxelspace'}, 'streamlines', '.tt.gz')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'bundle': 'ML', 'hemi': 'L', 'desc': 'voxelspace'}, 'streamlines', '.tt.gz')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'bundle': 'ML', 'hemi': 'R', 'desc': 'voxelspace'}, 'streamlines', '.tt.gz')),
- ]
- re_flip_node.inputs.flip_x_axis = False
- re_flip_node.inputs.flip_y_axis = True
- re_flip_node.inputs.flip_z_axis = False
- gather_refined_tracts = Node(IdentityInterface(fields=['out']), name='gather_refined_tracts')
- dwi_workflow.connect(re_flip_node, 'out_tt', gather_refined_tracts, 'out')
- else:
- gather_refined_tracts = Node(Merge(6), name='gather_refined_tracts')
- dwi_workflow.connect(vp_refine_node, 'refined_lh_ot', gather_refined_tracts, 'in1')
- dwi_workflow.connect(vp_refine_node, 'refined_rh_ot', gather_refined_tracts, 'in2')
- dwi_workflow.connect(vp_refine_node, 'refined_lh_or', gather_refined_tracts, 'in3')
- dwi_workflow.connect(vp_refine_node, 'refined_rh_or', gather_refined_tracts, 'in4')
- dwi_workflow.connect(vp_refine_node, 'refined_lh_ml', gather_refined_tracts, 'in5')
- dwi_workflow.connect(vp_refine_node, 'refined_rh_ml', gather_refined_tracts, 'in6')
- # tract stats
- tract_stats_node = MapNode(TractStatsInterface(), name='tract_stats', iterfield=['tract', 'output_txt'])
- dwi_workflow.connect(gather_refined_tracts, 'out', tract_stats_node, 'tract')
- dwi_workflow.connect(gather_gqi_fib_node, 'gqi_fib', tract_stats_node, 'source')
- tract_stats_node.inputs.output_txt = [
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': 'OT', 'hemi': 'L'}, 'stats', '.txt')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': 'OT', 'hemi': 'R'}, 'stats', '.txt')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': 'OR', 'hemi': 'L'}, 'stats', '.txt')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': 'OR', 'hemi': 'R'}, 'stats', '.txt')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': 'ML', 'hemi': 'L'}, 'stats', '.txt')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': 'ML', 'hemi': 'R'}, 'stats', '.txt'))
- ]
- tract_stats_node.inputs.export = 'stat'
- # tt.gz to tck
- tt_to_tck_node = MapNode(TTGZToTCK(), name='tt_to_tck', iterfield=['tt_gz_path', 'out_tck'])
- tt_to_tck_node.synchronize = True
- dwi_workflow.connect(gather_refined_tracts, 'out', tt_to_tck_node, 'tt_gz_path')
- dwi_workflow.connect(preproc_dwi_node, 'b0', tt_to_tck_node, 'ref_nii')
- tt_to_tck_node.inputs.out_tck = [
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': 'OT', 'hemi': 'L'}, 'streamlines', '.tck')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': 'OT', 'hemi': 'R'}, 'streamlines', '.tck')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': 'OR', 'hemi': 'L'}, 'streamlines', '.tck')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': 'OR', 'hemi': 'R'}, 'streamlines', '.tck')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': 'ML', 'hemi': 'L'}, 'streamlines', '.tck')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': 'ML', 'hemi': 'R'}, 'streamlines', '.tck'))
- ]
- tt_to_tck_node.inputs.flip_y_axis = vp_flip_y
- # tt.gz to TDI image
- tt_to_tdi_node = MapNode(TTGZToTDI(), name='tt_to_tdi', iterfield=['tt_gz_path', 'out_tdi'])
- tt_to_tdi_node.synchronize = True
- dwi_workflow.connect(gather_refined_tracts, 'out', tt_to_tdi_node, 'tt_gz_path')
- dwi_workflow.connect(preproc_dwi_node, 'b0', tt_to_tdi_node, 'ref_nii')
- tt_to_tdi_node.inputs.out_tdi = [
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': 'OT', 'hemi': 'L'}, 'tdi', '.nii.gz')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': 'OT', 'hemi': 'R'}, 'tdi', '.nii.gz')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': 'OR', 'hemi': 'L'}, 'tdi', '.nii.gz')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': 'OR', 'hemi': 'R'}, 'tdi', '.nii.gz')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': 'ML', 'hemi': 'L'}, 'tdi', '.nii.gz')),
- os.path.join(vpa_output_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': 'ML', 'hemi': 'R'}, 'tdi', '.nii.gz'))
- ]
- tt_to_tdi_node.inputs.flip_y_axis = vp_flip_y
- # gather OR and OT tck files for QC
- select_or_ot = Node(Select(index=[0, 1, 2, 3]), name="select_or_ot")
- dwi_workflow.connect(tt_to_tck_node, "out_tck", select_or_ot, "inlist")
- or_ot_qc_node = Node(PlotTckOnSliceCmd(), name="or_ot_qc")
- or_ot_qc_node.inputs.script_path = get_package_path("pipelines", "dmri", "visualization", "plot_tck_cli.py")
- dwi_workflow.connect(select_or_ot, "out", or_ot_qc_node, "tck_files")
- dwi_workflow.connect(preproc_dwi_node, "b0", or_ot_qc_node, "ref_nii")
- or_ot_qc_node.inputs.out_png = os.path.join(vpa_output_dir, "QC.png")
- # Detect existing visual pathway tracking outputs for value extraction
- # (allows re-extracting values from existing tracts without re-running tracking)
- vpa_check_dir = os.path.join(self.output_path, 'visual_pathway_analysis')
- vp_tck_files = [
- os.path.join(vpa_check_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': b, 'hemi': h}, 'streamlines', '.tck'))
- for h, b in [('L', 'OT'), ('R', 'OT'), ('L', 'OR'), ('R', 'OR'), ('L', 'ML'), ('R', 'ML')]
- ]
- vp_tdi_files = [
- os.path.join(vpa_check_dir, rename_bids_file(preproc_dwi_filename, {'desc': None, 'bundle': b, 'hemi': h}, 'tdi', '.nii.gz'))
- for h, b in [('L', 'OT'), ('R', 'OT'), ('L', 'OR'), ('R', 'OR'), ('L', 'ML'), ('R', 'ML')]
- ]
- vp_tck_exist = all(os.path.exists(f) for f in vp_tck_files)
- vp_tdi_exist = all(os.path.exists(f) for f in vp_tdi_files)
- if self.visual_pathway_analysis:
- vp_extract = True
- elif vp_tck_exist and vp_tdi_exist:
- print("[DWI Pipeline] Visual pathway tracking outputs found. Enabling value extraction from existing tracts.")
- vp_extract = True
- else:
- vp_extract = False
- if vp_extract and not self.visual_pathway_analysis:
- # Use existing tracking outputs for value extraction
- tt_to_tck_node = Node(IdentityInterface(fields=['out_tck']), name='tt_to_tck_stub')
- tt_to_tck_node.inputs.out_tck = vp_tck_files
- tt_to_tdi_node = Node(IdentityInterface(fields=['out_tdi']), name='tt_to_tdi_stub')
- tt_to_tdi_node.inputs.out_tdi = vp_tdi_files
- # Find existing WMH probmap in DWI space
- anat_dir = os.path.join(self.output_path, 'anat')
- wmh_probmap_dwi_glob = os.path.join(anat_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_space-{space_entity}_*probmap.nii.gz")
- wmh_probmap_dwi_candidates = glob.glob(wmh_probmap_dwi_glob)
- wmh_probmap_dwi_file = wmh_probmap_dwi_candidates[0] if len(wmh_probmap_dwi_candidates) == 1 else ''
- wmhprobmap_to_dwi_node = Node(IdentityInterface(fields=['out_file']), name='wmhprobmap_stub')
- wmhprobmap_to_dwi_node.inputs.out_file = wmh_probmap_dwi_file
- #########################
- # Calculate DWI metrics #
- #########################
- if self.calculate_dwi_metrics:
- dwi_metrics_output_dir = os.path.join(self.output_path, 'dwi_metrics_stats')
- os.makedirs(dwi_metrics_output_dir, exist_ok=True)
- dwi_metrics_node = Node(Merge(15), name='dwi_metrics_node')
- dwi_workflow.connect(dwi_scalarmaps_output_node, 'fa_img', dwi_metrics_node, 'in1')
- dwi_workflow.connect(dwi_scalarmaps_output_node, 'md_img', dwi_metrics_node, 'in2')
- dwi_workflow.connect(dwi_scalarmaps_output_node, 'markvcid2_fw_img', dwi_metrics_node, 'in3')
- dwi_workflow.connect(dwi_scalarmaps_output_node, 'ad_img', dwi_metrics_node, 'in4')
- dwi_workflow.connect(dwi_scalarmaps_output_node, 'rd_img', dwi_metrics_node, 'in5')
- dwi_workflow.connect(dwi_scalarmaps_output_node, 'odi_img', dwi_metrics_node, 'in6')
- dwi_workflow.connect(dwi_scalarmaps_output_node, 'icvf_img', dwi_metrics_node, 'in7')
- dwi_workflow.connect(dwi_scalarmaps_output_node, 'isovf_img', dwi_metrics_node, 'in8')
- dwi_workflow.connect(dwi_scalarmaps_output_node, 'gqi_gfa_img', dwi_metrics_node, 'in9')
- dwi_workflow.connect(dwi_scalarmaps_output_node, 'gqi_iso_img', dwi_metrics_node, 'in10')
- dwi_workflow.connect(dwi_scalarmaps_output_node, 'gqi_qa_img', dwi_metrics_node, 'in11')
- dwi_workflow.connect(dwi_scalarmaps_output_node, 'chidia_img', dwi_metrics_node, 'in12')
- dwi_workflow.connect(dwi_scalarmaps_output_node, 'mk_img', dwi_metrics_node, 'in13')
- dwi_workflow.connect(dwi_scalarmaps_output_node, 'ak_img', dwi_metrics_node, 'in14')
- dwi_workflow.connect(dwi_scalarmaps_output_node, 'rk_img', dwi_metrics_node, 'in15')
- exist_dwi_metrics_node = Node(FilterExisting(), name='exist_dwi_metrics')
- dwi_workflow.connect(dwi_metrics_node, 'out', exist_dwi_metrics_node, 'input_file_list')
- if vp_extract:
- # Add WMH probmap to scalar maps
- vp_dwi_metrics_node = Node(Merge(2), name='vp_dwi_metrics_node')
- dwi_workflow.connect(dwi_metrics_node, 'out', vp_dwi_metrics_node, 'in1')
- dwi_workflow.connect(wmhprobmap_to_dwi_node, 'out_file', vp_dwi_metrics_node, 'in2')
- # -------------------------------------- #
- # Calculate DWI metrics using .tck files #
- # -------------------------------------- #
- if 'mrtrix3' in self.tractography:
- seed_tract_scalar_maps_node = Node(TckSampleMultiScalarBundle(), name='seed_tract_scalar_maps')
- dwi_workflow.connect(seed_based_track_node, 'seed_based_track', seed_tract_scalar_maps_node, 'tck_file')
- dwi_workflow.connect(dwi_metrics_node, 'out', seed_tract_scalar_maps_node, 'scalar_files')
- seed_tract_scalar_maps_node.inputs.scalar_names = ["FA", "MD", "FW (MarkVCID2)", "AD", "RD", "ODI", "ICVF", "ISOVF", "GQI_GFA", "GQI_ISO", "GQI_QA", "CHIDIA", "MK", "AK", "RK"]
- seed_tract_scalar_maps_node.inputs.stat_tck = 'mean'
- seed_tract_scalar_maps_node.inputs.output_csv = os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_label-ROIseedTract_desc-mean_dwimap.csv")
- if vp_extract:
- vp_tract_scalar_maps_node = MapNode(TckSampleMultiScalarProfile(), name='vp_tract_scalar_maps', iterfield=['tck_file', 'output_csv'])
- dwi_workflow.connect(tt_to_tck_node, 'out_tck', vp_tract_scalar_maps_node, 'tck_file')
- dwi_workflow.connect(vp_dwi_metrics_node, 'out', vp_tract_scalar_maps_node, 'scalar_files')
- vp_tract_scalar_maps_node.inputs.scalar_names = ["FA", "MD", "FW (MarkVCID2)", "AD", "RD", "ODI", "ICVF", "ISOVF", "GQI_GFA", "GQI_ISO", "GQI_QA", "CHIDIA", "WMHprobmap"]
- vp_tract_scalar_maps_node.inputs.output_csv = [
- os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-L_label-OT_desc-alongtract_dwimap.csv"),
- os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-R_label-OT_desc-alongtract_dwimap.csv"),
- os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-L_label-OR_desc-alongtract_dwimap.csv"),
- os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-R_label-OR_desc-alongtract_dwimap.csv"),
- os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-L_label-ML_desc-alongtract_dwimap.csv"),
- os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-R_label-ML_desc-alongtract_dwimap.csv")
- ]
- # ------------------------------------------------ #
- # Calculate DWI metrics using volumetric masks #
- # ------------------------------------------------ #
- # 1. Seed mask
- if seed_mask != '' and seed_mask is not None:
- calc_seedmask_node = Node(CalculateScalarMaps(), name="scalar_maps_for_seedmask")
- calc_seedmask_node.inputs.colnames = ["FA", "MD", "FW (MarkVCID2)", "AD", "RD", "ODI", "ICVF", "ISOVF", "GQI_GFA", "GQI_ISO", "GQI_QA", "CHIDIA", "MK", "AK", "RK"]
- calc_seedmask_node.inputs.roi_label = 1
- calc_seedmask_node.inputs.output_csv = os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_label-seedmask_desc-mean_dwimap.csv")
- dwi_workflow.connect(seed_mask_to_dwi_node, 'out_file', calc_seedmask_node, 'mask_file')
- dwi_workflow.connect(dwi_metrics_node, 'out', calc_seedmask_node, 'data_files')
- calc_seedmask_node.inputs.ignore_background = False
- # 2. WMH mask
- if wmh_mask_file != '' and wmh_mask_file is not None:
- scalar_maps_for_wmhmask_node = Node(CalculateScalarMaps(), name='scalar_maps_for_wmhmask')
- scalar_maps_for_wmhmask_node.inputs.colnames = ["FA", "MD", "FW (MarkVCID2)", "AD", "RD", "ODI", "ICVF", "ISOVF", "GQI_GFA", "GQI_ISO", "GQI_QA", "CHIDIA", "MK", "AK", "RK"]
- scalar_maps_for_wmhmask_node.inputs.roi_label = 1
- scalar_maps_for_wmhmask_node.inputs.output_csv = os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_label-WMH_desc-mean_dwimap.csv")
- dwi_workflow.connect(wmh_mask_to_dwi_node, 'out_file', scalar_maps_for_wmhmask_node, 'mask_file')
- dwi_workflow.connect(dwi_metrics_node, 'out', scalar_maps_for_wmhmask_node, 'data_files')
- scalar_maps_for_wmhmask_node.inputs.ignore_background = False
- # 3. NAWM dseg (1=left, 2=right): per-ROI and combined
- if self.dwi_t1w_register and nawm_dseg is not None:
- scalar_maps_for_nawm_perroi_node = Node(CalculateScalarMaps(), name='scalar_maps_for_nawm_perroi')
- scalar_maps_for_nawm_perroi_node.inputs.colnames = ["FA", "MD", "FW (MarkVCID2)", "AD", "RD", "ODI", "ICVF", "ISOVF", "GQI_GFA", "GQI_ISO", "GQI_QA", "CHIDIA", "MK", "AK", "RK"]
- scalar_maps_for_nawm_perroi_node.inputs.roi_labels = [1, 2]
- scalar_maps_for_nawm_perroi_node.inputs.statistic = "mean"
- scalar_maps_for_nawm_perroi_node.inputs.output_csv = os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_label-NAWM_desc-perROI_dwimap.csv")
- dwi_workflow.connect(merge_nawm_dseg_node, 'out_file', scalar_maps_for_nawm_perroi_node, 'mask_file')
- dwi_workflow.connect(dwi_metrics_node, 'out', scalar_maps_for_nawm_perroi_node, 'data_files')
- scalar_maps_for_nawm_perroi_node.inputs.ignore_background = False
- scalar_maps_for_nawm_combined_node = Node(CalculateScalarMaps(), name='scalar_maps_for_nawm_combined')
- scalar_maps_for_nawm_combined_node.inputs.colnames = ["FA", "MD", "FW (MarkVCID2)", "AD", "RD", "ODI", "ICVF", "ISOVF", "GQI_GFA", "GQI_ISO", "GQI_QA", "CHIDIA", "MK", "AK", "RK"]
- scalar_maps_for_nawm_combined_node.inputs.roi_labels = [1, 2]
- scalar_maps_for_nawm_combined_node.inputs.combine_rois = True
- scalar_maps_for_nawm_combined_node.inputs.statistic = "mean"
- scalar_maps_for_nawm_combined_node.inputs.output_csv = os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_label-NAWM_desc-mean_dwimap.csv")
- dwi_workflow.connect(merge_nawm_dseg_node, 'out_file', scalar_maps_for_nawm_combined_node, 'mask_file')
- dwi_workflow.connect(dwi_metrics_node, 'out', scalar_maps_for_nawm_combined_node, 'data_files')
- scalar_maps_for_nawm_combined_node.inputs.ignore_background = False
- # 4. NAWM dseg without tract mask (combined)
- if self.dwi_t1w_register and nawm_dseg is not None and 'mrtrix3' in self.tractography:
- scalar_maps_for_nawm_without_tract_node = Node(CalculateScalarMaps(), name='scalar_maps_for_nawm_without_tract')
- scalar_maps_for_nawm_without_tract_node.inputs.colnames = ["FA", "MD", "FW (MarkVCID2)", "AD", "RD", "ODI", "ICVF", "ISOVF", "GQI_GFA", "GQI_ISO", "GQI_QA", "CHIDIA", "MK", "AK", "RK"]
- scalar_maps_for_nawm_without_tract_node.inputs.roi_labels = [1, 2]
- scalar_maps_for_nawm_without_tract_node.inputs.combine_rois = True
- scalar_maps_for_nawm_without_tract_node.inputs.statistic = "mean"
- scalar_maps_for_nawm_without_tract_node.inputs.output_csv = os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_label-NAWMwithoutTract_desc-mean_dwimap.csv")
- dwi_workflow.connect(remove_tract_region_node, 'out_wm_mask', scalar_maps_for_nawm_without_tract_node, 'mask_file')
- dwi_workflow.connect(dwi_metrics_node, 'out', scalar_maps_for_nawm_without_tract_node, 'data_files')
- scalar_maps_for_nawm_without_tract_node.inputs.ignore_background = False
- if self.dwi_t1w_register and fs_output_process:
- # 5. wmparc from fs
- scalar_maps_for_wmparc_node = Node(CalculateScalarMaps(), name='scalar_maps_for_wmparc')
- scalar_maps_for_wmparc_node.inputs.colnames = ["FA", "MD", "FW (MarkVCID2)", "AD", "RD", "ODI", "ICVF", "ISOVF", "GQI_GFA", "GQI_ISO", "GQI_QA", "CHIDIA", "MK", "AK", "RK"]
- scalar_maps_for_wmparc_node.inputs.output_csv = os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_label-wmparc_desc-median_dwimap.csv")
- scalar_maps_for_wmparc_node.inputs.statistic = "median" # From Dr.Maria's code
- dwi_workflow.connect(final_wmparc_node, 'final_wmparc', scalar_maps_for_wmparc_node, 'mask_file')
- dwi_workflow.connect(dwi_metrics_node, 'out', scalar_maps_for_wmparc_node, 'data_files')
- scalar_maps_for_wmparc_node.inputs.ignore_background = False
- # 6. aparc+aseg from fs
- scalar_maps_for_aparc_aseg_node = Node(CalculateScalarMaps(), name='scalar_maps_for_aparc_aseg')
- scalar_maps_for_aparc_aseg_node.inputs.colnames = ["FA", "MD", "FW (MarkVCID2)", "AD", "RD", "ODI", "ICVF", "ISOVF", "GQI_GFA", "GQI_ISO", "GQI_QA", "CHIDIA", "MK", "AK", "RK"]
- scalar_maps_for_aparc_aseg_node.inputs.output_csv = os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_label-aparcaseg_desc-mean_dwimap.csv")
- scalar_maps_for_aparc_aseg_node.inputs.statistic = "mean"
- dwi_workflow.connect(fs_aparcaseg_to_dwi_node, 'out_file', scalar_maps_for_aparc_aseg_node, 'mask_file')
- dwi_workflow.connect(dwi_metrics_node, 'out', scalar_maps_for_aparc_aseg_node, 'data_files')
- scalar_maps_for_aparc_aseg_node.inputs.ignore_background = False
- # 7. chpseg
- if chpseg_process:
- scalar_maps_for_chpseg_node = Node(CalculateScalarMaps(), name='scalar_maps_for_chpseg')
- scalar_maps_for_chpseg_node.inputs.colnames = ["FA", "MD", "FW (MarkVCID2)", "AD", "RD", "ODI", "ICVF", "ISOVF", "GQI_GFA", "GQI_ISO", "GQI_QA", "CHIDIA", "MK", "AK", "RK"]
- scalar_maps_for_chpseg_node.inputs.output_csv = os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_label-chpseg_desc-mean_dwimap.csv")
- scalar_maps_for_chpseg_node.inputs.statistic = "mean"
- dwi_workflow.connect(chpseg_mask_to_dwi_node, 'out_file', scalar_maps_for_chpseg_node, 'mask_file')
- dwi_workflow.connect(dwi_metrics_node, 'out', scalar_maps_for_chpseg_node, 'data_files')
- scalar_maps_for_chpseg_node.inputs.ignore_background = False
- # 8. JHU atlas
- if self.dwi_t1w_register and mni_to_t1w_warp != "":
- jhu_atlas_to_t1w_node = Node(MRIConvertApplyWarp(), name='jhu_atlas_to_t1w')
- dwi_workflow.connect(inputnode, 'mni_to_t1w_warp', jhu_atlas_to_t1w_node, 'warp_image')
- jhu_atlas_to_t1w_node.inputs.input_image = get_package_path('data', 'standard', 'JHU', 'JHU-ICBM-labels-1mm.nii.gz')
- jhu_atlas_to_t1w_node.inputs.output_image = os.path.join(anat_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_space-T1w_seg-JHU_dseg.nii.gz")
- jhu_atlas_to_t1w_node.inputs.interp = 'nearest'
- jhu_atlas_to_dwi_node = Node(FLIRT(), name='jhu_atlas_to_dwi')
- dwi_workflow.connect(jhu_atlas_to_t1w_node, 'output_image', jhu_atlas_to_dwi_node, 'in_file')
- dwi_workflow.connect(invert_dwi_to_t1w_reg_node, 'out_file', jhu_atlas_to_dwi_node, 'in_matrix_file')
- dwi_workflow.connect(preproc_dwi_node, 'b0', jhu_atlas_to_dwi_node, 'reference')
- jhu_atlas_to_dwi_node.inputs.interp = 'nearestneighbour'
- jhu_atlas_to_dwi_node.inputs.apply_xfm = True
- jhu_atlas_to_dwi_node.inputs.out_file = os.path.join(anat_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_space-{space_entity}_seg-JHU_dseg.nii.gz")
- scalar_maps_for_jhu_node = Node(CalculateScalarMaps(), name='scalar_maps_for_jhu')
- scalar_maps_for_jhu_node.inputs.colnames = ["FA", "MD", "FW (MarkVCID2)", "AD", "RD", "ODI", "ICVF", "ISOVF", "GQI_GFA", "GQI_ISO", "GQI_QA", "CHIDIA", "MK", "AK", "RK"]
- scalar_maps_for_jhu_node.inputs.output_csv = os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_label-JHU_desc-mean_dwimap.csv")
- scalar_maps_for_jhu_node.inputs.statistic = 'mean'
- dwi_workflow.connect(jhu_atlas_to_dwi_node, 'out_file', scalar_maps_for_jhu_node, 'mask_file')
- dwi_workflow.connect(dwi_metrics_node, 'out', scalar_maps_for_jhu_node, 'data_files')
- scalar_maps_for_jhu_node.inputs.ignore_background = True
- # visual pathway analysis: TDI image
- if vp_extract:
- scalar_maps_for_vptdi_node = MapNode(CalculateTDIWeightedScalars(), name='scalar_maps_for_vptdi', iterfield=['weight_file', 'output_csv'])
- scalar_maps_for_vptdi_node.inputs.colnames = ["FA", "MD", "FW (MarkVCID2)", "AD", "RD", "ODI", "ICVF", "ISOVF", "GQI_GFA", "GQI_ISO", "GQI_QA", "CHIDIA", "WMHprobmap"]
- dwi_workflow.connect(vp_dwi_metrics_node, 'out', scalar_maps_for_vptdi_node, 'data_files')
- dwi_workflow.connect(tt_to_tdi_node, 'out_tdi', scalar_maps_for_vptdi_node, 'weight_file')
- scalar_maps_for_vptdi_node.inputs.output_csv = [
- os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-L_label-OT_desc-TDIweighted_dwimap.csv"),
- os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-R_label-OT_desc-TDIweighted_dwimap.csv"),
- os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-L_label-OR_desc-TDIweighted_dwimap.csv"),
- os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-R_label-OR_desc-TDIweighted_dwimap.csv"),
- os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-L_label-ML_desc-TDIweighted_dwimap.csv"),
- os.path.join(dwi_metrics_output_dir, f"sub-{self.subject.subject_id}_ses-{self.session.session_id}_hemi-R_label-ML_desc-TDIweighted_dwimap.csv")
- ]
- scalar_maps_for_vptdi_node.inputs.background_value = -1
- return dwi_workflow
- def extract_results(self):
- def _sanitize_metric_name(name):
- """
- Convert metric names to safe column names.
- Example:
- 'FW (MarkVCID2)' -> 'markvcid_FW'
- """
- mapping = {
- "FW (MarkVCID2)": "markvcid_FW"
- }
- if name in mapping:
- return mapping[name]
- safe_name = str(name).strip()
- safe_name = safe_name.replace(" ", "_")
- safe_name = safe_name.replace("(", "")
- safe_name = safe_name.replace(")", "")
- safe_name = safe_name.replace("-", "_")
- safe_name = safe_name.replace("/", "_")
- return safe_name
- def _read_single_row_csv_as_dict(csv_path):
- """
- Read a single-row CSV and return a dict using sanitized column names.
- Missing file or empty file -> {}
- """
- if csv_path is None or not os.path.exists(csv_path):
- return {}
- df = pd.read_csv(csv_path)
- if df.empty or df.shape[0] < 1:
- return {}
- row0 = df.iloc[0]
- out = {}
- for col in df.columns:
- safe_col = _sanitize_metric_name(col)
- out[safe_col] = row0[col]
- return out
- def _read_alongtract_metrics_wide(csv_path, n_points=100):
- """
- Read along-tract metrics CSV (expected 100 rows, one row per tract point)
- and flatten it into a single-row dict in wide format:
- FA_001 ... FA_100
- MD_001 ... MD_100
- ...
- Missing file -> empty dict
- Missing rows -> pad with np.nan
- Extra rows -> truncate to n_points
- """
- if csv_path is None or not os.path.exists(csv_path):
- return {}
- df = pd.read_csv(csv_path)
- if df.empty:
- return {}
- df = df.reset_index(drop=True)
- if df.shape[0] < n_points:
- pad_df = pd.DataFrame(np.nan, index=range(n_points - df.shape[0]), columns=df.columns)
- df = pd.concat([df, pad_df], ignore_index=True)
- elif df.shape[0] > n_points:
- df = df.iloc[:n_points, :].copy()
- out = {}
- for col in df.columns:
- prefix = _sanitize_metric_name(col)
- for i in range(n_points):
- out[f"{prefix}_{i+1:03d}"] = df.iloc[i][col]
- return out
- def _read_wmparc_metrics_wide(csv_path, wmparc_dict):
- if csv_path is None or not os.path.exists(csv_path):
- return {}
- df = pd.read_csv(csv_path)
- if df.empty or "roi_label" not in df.columns:
- return {}
- metric_cols = [c for c in df.columns if c != "roi_label"]
- out = {}
- for metric in metric_cols:
- metric_name = _sanitize_metric_name(metric)
- for _, row in df.iterrows():
- roi_label = int(row["roi_label"])
- if roi_label not in wmparc_dict:
- continue
- roi_name = wmparc_dict[roi_label]
- out[f"{metric_name}_{roi_name}"] = row[metric]
- return out
- os.makedirs(self.output_path, exist_ok=True)
- dwi_output_path = self.extract_from
- # DTI-ALPS
- alps_columns = ["Subject", "Session", "ALPS_L", "ALPS_R", "ALPS_mean", "G-ALPS_L", "G-ALPS_R", "G-ALPS_avg", "G-ALPS_sum"]
- alps_results_df = pd.DataFrame(columns=alps_columns)
- # PSMD
- psmd_columns = ["Subject", "Session", "PSMD", "PSMD_Left", "PSMD_Right"]
- psmd_results_df = pd.DataFrame(columns=psmd_columns)
- # PVeD
- pved_columns = ["Subject", "Session", "PVeD", "PVeD_L", "PVeD_R", "QA_index"]
- pved_results_df = pd.DataFrame(columns=pved_columns)
- # Track-based DWI metrics
- track_dwi_metrics_columns = ["Subject", "Session", "Track_DTI_FA", "Track_DTI_MD", "Track_SS_FW", "Track_DTI_FW"]
- track_dwi_metrics_df = pd.DataFrame(columns=track_dwi_metrics_columns)
- # Mask-based mean dwimap results, grouped by mask type
- mean_dwimap_seedmask_df = pd.DataFrame()
- mean_dwimap_WMH_df = pd.DataFrame()
- mean_dwimap_NAWM_df = pd.DataFrame()
- mean_dwimap_NAWM_perroi_df = pd.DataFrame()
- median_dwimap_wmparc_df = pd.DataFrame()
- mean_dwimap_aparcaseg_df = pd.DataFrame()
- mean_dwimap_chpseg_df = pd.DataFrame()
- mean_dwimap_jhu_df = pd.DataFrame()
- # Surface parameters
- surface_parameters_df = pd.DataFrame()
- mirror_surface_parameters_df = pd.DataFrame()
- # Along-tract DWI metrics
- alongtract_L_OR_df = pd.DataFrame()
- alongtract_L_OT_df = pd.DataFrame()
- alongtract_R_OR_df = pd.DataFrame()
- alongtract_R_OT_df = pd.DataFrame()
- # TDIweighted dwimap results
- tdiweighted_L_OR_df = pd.DataFrame()
- tdiweighted_L_OT_df = pd.DataFrame()
- tdiweighted_R_OR_df = pd.DataFrame()
- tdiweighted_R_OT_df = pd.DataFrame()
- # Parse FreeSurferColorLUT.txt for label lookups
- import re
- import xml.etree.ElementTree as ET
- fs_lut = {}
- lut_path = get_package_path('data', 'labelconvert_in', 'FreeSurferColorLUT.txt')
- with open(lut_path, 'r') as f:
- for raw_line in f:
- line = raw_line.strip()
- if not line or line.startswith('#'):
- continue
- m = re.match(r'^(\d+)\s+(.+?)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*$', line)
- if m:
- fs_lut[int(m.group(1))] = m.group(2).strip()
- # Build wmparc dict from FS LUT (WM labels in 3000-4999 range)
- wmparc_dict = {k: v for k, v in fs_lut.items() if 3000 <= k <= 4999}
- # Parse JHU-labels.xml
- jhu_lut = {}
- xml_path = get_package_path('data', 'standard', 'JHU', 'JHU-labels.xml')
- tree = ET.parse(xml_path)
- for label_elem in tree.getroot().findall('.//label'):
- jhu_lut[int(label_elem.get('index'))] = label_elem.text.strip()
- print(f"Reading results from {dwi_output_path}...")
- for subject_folder in os.listdir(dwi_output_path):
- if not subject_folder.startswith("sub-"):
- continue
- subject_id = subject_folder.split("-", 1)[1]
- subject_folder_path = os.path.join(dwi_output_path, subject_folder)
- if not os.path.isdir(subject_folder_path):
- continue
- for session_folder in os.listdir(subject_folder_path):
- if not session_folder.startswith("ses-"):
- continue
- session_id = session_folder.split("-", 1)[1]
- session_path = os.path.join(subject_folder_path, session_folder)
- if not os.path.isdir(session_path):
- continue
- dwi_metrics_stats_dir = os.path.join(session_path, "dwi_metrics_stats")
- dti_alps_csv = os.path.join(session_path, "DTI-ALPS", "alps.stat", "alps.csv")
- psmd_txt = os.path.join(session_path, "psmd", "psmd_out.txt")
- pved_csv = os.path.join(session_path, "PVeD", "PVeD_metrics.csv")
- tract_fa_csv = os.path.join(dwi_metrics_stats_dir, "track_in_dti_FA_mean.csv")
- tract_md_csv = os.path.join(dwi_metrics_stats_dir, "track_in_dti_MD_mean.csv")
- tract_ss_fw_csv = os.path.join(dwi_metrics_stats_dir, "track_in_freewater_mean.csv")
- tract_dti_fw_csv = os.path.join(dwi_metrics_stats_dir, "track_in_dti_freewater_mean.csv")
- seedmask_mean_csv = os.path.join(
- dwi_metrics_stats_dir,
- f"sub-{subject_id}_ses-{session_id}_label-seedmask_desc-mean_dwimap.csv"
- )
- wmh_mean_csv = os.path.join(
- dwi_metrics_stats_dir,
- f"sub-{subject_id}_ses-{session_id}_label-WMH_desc-mean_dwimap.csv"
- )
- nawm_mean_csv = os.path.join(
- dwi_metrics_stats_dir,
- f"sub-{subject_id}_ses-{session_id}_label-NAWM_desc-mean_dwimap.csv"
- )
- nawm_perroi_csv = os.path.join(
- dwi_metrics_stats_dir,
- f"sub-{subject_id}_ses-{session_id}_label-NAWM_desc-perROI_dwimap.csv"
- )
- wmparc_median_csv = os.path.join(
- dwi_metrics_stats_dir,
- f"sub-{subject_id}_ses-{session_id}_label-wmparc_desc-median_dwimap.csv"
- )
- aparcaseg_mean_csv = os.path.join(
- dwi_metrics_stats_dir,
- f"sub-{subject_id}_ses-{session_id}_label-aparcaseg_desc-mean_dwimap.csv"
- )
- chpseg_mean_csv = os.path.join(
- dwi_metrics_stats_dir,
- f"sub-{subject_id}_ses-{session_id}_label-chpseg_desc-mean_dwimap.csv"
- )
- jhu_mean_csv = os.path.join(
- dwi_metrics_stats_dir,
- f"sub-{subject_id}_ses-{session_id}_label-JHU_desc-mean_dwimap.csv"
- )
- surface_csv = os.path.join(session_path, "surface_parameters.csv")
- mirror_surface_csv = os.path.join(session_path, "mirror_surface_parameters.csv")
- alongtract_L_OR_csv = os.path.join(
- dwi_metrics_stats_dir,
- f"sub-{subject_id}_ses-{session_id}_hemi-L_label-OR_desc-alongtract_dwimap.csv"
- )
- alongtract_L_OT_csv = os.path.join(
- dwi_metrics_stats_dir,
- f"sub-{subject_id}_ses-{session_id}_hemi-L_label-OT_desc-alongtract_dwimap.csv"
- )
- alongtract_R_OR_csv = os.path.join(
- dwi_metrics_stats_dir,
- f"sub-{subject_id}_ses-{session_id}_hemi-R_label-OR_desc-alongtract_dwimap.csv"
- )
- alongtract_R_OT_csv = os.path.join(
- dwi_metrics_stats_dir,
- f"sub-{subject_id}_ses-{session_id}_hemi-R_label-OT_desc-alongtract_dwimap.csv"
- )
- tdiweighted_L_OR_csv = os.path.join(
- dwi_metrics_stats_dir,
- f"sub-{subject_id}_ses-{session_id}_hemi-L_label-OR_desc-TDIweighted_dwimap.csv"
- )
- tdiweighted_L_OT_csv = os.path.join(
- dwi_metrics_stats_dir,
- f"sub-{subject_id}_ses-{session_id}_hemi-L_label-OT_desc-TDIweighted_dwimap.csv"
- )
- tdiweighted_R_OR_csv = os.path.join(
- dwi_metrics_stats_dir,
- f"sub-{subject_id}_ses-{session_id}_hemi-R_label-OR_desc-TDIweighted_dwimap.csv"
- )
- tdiweighted_R_OT_csv = os.path.join(
- dwi_metrics_stats_dir,
- f"sub-{subject_id}_ses-{session_id}_hemi-R_label-OT_desc-TDIweighted_dwimap.csv"
- )
- # DTI-ALPS
- if os.path.exists(dti_alps_csv):
- alps_df = pd.read_csv(dti_alps_csv)
- if {"alps_L", "alps_R", "alps"}.issubset(alps_df.columns):
- new_data = pd.DataFrame([{
- "Subject": f"sub-{subject_id}",
- "Session": f"ses-{session_id}",
- "ALPS_L": alps_df["alps_L"].values[0],
- "ALPS_R": alps_df["alps_R"].values[0],
- "ALPS_mean": alps_df["alps"].values[0],
- "G-ALPS_L": alps_df["g_alps_L"].values[0] if "g_alps_L" in alps_df.columns else np.nan,
- "G-ALPS_R": alps_df["g_alps_R"].values[0] if "g_alps_R" in alps_df.columns else np.nan,
- "G-ALPS_avg": alps_df["g_alps_avg"].values[0] if "g_alps_avg" in alps_df.columns else np.nan,
- "G-ALPS_sum": alps_df["g_alps_sum"].values[0] if "g_alps_sum" in alps_df.columns else np.nan
- }])
- alps_results_df = pd.concat([alps_results_df, new_data], ignore_index=True)
- # PSMD
- if os.path.exists(psmd_txt):
- with open(psmd_txt, "r") as f:
- line = f.readline().strip()
- parts = line.split()
- if len(parts) == 1:
- new_data = pd.DataFrame([{
- "Subject": f"sub-{subject_id}",
- "Session": f"ses-{session_id}",
- "PSMD": float(parts[0]),
- "PSMD_Left": np.nan,
- "PSMD_Right": np.nan
- }])
- elif len(parts) == 2:
- new_data = pd.DataFrame([{
- "Subject": f"sub-{subject_id}",
- "Session": f"ses-{session_id}",
- "PSMD": np.nan,
- "PSMD_Left": float(parts[0]),
- "PSMD_Right": float(parts[1])
- }])
- else:
- new_data = pd.DataFrame([{
- "Subject": f"sub-{subject_id}",
- "Session": f"ses-{session_id}",
- "PSMD": np.nan,
- "PSMD_Left": np.nan,
- "PSMD_Right": np.nan
- }])
- psmd_results_df = pd.concat([psmd_results_df, new_data], ignore_index=True)
- # PVeD
- if os.path.exists(pved_csv):
- pved_df = pd.read_csv(pved_csv)
- if {"PVeD_total", "PVeD_L", "PVeD_R"}.issubset(pved_df.columns):
- qa_value = pved_df["QA_index"].values[0] if "QA_index" in pved_df.columns else np.nan
- new_data = pd.DataFrame([{
- "Subject": f"sub-{subject_id}",
- "Session": f"ses-{session_id}",
- "PVeD": pved_df["PVeD_total"].values[0],
- "PVeD_L": pved_df["PVeD_L"].values[0],
- "PVeD_R": pved_df["PVeD_R"].values[0],
- "QA_index": qa_value
- }])
- pved_results_df = pd.concat([pved_results_df, new_data], ignore_index=True)
- # Track-based DWI metrics
- track_csvs = [tract_fa_csv, tract_md_csv, tract_ss_fw_csv, tract_dti_fw_csv]
- if any(os.path.exists(csv) for csv in track_csvs):
- new_data = pd.DataFrame([{
- "Subject": f"sub-{subject_id}",
- "Session": f"ses-{session_id}",
- "Track_DTI_FA": pd.read_csv(tract_fa_csv)["mean"].values[0] if os.path.exists(tract_fa_csv) else None,
- "Track_DTI_MD": pd.read_csv(tract_md_csv)["mean"].values[0] if os.path.exists(tract_md_csv) else None,
- "Track_SS_FW": pd.read_csv(tract_ss_fw_csv)["mean"].values[0] if os.path.exists(tract_ss_fw_csv) else None,
- "Track_DTI_FW": pd.read_csv(tract_dti_fw_csv)["mean"].values[0] if os.path.exists(tract_dti_fw_csv) else None
- }])
- track_dwi_metrics_df = pd.concat([track_dwi_metrics_df, new_data], ignore_index=True)
- # Mean dwimap results grouped by mask type
- mean_specs = [
- ("seedmask", seedmask_mean_csv, mean_dwimap_seedmask_df),
- ("WMH", wmh_mean_csv, mean_dwimap_WMH_df),
- ("NAWM", nawm_mean_csv, mean_dwimap_NAWM_df),
- ]
- wmparc_metric_dict = _read_wmparc_metrics_wide(wmparc_median_csv, wmparc_dict)
- if wmparc_metric_dict:
- row = {
- "Subject": f"sub-{subject_id}",
- "Session": f"ses-{session_id}",
- }
- row.update(wmparc_metric_dict)
- median_dwimap_wmparc_df = pd.concat(
- [median_dwimap_wmparc_df, pd.DataFrame([row])],
- ignore_index=True,
- )
- # aparc+aseg metrics
- aparcaseg_metric_dict = _read_wmparc_metrics_wide(aparcaseg_mean_csv, fs_lut)
- if aparcaseg_metric_dict:
- row = {"Subject": f"sub-{subject_id}", "Session": f"ses-{session_id}"}
- row.update(aparcaseg_metric_dict)
- mean_dwimap_aparcaseg_df = pd.concat([mean_dwimap_aparcaseg_df, pd.DataFrame([row])], ignore_index=True)
- # chpseg metrics (1=left, 2=right)
- chpseg_dict = {1: "chp_L", 2: "chp_R"}
- chpseg_metric_dict = _read_wmparc_metrics_wide(chpseg_mean_csv, chpseg_dict)
- if chpseg_metric_dict:
- row = {"Subject": f"sub-{subject_id}", "Session": f"ses-{session_id}"}
- row.update(chpseg_metric_dict)
- mean_dwimap_chpseg_df = pd.concat([mean_dwimap_chpseg_df, pd.DataFrame([row])], ignore_index=True)
- # JHU atlas metrics
- jhu_metric_dict = _read_wmparc_metrics_wide(jhu_mean_csv, jhu_lut)
- if jhu_metric_dict:
- row = {"Subject": f"sub-{subject_id}", "Session": f"ses-{session_id}"}
- row.update(jhu_metric_dict)
- mean_dwimap_jhu_df = pd.concat([mean_dwimap_jhu_df, pd.DataFrame([row])], ignore_index=True)
- updated_mean_dfs = {}
- for mask_label, csv_path, target_df in mean_specs:
- metric_dict = _read_single_row_csv_as_dict(csv_path)
- if metric_dict:
- row = {
- "DWI_Pipeline_ID": f"sub-{subject_id}_ses-{session_id}",
- "Subject": f"sub-{subject_id}",
- "Session": f"ses-{session_id}",
- "Mask": mask_label,
- }
- row.update(metric_dict)
- target_df = pd.concat([target_df, pd.DataFrame([row])], ignore_index=True)
- updated_mean_dfs[mask_label] = target_df
- mean_dwimap_seedmask_df = updated_mean_dfs["seedmask"]
- mean_dwimap_WMH_df = updated_mean_dfs["WMH"]
- mean_dwimap_NAWM_df = updated_mean_dfs["NAWM"]
- # NAWM per-ROI metrics (2 rows: roi_label=1 left, roi_label=2 right)
- nawm_perroi_dict = {1: "NAWM_Left", 2: "NAWM_Right"}
- nawm_perroi_metric_dict = _read_wmparc_metrics_wide(nawm_perroi_csv, nawm_perroi_dict)
- if nawm_perroi_metric_dict:
- row = {"Subject": f"sub-{subject_id}", "Session": f"ses-{session_id}"}
- row.update(nawm_perroi_metric_dict)
- mean_dwimap_NAWM_perroi_df = pd.concat([mean_dwimap_NAWM_perroi_df, pd.DataFrame([row])], ignore_index=True)
- # Surface parameters
- if os.path.exists(surface_csv):
- df = pd.read_csv(surface_csv)
- df.insert(0, "DWI_Pipeline_ID", f"sub-{subject_id}_ses-{session_id}")
- df.insert(1, "Subject", f"sub-{subject_id}")
- df.insert(2, "Session", f"ses-{session_id}")
- surface_parameters_df = pd.concat([surface_parameters_df, df], ignore_index=True)
- if os.path.exists(mirror_surface_csv):
- df_mirror = pd.read_csv(mirror_surface_csv)
- df_mirror.insert(0, "DWI_Pipeline_ID", f"sub-{subject_id}_ses-{session_id}")
- df_mirror.insert(1, "Subject", f"sub-{subject_id}")
- df_mirror.insert(2, "Session", f"ses-{session_id}")
- mirror_surface_parameters_df = pd.concat([mirror_surface_parameters_df, df_mirror], ignore_index=True)
- # Visual Pathway: Along-tract DWI metrics
- alongtract_specs = [
- ("L", "OR", alongtract_L_OR_csv, alongtract_L_OR_df),
- ("L", "OT", alongtract_L_OT_csv, alongtract_L_OT_df),
- ("R", "OR", alongtract_R_OR_csv, alongtract_R_OR_df),
- ("R", "OT", alongtract_R_OT_csv, alongtract_R_OT_df),
- ]
- updated_alongtract_dfs = {}
- for hemi, tract_label, csv_path, target_df in alongtract_specs:
- wide_metrics = _read_alongtract_metrics_wide(csv_path, n_points=100)
- if wide_metrics:
- row = {
- "DWI_Pipeline_ID": f"sub-{subject_id}_ses-{session_id}",
- "Subject": f"sub-{subject_id}",
- "Session": f"ses-{session_id}",
- "Hemisphere": hemi,
- "Tract": tract_label,
- }
- row.update(wide_metrics)
- target_df = pd.concat([target_df, pd.DataFrame([row])], ignore_index=True)
- updated_alongtract_dfs[(hemi, tract_label)] = target_df
- alongtract_L_OR_df = updated_alongtract_dfs[("L", "OR")]
- alongtract_L_OT_df = updated_alongtract_dfs[("L", "OT")]
- alongtract_R_OR_df = updated_alongtract_dfs[("R", "OR")]
- alongtract_R_OT_df = updated_alongtract_dfs[("R", "OT")]
- # Visual Pathway: TDIweighted dwimap results
- tdiweighted_specs = [
- ("L", "OR", tdiweighted_L_OR_csv, tdiweighted_L_OR_df),
- ("L", "OT", tdiweighted_L_OT_csv, tdiweighted_L_OT_df),
- ("R", "OR", tdiweighted_R_OR_csv, tdiweighted_R_OR_df),
- ("R", "OT", tdiweighted_R_OT_csv, tdiweighted_R_OT_df),
- ]
- updated_tdiweighted_dfs = {}
- for hemi, tract_label, csv_path, target_df in tdiweighted_specs:
- metric_dict = _read_single_row_csv_as_dict(csv_path)
- if metric_dict:
- row = {
- "DWI_Pipeline_ID": f"sub-{subject_id}_ses-{session_id}",
- "Subject": f"sub-{subject_id}",
- "Session": f"ses-{session_id}",
- "Hemisphere": hemi,
- "Tract": tract_label,
- }
- row.update(metric_dict)
- target_df = pd.concat([target_df, pd.DataFrame([row])], ignore_index=True)
- updated_tdiweighted_dfs[(hemi, tract_label)] = target_df
- tdiweighted_L_OR_df = updated_tdiweighted_dfs[("L", "OR")]
- tdiweighted_L_OT_df = updated_tdiweighted_dfs[("L", "OT")]
- tdiweighted_R_OR_df = updated_tdiweighted_dfs[("R", "OR")]
- tdiweighted_R_OT_df = updated_tdiweighted_dfs[("R", "OT")]
- # Save results
- alps_output_excel = os.path.join(self.output_path, "dwi_alps_summary.xlsx")
- psmd_output_excel = os.path.join(self.output_path, "dwi_psmd_summary.xlsx")
- pved_output_excel = os.path.join(self.output_path, "dwi_pved_summary.xlsx")
- track_dwi_metrics_output_excel = os.path.join(self.output_path, "dwi_track_metrics_summary.xlsx")
- mean_dwimap_seedmask_output_xlsx = os.path.join(self.output_path, "dwi_seedmask_summary.xlsx")
- mean_dwimap_WMH_output_xlsx = os.path.join(self.output_path, "dwi_WMH_summary.xlsx")
- mean_dwimap_NAWM_output_xlsx = os.path.join(self.output_path, "dwi_NAWM_summary.xlsx")
- mean_dwimap_NAWM_perroi_output_xlsx = os.path.join(self.output_path, "dwi_NAWM_perROI_summary.xlsx")
- median_dwimap_wmparc_output_xlsx = os.path.join(self.output_path, "dwi_wmparc_summary.xlsx")
- surface_output_excel = os.path.join(self.output_path, "dwi_surface_parameters_summary.xlsx")
- mirror_output_excel = os.path.join(self.output_path, "dwi_mirror_surface_parameters_summary.xlsx")
- alongtract_L_OR_output_xlsx = os.path.join(self.output_path, "dwi_alongtract_hemi-L_label-OR_summary.xlsx")
- alongtract_L_OT_output_xlsx = os.path.join(self.output_path, "dwi_alongtract_hemi-L_label-OT_summary.xlsx")
- alongtract_R_OR_output_xlsx = os.path.join(self.output_path, "dwi_alongtract_hemi-R_label-OR_summary.xlsx")
- alongtract_R_OT_output_xlsx = os.path.join(self.output_path, "dwi_alongtract_hemi-R_label-OT_summary.xlsx")
- tdiweighted_L_OR_output_xlsx = os.path.join(self.output_path, "dwi_TDIweighted_hemi-L_label-OR_summary.xlsx")
- tdiweighted_L_OT_output_xlsx = os.path.join(self.output_path, "dwi_TDIweighted_hemi-L_label-OT_summary.xlsx")
- tdiweighted_R_OR_output_xlsx = os.path.join(self.output_path, "dwi_TDIweighted_hemi-R_label-OR_summary.xlsx")
- tdiweighted_R_OT_output_xlsx = os.path.join(self.output_path, "dwi_TDIweighted_hemi-R_label-OT_summary.xlsx")
- if not alps_results_df.empty:
- alps_results_df.to_excel(alps_output_excel, header=True, index=False)
- print(f"DTI-ALPS results saved to {alps_output_excel}")
- else:
- print("No DTI-ALPS results found.")
- if not psmd_results_df.empty:
- psmd_results_df.to_excel(psmd_output_excel, header=True, index=False)
- print(f"PSMD results saved to {psmd_output_excel}")
- else:
- print("No PSMD results found.")
- if not pved_results_df.empty:
- pved_results_df.to_excel(pved_output_excel, header=True, index=False)
- print(f"PVeD results saved to {pved_output_excel}")
- else:
- print("No PVeD results found.")
- if not track_dwi_metrics_df.empty:
- track_dwi_metrics_df.to_excel(track_dwi_metrics_output_excel, header=True, index=False)
- print(f"Track-based DWI metrics results saved to {track_dwi_metrics_output_excel}")
- else:
- print("No track-based DWI metrics results found.")
- if not mean_dwimap_seedmask_df.empty:
- mean_dwimap_seedmask_df.to_excel(mean_dwimap_seedmask_output_xlsx, index=False)
- print(f"Mean dwimap seedmask results saved to {mean_dwimap_seedmask_output_xlsx}")
- else:
- print("No mean dwimap seedmask results found.")
- if not mean_dwimap_WMH_df.empty:
- mean_dwimap_WMH_df.to_excel(mean_dwimap_WMH_output_xlsx, index=False)
- print(f"Mean dwimap WMH results saved to {mean_dwimap_WMH_output_xlsx}")
- else:
- print("No mean dwimap WMH results found.")
- if not mean_dwimap_NAWM_df.empty:
- mean_dwimap_NAWM_df.to_excel(mean_dwimap_NAWM_output_xlsx, index=False)
- print(f"Mean dwimap NAWM results saved to {mean_dwimap_NAWM_output_xlsx}")
- else:
- print("No mean dwimap NAWM results found.")
- if not mean_dwimap_NAWM_perroi_df.empty:
- mean_dwimap_NAWM_perroi_df.to_excel(mean_dwimap_NAWM_perroi_output_xlsx, index=False)
- print(f"Mean dwimap NAWM per-ROI results saved to {mean_dwimap_NAWM_perroi_output_xlsx}")
- else:
- print("No mean dwimap NAWM per-ROI results found.")
- if not median_dwimap_wmparc_df.empty:
- median_dwimap_wmparc_df.to_excel(median_dwimap_wmparc_output_xlsx, index=False)
- print(f"Median dwimap wmparc results saved to {median_dwimap_wmparc_output_xlsx}")
- else:
- print("No median dwimap wmparc results found.")
- if not mean_dwimap_aparcaseg_df.empty:
- mean_dwimap_aparcaseg_df.to_excel(os.path.join(self.output_path, "dwi_aparcaseg_summary.xlsx"), index=False)
- print(f"Mean dwimap aparcaseg results saved to {os.path.join(self.output_path, 'dwi_aparcaseg_summary.xlsx')}")
- else:
- print("No mean dwimap aparcaseg resul
dwi_pipeline.py at commit 93e022c, under MIT · at the source
Overview
Abstract
Background: Secondary cortical degeneration caused by the remote effects of subcortical infarcts has been implicated in long-term outcomes after acute ischemic stroke. However, this process remains insufficiently studied in recent small subcortical infarcts (RSSI). We aimed to verify RSSI-induced cortical damage, determine whether it can be captured by neuroimaging markers, and explore its association with clinical outcomes.
Methods: RSSI patients with longitudinal Magnetic Resonance Imaging (MRI) were included. Cortical degeneration was assessed using linear mixed-effects models, incorporating a direct approach based on individual diffusion weighted imaging and an indirect approach using the normative connectome from the Human Connectome Project (HCP). Principal component analysis (PCA) was employed to extract features of cortical alterations. The resulting component scores were used in general linear models to assess associations with neuroimaging markers and clinical outcomes.
Results: A total of 76 RSSI patients were analyzed. RSSI was found to induce progressive cortical thinning and volume loss in structurally connected regions. PCA identified a component reflecting parenchymal atrophy associated with diffusion-based markers of white matter integrity, as well as the presence of track/
Conclusions: RSSI induces secondary cortical damage through structurally connected fiber tracts, which is detectable by neuroimaging markers of white matter integrity. These regional cortical alterations may be relevant to post-stroke outcomes and require validation in larger longitudinal studies.
Reproduced under the paper's license (CC BY), from the paper cited above.
Repositories
Its files are read in the Code ↔ Paper reader above, with 7 matches between paragraphs and lines of code.
kjamison/nemo
d661f3fddba70dff1eb93f0c57b4f726f09723f9, 23 July 2026Availability: 1 check, the latest on 30 September 2026: the link answers
- 30 September 2026: the link answers
27 files
- chacoconn_allref_to_chac
ovol.py , Python, 143 lines - chacoconn_to_nemosc.py, Python, 234 lines
- chacovol_to_nifti.py, Python, 88 lines
- check_input_dimensions.p
y , Python, 41 lines - databaseprep/
nemo_convert_mni_tck_to_ , Python, 208 linessparsemat.py - databaseprep/
nemo_merge_sparsemat_sum , Python, 131 liness_and_weights.py - databaseprep/
nemo_merge_subject_parc_ , Python, 137 linesto_sparsemat.py - databaseprep/
nemo_run_warp_and_sparse , Shell, 31 linesmat_on_aws.sh - databaseprep/
nemo_save_chunklist.py , Python, 93 lines - databaseprep/
nemo_save_chunks.py , Python, 136 lines - databaseprep/
nemo_save_endpoints.py , Python, 163 lines - databaseprep/
nemo_save_parcellated_fi , Python, 50 linesgures.py - databaseprep/
nemo_save_subjchunks.py , Python, 176 lines - databaseprep/
run_full_database_prep.s , Shell, 98 linesh - databaseprep/
run_warp_tck_to_mni.sh , Shell, 137 lines - dilate_parcellation.py, Python, 73 lines
- nemo_lesion_to_chaco.py, Python, 1,342 lines
- nemo_parcellate_results.
py , Python, 168 lines - nemo_save_average_glassb
rain.py , Python, 161 lines - nemo_save_average_graphb
rain.py , Python, 197 lines - nemo_save_average_matrix
_figure.py , Python, 126 lines - website/
config/ , Shell, 1,236 linesnemo_startup.sh - website/
config/ , Python, 454 liness3-lambda.py - website/
update_website.sh , Shell, 85 lines - website/
uploader.js , JavaScript, 1,239 lines - LICENSE, License, 21 lines
- README.md, Text, 303 lines
LuuuXG/cvdproc
93e022c07bcf075aaf1d727dd6ca2261b1a69c57, 11 September 2026Availability: 1 check, the latest on 30 September 2026: the link answers
- 30 September 2026: the link answers
482 files
- cvdproc/
__init__.py , Python, 1 line - cvdproc/
bids_data/ , Python, 1 line__init__.py - cvdproc/
bids_data/ , Python, 54 linesbids_utils.py - cvdproc/
bids_data/ , Python, 195 linesrename_bids_file.py - cvdproc/
bids_data/ , Python, 157 linessession.py - cvdproc/
bids_data/ , Python, 34 linessubject.py - cvdproc/
config/ , Python, 1 line__init__.py - cvdproc/
config/ , Python, 40 lineslogger_config.py - cvdproc/
config/ , Python, 65 linespaths.py - cvdproc/
controllers/ , Python, 1 line__init__.py - cvdproc/
controllers/ , Python, 138 linespipeline_manager.py - cvdproc/
main.py , Python, 310 lines - cvdproc/
pipelines/ , Python, 1 line__init__.py - cvdproc/
pipelines/ , Python, 1 linebash/ __init__.py - cvdproc/
pipelines/ , Shell, 282 linesbash/ alps/ alps.sh - cvdproc/
pipelines/ , Shell, 22 linesbash/ ants/ simple_n4biascorr.sh - cvdproc/
pipelines/ , Shell, 234 linesbash/ arts/ run_ARTS_fast_synthmorph .sh - cvdproc/
pipelines/ , Shell, 132 linesbash/ arts/ run_ARTS_quick.sh - cvdproc/
pipelines/ , Shell, 141 linesbash/ brainage/ brainageR_custom.sh - cvdproc/
pipelines/ , Shell, 84 linesbash/ chpseg/ chpseg.sh - cvdproc/
pipelines/ , Shell, 22 linesbash/ exploreasl/ asl_register.sh - cvdproc/
pipelines/ , Shell, 304 linesbash/ fdt/ bedpostx_gpu_custom.sh - cvdproc/
pipelines/ , Shell, 76 linesbash/ fdt/ extract_b0.sh - cvdproc/
pipelines/ , Shell, 109 linesbash/ fdt/ fdt_order_eddyout.sh - cvdproc/
pipelines/ , Shell, 34 linesbash/ fdt/ pad_dwi.sh - cvdproc/
pipelines/ , Shell, 79 linesbash/ fdt_fs_processing.sh - cvdproc/
pipelines/ , Shell, 210 linesbash/ fmriprep/ fmriprep_single.sh - cvdproc/
pipelines/ , Shell, 100 linesbash/ freesurfer/ batch_script/ freesurfer_reconall_long itudinal_single.sh - cvdproc/
pipelines/ , Shell, 107 linesbash/ freesurfer/ batch_script/ freesurfer_reconall_sing le.sh - cvdproc/
pipelines/ , Shell, 20 linesbash/ freesurfer/ batch_script/ run_freesurfer_batch.sh - cvdproc/
pipelines/ , Shell, 68 linesbash/ freesurfer/ batch_script/ run_freesurfer_longitudi nal_batch.sh - cvdproc/
pipelines/ , Shell, 220 linesbash/ freesurfer/ freesurfer_reconall_long itudinal_single.sh - cvdproc/
pipelines/ , Shell, 25 linesbash/ freesurfer/ merge_ribbon.sh - cvdproc/
pipelines/ , Shell, 129 linesbash/ freesurfer/ mri_synthmorph_single.sh - cvdproc/
pipelines/ , Shell, 3 linesbash/ freesurfer/ mri_synthseg.sh - cvdproc/
pipelines/ , Shell, 32 linesbash/ freesurfer/ mri_synthseg_post.sh - cvdproc/
pipelines/ , Shell, 82 linesbash/ freesurfer/ mri_synthstrip_flexible. sh - cvdproc/
pipelines/ , Shell, 32 linesbash/ freesurfer/ tkregister2fs2t1w.sh - cvdproc/
pipelines/ , Shell, 36 linesbash/ fsl/ epi_reg_xfm.sh - cvdproc/
pipelines/ , Shell, 53 linesbash/ fsl/ fsl_anat_custom.sh - cvdproc/
pipelines/ , Shell, 38 linesbash/ fsl/ make_bianca_mask.sh - cvdproc/
pipelines/ , Shell, 126 linesbash/ fsl/ truenet_preprocess_custo m.sh - cvdproc/
pipelines/ , Shell, 71 linesbash/ fsqc/ fsqc_single.sh - cvdproc/
pipelines/ , Shell, 97 linesbash/ generate_wm_from_fs.sh - cvdproc/
pipelines/ , Shell, 69 linesbash/ hipsta/ hipsta_docker.sh - cvdproc/
pipelines/ , Shell, 362 linesbash/ lesion_fill/ lesion_fill_symmMNI_synt hmorph.sh - cvdproc/
pipelines/ , Shell, 293 linesbash/ lesion_fill/ lesion_fill_synthmorph.s h - cvdproc/
pipelines/ , Shell, 128 linesbash/ mri_WMHsynthseg_single.s h - cvdproc/
pipelines/ , Shell, 244 linesbash/ mrtrix3/ mrtrix_connectome_prepro cess.sh - cvdproc/
pipelines/ , Shell, 23 linesbash/ mrtrix3/ mrtrix_degibbs.sh - cvdproc/
pipelines/ , Shell, 25 linesbash/ mrtrix3/ mrtrix_denoise.sh - cvdproc/
pipelines/ , Shell, 25 linesbash/ mrtrix3/ mrtrix_denoise_degibbs.s h - cvdproc/
pipelines/ , Shell, 121 linesbash/ mrtrix3/ remove_conn_region.sh - cvdproc/
pipelines/ , Shell, 51 linesbash/ qsiprep_qsirecon/ DRBUDDI_cuda.sh - cvdproc/
pipelines/ , Shell, 132 linesbash/ qsiprep_qsirecon/ qsiprep_orig_to_acpc.sh - cvdproc/
pipelines/ , Shell, 77 linesbash/ qsiprep_qsirecon/ qsiprep_single.sh - cvdproc/
pipelines/ , Shell, 88 linesbash/ qsiprep_qsirecon/ qsirecon_batch.sh - cvdproc/
pipelines/ , Shell, 73 linesbash/ qsiprep_qsirecon/ qsirecon_single.sh - cvdproc/
pipelines/ , Shell, 162 linesbash/ qsm/ qsm_register2.sh - cvdproc/
pipelines/ , Shell, 77 linesbash/ qsm_register.sh - cvdproc/
pipelines/ , Shell, 77 linesbash/ register.sh - cvdproc/
pipelines/ , Shell, 54 linesbash/ segcsvd/ segcsvd_pvs.sh - cvdproc/
pipelines/ , Shell, 143 lines, 1 matchbash/ synb0/ run_synb0.sh - cvdproc/
pipelines/ , Shell, 86 linesbash/ tbss_and_gbss/ gbss_preprocess.sh - cvdproc/
pipelines/ , Shell, 473 linesbash/ tbss_and_gbss/ tbss_and_gbss_order.sh - cvdproc/
pipelines/ , Shell, 503 linesbash/ tbss_and_gbss/ tbss_gbss_full.sh - cvdproc/
pipelines/ , Shell, 263 linesbash/ visual_pathway_project/ prepare_roi.sh - cvdproc/
pipelines/ , Python, 1 linecommon/ __init__.py - cvdproc/
pipelines/ , Python, 72 linescommon/ calculate_roi_volume.py - cvdproc/
pipelines/ , Python, 63 linescommon/ calculate_volume.py - cvdproc/
pipelines/ , Python, 61 linescommon/ copy_to_rawdata.py - cvdproc/
pipelines/ , Python, 128 linescommon/ dwi_resample.py - cvdproc/
pipelines/ , Python, 78 linescommon/ extract_region.py - cvdproc/
pipelines/ , Python, 96 linescommon/ files.py - cvdproc/
pipelines/ , Python, 49 linescommon/ flip_b_table.py - cvdproc/
pipelines/ , Python, 412 linescommon/ image_calc.py - cvdproc/
pipelines/ , Python, 80 linescommon/ merge_gifti.py - cvdproc/
pipelines/ , Python, 131 linescommon/ mpr.py - cvdproc/
pipelines/ , Python, 48 linescommon/ pad_dwi.py - cvdproc/
pipelines/ , Python, 319 linescommon/ register.py - cvdproc/
pipelines/ , Python, 88 linescommon/ unzip.py - cvdproc/
pipelines/ , Python, 1 linedcm2bids/ __init__.py - cvdproc/
pipelines/ , Python, 952 linesdcm2bids/ dcm2bids_processor.py - cvdproc/
pipelines/ , Python, 1 linedmri/ __init__.py - cvdproc/
pipelines/ , Python, 1 linedmri/ alps/ __init__.py - cvdproc/
pipelines/ , Python, 230 linesdmri/ alps/ alps_nipype.py - cvdproc/
pipelines/ , Python, 1 linedmri/ amico/ __init__.py - cvdproc/
pipelines/ , Python, 195 linesdmri/ amico/ amico_nipype.py - cvdproc/
pipelines/ , Python, 39 linesdmri/ dipy/ dipy_degibbs.py - cvdproc/
pipelines/ , Python, 224 linesdmri/ dipy/ dipy_dki.py - cvdproc/
pipelines/ , Python, 71 linesdmri/ dipy/ dipy_freewater_dti.py - cvdproc/
pipelines/ , Python, 74 linesdmri/ dipy/ dipy_mapmri.py - cvdproc/
pipelines/ , Python, 726 linesdmri/ disconnection/ disconnection_nipype.py - cvdproc/
pipelines/ , Python, 618 linesdmri/ disconnection/ individual_disconnection _nipype.py - cvdproc/
pipelines/ , Python, 420 linesdmri/ disconnection_pipeline.p y - cvdproc/
pipelines/ , Python, 1 linedmri/ dsistudio/ __init__.py - cvdproc/
pipelines/ , Python, 51 linesdmri/ dsistudio/ create_src_nipype.py - cvdproc/
pipelines/ , Python, 166 linesdmri/ dsistudio/ fixing_nipype.py - cvdproc/
pipelines/ , Python, 242 linesdmri/ dsistudio/ reconstruction_nipype.py - cvdproc/
pipelines/ , Python, 71 linesdmri/ dsistudio/ tracking_nipype.py - cvdproc/
pipelines/ , Python, 161 linesdmri/ dsistudio/ tract_analysis.py - cvdproc/
pipelines/ , Python, 509 linesdmri/ dsistudio/ tt_utils.py - cvdproc/
pipelines/ , Python, 2,919 lines, 2 matchesdmri/ dwi_pipeline.py - cvdproc/
pipelines/ , Python, 1,160 lines, 1 matchdmri/ fdt/ fdt_nipype.py - cvdproc/
pipelines/ , Python, 1 linedmri/ freewater/ __init__.py - cvdproc/
pipelines/ , Python, 34 linesdmri/ freewater/ markvcid_freewater.py - cvdproc/
pipelines/ , Python, 124 linesdmri/ freewater/ single_shell_freewater.p y - cvdproc/
pipelines/ , Python, 1 linedmri/ lqt/ __init__.py - cvdproc/
pipelines/ , Python, 114 linesdmri/ lqt/ lqt_alps_disconnection.p y - cvdproc/
pipelines/ , Python, 330 linesdmri/ lqt/ lqt_nipype.py - cvdproc/
pipelines/ , Python, 238 linesdmri/ lqt_pipeline.py - cvdproc/
pipelines/ , Python, 1 linedmri/ mrtrix3/ __init__.py - cvdproc/
pipelines/ , Python, 160 linesdmri/ mrtrix3/ connectome_nipype.py - cvdproc/
pipelines/ , Python, 81 linesdmri/ mrtrix3/ denoise_degibbs_nipype.p y - cvdproc/
pipelines/ , Python, 147 linesdmri/ mrtrix3/ tcksample_nipype.py - cvdproc/
pipelines/ , Python, 33 linesdmri/ mrtrix3/ utils.py - cvdproc/
pipelines/ , Python, 1 linedmri/ nemo/ __init__.py - cvdproc/
pipelines/ , Python, 515 linesdmri/ nemo/ nemo_postprocess_nipype. py - cvdproc/
pipelines/ , Python, 308 linesdmri/ nemo_postprocess_pipelin e.py - cvdproc/
pipelines/ , Python, 1 linedmri/ psmd/ __init__.py - cvdproc/
pipelines/ , Python, 96 linesdmri/ psmd/ psmd_nipype.py - cvdproc/
pipelines/ , Python, 1 linedmri/ pved/ __init__.py - cvdproc/
pipelines/ , Python, 224 linesdmri/ pved/ pved_atropos.py - cvdproc/
pipelines/ , Python, 226 linesdmri/ pved/ pved_nipype.py - cvdproc/
pipelines/ , Python, 45 linesdmri/ qsiprep/ register.py - cvdproc/
pipelines/ , Python, 1 linedmri/ stats/ __init__.py - cvdproc/
pipelines/ , Python, 396 linesdmri/ stats/ dti_scalar_maps.py - cvdproc/
pipelines/ , Python, 212 linesdmri/ stats/ extract_skeleton_roi_val ues.py - cvdproc/
pipelines/ , Python, 1 linedmri/ synb0/ __init__.py - cvdproc/
pipelines/ , Python, 53 linesdmri/ synb0/ synb0_nipype.py - cvdproc/
pipelines/ , Python, 275 linesdmri/ visualization/ plot_tck_cli.py - cvdproc/
pipelines/ , Python, 96 linesdmri/ visualization/ vis.py - cvdproc/
pipelines/ , Python, 1 linedmri/ vp_project/ __init__.py - cvdproc/
pipelines/ , Python, 71 linesdmri/ vp_project/ prepare_roi_nipype.py - cvdproc/
pipelines/ , Python, 990 linesdmri/ vp_project/ refine_vp_nipype.py - cvdproc/
pipelines/ , Python, 350 linesdmri/ vp_project/ tt_process_nipype.py - cvdproc/
pipelines/ , Python, 319 linesexternal/ AIF_selection_auto/ AIF_selection_automatic. py - cvdproc/
pipelines/ , Python, 1 lineexternal/ AIF_selection_auto/ __init__.py - cvdproc/
pipelines/ , Jupyter, 36 linesexternal/ AIF_selection_auto/ demo/ demo.ipynb - cvdproc/
pipelines/ , Python, 344 linesexternal/ AIF_selection_auto/ demo/ plots.py - cvdproc/
pipelines/ , Python, 344 linesexternal/ AIF_selection_auto/ plots.py - cvdproc/
pipelines/ , Python, 1 lineexternal/ DiffusionTensorImaging/ __init__.py - cvdproc/
pipelines/ , Python, 71 linesexternal/ DiffusionTensorImaging/ example.py - cvdproc/
pipelines/ , Jupyter, 102 linesexternal/ DiffusionTensorImaging/ notebooks/ Example.ipynb - cvdproc/
pipelines/ , Python, 1 lineexternal/ DiffusionTensorImaging/ pymods/ __init__.py - cvdproc/
pipelines/ , Python, 336 linesexternal/ DiffusionTensorImaging/ pymods/ freewater.py - cvdproc/
pipelines/ , Python, 218 linesexternal/ DiffusionTensorImaging/ pymods/ freewater_runner.py - cvdproc/
pipelines/ , Python, 29 linesexternal/ DiffusionTensorImaging/ pymods/ plotutils.py - cvdproc/
pipelines/ , Python, 150 linesexternal/ DiffusionTensorImaging/ pymods/ tracer.py - cvdproc/
pipelines/ , Python, 1 lineexternal/ LCB_BNI_USA/ __init__.py - cvdproc/
pipelines/ , Python, 162 linesexternal/ LCB_BNI_USA/ dce.py - cvdproc/
pipelines/ , Python, 96 linesexternal/ LCB_BNI_USA/ dsc.py - cvdproc/
pipelines/ , Jupyter, 343 linesexternal/ LCB_BNI_USA/ notebooks/ DSCpipeline-use-module.i pynb - cvdproc/
pipelines/ , Jupyter, 368 linesexternal/ LCB_BNI_USA/ notebooks/ DSCpipeline.ipynb - cvdproc/
pipelines/ , Shell, 12 linesexternal/ LIT/ containerization/ build_docker.sh - cvdproc/
pipelines/ , Shell, 30 linesexternal/ LIT/ containerization/ build_singularity.sh - cvdproc/
pipelines/ , Python, 831 linesexternal/ LIT/ lit/ data/ conform.py - cvdproc/
pipelines/ , Python, 172 linesexternal/ LIT/ lit/ data/ datasets.py - cvdproc/
pipelines/ , Python, 75 linesexternal/ LIT/ lit/ data/ transforms.py - cvdproc/
pipelines/ , Python, 872 linesexternal/ LIT/ lit/ inference.py - cvdproc/
pipelines/ , Python, 430 linesexternal/ LIT/ lit/ inpaint_image.py - cvdproc/
pipelines/ , Python, 2,182 linesexternal/ LIT/ lit/ networks/ DiffusionUnet.py - cvdproc/
pipelines/ , Python, 622 linesexternal/ LIT/ lit/ networks/ interpolation_layer.py - cvdproc/
pipelines/ , Python, 64 linesexternal/ LIT/ lit/ postprocessing/ lesion_to_segmentation.p y - cvdproc/
pipelines/ , Python, 547 linesexternal/ LIT/ lit/ postprocessing/ lesion_to_surface.py - cvdproc/
pipelines/ , Python, 408 linesexternal/ LIT/ lit/ postprocessing/ smooth_aparc.py - cvdproc/
pipelines/ , Python, 283 linesexternal/ LIT/ lit/ train_ddpm.py - cvdproc/
pipelines/ , Python, 113 linesexternal/ LIT/ lit/ utils/ download_checkpoints.py - cvdproc/
pipelines/ , Python, 149 linesexternal/ LIT/ lit/ utils/ plotting.py - cvdproc/
pipelines/ , Python, 18 linesexternal/ LIT/ lit/ utils/ utils.py - cvdproc/
pipelines/ , Shell, 479 linesexternal/ LIT/ run_lit.sh - cvdproc/
pipelines/ , Shell, 207 linesexternal/ LIT/ run_lit_containerized.sh - cvdproc/
pipelines/ , Shell, 271 linesexternal/ LIT/ run_lit_containerized_cu stom.sh - cvdproc/
pipelines/ , Shell, 28 linesexternal/ LIT/ run_training_docker.sh - cvdproc/
pipelines/ , R, 35 linesexternal/ LQT/ Example_Multi_Patient_Sc ript.R - cvdproc/
pipelines/ , R, 30 linesexternal/ LQT/ Example_Single_Patient_S cript.R - cvdproc/
pipelines/ , R, 72 linesexternal/ LQT/ R/ compile_data.R - cvdproc/
pipelines/ , R, 177 linesexternal/ LQT/ R/ create_cfg_object.R - cvdproc/
pipelines/ , R, 43 linesexternal/ LQT/ R/ download_templates.R - cvdproc/
pipelines/ , R, 36 linesexternal/ LQT/ R/ get_atlas_sspl.R - cvdproc/
pipelines/ , R, 66 linesexternal/ LQT/ R/ get_parcel_atlas.R - cvdproc/
pipelines/ , R, 46 linesexternal/ LQT/ R/ get_parcel_cons.R - cvdproc/
pipelines/ , R, 86 linesexternal/ LQT/ R/ get_parcel_damage.R - cvdproc/
pipelines/ , R, 237 linesexternal/ LQT/ R/ get_parcel_discon.R - cvdproc/
pipelines/ , R, 175 linesexternal/ LQT/ R/ get_patient_sspl.R - cvdproc/
pipelines/ , R, 122 linesexternal/ LQT/ R/ get_tract_discon.R - cvdproc/
pipelines/ , R, 23 linesexternal/ LQT/ R/ n2ndis.R - cvdproc/
pipelines/ , R, 15 linesexternal/ LQT/ R/ ndis.R - cvdproc/
pipelines/ , R, 17 linesexternal/ LQT/ R/ p2pdis.R - cvdproc/
pipelines/ , R, 10 linesexternal/ LQT/ R/ p2pnet.R - cvdproc/
pipelines/ , R, 11 linesexternal/ LQT/ R/ pdam.R - cvdproc/
pipelines/ , R, 11 linesexternal/ LQT/ R/ pdis.R - cvdproc/
pipelines/ , R, 437 linesexternal/ LQT/ R/ plot_lqt_subject.R - cvdproc/
pipelines/ , R, 11 linesexternal/ LQT/ R/ tdis.R - cvdproc/
pipelines/ , R, 29 linesexternal/ LQT/ R/ util_get_coords.R - cvdproc/
pipelines/ , R, 99 linesexternal/ LQT/ README.Rmd - cvdproc/
pipelines/ , Python, 52 linesexternal/ MIND/ MIND.py - cvdproc/
pipelines/ , Python, 149 linesexternal/ MIND/ MIND_helpers.py - cvdproc/
pipelines/ , Python, 203 linesexternal/ MIND/ get_vertex_df.py - cvdproc/
pipelines/ , Python, 115 linesexternal/ MIND/ register_and_vol2surf.py - cvdproc/
pipelines/ , Shell, 76 linesexternal/ MarkVCID2/ scripts_FW_CONSORTIUM/ MAIN_script_FW.sh - cvdproc/
pipelines/ , Shell, 64 linesexternal/ MarkVCID2/ scripts_FW_CONSORTIUM/ MAIN_script_FW_custom.sh - cvdproc/
pipelines/ , Python, 339 linesexternal/ MarkVCID2/ scripts_FW_CONSORTIUM/ fw_mrn.py - cvdproc/
pipelines/ , Shell, 113 linesexternal/ WMH_Bullseye/ bullseye_pipeline.sh - cvdproc/
pipelines/ , Shell, 155 linesexternal/ WMH_Bullseye/ bullseye_pipeline_custom .sh - cvdproc/
pipelines/ , Python, 23 linesexternal/ WMH_Bullseye/ concentric_regions/ get_cortex_mask.py - cvdproc/
pipelines/ , Python, 30 linesexternal/ WMH_Bullseye/ concentric_regions/ get_distance.py - cvdproc/
pipelines/ , Python, 29 linesexternal/ WMH_Bullseye/ concentric_regions/ get_distance_groups.py - cvdproc/
pipelines/ , Python, 32 linesexternal/ WMH_Bullseye/ concentric_regions/ get_distance_groups_mask ing.py - cvdproc/
pipelines/ , Python, 23 linesexternal/ WMH_Bullseye/ concentric_regions/ get_ventricles_mask.py - cvdproc/
pipelines/ , Python, 35 linesexternal/ WMH_Bullseye/ intersect_regions/ get_intersect_parcellati on.py - cvdproc/
pipelines/ , Python, 37 linesexternal/ WMH_Bullseye/ lobar_regions/ get_lobar_aseg_mask.py - cvdproc/
pipelines/ , Shell, 26 linesexternal/ WMH_Bullseye/ lobar_regions/ get_lobes_labels.sh - cvdproc/
pipelines/ , Python, 61 linesexternal/ WMH_Bullseye/ mask_wmh/ get_intersect_bullseye_w mh.py - cvdproc/
pipelines/ , R, 15 linesexternal/ WMH_Bullseye/ mask_wmh/ get_stats_table.R - cvdproc/
pipelines/ , Shell, 120 linesexternal/ WMH_Bullseye/ mask_wmh/ intersect_wmh.sh - cvdproc/
pipelines/ , Shell, 115 linesexternal/ WMH_Bullseye/ mask_wmh/ intersect_wmh_onlybis.sh - cvdproc/
pipelines/ , Python, 1 lineexternal/ __init__.py - cvdproc/
pipelines/ , Python, 1 lineexternal/ alps/ __init__.py - cvdproc/
pipelines/ , Shell, 693 linesexternal/ alps/ alps.sh - cvdproc/
pipelines/ , Shell, 713 linesexternal/ alps/ alps_custom.sh - cvdproc/
pipelines/ , Shell, 736 linesexternal/ alps/ alps_custom2.sh - cvdproc/
pipelines/ , Python, 161 linesexternal/ alps/ dti_alps_wf.py - cvdproc/
pipelines/ , Python, 1 lineexternal/ bullseye_WMH/ __init__.py - cvdproc/
pipelines/ , Python, 164 linesexternal/ bullseye_WMH/ bullseye_pipeline.py - cvdproc/
pipelines/ , Python, 288 linesexternal/ bullseye_WMH/ bullseye_pipeline_custom .py - cvdproc/
pipelines/ , Python, 14 linesexternal/ bullseye_WMH/ configoptions.py - cvdproc/
pipelines/ , Python, 99 linesexternal/ bullseye_WMH/ run_bullseye_pipeline.py - cvdproc/
pipelines/ , Python, 334 linesexternal/ bullseye_WMH/ utils.py - cvdproc/
pipelines/ , Shell, 302 linesexternal/ fsl_truenet/ prepare_truenet_data_cus tom.sh - cvdproc/
pipelines/ , Shell, 436 linesexternal/ hipsthomas/ test.sh - cvdproc/
pipelines/ , Shell, 29 linesexternal/ hipsthomas/ thomas_t1.sh - cvdproc/
pipelines/ , Python, 1 lineexternal/ psmd/ __init__.py - cvdproc/
pipelines/ , Shell, 514 linesexternal/ psmd/ psmd.sh - cvdproc/
pipelines/ , Python, 480 linesexternal/ qqnet/ JH_funcs/ funcJH.py - cvdproc/
pipelines/ , Python, 1 lineexternal/ qqnet/ __init__.py - cvdproc/
pipelines/ , Python, 1 lineexternal/ qqnet/ basics/ __init__.py - cvdproc/
pipelines/ , Python, 71 linesexternal/ qqnet/ basics/ unet3d_b.py - cvdproc/
pipelines/ , Python, 153 linesexternal/ qqnet/ basics/ unet3d_b_limit_m.py - cvdproc/
pipelines/ , Python, 787 linesexternal/ qqnet/ basics/ utils_u.py - cvdproc/
pipelines/ , Python, 785 linesexternal/ qqnet/ basics/ utils_u_b.py - cvdproc/
pipelines/ , Python, 80 linesexternal/ qqnet/ pad_mag.py - cvdproc/
pipelines/ , Python, 338 linesexternal/ qqnet/ qqnet_predict.py - cvdproc/
pipelines/ , Python, 427 linesexternal/ qqnet/ utils.py - cvdproc/
pipelines/ , Python, 1 linefmri/ __init__.py - cvdproc/
pipelines/ , Python, 1 linematlab/ __init__.py - cvdproc/
pipelines/ , Python, 1 linematlab/ dsc_mri_toolbox/ __init__.py - cvdproc/
pipelines/ , MATLAB, 39 linesmatlab/ dsc_mri_toolbox/ conc.m - cvdproc/
pipelines/ , MATLAB, 57 linesmatlab/ dsc_mri_toolbox/ pwimap.m - cvdproc/
pipelines/ , MATLAB, 335 linesmatlab/ exploreasl/ exploreasl_process.m - cvdproc/
pipelines/ , MATLAB, 134 linesmatlab/ lst/ wmh_seg_lst_script.m - cvdproc/
pipelines/ , MATLAB, 10 linesmatlab/ pved/ pved_single.m - cvdproc/
pipelines/ , MATLAB, 609 linesmatlab/ qsm_pipeline_part1/ QSM_pipeline_part1.m - cvdproc/
pipelines/ , MATLAB, 237 linesmatlab/ sepia_qsm/ SEPIA_batch.m - cvdproc/
pipelines/ , MATLAB, 62 linesmatlab/ sepia_qsm/ SEPIA_segment_batch.m - cvdproc/
pipelines/ , Python, 1 linematlab/ sepia_qsm/ __init__.py - cvdproc/
pipelines/ , MATLAB, 19 linesmatlab/ sepia_qsm/ extract_parcellation.m - cvdproc/
pipelines/ , MATLAB, 50 linesmatlab/ sepia_qsm/ func_Extract_ROI.m - cvdproc/
pipelines/ , MATLAB, 132 linesmatlab/ sepia_qsm/ sepia_process.m - cvdproc/
pipelines/ , Python, 1 linematlab/ test/ __init__.py - cvdproc/
pipelines/ , MATLAB, 12 linesmatlab/ test/ matlab_test.m - cvdproc/
pipelines/ , Python, 1 linemulti/ __init__.py - cvdproc/
pipelines/ , Python, 266 linesmulti/ arts/ arts_nipype.py - cvdproc/
pipelines/ , Python, 330 linesmulti/ arts_pipeline.py - cvdproc/
pipelines/ , Python, 1 linenetwork/ __init__.py - cvdproc/
pipelines/ , Python, 389 linesnetwork/ network_control_theory.p y - cvdproc/
pipelines/ , Python, 261 linesnetwork/ network_graph_measures_n ipype.py - cvdproc/
pipelines/ , Python, 394 linesnetwork/ network_measures.py - cvdproc/
pipelines/ , Python, 1 linenipype_test/ __init__.py - cvdproc/
pipelines/ , Python, 9 linesnipype_test/ run_matlab_script.py - cvdproc/
pipelines/ , Python, 77 linesnipype_test/ test.py - cvdproc/
pipelines/ , Python, 98 linesnipype_test/ test_matlab.py - cvdproc/
pipelines/ , Python, 1 lineperfusion/ __init__.py - cvdproc/
pipelines/ , Python, 641 linesperfusion/ asl_pipeline.py - cvdproc/
pipelines/ , Python, 1 lineperfusion/ exploreasl/ __init__.py - cvdproc/
pipelines/ , Python, 81 linesperfusion/ exploreasl/ exploreasl_nipype.py - cvdproc/
pipelines/ , Python, 1 linepwi/ __init__.py - cvdproc/
pipelines/ , Python, 117 linespwi/ aif/ auto_aif_nipype.py - cvdproc/
pipelines/ , Python, 1 linepwi/ dsc_mri_toolbox/ __init__.py - cvdproc/
pipelines/ , Python, 198 linespwi/ dsc_mri_toolbox/ dsc_mri_nipype.py - cvdproc/
pipelines/ , Python, 220 linespwi/ pwi_pipeline.py - cvdproc/
pipelines/ , Python, 1 lineqmri/ __init__.py - cvdproc/
pipelines/ , Python, 1 lineqmri/ qqnet/ __init__.py - cvdproc/
pipelines/ , Python, 80 linesqmri/ qqnet/ qqnet_nipype.py - cvdproc/
pipelines/ , Python, 483 linesqmri/ qsm_pipeline.py - cvdproc/
pipelines/ , Python, 1 lineqmri/ qsm_pipeline_part1/ __init__.py - cvdproc/
pipelines/ , Python, 99 linesqmri/ qsm_pipeline_part1/ qsm_pipeline_part1_nipyp e.py - cvdproc/
pipelines/ , Python, 1 lineqmri/ qsm_register/ __init__.py - cvdproc/
pipelines/ , Python, 68 linesqmri/ qsm_register/ qsm_register2_nipype.py - cvdproc/
pipelines/ , Python, 1 lineqmri/ sepia_qsm/ __init__.py - cvdproc/
pipelines/ , Python, 102 linesqmri/ sepia_qsm/ sepia_qsm_nipype.py - cvdproc/
pipelines/ , Python, 170 linesqmri/ sepia_qsm_pipeline.py - cvdproc/
pipelines/ , R, 44 linesr/ lqt/ lqt_initialize.r - cvdproc/
pipelines/ , R, 35 linesr/ lqt/ lqt_single_subject.R - cvdproc/
pipelines/ , Python, 1 linesmri/ __init__.py - cvdproc/
pipelines/ , Python, 1 linesmri/ anat_seg/ __init__.py - cvdproc/
pipelines/ , Python, 43 linessmri/ anat_seg/ avp_seg/ avpseg.py - cvdproc/
pipelines/ , Python, 1 linesmri/ anat_seg/ cp_seg/ __init__.py - cvdproc/
pipelines/ , Python, 34 linessmri/ anat_seg/ cp_seg/ chpseg.py - cvdproc/
pipelines/ , Python, 212 linessmri/ anat_seg_pipeline.py - cvdproc/
pipelines/ , Python, 1 linesmri/ ants/ __init__.py - cvdproc/
pipelines/ , Python, 26 linessmri/ ants/ n4biascorr_nipype.py - cvdproc/
pipelines/ , Python, 29 linessmri/ brainage/ brainage_nipype.py - cvdproc/
pipelines/ , Python, 112 linessmri/ brainage_pipeline.py - cvdproc/
pipelines/ , Python, 223 linessmri/ cat12_pipeline.py - cvdproc/
pipelines/ , Python, 1 linesmri/ csvd_quantification/ __init__.py - cvdproc/
pipelines/ , Python, 314 linessmri/ csvd_quantification/ cmb_pipeline.py - cvdproc/
pipelines/ , Python, 1 linesmri/ csvd_quantification/ frangi_pvs/ __init__.py - cvdproc/
pipelines/ , Python, 28 linessmri/ csvd_quantification/ frangi_pvs/ frangi.py - cvdproc/
pipelines/ , Python, 65 linessmri/ csvd_quantification/ frangi_pvs/ frangi_pvs.py - cvdproc/
pipelines/ , Python, 26 linessmri/ csvd_quantification/ frangi_pvs/ nlm.py - cvdproc/
pipelines/ , Python, 330 linessmri/ csvd_quantification/ pvs_pipeline.py - cvdproc/
pipelines/ , Python, 1 linesmri/ csvd_quantification/ segcsvd/ __init__.py - cvdproc/
pipelines/ , Python, 30 linessmri/ csvd_quantification/ segcsvd/ segment_pvs.py - cvdproc/
pipelines/ , Python, 1 linesmri/ csvd_quantification/ shiva_segmentation/ __init__.py - cvdproc/
pipelines/ , Python, 132 linessmri/ csvd_quantification/ shiva_segmentation/ shiva_nipype.py - cvdproc/
pipelines/ , Python, 646 linessmri/ csvd_quantification/ shiva_segmentation/ shiva_parc.py - cvdproc/
pipelines/ , Python, 361 linessmri/ csvd_quantification/ shiva_segmentation/ shiva_segmentation.py - cvdproc/
pipelines/ , Python, 85 linessmri/ csvd_quantification/ wmh/ BC3D/ BoxCountingMethod_Solid. py - cvdproc/
pipelines/ , Python, 154 linessmri/ csvd_quantification/ wmh/ BC3D/ Draw.py - cvdproc/
pipelines/ , Python, 156 linessmri/ csvd_quantification/ wmh/ BC3D/ FD_plot.py - cvdproc/
pipelines/ , Python, 49 linessmri/ csvd_quantification/ wmh/ BC3D/ Run_Solid.py - cvdproc/
pipelines/ , Python, 1 linesmri/ csvd_quantification/ wmh/ BC3D/ __init__.py - cvdproc/
pipelines/ , Python, 44 linessmri/ csvd_quantification/ wmh/ BC3D/ data_excel.py - cvdproc/
pipelines/ , Python, 1 linesmri/ csvd_quantification/ wmh/ __init__.py - cvdproc/
pipelines/ , Python, 188 lines, 1 matchsmri/ csvd_quantification/ wmh/ wmh_location_nipype.py - cvdproc/
pipelines/ , Python, 562 linessmri/ csvd_quantification/ wmh/ wmh_seg_nipype.py - cvdproc/
pipelines/ , Python, 304 linessmri/ csvd_quantification/ wmh/ wmh_shape_nipype.py - cvdproc/
pipelines/ , Python, 1,246 linessmri/ csvd_quantification/ wmh_pipeline.py - cvdproc/
pipelines/ , Python, 1 linesmri/ freesurfer/ __init__.py - cvdproc/
pipelines/ , Python, 359 linessmri/ freesurfer/ post_freesurfer.py - cvdproc/
pipelines/ , Python, 186 linessmri/ freesurfer/ recon_all_clinical.py - cvdproc/
pipelines/ , Python, 38 linessmri/ freesurfer/ recon_all_external_mask. py - cvdproc/
pipelines/ , Python, 63 linessmri/ freesurfer/ recon_all_longitudinal.p y - cvdproc/
pipelines/ , Python, 304 linessmri/ freesurfer/ results_extractor.py - cvdproc/
pipelines/ , Python, 311 linessmri/ freesurfer/ subfieldseg.py - cvdproc/
pipelines/ , Python, 81 linessmri/ freesurfer/ synthSR.py - cvdproc/
pipelines/ , Python, 36 linessmri/ freesurfer/ synthmorph.py - cvdproc/
pipelines/ , Python, 95 linessmri/ freesurfer/ synthseg.py - cvdproc/
pipelines/ , Python, 68 linessmri/ freesurfer/ synthstrip.py - cvdproc/
pipelines/ , Python, 77 linessmri/ freesurfer/ utils.py - cvdproc/
pipelines/ , Python, 578 linessmri/ freesurfer_pipeline.py - cvdproc/
pipelines/ , Python, 1 linesmri/ fsl/ __init__.py - cvdproc/
pipelines/ , Python, 86 linessmri/ fsl/ deface_nipype.py - cvdproc/
pipelines/ , Python, 58 linessmri/ fsl/ distancemap_nipype.py - cvdproc/
pipelines/ , Python, 32 linessmri/ fsl/ epi_reg_nipype.py - cvdproc/
pipelines/ , Python, 71 linessmri/ fsl/ fsl_anat_nipype.py - cvdproc/
pipelines/ , Python, 26 linessmri/ fsl/ fslcpgeom_nipype.py - cvdproc/
pipelines/ , Python, 47 linessmri/ fsl/ fslmaths_thr.py - cvdproc/
pipelines/ , Python, 23 linessmri/ fsl/ make_bianca_mask_nipype. py - cvdproc/
pipelines/ , Python, 76 lines, 1 matchsmri/ fsl_anat_pipeline.py - cvdproc/
pipelines/ , Python, 1 linesmri/ hd_bet/ __init__.py - cvdproc/
pipelines/ , Python, 145 linessmri/ hd_bet/ hd_bet_nipype.py - cvdproc/
pipelines/ , Python, 1 linesmri/ hipsta/ __init__.py - cvdproc/
pipelines/ , Python, 96 linessmri/ hipsta/ hipsta_nipype.py - cvdproc/
pipelines/ , Python, 49 linessmri/ hipsta/ patch_hipsta_mode.py - cvdproc/
pipelines/ , Python, 192 linessmri/ hipsta_pipeline.py - cvdproc/
pipelines/ , Python, 274 linessmri/ lesion_analysis_pipeline .py - cvdproc/
pipelines/ , Python, 1 linesmri/ lesion_preprocess/ __init__.py - cvdproc/
pipelines/ , Python, 402 linessmri/ lesion_preprocess/ lesion_preprocess.py - cvdproc/
pipelines/ , Python, 1 linesmri/ mirror/ __init__.py - cvdproc/
pipelines/ , Python, 90 linessmri/ mirror/ mirror_nipype.py - cvdproc/
pipelines/ , Python, 1 linesmri/ scn/ __init__.py - cvdproc/
pipelines/ , Python, 68 linessmri/ scn/ scn_nipype.py - cvdproc/
pipelines/ , Python, 70 lines, 1 matchsmri/ scn_pipeline.py - cvdproc/
pipelines/ , Python, 120 linessmri/ t1_register.py - cvdproc/
utils/ , Python, 1 line__init__.py - cvdproc/
utils/ , Python, 1 linebash/ __init__.py - cvdproc/
utils/ , Shell, 68 linesbash/ freesurfer_file_order/ fix_fsaverage_link.sh - cvdproc/
utils/ , Shell, 51 linesbash/ freesurfer_file_order/ move_subject.sh - cvdproc/
utils/ , Shell, 173 linesbash/ freesurfer_file_order/ repair_symlinks.sh - cvdproc/
utils/ , Shell, 140 linesbash/ generic_registration.sh - cvdproc/
utils/ , Shell, 122 linesbash/ lesion_mask_transform.sh - cvdproc/
utils/ , Shell, 130 linesbash/ order_for_nemo.sh - cvdproc/
utils/ , Shell, 24 linesbash/ order_freesurfer_for_fmr iprep.sh - cvdproc/
utils/ , MATLAB, 510 linesmatlab/ Chisep_script.m - cvdproc/
utils/ , MATLAB, 634 linesmatlab/ QSM_pipeline_part1.m - cvdproc/
utils/ , MATLAB, 116 linesmatlab/ QSM_pipeline_part1_func/ SaveData_Chisep.m - cvdproc/
utils/ , MATLAB, 43 linesmatlab/ QSM_pipeline_part1_func/ convert_nii_to_gz.m - cvdproc/
utils/ , MATLAB, 6 linesmatlab/ QSM_pipeline_part1_func/ gzip_if_needed.m - cvdproc/
utils/ , MATLAB, 8 linesmatlab/ QSM_pipeline_part1_func/ parse_echo_from_name.m - cvdproc/
utils/ , MATLAB, 9 linesmatlab/ QSM_pipeline_part1_func/ replace_ext.m - cvdproc/
utils/ , MATLAB, 9 linesmatlab/ QSM_pipeline_part1_func/ replace_part_token.m - cvdproc/
utils/ , MATLAB, 8 linesmatlab/ QSM_pipeline_part1_func/ win2wsl.m - cvdproc/
utils/ , MATLAB, 701 linesmatlab/ QSM_pipeline_part1_multi .m - cvdproc/
utils/ , Python, 1 linematlab/ __init__.py - cvdproc/
utils/ , MATLAB, 44 linesmatlab/ view_b_table.m - cvdproc/
utils/ , MATLAB, 726 linesmatlab/ xASL_quant_External.m - cvdproc/
utils/ , Python, 27 linespython/ GM_mask_refine.py - cvdproc/
utils/ , Python, 1 linepython/ __init__.py - cvdproc/
utils/ , Python, 67 linespython/ annotate_aal_surface_lab els.py - cvdproc/
utils/ , Python, 618 linespython/ basic_image_processor.py - cvdproc/
utils/ , Python, 51 linespython/ bids_collect_file.py - cvdproc/
utils/ , Python, 45 linespython/ check_gii.py - cvdproc/
utils/ , Python, 156 linespython/ check_tck.py - cvdproc/
utils/ , Python, 167 linespython/ combine_xlsx.py - cvdproc/
utils/ , Python, 303 linespython/ compare_two_maps.py - cvdproc/
utils/ , Python, 409 linespython/ compare_two_parcellated_ maps.py - cvdproc/
utils/ , Python, 92 linespython/ dicom_sort.py - cvdproc/
utils/ , Python, 73 linespython/ dmipy_test.py - cvdproc/
utils/ , Python, 67 linespython/ extend_mask_x.py - cvdproc/
utils/ , Python, 327 linespython/ extract_tbss_roi_stats.p y - cvdproc/
utils/ , Python, 98 linespython/ flip_b_qsiprep.py - cvdproc/
utils/ , Python, 232 linespython/ fs_stats2table.py - cvdproc/
utils/ , Python, 35 linespython/ fsaverage_mirror.py - cvdproc/
utils/ , Python, 42 linespython/ gifti2mgh.py - cvdproc/
utils/ , Python, 68 linespython/ invert_h5.py - cvdproc/
utils/ , Python, 41 linespython/ merge_bedpostX.py - cvdproc/
utils/ , Python, 15 linespython/ mni2surface.py - cvdproc/
utils/ , Python, 69 linespython/ move_file4shivai.py - cvdproc/
utils/ , Python, 115 linespython/ nifti_warp_to_h5.py - cvdproc/
utils/ , Python, 66 linespython/ nifti_warp_to_h5_old.py - cvdproc/
utils/ , Python, 37 linespython/ organize_nemo.py - cvdproc/
utils/ , Python, 70 linespython/ reconstruct_subcortical_ display_mesh.py - cvdproc/
utils/ , Python, 87 linespython/ refine_subcortical_mesh. py - cvdproc/
utils/ , Python, 88 linespython/ render_subcortical_mesh_ preview.py - cvdproc/
utils/ , Python, 34 linespython/ replace_spaces.py - cvdproc/
utils/ , Python, 85 linespython/ smooth_surface_label_bou ndaries.py - cvdproc/
utils/ , Python, 52 linespython/ subjects2bids.py - cvdproc/
utils/ , Python, 219 linespython/ temp.py - cvdproc/
utils/ , Shell, 123 linespython/ temp.sh - cvdproc/
utils/ , Python, 46 linespython/ temp2.py - cvdproc/
utils/ , Python, 743 linespython/ tortoise.py - cvdproc/
utils/ , Python, 1 linepython/ visualization/ __init__.py - cvdproc/
utils/ , Python, 480 linespython/ visualization/ plot_AAL.py - cvdproc/
utils/ , Python, 546 linespython/ visualization/ plot_AAL_surface_enigma. py - cvdproc/
utils/ , Python, 420 linespython/ visualization/ plot_JHU_glass_brain.py - cvdproc/
utils/ , Python, 547 linespython/ visualization/ plot_JHU_mosaic.py - cvdproc/
utils/ , Python, 571 linespython/ visualization/ plot_Schirmer_mosaic.py - cvdproc/
utils/ , Python, 87 linespython/ visualization/ plot_csvdoverlay.py - cvdproc/
utils/ , Python, 118 linespython/ visualization/ plot_csvdoverlay2.py - cvdproc/
utils/ , Python, 56 linespython/ visualization/ plot_dk_surface_enigma.p y - cvdproc/
utils/ , Python, 338 linespython/ visualization/ plot_glass_brain.py - cvdproc/
utils/ , Python, 361 linespython/ visualization/ plot_glass_brain_mesh_ve r.py - cvdproc/
utils/ , Python, 205 linespython/ visualization/ plot_isc2026.py - cvdproc/
utils/ , Python, 47 linespython/ visualization/ plot_psmd.py - cvdproc/
utils/ , Python, 151 linespython/ visualization/ plot_pved.py - cvdproc/
utils/ , Python, 397 linespython/ visualization/ plot_tck.py - cvdproc/
utils/ , Python, 352 linespython/ visualization/ plot_wmparc.py - cvdproc/
utils/ , Python, 229 linespython/ visualization/ surface_plotting_utils.p y - cvdproc/
utils/ , R, 102 linesr/ plot_surface_dk.R - cvdproc/
utils/ , R, 223 linesr/ plot_surface_dk_sig.R - cvdproc/
utils/ , Python, 65 linestool_manager.py - setup.py, Python, 4 lines
- site/
assets/ , JavaScript, 16 linesjavascripts/ bundle.50899def.min.js - site/
assets/ , JavaScript, 1 linejavascripts/ lunr/ min/ lunr.ar.min.js - site/
assets/ , JavaScript, 18 linesjavascripts/ lunr/ min/ lunr.da.min.js - site/
assets/ , JavaScript, 18 linesjavascripts/ lunr/ min/ lunr.de.min.js - site/
assets/ , JavaScript, 18 linesjavascripts/ lunr/ min/ lunr.du.min.js - site/
assets/ , JavaScript, 1 linejavascripts/ lunr/ min/ lunr.el.min.js - site/
assets/ , JavaScript, 18 linesjavascripts/ lunr/ min/ lunr.es.min.js - site/
assets/ , JavaScript, 18 linesjavascripts/ lunr/ min/ lunr.fi.min.js - site/
assets/ , JavaScript, 18 linesjavascripts/ lunr/ min/ lunr.fr.min.js - site/
assets/ , JavaScript, 1 linejavascripts/ lunr/ min/ lunr.he.min.js - site/
assets/ , JavaScript, 1 linejavascripts/ lunr/ min/ lunr.hi.min.js - site/
assets/ , JavaScript, 18 linesjavascripts/ lunr/ min/ lunr.hu.min.js - site/
assets/ , JavaScript, 1 linejavascripts/ lunr/ min/ lunr.hy.min.js - site/
assets/ , JavaScript, 18 linesjavascripts/ lunr/ min/ lunr.it.min.js - site/
assets/ , JavaScript, 1 linejavascripts/ lunr/ min/ lunr.ja.min.js - site/
assets/ , JavaScript, 1 linejavascripts/ lunr/ min/ lunr.jp.min.js - site/
assets/ , JavaScript, 1 linejavascripts/ lunr/ min/ lunr.kn.min.js - site/
assets/ , JavaScript, 1 linejavascripts/ lunr/ min/ lunr.ko.min.js - site/
assets/ , JavaScript, 1 linejavascripts/ lunr/ min/ lunr.multi.min.js - site/
assets/ , JavaScript, 18 linesjavascripts/ lunr/ min/ lunr.nl.min.js - site/
assets/ , JavaScript, 18 linesjavascripts/ lunr/ min/ lunr.no.min.js - site/
assets/ , JavaScript, 18 linesjavascripts/ lunr/ min/ lunr.pt.min.js - site/
assets/ , JavaScript, 18 linesjavascripts/ lunr/ min/ lunr.ro.min.js - site/
assets/ , JavaScript, 18 linesjavascripts/ lunr/ min/ lunr.ru.min.js - site/
assets/ , JavaScript, 1 linejavascripts/ lunr/ min/ lunr.sa.min.js - site/
assets/ , JavaScript, 1 linejavascripts/ lunr/ min/ lunr.stemmer.support.min .js - site/
assets/ , JavaScript, 18 linesjavascripts/ lunr/ min/ lunr.sv.min.js - site/
assets/ , JavaScript, 1 linejavascripts/ lunr/ min/ lunr.ta.min.js - site/
assets/ , JavaScript, 1 linejavascripts/ lunr/ min/ lunr.te.min.js - site/
assets/ , JavaScript, 1 linejavascripts/ lunr/ min/ lunr.th.min.js - site/
assets/ , JavaScript, 18 linesjavascripts/ lunr/ min/ lunr.tr.min.js - site/
assets/ , JavaScript, 1 linejavascripts/ lunr/ min/ lunr.vi.min.js - site/
assets/ , JavaScript, 1 linejavascripts/ lunr/ min/ lunr.zh.min.js - site/
assets/ , JavaScript, 206 linesjavascripts/ lunr/ tinyseg.js - site/
assets/ , JavaScript, 186 linesjavascripts/ lunr/ wordcut.js - site/
assets/ , JavaScript, 42 linesjavascripts/ workers/ search.d50fe291.min.js - LICENSE, License, 21 lines
- README.md, Text, 76 lines
The paper's code and data availability statement is in the Data section.
Tracing map
Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.
What the map holds:
- 2 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 505 scripts, each with its path and the digest of its content;
- 7 matches between paragraphs of the paper and lines of the code (method lexical-v1);
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
No dataset and no data link were found in the paper.
Data availability
Data will be made available on request.
Mr. Wang had full access to all the data in the study and took full responsibility for the integrity of the data and the accuracy of the analyses. The neuroimaging analysis code is available at https://
Reproduced under the paper's license (CC BY), from the paper cited above.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 1, 30 September 2026: the first record
Recorded: type, language, journal, volume, pages, dates, 9 authors, 3 keywords, 11 MeSH terms, 4 funders, 43 references.
Cite
This paper
Wang, Y., Cui, J., Yan, Y., Yang, T., Yuan, Y., Lei, R., Luo, R., Wu, B., & Jiang, S. (2026). Remote cortical degeneration related to structural connectivity following recent small subcortical infarcts. NeuroImage. Clinical, 50, 103987. https://
BibTeX
@article{wang2026remote,
author = {Wang, Youjie and Cui, Jingyu and Yan, Yuying and Yang, Tang and Yuan, Yue and Lei, Rumei and Luo, Rongfeng and Wu, Bo and Jiang, Shuai},
title = {{Remote cortical degeneration related to structural connectivity following recent small subcortical infarcts}},
journal = {NeuroImage. Clinical},
year = {2026},
month = mar,
volume = {50},
pages = {103987},
publisher = {Elsevier},
issn = {2213-1582},
doi = {10.1016/
url = {https://
pmid = {41875823},
pmcid = {PMC13049619}
}
RIS
TY - JOUR
AU - Wang, Youjie
AU - Cui, Jingyu
AU - Yan, Yuying
AU - Yang, Tang
AU - Yuan, Yue
AU - Lei, Rumei
AU - Luo, Rongfeng
AU - Wu, Bo
AU - Jiang, Shuai
TI - Remote cortical degeneration related to structural connectivity following recent small subcortical infarcts
T2 - NeuroImage. Clinical
J2 - Neuroimage Clin
PY - 2026
DA - 2026/
VL - 50
SP - 103987
SN - 2213-1582
PB - Elsevier
DO - 10.1016/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1016/
"type": "article-journal",
"title": "Remote cortical degeneration related to structural connectivity following recent small subcortical infarcts",
"container-title": "NeuroImage. Clinical",
"author": [
{
"family": "Wang",
"given": "Youjie"
},
{
"family": "Cui",
"given": "Jingyu"
},
{
"family": "Yan",
"given": "Yuying"
},
{
"family": "Yang",
"given": "Tang"
},
{
"family": "Yuan",
"given": "Yue"
},
{
"family": "Lei",
"given": "Rumei"
},
{
"family": "Luo",
"given": "Rongfeng"
},
{
"family": "Wu",
"given": "Bo"
},
{
"family": "Jiang",
"given": "Shuai"
}
],
"container-title-short":
"volume": "50",
"page": "103987",
"DOI": "10.1016/
"PMID": "41875823",
"PMCID": "PMC13049619",
"ISSN": "2213-1582",
"publisher": "Elsevier",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
3,
20
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.3390/bs15060746 [code]
- Brain White Matter Alterations in Young Adults with Childhood Emotional Neglect Experience.Journal: Behavioral sciences (Basel, Switzerland)In common: QSIPrep, PyBIDS, MRtrix3, 20 other tools, structural MRI / diffusion
- [2] doi:10.1038/s41592-025-02599-1 [code]
- DeepPrep: an accelerated, scalable and robust pipeline for neuroimaging preprocessing empowered by deep learning.Journal: Nature methodsIn common: fMRIPrep, neuromaps, PyBIDS, 20 other tools, structural MRI / diffusion, 1 reference
- [3] doi:10.1038/s41467-026-68714-8 [code]
- Two axes of white matter development.Journal: Nature communicationsIn common: QSIPrep, fMRIPrep, ggseg, 18 other tools, structural MRI / diffusion, 1 reference
- [4] doi:10.1002/alz.70196 [code]
- Cognitive implications and associated transcriptomic signatures of distinct regional iron depositions in cerebral small vessel disease.Journal: Alzheimer's & dementia : the journal of the Alzheimer's AssociationIn common: ggseg, neuromaps, Nipype, 14 other tools, stroke, structural MRI / diffusion, 3 references, author Youjie Wang
- [5] doi:10.1038/s41598-023-32924-7 [code]
- Reproducible Tract Profiles 2 (RTP2) suite, from diffusion MRI acquisition to clinical practice and research.Journal: Scientific reportsIn common: fMRIPrep, PyBIDS, MRtrix3, 16 other tools, structural MRI / diffusion, clinical / translational, 3 references
- [6] doi:10.1038/s41398-025-03614-x [code]
- Sex differences in response to violence: role of salience network expansion and connectivity on depression.Journal: Translational psychiatryIn common: fMRIPrep, neuromaps, PyBIDS, 18 other tools, 1 reference
- [7] doi:10.1038/s42003-025-08573-z [code]
- Profiling brain morphology for autism spectrum disorder with two cross-culture large-scale consortia.Journal: Communications biologyIn common: ggseg, MONAI, SimpleITK, 20 other tools, structural MRI / diffusion
- [8] doi:10.1016/j.neuroimage.2023.120109 [code]
- Benchmarking explanation methods for mental state decoding with deep learning models.Journal: NeuroImageIn common: fMRIPrep, PyBIDS, DIPY, 18 other tools, 1 reference
- [9] doi:10.1093/brain/awae315 [code]
- Enhancing cognitive performance prediction by white matter hyperintensity connectivity assessment.Journal: Brain : a journal of neurologyIn common: neuromaps, Nipype, pydicom, 15 other tools, stroke, structural MRI / diffusion, 4 references
- [10] doi:10.1038/s41586-025-09708-2 [code]
- A probabilistic histological atlas of the human brain for MRI segmentation.Journal: NatureIn common: MRtrix3, DIPY, Nipype, 18 other tools, structural MRI / diffusion, 1 reference
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 2 repositories of the authors' code, each at its verified commit and with its license, 505 scripts, and 7 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:5f3ed8c78efc3519…
Add the badge to the code's README
If you are an author or a maintainer of this paper's code, you can add this badge. It says what OSCR knows of the paper (its code, its validated tracing map) and links the code to this page. Paste one of these snippets under the README's title. Keep the two marks around it, so that the badge can be replaced in place and is never added twice. OSCR changes nothing in your repository.
Markdown
<!-- oscr-badge:start -->
[](https://openscicode.org/badge/doi/10.1016/j.nicl.2026.103987)
<!-- oscr-badge:end -->reStructuredText
.. oscr-badge:start
.. image:: https://openscicode.org/badge/doi/10.1016/j.nicl.2026.103987.svg
:target: https://openscicode.org/badge/doi/10.1016/j.nicl.2026.103987
:alt: OSCR
.. oscr-badge:endHTML
<!-- oscr-badge:start -->
<a href="https://openscicode.org/badge/doi/10.1016/j.nicl.2026.103987"><img src="https://openscicode.org/badge/doi/10.1016/j.nicl.2026.103987.svg" alt="OSCR"></a>
<!-- oscr-badge:end -->On GitHub, open the README, choose Edit (the pencil), paste the snippet under the title, then "Commit changes".
If you choose to, you can have the badge kept up to date for you, with oscr badge --write from the command line or with the GitHub Action in your repository's workflows. Every other form and option (a card, AsciiDoc, LaTeX, shields.io) is on the badge page.
Request its removal
To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).
Discussion, reproductions, activity
Discussion: code errors and mismatches between the code and the paper about this paper and its code, from readers and its authors, beside the paper and its tracing map.
Report a mismatch between the code and the paper · Report a code error (signed in)
Reproductions: reports from readers who ran the authors' code and did not get the paper's result, what they ran, with which environment, commit and data.
Report a reproduction failure (signed in)
Versions of its code: the releases of its code tied to a version of this paper (the preprint, the accepted manuscript, the version of record…), each with the tracing map versioned with it.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
