Source code for cmtklib.bids.io

# Copyright (C) 2009-2022, Ecole Polytechnique Federale de Lausanne (EPFL) and
# Hospital Center and University of Lausanne (UNIL-CHUV), Switzerland, and CMP3 contributors
# All rights reserved.
#
#  This software is distributed under the open-source license Modified BSD.

"""This module provides classes to handle custom BIDS derivatives file input."""
import csv
import os
import json
from traits.api import (HasTraits, Str)

from cmp.info import __version__
from nipype import __version__ as nipype_version


# Directories for derivatives compliant to BIDS `1.4.0` (e.g. <toolbox>-<version>)
# Need to be declared before import the pipeline modules
__cmp_directory__ = f'cmp-{__version__}'
__nipype_directory__ = f'nipype-{nipype_version}'
__freesurfer_directory__ = f'freesurfer-7.1.1'
__cartool_directory__ = f'cartool-v3.80'
__eeglab_directory__ = f'eeglab-v14.1.1'


[docs]class CustomBIDSFile(HasTraits): """Base class used to represent a BIDS-formatted file inside a custom BIDS derivatives directory. Attributes ---------- toolbox_derivatives_dir : Str Toolbox folder name in the `derivatives/` of the BIDS dataset suffix : Str Filename suffix e.g. `sub-01_T1w.nii.gz` has suffix `T1w` acquisition : Str Label used in `_acq-<label>_` res : Str Label used in `_res-<label>_` extension : Str File extension atlas : Str Label used in `_atlas-<label>_` label : Str Label used in `_label-<label>_` desc : Str Label used in `_desc-<label>_` """ toolbox_derivatives_dir = Str suffix = Str acquisition = Str res = Str extension = Str atlas = Str label = Str desc = Str def __init__( self, p_toolbox_derivatives_dir="", p_suffix="", p_extension="", p_acquisition="", p_atlas="", p_res="", p_label="", p_desc="" ): self.toolbox_derivatives_dir = p_toolbox_derivatives_dir self.suffix = p_suffix self.extension = p_extension self.acquisition = p_acquisition self.atlas = p_atlas self.res = p_res self.label = p_label self.desc = p_desc def __str__(self): msg = "{" msg += f' "custom_derivatives_dir": "{self.toolbox_derivatives_dir}"' if self.suffix: msg += f', "suffix": "{self.suffix}"' if self.extension: msg += f', "extension": "{self.extension}"' if self.acquisition: msg += f', "acquisition": "{self.acquisition}"' if self.atlas: msg += f', "atlas": "{self.atlas}"' if self.res: msg += f', "res": "{self.res}"' if self.label: msg += f', "label": "{self.label}"' if self.desc: msg += f', "desc": "{self.desc}"' msg += "}" return msg def _string2dict(self): return json.loads(self.__str__())
[docs] def get_query_dict(self): """Return the dictionary to be passed to `BIDSDataGrabber` to query a list of files.""" query_dict = self._string2dict() del query_dict["custom_derivatives_dir"] return query_dict
[docs] def get_toolbox_derivatives_dir(self): """Return the value of `custom_derivatives_dir` attribute.""" return self.toolbox_derivatives_dir
[docs] def get_filename_path(self, base_dir, subject, session=None, debug=True): """Return the number of regions by reading its associated TSV side car file describing the nodes. Parameters ---------- base_dir: str BIDS root directory or `derivatives/` directory in BIDS root directory subject: str Subject filename entity e.g. "sub-01" session: str Session filename entity e.g. "ses-01" if applicable (Default: None) debug: bool Debug mode (Extra outputed messages) if `True` """ # Build path to BIDS TSV side car of the parcellation file filepath = os.path.join( base_dir, subject ) fname = f'{subject}' if session is not None and session != "": fname += f'_{session}' filepath = os.path.join(filepath, session) if self.label is not None and self.label != "": fname += f'_label-{self.label}' if self.atlas is not None and self.atlas != "": fname += f'_atlas-{self.atlas}' if self.res is not None and self.res != "": fname += f'_res-{self.res}' if self.desc is not None and self.desc != "": fname += f'_desc-{self.desc}' fname += f'_{self.suffix}' filepath = os.path.join(filepath, "anat", fname) if debug: # pragma: no cover print(f" .. DEBUG : Generated parcellation file path (no extension) = {filepath}") return filepath
[docs]class CustomParcellationBIDSFile(CustomBIDSFile): """Represent a custom parcellation files in the form `sub-<label>_atlas-<label>[_res-<label>]_dseg.nii.gz`.""" def __init__(self): super().__init__(p_suffix="dseg", p_atlas="L2018", p_extension="nii.gz")
[docs] def get_nb_of_regions(self, bids_dir, subject, session=None, debug=True): """Return the number of regions by reading its associated TSV side car file describing the nodes. Parameters ---------- bids_dir: str BIDS root directory subject: str Subject filename entity e.g. "sub-01" session: str Session filename entity e.g. "ses-01" if applicable (Default: None) debug: bool Debug mode (Extra outputed messages) if `True` """ # Build path to BIDS TSV side car of the parcellation file parc_filepath = self.get_filename_path( base_dir=os.path.join( bids_dir, "derivatives", self.toolbox_derivatives_dir), subject=subject, session=session, debug=debug ) + '.tsv' if os.path.exists(parc_filepath): if debug: # pragma: no cover print(f" .. DEBUG : Open {parc_filepath} to get number of regions") with open(parc_filepath) as file: tsv_file = csv.reader(file, delimiter="\t") nb_of_regions = len(list(tsv_file)) - 1 # Remove 1 to account for the header (# of lines - 1 = # regions) if debug: # pragma: no cover print(f" .. DEBUG : Number of regions = {nb_of_regions}") return nb_of_regions else: return None
[docs]class CustomBrainMaskBIDSFile(CustomBIDSFile): """Represent a custom brain mask in the form `sub-<label>_desc-brain_mask.nii.gz`.""" def __init__(self): super().__init__(p_suffix="mask", p_desc="brain", p_extension="nii.gz")
[docs]class CustomWMMaskBIDSFile(CustomBIDSFile): """Represent a custom white-matter mask in the form `sub-<label>_label-WM_dseg.nii.gz`.""" def __init__(self): super().__init__(p_suffix="dseg", p_label="WM", p_extension="nii.gz")
[docs]class CustomGMMaskBIDSFile(CustomBIDSFile): """Represent a custom gray-matter mask in the form `sub-<label>_label-GM_dseg.nii.gz`.""" def __init__(self): super().__init__(p_suffix="dseg", p_label="GM", p_extension="nii.gz")
[docs]class CustomCSFMaskBIDSFile(CustomBIDSFile): """Represent a custom CSF mask in the form `sub-<label>_label-CSF_dseg.nii.gz`.""" def __init__(self): super().__init__(p_suffix="dseg", p_label="CSF", p_extension="nii.gz")
[docs]class CustomAparcAsegBIDSFile(CustomBIDSFile): """Represent a custom BIDS-formatted Freesurfer aparc+aseg file in the form `sub-<label>_desc-aparcaseg_dseg.nii.gz`.""" def __init__(self): super().__init__(p_suffix="dseg", p_desc="aparcaseg", p_extension="nii.gz")