Repository 'points2labelimage'
hg clone https://toolshed.g2.bx.psu.edu/repos/imgteam/points2labelimage

Changeset 5:4a49f74a3c14 (2025-05-12)
Previous changeset 4:aef9911c0d5c (2025-04-23) Next changeset 6:22bb32eae6a1 (2025-11-06)
Commit message:
planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tree/master/tools/points2labelimage/ commit 9a40a5d1e1008c26cc327c6d163df2a1af22a1a0
modified:
creators.xml
points2label.py
points2label.xml
added:
test-data/output1_binary.tiff
test-data/output2.tiff
test-data/output2_binary.tiff
test-data/output3.tiff
test-data/output3_binary.tiff
test-data/output4.tiff
test-data/output5.tiff
test-data/output6.tiff
test-data/rois-illegal1.geojson
test-data/rois-illegal2.geojson
test-data/rois-noname.geojson
test-data/rois.geojson
removed:
test-data/output1_binary.tif
test-data/output2.tif
test-data/output2_binary.tif
test-data/output3.tif
test-data/output3_binary.tif
test-data/output4.tif
b
diff -r aef9911c0d5c -r 4a49f74a3c14 creators.xml
--- a/creators.xml Wed Apr 23 14:37:42 2025 +0000
+++ b/creators.xml Mon May 12 14:01:26 2025 +0000
b
@@ -24,5 +24,10 @@
         <person givenName="Till" familyName="Korten"/>
         <yield/>
     </xml>
-    
+
+    <xml name="creators/pavanvidem">
+        <person givenName="Pavan" familyName="Videm"/>
+        <yield/>
+    </xml>
+
 </macros>
