From cb1c1c3dd47bfe8a91c55f8ccba038b7ea0a9798 Mon Sep 17 00:00:00 2001 From: gagnonanthony <79757265+gagnonanthony@users.noreply.github.com> Date: Thu, 28 Mar 2024 19:48:20 +0000 Subject: [PATCH 01/10] [WIP] registration subworkflow + working tests. --- pyproject.toml | 2 +- subworkflows/nf-scil/registration/main.nf | 65 +++++++++++++++++++ subworkflows/nf-scil/registration/meta.yml | 62 ++++++++++++++++++ tests/config/pytest_modules.yml | 13 ++-- .../subworkflows/nf-scil/registration/main.nf | 47 ++++++++++++++ .../nf-scil/registration/nextflow.config | 14 ++++ .../nf-scil/registration/test.yml | 39 +++++++++++ 7 files changed, 237 insertions(+), 5 deletions(-) create mode 100644 subworkflows/nf-scil/registration/main.nf create mode 100644 subworkflows/nf-scil/registration/meta.yml create mode 100644 tests/subworkflows/nf-scil/registration/main.nf create mode 100644 tests/subworkflows/nf-scil/registration/nextflow.config create mode 100644 tests/subworkflows/nf-scil/registration/test.yml diff --git a/pyproject.toml b/pyproject.toml index d5d28873..590fecdb 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ repository = "https://github.com/scilus/nf-scil.git" [tool.poetry.dependencies] python = "<3.13,>=3.9" -nf-core = { git = "https://github.com/AlexVCaron/nf-core-tools.git", branch = "fix/bad_subworkflow_linting" } +nf-core = { git = "https://github.com/gagnonanthony/nf-core-tools.git", branch = "nf-scil-test-fix" } nibabel = "^5.1.0" numpy = "^1.24" requests = "^2.28" diff --git a/subworkflows/nf-scil/registration/main.nf b/subworkflows/nf-scil/registration/main.nf new file mode 100644 index 00000000..db882f6e --- /dev/null +++ b/subworkflows/nf-scil/registration/main.nf @@ -0,0 +1,65 @@ +include { REGISTER_ANATTODWI } from '../../../modules/nf-scil/register/anattodwi/main' +include { REGISTRATION_ANTS } from '../../../modules/nf-scil/registration/ants/main' + +workflow REGISTRATION { + + // ** The subworkflow requires at least ch_image and ch_ref as inputs to ** // + // ** properly perform the registration. Supplying a ch_metric will select ** // + // ** the REGISTER_ANATTODWI module meanwhile NOT supplying a ch_metric ** // + // ** will select the REGISTRATION_ANTS (SyN or SyNQuick) module. ** // + + take: + ch_image // channel: [ val(meta), [ image ] ] + ch_ref // channel: [ val(meta), [ ref ] ] + ch_metric // channel: [ val(meta), [ metric ] ], optional + ch_mask // channel: [ val(meta), [ mask ] ], optional + + main: + + ch_versions = Channel.empty() + + if ( ch_metric ) { + // ** Set up input channel ** // + ch_register = ch_image.combine(ch_ref, by: 0) + .combine(ch_metric, by: 0) + + // ** Registration using AntsRegistration ** // + REGISTER_ANATTODWI ( ch_register ) + ch_versions = ch_versions.mix(REGISTER_ANATTODWI.out.versions.first()) + + // ** Setting outputs ** // + image_warped = REGISTER_ANATTODWI.out.t1_warped + transfo_image = REGISTER_ANATTODWI.out.transfo_image + transfo_trk = REGISTER_ANATTODWI.out.transfo_trk + } + else { + // ** Set up input channel, input are inversed compared to REGISTER_ANATTODWI. ** // + if ( ch_mask ) { + ch_register = ch_ref.combine(ch_image, by: 0) + .combine(ch_mask, by: 0) + } + else { + ch_register = ch_ref.combine(ch_image, by: 0) + .map{ [it[0], it[1], it[2], []] } + } + + // ** Registration using antsRegistrationSyN.sh or antsRegistrationSyNQuick.sh. ** // + // ** Has to be defined in the config file or else the default (SyN) will be ** // + // ** used. ** // + REGISTRATION_ANTS ( ch_register ) + ch_versions = ch_versions.mix(REGISTRATION_ANTS.out.versions.first()) + + // ** Setting outputs ** // + image_warped = REGISTRATION_ANTS.out.image + transfo_image = REGISTRATION_ANTS.out.transfo_image + transfo_trk = REGISTRATION_ANTS.out.transfo_trk + } + + emit: + image_warped = image_warped // channel: [ val(meta), [ image ] ] + transfo_image = transfo_image // channel: [ val(meta), [ warp, affine ] ] + transfo_trk = transfo_trk // channel: [ val(meta), [ inversewarp, inverseaffine ] ] + + versions = ch_versions // channel: [ versions.yml ] +} + diff --git a/subworkflows/nf-scil/registration/meta.yml b/subworkflows/nf-scil/registration/meta.yml new file mode 100644 index 00000000..fb8e3853 --- /dev/null +++ b/subworkflows/nf-scil/registration/meta.yml @@ -0,0 +1,62 @@ +name: "registration" +description: Subworkflow to perform registration between a moving and a fixed image (e.g. T1 -> DWI). +keywords: + - Registration + - Transformations + - DWI + - Anatomical +components: + - register/anattodwi + - registration/ants +input: + - ch_image: + type: file + description: | + The input channel containing the moving image files. + Structure: [ val(meta), path(image) ] + pattern: "*.{nii,nii.gz}" + - ch_ref: + type: file + description: | + The input channel containing the fixed image files. + Structure: [ val(meta), path(ref) ] + pattern: "*.{nii,nii.gz}" + - ch_metric: + type: file + description: | + The input channel containing the metric files for AntsRegistration. + Structure: [ val(meta), path(metric) ] + pattern: "*.{nii,nii.gz}" + - ch_mask: + type: file + description: | + The input channel containing the mask files. + Structure: [ val(meta), path(mask) ] + pattern: "*.{nii,nii.gz}" +output: + - image_warped: + type: file + description: | + Channel containing warped moving images. + Structure: [ val(meta), path(image) ] + pattern: "*.{nii,nii.gz}" + - transfo_image: + type: file + description: | + Channel containing the image transformation files. + Structure: [ val(meta), [ path(warp), path(affine) ] ] + pattern: "*.{nii,nii.gz,mat}" + - transfo_trk: + type: file + description: | + Channel containing the tractogram transformation files. + Structure: [ val(meta), [ path(inversewarp), path(inverseaffine) ] + pattern: "*.{nii,nii.gz,mat}" + - versions: + type: file + description: | + File containing software versions + Structure: [ path(versions.yml) ] + pattern: "versions.yml" +authors: + - "@gagnonanthony" diff --git a/tests/config/pytest_modules.yml b/tests/config/pytest_modules.yml index d2f708a4..27301701 100755 --- a/tests/config/pytest_modules.yml +++ b/tests/config/pytest_modules.yml @@ -18,17 +18,18 @@ denoising/nlmeans: - modules/nf-scil/denoising/nlmeans/** - tests/modules/nf-scil/denoising/nlmeans/** -io/readbids: - - modules/nf-scil/io/readbids/** - - tests/modules/nf-scil/io/readbids/** - image/resample: - modules/nf-scil/image/resample/** - tests/modules/nf-scil/image/resample/** +io/readbids: + - modules/nf-scil/io/readbids/** + - tests/modules/nf-scil/io/readbids/** + preproc/eddy: - modules/nf-scil/preproc/eddy/** - tests/modules/nf-scil/preproc/eddy/** + preproc/gibbs: - modules/nf-scil/preproc/gibbs/** - tests/modules/nf-scil/preproc/gibbs/** @@ -93,6 +94,10 @@ subworkflows/load_test_data: - subworkflows/nf-scil/load_test_data/** - tests/subworkflows/nf-scil/load_test_data/** +subworkflows/register_anattodwi: + - subworkflows/nf-scil/register_anattodwi/** + - tests/subworkflows/nf-scil/register_anattodwi/** + testdata/scilpy: - modules/nf-scil/testdata/scilpy/** - tests/modules/nf-scil/testdata/scilpy/** diff --git a/tests/subworkflows/nf-scil/registration/main.nf b/tests/subworkflows/nf-scil/registration/main.nf new file mode 100644 index 00000000..5bc5470a --- /dev/null +++ b/tests/subworkflows/nf-scil/registration/main.nf @@ -0,0 +1,47 @@ +#!/usr/bin/env nextflow + +nextflow.enable.dsl = 2 + +include { LOAD_TEST_DATA } from '../load_test_data/main' +include { REGISTRATION } from '../../../../subworkflows/nf-scil/registration/main' + +workflow test_register_anattodwi_antsregistration { + + input_fetch = Channel.from( [ "processing.zip" ] ) + LOAD_TEST_DATA ( input_fetch, "test.load-test-data" ) + + ch_image = LOAD_TEST_DATA.out.test_data_directory + .map{ test_data_directory -> [ + [ id:'test', single_end:false ], + file("${test_data_directory}/mni_masked_2x2x2.nii.gz")]} + ch_ref = LOAD_TEST_DATA.out.test_data_directory + .map{ test_data_directory -> [ + [ id:'test', single_end:false ], + file("${test_data_directory}/b0_mean.nii.gz")]} + ch_metric = LOAD_TEST_DATA.out.test_data_directory + .map{ test_data_directory -> [ + [ id:'test', single_end:false ], + file("${test_data_directory}/fa.nii.gz")]} + ch_mask = [] + + REGISTRATION ( ch_image, ch_ref, ch_metric, ch_mask ) +} + +workflow test_register_anattodwi_syn { + + input_fetch = Channel.from( [ "processing.zip" ] ) + LOAD_TEST_DATA ( input_fetch, "test.load-test-data" ) + + ch_image = LOAD_TEST_DATA.out.test_data_directory + .map{ test_data_directory -> [ + [ id:'test', single_end:false ], + file("${test_data_directory}/mni_masked_2x2x2.nii.gz")]} + ch_ref = LOAD_TEST_DATA.out.test_data_directory + .map{ test_data_directory -> [ + [ id:'test', single_end:false ], + file("${test_data_directory}/b0_mean.nii.gz")]} + ch_metric = [] + ch_mask = [] + + REGISTRATION ( ch_image, ch_ref, ch_metric, ch_mask ) +} diff --git a/tests/subworkflows/nf-scil/registration/nextflow.config b/tests/subworkflows/nf-scil/registration/nextflow.config new file mode 100644 index 00000000..614119c0 --- /dev/null +++ b/tests/subworkflows/nf-scil/registration/nextflow.config @@ -0,0 +1,14 @@ +process { + + withName: "REGISTER_ANATTODWI" { + ext.cpus = 1 + publishDir = { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" } + } + + withName: "REGISTRATION_ANTS" { + publishDir = { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" } + ext.quick = true + ext.threads = 1 + } + +} diff --git a/tests/subworkflows/nf-scil/registration/test.yml b/tests/subworkflows/nf-scil/registration/test.yml new file mode 100644 index 00000000..846c3bd5 --- /dev/null +++ b/tests/subworkflows/nf-scil/registration/test.yml @@ -0,0 +1,39 @@ +- name: registration test_register_anattodwi_antsregistration + command: nextflow run ./tests/subworkflows/nf-scil/registration -entry test_register_anattodwi_antsregistration -c ./tests/config/nextflow.config + tags: + - register + - register/anattodwi + - registration + - registration/ants + - subworkflows + - subworkflows/registration + files: + - path: output/register/test__output0GenericAffine.mat + md5sum: 043decee0d131e79a802e3dcc61c22f0 + - path: output/register/test__output1InverseWarp.nii.gz + md5sum: 450c5f85d59a753cb8dd29c8de59905e + - path: output/register/test__output1Warp.nii.gz + md5sum: 4e3131a45dabb26b18122fdf5520ba2c + - path: output/register/test__t1_warped.nii.gz + md5sum: c5a57cfa815caceab567a61d57f07fb4 + +- name: registration test_register_anattodwi_syn + command: nextflow run ./tests/subworkflows/nf-scil/registration -entry test_register_anattodwi_syn -c ./tests/config/nextflow.config + tags: + - register + - register/anattodwi + - registration + - registration/ants + - subworkflows + - subworkflows/registration + files: + - path: output/registration/test__output0InverseAffine.mat + md5sum: 3708568fa59a2e19cc25d733cb6d06e7 + - path: output/registration/test__output0Warp.nii.gz + md5sum: 3661f11f8525ba33b190ecf031f822e9 + - path: output/registration/test__output1GenericAffine.mat + md5sum: ec5a132d6c53daf84218ccc7fa290073 + - path: output/registration/test__output1InverseWarp.nii.gz + md5sum: cc04fdb62bbd99ae92145186843f2cf6 + - path: output/registration/test__warped.nii.gz + md5sum: 9f6678db85757e5aceda21122e89b3a4 From 41233ca0174e026966e45a58eae7a9498d6cee6a Mon Sep 17 00:00:00 2001 From: gagnonanthony <79757265+gagnonanthony@users.noreply.github.com> Date: Thu, 28 Mar 2024 19:53:59 +0000 Subject: [PATCH 02/10] [FIX] editorconfig + prettier --- subworkflows/nf-scil/registration/main.nf | 8 ++++---- subworkflows/nf-scil/registration/meta.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/subworkflows/nf-scil/registration/main.nf b/subworkflows/nf-scil/registration/main.nf index db882f6e..8395f7ea 100644 --- a/subworkflows/nf-scil/registration/main.nf +++ b/subworkflows/nf-scil/registration/main.nf @@ -12,7 +12,7 @@ workflow REGISTRATION { ch_image // channel: [ val(meta), [ image ] ] ch_ref // channel: [ val(meta), [ ref ] ] ch_metric // channel: [ val(meta), [ metric ] ], optional - ch_mask // channel: [ val(meta), [ mask ] ], optional + ch_mask // channel: [ val(meta), [ mask ] ], optional main: @@ -21,7 +21,7 @@ workflow REGISTRATION { if ( ch_metric ) { // ** Set up input channel ** // ch_register = ch_image.combine(ch_ref, by: 0) - .combine(ch_metric, by: 0) + .combine(ch_metric, by: 0) // ** Registration using AntsRegistration ** // REGISTER_ANATTODWI ( ch_register ) @@ -36,7 +36,7 @@ workflow REGISTRATION { // ** Set up input channel, input are inversed compared to REGISTER_ANATTODWI. ** // if ( ch_mask ) { ch_register = ch_ref.combine(ch_image, by: 0) - .combine(ch_mask, by: 0) + .combine(ch_mask, by: 0) } else { ch_register = ch_ref.combine(ch_image, by: 0) @@ -45,7 +45,7 @@ workflow REGISTRATION { // ** Registration using antsRegistrationSyN.sh or antsRegistrationSyNQuick.sh. ** // // ** Has to be defined in the config file or else the default (SyN) will be ** // - // ** used. ** // + // ** used. ** // REGISTRATION_ANTS ( ch_register ) ch_versions = ch_versions.mix(REGISTRATION_ANTS.out.versions.first()) diff --git a/subworkflows/nf-scil/registration/meta.yml b/subworkflows/nf-scil/registration/meta.yml index fb8e3853..6295b11e 100644 --- a/subworkflows/nf-scil/registration/meta.yml +++ b/subworkflows/nf-scil/registration/meta.yml @@ -1,5 +1,5 @@ name: "registration" -description: Subworkflow to perform registration between a moving and a fixed image (e.g. T1 -> DWI). +description: Subworkflow to perform registration between a moving and a fixed image (e.g. T1 -> DWI). keywords: - Registration - Transformations From 390a73f9c3b63827b3fe12bf408ef29b5d5ad17f Mon Sep 17 00:00:00 2001 From: gagnonanthony <79757265+gagnonanthony@users.noreply.github.com> Date: Thu, 28 Mar 2024 19:55:59 +0000 Subject: [PATCH 03/10] [FIX] add subworkflow to pytest_modules --- tests/config/pytest_modules.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/config/pytest_modules.yml b/tests/config/pytest_modules.yml index 27301701..b55dc48e 100755 --- a/tests/config/pytest_modules.yml +++ b/tests/config/pytest_modules.yml @@ -94,7 +94,7 @@ subworkflows/load_test_data: - subworkflows/nf-scil/load_test_data/** - tests/subworkflows/nf-scil/load_test_data/** -subworkflows/register_anattodwi: +subworkflows/registration: - subworkflows/nf-scil/register_anattodwi/** - tests/subworkflows/nf-scil/register_anattodwi/** From 995919986deb2edb0a3efb56fcd85a44a3b68c79 Mon Sep 17 00:00:00 2001 From: gagnonanthony <79757265+gagnonanthony@users.noreply.github.com> Date: Thu, 28 Mar 2024 19:57:08 +0000 Subject: [PATCH 04/10] [FIX] dumb mistake --- tests/config/pytest_modules.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/config/pytest_modules.yml b/tests/config/pytest_modules.yml index b55dc48e..91f7fe25 100755 --- a/tests/config/pytest_modules.yml +++ b/tests/config/pytest_modules.yml @@ -95,8 +95,8 @@ subworkflows/load_test_data: - tests/subworkflows/nf-scil/load_test_data/** subworkflows/registration: - - subworkflows/nf-scil/register_anattodwi/** - - tests/subworkflows/nf-scil/register_anattodwi/** + - subworkflows/nf-scil/registration/** + - tests/subworkflows/nf-scil/registration/** testdata/scilpy: - modules/nf-scil/testdata/scilpy/** From 4acb802ce3ad76b9cebc0bcbe31ba39253aa0b86 Mon Sep 17 00:00:00 2001 From: gagnonanthony <79757265+gagnonanthony@users.noreply.github.com> Date: Tue, 2 Apr 2024 17:10:20 +0000 Subject: [PATCH 05/10] [WIP] Change ANTs seed --- .../subworkflows/nf-scil/registration/nextflow.config | 1 + tests/subworkflows/nf-scil/registration/test.yml | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/subworkflows/nf-scil/registration/nextflow.config b/tests/subworkflows/nf-scil/registration/nextflow.config index 614119c0..70d31ebb 100644 --- a/tests/subworkflows/nf-scil/registration/nextflow.config +++ b/tests/subworkflows/nf-scil/registration/nextflow.config @@ -9,6 +9,7 @@ process { publishDir = { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" } ext.quick = true ext.threads = 1 + ext.random_seed = 44 } } diff --git a/tests/subworkflows/nf-scil/registration/test.yml b/tests/subworkflows/nf-scil/registration/test.yml index 846c3bd5..611d133c 100644 --- a/tests/subworkflows/nf-scil/registration/test.yml +++ b/tests/subworkflows/nf-scil/registration/test.yml @@ -28,12 +28,12 @@ - subworkflows/registration files: - path: output/registration/test__output0InverseAffine.mat - md5sum: 3708568fa59a2e19cc25d733cb6d06e7 + md5sum: b30458b37b728d8ce97890b391ad9df9 - path: output/registration/test__output0Warp.nii.gz - md5sum: 3661f11f8525ba33b190ecf031f822e9 + md5sum: 1cc8863a565c9df46e1a3bb613154a17 - path: output/registration/test__output1GenericAffine.mat - md5sum: ec5a132d6c53daf84218ccc7fa290073 + md5sum: 3946e4d33c15911a9a1d72374fdbe4df - path: output/registration/test__output1InverseWarp.nii.gz - md5sum: cc04fdb62bbd99ae92145186843f2cf6 + md5sum: 987bd5da0a356f1b12194acc4ab85750 - path: output/registration/test__warped.nii.gz - md5sum: 9f6678db85757e5aceda21122e89b3a4 + md5sum: 685cab659941bb735eaded4b4bfc93bf From ee8780d3d91d109c8921fff7e48aca2e960e9682 Mon Sep 17 00:00:00 2001 From: gagnonanthony <79757265+gagnonanthony@users.noreply.github.com> Date: Thu, 11 Apr 2024 13:56:02 +0000 Subject: [PATCH 06/10] [WIP] Answered Alex's comments. --- subworkflows/nf-scil/registration/main.nf | 2 +- subworkflows/nf-scil/registration/meta.yml | 38 ++++++++++++++++------ 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/subworkflows/nf-scil/registration/main.nf b/subworkflows/nf-scil/registration/main.nf index 8395f7ea..73e9d7d7 100644 --- a/subworkflows/nf-scil/registration/main.nf +++ b/subworkflows/nf-scil/registration/main.nf @@ -21,7 +21,7 @@ workflow REGISTRATION { if ( ch_metric ) { // ** Set up input channel ** // ch_register = ch_image.combine(ch_ref, by: 0) - .combine(ch_metric, by: 0) + .combine(ch_metric, by: 0) // ** Registration using AntsRegistration ** // REGISTER_ANATTODWI ( ch_register ) diff --git a/subworkflows/nf-scil/registration/meta.yml b/subworkflows/nf-scil/registration/meta.yml index 6295b11e..6670a1cb 100644 --- a/subworkflows/nf-scil/registration/meta.yml +++ b/subworkflows/nf-scil/registration/meta.yml @@ -1,8 +1,16 @@ name: "registration" -description: Subworkflow to perform registration between a moving and a fixed image (e.g. T1 -> DWI). +description: | + Subworkflow to perform registration between a moving and a fixed image (e.g. T1 -> DWI). + It requires as input at least a moving (ch_image) and a reference image (ch_ref) to properly + perform registration. Two modes are available: + 1) if a metric file is supplied (ch_metric), the subworkflow will use the REGISTER_ANATTODWI + module calling AntsRegistration and + 2) if a metric file is NOT supplied, the subworkflow will use the REGISTRATION_ANTS module calling + antsRegistrationSyN.sh or antsRegistrationSyNQuick.sh. + Typical next steps after this subworkflow would be ANATOMICAL_SEGMENTATION or TRACKING. keywords: - Registration - - Transformations + - Transformation - DWI - Anatomical components: @@ -12,50 +20,60 @@ input: - ch_image: type: file description: | - The input channel containing the moving image files. + The input channel containing the moving image files. Typically your anatomical image to be + registered to dwi space. Structure: [ val(meta), path(image) ] pattern: "*.{nii,nii.gz}" - ch_ref: type: file description: | - The input channel containing the fixed image files. + The input channel containing the fixed image files. Typically a reference image from the dwi + space (e.g. b0 image, etc.). Structure: [ val(meta), path(ref) ] pattern: "*.{nii,nii.gz}" - ch_metric: type: file description: | - The input channel containing the metric files for AntsRegistration. + The input channel containing the metric files. Supplying this channel will make the subworkflow + use the module REGISTER_ANATTODWI calling AntsRegistration using the moving, reference and metric + image. For a T1 -> DWI registration, this is typically a FA map. Structure: [ val(meta), path(metric) ] pattern: "*.{nii,nii.gz}" - ch_mask: type: file description: | - The input channel containing the mask files. + The input channel containing the mask files. Supplying this channel only affect the subworkflow + if the ch_metric is NOT supplied. This channel is only being used if the module called is + REGISTRATION_ANTS, see the description section above for more details. Structure: [ val(meta), path(mask) ] pattern: "*.{nii,nii.gz}" output: - image_warped: type: file description: | - Channel containing warped moving images. + Channel containing warped moving images. Typically, this would be the warped T1 in DWI space. Structure: [ val(meta), path(image) ] pattern: "*.{nii,nii.gz}" - transfo_image: type: file description: | - Channel containing the image transformation files. + Channel containing the image transformation files. This channel contains the necessary transformation + files (warp and affine) to perform the anatomical -> dwi space registration. Those files could be + used in the future to bring anatomical labels into DWI space for connectomics. Structure: [ val(meta), [ path(warp), path(affine) ] ] pattern: "*.{nii,nii.gz,mat}" - transfo_trk: type: file description: | - Channel containing the tractogram transformation files. + Channel containing the tractogram transformation files. This channel contains the necessary transformation + files (inversewarp, inverseaffine) to perform the dwi -> anatomical space registration. Those files could + be used to register tractograms or bundle in the subject's anatomical space. Structure: [ val(meta), [ path(inversewarp), path(inverseaffine) ] pattern: "*.{nii,nii.gz,mat}" - versions: type: file description: | - File containing software versions + File containing software versions used. Structure: [ path(versions.yml) ] pattern: "versions.yml" authors: From 86f3d8f6dd8861c81a137599c8bc33a553827343 Mon Sep 17 00:00:00 2001 From: gagnonanthony <79757265+gagnonanthony@users.noreply.github.com> Date: Thu, 11 Apr 2024 13:58:52 +0000 Subject: [PATCH 07/10] [FIX] fix editorconfig errors. --- subworkflows/nf-scil/registration/main.nf | 2 +- subworkflows/nf-scil/registration/meta.yml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/subworkflows/nf-scil/registration/main.nf b/subworkflows/nf-scil/registration/main.nf index 73e9d7d7..496a4c72 100644 --- a/subworkflows/nf-scil/registration/main.nf +++ b/subworkflows/nf-scil/registration/main.nf @@ -21,7 +21,7 @@ workflow REGISTRATION { if ( ch_metric ) { // ** Set up input channel ** // ch_register = ch_image.combine(ch_ref, by: 0) - .combine(ch_metric, by: 0) + .combine(ch_metric, by: 0) // ** Registration using AntsRegistration ** // REGISTER_ANATTODWI ( ch_register ) diff --git a/subworkflows/nf-scil/registration/meta.yml b/subworkflows/nf-scil/registration/meta.yml index 6670a1cb..835e7421 100644 --- a/subworkflows/nf-scil/registration/meta.yml +++ b/subworkflows/nf-scil/registration/meta.yml @@ -2,11 +2,11 @@ name: "registration" description: | Subworkflow to perform registration between a moving and a fixed image (e.g. T1 -> DWI). It requires as input at least a moving (ch_image) and a reference image (ch_ref) to properly - perform registration. Two modes are available: - 1) if a metric file is supplied (ch_metric), the subworkflow will use the REGISTER_ANATTODWI - module calling AntsRegistration and - 2) if a metric file is NOT supplied, the subworkflow will use the REGISTRATION_ANTS module calling - antsRegistrationSyN.sh or antsRegistrationSyNQuick.sh. + perform registration. Two modes are available: + 1) if a metric file is supplied (ch_metric), the subworkflow will use the REGISTER_ANATTODWI + module calling AntsRegistration and + 2) if a metric file is NOT supplied, the subworkflow will use the REGISTRATION_ANTS module calling + antsRegistrationSyN.sh or antsRegistrationSyNQuick.sh. Typical next steps after this subworkflow would be ANATOMICAL_SEGMENTATION or TRACKING. keywords: - Registration @@ -36,7 +36,7 @@ input: description: | The input channel containing the metric files. Supplying this channel will make the subworkflow use the module REGISTER_ANATTODWI calling AntsRegistration using the moving, reference and metric - image. For a T1 -> DWI registration, this is typically a FA map. + image. For a T1 -> DWI registration, this is typically a FA map. Structure: [ val(meta), path(metric) ] pattern: "*.{nii,nii.gz}" - ch_mask: From be6407b3d9f2d8381e98b9f3f17e032376f6aaab Mon Sep 17 00:00:00 2001 From: gagnonanthony <79757265+gagnonanthony@users.noreply.github.com> Date: Fri, 12 Apr 2024 14:57:43 +0000 Subject: [PATCH 08/10] [WIP] Migrate from pytest to nf-test. --- .../nf-scil/registration/tests/main.nf.test | 91 ++++++++++ .../registration/tests/main.nf.test.snap | 160 ++++++++++++++++++ .../registration/tests}/nextflow.config | 5 +- .../nf-scil/registration/tests/tags.yml | 2 + tests/config/pytest_modules.yml | 11 -- .../subworkflows/nf-scil/registration/main.nf | 47 ----- .../nf-scil/registration/test.yml | 39 ----- 7 files changed, 254 insertions(+), 101 deletions(-) create mode 100644 subworkflows/nf-scil/registration/tests/main.nf.test create mode 100644 subworkflows/nf-scil/registration/tests/main.nf.test.snap rename {tests/subworkflows/nf-scil/registration => subworkflows/nf-scil/registration/tests}/nextflow.config (99%) create mode 100644 subworkflows/nf-scil/registration/tests/tags.yml delete mode 100644 tests/subworkflows/nf-scil/registration/main.nf delete mode 100644 tests/subworkflows/nf-scil/registration/test.yml diff --git a/subworkflows/nf-scil/registration/tests/main.nf.test b/subworkflows/nf-scil/registration/tests/main.nf.test new file mode 100644 index 00000000..7668e743 --- /dev/null +++ b/subworkflows/nf-scil/registration/tests/main.nf.test @@ -0,0 +1,91 @@ +nextflow_workflow { + + name "Test Subworkflow REGISTRATION" + script "../main.nf" + workflow "REGISTRATION" + config "./nextflow.config" + + tag "subworkflows" + tag "subworkflows_nfcore" + tag "subworkflows/registration" + + tag "register" + tag "register/anattodwi" + tag "registration" + tag "registration/ants" + + + setup { + run("LOAD_TEST_DATA", alias: "LOAD_DATA") { + script "../../load_test_data/main.nf" + process { + """ + input[0] = Channel.from( [ "processing.zip" ] ) + input[1] = "test.load-test-data" + """ + } + } + } + + test("registration - antsRegistration") { + + when { + workflow { + """ + input[0] = LOAD_DATA.out.test_data_directory.map{ + test_data_directory -> [ + [ id:'test', single_end:false ], + file("\${test_data_directory}/mni_masked_2x2x2.nii.gz") + ]} + input[1] = LOAD_DATA.out.test_data_directory.map{ + test_data_directory -> [ + [ id:'test', single_end:false ], + file("\${test_data_directory}/b0_mean.nii.gz") + ]} + input[2] = LOAD_DATA.out.test_data_directory.map{ + test_data_directory -> [ + [ id:'test', single_end:false ], + file("\${test_data_directory}/fa.nii.gz") + ]} + input[3] = [] + """ + } + } + + then { + assertAll( + { assert workflow.success}, + { assert snapshot(workflow.out).match()} + ) + } + } + + test("registration - SyNQuick") { + + when { + workflow { + """ + input[0] = LOAD_DATA.out.test_data_directory.map{ + test_data_directory -> [ + [ id:'test', single_end:false ], + file("\${test_data_directory}/mni_masked_2x2x2.nii.gz") + ]} + input[1] = LOAD_DATA.out.test_data_directory.map{ + test_data_directory -> [ + [ id:'test', single_end:false ], + file("\${test_data_directory}/b0_mean.nii.gz") + ]} + input[2] = [] + input[3] = [] + """ + } + } + + then { + assertAll( + { assert workflow.success}, + { assert snapshot(workflow.out).match()} + ) + } + } +} diff --git a/subworkflows/nf-scil/registration/tests/main.nf.test.snap b/subworkflows/nf-scil/registration/tests/main.nf.test.snap new file mode 100644 index 00000000..c45e46e8 --- /dev/null +++ b/subworkflows/nf-scil/registration/tests/main.nf.test.snap @@ -0,0 +1,160 @@ +{ + "registration - antsRegistration": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": false + }, + "test__t1_warped.nii.gz:md5,ff651c76f4ea493de0a06a202e9f9fb2" + ] + ], + "1": [ + [ + { + "id": "test", + "single_end": false + }, + "test__output1Warp.nii.gz:md5,f896e4fef21f26c84bf834afd7fb5d55", + "test__output0GenericAffine.mat:md5,043decee0d131e79a802e3dcc61c22f0" + ] + ], + "2": [ + [ + { + "id": "test", + "single_end": false + }, + "test__output0GenericAffine.mat:md5,043decee0d131e79a802e3dcc61c22f0", + "test__output1InverseWarp.nii.gz:md5,62d6287e60dd36ffd477e71061f23077" + ] + ], + "3": [ + "versions.yml:md5,2207e66bf22435748071842a500885f7" + ], + "image_warped": [ + [ + { + "id": "test", + "single_end": false + }, + "test__t1_warped.nii.gz:md5,ff651c76f4ea493de0a06a202e9f9fb2" + ] + ], + "transfo_image": [ + [ + { + "id": "test", + "single_end": false + }, + "test__output1Warp.nii.gz:md5,f896e4fef21f26c84bf834afd7fb5d55", + "test__output0GenericAffine.mat:md5,043decee0d131e79a802e3dcc61c22f0" + ] + ], + "transfo_trk": [ + [ + { + "id": "test", + "single_end": false + }, + "test__output0GenericAffine.mat:md5,043decee0d131e79a802e3dcc61c22f0", + "test__output1InverseWarp.nii.gz:md5,62d6287e60dd36ffd477e71061f23077" + ] + ], + "versions": [ + "versions.yml:md5,2207e66bf22435748071842a500885f7" + ] + } + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-04-12T10:46:36.252304" + }, + "registration - SyNQuick": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": false + }, + "test__warped.nii.gz:md5,88442ab57066e1fb1e03f90d73cdfc42" + ] + ], + "1": [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test__output0Warp.nii.gz:md5,61436a721b29633474255640b5e44f7f", + "test__output1GenericAffine.mat:md5,3946e4d33c15911a9a1d72374fdbe4df" + ] + ] + ], + "2": [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test__output0InverseAffine.mat:md5,b30458b37b728d8ce97890b391ad9df9", + "test__output1InverseWarp.nii.gz:md5,278d1774561435ff0a3ec239872b74e5" + ] + ] + ], + "3": [ + "versions.yml:md5,96376b8bc28640eef565e7f1e258dbb2" + ], + "image_warped": [ + [ + { + "id": "test", + "single_end": false + }, + "test__warped.nii.gz:md5,88442ab57066e1fb1e03f90d73cdfc42" + ] + ], + "transfo_image": [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test__output0Warp.nii.gz:md5,61436a721b29633474255640b5e44f7f", + "test__output1GenericAffine.mat:md5,3946e4d33c15911a9a1d72374fdbe4df" + ] + ] + ], + "transfo_trk": [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test__output0InverseAffine.mat:md5,b30458b37b728d8ce97890b391ad9df9", + "test__output1InverseWarp.nii.gz:md5,278d1774561435ff0a3ec239872b74e5" + ] + ] + ], + "versions": [ + "versions.yml:md5,96376b8bc28640eef565e7f1e258dbb2" + ] + } + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-04-12T10:50:27.051312" + } +} \ No newline at end of file diff --git a/tests/subworkflows/nf-scil/registration/nextflow.config b/subworkflows/nf-scil/registration/tests/nextflow.config similarity index 99% rename from tests/subworkflows/nf-scil/registration/nextflow.config rename to subworkflows/nf-scil/registration/tests/nextflow.config index 70d31ebb..21095996 100644 --- a/tests/subworkflows/nf-scil/registration/nextflow.config +++ b/subworkflows/nf-scil/registration/tests/nextflow.config @@ -1,15 +1,12 @@ process { - withName: "REGISTER_ANATTODWI" { ext.cpus = 1 publishDir = { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" } } - withName: "REGISTRATION_ANTS" { - publishDir = { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" } ext.quick = true ext.threads = 1 ext.random_seed = 44 + publishDir = { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" } } - } diff --git a/subworkflows/nf-scil/registration/tests/tags.yml b/subworkflows/nf-scil/registration/tests/tags.yml new file mode 100644 index 00000000..3e78c179 --- /dev/null +++ b/subworkflows/nf-scil/registration/tests/tags.yml @@ -0,0 +1,2 @@ +subworkflows/registration: + - subworkflows/nf-scil/registration/** diff --git a/tests/config/pytest_modules.yml b/tests/config/pytest_modules.yml index e525677d..8a494f21 100755 --- a/tests/config/pytest_modules.yml +++ b/tests/config/pytest_modules.yml @@ -7,11 +7,9 @@ betcrop/cropvolume: betcrop/fslbetcrop: - modules/nf-scil/betcrop/fslbetcrop/** - tests/modules/nf-scil/betcrop/fslbetcrop/** - bundle/recognize: - modules/nf-scil/bundle/recognize/** - tests/modules/nf-scil/bundle/recognize/** - denoising/mppca: - modules/nf-scil/denoising/mppca/** - tests/modules/nf-scil/denoising/mppca/** @@ -27,7 +25,6 @@ io/readbids: preproc/eddy: - modules/nf-scil/preproc/eddy/** - tests/modules/nf-scil/preproc/eddy/** - preproc/gibbs: - modules/nf-scil/preproc/gibbs/** - tests/modules/nf-scil/preproc/gibbs/** @@ -70,14 +67,6 @@ registration/antsapplytransforms: segmentation/freesurferseg: - modules/nf-scil/segmentation/freesurferseg/** - tests/modules/nf-scil/segmentation/freesurferseg/** -subworkflows/load_test_data: - - subworkflows/nf-scil/load_test_data/** - - tests/subworkflows/nf-scil/load_test_data/** - -subworkflows/registration: - - subworkflows/nf-scil/registration/** - - tests/subworkflows/nf-scil/registration/** - testdata/scilpy: - modules/nf-scil/testdata/scilpy/** - tests/modules/nf-scil/testdata/scilpy/** diff --git a/tests/subworkflows/nf-scil/registration/main.nf b/tests/subworkflows/nf-scil/registration/main.nf deleted file mode 100644 index 5bc5470a..00000000 --- a/tests/subworkflows/nf-scil/registration/main.nf +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env nextflow - -nextflow.enable.dsl = 2 - -include { LOAD_TEST_DATA } from '../load_test_data/main' -include { REGISTRATION } from '../../../../subworkflows/nf-scil/registration/main' - -workflow test_register_anattodwi_antsregistration { - - input_fetch = Channel.from( [ "processing.zip" ] ) - LOAD_TEST_DATA ( input_fetch, "test.load-test-data" ) - - ch_image = LOAD_TEST_DATA.out.test_data_directory - .map{ test_data_directory -> [ - [ id:'test', single_end:false ], - file("${test_data_directory}/mni_masked_2x2x2.nii.gz")]} - ch_ref = LOAD_TEST_DATA.out.test_data_directory - .map{ test_data_directory -> [ - [ id:'test', single_end:false ], - file("${test_data_directory}/b0_mean.nii.gz")]} - ch_metric = LOAD_TEST_DATA.out.test_data_directory - .map{ test_data_directory -> [ - [ id:'test', single_end:false ], - file("${test_data_directory}/fa.nii.gz")]} - ch_mask = [] - - REGISTRATION ( ch_image, ch_ref, ch_metric, ch_mask ) -} - -workflow test_register_anattodwi_syn { - - input_fetch = Channel.from( [ "processing.zip" ] ) - LOAD_TEST_DATA ( input_fetch, "test.load-test-data" ) - - ch_image = LOAD_TEST_DATA.out.test_data_directory - .map{ test_data_directory -> [ - [ id:'test', single_end:false ], - file("${test_data_directory}/mni_masked_2x2x2.nii.gz")]} - ch_ref = LOAD_TEST_DATA.out.test_data_directory - .map{ test_data_directory -> [ - [ id:'test', single_end:false ], - file("${test_data_directory}/b0_mean.nii.gz")]} - ch_metric = [] - ch_mask = [] - - REGISTRATION ( ch_image, ch_ref, ch_metric, ch_mask ) -} diff --git a/tests/subworkflows/nf-scil/registration/test.yml b/tests/subworkflows/nf-scil/registration/test.yml deleted file mode 100644 index 611d133c..00000000 --- a/tests/subworkflows/nf-scil/registration/test.yml +++ /dev/null @@ -1,39 +0,0 @@ -- name: registration test_register_anattodwi_antsregistration - command: nextflow run ./tests/subworkflows/nf-scil/registration -entry test_register_anattodwi_antsregistration -c ./tests/config/nextflow.config - tags: - - register - - register/anattodwi - - registration - - registration/ants - - subworkflows - - subworkflows/registration - files: - - path: output/register/test__output0GenericAffine.mat - md5sum: 043decee0d131e79a802e3dcc61c22f0 - - path: output/register/test__output1InverseWarp.nii.gz - md5sum: 450c5f85d59a753cb8dd29c8de59905e - - path: output/register/test__output1Warp.nii.gz - md5sum: 4e3131a45dabb26b18122fdf5520ba2c - - path: output/register/test__t1_warped.nii.gz - md5sum: c5a57cfa815caceab567a61d57f07fb4 - -- name: registration test_register_anattodwi_syn - command: nextflow run ./tests/subworkflows/nf-scil/registration -entry test_register_anattodwi_syn -c ./tests/config/nextflow.config - tags: - - register - - register/anattodwi - - registration - - registration/ants - - subworkflows - - subworkflows/registration - files: - - path: output/registration/test__output0InverseAffine.mat - md5sum: b30458b37b728d8ce97890b391ad9df9 - - path: output/registration/test__output0Warp.nii.gz - md5sum: 1cc8863a565c9df46e1a3bb613154a17 - - path: output/registration/test__output1GenericAffine.mat - md5sum: 3946e4d33c15911a9a1d72374fdbe4df - - path: output/registration/test__output1InverseWarp.nii.gz - md5sum: 987bd5da0a356f1b12194acc4ab85750 - - path: output/registration/test__warped.nii.gz - md5sum: 685cab659941bb735eaded4b4bfc93bf From b9b0dd50f7c04d26782dcecc6e6cf3407892856e Mon Sep 17 00:00:00 2001 From: gagnonanthony <79757265+gagnonanthony@users.noreply.github.com> Date: Fri, 12 Apr 2024 15:02:25 +0000 Subject: [PATCH 09/10] [FIX] Linting. --- subworkflows/nf-scil/registration/tests/main.nf.test | 1 + 1 file changed, 1 insertion(+) diff --git a/subworkflows/nf-scil/registration/tests/main.nf.test b/subworkflows/nf-scil/registration/tests/main.nf.test index 7668e743..3c8c4211 100644 --- a/subworkflows/nf-scil/registration/tests/main.nf.test +++ b/subworkflows/nf-scil/registration/tests/main.nf.test @@ -14,6 +14,7 @@ nextflow_workflow { tag "registration" tag "registration/ants" + tag "load_test_data" setup { run("LOAD_TEST_DATA", alias: "LOAD_DATA") { From 10d399e7b2dcdf6891e33170d2e7ba66b9147411 Mon Sep 17 00:00:00 2001 From: gagnonanthony <79757265+gagnonanthony@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:59:45 +0000 Subject: [PATCH 10/10] [FIX] Alex's comments. --- subworkflows/nf-scil/registration/main.nf | 8 ++++---- subworkflows/nf-scil/registration/meta.yml | 15 +++++++++------ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/subworkflows/nf-scil/registration/main.nf b/subworkflows/nf-scil/registration/main.nf index 496a4c72..e1bcf15a 100644 --- a/subworkflows/nf-scil/registration/main.nf +++ b/subworkflows/nf-scil/registration/main.nf @@ -20,7 +20,7 @@ workflow REGISTRATION { if ( ch_metric ) { // ** Set up input channel ** // - ch_register = ch_image.combine(ch_ref, by: 0) + ch_register = ch_image.combine(ch_ref, by: 0) .combine(ch_metric, by: 0) // ** Registration using AntsRegistration ** // @@ -33,14 +33,14 @@ workflow REGISTRATION { transfo_trk = REGISTER_ANATTODWI.out.transfo_trk } else { - // ** Set up input channel, input are inversed compared to REGISTER_ANATTODWI. ** // + // ** Set up input channel, input are inverted compared to REGISTER_ANATTODWI. ** // if ( ch_mask ) { ch_register = ch_ref.combine(ch_image, by: 0) .combine(ch_mask, by: 0) } else { ch_register = ch_ref.combine(ch_image, by: 0) - .map{ [it[0], it[1], it[2], []] } + .map{ it + [[]] } } // ** Registration using antsRegistrationSyN.sh or antsRegistrationSyNQuick.sh. ** // @@ -58,7 +58,7 @@ workflow REGISTRATION { emit: image_warped = image_warped // channel: [ val(meta), [ image ] ] transfo_image = transfo_image // channel: [ val(meta), [ warp, affine ] ] - transfo_trk = transfo_trk // channel: [ val(meta), [ inversewarp, inverseaffine ] ] + transfo_trk = transfo_trk // channel: [ val(meta), [ inverseAffine, inverseWarp ] ] versions = ch_versions // channel: [ versions.yml ] } diff --git a/subworkflows/nf-scil/registration/meta.yml b/subworkflows/nf-scil/registration/meta.yml index 835e7421..d6fc95ff 100644 --- a/subworkflows/nf-scil/registration/meta.yml +++ b/subworkflows/nf-scil/registration/meta.yml @@ -1,13 +1,16 @@ name: "registration" description: | Subworkflow to perform registration between a moving and a fixed image (e.g. T1 -> DWI). - It requires as input at least a moving (ch_image) and a reference image (ch_ref) to properly + It requires as input at least a moving (ch_image) and a reference (ch_ref) image to properly perform registration. Two modes are available: 1) if a metric file is supplied (ch_metric), the subworkflow will use the REGISTER_ANATTODWI - module calling AntsRegistration and - 2) if a metric file is NOT supplied, the subworkflow will use the REGISTRATION_ANTS module calling + module calling AntsRegistration, with the metric as additional target. + 2) if NO metric file is supplied, the subworkflow will use the REGISTRATION_ANTS module calling antsRegistrationSyN.sh or antsRegistrationSyNQuick.sh. - Typical next steps after this subworkflow would be ANATOMICAL_SEGMENTATION or TRACKING. + This subworkflow outputs transformation files that can be used with the ANTSAPPLYTRANSFORMS module + to warp any new image. Simply provide your moving image, reference image, and transformations files + to the module to register a new image in the current space. Similar steps can be used to register + bundles/tractograms using the BUNDLEREGISTRATION module. keywords: - Registration - Transformation @@ -66,9 +69,9 @@ output: type: file description: | Channel containing the tractogram transformation files. This channel contains the necessary transformation - files (inversewarp, inverseaffine) to perform the dwi -> anatomical space registration. Those files could + files (inverseAffine, inverseWarp) to perform the dwi -> anatomical space registration. Those files could be used to register tractograms or bundle in the subject's anatomical space. - Structure: [ val(meta), [ path(inversewarp), path(inverseaffine) ] + Structure: [ val(meta), [ path(inverseAffine), path(inverseWarp) ] pattern: "*.{nii,nii.gz,mat}" - versions: type: file