changeset 4:3179853faae9 draft

planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tree/master/tools/scale_image/ commit c045f067a57e8308308cf6329060c7ccd3fc372f
author imgteam
date Thu, 04 Apr 2024 15:26:23 +0000
parents d09507d3fb0e
children 85666e555698
files creators.xml scale_image.py scale_image.xml test-data/anisotropic.png test-data/input1_binary_rgb.png test-data/input2_normalized.tiff test-data/input3_not_normalized.tiff test-data/normalized.tiff test-data/not_normalized.tiff test-data/out.png test-data/out2.png test-data/sample1.png test-data/uniform.png test-data/uniform_binary.png tests.xml
diffstat 15 files changed, 220 insertions(+), 61 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/creators.xml	Thu Apr 04 15:26:23 2024 +0000
@@ -0,0 +1,23 @@
+<macros>
+
+    <xml name="creators/bmcv">
+        <organization name="Biomedical Computer Vision Group, Heidelberg Universtiy" alternateName="BMCV" url="http://www.bioquant.uni-heidelberg.de/research/groups/biomedical_computer_vision.html" />
+        <yield />
+    </xml>
+
+    <xml name="creators/alliecreason">
+        <person givenName="Allison" familyName="Creason"/>
+        <yield/>
+    </xml>
+
+    <xml name="creators/bugraoezdemir">
+        <person givenName="Bugra" familyName="Oezdemir"/>
+        <yield/>
+    </xml>
+
+    <xml name="creators/thawn">
+        <person givenName="Till" familyName="Korten"/>
+        <yield/>
+    </xml>
+    
+</macros>
--- a/scale_image.py	Fri Nov 10 14:23:33 2023 +0000
+++ b/scale_image.py	Thu Apr 04 15:26:23 2024 +0000
@@ -1,38 +1,50 @@
 import argparse
 import sys
 
-import scipy.misc
+import numpy as np
 import skimage.io
 import skimage.transform
+import skimage.util
 from PIL import Image
 
 
-def scale_image(input_file, output_file, scale, order=1):
+def scale_image(input_file, output_file, scale, order, antialias):
     Image.MAX_IMAGE_PIXELS = 50000 * 50000
-    img_in = skimage.io.imread(input_file)
-    if order == 0:
-        interp = 'nearest'
-    elif order == 1:
-        interp = 'bilinear'
-    elif order == 2:
-        interp = 'bicubic'
+    im = skimage.io.imread(input_file)
+
+    # Parse `--scale` argument
     if ',' in scale:
-        scale = scale[1:-1].split(',')
-        scale = [int(i) for i in scale]
-    elif '.' in scale:
+        scale = [float(s.strip()) for s in scale.split(',')]
+        assert len(scale) <= im.ndim, f'Image has {im.ndim} axes, but scale factors were given for {len(scale)} axes.'
+        scale = scale + [1] * (im.ndim - len(scale))
+
+    else:
         scale = float(scale)
-    else:
-        scale = int(scale)
-    res = scipy.misc.imresize(img_in, scale, interp=interp)
+
+        # For images with 3 or more axes, the last axis is assumed to correspond to channels
+        if im.ndim >= 3:
+            scale = [scale] * (im.ndim - 1) + [1]
+
+    # Do the scaling
+    res = skimage.transform.rescale(im, scale, order, anti_aliasing=antialias, preserve_range=True)
+
+    # Preserve the `dtype` so that both brightness and range of values is preserved
+    if res.dtype != im.dtype:
+        if np.issubdtype(im.dtype, np.integer):
+            res = res.round()
+        res = res.astype(im.dtype)
+
+    # Save result
     skimage.io.imsave(output_file, res)
 
 
 if __name__ == "__main__":
     parser = argparse.ArgumentParser()
-    parser.add_argument('input_file', type=argparse.FileType('r'), default=sys.stdin, help='input file')
-    parser.add_argument('out_file', type=argparse.FileType('w'), default=sys.stdin, help='out file (PNG)')
-    parser.add_argument('scale', type=str, help='fraction scaling factor(float), percentage scaling factor(int), output size(tuple(height,width))')  # integer option not implemented in galaxy wrapper
-    parser.add_argument('order', type=int, default=1, help='interpolation method')
+    parser.add_argument('input_file', type=argparse.FileType('r'), default=sys.stdin)
+    parser.add_argument('out_file', type=argparse.FileType('w'), default=sys.stdin)
+    parser.add_argument('--scale', type=str, required=True)
+    parser.add_argument('--order', type=int, required=True)
+    parser.add_argument('--antialias', default=False, action='store_true')
     args = parser.parse_args()
 
-    scale_image(args.input_file.name, args.out_file.name, args.scale, args.order)
+    scale_image(args.input_file.name, args.out_file.name, args.scale, args.order, args.antialias)
--- a/scale_image.xml	Fri Nov 10 14:23:33 2023 +0000
+++ b/scale_image.xml	Thu Apr 04 15:26:23 2024 +0000
@@ -1,5 +1,14 @@
-<tool id="ip_scale_image" name="Scale image" version="0.4-2"> 
+<tool id="ip_scale_image" name="Scale image" version="@TOOL_VERSION@+galaxy@VERSION_SUFFIX@" profile="20.05"> 
     <description>with scikit-image</description>
+    <macros>
+        <import>creators.xml</import>
+        <import>tests.xml</import>
+        <token name="@TOOL_VERSION@">0.18.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>
@@ -8,64 +17,84 @@
         <xref type="biii">scikit-image</xref>
     </xrefs>
     <requirements>
-        <requirement type="package" version="6.1.0">pillow</requirement>
-        <requirement type="package" version="0.14.2">scikit-image</requirement>
-        <requirement type="package" version="1.15.4">numpy</requirement>
-        <requirement type="package" version="1.1.0">scipy</requirement>
-        <requirement type="package" version="0.15.1">tifffile</requirement>
+        <requirement type="package" version="@TOOL_VERSION@">scikit-image</requirement>
+        <requirement type="package" version="10.0.1">pillow</requirement>
+        <requirement type="package" version="1.24.4">numpy</requirement>
+        <requirement type="package" version="2021.7.2">tifffile</requirement>
     </requirements> 
-    <command detect_errors="aggressive">
-        <![CDATA[
-        python '$__tool_directory__/scale_image.py' '$input' ./out.png
-        #if $use_scale_option.use_scale == 'true'
-          $use_scale_option.scale
-        #else if $use_scale_option.use_scale == 'false'
-          '[$use_scale_option.scale_x, $use_scale_option.scale_y]'
-        #end if
-        $order
+    <command detect_errors="aggressive"><![CDATA[
+
+        python '$__tool_directory__/scale_image.py' '$input'
+
+        ./output.${input.ext}
 
-        ]]>
-    </command>
+        --scale '$scale'
+        --order  $order
+        $antialias
+
+        && mv ./output.${input.ext} ./output
+
+    ]]></command>
     <inputs>
         <param name="input" type="data" format="png" label="Image file"/>
-        <conditional name="use_scale_option"><!--a third option (using one integer as percentage scaling) could still be implemented-->
-            <param label="Lock scaling between image dimensions" name="use_scale" type="select">
-                <option selected="true" value="true">Lock</option>
-                <option value="false">Individual scales</option>
-            </param>
-            <when value="true">
-                <param name="scale" type="float" value="1.0" label="Scaling factor" />
-            </when>
-            <when value="false">
-                <param name="scale_x" type="integer" value="100" label="New height of the image in pixels" />
-                <param name="scale_y" type="integer" value="100" label="New width of the image in pixels" />
-            </when>
-        </conditional>
-        <param name="order" type="select" label="Interpolation method">
+        <param argument="--scale" type="text" value="1" label="Scaling factor" help="Use either a single scaling factor (uniform scaling), or a comma-separated list of scaling factors (anistropic scaling). For a 2-D single-channel or RGB image, the first scaling factor corresponds to the image width and the second corresponds to the image height. For images with 3 or more axes, the last axis is assumed to correspond to the image channels if uniform scaling is used (a single value)."/>
+        <param argument="--order" type="select" label="Interpolation method">
             <option value="0">Nearest-neighbor</option>
             <option value="1" selected="true">Bi-linear</option>
             <option value="2">Bi-cubic</option>
         </param>
+        <param name="antialias" type="boolean" truevalue="--antialias" falsevalue="" checked="true" label="Enable anti-aliasing" help="This should only be used for down-scaling."/>
     </inputs>
     <outputs>
-        <data format="png" name="output" from_work_dir="out.png"/>
+        <data name="output" from_work_dir="output" format_source="input" metadata_source="input"/>
     </outputs>
     <tests>
+        <!-- Test PNG, without antialias -->
         <test>
-            <param name="input" value="sample1.png"/> <!--continue here-->
-            <conditional name="use_scale_option">
-                <param name="use_scale" value="false"/>
-                <param name="scale_x" value="200"/>
-                <param name="scale_y" value="150"/>
-            </conditional>
+            <param name="input" value="input1_binary_rgb.png"/>
+            <param name="scale" value="0.5"/>
+            <param name="antialias" value="false"/>
             <param name="order" value="0"/>
-            <output name="output" value="out2.png" ftype="png" compare="sim_size"/>
+            <expand macro="tests/binary_image_diff" name="output" value="uniform_binary.png" ftype="png"/>
+        </test>
+        <!-- Test PNG, uniform scaling -->
+        <test>
+            <param name="input" value="input1_binary_rgb.png"/>
+            <param name="scale" value="0.5"/>
+            <expand macro="tests/intensity_image_diff" name="output" value="uniform.png" ftype="png"/>
+        </test>
+        <!-- Test PNG, anistropic scaling -->
+        <test>
+            <param name="input" value="input1_binary_rgb.png"/>
+            <param name="scale" value="0.5, 0.8"/>
+            <expand macro="tests/intensity_image_diff" name="output" value="anisotropic.png" ftype="png"/>
+        </test>
+        <test>
+            <param name="input" value="input1_binary_rgb.png"/>
+            <param name="scale" value="0.5, 0.8, 1"/>
+            <expand macro="tests/intensity_image_diff" name="output" value="anisotropic.png" ftype="png"/>
+        </test>
+        <!-- Test TIFF, normalized -->
+        <test>
+            <param name="input" value="input2_normalized.tiff"/>
+            <param name="scale" value="0.5"/>
+            <expand macro="tests/intensity_image_diff" name="output" value="normalized.tiff" ftype="tiff"/>
+        </test>
+        <!-- Test TIFF, not normalized -->
+        <test>
+            <param name="input" value="input3_not_normalized.tiff"/>
+            <param name="scale" value="0.5"/>
+            <expand macro="tests/intensity_image_diff" name="output" value="not_normalized.tiff" ftype="tiff"/>
         </test>
     </tests>
     <help>
-    **What it does**
+
+        **Scales an image using one or more scaling factors.**
 
-    This tool scales an image using the scaling factor.
+        The image is rescaled uniformly along all axes, or anistropically if multiple scale factors are given.
+
+        This operation preserves both the brightness of the image, and the range of values.
+
     </help>
     <citations>
         <citation type="doi">10.1016/j.jbiotec.2017.07.019</citation>
Binary file test-data/anisotropic.png has changed
Binary file test-data/input1_binary_rgb.png has changed
Binary file test-data/input2_normalized.tiff has changed
Binary file test-data/input3_not_normalized.tiff has changed
Binary file test-data/normalized.tiff has changed
Binary file test-data/not_normalized.tiff has changed
Binary file test-data/out.png has changed
Binary file test-data/out2.png has changed
Binary file test-data/sample1.png has changed
Binary file test-data/uniform.png has changed
Binary file test-data/uniform_binary.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests.xml	Thu Apr 04 15:26:23 2024 +0000
@@ -0,0 +1,95 @@
+<macros>
+
+    <!-- Macros for verification of image outputs -->
+
+    <xml
+        name="tests/binary_image_diff"
+        tokens="name,value,ftype,metric,eps"
+        token_metric="mae"
+        token_eps="0.01">
+
+        <output name="@NAME@" value="@VALUE@" ftype="@FTYPE@" compare="image_diff" metric="@METRIC@" eps="@EPS@" pin_labels="0">
+            <assert_contents>
+                <has_image_n_labels n="2"/>
+                <yield/>
+            </assert_contents>
+        </output>
+
+    </xml>
+
+    <xml
+        name="tests/label_image_diff"
+        tokens="name,value,ftype,metric,eps,pin_labels"
+        token_metric="iou"
+        token_eps="0.01"
+        token_pin_labels="0">
+
+        <output name="@NAME@" value="@VALUE@" ftype="@FTYPE@" compare="image_diff" metric="@METRIC@" eps="@EPS@" pin_labels="@PIN_LABELS@">
+            <assert_contents>
+                <yield/>
+            </assert_contents>
+        </output>
+
+    </xml>
+
+    <xml
+        name="tests/intensity_image_diff"
+        tokens="name,value,ftype,metric,eps"
+        token_metric="rms"
+        token_eps="0.01">
+
+        <output name="@NAME@" value="@VALUE@" ftype="@FTYPE@" compare="image_diff" metric="@METRIC@" eps="@EPS@">
+            <assert_contents>
+                <yield/>
+            </assert_contents>
+        </output>
+
+    </xml>
+
+    <!-- Variants of the above for verification of collection elements -->
+
+    <xml
+        name="tests/binary_image_diff/element"
+        tokens="name,value,ftype,metric,eps"
+        token_metric="mae"
+        token_eps="0.01">
+
+        <element name="@NAME@" value="@VALUE@" ftype="@FTYPE@" compare="image_diff" metric="@METRIC@" eps="@EPS@" pin_labels="0">
+            <assert_contents>
+                <has_image_n_labels n="2"/>
+                <yield/>
+            </assert_contents>
+        </element>
+
+    </xml>
+
+    <xml
+        name="tests/label_image_diff/element"
+        tokens="name,value,ftype,metric,eps"
+        token_metric="iou"
+        token_eps="0.01"
+        token_pin_labels="0">
+
+        <element name="@NAME@" value="@VALUE@" ftype="@FTYPE@" compare="image_diff" metric="@METRIC@" eps="@EPS@" pin_labels="@PIN_LABELS@">
+            <assert_contents>
+                <yield/>
+            </assert_contents>
+        </element>
+
+    </xml>
+
+    <xml
+        name="tests/intensity_image_diff/element"
+        tokens="name,value,ftype,metric,eps"
+        token_metric="rms"
+        token_eps="0.01">
+
+        <element name="@NAME@" value="@VALUE@" ftype="@FTYPE@" compare="image_diff" metric="@METRIC@" eps="@EPS@">
+            <assert_contents>
+                <yield/>
+            </assert_contents>
+        </element>
+
+    </xml>
+
+</macros>