Coregistration¶
The next step is to coregister the anatomical and functional images.
Outputs¶
We want to produce a single NIFTI file per participant that is their anatomical image in alignment with the mean functional image.
We will give the desc entity the label coreg.
We start by creating a new rule (workflow/rules/coreg.smk) that has this output information:
workflow/rules/coreg.smk¶rule coreg:
"Coregisters the anatomical to the mean functional"
output:
anat_img="results/sub-{sub_num}/anat/sub-{sub_num}_desc-coreg_T1w.nii.gz",
For subsequent visualisation, it is also useful to have a copy of the mean functional image after it has been resampled into the grid of the coregistered anatomical image. We add that file as an additional output of the rule:
workflow/rules/coreg.smk¶rule coreg:
"Coregisters the anatomical to the mean functional"
output:
anat_img="results/sub-{sub_num}/anat/sub-{sub_num}_desc-coreg_T1w.nii.gz",
func_img="results/sub-{sub_num}/func/sub-{sub_num}_desc-tmean_grid-anat_bold.nii.gz",
Inputs¶
The coregistration process requires two inputs per participant: their anatomical image and their mean functional image. We enter these as separate items, with their paths given by the output from the relevant rule.
workflow/rules/coreg.smk¶rule coreg:
"Coregisters the anatomical to the mean functional"
input:
anat=rules.acquire_anat.output.img,
func=rules.tmean.output.img,
output:
anat_img="results/sub-{sub_num}/anat/sub-{sub_num}_desc-coreg_T1w.nii.gz",
func_img="results/sub-{sub_num}/func/sub-{sub_num}_desc-tmean_grid-anat_bold.nii.gz",
Parameters¶
Because we will want to rename an intermediate output during execution of this rule, it is helpful for us to be able to access a path’s ‘stem’ (i.e., the filename without its suffix).
We can use the Snakemake helper function subpath to do that; the strip_suffix option allows us to remove the .nii.gz from the path and the basename=True removes the directory information.
workflow/rules/coreg.smk¶rule coreg:
"Coregisters the anatomical to the mean functional"
input:
anat=rules.acquire_anat.output.img,
func=rules.tmean.output.img,
output:
anat_img="results/sub-{sub_num}/anat/sub-{sub_num}_desc-coreg_T1w.nii.gz",
func_img="results/sub-{sub_num}/func/sub-{sub_num}_desc-tmean_grid-anat_bold.nii.gz",
params:
anat_stem=subpath(input.anat, strip_suffix=".nii.gz", basename=True),
Mechanism¶
Container¶
We will use the same AFNI container that we have used in previous rules:
workflow/rules/coreg.smk¶rule coreg:
"Coregisters the anatomical to the mean functional"
input:
anat=rules.acquire_anat.output.img,
func=rules.tmean.output.img,
output:
anat_img="results/sub-{sub_num}/anat/sub-{sub_num}_desc-coreg_T1w.nii.gz",
func_img="results/sub-{sub_num}/func/sub-{sub_num}_desc-tmean_grid-anat_bold.nii.gz",
params:
anat_stem=subpath(input.anat, strip_suffix=".nii.gz", basename=True),
container:
CONTAINER_SOURCES["AFNI"]
Logging¶
As usual, we need to specify the file to store logging information (particularly useful for this rule, given the importance of the coregistration process summary that is printed by AFNI):
workflow/rules/coreg.smk¶rule coreg:
"Coregisters the anatomical to the mean functional"
input:
anat=rules.acquire_anat.output.img,
func=rules.tmean.output.img,
output:
anat_img="results/sub-{sub_num}/anat/sub-{sub_num}_desc-coreg_T1w.nii.gz",
func_img="results/sub-{sub_num}/func/sub-{sub_num}_desc-tmean_grid-anat_bold.nii.gz",
params:
anat_stem=subpath(input.anat, strip_suffix=".nii.gz", basename=True),
container:
CONTAINER_SOURCES["AFNI"]
log:
"logs/coreg/coreg_{sub_num}.txt"
Shell command¶
We will use the align_epi_anat.py AFNI command to run the coregistration and resample the mean functional image:
workflow/rules/coreg.smk¶rule coreg:
"Coregisters the anatomical to the mean functional"
input:
anat=rules.acquire_anat.output.img,
func=rules.tmean.output.img,
output:
anat_img="results/sub-{sub_num}/anat/sub-{sub_num}_desc-coreg_T1w.nii.gz",
func_img="results/sub-{sub_num}/func/sub-{sub_num}_desc-tmean_grid-anat_bold.nii.gz",
params:
anat_stem=subpath(input.anat, strip_suffix=".nii.gz", basename=True),
container:
CONTAINER_SOURCES["AFNI"]
log:
"logs/coreg/coreg_{sub_num}.txt"
shell:
"""
# run the coregistration
align_epi_anat.py \
-anat {input.anat} -epi {input.func} \
-epi_base 0 -giant_move \
> {log} 2>&1
# convert the resulting files into NIFTI format
3dcopy \
{params.anat_stem}_al+orig \
{output.anat_img} \
>> {log} 2>&1
# save a copy of the functional in the anatomical grid
3dresample \
-inset {input.func} \
-master {output.anat_img} \
-prefix {output.func_img} \
>> {log} 2>&1
"""
Shadowing¶
Because this command produces quite a few intermediate outputs, we will again use rule shadowing:
workflow/rules/coreg.smk¶rule coreg:
"Coregisters the anatomical to the mean functional"
input:
anat=rules.acquire_anat.output.img,
func=rules.tmean.output.img,
output:
anat_img="results/sub-{sub_num}/anat/sub-{sub_num}_desc-coreg_T1w.nii.gz",
func_img="results/sub-{sub_num}/func/sub-{sub_num}_desc-tmean_grid-anat_bold.nii.gz",
params:
anat_stem=subpath(input.anat, strip_suffix=".nii.gz", basename=True),
container:
CONTAINER_SOURCES["AFNI"]
log:
"logs/coreg/coreg_{sub_num}.txt"
shadow:
"shallow"
shell:
"""
# run the coregistration
align_epi_anat.py \
-anat {input.anat} -epi {input.func} \
-epi_base 0 -giant_move \
> {log} 2>&1
# convert the resulting files into NIFTI format
3dcopy \
{params.anat_stem}_al+orig \
{output.anat_img} \
>> {log} 2>&1
# save a copy of the functional in the anatomical grid
3dresample \
-inset {input.func} \
-master {output.anat_img} \
-prefix {output.func_img} \
>> {log} 2>&1
"""
Resources¶
We will give Snakemake a bit more RAM allowance for this job:
workflow/rules/coreg.smk¶rule coreg:
"Coregisters the anatomical to the mean functional"
input:
anat=rules.acquire_anat.output.img,
func=rules.tmean.output.img,
output:
anat_img="results/sub-{sub_num}/anat/sub-{sub_num}_desc-coreg_T1w.nii.gz",
func_img="results/sub-{sub_num}/func/sub-{sub_num}_desc-tmean_grid-anat_bold.nii.gz",
params:
anat_stem=subpath(input.anat, strip_suffix=".nii.gz", basename=True),
container:
CONTAINER_SOURCES["AFNI"]
log:
"logs/coreg/coreg_{sub_num}.txt"
shadow:
"shallow"
resources:
mem="4GB",
shell:
"""
# run the coregistration
align_epi_anat.py \
-anat {input.anat} -epi {input.func} \
-epi_base 0 -giant_move \
> {log} 2>&1
# convert the resulting files into NIFTI format
3dcopy \
{params.anat_stem}_al+orig \
{output.anat_img} \
>> {log} 2>&1
# save a copy of the functional in the anatomical grid
3dresample \
-inset {input.func} \
-master {output.anat_img} \
-prefix {output.func_img} \
>> {log} 2>&1
"""
We will also allow it to use two cores when executing each job using the threads directive:
workflow/rules/coreg.smk¶rule coreg:
"Coregisters the anatomical to the mean functional"
input:
anat=rules.acquire_anat.output.img,
func=rules.tmean.output.img,
output:
anat_img="results/sub-{sub_num}/anat/sub-{sub_num}_desc-coreg_T1w.nii.gz",
func_img="results/sub-{sub_num}/func/sub-{sub_num}_desc-tmean_grid-anat_bold.nii.gz",
params:
anat_stem=subpath(input.anat, strip_suffix=".nii.gz", basename=True),
container:
CONTAINER_SOURCES["AFNI"]
log:
"logs/coreg/coreg_{sub_num}.txt"
shadow:
"shallow"
resources:
mem="4GB",
threads: 2
shell:
"""
# run the coregistration
align_epi_anat.py \
-anat {input.anat} -epi {input.func} \
-epi_base 0 -giant_move \
> {log} 2>&1
# convert the resulting files into NIFTI format
3dcopy \
{params.anat_stem}_al+orig \
{output.anat_img} \
>> {log} 2>&1
# save a copy of the functional in the anatomical grid
3dresample \
-inset {input.func} \
-master {output.anat_img} \
-prefix {output.func_img} \
>> {log} 2>&1
"""
Preparing for execution¶
As usual, our next step is to add the new rule file to the Snakefile and adjust the output of the all rule:
workflow/Snakefile¶include: "rules/common.smk"
include: "rules/acquire_anat.smk"
include: "rules/acquire_func.smk"
include: "rules/mot_correct.smk"
include: "rules/tmean.smk"
include: "rules/coreg.smk"
wildcard_constraints:
task="|".join(TASKS)
rule all:
input:
expand(rules.coreg.output.img, sub_num=SUB_NUMS),
Note that we only need to expand this single rule now, given it depends on output from all of the other rules.
Executing the workflow¶
Finally, you can run Snakemake and execute the workflow:
$ uv run snakemake