Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Module] Freesurfer registration #54

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
73ca1de
initial module commit
anroy1 Jan 9, 2024
f9b9b3b
manage output format for ants and include init transform as output
anroy1 Jan 14, 2024
819a821
quick fix
anroy1 Jan 19, 2024
943c25d
test fix 1
anroy1 Jan 26, 2024
944b2d2
2nd fix for tests
anroy1 Jan 26, 2024
a4ab7bc
quick fix
anroy1 Feb 13, 2024
8527df2
test naming fix
anroy1 Feb 21, 2024
ec3abcf
fix scilpy fetcher path
anroy1 Feb 22, 2024
8563d0f
fix tests parameters
anroy1 Feb 22, 2024
d865f3e
[WIP] modify module and test to manage freesurfer license
anroy1 Mar 1, 2024
d3bbf09
fix test data, freesurfer license
anroy1 Apr 9, 2024
f1cbf98
remove fs license
anroy1 Apr 9, 2024
e0d3143
[WIP] improve test
anroy1 Apr 10, 2024
de0a5c5
Adjust nextflow config and output format
anroy1 Apr 16, 2024
5abd3ac
Swith transform format to freesurfer standard
anroy1 Apr 20, 2024
385b0fe
run prettier
anroy1 Apr 25, 2024
e4a816f
Remove license as module do not require it and update parameters with…
anroy1 May 9, 2024
985d5a9
Switch transform output to fs .nii.gz format
anroy1 May 14, 2024
f642c8e
Migrate tests to nf-tests
anroy1 Jun 1, 2024
e840787
fix nf-tests
anroy1 Jun 5, 2024
0154dcc
Remove pytests
anroy1 Jun 5, 2024
3969605
Run prettier
anroy1 Jun 5, 2024
6b075e8
Modify tests config for remote tests
anroy1 Jun 5, 2024
7ad4e6a
Fix test assertion for name only
anroy1 Jun 5, 2024
a4f0778
Rerun tests and fix linting
anroy1 Jun 6, 2024
a7c45de
adjust process parameters for tests locally
anroy1 Jun 11, 2024
6570e2e
[wip] fix alex comments 1/2
anroy1 Jun 13, 2024
b802440
Add precisions regarding convertion on meta.yml
anroy1 Jun 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
name: "registration_synthregistration"
channels:
- Docker
- Apptainer
dependencies:
- "Freesurfer:synthmorph"
64 changes: 64 additions & 0 deletions modules/nf-scil/registration/synthregistration/main.nf
Original file line number Diff line number Diff line change
@@ -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 : ""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be handled directly in the script with $task.cpus

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this, for both calls? :

mri_synthmorph -j $task.cpus [...]

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
Comment on lines +39 to +40
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering, is the command able to support multiple transforms ? Just thinking that the module should be able to take a sequence of transforms (not just affine+warp),. Not worth it if the whole script needs a redo though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I fully understand. There is a "rigid" model also, but initialization with it seems to break the registration, which is consstent with the doc: "Deformable assumes prior affine alignment or initialization with -i". The flag "-m joint" would work so it doesn't require successive affine + deform run: "Joint includes affine and deformable but differs from running both in sequence in that it applies the deformable step in an affine mid-space to guarantee symmetric joint transforms." But building it this way allows for producing a affine only transform also. The warp output of the second synthmorph run generates a combined warp field (of affine + deform).

But if there were let's say a second initialzation (rigid + affine + deform), synthmorph always combine successive registration with any initialization.


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
"""
}
58 changes: 58 additions & 0 deletions modules/nf-scil/registration/synthregistration/meta.yml
Original file line number Diff line number Diff line change
@@ -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:
anroy1 marked this conversation as resolved.
Show resolved Hide resolved
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"
53 changes: 53 additions & 0 deletions modules/nf-scil/registration/synthregistration/tests/main.nf.test
Original file line number Diff line number Diff line change
@@ -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() }
)
}
}
}
Original file line number Diff line number Diff line change
@@ -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"
}
}
Original file line number Diff line number Diff line change
@@ -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
}
}
2 changes: 2 additions & 0 deletions modules/nf-scil/registration/synthregistration/tests/tags.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
registration/synthregistration:
- "modules/nf-scil/registration/synthregistration/**"