diff --git a/.github/workflows/python-testing.yml b/.github/workflows/python-testing.yml index c5ab746a..e411ba21 100644 --- a/.github/workflows/python-testing.yml +++ b/.github/workflows/python-testing.yml @@ -77,5 +77,11 @@ jobs: - name: Test cropseg bids, with path override, left hemi run: | $HIPPUNFOLD - test_out participant -np --modality cropseg --path_cropseg test_data/data_cropseg_1hemi/sub-{subject}_hemi-{hemi}_dseg.nii.gz --hemi L + - name: Test T2w with T1w template registration + run: | + $HIPPUNFOLD test_data/bids_singleT2w test_out participant -np --modality T2w --t1_reg_template + - name: Test T2w with additional T1w output space + run: | + $HIPPUNFOLD test_data/bids_singleT2w test_out participant -np --modality T2w --output_space T1w diff --git a/docs/tutorials/standardBIDS.md b/docs/tutorials/standardBIDS.md index daeae7b4..f307b104 100644 --- a/docs/tutorials/standardBIDS.md +++ b/docs/tutorials/standardBIDS.md @@ -20,18 +20,19 @@ prefixing the command with `singularity run`. This will expect └── sub-002/ ... +The `--modality` flag is required to specify which input image type should be used and in most cases, T1w should be most robust (though other types are supported!). + The T1w image is used to register to a standardized template (CITI168), making it possible to reorient, upsample, and crop around the left and right hippocampi (this is referred to within -HippUnfold as `space-corobl`). Note that only the T1w image needs to -have a whole-brain field of view. By default, any additional input images -are coregistered and preprocessed, but this can be skipped with the -flags `--skip_coreg` and `--skip_preproc`, repsectively. +HippUnfold as `space-corobl`). Note that the T1w image should +have a whole-brain field of view. More examples of possible BIDS-compliant datasets can be found in [hippunfold/test\_data/](https://github.com/khanlab/hippunfold/tree/master/test_data). ## Different input modalities + By default, HippUnfold expects the `PATH_TO_BIDS_DIR` to contain at least one T1w file for segmenting intrahippocampal structures like the SRLM. However, we have @@ -39,18 +40,20 @@ also provided models trained with T1w, T2w, or DWI data, or, users can input their own custom manual segmentations for unfolding, which can be specified with the `--modality` flag. For example: - hippunfold PATH_TO_BIDS_DIR PATH_TO_OUTPUT_DIR participant --modality T1w + hippunfold PATH_TO_BIDS_DIR PATH_TO_OUTPUT_DIR participant --modality T2w -would work for a dataset with only T1w images, like this one: +would work for a dataset with only T2w images, like this one: PATH_TO_BIDS_DIR/ └── sub-001/ └── anat/ - └── sub-001_T1w.nii.gz + └── sub-001_T2w.nii.gz ... -Note that specifying a manual segmentation (eg. `--modality segT1w`) -expects to additionally find a file with the suffix `_dseg` which should +Note that in this case, registration to a T2w CITI168 template will be performed with the input T2w image. In some cases it may be preferrable to use a T1w image for registration to the standard CITI168 template. A T1w image can be registered to both the input T2w and T1w CITI168 template with the `--t1_reg_template` flag. This is typically most robust as long as a full brain FOV T1w image is available. If this registering is still failing then it may be improved with the `--rigid-reg-template` flag. + +Specifying a manual segmentation (eg. `--modality segT1w`) +expects to additionally find an input file with the suffix `_dseg` which should contain labels following the protocol outlined [here](https://ars.els-cdn.com/content/image/1-s2.0-S1053811917309977-mmc1.pdf). More details are provided on using manual segmentations on the following @@ -84,17 +87,4 @@ This will search for any any files following the naming scheme and fill in `{subject}` IDs for any files it can. Alternatively, `{subject}` IDs can be provided in a list with the `--participant_label` flag. -## No T1w images - -It is difficult to automatically reorient and crop images appropriately -without a whole-brain T1w image, which we recommend collecting as a part -of any acquisition protocol when possible. However, if this is not -possible, the T2w image may be used instead by specifying it as the T1w -with the `--path_T1w` flag. This registration may fail, and is -especially likely to fail if the T2w images are not whole-brain. This -can sometimes be ameliorated with the `--rigid_reg_template` flag. -Alternatively, if you do not have a standard T1w scan, consider manually -orienting and/or cropping your data and following the examples outlined -in the [Specialized scans -tutorial](specializedScans.md). diff --git a/hippunfold/config/snakebids.yml b/hippunfold/config/snakebids.yml index 731529f3..2bb38929 100644 --- a/hippunfold/config/snakebids.yml +++ b/hippunfold/config/snakebids.yml @@ -134,7 +134,6 @@ parse_args: - segT1w - segT2w - cropseg - - neonateT1w --derivatives: help: 'Path to the derivatives folder (e.g. for finding manual segs) (default: %(default)s) ' @@ -179,7 +178,10 @@ parse_args: default: 'CITI168' help: 'Set the template to use for registration to coronal oblique. (default: %(default)s)' - + --t1_reg_template: + help: 'Use T1w to register to template space, instead of the segmentation modality. Note: this was the default behavior prior to v1.0.0. (default: %(default)s)' + default: false + action: store_true --use_gpu: help: 'Enable gpu for inference by setting resource gpus=1 in run_inference rule (default: %(default)s)' @@ -193,12 +195,9 @@ parse_args: --output_spaces: choices: - - 'cropT1w' - - 'corobl' - default: - - 'cropT1w' + - 'T1w' nargs: '+' - help: 'Sets the output space for results (default: %(default)s)' + help: 'Sets additional output spaces for results (default: %(default)s)' --output_density: choices: @@ -261,10 +260,11 @@ singularity: ants: 'docker://kaczmarj/ants:2.3.4' xfm_identity: resources/identity_xfm.txt -template: dHCP +template: CITI168 template_files: CITI168: T1w: resources/CITI168/T1w_head_700um.nii.gz + T2w: resources/CITI168/T2w_head_700um.nii.gz xfm_corobl: resources/CITI168/CoronalOblique_rigid.txt crop_ref: resources/CITI168/T2w_300umCoronalOblique_hemi-{hemi}.nii.gz crop_refT1w: resources/CITI168/T1w_300umCoronalOblique_hemi-{hemi}.nii.gz @@ -399,7 +399,6 @@ laplace_labels: output_spaces: - - cropT1w participant_label: exclude_participant_label: @@ -418,4 +417,5 @@ skip_coreg: False keep_work: False skip_inject_template_labels: False force_nnunet_model: False -root: results \ No newline at end of file +t1_reg_template: False +root: results diff --git a/hippunfold/resources/CITI168/T2w_head_700um.nii.gz b/hippunfold/resources/CITI168/T2w_head_700um.nii.gz new file mode 100644 index 00000000..022c4d34 Binary files /dev/null and b/hippunfold/resources/CITI168/T2w_head_700um.nii.gz differ diff --git a/hippunfold/workflow/Snakefile b/hippunfold/workflow/Snakefile index a3af7346..97efe6fa 100644 --- a/hippunfold/workflow/Snakefile +++ b/hippunfold/workflow/Snakefile @@ -7,21 +7,63 @@ from snakebids import bids configfile: "config/snakebids.yml" -# make adjustments to input params: -# right now hippb500 or cropseg cannot use T1w output space -if config["modality"] == "hippb500" or config["modality"] == "cropseg": - config["output_spaces"] = ["corobl"] +# consider moving this logic to another smk file to clean-up -if config["skip_inject_template_labels"]: - config["autotop_labels"] = ["hipp"] - -# get list of inputs to limit to (T1w + modalities) limit_to_list = set() -if "cropT1w" in config["output_spaces"]: +ref_spaces = [] +crop_ref_spaces = [] +template_modality = False + +# if no additional output spaces chosen, then empty list: +if config["output_spaces"] == None: + config["output_spaces"] = [] + +# set the lists used for output spaces +if ( + config["modality"] == "T1w" + or config["modality"] == "segT1w" + or "T1w" in config["output_spaces"] +): + ref_spaces.append("T1w") + crop_ref_spaces.append("cropT1w") limit_to_list.add("T1w") +if ( + config["modality"] == "T2w" + or config["modality"] == "segT2w" + or "T2w" in config["output_spaces"] +): + ref_spaces.append("T2w") + crop_ref_spaces.append("cropT2w") + limit_to_list.add("T2w") +if ( + config["modality"] == "hippb500" + or config["modality"] == "cropseg" + or "corobl" in config["output_spaces"] +): + ref_spaces.append("corobl") + crop_ref_spaces.append("corobl") + + +# set the modality to use for template registration +if "T2w" in config["modality"]: + if config["t1_reg_template"]: + limit_to_list.add("T1w") + template_modality = "T1w" + else: + template_modality = "T2w" + +elif "T1w" in config["modality"]: + template_modality = "T1w" + + +# only generate hipp surface if skipping template injection +# i.e. drop dentate +if config["skip_inject_template_labels"]: + config["autotop_labels"] = ["hipp"] -if config["modality"][:3] == "seg": # if modality is segT2w, then add seg and T2w +# add seg (if chosen) and the modality to the inputs +if config["modality"][:3] == "seg": # if modality is segT2w, then add seg limit_to_list.add("seg") limit_to_list.add(config["modality"][3:]) else: @@ -95,7 +137,7 @@ include: "rules/warps.smk" include: "rules/shape_inject.smk" include: "rules/gifti.smk" include: "rules/subfields.smk" -include: "rules/resample_final_to_crop_t1.smk" +include: "rules/resample_final_to_crop_native.smk" rule all: diff --git a/hippunfold/workflow/rules/common.smk b/hippunfold/workflow/rules/common.smk index 1d1d1cd9..4eeeaf1b 100644 --- a/hippunfold/workflow/rules/common.smk +++ b/hippunfold/workflow/rules/common.smk @@ -24,11 +24,6 @@ def get_modality_suffix(modality): def get_final_spec(): - surf_spaces = [] - if "cropT1w" in config["output_spaces"]: - surf_spaces.append("T1w") - if "corobl" in config["output_spaces"]: - surf_spaces.append("corobl") if len(config["hemi"]) == 2: specs = expand( @@ -42,7 +37,7 @@ def get_final_spec(): **config["subj_wildcards"], ), density=config["output_density"], - space=surf_spaces, + space=ref_spaces, autotop=config["autotop_labels"], allow_missing=True, ) @@ -59,7 +54,7 @@ def get_final_spec(): **config["subj_wildcards"], ), density=config["output_density"], - space=surf_spaces, + space=ref_spaces, hemi=config["hemi"], autotop=config["autotop_labels"], allow_missing=True, @@ -79,7 +74,7 @@ def get_final_subfields(): **config["subj_wildcards"], ), hemi=config["hemi"], - space=config["output_spaces"], + space=crop_ref_spaces, allow_missing=True, ) @@ -109,7 +104,7 @@ def get_final_coords(): dir=["AP", "PD", "IO"], autotop=config["autotop_labels"], hemi=config["hemi"], - space=config["output_spaces"], + space=crop_ref_spaces, allow_missing=True, ) ) @@ -129,7 +124,7 @@ def get_final_coords(): desc=[desc_io], dir=["IO"], hemi=config["hemi"], - space=config["output_spaces"], + space=crop_ref_spaces, allow_missing=True, ) ) @@ -137,11 +132,6 @@ def get_final_coords(): def get_final_transforms(): - if "cropT1w" in config["output_spaces"]: - output_ref = "T1w" - else: - output_ref = "corobl" - xfms = [] xfms.extend( @@ -157,7 +147,7 @@ def get_final_transforms(): to="unfold", mode="image", ), - space=output_ref, + space=ref_spaces, autotop=config["autotop_labels"], hemi=config["hemi"], allow_missing=True, @@ -177,31 +167,13 @@ def get_final_transforms(): to="{space}", mode="image", ), - space=output_ref, - autotop=config["autotop_labels"], - hemi=config["hemi"], - allow_missing=True, - ) - ) - xfms.extend( - expand( - bids( - root=root, - datatype="seg", - **config["subj_wildcards"], - label="{autotop}", - suffix="xfm.nii.gz", - hemi="{hemi}", - from_="{space}", - to="unfold", - mode="image", - ), - space=output_ref, + space=ref_spaces, autotop=config["autotop_labels"], hemi=config["hemi"], allow_missing=True, ) ) + xfms.extend( expand( bids( @@ -223,7 +195,7 @@ def get_final_transforms(): def get_final_anat(): anat = [] - if "cropT1w" in config["output_spaces"]: + if "T1w" in ref_spaces or "T2w" in ref_spaces: anat.extend( expand( bids( @@ -237,7 +209,7 @@ def get_final_anat(): hemi="{hemi}", **config["subj_wildcards"], ), - space=config["output_spaces"], + space=crop_ref_spaces, hemi=config["hemi"], allow_missing=True, ) @@ -247,19 +219,26 @@ def get_final_anat(): def get_final_qc(): qc = [] + # right now can only do qc from cropT1w space - if "cropT1w" in config["output_spaces"]: + # output_ref = [] + # if "cropT1w" in config["output_spaces"]: + # output_ref.append("T1w") + # if "cropT2w" in config["output_spaces"]: + # output_ref.append("T2w") + if not template_modality == False: qc.extend( expand( bids( root=root, datatype="qc", suffix="regqc.png", - from_="subject", + from_="{native_modality}", to=config["template"], **config["subj_wildcards"], ), + native_modality=template_modality, allow_missing=True, ) ) @@ -271,11 +250,13 @@ def get_final_qc(): datatype="qc", suffix="dseg.png", desc="subfields", - space="cropT1w", + # space="crop{native_modality}", + space="{native_modality}", hemi="{hemi}", **config["subj_wildcards"], ), hemi=config["hemi"], + native_modality=crop_ref_spaces, allow_missing=True, ) ) @@ -287,7 +268,8 @@ def get_final_qc(): suffix="midthickness.surf.png", den="{density}", desc="subfields", - space="cropT1w", + # space="crop{native_modality}", + space="{native_modality}", hemi="{hemi}", label="{autotop}", **config["subj_wildcards"], @@ -295,38 +277,41 @@ def get_final_qc(): hemi=config["hemi"], autotop=config["autotop_labels"], density=config["output_density"], + native_modality=crop_ref_spaces, allow_missing=True, ) ) - if len(config["hemi"]) == 2: - qc.extend( - expand( - bids( - root=root, - datatype="qc", - desc="subfields", - suffix="volumes.png", - **config["subj_wildcards"], - ), - allow_missing=True, - ) + if len(config["hemi"]) == 2: + qc.extend( + expand( + bids( + root=root, + datatype="qc", + desc="subfields", + space="{native_modality}", + suffix="volumes.png", + **config["subj_wildcards"], + ), + native_modality=ref_spaces, + allow_missing=True, ) + ) - if (config["modality"] == "T1w") or (config["modality"] == "T2w"): - qc.extend( - expand( - bids( - root=root, - datatype="qc", - desc="unetf3d", - suffix="dice.tsv", - hemi="{hemi}", - **config["subj_wildcards"], - ), - hemi=config["hemi"], - allow_missing=True, - ) + if (config["modality"] == "T1w") or (config["modality"] == "T2w"): + qc.extend( + expand( + bids( + root=root, + datatype="qc", + desc="unetf3d", + suffix="dice.tsv", + hemi="{hemi}", + **config["subj_wildcards"], + ), + hemi=config["hemi"], + allow_missing=True, ) + ) return qc diff --git a/hippunfold/workflow/rules/gifti.smk b/hippunfold/workflow/rules/gifti.smk index ad4cfbed..5b39f20c 100644 --- a/hippunfold/workflow/rules/gifti.smk +++ b/hippunfold/workflow/rules/gifti.smk @@ -306,8 +306,8 @@ rule unflip_gii_unfolded: "cp {input.gii} {output.gii}" -# warp from corobl to T1w -rule warp_gii_to_T1w: +# warp from corobl to native +rule warp_gii_to_native: input: gii=bids( root=work, @@ -324,7 +324,7 @@ rule warp_gii_to_T1w: datatype="anat", **config["subj_wildcards"], suffix="xfm.txt", - from_="T1w", + from_="{native_modality}", to="corobl", desc="affine", type_="ras" @@ -335,7 +335,7 @@ rule warp_gii_to_T1w: datatype="surf", den="{density}", suffix="{surfname}.surf.gii", - space="T1w", + space="{native_modality}", hemi="{hemi}", label="{autotop}", **config["subj_wildcards"] @@ -348,7 +348,7 @@ rule warp_gii_to_T1w: "wb_command -surface-apply-affine {input.gii} {input.xfm} {output.gii}" -# morphological features, calculated in T1w space: +# morphological features, calculated in native space: rule calculate_surface_area: input: gii=bids( diff --git a/hippunfold/workflow/rules/preproc_t1.smk b/hippunfold/workflow/rules/preproc_t1.smk index 8add34da..1e3672e1 100644 --- a/hippunfold/workflow/rules/preproc_t1.smk +++ b/hippunfold/workflow/rules/preproc_t1.smk @@ -125,14 +125,16 @@ rule reg_to_template: rule qc_reg_to_template: input: - ref=os.path.join( - workflow.basedir, "..", config["template_files"][config["template"]]["T1w"] + ref=lambda wildcards: os.path.join( + workflow.basedir, + "..", + config["template_files"][config["template"]][wildcards.native_modality], ), flo=bids( root=work, datatype="anat", **config["subj_wildcards"], - suffix="T1w.nii.gz", + suffix="{native_modality}.nii.gz", space=config["template"], desc="affine" ), @@ -143,7 +145,7 @@ rule qc_reg_to_template: datatype="qc", **config["subj_wildcards"], suffix="regqc.png", - from_="subject", + from_="{native_modality}", to=config["template"] ), caption="../report/t1w_template_regqc.rst", @@ -162,7 +164,7 @@ rule convert_template_xfm_ras2itk: datatype="anat", **config["subj_wildcards"], suffix="xfm.txt", - from_="T1w", + from_="{reg_suffix}", to=config["template"], desc="affine", type_="ras" @@ -173,7 +175,7 @@ rule convert_template_xfm_ras2itk: datatype="anat", **config["subj_wildcards"], suffix="xfm.txt", - from_="T1w", + from_="{reg_suffix}", to=config["template"], desc="affine", type_="itk" @@ -230,7 +232,7 @@ rule invert_template_xfm_itk2ras: datatype="anat", **config["subj_wildcards"], suffix="xfm.txt", - from_="T1w", + from_="{native_modality}", to="corobl", desc="affine", type_="itk" @@ -241,7 +243,7 @@ rule invert_template_xfm_itk2ras: datatype="anat", **config["subj_wildcards"], suffix="xfm.txt", - from_="T1w", + from_="{native_modality}", to="corobl", desc="affineInverse", type_="ras" @@ -261,7 +263,7 @@ rule template_xfm_itk2ras: datatype="anat", **config["subj_wildcards"], suffix="xfm.txt", - from_="T1w", + from_="{native_modality}", to="corobl", desc="affine", type_="itk" @@ -272,7 +274,7 @@ rule template_xfm_itk2ras: datatype="anat", **config["subj_wildcards"], suffix="xfm.txt", - from_="T1w", + from_="{native_modality}", to="corobl", desc="affine", type_="ras" diff --git a/hippunfold/workflow/rules/preproc_t2.smk b/hippunfold/workflow/rules/preproc_t2.smk index e49a74f9..9eec9f2f 100644 --- a/hippunfold/workflow/rules/preproc_t2.smk +++ b/hippunfold/workflow/rules/preproc_t2.smk @@ -240,29 +240,110 @@ rule reg_t2_to_t1: "c3d_affine_tool {output.xfm_ras} -oitk {output.xfm_itk}" -# now have t2 to t1 xfm, compose this with t1 to corobl xfm -rule compose_t2_xfm_corobl: +rule reg_t2_to_template: + """register t2 directly to template, instead of using t1""" input: - t2_to_t1=bids( + flo=bids( root=work, datatype="anat", **config["subj_wildcards"], - suffix="xfm.txt", - from_="T2w", - to="T1w", - desc="rigid", - type_="itk" + suffix="T2w.nii.gz", + desc="preproc" + ), + ref=os.path.join( + workflow.basedir, "..", config["template_files"][config["template"]]["T2w"] + ), + xfm_identity=os.path.join(workflow.basedir, "..", config["xfm_identity"]), + params: + cmd=reg_to_template_cmd, + output: + warped_subj=bids( + root=work, + datatype="anat", + **config["subj_wildcards"], + suffix="T2w.nii.gz", + space=config["template"], + desc="affine" ), - t1_to_cor=bids( + xfm_ras=bids( root=work, datatype="anat", **config["subj_wildcards"], suffix="xfm.txt", - from_="T1w", - to="corobl", + from_="T2w", + to=config["template"], desc="affine", - type_="itk" + type_="ras" ), + container: + config["singularity"]["autotop"] + group: + "subj" + shell: + "{params.cmd}" + + +def get_inputs_compose_t2_xfm_corobl(wildcards): + if config["t1_reg_template"]: + # xfm0: t2 to t1 + # xfm1: t1 to corobl + t2_to_t1 = ( + bids( + root=work, + datatype="anat", + **config["subj_wildcards"], + suffix="xfm.txt", + from_="T2w", + to="T1w", + desc="rigid", + type_="itk" + ), + ) + t1_to_cor = ( + bids( + root=work, + datatype="anat", + **config["subj_wildcards"], + suffix="xfm.txt", + from_="T1w", + to="corobl", + desc="affine", + type_="itk" + ), + ) + return {"t2_to_t1": t2_to_t1, "t1_to_cor": t1_to_cor} + + else: + + # xfm0: t2 to template + t2_to_std = ( + bids( + root=work, + datatype="anat", + **config["subj_wildcards"], + suffix="xfm.txt", + from_="T2w", + to=config["template"], + desc="affine", + type_="itk" + ), + ) + + # xfm1: template to corobl + std_to_cor = ( + os.path.join( + workflow.basedir, + "..", + config["template_files"][config["template"]]["xfm_corobl"], + ), + ) + return {"t2_to_std": t2_to_std, "std_to_cor": std_to_cor} + + +# now have t2 to t1 xfm, compose this with t1 to corobl xfm +rule compose_t2_xfm_corobl: + input: + unpack(get_inputs_compose_t2_xfm_corobl), output: t2_to_cor=bids( root=work, diff --git a/hippunfold/workflow/rules/resample_final_to_crop_t1.smk b/hippunfold/workflow/rules/resample_final_to_crop_native.smk similarity index 89% rename from hippunfold/workflow/rules/resample_final_to_crop_t1.smk rename to hippunfold/workflow/rules/resample_final_to_crop_native.smk index 6b5f2310..14b92207 100644 --- a/hippunfold/workflow/rules/resample_final_to_crop_t1.smk +++ b/hippunfold/workflow/rules/resample_final_to_crop_native.smk @@ -7,19 +7,19 @@ rule create_native_crop_ref: datatype="seg", suffix="dseg.nii.gz", desc="subfields", - space="T1w", + space="{native_modality}", hemi="{hemi}", **config["subj_wildcards"] ), params: resample="400%", - pad_to="192x256x256vox", + pad_to="256x256x256vox", output: ref=bids( root=work, datatype="seg", suffix="cropref.nii.gz", - space="T1w", + space="{native_modality}", hemi="{hemi}", **config["subj_wildcards"] ), @@ -47,7 +47,7 @@ rule resample_unet_native_crop: datatype="anat", **config["subj_wildcards"], suffix="xfm.txt", - from_="T1w", + from_="{native_modality}", to="corobl", desc="affine", type_="itk" @@ -56,7 +56,7 @@ rule resample_unet_native_crop: root=work, datatype="seg", suffix="cropref.nii.gz", - space="T1w", + space="{native_modality}", hemi="{hemi}", **config["subj_wildcards"] ), @@ -66,7 +66,7 @@ rule resample_unet_native_crop: datatype="seg", suffix="dseg.nii.gz", desc="unet", - space="cropT1w", + space="crop{native_modality}", hemi="{hemi}", **config["subj_wildcards"] ), @@ -95,7 +95,7 @@ rule resample_postproc_native_crop: datatype="anat", **config["subj_wildcards"], suffix="xfm.txt", - from_="T1w", + from_="{native_modality}", to="corobl", desc="affine", type_="itk" @@ -104,7 +104,7 @@ rule resample_postproc_native_crop: root=work, datatype="seg", suffix="cropref.nii.gz", - space="T1w", + space="{native_modality}", hemi="{hemi}", **config["subj_wildcards"] ), @@ -114,7 +114,7 @@ rule resample_postproc_native_crop: datatype="seg", suffix="dseg.nii.gz", desc="postproc", - space="cropT1w", + space="crop{native_modality}", hemi="{hemi}", **config["subj_wildcards"] ), @@ -143,7 +143,7 @@ rule resample_subfields_native_crop: datatype="anat", **config["subj_wildcards"], suffix="xfm.txt", - from_="T1w", + from_="{native_modality}", to="corobl", desc="affine", type_="itk" @@ -152,7 +152,7 @@ rule resample_subfields_native_crop: root=work, datatype="seg", suffix="cropref.nii.gz", - space="T1w", + space="{native_modality}", hemi="{hemi}", **config["subj_wildcards"] ), @@ -162,7 +162,7 @@ rule resample_subfields_native_crop: datatype="seg", suffix="dseg.nii.gz", desc="subfields", - space="cropT1w", + space="crop{native_modality}", hemi="{hemi}", **config["subj_wildcards"] ), @@ -193,7 +193,7 @@ rule resample_coords_native_crop: datatype="anat", **config["subj_wildcards"], suffix="xfm.txt", - from_="T1w", + from_="{native_modality}", to="corobl", desc="affine", type_="itk" @@ -202,7 +202,7 @@ rule resample_coords_native_crop: root=work, datatype="seg", suffix="cropref.nii.gz", - space="T1w", + space="{native_modality}", hemi="{hemi}", **config["subj_wildcards"] ), @@ -213,7 +213,7 @@ rule resample_coords_native_crop: dir="{dir}", suffix="coords.nii.gz", desc="{desc}", - space="cropT1w", + space="crop{native_modality}", hemi="{hemi}", label="{autotop}", **config["subj_wildcards"] @@ -227,20 +227,20 @@ rule resample_coords_native_crop: "antsApplyTransforms -d 3 --interpolation NearestNeighbor -i {input.nii} -o {output.nii} -r {input.ref} -t [{input.xfm},1]" -rule resample_t1_to_crop: +rule resample_native_to_crop: input: nii=bids( root=root, datatype="anat", **config["subj_wildcards"], desc="preproc", - suffix="T1w.nii.gz" + suffix="{native_modality}.nii.gz" ), ref=bids( root=work, datatype="seg", suffix="cropref.nii.gz", - space="T1w", + space="{native_modality}", hemi="{hemi}", **config["subj_wildcards"] ), @@ -249,8 +249,8 @@ rule resample_t1_to_crop: root=root, datatype="seg", desc="preproc", - suffix="T1w.nii.gz", - space="cropT1w", + suffix="{native_modality}.nii.gz", + space="crop{native_modality}", hemi="{hemi}", **config["subj_wildcards"] ), @@ -273,7 +273,7 @@ def get_xfm_t2_to_t1(): **config["subj_wildcards"], suffix="xfm.txt", from_="T2w", - to="T1w", + to="{native_modality}", desc="rigid", type_="itk" ) @@ -293,7 +293,7 @@ rule resample_t2_to_crop: root=work, datatype="seg", suffix="cropref.nii.gz", - space="T1w", + space="{native_modality}", hemi="{hemi}", **config["subj_wildcards"] ), @@ -308,7 +308,7 @@ rule resample_t2_to_crop: datatype="seg", desc="preproc", suffix="T2w.nii.gz", - space="cropT1w", + space="crop{native_modality}", hemi="{hemi}", **config["subj_wildcards"] ), diff --git a/hippunfold/workflow/rules/subfields.smk b/hippunfold/workflow/rules/subfields.smk index 4f81bbb5..f4eb724d 100644 --- a/hippunfold/workflow/rules/subfields.smk +++ b/hippunfold/workflow/rules/subfields.smk @@ -93,8 +93,8 @@ rule combine_tissue_subfield_labels_corobl: "c3d {input.tissue} -dup {params.remap_dg} -dup {params.remap_srlm} {params.remap_cyst} {input.subfields} -push dg -max -push srlm -max -push cyst -max -type uchar -o {output}" -rule resample_subfields_to_T1w: - """Resampling to T1w native space""" +rule resample_subfields_to_native: + """Resampling to native space""" input: nii=bids( root=work, @@ -110,13 +110,17 @@ rule resample_subfields_to_T1w: datatype="anat", **config["subj_wildcards"], suffix="xfm.txt", - from_="T1w", + from_="{native_modality}", to="corobl", desc="affine", type_="itk" ), ref=bids( - root=work, datatype="anat", **config["subj_wildcards"], suffix="T1w.nii.gz" + root=root, + datatype="anat", + **config["subj_wildcards"], + desc="preproc", + suffix="{native_modality}.nii.gz" ), output: nii=bids( @@ -124,7 +128,7 @@ rule resample_subfields_to_T1w: datatype="seg", suffix="dseg.nii.gz", desc="subfields", - space="T1w", + space="{native_modality,T1w|T2w}", hemi="{hemi}", **config["subj_wildcards"] ), @@ -137,8 +141,8 @@ rule resample_subfields_to_T1w: "antsApplyTransforms -d 3 --interpolation MultiLabel -i {input.nii} -o {output.nii} -r {input.ref} -t [{input.xfm},1]" -rule resample_postproc_to_T1w: - """Resample post-processed tissue seg to T1w""" +rule resample_postproc_to_native: + """Resample post-processed tissue seg to native""" input: nii=bids( root=work, @@ -154,13 +158,17 @@ rule resample_postproc_to_T1w: datatype="anat", **config["subj_wildcards"], suffix="xfm.txt", - from_="T1w", + from_="{native_modality}", to="corobl", desc="affine", type_="itk" ), ref=bids( - root=work, datatype="anat", **config["subj_wildcards"], suffix="T1w.nii.gz" + root=root, + datatype="anat", + **config["subj_wildcards"], + desc="preproc", + suffix="{native_modality}.nii.gz" ), output: nii=bids( @@ -168,7 +176,7 @@ rule resample_postproc_to_T1w: datatype="seg", suffix="dseg.nii.gz", desc="postproc", - space="T1w", + space="{native_modality,T2w|T2w}", hemi="{hemi}", **config["subj_wildcards"] ), @@ -181,8 +189,8 @@ rule resample_postproc_to_T1w: "antsApplyTransforms -d 3 --interpolation MultiLabel -i {input.nii} -o {output.nii} -r {input.ref} -t [{input.xfm},1]" -rule resample_unet_to_T1w: - """Resample unet tissue seg to T1w""" +rule resample_unet_to_native: + """Resample unet tissue seg to native""" input: nii=bids( root=work, @@ -198,13 +206,17 @@ rule resample_unet_to_T1w: datatype="anat", **config["subj_wildcards"], suffix="xfm.txt", - from_="T1w", + from_="{native_modality}", to="corobl", desc="affine", type_="itk" ), ref=bids( - root=work, datatype="anat", **config["subj_wildcards"], suffix="T1w.nii.gz" + root=root, + datatype="anat", + **config["subj_wildcards"], + desc="preproc", + suffix="{native_modality}.nii.gz" ), output: nii=bids( @@ -212,7 +224,7 @@ rule resample_unet_to_T1w: datatype="seg", suffix="dseg.nii.gz", desc="unet", - space="T1w", + space="{native_modality,T1w|T2w}", hemi="{hemi}", **config["subj_wildcards"] ), @@ -234,7 +246,7 @@ rule get_subfield_vols_subj: **config["subj_wildcards"], datatype="seg", hemi="{hemi}", - space="cropT1w", + space="{native_modality}", desc="subfields", suffix="dseg.nii.gz" ), @@ -250,6 +262,7 @@ rule get_subfield_vols_subj: tsv=bids( root=root, datatype="seg", + space="{native_modality}", desc="subfields", suffix="volumes.tsv", **config["subj_wildcards"] @@ -263,6 +276,7 @@ rule plot_subj_subfields: tsv=bids( root=root, datatype="seg", + space="{native_modality}", desc="subfields", suffix="volumes.tsv", **config["subj_wildcards"] @@ -272,6 +286,7 @@ rule plot_subj_subfields: bids( root=root, datatype="qc", + space="{native_modality}", desc="subfields", suffix="volumes.png", **config["subj_wildcards"] @@ -297,7 +312,7 @@ def get_bg_img_for_subfield_qc(wildcards): datatype="seg", desc="preproc", suffix=f"{bg_modality}.nii.gz", - space="cropT1w", + space="crop{native_modality}", hemi="{hemi}", **config["subj_wildcards"], ) @@ -311,7 +326,7 @@ rule qc_subfield: datatype="seg", suffix="dseg.nii.gz", desc="subfields", - space="cropT1w", + space="crop{native_modality}", hemi="{hemi}", **config["subj_wildcards"] ), @@ -322,7 +337,7 @@ rule qc_subfield: datatype="qc", suffix="dseg.png", desc="subfields", - space="cropT1w", + space="crop{native_modality}", hemi="{hemi}", **config["subj_wildcards"] ), @@ -342,7 +357,7 @@ rule qc_subfield_surf: datatype="surf", suffix="midthickness.surf.gii", den="{density}", - space="T1w", + space="{native_modality}", hemi="{hemi}", label="{autotop}", **config["subj_wildcards"] @@ -355,7 +370,7 @@ rule qc_subfield_surf: suffix="midthickness.surf.png", den="{density}", desc="subfields", - space="cropT1w", + space="crop{native_modality}", hemi="{hemi}", label="{autotop}", **config["subj_wildcards"] diff --git a/hippunfold/workflow/rules/warps.smk b/hippunfold/workflow/rules/warps.smk index 6f1959d2..ec2246e4 100644 --- a/hippunfold/workflow/rules/warps.smk +++ b/hippunfold/workflow/rules/warps.smk @@ -451,8 +451,8 @@ rule compose_warps_unfold2corobl_lhemi: "antsApplyTransforms -o [{output.unfold2corobl},1] -r {input.ref} -t {input.flipLR_xfm} -t {input.unfold2native} -i {input.unfold_ref} -v" -rule compose_warps_t1_to_unfold: - """ Compose warps from T1w to unfold """ +rule compose_warps_native_to_unfold: + """ Compose warps from native to unfold """ input: corobl2unfold=bids( root=work, @@ -473,12 +473,12 @@ rule compose_warps_t1_to_unfold: suffix="refvol.nii.gz", **config["subj_wildcards"] ), - t1w2corobl=bids( + native2corobl=bids( root=work, datatype="anat", **config["subj_wildcards"], suffix="xfm.txt", - from_="T1w", + from_="{native_modality}", to="corobl", desc="affine", type_="itk" @@ -491,7 +491,7 @@ rule compose_warps_t1_to_unfold: label="{autotop}", suffix="xfm.nii.gz", hemi="{hemi}", - from_="T1w", + from_="{native_modality}", to="unfold", mode="image" ), @@ -500,11 +500,11 @@ rule compose_warps_t1_to_unfold: group: "subj" shell: - "ComposeMultiTransform 3 {output} -R {input.ref} {input.corobl2unfold} {input.t1w2corobl}" + "ComposeMultiTransform 3 {output} -R {input.ref} {input.corobl2unfold} {input.native2corobl}" -rule compose_warps_unfold_to_cropt1: - """ Compose warps from unfold to cropT1w """ +rule compose_warps_unfold_to_crop_native: + """ Compose warps from unfold to crop native """ input: unfold2corobl=bids( root=root, @@ -521,16 +521,16 @@ rule compose_warps_unfold_to_cropt1: root=work, datatype="seg", suffix="cropref.nii.gz", - space="T1w", + space="{native_modality}", hemi="{hemi}", **config["subj_wildcards"] ), - t1w2corobl=bids( + native2corobl=bids( root=work, datatype="anat", **config["subj_wildcards"], suffix="xfm.txt", - from_="T1w", + from_="{native_modality}", to="corobl", desc="affine", type_="itk" @@ -544,7 +544,7 @@ rule compose_warps_unfold_to_cropt1: **config["subj_wildcards"] ), output: - unfold2cropt1=bids( + unfold2cropnative=bids( root=root, datatype="seg", **config["subj_wildcards"], @@ -552,7 +552,7 @@ rule compose_warps_unfold_to_cropt1: suffix="xfm.nii.gz", hemi="{hemi}", from_="unfold", - to="T1w", + to="{native_modality}", mode="image" ), container: @@ -560,4 +560,4 @@ rule compose_warps_unfold_to_cropt1: group: "subj" shell: - "antsApplyTransforms -o [{output.unfold2cropt1},1] -r {input.ref} -t [{input.t1w2corobl},1] -t {input.unfold2corobl} -i {input.unfold_ref} -v" + "antsApplyTransforms -o [{output.unfold2cropnative},1] -r {input.ref} -t [{input.native2corobl},1] -t {input.unfold2corobl} -i {input.unfold_ref} -v"