diff --git a/subworkflows/nf-scil/registration/main.nf b/subworkflows/nf-scil/registration/main.nf index 0e10925a..1fa7c801 100644 --- a/subworkflows/nf-scil/registration/main.nf +++ b/subworkflows/nf-scil/registration/main.nf @@ -1,21 +1,35 @@ include { REGISTRATION_ANATTODWI } from '../../../modules/nf-scil/registration/anattodwi/main' include { REGISTRATION_ANTS } from '../../../modules/nf-scil/registration/ants/main' +include { REGISTRATION_EASYREG } from '../../../modules/nf-scil/registration/easyreg/main' + +params.run_surgery = false workflow REGISTRATION { - // ** The subworkflow requires at least ch_image and ch_ref as inputs to ** // + // ** The subworkflow requires at least ch_image and ch_ref as inputs to ** // // ** properly perform the registration. Supplying a ch_metric will select ** // - // ** the REGISTRATION_ANATTODWI module meanwhile NOT supplying a ch_metric ** // - // ** will select the REGISTRATION_ANTS (SyN or SyNQuick) module. ** // + // ** the REGISTRATION_ANATTODWI module meanwhile NOT supplying a ch_metric ** // + // ** will select the REGISTRATION_ANTS (SyN or SyNQuick) module. Alternatively, ** // + // ** NOT supplying ch_metric and activating run_surgery flag with select REGISTRATION_EASYREG ** // 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 + ch_segmentation // channel: [ val(meta), [ flo_segmentation ] ], optional + ch_ref_segmentation // channel: [ val(meta), [ ref_segmentation ] ], optional + main: + image_warped = Channel.empty() + ref_warped = Channel.empty() + transfo_image = Channel.empty() + transfo_trk = Channel.empty() + out_segmentation = Channel.empty() + out_ref_segmentation = Channel.empty() + ch_versions = Channel.empty() if ( ch_metric ) { @@ -31,6 +45,31 @@ workflow REGISTRATION { image_warped = REGISTRATION_ANATTODWI.out.t1_warped transfo_image = REGISTRATION_ANATTODWI.out.transfo_image transfo_trk = REGISTRATION_ANATTODWI.out.transfo_trk + } + else if ( params.run_surgery ) { + // ** Set up input channel ** // + ch_register = ch_ref.combine(ch_image, by: 0) + .combine(ch_ref_segmentation, by: 0) + .combine(ch_segmentation, by: 0) + + // ** Registration using Easyreg ** // + REGISTRATION_EASYREG ( ch_register ) + ch_versions = ch_versions.mix(REGISTRATION_EASYREG.out.versions.first()) + + // ** Setting outputs ** // + image_warped = REGISTRATION_EASYREG.out.flo_reg + transfo_image = REGISTRATION_EASYREG.out.fwd_field + .map{ it + [[]] } + transfo_trk = REGISTRATION_EASYREG.out.bak_field + .map{ [[]] + it } + ref_warped - REGISTRATION_EASYREG.out.ref_reg + + // ** Setting optional outputs. If segmentations are not provided as inputs, ** // + // ** easyreg will outputs synthseg segmentations ** // + out_segmentation = ch_segmentation ? Channel.empty() : REGISTRATION_EASYREG.out.flo_seg + out_ref_segmentation = ch_ref_segmentation ? Channel.empty() : REGISTRATION_EASYREG.out.ref_seg + + } else { // ** Set up input channel, input are inverted compared to REGISTRATION_ANATTODWI. ** // @@ -56,9 +95,13 @@ 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), [ inverseAffine, inverseWarp ] ] + image_warped = image_warped // channel: [ val(meta), [ image ] ] + ref_warped = ref_warped // channel: [ val(meta), [ ref ] ] + transfo_image = transfo_image // channel: [ val(meta), [ warp, affine ] ] + transfo_trk = transfo_trk // channel: [ val(meta), [ inverseAffine, inverseWarp ] ] + segmentation = out_segmentation // channel: [ val(meta), [ segmentation ] ] + ref_segmentation = out_ref_segmentation // channel: [ val(meta), [ ref_segmentation ] ] + versions = ch_versions // channel: [ versions.yml ] } diff --git a/subworkflows/nf-scil/registration/meta.yml b/subworkflows/nf-scil/registration/meta.yml index 26317da7..5d37b95b 100644 --- a/subworkflows/nf-scil/registration/meta.yml +++ b/subworkflows/nf-scil/registration/meta.yml @@ -2,11 +2,13 @@ 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 (ch_ref) image to properly - perform registration. Two modes are available: + perform registration. Three modes are available: 1) if a metric file is supplied (ch_metric), the subworkflow will use the REGISTER_ANATTODWI 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. + 3) alternatively, if the run_synth parameter is activated, the subworkflow will use the REGISTRATION_EASYREG module + calling mri_easyreg. 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 @@ -18,6 +20,7 @@ keywords: - Anatomical components: - registration/anattodwi + - registration/easyreg - registration/ants input: - ch_image: @@ -50,6 +53,22 @@ input: REGISTRATION_ANTS, see the description section above for more details. Structure: [ val(meta), path(mask) ] pattern: "*.{nii,nii.gz}" + - ch_segmentation: + type: file + description: | + The input channel containing the the SynthSeg v2 (non-robust) segmentation + parcellation of the moving (floating in Easyreg naming convention) image. + If it does not exist, Easyreg will create it. If it already exists (e.g., from a previous EasyReg run), + then EasyReg will read it from disk (which is faster than segmenting). + pattern: "*.{nii,nii.gz}" + - ch_ref_segmentation: + type: file + description: | + The input channel containing the SynthSeg v2 (non-robust) segmentation + parcellation of the fixed (reference in Easyreg naming convention) image. + If it does not exist, Easyreg will create it. If it already exists (e.g., from a previous EasyReg run), + then EasyReg will read it from disk (which is faster than segmenting). + pattern: "*.{nii,nii.gz}" + + output: - image_warped: type: file @@ -57,6 +76,12 @@ output: Channel containing warped moving images. Typically, this would be the warped T1 in DWI space. Structure: [ val(meta), path(image) ] pattern: "*.{nii,nii.gz}" + - ref_warped: + type: file + description: | + Channel containing warped reference 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: | @@ -73,6 +98,16 @@ output: be used to register tractograms or bundle in the subject's anatomical space. Structure: [ val(meta), [ path(inverseAffine), path(inverseWarp) ] pattern: "*.{nii,nii.gz,mat}" + - out_segmentation: + type: file + description: | + Channel containing the file with the SynthSeg v2 (non-robust) segmentation + parcellation of the moving (floating in Easyreg naming convention) image. + pattern: "*.{nii,nii.gz}" + - out_ref_segmentation: + type: file + description: | + Channel containing the file with the SynthSeg v2 (non-robust) segmentation + parcellation of the fixed (reference in Easyreg naming convention) image. + pattern: "*.{nii,nii.gz}" - versions: type: file description: | diff --git a/subworkflows/nf-scil/registration/tests/main.nf.test b/subworkflows/nf-scil/registration/tests/main.nf.test index 82e41363..a2431c05 100644 --- a/subworkflows/nf-scil/registration/tests/main.nf.test +++ b/subworkflows/nf-scil/registration/tests/main.nf.test @@ -3,7 +3,6 @@ nextflow_workflow { name "Test Subworkflow REGISTRATION" script "../main.nf" workflow "REGISTRATION" - config "./nextflow.config" tag "subworkflows" tag "subworkflows_nfcore" @@ -13,6 +12,7 @@ nextflow_workflow { tag "registration/anattodwi" tag "registration" tag "registration/ants" + tag "registration/easyreg" tag "load_test_data" @@ -29,7 +29,7 @@ nextflow_workflow { } test("registration - antsRegistration") { - + config "./nextflow.config" when { workflow { """ @@ -49,6 +49,8 @@ nextflow_workflow { file("\${test_data_directory}/fa.nii.gz") ]} input[3] = [] + input[4] = [] + input[5] = [] """ } } @@ -56,13 +58,54 @@ nextflow_workflow { then { assertAll( { assert workflow.success}, - { assert snapshot(workflow.out).match()} + { assert snapshot( + niftiMD5SUM(workflow.out.image_warped.get(0).get(1)), + workflow.out.transfo_image, + workflow.out.transfo_trk, + workflow.out.versions + ).match()} ) } } test("registration - SyNQuick") { + config "./nextflow.config" + 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] = [] + input[4] = [] + input[5] = [] + """ + } + } + + then { + assertAll( + { assert workflow.success}, + { assert snapshot( + niftiMD5SUM(workflow.out.image_warped.get(0).get(1)), + workflow.out.transfo_image, + workflow.out.transfo_trk, + workflow.out.versions + ).match()} + ) + } + } + test("registration - surgery") { + config "./nextflow_surgery.config" when { workflow { """ @@ -78,6 +121,8 @@ nextflow_workflow { ]} input[2] = [] input[3] = [] + input[4] = [] + input[5] = [] """ } } @@ -85,7 +130,15 @@ nextflow_workflow { then { assertAll( { assert workflow.success}, - { assert snapshot(workflow.out).match()} + { assert snapshot( + niftiMD5SUM(workflow.out.image_warped.get(0).get(1)), + niftiMD5SUM(workflow.out.ref_warped.get(0).get(1)), + workflow.out.transfo_image, + workflow.out.transfo_trk, + niftiMD5SUM(workflow.out.segmentation.get(0).get(1)), + niftiMD5SUM(workflow.out.ref_segmentation.get(0).get(1)), + workflow.out.versions + ).match()} ) } } diff --git a/subworkflows/nf-scil/registration/tests/main.nf.test.snap b/subworkflows/nf-scil/registration/tests/main.nf.test.snap index 864970d1..9f4faa14 100644 --- a/subworkflows/nf-scil/registration/tests/main.nf.test.snap +++ b/subworkflows/nf-scil/registration/tests/main.nf.test.snap @@ -1,160 +1,72 @@ { "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,9c255f4c751382be7bf6fe6ffb0d635d" - ], - "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,9c255f4c751382be7bf6fe6ffb0d635d" + "test__t1_warped.nii.gz:md5:header,e327665710b9c0888bf345a10a73c20f,data,1e29233890cb0b9cd0420d581cc77397", + [ + [ + { + "id": "test", + "single_end": false + }, + "test__output1Warp.nii.gz:md5,f896e4fef21f26c84bf834afd7fb5d55", + "test__output0GenericAffine.mat:md5,043decee0d131e79a802e3dcc61c22f0" + ] + ], + [ + [ + { + "id": "test", + "single_end": false + }, + "test__output0GenericAffine.mat:md5,043decee0d131e79a802e3dcc61c22f0", + "test__output1InverseWarp.nii.gz:md5,62d6287e60dd36ffd477e71061f23077" ] - } + ], + [ + "versions.yml:md5,9c255f4c751382be7bf6fe6ffb0d635d" + ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" + "nf-test": "0.9.0-rc1", + "nextflow": "24.04.4" }, - "timestamp": "2024-05-01T19:09:12.780118" + "timestamp": "2024-09-13T15:55:40.238221" }, "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": [ + "test__warped.nii.gz:md5:header,e327665710b9c0888bf345a10a73c20f,data,61793ed987f5eac9779dd84acc189f9a", + [ + [ + { + "id": "test", + "single_end": false + }, [ - { - "id": "test", - "single_end": false - }, - [ - "test__output0InverseAffine.mat:md5,b30458b37b728d8ce97890b391ad9df9", - "test__output1InverseWarp.nii.gz:md5,278d1774561435ff0a3ec239872b74e5" - ] + "test__output0Warp.nii.gz:md5,61436a721b29633474255640b5e44f7f", + "test__output1GenericAffine.mat:md5,3946e4d33c15911a9a1d72374fdbe4df" ] - ], - "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 + }, [ - { - "id": "test", - "single_end": false - }, - [ - "test__output0InverseAffine.mat:md5,b30458b37b728d8ce97890b391ad9df9", - "test__output1InverseWarp.nii.gz:md5,278d1774561435ff0a3ec239872b74e5" - ] + "test__output0InverseAffine.mat:md5,b30458b37b728d8ce97890b391ad9df9", + "test__output1InverseWarp.nii.gz:md5,278d1774561435ff0a3ec239872b74e5" ] - ], - "versions": [ - "versions.yml:md5,96376b8bc28640eef565e7f1e258dbb2" ] - } + ], + [ + "versions.yml:md5,96376b8bc28640eef565e7f1e258dbb2" + ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" + "nf-test": "0.9.0-rc1", + "nextflow": "24.04.4" }, - "timestamp": "2024-05-10T15:06:53.240526" + "timestamp": "2024-09-13T15:55:52.86551" } } \ No newline at end of file diff --git a/subworkflows/nf-scil/registration/tests/nextflow_surgery.config b/subworkflows/nf-scil/registration/tests/nextflow_surgery.config new file mode 100644 index 00000000..dffb4b8c --- /dev/null +++ b/subworkflows/nf-scil/registration/tests/nextflow_surgery.config @@ -0,0 +1,10 @@ +process { + withName: "REGISTRATION_EASYREG" { + publishDir = { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" } + ext.field = true + } +} + +params { + run_surgery = true +}