Skip to content

Commit

Permalink
Add "Split image along axes" tool (#141)
Browse files Browse the repository at this point in the history
* Add "Split image along axes" tool

* Add "Split image along axes" tool

* Add tests and fix bugs

* Bump giatools to 0.3.2

* Update split.py
  • Loading branch information
kostrykin authored Mar 7, 2025
1 parent f546b3c commit f105236
Show file tree
Hide file tree
Showing 17 changed files with 163 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ If Galaxy Image Analysis helped with the analysis of your data, please do not fo
- [Scale image](https://usegalaxy.eu/root?tool_id=toolshed.g2.bx.psu.edu/repos/imgteam/scale_image/ip_scale_image) with scikit-image
- [Show image info](https://usegalaxy.eu/root?tool_id=toolshed.g2.bx.psu.edu/repos/imgteam/image_info/ip_imageinfo) with Bioformats
- [Slice image into patches](https://usegalaxy.eu/root?tool_id=toolshed.g2.bx.psu.edu/repos/imgteam/slice_image/ip_slice_image)
- [Split image along axes](https://usegalaxy.eu/root?tool_id=toolshed.g2.bx.psu.edu/repos/imgteam/split_image/ip_split_image) with NumPy
- [Switch axis coordinates](https://usegalaxy.eu/root?tool_id=toolshed.g2.bx.psu.edu/repos/imgteam/imagecoordinates_flipaxis/imagecoordinates_flipaxis)

### Image conversion
Expand Down
8 changes: 8 additions & 0 deletions tools/split_image/.shed.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
categories:
- Imaging
description: Split image along axes
long_description: This tool splits an image along a specific axis (e.g., channels).
name: split_image
owner: imgteam
homepage_url: https://github.com/bmcv
remote_repository_url: https://github.com/BMCV/galaxy-image-analysis/tree/master/tools/split_image/
1 change: 1 addition & 0 deletions tools/split_image/creators.xml
44 changes: 44 additions & 0 deletions tools/split_image/split.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import argparse
import math

import giatools
import giatools.util
import numpy as np
import tifffile


parser = argparse.ArgumentParser()
parser.add_argument('input', type=str)
parser.add_argument('axis', type=str)
parser.add_argument('output', type=str)
parser.add_argument('--squeeze', action='store_true', default=False)
args = parser.parse_args()

# Validate and normalize input parameters
assert len(args.axis) == 1, 'Axis input must be a single character.'
axis = args.axis.replace('S', 'C')

# Read input image as TZYXC
img_in = giatools.Image.read(args.input)

# Determine the axis to split along
axis_pos = img_in.axes.index(axis)

# Perform the splitting
arr = np.moveaxis(img_in.data, axis_pos, 0)
decimals = math.ceil(math.log10(1 + arr.shape[0]))
output_filename_pattern = f'{args.output}/%0{decimals}d.tiff'
for img_idx, img in enumerate(arr):
img = np.moveaxis(img[None], 0, axis_pos)

# Optionally, squeeze the image
if args.squeeze:
s = [axis_pos for axis_pos in range(len(img_in.axes)) if img.shape[axis_pos] == 1 and img_in.axes[axis_pos] not in 'YX']
img = np.squeeze(img, axis=tuple(s))
img_axes = giatools.util.str_without_positions(img_in.axes, s)
else:
img_axes = img_in.axes

# Save the result
filename = output_filename_pattern % (img_idx + 1)
tifffile.imwrite(filename, img, metadata=dict(axes=img_axes))
108 changes: 108 additions & 0 deletions tools/split_image/split_image.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<tool id="ip_split_image" name="Split image along axes" version="@TOOL_VERSION@+galaxy@VERSION_SUFFIX@" profile="20.05">
<description>with NumPy</description>
<macros>
<import>creators.xml</import>
<import>tests.xml</import>
<token name="@TOOL_VERSION@">2.2.3</token>
<token name="@VERSION_SUFFIX@">0</token>
</macros>
<creator>
<expand macro="creators/bmcv"/>
</creator>
<edam_operations>
<edam_operation>operation_3443</edam_operation>
</edam_operations>
<requirements>
<requirement type="package" version="@TOOL_VERSION@">numpy</requirement>
<requirement type="package" version="0.3.2">giatools</requirement>
<requirement type="package" version="2024.7.24">tifffile</requirement>
</requirements>
<command detect_errors="aggressive"><![CDATA[
mkdir output &&
python '$__tool_directory__/split.py'
'$input'
'$axis'
output
$squeeze
]]></command>
<inputs>
<param name="input" type="data" format="tiff,png" label="Image to split" />
<param name="axis" type="select" label="Axis to split along">
<option value="T">T-axis (split the frames of a temporal image sequence)</option>
<option value="Z">Z-axis (split the slices of a 3-D image or image sequence)</option>
<option value="C" selected="true">C-axis (split the channels of an image or image sequence)</option>
<option value="S">S-axis (split the samples of an image or image sequence)</option>
</param>
<param name="squeeze" type="boolean" checked="false" truevalue="--squeeze" falsevalue="" label="Squeeze result imags" help="Only axes with more than one element will be retained in the result images. Does not apply for X and Y axes." />
</inputs>
<outputs>
<collection type="list" name="output" label="Split ${on_string} along ${axis} axis">
<discover_datasets directory="output" pattern="__name__" format="tiff" />
</collection>
</outputs>
<tests>

<!-- PNG tests -->
<test>
<param name="input" value="rgb1.png" />
<param name="axis" value="C" />
<param name="squeeze" value="false" />
<output_collection name="output" type="list" count="3">
<expand macro="tests/intensity_image_diff/element" name="1.tiff" value="rgb1_r.tiff" ftype="tiff"/>
<expand macro="tests/intensity_image_diff/element" name="2.tiff" value="rgb1_g.tiff" ftype="tiff"/>
<expand macro="tests/intensity_image_diff/element" name="3.tiff" value="rgb1_b.tiff" ftype="tiff"/>
</output_collection>
</test>

<!-- TIFF tests -->
<test>
<param name="input" value="zcyx.tiff" />
<param name="axis" value="Z" />
<param name="squeeze" value="false" />
<output_collection name="output" type="list" count="25">
<expand macro="tests/intensity_image_diff/element" name="01.tiff" value="zcyx_slice01.tiff" ftype="tiff"/>
<expand macro="tests/intensity_image_diff/element" name="25.tiff" value="zcyx_slice25.tiff" ftype="tiff"/>
</output_collection>
</test>

<!-- Test squeezing -->
<test>
<param name="input" value="rgb1.png" />
<param name="axis" value="C" />
<param name="squeeze" value="true" />
<output_collection name="output" type="list" count="3">
<expand macro="tests/intensity_image_diff/element" name="1.tiff" value="rgb1_squeezed_r.tiff" ftype="tiff"/>
<expand macro="tests/intensity_image_diff/element" name="2.tiff" value="rgb1_squeezed_g.tiff" ftype="tiff"/>
<expand macro="tests/intensity_image_diff/element" name="3.tiff" value="rgb1_squeezed_b.tiff" ftype="tiff"/>
</output_collection>
</test>

<!-- Test with missing axes -->
<test>
<param name="input" value="rgb1.png" />
<param name="axis" value="Z" />
<param name="squeeze" value="false" />
<output_collection name="output" type="list" count="1">
<expand macro="tests/intensity_image_diff/element" name="1.tiff" value="rgb1_split_z.tiff" ftype="tiff"/>
</output_collection>
</test>

</tests>
<help>

**Splits an image along a specific axis (e.g., channels).**

This tool splits an image along a specifc axis and yields a collection of images.
This can be used, for example, to convert a multi-channel image into a collection of single-channel images.

The pixel data type of the split image is preserved (will be the same as the input image).

</help>
<citations>
<citation type="doi">10.1038/s41586-020-2649-2</citation>
</citations>
</tool>
Binary file added tools/split_image/test-data/rgb1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tools/split_image/test-data/rgb1_b.tiff
Binary file not shown.
Binary file added tools/split_image/test-data/rgb1_g.tiff
Binary file not shown.
Binary file added tools/split_image/test-data/rgb1_r.tiff
Binary file not shown.
Binary file added tools/split_image/test-data/rgb1_split_z.tiff
Binary file not shown.
Binary file added tools/split_image/test-data/rgb1_squeezed_b.tiff
Binary file not shown.
Binary file added tools/split_image/test-data/rgb1_squeezed_g.tiff
Binary file not shown.
Binary file added tools/split_image/test-data/rgb1_squeezed_r.tiff
Binary file not shown.
Binary file added tools/split_image/test-data/zcyx.tiff
Binary file not shown.
Binary file added tools/split_image/test-data/zcyx_slice01.tiff
Binary file not shown.
Binary file added tools/split_image/test-data/zcyx_slice25.tiff
Binary file not shown.
1 change: 1 addition & 0 deletions tools/split_image/tests.xml

0 comments on commit f105236

Please sign in to comment.