diff --git a/modules/nf-scil/registration/synthregistration/environment.yml b/modules/nf-scil/registration/synthregistration/environment.yml new file mode 100644 index 00000000..52b962fd --- /dev/null +++ b/modules/nf-scil/registration/synthregistration/environment.yml @@ -0,0 +1,7 @@ +--- +name: "registration_synthregistration" +channels: + - Docker + - Apptainer +dependencies: + - "Freesurfer:synthmorph" diff --git a/modules/nf-scil/registration/synthregistration/main.nf b/modules/nf-scil/registration/synthregistration/main.nf new file mode 100644 index 00000000..8d130b82 --- /dev/null +++ b/modules/nf-scil/registration/synthregistration/main.nf @@ -0,0 +1,64 @@ +process REGISTRATION_SYNTHREGISTRATION { + tag "$meta.id" + label 'process_single' + + container "freesurfer/synthmorph:latest" + containerOptions "--entrypoint ''" + + input: + tuple val(meta), path(moving), path(fixed) + + output: + tuple val(meta), path("*__output_warped.nii.gz"), emit: warped_image + tuple val(meta), path("*__deform_warp.nii.gz"), emit: deform_transform + tuple val (meta), path("*__init_warp.lta"), emit: init_transform + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + + def init = task.ext.init ? "-m " + task.ext.init : "-m affine" + def warp = task.ext.warp ? "-m " + task.ext.warp : "-m deform" + def header = task.ext.header ? "-H" : "" + def threads = task.ext.threads ? "-j " + task.ext.threads : "" + def gpu = task.ext.gpu ? "-g" : "" + def lambda = task.ext.lambda ? "-r " + task.ext.lambda : "" + def steps = task.ext.steps ? "-n " + task.ext.steps : "" + def extent = task.ext.extent ? "-e " + task.ext.extent : "" + def weight = task.ext.weight ? "-w " + task.ext.weight : "" + + """ + export ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=1 + export OMP_NUM_THREADS=1 + export OPENBLAS_NUM_THREADS=1 + + mri_synthmorph ${init} -t ${prefix}__init_warp.lta $moving $fixed + mri_synthmorph ${warp} ${gpu} ${lambda} ${steps} ${extent} ${weight} -i ${prefix}__init_warp.lta -t ${prefix}__deform_warp.nii.gz -o ${prefix}__output_warped.nii.gz $moving $fixed + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + Freesurfer: 7.4 + END_VERSIONS + """ + + stub: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + + """ + mri_synthmorph -h + + touch ${prefix}__output_warped.nii.gz + touch ${prefix}__deform_warp.nii.gz + touch ${prefix}__init_warp.lta + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + Freesurfer: 7.4 + END_VERSIONS + """ +} diff --git a/modules/nf-scil/registration/synthregistration/meta.yml b/modules/nf-scil/registration/synthregistration/meta.yml new file mode 100644 index 00000000..6cbb5563 --- /dev/null +++ b/modules/nf-scil/registration/synthregistration/meta.yml @@ -0,0 +1,58 @@ +--- +name: "registration_synthregistration" +description: Perform registration using SynthMorph from Freesurfer. Outputs transforms in Freesurfer format .lta for affine and .nii.gz (synthmorph also supports .mgz) for deform, both in RAS orientation, which can be converted for ANTs with respectively lta_convert and mri_warp_convert, which support a wide range of conversion formats and orientations. Conversion can be processed using the registration/convert module which can be used successively to this one. +keywords: + - registration + - Brain imaging + - MRI +tools: + - "Freesurfer": + description: "Freesurfer Synthmorph" + homepage: "https://martinos.org/malte/synthmorph/" + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + + - moving: + type: file + description: Nifti volume moving for registration + pattern: "*.{nii,nii.gz}" + + - fixed: + type: file + description: Nifti volume fixed for registration + pattern: "*.{nii,nii.gz}" + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + + - init_transform: + type: file + description: Affine transform for initialization + pattern: "*.{lta}" + + - deform_transform: + type: file + description: Deform transformation + pattern: "*.{nii.gz}" + + - warped_image: + type: file + description: Warped image + pattern: "*.{nii,.nii.gz}" + + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@anroy1" diff --git a/modules/nf-scil/registration/synthregistration/tests/main.nf.test b/modules/nf-scil/registration/synthregistration/tests/main.nf.test new file mode 100644 index 00000000..67f9cdfc --- /dev/null +++ b/modules/nf-scil/registration/synthregistration/tests/main.nf.test @@ -0,0 +1,53 @@ +nextflow_process { + + name "Test Process REGISTRATION_SYNTHREGISTRATION" + script "../main.nf" + process "REGISTRATION_SYNTHREGISTRATION" + + tag "modules" + tag "modules_nfcore" + tag "registration" + tag "registration/synthregistration" + + tag "subworkflows" + tag "subworkflows/load_test_data" + + setup { + run("LOAD_TEST_DATA", alias: "LOAD_DATA") { + script "../../../../../subworkflows/nf-scil/load_test_data/main.nf" + process { + """ + input[0] = Channel.from( [ "freesurfer.zip" ] ) + input[1] = "test.load-test-data" + """ + } + } + } + + test("registration - synthregistration") { + config "./nextflow.config" + when { + process { + """ + input[0] = LOAD_DATA.out.test_data_directory + .map{ test_data_directory -> [ + [ id:'test', single_end:false ], + file("\${test_data_directory}/t1.nii.gz"), + file("\${test_data_directory}/fa.nii.gz") + ]} + """ + } + } + then { + assertAll( + { assert process.success }, + { assert snapshot( + file(process.out.warped_image.get(0).get(1)).name, + file(process.out.deform_transform.get(0).get(1)).name, + file(process.out.init_transform.get(0).get(1)).name, + process.out.versions + ).match() } + ) + } + } +} diff --git a/modules/nf-scil/registration/synthregistration/tests/main.nf.test.snap b/modules/nf-scil/registration/synthregistration/tests/main.nf.test.snap new file mode 100644 index 00000000..0cd56115 --- /dev/null +++ b/modules/nf-scil/registration/synthregistration/tests/main.nf.test.snap @@ -0,0 +1,17 @@ +{ + "registration - synthregistration": { + "content": [ + "test__output_warped.nii.gz", + "test__deform_warp.nii.gz", + "test__init_warp.lta", + [ + "versions.yml:md5,49fb9c85da9f696926d1ab46ef0968fb" + ] + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-06-06T13:03:51.883573" + } +} \ No newline at end of file diff --git a/modules/nf-scil/registration/synthregistration/tests/nextflow.config b/modules/nf-scil/registration/synthregistration/tests/nextflow.config new file mode 100644 index 00000000..41fae205 --- /dev/null +++ b/modules/nf-scil/registration/synthregistration/tests/nextflow.config @@ -0,0 +1,11 @@ +process { + withName: "REGISTRATION_SYNTHREGISTRATION" { + publishDir = { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" } + memory = 20 + ext.init = "affine" + ext.warp = "deform" + ext.threads = 1 + ext.lambda = 0.9 + ext.steps = 9 + } +} diff --git a/modules/nf-scil/registration/synthregistration/tests/tags.yml b/modules/nf-scil/registration/synthregistration/tests/tags.yml new file mode 100644 index 00000000..e29e4f02 --- /dev/null +++ b/modules/nf-scil/registration/synthregistration/tests/tags.yml @@ -0,0 +1,2 @@ +registration/synthregistration: + - "modules/nf-scil/registration/synthregistration/**"