From 0ef3266dfe1eaf7906bb8198ad09c5c06c5fc0bc Mon Sep 17 00:00:00 2001 From: Gesa Voigt Date: Wed, 7 Aug 2024 15:19:42 +0000 Subject: [PATCH] Add palom as local module --- conf/modules.config | 12 +++++ .../palom/alignmultiplecycles/environment.yml | 11 +++++ .../local/palom/alignmultiplecycles/main.nf | 43 +++++++++++++++++ .../local/palom/alignmultiplecycles/meta.yml | 47 +++++++++++++++++++ nextflow.config | 2 + nextflow_schema.json | 17 ++++++- workflows/mcmicro.nf | 26 +++++++++- 7 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 modules/local/palom/alignmultiplecycles/environment.yml create mode 100644 modules/local/palom/alignmultiplecycles/main.nf create mode 100644 modules/local/palom/alignmultiplecycles/meta.yml diff --git a/conf/modules.config b/conf/modules.config index 76b219a..e1672aa 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -36,8 +36,20 @@ process { ] } + withName: ALIGNMULTIPLECYCLES { + ext.args = { params.palom_align_channel ? "--channel ${params.palom_align_channel}" : ''} + // TODO px_size specification? + ext.when = {params.registration_method.split(',').contains('palom')} + publishDir = [ + path: { "${params.outdir}/registration/palom" }, + mode: params.publish_dir_mode, + ] + } + + withName: ASHLAR { containerOptions = '--user root' + ext.when = {params.registration_method.split(',').contains('ashlar')} publishDir = [ path: { "${params.outdir}/registration/ashlar" }, mode: params.publish_dir_mode, diff --git a/modules/local/palom/alignmultiplecycles/environment.yml b/modules/local/palom/alignmultiplecycles/environment.yml new file mode 100644 index 0000000..a35bee9 --- /dev/null +++ b/modules/local/palom/alignmultiplecycles/environment.yml @@ -0,0 +1,11 @@ +name: "palom_multicycle" +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - "openslide=4.0.0" + - "scikit-image=0.19.3" + - pip + - pip: + - "palom==2024.4.1" diff --git a/modules/local/palom/alignmultiplecycles/main.nf b/modules/local/palom/alignmultiplecycles/main.nf new file mode 100644 index 0000000..ef1fc33 --- /dev/null +++ b/modules/local/palom/alignmultiplecycles/main.nf @@ -0,0 +1,43 @@ +process ALIGNMULTIPLECYCLES { + tag "$meta.id" + label 'process_single' + container "docker.io/voigtgesa/palom:v2024.4.1_cli2" + + input: + tuple val(meta), path(images, stageAs: 'image*/*') + + output: + tuple val(meta), path("*.ome.tif"), emit: tif + 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}" + + """ + python /palom/align_multiple_cycles.py \\ + --img_list $images \\ + --out_name ${prefix}.ome.tif \\ + --out_dir . \\ + $args + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + palom: \$(palom-svs --version | sed 's/palom v//') + END_VERSIONS + """ + + stub: + def prefix = task.ext.prefix ?: "${meta.id}" + """ + touch ${prefix}.ome.tif + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + palom: \$(palom-svs --version | sed 's/palom v//') + END_VERSIONS + """ +} diff --git a/modules/local/palom/alignmultiplecycles/meta.yml b/modules/local/palom/alignmultiplecycles/meta.yml new file mode 100644 index 0000000..44ced20 --- /dev/null +++ b/modules/local/palom/alignmultiplecycles/meta.yml @@ -0,0 +1,47 @@ +name: "palom_multiplecycles" +description: Alignment for Multiple Layers of Mosaics +keywords: + - image_processing + - registration + - alignment +tools: + - "palom": + description: "Piecewise Alignment for Layers of Mosaics. Palom started as a tool for registering whole-slide images of the same FFPE section with different IHC stainings" + homepage: "https://github.com/labsyspharm/palom" + documentation: "https://github.com/labsyspharm/palom/blob/main/README.md" + tool_dev_url: "https://github.com/labsyspharm/palom" + doi: "" + licence: "" ## TODO Wait for licence specification (opened issue) + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1', single_end:false ]` + - images: + type: path + description: List of paths to OME-TIF files, ordered by consecutive imaging cycles + pattern: "*.{ome.tiff,ome.tif,tif,tiff}" + +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1']` + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + - tif: + type: file + description: Registered image + pattern: "*.ome.tif" + +authors: + - "@kbestak" + - "@gesavoigt" +maintainers: + - "@kbestak" + - "@gesavoigt" diff --git a/nextflow.config b/nextflow.config index 2f9f598..898c1d3 100644 --- a/nextflow.config +++ b/nextflow.config @@ -15,6 +15,8 @@ params { marker_sheet = null segmentation = 'mesmer' cellpose_model = [] + registration_method = 'ashlar' + palom_align_channel = 0 // Illumination correction illumination = null diff --git a/nextflow_schema.json b/nextflow_schema.json index c0209e5..875dcd0 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -69,11 +69,24 @@ "segmentation": { "type": "string", "pattern": "^((cellpose|mesmer)?,?)*(? + [[id: meta.id], [meta.cycle_number, image_tiles]] + } + // FIXME: pass groupTuple size: from samplesheet cycle count + .groupTuple(sort: { a, b -> a[0] <=> b[0] }) + .map{ meta, cycles -> [meta, *cycles.collect{ it[1..-1] }.transpose()]} + .dump(tag: 'PALOM in') + | ALIGNMULTIPLECYCLES + if (params.registration_method.split(',').contains('palom')) { + ch_versions = ch_versions.mix(ALIGNMULTIPLECYCLES.out.versions) + ch_registration = ch_registration.mix(ALIGNMULTIPLECYCLES.out.tif) + } + + ch_samplesheet .map{ meta, image_tiles, dfp, ffp -> [[id: meta.id], [meta.cycle_number, image_tiles, dfp, ffp]] @@ -72,6 +91,10 @@ workflow MCMICRO { } | ASHLAR ch_versions = ch_versions.mix(ASHLAR.out.versions) + if (params.registration_method.split(',').contains('ashlar')) { + ch_versions = ch_versions.mix(ASHLAR.out.versions) + ch_registration = ch_registration.mix(ASHLAR.out.tif) + } // // Run Background Correction // BACKSUB(ASHLAR.out.tif, ch_markers) @@ -83,7 +106,8 @@ workflow MCMICRO { ch_versions = ch_versions.mix(BACKSUB.out.versions) } else { */ - ch_segmentation_input = ASHLAR.out.tif + ch_registration.view() + ch_segmentation_input = ch_registration /* } */