changeset 2:b74693340624 draft

planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tree/master/tools/overlay_images/ commit 71dae1df58f579b84d4f9d92fb0dd509c02dd48f
author imgteam
date Thu, 10 Aug 2023 07:29:34 +0000
parents bf590a9733ed
children 22ff7c705a83
files contours.py overlay_images.py overlay_images.xml test-data/mask2.tif test-data/test3.tif test-data/test4.tif
diffstat 6 files changed, 112 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contours.py	Thu Aug 10 07:29:34 2023 +0000
@@ -0,0 +1,49 @@
+"""
+Copyright (c) 2017-2023 Leonid Kostrykin, Biomedical Computer Vision Group, Heidelberg University.
+
+Distributed under the MIT license.
+See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
+"""
+
+import numpy as np
+import skimage.morphology as morph
+
+
+class ContourPaint:
+    """Yields masks corresponding to contours of objects.
+
+    :param fg_mask: Binary mask of the image foreground. Any contour never overlaps the image foreground except of those image regions corresponding to the contoured object itself.
+    :param thickness: The thickness of the contour (width, in pixels).
+    :param where: The position of the contour (``inner``, ``center``, or ``outer``).
+    """
+
+    def __init__(self, fg_mask, thickness, where='center'):
+        assert where in ('inner', 'center', 'outer')
+        self.fg_mask = fg_mask
+        self.where = where
+        self.thickness = thickness
+        if where == 'inner':
+            self.selem_inner = morph.disk(self.thickness)
+            self.selem_outer = None
+        elif where == 'center':
+            self.selem_inner = morph.disk(self.thickness - self.thickness // 2)
+            self.selem_outer = morph.disk(self.thickness // 2)
+        elif where == 'outer':
+            self.selem_inner = None
+            self.selem_outer = morph.disk(self.thickness)
+
+    def get_contour_mask(self, mask):
+        """Returns the binary mask of the contour of an object.
+
+        :param mask: Binary mask of an object.
+        :return: Binary mask of the contour
+        """
+        if self.selem_inner is not None:
+            inner_contour = np.logical_xor(mask, morph.binary_erosion(mask, self.selem_inner))
+        else:
+            inner_contour = np.zeros(mask.shape, bool)
+        if self.selem_outer is not None:
+            outer_contour = np.logical_and(np.logical_not(self.fg_mask), morph.binary_dilation(mask, self.selem_outer))
+        else:
+            outer_contour = np.zeros(mask.shape, bool)
+        return np.logical_or(inner_contour, outer_contour)
--- a/overlay_images.py	Tue Jul 19 08:52:04 2022 +0000
+++ b/overlay_images.py	Thu Aug 10 07:29:34 2023 +0000
@@ -1,19 +1,20 @@
 """
-Copyright 2022 Biomedical Computer Vision Group, Heidelberg University.
+Copyright 2022-2023 Biomedical Computer Vision Group, Heidelberg University.
 
 Distributed under the MIT license.
 See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
-
 """
 
 import argparse
 
+import matplotlib.colors
 import matplotlib.pyplot as plt
 import numpy as np
 import skimage.color
 import skimage.io
 import skimage.measure
 import tifffile
+from contours import ContourPaint
 
 
 def read_im_gray(fn):
@@ -28,6 +29,21 @@
         return img
 
 
+def get_rgb8_copy(img):
+    img = np.squeeze(img)
+    assert img.ndim == 2 or (img.ndim == 3 and img.shape[-1] in (3, 4))
+    if str(img.dtype).startswith('float'):
+        img = np.round(img * 255).astype('uint8')
+    elif img.dtype == 'uint16':
+        img = img // 256
+    elif img.dtype != 'uint8':
+        raise ValueError(f'unknown dtype: {img.dtype}')
+    if img.ndim == 2:
+        return np.dstack([img] * 3).copy()
+    else:
+        return img[:, :, :3].copy()
+
+
 def coloc_vis(in_red_fn, in_green_fn, out_fn):
     im1 = read_im_gray(in_red_fn)
     im2 = read_im_gray(in_green_fn)
@@ -53,23 +69,32 @@
         skimage.io.imsave(out_fn, out_im.astype(im1.dtype))  # format of output is the same as input
 
 
-def seg_contour(im1_fn, im2_fn, out_fn, linewidth=0.3, color='#ff0000', show_label=False):
+def seg_contour(im1_fn, im2_fn, out_fn, linewidth, color='#ff0000', show_label=False, label_color='#ffff00'):
     img = skimage.io.imread(im1_fn)
-    label = skimage.io.imread(im2_fn)
+    labels = skimage.io.imread(im2_fn)
+
+    result = get_rgb8_copy(img)
+    cp = ContourPaint(labels, linewidth, where='center')
+    color_rgb = np.multiply(255, matplotlib.colors.to_rgb(color))
 
-    fig = plt.figure()
-    ax = fig.add_axes([0, 0, 1, 1])
-    ax.axis('off')
+    for label in np.unique(labels):
+        if label > 0:
+            cc = (labels == label)
+            bd = cp.get_contour_mask(cc)
+            for i in range(3):
+                result[:, :, i][bd] = color_rgb[i]
+
     if show_label:
-        for reg in skimage.measure.regionprops(label):
-            ax.text(reg.centroid[1], reg.centroid[0], str(reg.label), color=color)
+        fig = plt.figure(figsize=np.divide(img.shape[:2][::-1], 100), dpi=100)
+        ax = fig.add_axes([0, 0, 1, 1])
+        ax.axis('off')
+        ax.imshow(result)
+        for reg in skimage.measure.regionprops(labels):
+            ax.text(reg.centroid[1], reg.centroid[0], str(reg.label), color=label_color)
+        fig.canvas.print_png(out_fn)
 
-    if len(img.shape) == 2:
-        plt.imshow(img, cmap=plt.cm.gray)
     else:
-        plt.imshow(img)
-    plt.contour(label, linewidths=linewidth, colors=color)
-    fig.canvas.print_png(out_fn)  # output is RGB
+        skimage.io.imsave(out_fn, result)  # format of output is RGB8
 
 
 if __name__ == "__main__":
@@ -79,9 +104,10 @@
     parser.add_argument("out", help="Output image")
     parser.add_argument('--method', dest='method', default='coloc_vis', help='How to overlay images')
     parser.add_argument('--alpha', dest='alpha', default=0.5, type=float, help='Blending weight')
-    parser.add_argument('--thickness', dest='thickness', default=0.3, type=float, help='Contour thickness')
-    parser.add_argument('--color', dest='color', default='#FFFF00', help='Contour color')
-    parser.add_argument('--show_label', dest='show_label', action='store_true', help='Plot label')
+    parser.add_argument('--thickness', dest='thickness', default=2, type=int, help='Contour thickness')
+    parser.add_argument('--color', dest='color', default='#FF0000', help='Contour color')
+    parser.add_argument('--show_label', dest='show_label', action='store_true', help='Show labels')
+    parser.add_argument('--label_color', dest='label_color', default='#FFFF00', help='Label color')
     args = parser.parse_args()
 
     if args.method == 'coloc_vis':
@@ -92,4 +118,5 @@
         seg_contour(args.im1, args.im2, args.out,
                     linewidth=args.thickness,
                     color=args.color,
-                    show_label=args.show_label)
+                    show_label=args.show_label,
+                    label_color=args.label_color)
--- a/overlay_images.xml	Tue Jul 19 08:52:04 2022 +0000
+++ b/overlay_images.xml	Thu Aug 10 07:29:34 2023 +0000
@@ -1,4 +1,4 @@
-<tool id="ip_overlay_images" name="Overlay Images" version="0.0.2" profile="20.05"> 
+<tool id="ip_overlay_images" name="Overlay Images" version="0.0.3" profile="20.05"> 
     <description>for visualization</description>
     <requirements>
         <requirement type="package" version="0.18.1">scikit-image</requirement> 
@@ -15,8 +15,9 @@
             --alpha $method_option.alpha
         #elif $method_option.method == "seg_contour"
             --thickness $method_option.thickness
-            --color '$method_option.colour'
+            --color '$method_option.color'
             $method_option.show_label
+            --label_color '$method_option.label_color'
         #end if
     ]]>
     </command>
@@ -39,9 +40,10 @@
             <when value="seg_contour">
                 <param name="im1" type="data" format="tiff,png" label="Image" />
                 <param name="im2" type="data" format="tiff,png" label="Label image" />
-                <param name="thickness" type="float" value="0.3" label="Contour thickness" />
-                <param name="colour" type="color" value="#ff0000" label="Contour color"/>
+                <param name="thickness" type="integer" value="2" min="1" label="Contour thickness (in pixels)" />
+                <param name="color" type="color" value="#ff0000" label="Contour color"/>
                 <param argument="--show_label" type="boolean" checked='false' truevalue="--show_label" falsevalue="" label="Show labels" />
+                <param name="label_color" type="color" value="#ffff00" label="Label color"/>
             </when>
         </conditional>
     </inputs>
@@ -66,10 +68,18 @@
             <param name="im1" value="sample1.tif"/>
             <param name="im2" value="mask1.tif"/>
             <param name="method" value="seg_contour"/>
-            <param name="thickness" value="0.4"/>
-            <param name="colour" value="#ffaa00"/>
+            <param name="thickness" value="2"/>
+            <param name="color" value="#ff0000"/>
             <param name="show_label" value="--show_label"/>
-            <output name="out" value="test3.tif" ftype="tiff" compare="sim_size" delta="20000" delta_frac="0.2"/>
+            <output name="out" value="test3.tif" ftype="tiff" compare="sim_size" delta_frac="0.1"/>
+        </test>
+        <test>
+            <param name="im1" value="sample1.tif"/>
+            <param name="im2" value="mask2.tif"/>
+            <param name="method" value="seg_contour"/>
+            <param name="thickness" value="2"/>
+            <param name="color" value="#ff0000"/>
+            <output name="out" value="test4.tif" ftype="tiff" compare="sim_size" delta_frac="0.1"/>
         </test>
     </tests>
     <help>
@@ -78,5 +88,6 @@
     This tool overlays two image to visualize 1) image blending, 2) colocalization, or 3) a segmentation mask over an image.
     </help>
     <citations>
+        <citation type="doi">10.1016/j.jbiotec.2017.07.019</citation>
     </citations>
 </tool>
Binary file test-data/mask2.tif has changed
Binary file test-data/test3.tif has changed
Binary file test-data/test4.tif has changed