b
diff -r aef9911c0d5c -r 4a49f74a3c14 points2label.py
--- a/points2label.py Wed Apr 23 14:37:42 2025 +0000
+++ b/points2label.py Mon May 12 14:01:26 2025 +0000
[
@@ -1,15 +1,82 @@
 import argparse
+import json
 import os
 import warnings
+from typing import (
+    Dict,
+    List,
+    Tuple,
+    Union,
+)
 
 import giatools.pandas
 import numpy as np
+import numpy.typing as npt
 import pandas as pd
 import scipy.ndimage as ndi
 import skimage.io
 import skimage.segmentation
 
 
+def is_rectangular(points: Union[List[Tuple[float, float]], npt.NDArray]) -> bool:
+    points = np.asarray(points)
+
+    # Rectangle must have 5 points, where first and last are identical
+    if len(points) != 5 or not (points[0] == points[-1]).all():
+        return False
+
+    # Check that all edges align with the axes
+    edges = points[1:] - points[:-1]
+    if any((edge == 0).sum() != 1 for edge in edges):
+        return False
+
+    # All checks have passed, the geometry is rectangular
+    return True
+
+
+def geojson_to_tabular(geojson: Dict):
+    rows = []
+    labels = []
+    for feature in geojson['features']:
+        assert feature['geometry']['type'].lower() == 'polygon', (
+            f'Unsupported geometry type: "{feature["geometry"]["type"]}"'
+        )
+        coords = feature['geometry']['coordinates'][0]
+
+        # Properties and name (label) are optional
+        try:
+            label = feature['properties']['name']
+        except KeyError:
+            label = max(labels, default=0) + 1
+        labels.append(label)
+
+        # Read geometry
+        xs = [pt[0] for pt in coords]
+        ys = [pt[1] for pt in coords]
+
+        x = min(xs)
+        y = min(ys)
+
+        width = max(xs) + 1 - x
+        height = max(ys) + 1 - y
+
+        # Validate geometry (must be rectangular)
+        assert is_rectangular(list(zip(xs, ys)))
+
+        # Append the rectangle
+        rows.append({
+            'pos_x': x,
+            'pos_y': y,
+            'width': width,
+            'height': height,
+            'label': label,
+        })
+    df = pd.DataFrame(rows)
+    point_file = './point_file.tabular'
+    df.to_csv(point_file, sep='\t', index=False)
+    return point_file
+
+
 def rasterize(point_file, out_file, shape, has_header=False, swap_xy=False, bg_value=0, fg_value=None):
 
     img = np.full(shape, dtype=np.uint16, fill_value=bg_value)
@@ -122,16 +189,16 @@
                 img[y, x] = label
 
     else:
-        raise Exception("{} is empty or does not exist.".format(point_file))  # appropriate built-in error?
+        raise Exception('{} is empty or does not exist.'.format(point_file))  # appropriate built-in error?
 
     with warnings.catch_warnings():
         warnings.simplefilter("ignore")
         skimage.io.imsave(out_file, img, plugin='tifffile')  # otherwise we get problems with the .dat extension
 
 
-if __name__ == "__main__":
+if __name__ == '__main__':
     parser = argparse.ArgumentParser()
-    parser.add_argument('point_file', type=argparse.FileType('r'), help='point file')
+    parser.add_argument('in_file', type=argparse.FileType('r'), help='Input point file or GeoJSON file')
     parser.add_argument('out_file', type=str, help='out file (TIFF)')
     parser.add_argument('shapex', type=int, help='shapex')
     parser.add_argument('shapey', type=int, help='shapey')
@@ -141,11 +208,25 @@
 
     args = parser.parse_args()
 
+    point_file = args.in_file.name
+    has_header = args.has_header
+
+    try:
+        with open(args.in_file.name, 'r') as f:
+            content = json.load(f)
+            if isinstance(content, dict) and content.get('type') == 'FeatureCollection' and isinstance(content.get('features'), list):
+                point_file = geojson_to_tabular(content)
+                has_header = True  # header included in the converted file
+            else:
+                raise ValueError('Input is a JSON file but not a valid GeoJSON file')
+    except json.JSONDecodeError:
+        print('Input is not a valid JSON file. Assuming it a tabular file.')
+
     rasterize(
-        args.point_file.name,
+        point_file,
         args.out_file,
         (args.shapey, args.shapex),
-        has_header=args.has_header,
+        has_header=has_header,
         swap_xy=args.swap_xy,
         fg_value=0xffff if args.binary else None,
     )
b
diff -r aef9911c0d5c -r 4a49f74a3c14 points2label.xml
--- a/points2label.xml Wed Apr 23 14:37:42 2025 +0000
+++ b/points2label.xml Mon May 12 14:01:26 2025 +0000
[
b'@@ -1,10 +1,10 @@\n-<tool id="ip_points_to_label" name="Convert point coordinates to label map" version="@TOOL_VERSION@+galaxy@VERSION_SUFFIX@" profile="20.05">\n+<tool id="ip_points_to_label" name="Convert coordinates to label map" version="@TOOL_VERSION@+galaxy@VERSION_SUFFIX@" profile="20.05">\n     <description></description>\n     <macros>\n         <import>creators.xml</import>\n         <import>tests.xml</import>\n         <token name="@TOOL_VERSION@">0.4.1</token>\n-        <token name="@VERSION_SUFFIX@">0</token>\n+        <token name="@VERSION_SUFFIX@">1</token>\n     </macros>\n     <creator>\n         <expand macro="creators/bmcv" />\n@@ -15,12 +15,12 @@\n     <xrefs>\n         <xref type="bio.tools">galaxy_image_analysis</xref>\n     </xrefs>\n-    <requirements> \n-        <requirement type="package" version="0.21">scikit-image</requirement> \n+    <requirements>\n+        <requirement type="package" version="0.21">scikit-image</requirement>\n         <requirement type="package" version="1.26.4">numpy</requirement>\n         <requirement type="package" version="1.2.4">pandas</requirement>\n         <requirement type="package" version="2024.6.18">tifffile</requirement>\n-        <requirement type="package" version="0.3.1">giatools</requirement> \n+        <requirement type="package" version="0.3.1">giatools</requirement>\n     </requirements>\n     <command detect_errors="aggressive"><![CDATA[\n \n@@ -34,13 +34,13 @@\n         $binary\n \n     ]]></command>\n-    <inputs> \n-        <param name="input" type="data" format="tabular" label="Tabular list of points"/> \n+    <inputs>\n+        <param name="input" type="data" format="tabular,geojson" label="List of points in tabular or geojson format"/>\n         <param name="shapex" type="integer" value="500" min="1" label="Width of output image" />\n         <param name="shapey" type="integer" value="500" min="1" label="Height of output image" />\n-        <param name="has_header" type="boolean" checked="true" truevalue="--has_header True" falsevalue="" optional="true" label="Tabular list of points has header" help="Turning this off will ignore the first row and assume that the X and Y coordinates correspond to the first and second column, respectively." /> \n+        <param name="has_header" type="boolean" checked="true" truevalue="--has_header True" falsevalue="" optional="true" label="Tabular list of points has header" help="Turning this off will ignore the first row and assume that the X and Y coordinates correspond to the first and second column, respectively. Ignored, if GeoJSON is used for input." />\n         <param name="swap_xy" type="boolean" checked="false" falsevalue="" truevalue="--swap_xy True" optional="true" label="Swap X and Y coordinates" help="Swap the X and Y coordinates, regardless of whether the tabular list has a header or not." />\n-        <param name="binary" type="boolean" checked="false" truevalue="--binary True" falsevalue="" optional="true" label="Produce binary image" help="Use the same label for all points (65535)." /> \n+        <param name="binary" type="boolean" checked="false" truevalue="--binary True" falsevalue="" optional="true" label="Produce binary image" help="Use the same label for all points (65535)." />\n     </inputs>\n     <outputs>\n         <data name="output" format="tiff" />\n@@ -54,7 +54,7 @@\n             <param name="has_header" value="false" />\n             <param name="swap_xy" value="true" />\n             <param name="binary" value="true" />\n-            <expand macro="tests/binary_image_diff" name="output" value="output1_binary.tif" ftype="tiff" />\n+            <expand macro="tests/binary_image_diff" name="output" value="output1_binary.tiff" ftype="tiff" />\n         </test>\n         <!-- Binary / TSV with header / Circles -->\n         <test>\n@@ -64,7 +64,7 @@\n             <param name="has_header" value="true" />\n             <param name="swap_xy" value="false" />\n             <param name="binary" value="true" />\n-            <expand macro="tests/binary_ima'..b'      </test>\n+        <test>\n+            <param name="input" value="rois-noname.geojson" />\n+            <param name="shapex" value="300" />\n+            <param name="shapey" value="300" />\n+            <param name="has_header" value="false" />\n+            <param name="swap_xy" value="false" />\n+            <param name="binary" value="false" />\n+            <expand macro="tests/label_image_diff" name="output" value="output5.tiff" ftype="tiff" />\n+        </test>\n+        <test>\n+            <param name="input" value="rois.geojson" />\n+            <param name="shapex" value="300" />\n+            <param name="shapey" value="300" />\n+            <param name="has_header" value="false" />\n+            <param name="swap_xy" value="true" />\n+            <param name="binary" value="false" />\n+            <expand macro="tests/label_image_diff" name="output" value="output6.tiff" ftype="tiff" />\n+        </test>\n+        <test expect_failure="true">\n+            <!-- The test should fail because the GeoJSON is not rectangular -->\n+            <param name="input" value="rois-illegal1.geojson" />\n+            <param name="shapex" value="300" />\n+            <param name="shapey" value="300" />\n+            <param name="has_header" value="false" />\n+            <param name="swap_xy" value="false" />\n+            <param name="binary" value="false" />\n+        </test>\n+        <test expect_failure="true">\n+            <!-- The test should fail because the GeoJSON is not of `Polygon` type -->\n+            <param name="input" value="rois-illegal2.geojson" />\n+            <param name="shapex" value="300" />\n+            <param name="shapey" value="300" />\n+            <param name="has_header" value="false" />\n+            <param name="swap_xy" value="false" />\n+            <param name="binary" value="false" />\n         </test>\n     </tests>\n     <help>\n \n-        **Converts a tabular list of points to a label map by rasterizing the point coordinates.**\n+        **Converts a list of points to a label map by rasterizing the coordinates.**\n \n         The created image is a single-channel image with 16 bits per pixel (unsigned integer). The points are\n         rasterized with unique labels, or the value 65535 (white) for binary image output. Pixels not corresponding to\n         any points in the tabular file are assigned the value 0 (black).\n \n-        The tabular list of points can either be header-less. In this case, the first and second columns are expected\n-        to be the X and Y coordinates, respectively. Otherwise, if a header is present, it is searched for the\n-        following column names:\n+        **Using a tabular input file:** The tabular list of points can either be header-less. In this case, the first\n+        and second columns are expected to be the X and Y coordinates, respectively. Otherwise, if a header is present,\n+        it is searched for the following column names:\n \n         - ``pos_x`` or ``POS_X``: This column corresponds to the X coordinates.\n         - ``pos_y`` or ``POS_Y``: This column corresponds to the Y coordinates.\n@@ -126,10 +172,13 @@\n         - If ``width`` or ``WIDTH`` and ``height`` or ``HEIGHT`` columns are present, then the points will be rasterized\n           as rectangles of the corresponding size.\n         - If a ``label`` or ``LABEL`` column is present, then the corresponding labels will be used for rasterization\n-          (unless "Produce binary image" is activated). Different points are allowed to use the same label.\n+          (unless "Produce binary image" is activated). Different points are allowed to use the same label. If used, the\n+          label must be numeric and integer.\n+\n+        **Using a GeoJSON input file:** Only rectangular specifications of `Polygon` type geometry is supported.\n \n     </help>\n     <citations>\n-        <citation type="doi">10.1016/j.jbiotec.2017.07.019</citation> \n+        <citation type="doi">10.1016/j.jbiotec.2017.07.019</citation>\n     </citations>\n </tool>\n'
b
diff -r aef9911c0d5c -r 4a49f74a3c14 test-data/output1_binary.tif
b
Binary file test-data/output1_binary.tif has changed
b
diff -r aef9911c0d5c -r 4a49f74a3c14 test-data/output1_binary.tiff
b
Binary file test-data/output1_binary.tiff has changed
b
diff -r aef9911c0d5c -r 4a49f74a3c14 test-data/output2.tif
b
Binary file test-data/output2.tif has changed
b
diff -r aef9911c0d5c -r 4a49f74a3c14 test-data/output2.tiff
b
Binary file test-data/output2.tiff has changed
b
diff -r aef9911c0d5c -r 4a49f74a3c14 test-data/output2_binary.tif
b
Binary file test-data/output2_binary.tif has changed
b
diff -r aef9911c0d5c -r 4a49f74a3c14 test-data/output2_binary.tiff
b
Binary file test-data/output2_binary.tiff has changed
b
diff -r aef9911c0d5c -r 4a49f74a3c14 test-data/output3.tif
b
Binary file test-data/output3.tif has changed
b
diff -r aef9911c0d5c -r 4a49f74a3c14 test-data/output3.tiff
b
Binary file test-data/output3.tiff has changed
b
diff -r aef9911c0d5c -r 4a49f74a3c14 test-data/output3_binary.tif
b
Binary file test-data/output3_binary.tif has changed
b
diff -r aef9911c0d5c -r 4a49f74a3c14 test-data/output3_binary.tiff
b
Binary file test-data/output3_binary.tiff has changed
b
diff -r aef9911c0d5c -r 4a49f74a3c14 test-data/output4.tif
b
Binary file test-data/output4.tif has changed
b
diff -r aef9911c0d5c -r 4a49f74a3c14 test-data/output4.tiff
b
Binary file test-data/output4.tiff has changed
b
diff -r aef9911c0d5c -r 4a49f74a3c14 test-data/output5.tiff
b
Binary file test-data/output5.tiff has changed
b
diff -r aef9911c0d5c -r 4a49f74a3c14 test-data/output6.tiff
b
Binary file test-data/output6.tiff has changed
b
diff -r aef9911c0d5c -r 4a49f74a3c14 test-data/rois-illegal1.geojson
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/rois-illegal1.geojson Mon May 12 14:01:26 2025 +0000
[
@@ -0,0 +1,40 @@
+{
+    "type": "FeatureCollection",
+    "features": [
+        {
+            "type": "Feature",
+            "id": "a5d9de43-1a4a-4194-b06d-a6c6d0f81f91",
+            "geometry": {
+                "type": "Polygon",
+                "coordinates": [
+                    [
+                        [
+                            201,
+                            48
+                        ],
+                        [
+                            292,
+                            48
+                        ],
+                        [
+                            292,
+                            184
+                        ],
+                        [
+                            201,
+                            48
+                        ],
+                        [
+                            201,
+                            48
+                        ]
+                    ]
+                ]
+            },
+            "properties": {
+                "objectType": "annotation",
+                "name": "1"
+            }
+        }
+    ]
+}
b
diff -r aef9911c0d5c -r 4a49f74a3c14 test-data/rois-illegal2.geojson
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/rois-illegal2.geojson Mon May 12 14:01:26 2025 +0000
[
@@ -0,0 +1,40 @@
+{
+    "type": "FeatureCollection",
+    "features": [
+        {
+            "type": "Feature",
+            "id": "a5d9de43-1a4a-4194-b06d-a6c6d0f81f91",
+            "geometry": {
+                "type": "Point",
+                "coordinates": [
+                    [
+                        [
+                            201,
+                            48
+                        ],
+                        [
+                            292,
+                            48
+                        ],
+                        [
+                            292,
+                            184
+                        ],
+                        [
+                            201,
+                            184
+                        ],
+                        [
+                            201,
+                            48
+                        ]
+                    ]
+                ]
+            },
+            "properties": {
+                "objectType": "annotation",
+                "name": "1"
+            }
+        }
+    ]
+}
b
diff -r aef9911c0d5c -r 4a49f74a3c14 test-data/rois-noname.geojson
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/rois-noname.geojson Mon May 12 14:01:26 2025 +0000
[
@@ -0,0 +1,104 @@
+{
+    "type": "FeatureCollection",
+    "features": [
+        {
+            "type": "Feature",
+            "id": "9ef0487b-29a5-4e53-8eca-5c9dbf7bfc80",
+            "geometry": {
+                "type": "Polygon",
+                "coordinates": [
+                    [
+                        [
+                            124,
+                            149
+                        ],
+                        [
+                            183,
+                            149
+                        ],
+                        [
+                            183,
+                            275
+                        ],
+                        [
+                            124,
+                            275
+                        ],
+                        [
+                            124,
+                            149
+                        ]
+                    ]
+                ]
+            },
+            "properties": {
+                "objectType": "annotation"
+            }
+        },
+        {
+            "type": "Feature",
+            "id": "a5d9de43-1a4a-4194-b06d-a6c6d0f81f91",
+            "geometry": {
+                "type": "Polygon",
+                "coordinates": [
+                    [
+                        [
+                            201,
+                            48
+                        ],
+                        [
+                            292,
+                            48
+                        ],
+                        [
+                            292,
+                            184
+                        ],
+                        [
+                            201,
+                            184
+                        ],
+                        [
+                            201,
+                            48
+                        ]
+                    ]
+                ]
+            }
+        },
+        {
+            "type": "Feature",
+            "id": "b7b348f7-5438-47e3-a4ae-dbc923aa4e3b",
+            "geometry": {
+                "type": "Polygon",
+                "coordinates": [
+                    [
+                        [
+                            151,
+                            95
+                        ],
+                        [
+                            260,
+                            95
+                        ],
+                        [
+                            260,
+                            162
+                        ],
+                        [
+                            151,
+                            162
+                        ],
+                        [
+                            151,
+                            95
+                        ]
+                    ]
+                ]
+            },
+            "properties": {
+                "objectType": "annotation"
+            }
+        }
+    ]
+}
b
diff -r aef9911c0d5c -r 4a49f74a3c14 test-data/rois.geojson
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/rois.geojson Mon May 12 14:01:26 2025 +0000
[
@@ -0,0 +1,110 @@
+{
+    "type": "FeatureCollection",
+    "features": [
+        {
+            "type": "Feature",
+            "id": "9ef0487b-29a5-4e53-8eca-5c9dbf7bfc80",
+            "geometry": {
+                "type": "Polygon",
+                "coordinates": [
+                    [
+                        [
+                            124,
+                            149
+                        ],
+                        [
+                            183,
+                            149
+                        ],
+                        [
+                            183,
+                            275
+                        ],
+                        [
+                            124,
+                            275
+                        ],
+                        [
+                            124,
+                            149
+                        ]
+                    ]
+                ]
+            },
+            "properties": {
+                "objectType": "annotation",
+                "name": "2"
+            }
+        },
+        {
+            "type": "Feature",
+            "id": "a5d9de43-1a4a-4194-b06d-a6c6d0f81f91",
+            "geometry": {
+                "type": "Polygon",
+                "coordinates": [
+                    [
+                        [
+                            201,
+                            48
+                        ],
+                        [
+                            292,
+                            48
+                        ],
+                        [
+                            292,
+                            184
+                        ],
+                        [
+                            201,
+                            184
+                        ],
+                        [
+                            201,
+                            48
+                        ]
+                    ]
+                ]
+            },
+            "properties": {
+                "objectType": "annotation",
+                "name": "1"
+            }
+        },
+        {
+            "type": "Feature",
+            "id": "b7b348f7-5438-47e3-a4ae-dbc923aa4e3b",
+            "geometry": {
+                "type": "Polygon",
+                "coordinates": [
+                    [
+                        [
+                            151,
+                            95
+                        ],
+                        [
+                            260,
+                            95
+                        ],
+                        [
+                            260,
+                            162
+                        ],
+                        [
+                            151,
+                            162
+                        ],
+                        [
+                            151,
+                            95
+                        ]
+                    ]
+                ]
+            },
+            "properties": {
+                "objectType": "annotation",
+                "name": "3"
+            }
+        }
+    ]
+}
\ No newline at end of file