Skip to content

Commit

Permalink
initial nf-neuro commit (following nf-scil:PR#186)
Browse files Browse the repository at this point in the history
  • Loading branch information
anroy1 committed Sep 24, 2024
1 parent fd48db7 commit 4ff186b
Show file tree
Hide file tree
Showing 7 changed files with 374 additions and 0 deletions.
7 changes: 7 additions & 0 deletions modules/nf-neuro/segmentation/synthseg/environment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
name: "segmentation_synthseg"
channels:
- Docker
- Apptainer
dependencies:
- "Freesurfer"
110 changes: 110 additions & 0 deletions modules/nf-neuro/segmentation/synthseg/main.nf
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
process SEGMENTATION_SYNTHSEG {
tag "$meta.id"
label 'process_single'
label 'process_high'

container "freesurfer/freesurfer:7.4.1"

input:
tuple val(meta), path(image), path(lesion) /* optional, input = [] */, path(fs_license) /* optional, input = [] */

output:
tuple val(meta), path("*__mask_wm.nii.gz") , emit: wm_mask
tuple val(meta), path("*__mask_gm.nii.gz") , emit: gm_mask
tuple val(meta), path("*__mask_csf.nii.gz") , emit: csf_mask
tuple val(meta), path("*__parc.nii.gz") , emit: parc, optional: true
tuple val(meta), path("*__resampled_image.nii.gz") , emit: resample, optional: true
tuple val(meta), path("*__vol.csv") , emit: vol, optional: true
tuple val(meta), path("*__qc.csv") , emit: qc, optional: true
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 gpu = task.ext.gpu ? "" : "--cpu"
def parc = task.ext.parc ? "--parc" : ""
def robust = task.ext.robust ? "--robust" : ""
def fast = task.ext.fast ? "--fast" : ""
def ct = task.ext.ct ? "--ct" : ""
def output_resample = task.ext.output_resample ? "--resample ${prefix}__resampled_image.nii.gz": ""
def output_vol = task.ext.output_vol ? "--vol ${prefix}__vol.csv" : ""
def output_qc = task.ext.output_qc ? "--qc ${prefix}__qc.csv" : ""
def crop = task.ext.crop ? "--crop " + task.ext.crop: ""

"""
export ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=1
export OMP_NUM_THREADS=1
export OPENBLAS_NUM_THREADS=1
cp $fs_license \$FREESURFER_HOME/license.txt
mri_synthseg --i $image --o seg.nii.gz --threads $task.cpus $gpu $robust $fast $ct $output_resample $output_vol $output_qc $crop
if [[ $task.parc ]];
then
# Cortical parcellation
mri_synthseg --i $image --o ${prefix}__parc.nii.gz --threads $task.cpus $gpu $parc $robust $fast $crop
mri_convert -i ${prefix}__parc.nii.gz --out_data_type uchar -o ${prefix}__parc.nii.gz
fi
# WM Mask
mri_binarize --i seg.nii.gz \
--match 2 7 10 12 13 16 28 41 46 49 51 52 60 \
--o ${prefix}__mask_wm.nii.gz
# GM Mask
mri_binarize --i seg.nii.gz \
--match 3 8 11 17 18 26 42 47 50 52 53 54 58 \
--o ${prefix}__mask_gm.nii.gz
# CSF Mask
mri_binarize --i seg.nii.gz \
--match 4 5 14 15 24 43 44 \
--o ${prefix}__mask_csf.nii.gz
if [[ -f "$lesion" ]];
then
mri_binarize --i ${prefix}__mask_wm.nii.gz --merge $lesion --min 0.5 --o ${prefix}__mask_wm.nii.gz
fi
mri_convert -i ${prefix}__mask_wm.nii.gz --out_data_type uchar -o ${prefix}__mask_wm.nii.gz
mri_convert -i ${prefix}__mask_gm.nii.gz --out_data_type uchar -o ${prefix}__mask_gm.nii.gz
mri_convert -i ${prefix}__mask_csf.nii.gz --out_data_type uchar -o ${prefix}__mask_csf.nii.gz
mri_convert -i ${prefix}__parc.nii.gz --out_data_type uchar ${prefix}__parc.nii.gz
rm \$FREESURFER_HOME/license.txt
cat <<-END_VERSIONS > versions.yml
"${task.process}":
Freesurfer: 7.4.1
END_VERSIONS
"""

stub:
def args = task.ext.args ?: ''
def prefix = task.ext.prefix ?: "${meta.id}"

"""
mri_synthseg -h
mri_binarize -h
mri_convert -h
touch ${prefix}__mask_wm.nii.gz
touch ${prefix}__mask_gm.nii.gz
touch ${prefix}__mask_csf.nii.gz
touch ${prefix}__parc.nii.gz
touch ${prefix}__resampled_image.nii.gz
touch ${prefix}__vol.csv
touch ${prefix}__qc.csv
cat <<-END_VERSIONS > versions.yml
"${task.process}":
Freesurfer: 7.4.1
END_VERSIONS
"""
}
84 changes: 84 additions & 0 deletions modules/nf-neuro/segmentation/synthseg/meta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: "segmentation_synthseg"
description: Perform Brain Tissues Segmentation using Freesurfer synthseg on a T1 image. Optionally, a binary mask of lesion can be add to correct the white matter mask.
keywords:
- Segmentation
- Freesurfer
- Tissues
tools:
- "Freesurfer":
description: "An open source neuroimaging toolkit for processing, analyzing, and visualizing human brain MR images."
homepage: "https://surfer.nmr.mgh.harvard.edu/"

input:
- meta:
type: map
description: |
Groovy Map containing sample information
e.g. `[ id:'sample1', single_end:false ]`
- image:
type: file
description: Nifti T1 volume to segment into tissue maps.
pattern: "*.{nii,nii.gz}"

- lesion:
type: file
description: Nifti lesion volume to correct the white matter with a lesion mask. The lesion mask must be a binary mask.
pattern: "*.{nii,nii.gz}"

- fs_license:
type: file
description: The path to your FreeSurfer license. To get one, go to https://surfer.nmr.mgh.harvard.edu/registration.html. Optional. If you have already set your license as prescribed by Freesurfer (copied to a .license file in your $FREESURFER_HOME), this is not required.
pattern: "*.txt"

output:
- meta:
type: map
description: |
Groovy Map containing sample information
e.g. `[ id:'sample1', single_end:false ]`
- wm_mask:
type: file
description: Nifti WM mask volume.
pattern: "*.{nii,nii.gz}"

- gm_mask:
type: file
description: Nifti GM mask volume.
pattern: "*.{nii,nii.gz}"

- csf_mask:
type: file
description: Nifti CSF mask volume.
pattern: "*.{nii,nii.gz}"

- parc:
type: file
description: (optional) Nifti cortical parcellation volume.
pattern: "*.{nii,nii.gz}"

- vol:
type: file
description: (optional) Output CSV file with volumes for all structures and subjects.
pattern: "*.csv"

- qc:
type: file
description: (optional) Output CSV file with qc scores for all subjects.
pattern: "*.csv"

- resample:
type: file
description: (optional) in order to return segmentations at 1mm resolution, the input images are internally resampled (except if they already are at 1mm). Use this optional flag to save the resampled images. This must be the same type as --i.
pattern: "*.{nii,nii.gz}"

- versions:
type: file
description: File containing software versions
pattern: "versions.yml"

authors:
- "@anroy1"
maintainers:
- "@anroy1"
156 changes: 156 additions & 0 deletions modules/nf-neuro/segmentation/synthseg/tests/main.nf.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
nextflow_process {

name "Test Process SEGMENTATION_SYNTHSEG"
script "../main.nf"
process "SEGMENTATION_SYNTHSEG"

tag "modules"
tag "modules_nfcore"
tag "segmentation"
tag "segmentation/synthseg"

tag "subworkflows"
tag "subworkflows/load_test_data"

setup {
run("LOAD_TEST_DATA", alias: "LOAD_DATA") {
script "../../../../../subworkflows/nf-neuro/load_test_data/main.nf"
process {
"""
input[0] = Channel.from( [ "heavy.zip", "freesurfer.zip" ] )
input[1] = "test.load-test-data"
"""
}
}
}

test("segmentation - synthseg - basic") {
config "./nextflow_basic.config"
when {
process {
"""
ch_split_test_data = LOAD_DATA.out.test_data_directory
.branch{
heavy: it.simpleName == "heavy"
freesurfer: it.simpleName == "freesurfer"
}
ch_anat = ch_split_test_data.heavy.map{
test_data_directory -> [
[ id:'test' ],
file("\${test_data_directory}/anat/anat_image.nii.gz"),
[]
]
}
ch_license = ch_split_test_data.freesurfer.map{
test_data_directory -> [
[ id:'test' ],
file("\${test_data_directory}/license.txt")
]
}
input[0] = ch_anat
.join(ch_license)
"""
}
}

then {
assertAll(
{ assert process.success },
{ assert snapshot(
niftiMD5SUM(process.out.wm_mask.get(0).get(1)),
niftiMD5SUM(process.out.gm_mask.get(0).get(1)),
niftiMD5SUM(process.out.csf_mask.get(0).get(1)),
process.out.versions
).match() }
)
}
}

test("segmentation - synthseg - options") {
config "./nextflow_options.config"
when {
process {
"""
ch_split_test_data = LOAD_DATA.out.test_data_directory
.branch{
heavy: it.simpleName == "heavy"
freesurfer: it.simpleName == "freesurfer"
}
ch_anat = ch_split_test_data.heavy.map{
test_data_directory -> [
[ id:'test' ],
file("\${test_data_directory}/anat/anat_image.nii.gz"),
[]
]
}
ch_license = ch_split_test_data.freesurfer.map{
test_data_directory -> [
[ id:'test' ],
file("\${test_data_directory}/license.txt")
]
}
input[0] = ch_anat
.join(ch_license)
"""
}
}

then {
assertAll(
{ assert process.success },
{ assert snapshot(
niftiMD5SUM(process.out.wm_mask.get(0).get(1)),
niftiMD5SUM(process.out.gm_mask.get(0).get(1)),
niftiMD5SUM(process.out.csf_mask.get(0).get(1)),
niftiMD5SUM(process.out.parc.get(0).get(1)),
process.out.resample,
process.out.vol,
process.out.qc,
process.out.versions
).match() }
)
}
}

test("segmentation - synthseg - lesion") {
config "./nextflow_basic.config"
when {
process {
"""
ch_split_test_data = LOAD_DATA.out.test_data_directory
.branch{
heavy: it.simpleName == "heavy"
freesurfer: it.simpleName == "freesurfer"
}
ch_anat = ch_split_test_data.heavy.map{
test_data_directory -> [
[ id:'test' ],
file("\${test_data_directory}/anat/anat_image.nii.gz"),
file("\${test_data_directory}/anat/anat_mask.nii.gz")
]
}
ch_license = ch_split_test_data.freesurfer.map{
test_data_directory -> [
[ id:'test' ],
file("\${test_data_directory}/license.txt")
]
}
input[0] = ch_anat
.join(ch_license)
"""
}
}

then {
assertAll(
{ assert process.success },
{ assert snapshot(
niftiMD5SUM(process.out.wm_mask.get(0).get(1)),
niftiMD5SUM(process.out.gm_mask.get(0).get(1)),
niftiMD5SUM(process.out.csf_mask.get(0).get(1)),
process.out.versions
).match() }
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
process {
memory = "8G"
publishDir = { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }
ext.fast = true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
process {
memory = "8G"
publishDir = { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }
ext.robust = true
ext.output_resample = true
ext.output_vol = true
ext.output_qc = true
ext.crop = 150
ext.parc = true
}
2 changes: 2 additions & 0 deletions modules/nf-neuro/segmentation/synthseg/tests/tags.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
segmentation/synthseg:
- "modules/nf-neuro/segmentation/synthseg/**"

0 comments on commit 4ff186b

Please sign in to comment.