-
Notifications
You must be signed in to change notification settings - Fork 47
ENH: Add SpatialReference field to some outputs #513
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
base: master
Are you sure you want to change the base?
Changes from all commits
b5111a8
bf78983
b265c2d
27878f8
15f7fc8
d3f4e63
c8633d3
7b3edfc
f02d4d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| """BIDS-related interfaces.""" | ||
|
|
||
| from pathlib import Path | ||
|
|
||
| from bids.utils import listify | ||
| from nipype.interfaces.base import ( | ||
| DynamicTraitedSpec, | ||
| SimpleInterface, | ||
| TraitedSpec, | ||
| isdefined, | ||
| traits, | ||
| ) | ||
| from nipype.interfaces.io import add_traits | ||
| from nipype.interfaces.utility.base import _ravel | ||
|
|
||
| from ..utils.bids import _find_nearest_path | ||
|
|
||
|
|
||
| class _BIDSURIInputSpec(DynamicTraitedSpec): | ||
| dataset_links = traits.Dict(mandatory=True, desc='Dataset links') | ||
| out_dir = traits.Str(mandatory=True, desc='Output directory') | ||
|
|
||
|
|
||
| class _BIDSURIOutputSpec(TraitedSpec): | ||
| out = traits.List( | ||
| traits.Str, | ||
| desc='BIDS URI(s) for file', | ||
| ) | ||
|
|
||
|
|
||
| class BIDSURI(SimpleInterface): | ||
| """Convert input filenames to BIDS URIs, based on links in the dataset. | ||
|
|
||
| This interface can combine multiple lists of inputs. | ||
| """ | ||
|
|
||
| input_spec = _BIDSURIInputSpec | ||
| output_spec = _BIDSURIOutputSpec | ||
|
|
||
| def __init__(self, numinputs=0, **inputs): | ||
| super().__init__(**inputs) | ||
| self._numinputs = numinputs | ||
| if numinputs >= 1: | ||
| input_names = [f'in{i + 1}' for i in range(numinputs)] | ||
| else: | ||
| input_names = [] | ||
| add_traits(self.inputs, input_names) | ||
|
|
||
| def _run_interface(self, runtime): | ||
| inputs = [getattr(self.inputs, f'in{i + 1}') for i in range(self._numinputs)] | ||
| in_files = listify(inputs) | ||
| in_files = _ravel(in_files) | ||
| # Remove undefined inputs | ||
| in_files = [f for f in in_files if isdefined(f)] | ||
| # Convert the dataset links to BIDS URI prefixes | ||
| updated_keys = {f'bids:{k}:': Path(v) for k, v in self.inputs.dataset_links.items()} | ||
| updated_keys['bids::'] = Path(self.inputs.out_dir) | ||
| # Convert the paths to BIDS URIs | ||
| out = [_find_nearest_path(updated_keys, f) for f in in_files] | ||
| self._results['out'] = out | ||
|
|
||
| return runtime |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,7 +33,12 @@ | |
| from niworkflows.interfaces.utility import KeySelect | ||
|
|
||
| from ..interfaces import DerivativesDataSink | ||
| from ..interfaces.templateflow import TemplateFlowSelect, fetch_template_files | ||
| from ..interfaces.bids import BIDSURI | ||
| from ..interfaces.templateflow import ( | ||
| TemplateFlowReference, | ||
| TemplateFlowSelect, | ||
| fetch_template_files, | ||
| ) | ||
|
|
||
| if ty.TYPE_CHECKING: | ||
| from niworkflows.utils.spaces import SpatialReferences | ||
|
|
@@ -929,6 +934,7 @@ def init_ds_anat_volumes_wf( | |
| *, | ||
| bids_root: str, | ||
| output_dir: str, | ||
| dataset_links: dict[str, str] | None = None, | ||
| name='ds_anat_volumes_wf', | ||
| tpm_labels=BIDS_TISSUE_ORDER, | ||
| ) -> pe.Workflow: | ||
|
|
@@ -958,6 +964,26 @@ def init_ds_anat_volumes_wf( | |
| raw_sources = pe.Node(niu.Function(function=_bids_relative), name='raw_sources') | ||
| raw_sources.inputs.bids_root = bids_root | ||
|
|
||
| spatial_reference = pe.Node( | ||
| TemplateFlowReference(), | ||
| name='spatial_reference', | ||
| ) | ||
|
Comment on lines
+967
to
+970
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm worried this is going to interfere with passing
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should update fMRIPrep to point to the website when it's a built-in template, but I could pass the dataset_links into this node to use a local path if templateflow is a key in the dictionary and doesn't start with
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One reason I was leaning toward the website is that the CIFTI SpatialReference dictionary uses the website. If we do commit to using the local templateflow location then we should change that as well. |
||
|
|
||
| dataset_links = dataset_links.copy() or {} | ||
| if 'bids' not in dataset_links: | ||
| dataset_links['bids'] = str(output_dir) | ||
| if 'templateflow' not in dataset_links: | ||
| dataset_links['templateflow'] = 'https://templateflow.s3.amazonaws.com' | ||
|
|
||
| spatial_reference_uri = pe.Node( | ||
| BIDSURI( | ||
| numinputs=1, | ||
| dataset_links=dataset_links, | ||
| out_dir=str(output_dir), | ||
| ), | ||
| name='spatial_reference_uri', | ||
| ) | ||
|
|
||
| gen_ref = pe.Node(GenerateSamplingReference(), name='gen_ref', mem_gb=0.01) | ||
|
|
||
| # Mask T1w preproc images | ||
|
|
@@ -1018,9 +1044,12 @@ def init_ds_anat_volumes_wf( | |
| ds_std_tpms.inputs.label = tpm_labels | ||
|
|
||
| workflow.connect([ | ||
| (inputnode, spatial_reference, [('ref_file', 'template')]), | ||
| (spatial_reference, spatial_reference_uri, [('uri', 'in1')]), | ||
| (inputnode, gen_ref, [ | ||
| ('ref_file', 'fixed_image'), | ||
| (('resolution', _is_native), 'keep_native'), | ||
| ('anat_preproc', 'moving_image'), | ||
| ]), | ||
| (inputnode, mask_anat, [ | ||
| ('anat_preproc', 'in_file'), | ||
|
|
@@ -1030,11 +1059,14 @@ def init_ds_anat_volumes_wf( | |
| (inputnode, anat2std_mask, [('anat_mask', 'input_image')]), | ||
| (inputnode, anat2std_dseg, [('anat_dseg', 'input_image')]), | ||
| (inputnode, anat2std_tpms, [('anat_tpms', 'input_image')]), | ||
| (inputnode, gen_ref, [('anat_preproc', 'moving_image')]), | ||
| (anat2std_t1w, ds_std_t1w, [('output_image', 'in_file')]), | ||
| (spatial_reference_uri, ds_std_t1w, [(('out', _pop), 'SpatialReference')]), | ||
| (anat2std_mask, ds_std_mask, [('output_image', 'in_file')]), | ||
| (spatial_reference_uri, ds_std_mask, [(('out', _pop), 'SpatialReference')]), | ||
| (anat2std_dseg, ds_std_dseg, [('output_image', 'in_file')]), | ||
| (spatial_reference_uri, ds_std_dseg, [(('out', _pop), 'SpatialReference')]), | ||
| (anat2std_tpms, ds_std_tpms, [('output_image', 'in_file')]), | ||
| (spatial_reference_uri, ds_std_tpms, [(('out', _pop), 'SpatialReference')]), | ||
| ]) # fmt:skip | ||
|
|
||
| workflow.connect( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Presumably this is going to return custom templates found in TF_LAYOUT, so I'm not sure that the
elsebranch will ever get hit.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh... I had hoped that
get_templateswould only return built-in templates, but that makes sense. Is there any way to distinguish built-in templates from custom ones?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. I suppose we could inspect the skeleton in the templateflow client.
That's very much unsupported API, but it's doable.