Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🎨 Standardize naming for atlases used in RBC #1823

Merged
merged 5 commits into from
Nov 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions CPAC/pipeline/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -1785,12 +1785,12 @@ def ingress_pipeconfig_paths(cfg, rpool, unique_id, creds_path=None):
val = val.replace('${func_resolution}', cfg.registration_workflows[
'functional_registration']['func_registration_to_template'][
'output_resolution'][tag])

if desc:
template_name = lookup_identifier(val)
template_name, _template_desc = lookup_identifier(val)
if template_name:
desc = f"{template_name} - {desc}"
json_info['Description'] = f"{desc} - {val}"
json_info['Description'] = f"{desc} - {val}"
if resolution:
resolution = cfg.get_nested(cfg, res_keys)
json_info['Resolution'] = resolution
Expand Down
8 changes: 5 additions & 3 deletions CPAC/pipeline/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from pathvalidate import sanitize_filename
from voluptuous import All, ALLOW_EXTRA, Any, Capitalize, Coerce, \
ExactSequence, ExclusiveInvalid, In, Length, Lower, \
Match, Maybe, Optional, Range, Required, Schema
Match, Maybe, Optional, Range, Required, Schema, Title
from CPAC import docs_prefix
from CPAC.utils.datatypes import ListFromItem
from CPAC.utils.utils import YAML_BOOLS
Expand Down Expand Up @@ -204,6 +204,8 @@ def str_to_bool1_1(x): # pylint: disable=invalid-name
},
}, dict # TODO: specify other valid ANTs parameters
)]
target_space = All(Coerce(ListFromItem),
[All(Title, In(valid_options['target_space']))])


