From 4f03f32c0dfd7e795b56e445f90fa1d35a9d5fc6 Mon Sep 17 00:00:00 2001 From: Matt Cieslak Date: Mon, 25 Nov 2024 16:40:28 -0500 Subject: [PATCH 1/2] initial commit [skip ci] --- qsirecon/data/recon_scalar_bids_config.json | 41 ++++++++++++++++++ qsirecon/workflows/recon/scalar_mapping.py | 47 +++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 qsirecon/data/recon_scalar_bids_config.json diff --git a/qsirecon/data/recon_scalar_bids_config.json b/qsirecon/data/recon_scalar_bids_config.json new file mode 100644 index 00000000..3241fc6d --- /dev/null +++ b/qsirecon/data/recon_scalar_bids_config.json @@ -0,0 +1,41 @@ +{ + "name": "qsirecon", + "entities": [ + { + "name": "datatype", + "pattern": "[/\\\\]+(anat|beh|dwi|eeg|figures|fmap|func|ieeg|meg|micr|perf|pet)[/\\\\]+" + }, + { + "name": "cohort", + "pattern": "(?:^|_)cohort-([0-9]+)", + "dtype": "int" + }, + { + "name": "seg", + "pattern": "(?:^|_)seg-([a-zA-Z0-9]+)" + }, + { + "name": "model", + "pattern": "(?:^|_)model-([a-zA-Z0-9]+)" + }, + { + "name": "bundles", + "pattern": "(?:^|_)bundles-([a-zA-Z0-9]+)" + }, + { + "name": "param", + "pattern": "(?:^|_)param-([a-zA-Z0-9]+)" + }, + { + "name": "bundle", + "pattern": "(?:^|_)bundle-([a-zA-Z0-9]+)" + }, + { + "name": "label", + "pattern": "(?:^|_)label-([a-zA-Z0-9]+)" + } + ], + "default_path_patterns": [ + "derivatives/qsirecon-{qsireconsuffix}/sub-{subject}[/ses-{session}]/{datatype|dwi}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_dir-{direction}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_seg-{seg}][_model-{model}][_bundles-{bundles}][_param-{param}][_bundle-{bundle}][_label-{label}][_desc-{desc}]_{suffix}.{extension|nii.gz}" + ] +} diff --git a/qsirecon/workflows/recon/scalar_mapping.py b/qsirecon/workflows/recon/scalar_mapping.py index bd277696..22a650a7 100644 --- a/qsirecon/workflows/recon/scalar_mapping.py +++ b/qsirecon/workflows/recon/scalar_mapping.py @@ -12,10 +12,12 @@ import logging +from bids.layout import BIDSLayout import nipype.interfaces.utility as niu import nipype.pipeline.engine as pe from niworkflows.engine.workflows import LiterateWorkflow as Workflow +from ... import config from ...interfaces.bids import DerivativesDataSink from ...interfaces.interchange import recon_workflow_input_fields from ...interfaces.recon_scalars import ReconScalarsTableSplitterDataSink @@ -23,6 +25,7 @@ from ...utils.bids import clean_datasinks from .utils import init_scalar_output_wf + LOGGER = logging.getLogger("nipype.workflow") @@ -199,3 +202,47 @@ def init_scalar_to_surface_wf( ): """Maps scalar data to a surface.""" raise NotImplementedError() + + +def init_ingress_scalars_wf( + inputs_dict, + name="ingress_scalars", + qsirecon_suffix="", + params={}, +): + """Read in scalars from an input dataset.""" + + from qsirecon.data import load as load_data + + inputnode = pe.Node( + niu.IdentityInterface( + fields=recon_workflow_input_fields, + ), + name="inputnode", + ) + outputnode = pe.Node( + niu.IdentityInterface( + fields=[ + "scalar_image_info", + "recon_scalars", + ] + ), + name="outputnode", + ) + workflow = Workflow(name=name) + dataset_name = params.get("dataset") + if not dataset_name: + raise Exception("Must specify a dataset key in the recon spec parameters") + + dataset_path = config.execution.datasets.get(dataset_name) + if dataset_path is None: + raise Exception( + f"Dataset {dataset_name} is not available in the specified " + "inputs datasets. Either change the recon spec yaml file or " + f"include --datasets {dataset_name}=/path/to/dataset on the command line." + ) + scalars_cfg = load_data("recon_scalars_bids_config.json") + layout = BIDSLayout(dataset_path, config=[scalars_cfg], validate=False) + + + return workflow From 3ad02b4449549a61f785440bed216ee32e262719 Mon Sep 17 00:00:00 2001 From: Matt Cieslak Date: Mon, 2 Dec 2024 17:04:46 -0500 Subject: [PATCH 2/2] continue [skip ci] --- .../ingress_scalars_for_bundles.yaml | 43 +++++++++++++++++++ qsirecon/data/recon_scalar_bids_config.json | 14 +----- qsirecon/workflows/recon/build_workflow.py | 9 +++- qsirecon/workflows/recon/scalar_mapping.py | 10 ++++- 4 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 qsirecon/data/pipelines/ingress_scalars_for_bundles.yaml diff --git a/qsirecon/data/pipelines/ingress_scalars_for_bundles.yaml b/qsirecon/data/pipelines/ingress_scalars_for_bundles.yaml new file mode 100644 index 00000000..93524996 --- /dev/null +++ b/qsirecon/data/pipelines/ingress_scalars_for_bundles.yaml @@ -0,0 +1,43 @@ +name: bundle_scalar_map +anatomical: [] +nodes: + +- action: import_scalars + input: qsirecon + name: dipy_dki_ingress + parameters: + # Whatever was specified with --datasets dipydki=/x/y/z + dataset_name: dipydki + # Do you want a copy of the inputs to go in the output dataset? + # Metadata from the input will be included in the output no matter what + copy_inputs: false + # This is where the tsv will go + qsirecon_suffix: DIPYDKI + # Future idea: Get ALFF/REHO/something into ACPC, then resample into each dwi ACPC space + +- action: autotrack + input: dsistudio_gqi + name: autotrackgqi + parameters: + tolerance: 22,26,30 + track_id: Fasciculus,Cingulum,Aslant,Corticos,Thalamic_R,Reticular,Optic,Fornix,Corpus + track_voxel_ratio: 2.0 + yield_rate: 1.0e-06 + qsirecon_suffix: DSIStudio + software: DSI Studio + +- action: bundle_map + input: autotrackgqi + name: bundle_means + scalars_from: + - dipy_dki_ingress + software: qsirecon + +- action: template_map + input: qsirecon + name: template_map + parameters: + interpolation: NearestNeighbor + scalars_from: + - dipy_dki + software: qsirecon diff --git a/qsirecon/data/recon_scalar_bids_config.json b/qsirecon/data/recon_scalar_bids_config.json index 3241fc6d..a83630a8 100644 --- a/qsirecon/data/recon_scalar_bids_config.json +++ b/qsirecon/data/recon_scalar_bids_config.json @@ -1,10 +1,6 @@ { "name": "qsirecon", "entities": [ - { - "name": "datatype", - "pattern": "[/\\\\]+(anat|beh|dwi|eeg|figures|fmap|func|ieeg|meg|micr|perf|pet)[/\\\\]+" - }, { "name": "cohort", "pattern": "(?:^|_)cohort-([0-9]+)", @@ -14,10 +10,6 @@ "name": "seg", "pattern": "(?:^|_)seg-([a-zA-Z0-9]+)" }, - { - "name": "model", - "pattern": "(?:^|_)model-([a-zA-Z0-9]+)" - }, { "name": "bundles", "pattern": "(?:^|_)bundles-([a-zA-Z0-9]+)" @@ -29,13 +21,9 @@ { "name": "bundle", "pattern": "(?:^|_)bundle-([a-zA-Z0-9]+)" - }, - { - "name": "label", - "pattern": "(?:^|_)label-([a-zA-Z0-9]+)" } ], "default_path_patterns": [ - "derivatives/qsirecon-{qsireconsuffix}/sub-{subject}[/ses-{session}]/{datatype|dwi}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_dir-{direction}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_seg-{seg}][_model-{model}][_bundles-{bundles}][_param-{param}][_bundle-{bundle}][_label-{label}][_desc-{desc}]_{suffix}.{extension|nii.gz}" + "sub-{subject}[/ses-{session}]/{datatype|dwi}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_dir-{direction}][_rec-{reconstruction}][_run-{run}][_space-{space}][_cohort-{cohort}][_seg-{seg}][_model-{model}][_bundles-{bundles}][_param-{param}][_bundle-{bundle}][_label-{label}][_desc-{desc}]_{suffix}.{extension|nii.gz}" ] } diff --git a/qsirecon/workflows/recon/build_workflow.py b/qsirecon/workflows/recon/build_workflow.py index cc891402..2280e360 100644 --- a/qsirecon/workflows/recon/build_workflow.py +++ b/qsirecon/workflows/recon/build_workflow.py @@ -27,7 +27,12 @@ from .scalar_mapping import init_scalar_to_bundle_wf, init_scalar_to_template_wf from .steinhardt import init_steinhardt_order_param_wf from .tortoise import init_tortoise_estimator_wf -from .utils import init_conform_dwi_wf, init_discard_repeated_samples_wf, init_test_wf +from .utils import ( + init_conform_dwi_wf, + init_discard_repeated_samples_wf, + init_test_wf, + init_import_scalars_wf, +) def _check_repeats(nodelist): @@ -286,6 +291,8 @@ def workflow_from_spec(inputs_dict, node_spec): return init_scalar_to_template_wf(**kwargs) if node_spec["action"] == "test_workflow": return init_test_wf(**kwargs) + if node_spec["action"] == "import_scalars": + return init_import_scalars_wf(**kwargs) raise Exception("Unknown node %s" % node_spec) diff --git a/qsirecon/workflows/recon/scalar_mapping.py b/qsirecon/workflows/recon/scalar_mapping.py index 22a650a7..2960c972 100644 --- a/qsirecon/workflows/recon/scalar_mapping.py +++ b/qsirecon/workflows/recon/scalar_mapping.py @@ -204,9 +204,9 @@ def init_scalar_to_surface_wf( raise NotImplementedError() -def init_ingress_scalars_wf( +def init_import_scalars_wf( inputs_dict, - name="ingress_scalars", + name="import_scalars", qsirecon_suffix="", params={}, ): @@ -244,5 +244,11 @@ def init_ingress_scalars_wf( scalars_cfg = load_data("recon_scalars_bids_config.json") layout = BIDSLayout(dataset_path, config=[scalars_cfg], validate=False) + # Find all dwimaps that have the same entities (-desc) as the input dwi file + input_dwi_file = config.execution.layout.get(inputs_dict["dwi_file"]) + dwimaps = layout.get(suffix="dwimap", **input_dwi_file.entities) + + # Send them to the scalar gatherer + return workflow