Mercurial > repos > perssond > ashlar
changeset 1:f183d9de4622 draft
planemo upload for repository https://github.com/ohsu-comp-bio/ashlar commit 95998c84e130c9f3d2183591957464df7d90dd53
author | goeckslab |
---|---|
date | Wed, 24 Aug 2022 19:19:40 +0000 |
parents | b3054f3d42b2 |
children | 33ab2058c6d9 |
files | ashlar.xml macros.xml pyramid_upgrade.py test-data/ashlar_test_c0.tiff test-data/ashlar_test_c1.tiff |
diffstat | 5 files changed, 116 insertions(+), 136 deletions(-) [+] |
line wrap: on
line diff
--- a/ashlar.xml Fri Mar 12 00:14:49 2021 +0000 +++ b/ashlar.xml Wed Aug 24 19:19:40 2022 +0000 @@ -1,11 +1,11 @@ -<tool id="ashlar" name="ASHLAR" version="@VERSION@.6" profile="17.09"> +<tool id="ashlar" name="ASHLAR" version="@TOOL_VERSION@+galaxy@VERSION_SUFFIX@" profile="19.01"> <description>Alignment by Simultaneous Harmonization of Layer/Adjacency Registration</description> <macros> <import>macros.xml</import> </macros> <expand macro="requirements"/> - @VERSION_CMD@ + <expand macro="version_cmd"/> <command detect_errors="exit_code"><![CDATA[ @@ -13,41 +13,41 @@ #set name_clean = str($file.element_identifier).replace('.ome.tiff','').replace('.tiff','').replace('.tiff.','') #if $type == "raw" - #set file_clean = $name_clean + ".ome.tiff" + #set file_clean = $name_clean + '.ome.tiff' #elif $type == "ffp" - #set file_clean = $name_clean + "_ffp.ome.tiff" + #set file_clean = $name_clean + '_ffp.ome.tiff' #elif $type == "dfp" - #set file_clean = $name_clean + "_dfp.ome.tiff" + #set file_clean = $name_clean + '_dfp.ome.tiff' #end if #return $file_clean #end def ## Link the illumination files to appropriate file extension - #for $dfp in $ldfp: - ln -s "$dfp" "$clean($dfp,"dfp")" && - #end for - #for $ffp in $lffp: - ln -s "$ffp" "$clean($ffp,"ffp")" && - #end for + #if $ldfp + #for $dfp in $ldfp: + ln -s '$dfp' '$clean($dfp,"dfp")' && + #end for + #end if + + #if $lffp + #for $ffp in $lffp: + ln -s '$ffp' '$clean($ffp,"ffp")' && + #end for + #end if @CMD_BEGIN@ ## Supply the raw images #for $raw in $lraw: - "$raw" + '$raw' #end for ## Additional arguments -m $max_shift - #if $flip_x - --flip-x - #end if - - #if $flip_y - --flip-y - #end if + $flip_x + $flip_y -c $adv.align_channel @@ -59,50 +59,43 @@ --tile-size $adv.tile_size #end if + #if $lffp --ffp - #for $ffp in $lffp: - "$clean($ffp,"ffp")" - #end for + #for $ffp in $lffp: + '$clean($ffp,"ffp")' + #end for + #end if + + #if $ldfp --dfp - #for $dfp in $ldfp: - "$clean($dfp,"dfp")" - #end for - --pyramid + #for $dfp in $ldfp: + '$clean($dfp,"dfp")' + #end for + #end if + + $adv.pyramid + $adv.flip_mosaic_x + $adv.flip_mosaic_y + -f registered.ome.tif; #if $upgrade.decide == "do_upgrade" - python ${__tool_directory__}/pyramid_upgrade.py - registered.ome.tif + python3 '${__tool_directory__}/pyramid_upgrade.py' + registered.ome.tif - #if $upgrade.markers_file - -n `python "$get_markers" "${upgrade.markers_file}"` - #end if + #if $upgrade.markers_file + -n `echo \$(cat $upgrade.markers_file | tail -n +2 | awk -F, '{print \$3}')`; + #end if #end if ]]></command> - <configfiles> - <configfile name="get_markers"> -import pandas as pd -import sys - -marker_file = sys.argv[1] -df = pd.read_csv(marker_file) -print(' '.join(df['marker_name'].array)) - </configfile> - </configfiles> - - - <inputs> - <param name="lraw" type="data_collection" format="tiff" collection_type="list" label="Raw Images"/> - <param name="ldfp" type="data_collection" format="tiff" collection_type="list" label="Deep Field Profile Images"/> - <param name="lffp" type="data_collection" format="tiff" collection_type="list" label="Flat Field Profile Images"/> - - <param name="flip_x" type="boolean" value="false" label="Flip X-axis"/> - <param name="flip_y" type="boolean" value="false" label="Flip Y-axis"/> - + <param name="lraw" type="data_collection" format="ome.tiff,tiff" collection_type="list" label="Raw Images"/> + <param name="ldfp" type="data_collection" format="ome.tiff,tiff" collection_type="list" optional="true" label="Deep Field Profile Images"/> + <param name="lffp" type="data_collection" format="ome.tiff,tiff" collection_type="list" optional="true" label="Flat Field Profile Images"/> + <param name="flip_x" type="boolean" truevalue="--flip-x" falsevalue="" label="Flip X-axis"/> + <param name="flip_y" type="boolean" truevalue="--flip-y" falsevalue="" label="Flip Y-axis"/> <param name="max_shift" type="integer" value="30" label="Maximum allowed per-tile corrective shift" help="In micros"/> - <conditional name="upgrade"> <param name="decide" type="select" label="Upgrade to BF6-Compliant OME-TIFF Pyramid"> <option value="do_upgrade">Upgrade Pyramid</option> @@ -118,63 +111,48 @@ <param name="align_channel" type="integer" value="0" label="Align Channel Number"/> <param name="filter_sigma" type="float" optional="true" label="Sigma"/> <param name="tile_size" type="integer" optional="true" label="Cyto Mask Channel"/> + <param name="flip_mosaic_x" type="boolean" truevalue="--flip-mosaic-x" falsevalue="" label="Flip output image horizontally"/> + <param name="flip_mosaic_y" type="boolean" truevalue="--flip-mosaic-y" falsevalue="" label="Flip output image vertically"/> + <param name="pyramid" type="boolean" checked="true" truevalue="--pyramid" falsevalue="" label="Write output as a single pyramidal TIFF"/> </section> </inputs> <outputs> - <data format="tiff" name="output" from_work_dir="registered.ome.tif" label="${tool.name} on ${on_string}"/> + <data format="ome.tiff" name="output" from_work_dir="registered.ome.tif" label="${tool.name} on ${on_string}"/> </outputs> + <tests> + <test> + <param name="lraw"> + <collection type="list"> + <element name="rR1" value="ashlar_test_c0.tiff" /> + <element name="rR2" value="ashlar_test_c1.tiff" /> + </collection> + </param> + <output name="output" ftype="ome.tiff"> + <assert_contents> + <has_size value="4000000" delta="1000000" /> + </assert_contents> + </output> + </test> + </tests> <help><![CDATA[ -Ashlar python package for microscopy registration, developed by HMS (repo: https://github.com/labsyspharm/ashlar) - ashlar [-h] [-o DIR] [-c [CHANNEL]] - [--output-channels [CHANNEL [CHANNEL ...]]] [-m SHIFT] - [--filter-sigma SIGMA] [-f FORMAT] [--pyramid] - [--tile-size PIXELS] [--ffp [FILE [FILE ...]]] - [--dfp [FILE [FILE ...]]] [--plates] [-q] [--version] - [FILE [FILE ...]] - -Stitch and align one or more multi-series images - -positional arguments: - FILE an image file to be processed (one file per cycle) +-------------------------------------------------------------------------------- +ASHLAR: Alignment by Simultaneous Harmonization of Layer/Adjacency Registration +-------------------------------------------------------------------------------- -optional arguments: - -h, --help show this help message and exit - -o DIR, --output DIR write output image files to DIR; default is the - current directory - -c [CHANNEL], --align-channel [CHANNEL] - align images using channel number CHANNEL; numbering - starts at 0 - --output-channels [CHANNEL [CHANNEL ...]] - output only channels listed in CHANNELS; numbering - starts at 0 - -m SHIFT, --maximum-shift SHIFT - maximum allowed per-tile corrective shift in microns - --filter-sigma SIGMA width in pixels of Gaussian filter to apply to images - before alignment; default is 0 which disables - filtering - -f FORMAT, --filename-format FORMAT - use FORMAT to generate output filenames, with {cycle} - and {channel} as required placeholders for the cycle - and channel numbers; default is - cycle_{cycle}_channel_{channel}.tif - --pyramid write output as a single pyramidal TIFF - --tile-size PIXELS set tile width and height to PIXELS (pyramid output - only); default is 1024 - --ffp [FILE [FILE ...]] - read flat field profile image from FILES; if specified - must be one common file for all cycles or one file for - each cycle - --dfp [FILE [FILE ...]] - read dark field profile image from FILES; if specified - must be one common file for all cycles or one file for - each cycle - --plates enable plate mode for HTS data - -q, --quiet suppress progress display - --version print version +**Whole-slide microscopy image stitching and registration in Python** -OHSU Wrapper Repo: https://github.com/ohsu-comp-bio/ashlar -Conda Package Available From: https://anaconda.org/ohsu-comp-bio/ashlar +**Ashlar** performs fast, high-quality stitching of microscopy images. It also +co-registers multiple rounds of cyclic imaging for methods such as CyCIF and +CODEX. Ashlar can read image data directly from BioFormats-supported microscope +vendor file formats as well as a directory of plain TIFF files. Output is saved +as pyramidal, tiled OME-TIFF. + +Note that Ashlar requires unstitched individual "tile" images as input, so it is +not suitable for microscopes or slide scanners that only provide pre-stitched +images. + +*Visit https://labsyspharm.github.io/ashlar/ for the most up-to-date information on ASHLAR.* ]]></help> <expand macro="citations" /> </tool>
--- a/macros.xml Fri Mar 12 00:14:49 2021 +0000 +++ b/macros.xml Wed Aug 24 19:19:40 2022 +0000 @@ -2,19 +2,21 @@ <macros> <xml name="requirements"> <requirements> - <requirement type="package" version="@VERSION@">ashlar</requirement> - <requirement type="package" version="1.1.5">pandas</requirement> + <requirement type="package" version="@TOOL_VERSION@">ashlar</requirement> + <container type="docker">labsyspharm/ashlar:@TOOL_VERSION@</container> </requirements> </xml> <xml name="version_cmd"> - <version_command>echo @VERSION@</version_command> + <version_command>echo @TOOL_VERSION@</version_command> </xml> <xml name="citations"> <citations> + <citation type="doi">10.1101/2021.04.20.440625</citation> </citations> </xml> - <token name="@VERSION@">1.13.0</token> + <token name="@TOOL_VERSION@">1.14.0</token> + <token name="@VERSION_SUFFIX@">0</token> <token name="@CMD_BEGIN@">ashlar</token> </macros>
--- a/pyramid_upgrade.py Fri Mar 12 00:14:49 2021 +0000 +++ b/pyramid_upgrade.py Wed Aug 24 19:19:40 2022 +0000 @@ -1,34 +1,33 @@ -import sys -import os import argparse -import struct -import re +import dataclasses import fractions import io -import xml.etree.ElementTree -import collections +import os +import re import reprlib -import dataclasses -from typing import List, Any +import struct +import sys +import xml.etree.ElementTree +from typing import Any, List datatype_formats = { - 1: "B", # BYTE - 2: "s", # ASCII - 3: "H", # SHORT - 4: "I", # LONG - 5: "I", # RATIONAL (pairs) - 6: "b", # SBYTE - 7: "B", # UNDEFINED - 8: "h", # SSHORT - 9: "i", # SLONG - 10: "i", # SRATIONAL (pairs) - 11: "f", # FLOAT - 12: "d", # DOUBLE - 13: "I", # IFD - 16: "Q", # LONG8 - 17: "q", # SLONG8 - 18: "Q", # IFD8 + 1: "B", # BYTE + 2: "s", # ASCII + 3: "H", # SHORT + 4: "I", # LONG + 5: "I", # RATIONAL (pairs) + 6: "b", # SBYTE + 7: "B", # UNDEFINED + 8: "h", # SSHORT + 9: "i", # SLONG + 10: "i", # SRATIONAL (pairs) + 11: "f", # FLOAT + 12: "d", # DOUBLE + 13: "I", # IFD + 16: "Q", # LONG8 + 17: "q", # SLONG8 + 18: "Q", # IFD8 } rational_datatypes = {5, 10} @@ -253,6 +252,7 @@ + ")" ) + @dataclasses.dataclass(frozen=True) class TagSet: """Container for Tag objects as stored in a TIFF IFD. @@ -293,7 +293,7 @@ i = self.codes.index(code) except ValueError: raise KeyError(code) from None - self.tags[:] = self.tags[:i] + self.tags[i+1:] + self.tags[:] = self.tags[:i] + self.tags[i + 1:] def __contains__(self, code): return code in self.codes @@ -328,7 +328,7 @@ else: i = len(self.tags) n = len(self.tags) - self.tags[i:n+1] = [tag] + self.tags[i:n] + self.tags[i:n + 1] = [tag] + self.tags[i:n] @dataclasses.dataclass(frozen=True) @@ -372,7 +372,7 @@ def parse_args(): parser = argparse.ArgumentParser( description="Convert an OME-TIFF legacy pyramid to the BioFormats 6" - " OME-TIFF pyramid format in-place.", + " OME-TIFF pyramid format in-place.", ) parser.add_argument("image", help="OME-TIFF file to convert") parser.add_argument( @@ -382,8 +382,8 @@ default=[], metavar="NAME", help="Channel names to be inserted into OME metadata. Number of names" - " must match number of channels in image. Be sure to put quotes" - " around names containing spaces or other special shell characters." + " must match number of channels in image. Be sure to put quotes" + " around names containing spaces or other special shell characters." ) args = parser.parse_args() return args @@ -451,7 +451,7 @@ print(f"TIFF does not begin with SizeC={size_c} full-size pages.") sys.exit(1) for level in range(1, num_levels): - level_dims = page_dims[level * size_c : (level + 1) * size_c] + level_dims = page_dims[level * size_c: (level + 1) * size_c] if len(set(level_dims)) != 1: print( f"Pyramid level {level + 1} out of {num_levels} has inconsistent" @@ -513,7 +513,7 @@ print("Writing new TIFF headers...") stale_ranges = [ifd.offset_range for ifd in tiff.ifds] main_ifds = tiff.ifds[:size_c] - channel_sub_ifds = [tiff.ifds[c + size_c : : size_c] for c in range(size_c)] + channel_sub_ifds = [tiff.ifds[c + size_c::size_c] for c in range(size_c)] for i, (main_ifd, sub_ifds) in enumerate(zip(main_ifds, channel_sub_ifds)): for ifd in sub_ifds: if 305 in ifd.tags: