From 3e13e32e1f82e62bcddc7b415661e35fc5b3fa77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kre=C5=A1imir=20Be=C5=A1tak?= Date: Thu, 13 Feb 2025 10:28:56 +0000 Subject: [PATCH 1/4] croptiff change --- modules/local/croptiff.nf | 5 +---- workflows/molkart.nf | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/modules/local/croptiff.nf b/modules/local/croptiff.nf index 7e3ca43..db33f9f 100644 --- a/modules/local/croptiff.nf +++ b/modules/local/croptiff.nf @@ -5,8 +5,7 @@ process CROPTIFF { container 'ghcr.io/schapirolabor/molkart-local:v0.0.4' input: - tuple val(meta), path(image_stack) - tuple val(meta), path(crop_summary) + tuple val(meta), path(image_stack), path(crop_summary) output: tuple val(meta), path("*.tiff"), emit: crop_tiff @@ -18,8 +17,6 @@ process CROPTIFF { script: def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" - """ crop_tiff.py \\ --input $image_stack \\ diff --git a/workflows/molkart.nf b/workflows/molkart.nf index 4f283dd..a96e0f4 100644 --- a/workflows/molkart.nf +++ b/workflows/molkart.nf @@ -130,10 +130,7 @@ workflow MOLKART { ch_versions = ch_versions.mix(CROPHDF5.out.versions) // Combine images with crop_summary for making the same training tiff stacks as ilastik tiff_crop = stack_mix.join(CROPHDF5.out.crop_summary) - CROPTIFF( - tiff_crop.map(it -> tuple(it[0],it[1])), - tiff_crop.map(it -> tuple(it[0],it[2])), - ) + CROPTIFF(tiff_crop) ch_versions = ch_versions.mix(CROPTIFF.out.versions) MOLKARTQCPNG(CROPTIFF.out.overview.map{ tuple('matchkey', it[1]) From 138210e395da8e8f72020e3fb1cd7d49915a1bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kre=C5=A1imir=20Be=C5=A1tak?= Date: Thu, 13 Feb 2025 10:37:24 +0000 Subject: [PATCH 2/4] spot2cell change --- modules/local/spot2cell.nf | 3 +-- workflows/molkart.nf | 6 +----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/modules/local/spot2cell.nf b/modules/local/spot2cell.nf index 4eab5ba..2bf0f54 100644 --- a/modules/local/spot2cell.nf +++ b/modules/local/spot2cell.nf @@ -6,8 +6,7 @@ process SPOT2CELL{ container 'ghcr.io/schapirolabor/molkart-local:v0.0.4' input: - tuple val(meta) , path(spot_table) - tuple val(meta2), path(cell_mask) + tuple val(meta), path(spot_table), path(cell_mask) output: tuple val(meta), path("*.csv"), emit: cellxgene_table diff --git a/workflows/molkart.nf b/workflows/molkart.nf index a96e0f4..ff89409 100644 --- a/workflows/molkart.nf +++ b/workflows/molkart.nf @@ -246,11 +246,7 @@ workflow MOLKART { [new_meta, spots_table, mask] } .set { dedup_spots } - - SPOT2CELL( - dedup_spots.map(it -> tuple(it[0],it[1])), - dedup_spots.map(it -> tuple(it[0],it[2])) - ) + SPOT2CELL(dedup_spots) ch_versions = ch_versions.mix(SPOT2CELL.out.versions) // From bdc0c19825ee61d0346040a3de7e88300c7dbd0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kre=C5=A1imir=20Be=C5=A1tak?= Date: Thu, 13 Feb 2025 13:35:38 +0000 Subject: [PATCH 3/4] stain sorting cleanup --- assets/schema_input.json | 4 ++- workflows/molkart.nf | 74 +++++++++++++++++----------------------- 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/assets/schema_input.json b/assets/schema_input.json index 85f6cc2..2f3c017 100644 --- a/assets/schema_input.json +++ b/assets/schema_input.json @@ -21,6 +21,7 @@ "spot_table": { "type": "string", "pattern": "^\\S+\\.(txt|tsv)$", + "format": "file-path", "errorMessage": "Spot table must be provided, has to have shape x,y,z,gene with sep = '\t', cannot contain spaces and must have extension '.txt'" }, "membrane_image": { @@ -28,7 +29,8 @@ "anyOf": [ { "type": "string", - "pattern": "^\\S+\\.(tif|tiff)$" + "pattern": "^\\S+\\.(tif|tiff)$", + "format": "file-path" }, { "type": "string", diff --git a/workflows/molkart.nf b/workflows/molkart.nf index ff89409..d147b98 100644 --- a/workflows/molkart.nf +++ b/workflows/molkart.nf @@ -44,31 +44,21 @@ workflow MOLKART { // stain: "1" denotes membrane, stain: "0" denotes nuclear image // this is used to preserve the order later - ch_samplesheet - .map { - meta, nuclear, spots, membrane -> - def new_meta = meta.clone() - new_meta.stain = "1" - return membrane != [] ? [new_meta, membrane] : null - //it[3] != [] ? tuple([id:it[0],stain:"1"], it[3]) : null - }.set { membrane_tuple } // if a membrane image is provided, return membrane image channel tuple (meta, path) + membrane_tuple = ch_samplesheet + .filter { it[3] } // filter samples with membrane + .map {meta, _nuclear, _spots, membrane -> + [meta + [stain: '1'], membrane] + } - ch_samplesheet - .map { - meta, nuclear, spots, membrane -> - def new_meta = meta.clone() - new_meta.stain = "0" - return [new_meta, nuclear] + image_tuple = ch_samplesheet + .map {meta, nuclear, _spots, _membrane -> + [meta + [stain: '0'], nuclear] } - .set { image_tuple } // creates nuclear image channel tuple (meta, path) - ch_samplesheet - .map { - meta, nuclear, spots, membrane -> - def new_meta = meta.clone() - return [meta, spots] + spot_tuple = ch_samplesheet + .map {meta, _nuclear, spots, _membrane -> + [meta, spots] } - .set { spot_tuple } // creates spot table channel tuple (meta, path) // // MODULE: Run Mindagap_mindagap @@ -88,27 +78,25 @@ workflow MOLKART { map_for_stacks = params.skip_clahe ? clahe_in : CLAHE.out.img_clahe map_for_stacks - .map { - meta, tiff -> [meta.subMap("id"), tiff, meta.stain] // creates a channel containing only the sample id in meta, path to preprocessed image and the stain value ("0" or "1") - }.groupTuple() // combines based on meta + .map { meta, tiff -> + [ meta.subMap("id"), tiff, meta.stain ] + } + .groupTuple(by: 0) + .map { id, tiffs, stains -> + def sorted = [tiffs, stains].transpose().sort { it[1] } + def nuclear = sorted[0] + def membrane = sorted.size() > 1 ? sorted[1] : null + membrane ? [id, nuclear[0], membrane[0]] : [id, nuclear[0]] + } + .set{ grouped_map_stack } + + grouped_map_stack.filter { !it[2] } // for rows without a present membrane image, set channel to no_stack + .set{ no_stack } + + grouped_map_stack.filter{ it[2] } // for rows where the membrane image is present, create a list of images to be stacked .map{ - meta, paths, stains -> [meta, [paths[0], stains[0]], [paths[1], stains[1]]] // reorganizes to match path and stain - }.map{ - meta, stain1, stain2 -> [meta, [stain1, stain2].sort{ it[1] }] // sort by stain index (0 for nuclear, 1 for other) - }.map{ - meta, list -> [meta, list[0], list[1]] // sorted will have null as first list - }.map{ - it[1][0] != null ? [it[0],it[1][0],it[2][0]] : [it[0],it[2][0]] // if null, only return the valid nuclear path value, otherwise return both nuclear and membrane paths - }.set { grouped_map_stack } - - grouped_map_stack.filter{ // for rows without a present membrane image, set channel to no_stack - it[2] == null - }.set{ no_stack } - - grouped_map_stack.filter{ // for rows where the membrane image is present, make it compliant with STACK inputs - it[2] != null - }.map{ - [it[0],tuple(it[1],it[2])] + id, nuclear, membrane -> + [id, tuple(nuclear, membrane)] }.set{ create_stack_in } // @@ -219,7 +207,7 @@ workflow MOLKART { } segmentation_masks.map{ meta, mask, segmentation -> - new_meta = meta.clone() + def new_meta = meta.clone() new_meta.segmentation = segmentation [new_meta, mask] }.set { matched_segmasks } @@ -241,7 +229,7 @@ workflow MOLKART { .combine(filtered_masks, by: 0) .map { meta, spots_table, mask, segmethod -> - new_meta = meta.clone() + def new_meta = meta.clone() new_meta.segmentation = segmethod [new_meta, spots_table, mask] } From f8e8a92c7184d9d2c3a8e43fb776b208317815ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kre=C5=A1imir=20Be=C5=A1tak?= Date: Thu, 13 Feb 2025 13:53:26 +0000 Subject: [PATCH 4/4] code cleanup --- modules/local/createanndata.nf | 3 +-- modules/local/createstack.nf | 3 +-- modules/local/crophdf5.nf | 10 ++++------ modules/local/croptiff.nf | 8 ++++---- modules/local/maskfilter.nf | 1 - modules/local/molkartqc.nf | 13 ++++++------- modules/local/molkartqcpng.nf | 8 +++----- modules/local/tiffh5convert.nf | 10 ++++------ 8 files changed, 23 insertions(+), 33 deletions(-) diff --git a/modules/local/createanndata.nf b/modules/local/createanndata.nf index a11e3d7..bf9d64b 100644 --- a/modules/local/createanndata.nf +++ b/modules/local/createanndata.nf @@ -17,13 +17,12 @@ process CREATE_ANNDATA { script: def args = task.ext.args ?: '' def prefix = task.ext.prefix ?: "${meta.id}" - """ create_anndata.py \\ --input ${spot2cell} \\ --spatial_cols X_centroid Y_centroid \\ --output ${prefix}.adata \\ - $args + ${args} cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/createstack.nf b/modules/local/createstack.nf index 993c5a4..417dbf4 100644 --- a/modules/local/createstack.nf +++ b/modules/local/createstack.nf @@ -17,12 +17,11 @@ process CREATE_STACK { script: def args = task.ext.args ?: '' def prefix = task.ext.prefix ?: "${meta.id}" - """ stack.py \\ --input ${image} \\ --output ${prefix}.ome.tif \\ - $args + ${args} cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/crophdf5.nf b/modules/local/crophdf5.nf index 6d5229a..4b02ff0 100644 --- a/modules/local/crophdf5.nf +++ b/modules/local/crophdf5.nf @@ -16,15 +16,13 @@ process CROPHDF5 { task.ext.when == null || task.ext.when script: - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" - + def args = task.ext.args ?: '' """ crop_hdf5.py \\ - --input $image_stack \\ + --input ${image_stack} \\ --output . \\ - --num_channels $num_channels \\ - $args + --num_channels ${num_channels} \\ + ${args} cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/croptiff.nf b/modules/local/croptiff.nf index db33f9f..0a40316 100644 --- a/modules/local/croptiff.nf +++ b/modules/local/croptiff.nf @@ -16,12 +16,12 @@ process CROPTIFF { task.ext.when == null || task.ext.when script: - def args = task.ext.args ?: '' + def args = task.ext.args ?: '' """ crop_tiff.py \\ - --input $image_stack \\ - --crop_summary $crop_summary \\ - $args + --input ${image_stack} \\ + --crop_summary ${crop_summary} \\ + ${args} cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/maskfilter.nf b/modules/local/maskfilter.nf index 97e73c3..fa8dd2f 100644 --- a/modules/local/maskfilter.nf +++ b/modules/local/maskfilter.nf @@ -33,7 +33,6 @@ process MASKFILTER { """ stub: - def args = task.ext.args ?: '' def prefix = task.ext.prefix ?: "${meta.id}" """ diff --git a/modules/local/molkartqc.nf b/modules/local/molkartqc.nf index 57fe8d4..cfc5d82 100644 --- a/modules/local/molkartqc.nf +++ b/modules/local/molkartqc.nf @@ -20,13 +20,13 @@ process MOLKARTQC{ """ collect_QC.py \\ - --cellxgene $cellxgene_table \\ - --spots $spot_table \\ - --sample_id $prefix \\ - --segmentation_method $segmethod \\ - --filterqc $filterqc \\ + --cellxgene ${cellxgene_table} \\ + --spots ${spot_table} \\ + --sample_id ${prefix} \\ + --segmentation_method ${segmethod} \\ + --filterqc ${filterqc} \\ --outdir . \\ - $args + ${args} cat <<-END_VERSIONS > versions.yml "${task.process}": @@ -35,7 +35,6 @@ process MOLKARTQC{ """ stub: - def args = task.ext.args ?: '' def prefix = task.ext.prefix ?: "${meta.id}" """ diff --git a/modules/local/molkartqcpng.nf b/modules/local/molkartqcpng.nf index f5a44df..438e27b 100644 --- a/modules/local/molkartqcpng.nf +++ b/modules/local/molkartqcpng.nf @@ -18,9 +18,9 @@ process MOLKARTQCPNG { """ collect_QC.py \\ - --png_overview $png \\ + --png_overview ${png} \\ --outdir . \\ - $args + ${args} cat <<-END_VERSIONS > versions.yml "${task.process}": @@ -29,9 +29,7 @@ process MOLKARTQCPNG { """ stub: - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" - + def prefix = task.ext.prefix ?: ( png.name.toString().tokenize('.')[0] ) """ touch ${prefix}.png diff --git a/modules/local/tiffh5convert.nf b/modules/local/tiffh5convert.nf index f3d38ab..8fc03e5 100644 --- a/modules/local/tiffh5convert.nf +++ b/modules/local/tiffh5convert.nf @@ -15,15 +15,13 @@ process TIFFH5CONVERT { task.ext.when == null || task.ext.when script: - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" - + def args = task.ext.args ?: '' """ crop_hdf5.py \\ - --input $image \\ + --input ${image} \\ --output . \\ - --num_channels $num_channels \\ - $args + --num_channels ${num_channels} \\ + ${args} cat <<-END_VERSIONS > versions.yml "${task.process}":