diff --git a/niworkflows/interfaces/bids.py b/niworkflows/interfaces/bids.py index 05240a05fea..1131eb458f3 100644 --- a/niworkflows/interfaces/bids.py +++ b/niworkflows/interfaces/bids.py @@ -47,7 +47,7 @@ SimpleInterface, ) from nipype.interfaces.io import add_traits -from templateflow.api import templates as _get_template_list +import templateflow as tf from ..utils.bids import _init_layout, relative_to_root from ..utils.images import set_consumables, unsafe_write_nifti_header_and_data from ..utils.misc import _copy_any, unlink @@ -57,7 +57,7 @@ BIDS_DERIV_ENTITIES = frozenset({e["name"] for e in _pybids_spec["entities"]}) BIDS_DERIV_PATTERNS = tuple(_pybids_spec["default_path_patterns"]) -STANDARD_SPACES = _get_template_list() +STANDARD_SPACES = tf.api.templates() LOGGER = logging.getLogger("nipype.interface") @@ -576,6 +576,17 @@ def _run_interface(self, runtime): if out_entities.get("resolution") == "native" and out_entities.get("space"): out_entities.pop("resolution", None) + # Expand templateflow resolutions + resolution = out_entities.get("resolution") + space = out_entities.get("space") + if resolution: + # Standard spaces + if space in STANDARD_SPACES: + res = _get_tf_resolution(space, resolution) + else: # TODO: Nonstandard? + res = "Unknown" + self._metadata['Resolution'] = res + if len(set(out_entities["extension"])) == 1: out_entities["extension"] = out_entities["extension"][0] @@ -926,3 +937,38 @@ def _run_interface(self, runtime): ) return runtime + + +def _get_tf_resolution(space: str, resolution: str) -> str: + """ + Query templateflow template information to elaborate on template resolution. + + Examples + -------- + >>> _get_tf_resolution('MNI152NLin2009cAsym', '01') # doctest: +ELLIPSIS + 'Template MNI152NLin2009cAsym (1.0x1.0x1.0 mm^3)...' + >>> _get_tf_resolution('MNI152NLin2009cAsym', '1') # doctest: +ELLIPSIS + 'Template MNI152NLin2009cAsym (1.0x1.0x1.0 mm^3)...' + >>> _get_tf_resolution('MNI152NLin2009cAsym', '10') + 'Unknown' + """ + metadata = tf.api.get_metadata(space) + resolutions = metadata.get('res', {}) + res_meta = None + + # Due to inconsistencies, resolution keys may or may not be zero-padded + padded_res = f'{str(resolution):0>2}' + for r in (resolution, padded_res): + if r in resolutions: + res_meta = resolutions[r] + if res_meta is None: + return "Unknown" + + def _fmt_xyz(coords: list) -> str: + xyz = "x".join([str(c) for c in coords]) + return f"{xyz} mm^3" + + return ( + f"Template {space} ({_fmt_xyz(res_meta['zooms'])})," + f" curated by TemplateFlow {tf.__version__}" + )