def permutation_message(key, options):
Expand Down Expand Up @@ -865,7 +867,7 @@ def sanitize(filename):
},
'amplitude_low_frequency_fluctuation': {
'run': bool1_1,
'target_space': [In(valid_options['target_space'])],
'target_space': target_space,
'highpass_cutoff': [float],
'lowpass_cutoff': [float],
},
Expand All @@ -884,7 +886,7 @@ def sanitize(filename):
},
'regional_homogeneity': {
'run': bool1_1,
'target_space': [In(valid_options['target_space'])],
'target_space': target_space,
'cluster_size': In({7, 19, 27}),
},
'post_processing': {
Expand Down
10 changes: 10 additions & 0 deletions CPAC/resources/configs/pipeline_config_rbc-options.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ functional_preproc:
nuisance_corrections:
2-nuisance_regression:

# this is a fork point
# run: [On, Off] - this will run both and fork the pipeline
run: [On]

# Select which nuisance signal corrections to apply
Regressors:
- Name: 36-parameter
Expand Down Expand Up @@ -230,12 +234,18 @@ amplitude_low_frequency_fluctuation:
# Calculate Amplitude of Low Frequency Fluctuations (ALFF) and fractional ALFF (f/ALFF) for all voxels.
run: On

# space: Template or Native
target_space: [Template]

regional_homogeneity:

# ReHo
# Calculate Regional Homogeneity (ReHo) for all voxels.
run: On

# space: Template or Native
target_space: [Template]

network_centrality:

# Calculate Degree, Eigenvector Centrality, or Functional Connectivity Density.
Expand Down
43 changes: 30 additions & 13 deletions CPAC/resources/templates/BIDS_identifiers.tsv
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
/code/CPAC/resources/templates/tpl-MNI152NLin2009cAsym_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_T1w_reference.nii.gz MNI152NLin2009cAsym
/code/CPAC/resources/templates/tpl-MNI152NLin2009cAsym_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_desc-brain_T1w.nii.gz MNI152NLin2009cAsym
/code/CPAC/resources/templates/tpl-MNI152NLin2009cAsym_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_desc-brain_mask.nii.gz MNI152NLin2009cAsym
/code/CPAC/resources/templates/tpl-MNI152NLin2009cAsym_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_desc-fMRIPrep_boldref.nii.gz MNI152NLin2009cAsym
/code/CPAC/resources/templates/tpl-MNI152NLin2009cAsym_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_label-brain_probseg.nii.gz MNI152NLin2009cAsym
/usr/share/fsl/5.0/data/standard/MNI152_T1_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*.nii.gz MNI152NLin6ASym
/usr/share/fsl/5.0/data/standard/MNI152_T1_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_brain.nii.gz MNI152NLin6ASym
/usr/share/fsl/5.0/data/standard/MNI152_T1_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_brain_mask.nii.gz MNI152NLin6ASym
/usr/share/fsl/5.0/data/standard/MNI152_T1_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_brain_mask_dil.nii.gz MNI152NLin6ASym
/usr/share/fsl/5.0/data/standard/MNI152_T1_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_symmetric.nii.gz MNI152NLin6Sym
/usr/share/fsl/5.0/data/standard/MNI152_T1_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_brain_symmetric.nii.gz MNI152NLin6Sym
/usr/share/fsl/5.0/data/standard/MNI152_T1_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_brain_mask_symmetric.nii.gz MNI152NLin6Sym
/usr/share/fsl/5.0/data/standard/MNI152_T1_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_brain_mask_symmetric_dil.nii.gz MNI152NLin6Sym
/code/CPAC/resources/templates/tpl-MNI152NLin2009cAsym_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_T1w_reference.nii.gz MNI152NLin2009cAsym
/code/CPAC/resources/templates/tpl-MNI152NLin2009cAsym_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_desc-brain_T1w.nii.gz MNI152NLin2009cAsym
/code/CPAC/resources/templates/tpl-MNI152NLin2009cAsym_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_desc-brain_mask.nii.gz MNI152NLin2009cAsym
/code/CPAC/resources/templates/tpl-MNI152NLin2009cAsym_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_desc-fMRIPrep_boldref.nii.gz MNI152NLin2009cAsym
/code/CPAC/resources/templates/tpl-MNI152NLin2009cAsym_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_label-brain_probseg.nii.gz MNI152NLin2009cAsym
/usr/share/fsl/5.0/data/standard/MNI152_T1_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*.nii.gz MNI152NLin6ASym
/usr/share/fsl/5.0/data/standard/MNI152_T1_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_brain.nii.gz MNI152NLin6ASym
/usr/share/fsl/5.0/data/standard/MNI152_T1_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_brain_mask.nii.gz MNI152NLin6ASym
/usr/share/fsl/5.0/data/standard/MNI152_T1_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_brain_mask_dil.nii.gz MNI152NLin6ASym
/usr/share/fsl/5.0/data/standard/MNI152_T1_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_symmetric.nii.gz MNI152NLin6Sym
/usr/share/fsl/5.0/data/standard/MNI152_T1_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_brain_symmetric.nii.gz MNI152NLin6Sym
/usr/share/fsl/5.0/data/standard/MNI152_T1_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_brain_mask_symmetric.nii.gz MNI152NLin6Sym
/usr/share/fsl/5.0/data/standard/MNI152_T1_(res-){0,1}[0-9]+(\.[0-9]*){0,1}[a-z]*(x[0-9]+(\.[0-9]*){0,1}[a-z]*)*_brain_mask_symmetric_dil.nii.gz MNI152NLin6Sym
/ndmg_atlases/label/Human/AAL_space-MNI152NLin6_res-2x2x2.nii.gz AAL
/ndmg_atlases/label/Human/Brodmann_space-MNI152NLin6_res-2x2x2.nii.gz Brodmann
/cpac_templates/CC200.nii.gz CC 200
/cpac_templates/CC400.nii.gz CC 400
/ndmg_atlases/label/Human/Glasser_space-MNI152NLin6_res-2x2x2.nii.gz Glasser
/ndmg_atlases/label/Human/Slab907_space-MNI152NLin6_res-2x2x2.nii.gz Slab
/ndmg_atlases/label/Human/HarvardOxfordcort-maxprob-thr25_space-MNI152NLin6_res-2x2x2.nii.gz HOCPA th25
/ndmg_atlases/label/Human/HarvardOxfordsub-maxprob-thr25_space-MNI152NLin6_res-2x2x2.nii.gz HOSPA th25
/ndmg_atlases/label/Human/Juelich_space-MNI152NLin6_res-2x2x2.nii.gz Juelich
/cpac_templates/Schaefer2018_space-FSLMNI152_res-2mm_desc-200Parcels17NetworksOrder.nii.gz Schaefer2018 200p17n
/cpac_templates/Schaefer2018_space-FSLMNI152_res-2mm_desc-300Parcels17NetworksOrder.nii.gz Schaefer2018 300p17n
/cpac_templates/Schaefer2018_space-FSLMNI152_res-2mm_desc-400Parcels17NetworksOrder.nii.gz Schaefer2018 400p17n
/cpac_templates/Schaefer2018_space-FSLMNI152_res-2mm_desc-1000Parcels17NetworksOrder.nii.gz Schaefer2018 1000p17n
/ndmg_atlases/label/Human/Yeo-7_space-MNI152NLin6_res-2x2x2.nii.gz Yeo 7
/ndmg_atlases/label/Human/Yeo-7-liberal_space-MNI152NLin6_res-2x2x2.nii.gz Yeo 7liberal
/ndmg_atlases/label/Human/Yeo-17_space-MNI152NLin6_res-2x2x2.nii.gz Yeo 17
/ndmg_atlases/label/Human/Yeo-17-liberal_space-MNI152NLin6_res-2x2x2.nii.gz Yeo 17liberal
4 changes: 2 additions & 2 deletions CPAC/resources/templates/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
'''Template resources for C-PAC'''
from .lookup_table import lookup_identifier
from .lookup_table import format_identifier, lookup_identifier

__all__ = ['lookup_identifier']
__all__ = ['format_identifier', 'lookup_identifier']
75 changes: 60 additions & 15 deletions CPAC/resources/templates/lookup_table.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,58 @@
'''Utilities for determining BIDS standard template identifiers
# Copyright (C) 2022 C-PAC Developers

# This file is part of C-PAC.

# C-PAC is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.

# C-PAC is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.

# You should have received a copy of the GNU Lesser General Public
# License along with C-PAC. If not, see <https://www.gnu.org/licenses/>.
"""Utilities for determining BIDS standard template identifiers
(https://bids-specification.readthedocs.io/en/stable/99-appendices/08-coordinate-systems.html#standard-template-identifiers)
from in-container template paths'''
import os
import re
import numpy as np
from in-container template paths"""
from os import path as op
from re import search
from typing import Optional, Tuple
from numpy import loadtxt

LOOKUP_TABLE = {row[0]: (row[1], str(row[2]) if row[2] else None) for row in
loadtxt(op.join(op.dirname(__file__), 'BIDS_identifiers.tsv'),
dtype='str', delimiter='\t')}


def format_identifier(identifier: str, desc: Optional[str] = None) -> str:
'''Function to create an identifier string from a name and description

Parameters
----------
identifier : str

desc : str, optional

Returns
-------
str

LOOKUP_TABLE = {row[0]: row[1] for row in
np.loadtxt(os.path.join(os.path.dirname(__file__),
'BIDS_identifiers.tsv'),
dtype='str', delimiter='\t')}
Examples
--------
>>> format_identifier('CC', '200')
'CC_desc-200'
>>> format_identifier('AAL')
'AAL'
'''
if desc:
return f'{identifier}_desc-{desc}'
return identifier


def lookup_identifier(template_path: str) -> str:
def lookup_identifier(template_path: str) -> Tuple[str, None]:
'''Function to return a standard template identifier for a packaged
template, if known. Otherwise, returns the literal string
'template'
Expand All @@ -24,19 +65,23 @@ def lookup_identifier(template_path: str) -> str:
-------
identifier : str

desc : str or None

Examples
--------
>>> lookup_identifier('/usr/share/fsl/5.0/data/standard/'
... 'MNI152_T1_1mm_brain.nii.gz')
'MNI152NLin6ASym'
('MNI152NLin6ASym', None)
>>> lookup_identifier('/code/CPAC/resources/templates/'
... 'tpl-MNI152NLin2009cAsym_res-01_label-brain_'
... 'probseg.nii.gz')
'MNI152NLin2009cAsym'
('MNI152NLin2009cAsym', None)
>>> lookup_identifier('/cpac_templates/chd8_functional_template_sk.nii')
'template'
('template', None)
>>> lookup_identifier('/cpac_templates/CC200.nii.gz')
('CC', '200')
'''
for key, value in LOOKUP_TABLE.items():
if re.search(key, template_path) is not None:
if search(key, template_path) is not None:
return value
return 'template'
return 'template', None
54 changes: 29 additions & 25 deletions CPAC/utils/datasource.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
import csv
import json
import re
from typing import Optional, Tuple
from typing import Tuple
from nipype import logging
from nipype.interfaces import utility as util
from nipype.interfaces.fsl import MultiImageMaths
from CPAC.pipeline import nipype_pipeline_engine as pe
from CPAC.resources.templates.lookup_table import format_identifier, \
lookup_identifier
from CPAC.utils import function
from CPAC.utils.interfaces.function import Function
from CPAC.utils.utils import get_scan_params
Expand Down Expand Up @@ -1016,33 +1017,36 @@ def create_roi_mask_dataflow(masks, wf_name='datasource_roi_mask'):
if mask_file.strip() == '' or mask_file.startswith('#'):
continue

base_file = os.path.basename(mask_file)
name, desc = lookup_identifier(mask_file)

try:
valid_extensions = ['.nii', '.nii.gz']

base_name = [
base_file[:-len(ext)]
for ext in valid_extensions
if base_file.endswith(ext)
][0]

if base_name in mask_dict:
raise ValueError(
'Files with same name not allowed: %s %s' % (
mask_file,
mask_dict[base_name]
)
)
if name == 'template':
base_file = os.path.basename(mask_file)

mask_dict[base_name] = mask_file
try:
valid_extensions = ['.nii', '.nii.gz']

base_name = [
base_file[:-len(ext)]
for ext in valid_extensions
if base_file.endswith(ext)
][0]

except IndexError:
# pylint: disable=raise-missing-from
raise ValueError('Error in spatial_map_dataflow: File '
f'extension of {base_file} not ".nii" or '
'.nii.gz')

except Exception as e:
raise e
else:
base_name = format_identifier(name, desc)

except IndexError as e:
raise Exception('Error in spatial_map_dataflow: '
'File extension not in .nii and .nii.gz')
if base_name in mask_dict:
raise ValueError('Duplicate templates/atlases not allowed: '
f'{mask_file} {mask_dict[base_name]}')

except Exception as e:
raise e
mask_dict[base_name] = mask_file

wf = pe.Workflow(name=wf_name)

Expand Down
2 changes: 1 addition & 1 deletion CPAC/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def create_id_string(unique_id, resource, scan_id=None, template_desc=None,
"""

if atlas_id:
if '_' in atlas_id:
if not (atlas_id.count('_') == 1 and '_desc-' in atlas_id):
atlas_id = atlas_id.replace('_', '')
resource = f'atlas-{atlas_id}_{resource}'

Expand Down